1 module format.rulevalues;
2 
3 struct RuleValues {
4 private:
5 	import core.bitop;
6 	enum Bits = 8 * size_t.sizeof;
7 	enum Mask = Bits - 1;
8 	enum DirectBits = 2 * Bits;
9 	enum DirectCapacity = DirectBits - bsf(DirectBits);
10 	enum DirectShift = DirectCapacity - Bits;
11 
12 	union {
13 		struct {
14 			size_t* uptr;
15 			size_t ulength;
16 		}
17 
18 		size_t[2] direct;
19 	}
20 
21 	bool isDirect() const {
22 		return direct[0] & 0x01;
23 	}
24 
25 public:
26 	this(size_t frozen, size_t capacity) in {
27 		assert(frozen > 0 && capacity >= frozen);
28 	} do {
29 		if (capacity > DirectCapacity) {
30 			size_t length = 1 + (capacity + Bits - 1) / Bits;
31 
32 			indirect = new size_t[length];
33 			indirect[0] = frozen;
34 			indirect[1] = 0x01;
35 		} else {
36 			direct[0] = 0x01;
37 			direct[1] = frozen << DirectShift;
38 		}
39 	}
40 
41 	RuleValues clone() const {
42 		RuleValues ret = void;
43 		if (isDirect()) {
44 			ret.direct = direct;
45 		} else {
46 			ret.indirect = indirect.dup;
47 		}
48 
49 		return ret;
50 	}
51 
52 	RuleValues withFrozenSplit(size_t i) const in {
53 		assert(i >= frozen && i < capacity);
54 	} do {
55 		RuleValues ret = clone();
56 
57 		ret[i] = true;
58 		ret.frozen = i + 1;
59 		return ret;
60 	}
61 
62 	@property
63 	size_t capacity() const {
64 		return isDirect() ? DirectCapacity : (indirect.length - 1) * Bits;
65 	}
66 
67 	@property
68 	size_t frozen() const {
69 		return isDirect() ? (direct[1] >> DirectShift) : indirect[0];
70 	}
71 
72 	@property
73 	size_t frozen(size_t f) in {
74 		assert(f > 0 && f <= capacity);
75 		assert(this[f - 1]);
76 	} do {
77 		if (isDirect()) {
78 			// Replace the previous frozen value.
79 			direct[1] &= (size_t(1) << DirectShift) - 1;
80 			direct[1] |= f << DirectShift;
81 		} else {
82 			*uptr = f;
83 		}
84 
85 		return frozen;
86 	}
87 
88 	bool opIndex(size_t i) const {
89 		return (values[word(i)] >> shift(i)) & 0x01;
90 	}
91 
92 	void opIndexAssign(bool v, size_t i) in {
93 		assert(i >= frozen && i < capacity);
94 	} do {
95 		auto w = word(i);
96 		auto m = size_t(1) << shift(i);
97 
98 		if (v) {
99 			values[w] |= m;
100 		} else {
101 			values[w] &= ~m;
102 		}
103 	}
104 
105 	bool opEqual(const ref RuleValues rhs) const {
106 		const d = isDirect();
107 		if (rhs.isDirect() != d) {
108 			return false;
109 		}
110 
111 		return d ? direct == rhs.direct : indirect == rhs.indirect;
112 	}
113 
114 	int opCmp(const ref RuleValues rhs) const {
115 		// We don't really use this, but let's be throurough.
116 		if (capacity != rhs.capacity) {
117 			return cast(int) (capacity - rhs.capacity);
118 		}
119 
120 		// Explore candidate with a few follow up first.
121 		if (frozen != rhs.frozen) {
122 			return cast(int) (rhs.frozen - frozen);
123 		}
124 
125 		foreach_reverse (i; 0 .. capacity) {
126 			if (this[i] != rhs[i]) {
127 				return rhs[i] - this[i];
128 			}
129 		}
130 
131 		return 0;
132 	}
133 
134 private:
135 	@property
136 	inout(size_t)[] values() inout {
137 		return isDirect() ? direct[] : indirect[1 .. $];
138 	}
139 
140 	@property
141 	inout(size_t)[] indirect() inout {
142 		return uptr[0 .. ulength];
143 	}
144 
145 	@property
146 	size_t[] indirect(size_t[] v) {
147 		uptr = v.ptr;
148 		ulength = v.length;
149 		return indirect;
150 	}
151 
152 	static word(size_t i) {
153 		return i / Bits;
154 	}
155 
156 	static shift(size_t i) {
157 		return i & Mask;
158 	}
159 }
160 
161 unittest {
162 	foreach (i; 0 .. RuleValues.DirectCapacity) {
163 		const c = i + 1;
164 		const rv = RuleValues(1, c);
165 		assert(rv.isDirect());
166 		assert(rv.capacity == RuleValues.DirectCapacity);
167 	}
168 
169 	foreach (i; RuleValues.DirectCapacity .. 16 * size_t.sizeof) {
170 		const c = i + 1;
171 		const rv = RuleValues(1, c);
172 		assert(!rv.isDirect());
173 		assert(rv.capacity == 16 * size_t.sizeof);
174 	}
175 
176 	foreach (i; 16 * size_t.sizeof .. 24 * size_t.sizeof) {
177 		const c = i + 1;
178 		const rv = RuleValues(1, c);
179 		assert(!rv.isDirect());
180 		assert(rv.capacity == 24 * size_t.sizeof);
181 	}
182 }
183 
184 unittest {
185 	auto r0 = RuleValues(1, 10);
186 	auto r1 = r0.clone();
187 	assert(r0 == r1);
188 	assert(!(r0 < r1));
189 	assert(!(r1 > r0));
190 
191 	r0[5] = true;
192 	assert(r0 != r1);
193 	assert(r0 < r1);
194 	assert(r1 > r0);
195 
196 	r1[5] = true;
197 	assert(r0 == r1);
198 	assert(!(r0 < r1));
199 	assert(!(r1 > r0));
200 
201 	r1.frozen = 6;
202 	assert(r0 != r1);
203 	assert(r0 > r1);
204 	assert(r1 < r0);
205 
206 	r0.frozen = 6;
207 	assert(r0 == r1);
208 	assert(!(r0 < r1));
209 	assert(!(r1 > r0));
210 
211 	auto r2 = RuleValues(1, 1000);
212 	assert(r0 != r2);
213 	assert(r0 < r2);
214 	assert(r1 != r2);
215 	assert(r1 < r2);
216 }
217 
218 unittest {
219 	auto rv = RuleValues(1, 10);
220 
221 	assert(rv[0]);
222 	foreach (i; 1 .. 10) {
223 		assert(!rv[i]);
224 	}
225 
226 	rv[9] = true;
227 
228 	assert(rv[0]);
229 	assert(rv[9]);
230 	foreach (i; 1 .. 9) {
231 		assert(!rv[i]);
232 	}
233 
234 	rv[7] = true;
235 
236 	assert(rv[0]);
237 	assert(rv[7]);
238 	assert(!rv[8]);
239 	assert(rv[9]);
240 	foreach (i; 1 .. 7) {
241 		assert(!rv[i]);
242 	}
243 
244 	rv[9] = false;
245 
246 	assert(rv[0]);
247 	assert(rv[7]);
248 	assert(!rv[8]);
249 	assert(!rv[9]);
250 	foreach (i; 1 .. 7) {
251 		assert(!rv[i]);
252 	}
253 
254 	rv[7] = false;
255 
256 	assert(rv[0]);
257 	foreach (i; 1 .. 10) {
258 		assert(!rv[i]);
259 	}
260 }