diff --git a/vlib/arrays/arrays.v b/vlib/arrays/arrays.v index 905b6fcce4..fda4c84045 100644 --- a/vlib/arrays/arrays.v +++ b/vlib/arrays/arrays.v @@ -377,3 +377,156 @@ pub fn binary_search(arr []T, target T) ?int { } return error('') } + +// rotate_left rotates the array in-place such that the first `mid` elements of the array move to the end +// while the last `arr.len - mid` elements move to the front. After calling `rotate_left`, the element +// previously at index `mid` will become the first element in the array. +// Example: +// ```v +// mut x := [1,2,3,4,5,6] +// arrays.rotate_left(mut x,2) +// println(x) // [3, 4, 5, 6, 1, 2] +// ``` +pub fn rotate_left(mut arr []T, mid int) { + assert mid <= arr.len && mid >= 0 + k := arr.len - mid + p := &T(arr.data) + unsafe { + ptr_rotate(mid, &T(usize(voidptr(p)) + usize(sizeof(T)) * usize(mid)), k) + } +} + +// rotate_right rotates the array in-place such that the first `arr.len - k` elements of the array move to the end +// while the last `k` elements move to the front. After calling `rotate_right`, the element previously at index `arr.len - k` +// will become the first element in the array. +// Example: +// ```v +// mut x := [1,2,3,4,5,6] +// arrays.rotate_right(mut x, 2) +// println(x) // [5, 6, 1, 2, 3, 4] +// ``` +pub fn rotate_right(mut arr []T, k int) { + assert k <= arr.len && k >= 0 + mid := arr.len - k + p := &T(arr.data) + unsafe { + ptr_rotate(mid, &T(usize(voidptr(p)) + usize(sizeof(T)) * usize(mid)), k) + } +} + +[unsafe] +fn ptr_rotate(left_ int, mid &T, right_ int) { + mut left := usize(left_) + mut right := usize(right_) + for { + delta := if left < right { left } else { right } + + if delta <= raw_array_cap() { + break + } + unsafe { + swap_nonoverlapping(&T(usize(voidptr(mid)) - left * usize(sizeof(T))), + &T(usize(voidptr(mid)) + usize(right - delta) * usize(sizeof(T))), int(delta)) + } + if left <= right { + right -= delta + } else { + left -= delta + } + } + + unsafe { + sz := usize(sizeof(T)) + rawarray := C.malloc(raw_array_malloc_size()) + dim := &T(usize(voidptr(mid)) - left * sz + right * sz) + if left <= right { + C.memcpy(rawarray, voidptr(usize(voidptr(mid)) - left * sz), left * sz) + C.memmove(voidptr(usize(voidptr(mid)) - left * sz), voidptr(mid), right * sz) + C.memcpy(voidptr(dim), rawarray, left * sz) + } else { + C.memcpy(rawarray, voidptr(mid), right * sz) + C.memmove(voidptr(dim), voidptr(usize(voidptr(mid)) - left * sz), left * sz) + C.memcpy(voidptr(usize(voidptr(mid)) - left * sz), rawarray, right * sz) + } + C.free(rawarray) + } +} + +struct Block { +mut: + x u64 + y u64 + z u64 + w u64 +} + +struct UnalignedBlock { +mut: + x u64 + y u64 + z u64 + w u64 +} + +const ( + extra_size = 32 * sizeof(usize) +) + +fn raw_array_cap() usize { + if sizeof(T) > arrays.extra_size { + return 1 + } else { + return arrays.extra_size / sizeof(T) + } +} + +fn raw_array_malloc_size() usize { + if sizeof(T) > arrays.extra_size { + return usize(sizeof(T)) * 2 + } else { + return 32 * usize(sizeof(usize)) + } +} + +[unsafe] +fn memswap(x voidptr, y voidptr, len usize) { + block_size := sizeof(Block) + + mut i := usize(0) + for i + block_size <= len { + mut t_ := Block{} + t := voidptr(&t_) + + xi := usize(x) + i + yi := usize(y) + i + unsafe { + C.memcpy(t, voidptr(xi), block_size) + C.memcpy(voidptr(xi), voidptr(yi), block_size) + C.memcpy(t, voidptr(yi), block_size) + } + i += block_size + } + if i < len { + mut t_ := UnalignedBlock{} + t := voidptr(&t_) + rem := len - i + xi := usize(x) + i + yi := usize(y) + i + unsafe { + C.memcpy(t, voidptr(xi), rem) + C.memcpy(voidptr(xi), voidptr(yi), rem) + C.memcpy(voidptr(yi), t, rem) + } + } +} + +[unsafe] +fn swap_nonoverlapping(x_ &T, y_ &T, count int) { + x := voidptr(x_) + y := voidptr(y_) + + len := usize(sizeof(T)) * usize(count) + unsafe { + memswap(x, y, len) + } +} diff --git a/vlib/arrays/arrays_test.v b/vlib/arrays/arrays_test.v index b5563bc87a..c61d71f74f 100644 --- a/vlib/arrays/arrays_test.v +++ b/vlib/arrays/arrays_test.v @@ -218,3 +218,49 @@ fn test_upper_bound() ? { assert (upper_bound(b, 4) or { -1 }) == -1 assert upper_bound(c, 2) ? == 2 } + +fn test_rotate_right() { + mut x := [1, 2, 3, 4, 5, 6] + rotate_right(mut x, 2) + assert x == [5, 6, 1, 2, 3, 4] +} + +fn test_rotate_left() { + mut x := [1, 2, 3, 4, 5, 6] + rotate_left(mut x, 2) + assert x == [3, 4, 5, 6, 1, 2] +} + +struct Abc { + x u64 = 1 + y u64 = 2 + z u64 = 3 +} + +fn test_rotate_right_struct() { + mut x := [Abc{1, 0, 1}, Abc{2, 0, 1}, Abc{3, 0, 1}, Abc{4, 0, 1}, + Abc{5, 0, 1}, Abc{6, 0, 1}] + rotate_right(mut x, 2) + assert x == [Abc{5, 0, 1}, Abc{6, 0, 1}, Abc{1, 0, 1}, Abc{2, 0, 1}, + Abc{3, 0, 1}, Abc{4, 0, 1}] +} + +fn test_rotate_left_struct() { + mut x := [Abc{1, 0, 1}, Abc{2, 0, 1}, Abc{3, 0, 1}, Abc{4, 0, 1}, + Abc{5, 0, 1}, Abc{6, 0, 1}] + rotate_left(mut x, 2) + assert x == [Abc{3, 0, 1}, Abc{4, 0, 1}, Abc{5, 0, 1}, Abc{6, 0, 1}, + Abc{1, 0, 1}, Abc{2, 0, 1}] +} + +fn test_rotate_right_string() { + mut x := ['x1', 'x2', 'x3', 'x4', 'x5', 'x6'] + rotate_right(mut x, 2) + assert x == ['x5', 'x6', 'x1', 'x2', 'x3', 'x4'] +} + +fn test_rotate_left_string() { + mut x := ['x1', 'x2', 'x3', 'x4', 'x5', 'x6'] + rotate_left(mut x, 2) + assert x == ['x3', 'x4', 'x5', 'x6', 'x1', 'x2'] +}