use polydat::iteration::comprehension::ast::Comprehension;
use polydat::iteration::comprehension::ir::{compile, interpret};
use polydat::iteration::comprehension::optimize::optimize;
use polydat::iteration::comprehension::source::{LiteralValue, Source};
use polydat::iteration::comprehension::strategies::TupleValue;
use polydat::iteration::comprehension::strategy::{StrategyName, ZipMode};
use polydat::iteration::comprehension::validate::{validate, Mode};
struct Rng {
state: u64,
inc: u64,
}
impl Rng {
fn new(seed: u64) -> Self {
Self { state: seed.wrapping_mul(0x9E37_79B9_7F4A_7C15), inc: 1 }
}
fn next_u64(&mut self) -> u64 {
self.state = self.state.wrapping_add(self.inc).wrapping_mul(0x9E37_79B9_7F4A_7C15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn range(&mut self, lo: u64, hi: u64) -> u64 {
lo + (self.next_u64() % (hi - lo))
}
fn coin(&mut self, p_true_percent: u64) -> bool {
(self.next_u64() % 100) < p_true_percent
}
}
struct AstGen {
rng: Rng,
axis_counter: u64,
}
impl AstGen {
fn new(seed: u64) -> Self {
Self { rng: Rng::new(seed), axis_counter: 0 }
}
fn next_name(&mut self) -> String {
let n = self.axis_counter;
self.axis_counter += 1;
format!("a{n}")
}
fn generate(&mut self, depth: u32) -> Comprehension {
if depth == 0 {
return self.gen_clause();
}
match self.rng.range(0, 10) {
0..=2 => self.gen_clause(),
3 | 4 => self.gen_cartesian(depth),
5 => self.gen_zip(depth),
6 => self.gen_union(depth),
7 | 8 => self.gen_filter(depth),
_ => self.gen_order(depth),
}
}
fn gen_clause(&mut self) -> Comprehension {
let name = self.next_name();
let source = if self.rng.coin(50) {
let n = self.rng.range(2, 6);
let values: Vec<LiteralValue> = (0..n)
.map(|i| LiteralValue::Int((i as i64) * 10 + 1))
.collect();
Source::Literal { values }
} else {
let lo = 0;
let hi = self.rng.range(2, 6) as i64;
Source::IntRange { lo, hi, step: 1 }
};
Comprehension::clause(name, source)
}
fn gen_cartesian(&mut self, depth: u32) -> Comprehension {
let n = self.rng.range(2, 4) as usize;
let children: Vec<Comprehension> = (0..n).map(|_| self.generate(depth - 1)).collect();
Comprehension::cartesian(children)
}
fn gen_zip(&mut self, depth: u32) -> Comprehension {
let n = self.rng.range(2, 4) as usize;
let size = self.rng.range(2, 5) as i64;
let children: Vec<Comprehension> = (0..n)
.map(|_| {
let name = self.next_name();
Comprehension::clause(
name,
Source::IntRange { lo: 0, hi: size, step: 1 },
)
})
.collect();
let mode = match self.rng.range(0, 3) {
0 => ZipMode::Strict,
1 => ZipMode::Truncate,
_ => ZipMode::Cycle,
};
let _ = depth; Comprehension::zip(children, mode)
}
fn gen_union(&mut self, depth: u32) -> Comprehension {
let n = self.rng.range(2, 3) as usize;
let template = self.generate(depth - 1);
let children: Vec<Comprehension> = (0..n)
.map(|_| template.clone())
.collect();
Comprehension::union(children)
}
fn gen_filter(&mut self, depth: u32) -> Comprehension {
let child = self.generate(depth - 1);
Comprehension::filter(child, "true")
}
fn gen_order(&mut self, depth: u32) -> Comprehension {
let child = self.generate(depth - 1);
let strategy = StrategyName::Lex;
let truncation = if self.rng.coin(40) {
Some(self.rng.range(1, 8))
} else {
None
};
Comprehension::order(child, strategy, truncation)
}
}
fn dispense_naive(ast: &Comprehension) -> Vec<Vec<(String, TupleValue)>> {
let prog = compile(ast);
let mut stream = interpret(&prog);
let mut out = Vec::new();
while let Some(t) = stream.advance() {
out.push(t.bindings);
}
out
}
fn dispense_optimized(ast: &Comprehension) -> Vec<Vec<(String, TupleValue)>> {
let opt = optimize(ast.clone());
let prog = compile(&opt);
let mut stream = interpret(&prog);
let mut out = Vec::new();
while let Some(t) = stream.advance() {
out.push(t.bindings);
}
out
}
fn run_harness(seed_base: u64, n_cases: usize, max_depth: u32) -> (usize, usize) {
use rayon::prelude::*;
(0..n_cases)
.into_par_iter()
.map(|i| run_one(seed_base, i, max_depth))
.reduce(|| (0, 0), |a, b| (a.0 + b.0, a.1 + b.1))
}
fn run_one(seed_base: u64, i: usize, max_depth: u32) -> (usize, usize) {
let mut ast_gen = AstGen::new(seed_base.wrapping_add(i as u64));
let ast = ast_gen.generate(max_depth);
if validate(&ast, Mode::Permissive).is_err() {
return (1, 0);
}
let naive_seq = dispense_naive(&ast);
if naive_seq.len() > 200 {
return (1, 0);
}
let optimized_seq = dispense_optimized(&ast);
if naive_seq != optimized_seq {
panic!(
"§9.2 equivalence failure on case {i}:\n\
AST: {ast:?}\n\
naive ({} tuples): {naive_seq:?}\n\
optimized ({} tuples): {optimized_seq:?}",
naive_seq.len(),
optimized_seq.len()
);
}
(1, 1)
}
#[test]
fn section_92_equivalence_random_asts_depth_2() {
let (tried, compared) = run_harness(0xABCD_EF01, 100, 2);
assert!(tried > 0);
println!("depth=2: tried={tried}, compared={compared}");
assert!(compared > tried / 2, "too many cases skipped: {compared}/{tried}");
}
#[test]
fn section_92_equivalence_random_asts_depth_3() {
let (tried, compared) = run_harness(0x1234_5678, 100, 3);
assert!(tried > 0);
println!("depth=3: tried={tried}, compared={compared}");
assert!(compared > tried / 2);
}
#[test]
fn section_92_equivalence_random_asts_depth_4() {
let (tried, compared) = run_harness(0xCAFE_BABE, 100, 4);
assert!(tried > 0);
println!("depth=4: tried={tried}, compared={compared}");
assert!(compared > tried / 2);
}
#[test]
fn idempotence_on_random_corpus() {
for i in 0..200 {
let mut ast_gen = AstGen::new(0xBADCAFE0_u64.wrapping_add(i));
let ast = ast_gen.generate(3);
if validate(&ast, Mode::Permissive).is_err() {
continue;
}
let once = optimize(ast.clone());
let twice = optimize(once.clone());
assert_eq!(once, twice, "optimizer not idempotent on AST {i}: {ast:?}");
}
}
#[test]
fn determinism_on_random_corpus() {
for i in 0..100 {
let mut ast_gen = AstGen::new(0xDEADBEEF_u64.wrapping_add(i));
let ast = ast_gen.generate(3);
if validate(&ast, Mode::Permissive).is_err() {
continue;
}
let a = dispense_naive(&ast);
let b = dispense_naive(&ast);
assert_eq!(a, b, "non-deterministic dispense on AST {i}");
}
}
#[test]
fn validation_total_on_random_corpus() {
for i in 0..500 {
let mut ast_gen = AstGen::new(0xFEED_FACE_u64.wrapping_add(i));
let ast = ast_gen.generate(3);
let _ = validate(&ast, Mode::Permissive); }
}
#[test]
fn generator_smoke_test() {
let mut shapes_seen: std::collections::HashSet<String> = std::collections::HashSet::new();
for i in 0..100 {
let mut ast_gen = AstGen::new(i);
let ast = ast_gen.generate(3);
shapes_seen.insert(shape_label(&ast));
}
assert!(shapes_seen.len() >= 3, "generator too narrow: {shapes_seen:?}");
}
fn shape_label(c: &Comprehension) -> String {
match c {
Comprehension::Clause { .. } => "clause".into(),
Comprehension::Cartesian { children } => format!("cart{}", children.len()),
Comprehension::Zip { children, mode } => format!("zip{}_{:?}", children.len(), mode),
Comprehension::Union { children } => format!("union{}", children.len()),
Comprehension::Filter { .. } => "filter".into(),
Comprehension::Order { strategy, .. } => format!("order_{strategy:?}"),
}
}
#[test]
fn optimizer_never_rejects_valid_input() {
for i in 0..500 {
let mut ast_gen = AstGen::new(0xAAAA_0000_u64.wrapping_add(i));
let ast = ast_gen.generate(3);
if validate(&ast, Mode::Permissive).is_err() {
continue;
}
let _ = optimize(ast);
}
}
#[test]
fn coordinate_set_preserved_through_pipeline() {
for i in 0..200 {
let mut ast_gen = AstGen::new(0xBBBB_0000_u64.wrapping_add(i));
let ast = ast_gen.generate(3);
if validate(&ast, Mode::Permissive).is_err() {
continue;
}
let original_coords = ast.coordinate_names();
let optimized = optimize(ast.clone());
let optimized_coords = optimized.coordinate_names();
let mut a = original_coords.clone();
a.sort();
let mut b = optimized_coords.clone();
b.sort();
assert_eq!(a, b, "coord set changed: {ast:?} → {optimized:?}");
}
}