forked from odin-lang/Odin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpermute.odin
105 lines (88 loc) · 2.52 KB
/
permute.odin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
}