|
@@ -0,0 +1,105 @@
|
|
|
+package slice
|
|
|
+
|
|
|
+import "base:runtime"
|
|
|
+
|
|
|
+// An in-place permutation iterator.
|
|
|
+Permutation_Iterator :: struct($T: typeid) {
|
|
|
+ index: int,
|
|
|
+ slice: []T,
|
|
|
+ counters: []int,
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+Make an iterator to permute a slice in-place.
|
|
|
+
|
|
|
+*Allocates Using Provided Allocator*
|
|
|
+
|
|
|
+This procedure allocates some state to assist in permutation and does not make
|
|
|
+a copy of the underlying slice. If you want to permute a slice without altering
|
|
|
+the underlying data, use `clone` to create a copy, then permute that instead.
|
|
|
+
|
|
|
+Inputs:
|
|
|
+- slice: The slice to permute.
|
|
|
+- allocator: (default is context.allocator)
|
|
|
+
|
|
|
+Returns:
|
|
|
+- iter: The iterator, to be passed to `permute`.
|
|
|
+- error: An `Allocator_Error`, if allocation failed.
|
|
|
+*/
|
|
|
+make_permutation_iterator :: proc(
|
|
|
+ slice: []$T,
|
|
|
+ allocator := context.allocator,
|
|
|
+) -> (
|
|
|
+ iter: Permutation_Iterator(T),
|
|
|
+ error: runtime.Allocator_Error,
|
|
|
+) #optional_allocator_error {
|
|
|
+ iter.slice = slice
|
|
|
+ iter.counters = make([]int, len(iter.slice), allocator) or_return
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+/*
|
|
|
+Free the state allocated by `make_permutation_iterator`.
|
|
|
+
|
|
|
+Inputs:
|
|
|
+- iter: The iterator created by `make_permutation_iterator`.
|
|
|
+- allocator: The allocator used to create the iterator. (default is context.allocator)
|
|
|
+*/
|
|
|
+destroy_permutation_iterator :: proc(
|
|
|
+ iter: Permutation_Iterator($T),
|
|
|
+ allocator := context.allocator,
|
|
|
+) {
|
|
|
+ delete(iter.counters, allocator = allocator)
|
|
|
+}
|
|
|
+/*
|
|
|
+Permute a slice in-place.
|
|
|
+
|
|
|
+Note that the first iteration will always be the original, unpermuted slice.
|
|
|
+
|
|
|
+Inputs:
|
|
|
+- iter: The iterator created by `make_permutation_iterator`.
|
|
|
+
|
|
|
+Returns:
|
|
|
+- ok: True if the permutation succeeded, false if the iteration is complete.
|
|
|
+*/
|
|
|
+permute :: proc(iter: ^Permutation_Iterator($T)) -> (ok: bool) {
|
|
|
+ // This is an iterative, resumable implementation of Heap's algorithm.
|
|
|
+ //
|
|
|
+ // The original algorithm was described by B. R. Heap as "Permutations by
|
|
|
+ // interchanges" in The Computer Journal, 1963.
|
|
|
+ //
|
|
|
+ // This implementation is based on the nonrecursive version described by
|
|
|
+ // Robert Sedgewick in "Permutation Generation Methods" which was published
|
|
|
+ // in ACM Computing Surveys in 1977.
|
|
|
+
|
|
|
+ i := iter.index
|
|
|
+
|
|
|
+ if i == 0 {
|
|
|
+ iter.index = 1
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ n := len(iter.counters)
|
|
|
+ #no_bounds_check for i < n {
|
|
|
+ if iter.counters[i] < i {
|
|
|
+ if i & 1 == 0 {
|
|
|
+ iter.slice[0], iter.slice[i] = iter.slice[i], iter.slice[0]
|
|
|
+ } else {
|
|
|
+ iter.slice[iter.counters[i]], iter.slice[i] = iter.slice[i], iter.slice[iter.counters[i]]
|
|
|
+ }
|
|
|
+
|
|
|
+ iter.counters[i] += 1
|
|
|
+ i = 1
|
|
|
+
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ iter.counters[i] = 0
|
|
|
+ i += 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if i == n {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ iter.index = i
|
|
|
+ return true
|
|
|
+}
|