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
106
107
108
109
110
111
112
113
114
115
//! Combinatorial encoding and decoding utilities, such as binomial coefficients and multiset encoding/decoding to flattened index spaces
use ;
/// Binomial coefficient C(n, k)
// /// Encode a multiset (combination with repetition) into a unique, compact index space
// pub fn multiset_encode<I: PrimInt, O: PrimInt>(items: &[I]) -> O {
// // keep track of the index while we are calculating it as the sum of binomial coefficients, which guarantees uniqueness and compactness
// let mut idx = 0usize;
// // now, iterate over the items
// for (i, a) in items.iter().map(|x| x.to_usize().unwrap()).enumerate() {
// // compute the binomial coefficient and add it to the index
// idx = idx + binom(a + i, i + 1);
// }
// O::from::<usize>(idx).unwrap()
// }
// /// Decode an index into a multiset of length k with values in 0..n
// pub fn multiset_decode<I: PrimInt, O: PrimInt>(idx: I, n: O, k: O, items: &mut [O]) {
// // basically, we are just inverting the encoding process, so start with the index and work backwards
// let mut idx = idx.to_usize().unwrap();
// // convert to usize for the math
// let n = n.to_usize().unwrap();
// let k = k.to_usize().unwrap();
// // same as above, we start with 'a' as the largest possible item, and work backwards towards a=0
// let mut a = n + k - 1usize;
// // the position of the item we are currently decoding, which starts at the end and moves backwards
// // this indicates which index into 'items' we are currently writing to
// let mut pos = k - 1;
// // start with the largest possible item, and work backwards, since we greedily choose the largest possible item each time
// for i in (1..=k).rev() {
// // find largest a such that C(x, i) <= idx, which tells us what size chunk to 'chop off' from the index value
// while binom(a, i) > idx {
// a -= 1;
// }
// // write the item to the current position
// items[pos] = O::from::<usize>(a - pos).unwrap();
// // we need to break out early if we are at the first item, so we don't underflow values
// if i == 1 {
// break;
// }
// // otherwise, on most loops, we need to record the difference and 'chop off' that chunk from the index, and keep track of the new value of 'a'
// idx -= binom(a, i);
// a -= 1;
// pos -= 1;
// }
// }