use polydat::iteration::comprehension::ast::Comprehension;
use polydat::iteration::comprehension::ir::{check_bounds, compile, interpret};
use polydat::iteration::comprehension::metadata::IndexFn;
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};
fn clause(name: &str, vs: &[i64]) -> Comprehension {
Comprehension::clause(
name,
Source::Literal {
values: vs.iter().map(|n| LiteralValue::Int(*n)).collect(),
},
)
}
fn dispense(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
}
#[test]
fn spec_11_1_single_cartesian_basic_dispense() {
let ast = Comprehension::cartesian(vec![
clause("k", &[1, 2, 3]),
clause("profile", &[10, 20]),
]);
let m = ast.metadata();
assert!(matches!(
m.cardinality,
polydat::iteration::comprehension::cardinality::CardinalityClass::Bounded(6)
));
assert!(matches!(
m.index_addressable,
Some(IndexFn::Lattice { ref axis_sizes }) if axis_sizes == &vec![3u64, 2]
));
let tuples = dispense(&ast);
assert_eq!(tuples.len(), 6);
}
#[test]
fn spec_11_2_filter_then_extrema_truncated() {
let cart = Comprehension::cartesian(vec![clause("k", &[1, 5, 10]), clause("limit", &[10, 50, 100])]);
let filtered = Comprehension::filter(cart, "true");
let ast = Comprehension::order(filtered, StrategyName::Extrema, Some(1));
let tuples = dispense(&ast);
assert_eq!(tuples.len(), 4);
}
#[test]
fn spec_11_3_union_of_sub_spaces() {
let sub_a = Comprehension::cartesian(vec![clause("k", &[10]), clause("limit", &[10, 50, 100])]);
let sub_b = Comprehension::cartesian(vec![clause("k", &[100]), clause("limit", &[100, 200, 500])]);
let ast = Comprehension::union(vec![sub_a, sub_b]);
let tuples = dispense(&ast);
assert_eq!(tuples.len(), 6);
}
#[test]
fn spec_11_4_union_with_outer_lex_truncation() {
let sub_a = Comprehension::cartesian(vec![clause("k", &[10]), clause("limit", &[10, 20, 30, 40, 50])]);
let sub_b = Comprehension::cartesian(vec![clause("k", &[100]), clause("limit", &[100, 200, 300, 400, 500])]);
let outer = Comprehension::order(
Comprehension::union(vec![sub_a, sub_b]),
StrategyName::Lex,
Some(7),
);
let tuples = dispense(&outer);
assert_eq!(tuples.len(), 7);
}
#[test]
fn spec_11_5_halton_over_union() {
let sub_a = Comprehension::cartesian(vec![clause("k", &[10]), clause("limit", &[10, 20, 30])]);
let sub_b = Comprehension::cartesian(vec![clause("k", &[100]), clause("limit", &[100, 200, 300])]);
let ast = Comprehension::order(
Comprehension::union(vec![sub_a, sub_b]),
StrategyName::Halton,
Some(4),
);
let tuples = dispense(&ast);
assert_eq!(tuples.len(), 4);
}
#[test]
fn spec_11_6_form_a_order_then_filter() {
let cart = Comprehension::cartesian(vec![clause("k", &[1, 2, 3]), clause("limit", &[1, 2, 3])]);
let ordered = Comprehension::order(cart, StrategyName::Extrema, Some(4));
let form_a = Comprehension::filter(ordered, "true"); let tuples = dispense(&form_a);
assert_eq!(tuples.len(), 9);
}
#[test]
fn spec_11_6_form_b_filter_then_order() {
let cart = Comprehension::cartesian(vec![clause("k", &[1, 2, 3]), clause("limit", &[1, 2, 3])]);
let filtered = Comprehension::filter(cart, "true");
let form_b = Comprehension::order(filtered, StrategyName::Extrema, Some(4));
let tuples = dispense(&form_b);
assert_eq!(tuples.len(), 9);
}
#[test]
fn spec_11_7_bounded_zip() {
let ast = Comprehension::zip(
vec![clause("x", &[1, 2, 3]), clause("y", &[10, 20, 30])],
ZipMode::Strict,
);
let m = ast.metadata();
assert!(matches!(
m.cardinality,
polydat::iteration::comprehension::cardinality::CardinalityClass::Bounded(3)
));
let tuples = dispense(&ast);
assert_eq!(tuples.len(), 3);
assert_eq!(tuples[0].len(), 2);
}
#[test]
fn spec_11_8_cycle_zip_with_shorter_child() {
let ast = Comprehension::zip(
vec![
clause("k", &[1, 2, 3, 4, 5]),
clause("color", &[100, 200, 300]),
],
ZipMode::Cycle,
);
let tuples = dispense(&ast);
assert_eq!(tuples.len(), 5);
assert_eq!(tuples[0][1].1, TupleValue::I64(100));
assert_eq!(tuples[3][1].1, TupleValue::I64(100)); }
#[test]
fn spec_11_9_derived_streamers_independent() {
use polydat::iteration::comprehension::surfaces::compile as surfaces_compile;
let base = Comprehension::cartesian(vec![clause("k", &[1, 2, 3]), clause("limit", &[10, 20])]);
let compiled = surfaces_compile(&base);
let s1 = compiled.coordinate_stream();
let s2 = compiled.coordinate_stream();
let tuples1: Vec<_> = s1.collect();
let tuples2: Vec<_> = s2.collect();
assert_eq!(tuples1, tuples2);
assert_eq!(tuples1.len(), 6);
}
#[test]
fn spec_11_10_continuous_sampling_via_halton() {
use polydat::iteration::comprehension::cardinality::{Interval, ProductMeasure};
let alpha = Comprehension::clause(
"alpha",
Source::ContinuousInterval {
interval: Interval::closed(0.0, 1.0),
measure: ProductMeasure::Uniform,
},
);
let beta = Comprehension::clause(
"beta",
Source::ContinuousInterval {
interval: Interval::closed(0.0, 1.0),
measure: ProductMeasure::Uniform,
},
);
let cart = Comprehension::cartesian(vec![alpha, beta]);
let ast = Comprehension::order(cart, StrategyName::Halton, Some(50));
let m = ast.metadata();
assert!(matches!(
m.cardinality,
polydat::iteration::comprehension::cardinality::CardinalityClass::Bounded(50)
));
}
#[test]
fn spec_11_11_sample_then_zip_continuous_pairing() {
use polydat::iteration::comprehension::cardinality::{Interval, ProductMeasure};
let alpha = Comprehension::clause(
"alpha",
Source::ContinuousInterval {
interval: Interval::closed(0.0, 1.0),
measure: ProductMeasure::Uniform,
},
);
let beta = Comprehension::clause(
"beta",
Source::ContinuousInterval {
interval: Interval::closed(0.0, 1.0),
measure: ProductMeasure::Uniform,
},
);
let alpha_samples = Comprehension::order(alpha, StrategyName::Halton, Some(20));
let beta_samples = Comprehension::order(beta, StrategyName::Halton, Some(20));
let paired = Comprehension::zip(vec![alpha_samples, beta_samples], ZipMode::Strict);
let m = paired.metadata();
assert!(matches!(
m.cardinality,
polydat::iteration::comprehension::cardinality::CardinalityClass::Bounded(20)
));
}
#[test]
fn spec_11_12_dependent_source_loses_addressability() {
let k_clause = clause("k", &[1, 2, 3]);
let replicas_clause = Comprehension::clause(
"replicas",
Source::Generator {
expr: "range(0, 2 * {k})".into(),
cardinality_hint: Some(6),
},
);
let ast = Comprehension::cartesian(vec![k_clause, replicas_clause]);
let m = ast.metadata();
assert!(m.index_addressable.is_none());
}
#[test]
fn spec_11_13_three_surfaces_from_one_comprehension() {
use polydat::iteration::comprehension::strategies::Tuple;
use polydat::iteration::comprehension::surfaces::{compile as surfaces_compile, KernelScope};
#[derive(Clone)]
struct K(&'static str);
impl KernelScope for K {
type Scoped = (&'static str, Tuple);
fn scope(&self, coords: &Tuple) -> Self::Scoped {
(self.0, coords.clone())
}
}
let ast = Comprehension::cartesian(vec![clause("k", &[1, 2]), clause("profile", &[10, 20])]);
let compiled = surfaces_compile(&ast);
let coord_tuples: Vec<_> = compiled.coordinate_stream().collect();
let kernel_instances: Vec<_> = compiled.scoped_kernel_stream(K("parent")).collect();
let one_shot = compiled.scope_once(
&K("parent"),
&Tuple::new()
.with("k", TupleValue::I64(7))
.with("profile", TupleValue::I64(99)),
);
assert_eq!(coord_tuples.len(), 4);
assert_eq!(kernel_instances.len(), 4);
assert_eq!(one_shot.scoped.0, "parent");
}
#[test]
fn dispense_bound_matches_simple_cartesian() {
let ast = Comprehension::cartesian(vec![clause("a", &[1, 2, 3]), clause("b", &[10, 20])]);
let prog = compile(&optimize(ast));
let bound = check_bounds(&prog);
assert!(bound.barriers.is_empty(), "fully-streaming cartesian has no barriers");
assert!(bound.stack_depth >= 2);
}
#[test]
fn dispense_bound_matches_materialize() {
let ast = Comprehension::order(
Comprehension::cartesian(vec![clause("a", &[1, 2, 3]), clause("b", &[10, 20])]),
StrategyName::Halton,
Some(3),
);
let prog = compile(&optimize(ast));
let bound = check_bounds(&prog);
assert_eq!(bound.barriers.len(), 1);
assert_eq!(bound.barriers[0].working_set_size, Some(3));
}