package cardsim import ( "fmt" ) // InsertInto inserts one or more items into a slice at an arbitrary index. // Items already in the slice past the target position move to later positons. // // Like `append`, this may move the underlying array and it produces a new // slice header (under the hood, it uses `append`). It returns the new slice // (the original is in an undefined state and should no longer be used). // // If loc is negative or more than one past the end of T, Insert panics. func InsertInto[T any](slice []T, loc int, elements ...T) []T { if loc < 0 || loc > len(slice) { panic(fmt.Sprintf("can't Insert at location %d in %d-element slice", loc, len(slice))) } // is this a no-op? if len(elements) == 0 { return slice } // is this just an append? if loc == len(slice) { return append(slice, elements...) } offset := len(elements) oldLen := len(slice) newSize := oldLen + offset if newSize <= cap(slice) { // We can reslice in place. slice = slice[:newSize] // Scoot trailing to their new positions. copy(slice[loc+offset:], slice[loc:oldLen]) // Insert the new elements. copy(slice[loc:], elements) return slice } // Reallocate. Do the normal thing of doubling the size as a minimum // when increasing space for a dynamic array; this amortizes the // cost of repeatedly reallocating and moving the slice. newCap := cap(slice) * 2 if newCap < newSize { newCap = newSize } newSlice := make([]T, newSize, newCap) if loc > 0 { copy(newSlice, slice[0:loc]) } copy(newSlice[loc:], elements) copy(newSlice[loc+offset:], slice[loc:]) return newSlice } // DeleteFrom deletes an item from a slice at an arbitrary index. Items after it // scoot up to close the gap. This returns the modified slice (like Append). // // If the provided location is not a valid location in the slice, this panics. func DeleteFrom[T any](slice []T, loc int) []T { return DeleteNFrom(slice, loc, 1) } // DeleteNFrom deletes N items from a slice at an arbitrary index. Items after // it scoot up to close the gap. This returns the modified slice (like Append). // // If the range of items that would be deleted is not entirely valid within the // slice, this panics. func DeleteNFrom[T any](slice []T, loc, n int) []T { if loc < 0 || loc+n > len(slice) { panic(fmt.Sprintf("can't delete %d elements from a %d-element slice at location %d", n, len(slice), loc)) } // Easy cases. if n == 0 { return slice } if loc == 0 { return slice[n:] } if loc+n == len(slice) { return slice[0:loc] } // Is it shorter to move up or move down? if len(slice)-loc-n > loc { // Move forward -- the end is big. copy(slice[n:], slice[:loc]) return slice[n:] } // Move backward -- the beginnng is big or they're the same size // (and moving backwards preserves more usable append capacity later). copy(slice[loc:], slice[loc+n:]) return slice[:len(slice)-n] }