extern crate alloc;
use alloc::vec::Vec;
use proptest::prelude::*;
use crate::kairos::Kairos;
use crate::metis::{Anchor, Composer, Dot, DotSet, Locus, Metatheses, Metathesis, Rhapsody};
use super::{Moves, Text, clock, d, move_to};
fn three_siblings() -> (Text, Dot, Dot, Dot) {
let mut text: Text = Composer::new(1);
let clk = clock(1);
let mut born = [None; 3];
for slot in &mut born {
let rank = clk.now(0u16);
let (dot, _) = text.compose(|dot| {
let mut delta = Rhapsody::new();
assert!(delta.weave(
dot,
Locus {
anchor: Anchor::Origin,
rank,
}
));
(delta, DotSet::new())
});
*slot = Some(dot);
}
let [a, b, c] = born.map(|dot| dot.expect("three siblings born"));
(text, a, b, c)
}
#[test]
fn test_the_replay_is_a_fixed_point_not_an_optimum() {
let (text, a, b, c) = three_siblings();
let clk = clock(4);
let m1_to = Locus {
anchor: Anchor::After(a.into()),
rank: clk.now(0u16),
};
let m2_to = Locus {
anchor: Anchor::After(b.into()),
rank: clk.now(0u16),
};
let m3_to = Locus {
anchor: Anchor::After(c.into()),
rank: clk.now(0u16),
};
let mut moves: Moves = Composer::new(4);
let _ = move_to(&mut moves, b.into(), m1_to);
let _ = move_to(&mut moves, c.into(), m2_to);
let (m3, _) = move_to(&mut moves, a.into(), m3_to);
let recension = text.state().store().recension(moves.state().store());
assert_eq!(
recension.refused(),
[m3],
"the heaviest move is refused as a cycle-former"
);
assert_eq!(recension.order(), [a, b, c], "the applied chain a > b > c");
let mut heavier: Moves = Composer::new(5);
let _ = move_to(&mut heavier, c.into(), m2_to);
let _ = move_to(&mut heavier, a.into(), m3_to);
let alternative = text.state().store().recension(heavier.state().store());
assert!(
alternative.refused().is_empty(),
"the subset the replay forgoes is feasible on its own"
);
assert_eq!(alternative.order(), [b, c, a]);
}
#[test]
fn test_per_stream_replays_do_not_compose() {
let (text, a, b, c) = three_siblings();
let clk = clock(4);
let m12_to = Locus {
anchor: Anchor::After(a.into()),
rank: clk.now(0u16),
};
let m13_to = Locus {
anchor: Anchor::After(c.into()),
rank: clk.now(0u16),
};
let m23_to = Locus {
anchor: Anchor::After(b.into()),
rank: clk.now(0u16),
};
let mut stream_a: Moves = Composer::new(4);
let _ = move_to(&mut stream_a, b.into(), m12_to);
let (m23_in_a, _) = move_to(&mut stream_a, c.into(), m23_to);
let replay_a = text.state().store().recension(stream_a.state().store());
assert!(replay_a.refused().is_empty());
assert_eq!(replay_a.order(), [a, b, c]);
let _ = m23_in_a;
let mut stream_b: Moves = Composer::new(5);
let _ = move_to(&mut stream_b, a.into(), m13_to);
let replay_b = text.state().store().recension(stream_b.state().store());
assert!(replay_b.refused().is_empty());
let mut union: Moves = Composer::new(6);
let _ = move_to(&mut union, b.into(), m12_to);
let _ = move_to(&mut union, a.into(), m13_to);
let (m23, _) = move_to(&mut union, c.into(), m23_to);
let merged = text.state().store().recension(union.state().store());
assert_eq!(
merged.refused(),
[m23],
"the merged replay refuses a move both streams' replays would keep"
);
assert_eq!(merged.order(), [c, a, b], "the union's fixed point");
}
fn gadget_rank(physical: u64, station: u32) -> Kairos {
Kairos::new(physical, 0u16, station, 0u16)
}
fn encoded_record_with_probe(
x: &[bool],
probe_at: usize,
(a, b, c): (Dot, Dot, Dot),
) -> Metatheses {
let mut merged = Metatheses::new();
for (j, &bit) in x.iter().enumerate() {
let anchor = if bit {
Anchor::After(c.into())
} else {
Anchor::After(a.into())
};
let witness = d(4, j as u64 + 1);
assert!(merged.insert(
witness,
Metathesis {
target: b.into(),
to: Locus {
anchor,
rank: gadget_rank(100 + 10 * (j as u64 + 1), 4),
},
},
));
}
let probe_base = 100 + 10 * probe_at as u64;
assert!(merged.insert(
d(5, 1),
Metathesis {
target: a.into(),
to: Locus {
anchor: Anchor::After(b.into()),
rank: gadget_rank(probe_base + 3, 5),
},
},
));
assert!(merged.insert(
d(5, 2),
Metathesis {
target: c.into(),
to: Locus {
anchor: Anchor::After(b.into()),
rank: gadget_rank(probe_base + 6, 5),
},
},
));
merged
}
#[test]
fn test_the_probe_decodes_the_record_through_the_merged_replay() {
let (text, a, b, c) = three_siblings();
for encoded in 0u8..8 {
let x: Vec<bool> = (0..3).map(|j| encoded & (1 << j) != 0).collect();
let mut decoded = Vec::new();
for i in 1..=3 {
let merged = encoded_record_with_probe(&x, i, (a, b, c));
let replay = text.state().store().recension(&merged);
assert!(replay.pending().is_empty());
let order = replay.order();
decoded.push(if order == [a, b, c] {
false
} else {
assert_eq!(
order,
[c, b, a],
"the two probe outcomes are the two chains"
);
true
});
}
assert_eq!(
decoded, x,
"every bit of the record survives to the outcome"
);
}
}
proptest! {
#[test]
fn prop_the_probe_decodes_any_position_of_any_record(
x in prop::collection::vec(any::<bool>(), 1..24),
pick in any::<prop::sample::Index>(),
) {
let (text, a, b, c) = three_siblings();
let i = pick.index(x.len()) + 1;
let merged = encoded_record_with_probe(&x, i, (a, b, c));
let replay = text.state().store().recension(&merged);
prop_assert!(replay.pending().is_empty());
let bit = x[i - 1];
let expected_order = if bit { [c, b, a] } else { [a, b, c] };
prop_assert_eq!(replay.order(), expected_order);
let losing_probe = if bit { d(5, 2) } else { d(5, 1) };
let mut expected_refusals = alloc::vec![losing_probe];
for (j, &later) in x.iter().enumerate().skip(i) {
if later != bit {
expected_refusals.push(d(4, j as u64 + 1));
}
}
prop_assert_eq!(replay.refused(), expected_refusals.as_slice());
}
}