Skip to content

Commit

Permalink
slice: add Rotate
Browse files Browse the repository at this point in the history
  • Loading branch information
creachadair committed Nov 20, 2023
1 parent 92fda97 commit fd70e2e
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
40 changes: 40 additions & 0 deletions slice/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,43 @@ func MatchingKeys[T comparable, U any](m map[T]U, f func(U) bool) []T {
}
return out
}

// Rotate permutes the elements of ss in-place by k positions.
// If k > 0, elements are rotated rightward.
// If k < 0, elements are rotated leftward.
// If k is out of range, Rotate will panic.
//
// For example, if
//
// ss := []string{"a", "b", "c", "d"}
//
// then slice.Rotate(ss, 1) produces
//
// {"d", "a", "b", "c"}
//
// while slice.Rotate(ss, -1) produces
//
// {"b", "c", "d", "a"}
//
// The rotation operation takes time proportional to len(ss) but does not
// allocate storage outside the input slice.
func Rotate[T any, Slice ~[]T](ss Slice, k int) {
if k < 0 {
k += len(ss)
}
if k < 0 || k > len(ss) {
panic("index out of range")
} else if k == 0 || k == len(ss) {
return
}
i, cur := 0, ss[0]
for {
next := (i + k) % len(ss)
nextv := ss[next]
ss[next] = cur
if next == 0 {
break
}
i, cur = next, nextv
}
}
34 changes: 34 additions & 0 deletions slice/slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,40 @@ func TestMatchingKeys(t *testing.T) {
}
}

func TestRotate(t *testing.T) {
tests := []struct {
input string
k int
want string
}{
{"", 0, ""}, // empty input
{"X", 0, "X"},
{"X", 1, "X"},
{"X", -1, "X"},
{"A B", 0, "A B"},
{"A B", 1, "B A"},
{"A B", -1, "B A"},
{"A B C D E", 0, "A B C D E"},
{"A B C D E", 1, "E A B C D"},
{"A B C D E", 2, "D E A B C"},
{"A B C D E", 3, "C D E A B"},
{"A B C D E", 4, "B C D E A"},
{"A B C D E", 5, "A B C D E"},
{"A B C D E", -1, "B C D E A"},
{"A B C D E", -2, "C D E A B"},
{"A B C D E", -3, "D E A B C"},
{"A B C D E", -4, "E A B C D"},
{"A B C D E", -5, "A B C D E"},
}
for _, tc := range tests {
got := strings.Fields(tc.input)
slice.Rotate(got, tc.k)
if diff := cmp.Diff(got, strings.Fields(tc.want)); diff != "" {
t.Errorf("Rotate %q %d (-got, +want):\n%s", tc.input, tc.k, diff)
}
}
}

func (tc *testCase[T]) partition(t *testing.T) {
t.Helper()

Expand Down

0 comments on commit fd70e2e

Please sign in to comment.