1use crate::IndigoBuildHasher;
2use std::{
3 collections::{BTreeSet, HashSet},
4 hash::{BuildHasher, Hash},
5 ops::Deref,
6 sync::atomic::{AtomicUsize, Ordering},
7};
8
9pub trait Join {
26 fn join(&mut self, other: Self);
27}
28
29pub trait Apply<Delta> {
41 type Error;
42
43 fn apply(&mut self, delta: Delta) -> Result<(), Self::Error>;
44}
45
46static CLOCK: AtomicUsize = AtomicUsize::new(0);
47
48#[derive(Clone)]
49pub struct LastWriteWins<T: ?Sized> {
50 tick: usize,
51 value: T,
52}
53
54impl<T> LastWriteWins<T> {
55 #[must_use]
56 pub fn new(value: T) -> Self {
57 Self {
58 tick: CLOCK.fetch_add(1, Ordering::Relaxed) + 1,
59 value,
60 }
61 }
62
63 #[must_use]
64 pub fn read(&self) -> &T {
65 &self.value
66 }
67
68 pub fn write(&mut self, value: T) {
69 self.tick = CLOCK.fetch_add(1, Ordering::Relaxed) + 1;
70 self.value = value;
71 }
72}
73
74impl<T> Deref for LastWriteWins<T> {
75 type Target = T;
76 fn deref(&self) -> &Self::Target {
77 self.read()
78 }
79}
80
81impl<T> Join for LastWriteWins<T> {
82 fn join(&mut self, other: Self) {
83 if self.tick < other.tick {
84 self.tick = other.tick;
85 self.value = other.value;
86 }
87 }
88}
89
90pub struct GrowOnlyHashSet<T, S = IndigoBuildHasher> {
111 set: HashSet<T, S>,
112}
113
114impl<T, S> GrowOnlyHashSet<T, S> {
115 #[must_use]
116 pub fn new() -> Self
117 where
118 S: Default,
119 {
120 Self::default()
121 }
122
123 pub fn insert(&mut self, value: T) -> bool
124 where
125 T: Eq + Hash,
126 S: BuildHasher,
127 {
128 self.set.insert(value)
129 }
130}
131
132impl<T, S> Default for GrowOnlyHashSet<T, S>
133where
134 S: Default,
135{
136 fn default() -> Self {
137 Self {
138 set: HashSet::default(),
139 }
140 }
141}
142
143impl<T, S> Deref for GrowOnlyHashSet<T, S> {
144 type Target = HashSet<T, S>;
145 fn deref(&self) -> &Self::Target {
146 &self.set
147 }
148}
149
150impl<T, S> Join for GrowOnlyHashSet<T, S>
151where
152 T: Eq + Hash,
153 S: BuildHasher,
154{
155 fn join(&mut self, mut other: Self) {
156 self.set.extend(other.set.drain());
157 }
158}
159
160pub struct GrowOnlyBTreeSet<T> {
161 set: BTreeSet<T>,
162}
163
164impl<T> GrowOnlyBTreeSet<T> {
165 #[must_use]
166 pub fn new() -> Self {
167 Self::default()
168 }
169
170 pub fn insert(&mut self, value: T) -> bool
171 where
172 T: Ord,
173 {
174 self.set.insert(value)
175 }
176
177 pub fn append(&mut self, other: &mut Self)
178 where
179 T: Ord,
180 {
181 self.set.append(&mut other.set);
182 }
183}
184
185impl<T> Default for GrowOnlyBTreeSet<T> {
186 fn default() -> Self {
187 Self {
188 set: BTreeSet::default(),
189 }
190 }
191}
192
193impl<T> Deref for GrowOnlyBTreeSet<T> {
194 type Target = BTreeSet<T>;
195 fn deref(&self) -> &Self::Target {
196 &self.set
197 }
198}
199
200impl<T> Join for GrowOnlyBTreeSet<T>
201where
202 T: Ord,
203{
204 fn join(&mut self, mut other: Self) {
205 self.set.append(&mut other.set);
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use hegel::{TestCase, generators as gs};
213 use std::{cmp::max, iter::zip};
214
215 #[hegel::test(test_cases = 1_000)]
216 fn test_last_write_wins(tc: TestCase) {
217 #[derive(Default)]
218 struct StateMachine {
219 registers: Vec<LastWriteWins<i64>>,
220 model: Vec<(usize, i64)>,
221 next_sequence_number: usize,
222 }
223 #[expect(clippy::needless_pass_by_value)]
224 #[hegel::state_machine]
225 impl StateMachine {
226 fn draw_index(&self, tc: &TestCase) -> usize {
227 tc.assume(!self.registers.is_empty());
228 tc.draw(gs::integers::<usize>().max_value(self.registers.len() - 1))
229 }
230 fn next_write(&mut self, value: i64) -> (usize, i64) {
231 let sequence_number = self.next_sequence_number;
232 self.next_sequence_number += 1;
233 (sequence_number, value)
234 }
235 #[rule]
236 fn new_register(&mut self, tc: TestCase) {
237 let value = tc.draw(gs::integers::<i64>());
238 self.registers.push(LastWriteWins::new(value));
239 let write = self.next_write(value);
240 self.model.push(write);
241 }
242 #[rule]
243 fn write(&mut self, tc: TestCase) {
244 let index = self.draw_index(&tc);
245 let value = tc.draw(gs::integers::<i64>());
246 self.registers[index].write(value);
247 self.model[index] = self.next_write(value);
248 }
249 #[rule]
250 fn join(&mut self, tc: TestCase) {
251 let left = self.draw_index(&tc);
252 let right = self.draw_index(&tc);
253 let right_register = self.registers[right].clone();
254 self.registers[left].join(right_register);
255 self.model[left] = max(self.model[left], self.model[right]);
256 }
257 #[invariant]
260 fn reads_agree_with_model(&self, _: TestCase) {
261 for (register, (_, value)) in zip(&self.registers, &self.model) {
262 assert_eq!(register.read(), value);
263 }
264 }
265 }
266 let mut state_machine = StateMachine::default();
267 let value = tc.draw(gs::integers::<i64>());
268 state_machine.registers.push(LastWriteWins::new(value));
269 let write = state_machine.next_write(value);
270 state_machine.model.push(write);
271 hegel::stateful::machine(state_machine).run(tc);
272 }
273}