use polydat::dsl::compile::{compile_gk, compile_gk_strict};
use polydat::dsl::ast::BindingModifier;
use polydat::kernel::Construction;
use polydat::subcontext::GkMatter;
#[test]
fn empty_source() {
let result = compile_gk("");
assert!(result.is_ok(), "empty source should compile as zero-binding program");
}
#[test]
fn whitespace_only() {
let result = compile_gk(" \n\n \t \n ");
assert!(result.is_ok(), "whitespace-only source should compile as zero-binding program");
}
#[test]
fn comments_only() {
let result = compile_gk("// just a comment\n# another comment\n/* block */");
assert!(result.is_ok(), "comments-only source should compile as zero-binding program");
}
#[test]
fn unterminated_string() {
let result = compile_gk(r#"
input cycle: u64
s := "unterminated
"#);
assert!(result.is_err(), "unterminated string should fail");
}
#[test]
fn unterminated_block_comment() {
let result = compile_gk(r#"
input cycle: u64
/* never closed
h := hash(cycle)
"#);
let _ = result;
}
#[test]
fn unexpected_token_at_toplevel() {
let result = compile_gk("+ - * /");
assert!(result.is_err());
}
#[test]
fn duplicate_inputs_declaration() {
let result = compile_gk(r#"
input cycle: u64
input cycle: u64
h := hash(cycle)
"#);
assert!(result.is_ok(), "duplicate inputs should be accepted: {:?}", result.err());
}
#[test]
fn zero_inputs_explicit() {
let result = compile_gk(r#"
val := 42
"#);
assert!(result.is_ok(), "explicit empty inputs should work: {:?}", result.err());
}
#[test]
fn very_long_identifier() {
let long_name: String = "a".repeat(1000);
let src = format!("input cycle: u64\n{long_name} := hash(cycle)");
let result = compile_gk(&src);
assert!(result.is_ok(), "long identifier should work: {:?}", result.err());
}
#[test]
fn unicode_in_string_literal() {
let result = compile_gk(r#"
input cycle: u64
emoji := "hello 🌍 world"
"#);
assert!(result.is_ok(), "unicode in strings should work: {:?}", result.err());
}
#[test]
fn deeply_nested_function_calls() {
let src = r#"
input cycle: u64
deep := hash(hash(hash(hash(hash(cycle)))))
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[42]);
let _ = kernel.pull("deep").as_u64();
}
#[test]
fn deeply_nested_arithmetic() {
let src = r#"
input cycle: u64
v := ((((cycle + 1) * 2) + 3) * 4) + 5
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[10]);
let result = kernel.pull("v").as_u64();
assert_eq!(result, 105);
}
#[test]
fn hex_literals() {
let src = r#"
input cycle: u64
v := mod(hash(cycle), 0xFF)
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[1]);
assert!(kernel.pull("v").as_u64() < 255);
}
#[test]
fn negative_float_literal() {
let src = r#"
input cycle: u64
v := -3.14
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[0]);
let val = kernel.pull("v").as_f64();
assert!((val - (-3.14)).abs() < 0.001);
}
#[test]
fn unknown_function_name() {
let result = compile_gk(r#"
input cycle: u64
v := nonexistent_function(cycle)
"#);
assert!(result.is_err(), "unknown function should fail");
let err = result.unwrap_err();
assert!(err.contains("nonexistent_function"), "error should name the function: {err}");
}
#[test]
fn unknown_wire_reference() {
let result = compile_gk(r#"
input cycle: u64
v := hash(undefined_wire)
"#);
assert!(result.is_err(), "unknown wire should fail");
}
#[test]
fn wrong_arity_too_many() {
let result = compile_gk(r#"
input cycle: u64
v := hash(cycle, cycle, cycle)
"#);
assert!(result.is_err(), "too many args should fail");
}
#[test]
fn self_referential_binding() {
let result = compile_gk(r#"
input cycle: u64
x := hash(x)
"#);
assert!(result.is_err(), "self-referential binding should fail");
}
#[test]
fn strict_requires_explicit_inputs() {
let result = compile_gk_strict("h := hash(cycle)", None, true);
assert!(result.is_err(), "strict mode should require explicit inputs");
}
#[test]
fn strict_accepts_explicit_inputs() {
let result = compile_gk_strict(
"input cycle: u64\nh := hash(cycle)",
None,
true,
);
assert!(result.is_ok(), "strict with explicit inputs should work: {:?}", result.err());
}
#[test]
fn shared_on_cycle_binding() {
let src = r#"
input cycle: u64
shared counter := 0
"#;
let kernel = compile_gk(src).unwrap();
assert_eq!(kernel.program().output_modifier("counter"), BindingModifier::SHARED);
}
#[test]
fn shared_non_literal_rejected() {
let src = r#"
input cycle: u64
shared counter := hash(cycle)
"#;
let err = compile_gk(src).expect_err("non-literal shared const must error");
assert!(err.contains("shared binding 'counter'"), "error: {err}");
assert!(err.contains("literal"), "error: {err}");
}
#[test]
fn const_on_cycle_expr_rejected() {
let src = r#"
input cycle: u64
const max := mod(hash(cycle), 100)
"#;
let err = compile_gk(src).expect_err("const wired to cycle must be rejected");
assert!(err.contains("init contract") || err.contains("const"),
"diagnostic should call out the const contract: {err}");
}
#[test]
fn shared_literal_cell() {
let src = r#"
input cycle: u64
shared budget := 500
"#;
let kernel = compile_gk(src).unwrap();
assert_eq!(kernel.program().output_modifier("budget"), BindingModifier::SHARED);
assert_eq!(kernel.lookup("budget").unwrap().as_u64(), 500);
}
#[test]
fn const_literal_fold() {
let src = r#"
input cycle: u64
const dim := 128
"#;
let kernel = compile_gk(src).unwrap();
assert_eq!(kernel.program().output_modifier("dim"), BindingModifier::CONST);
assert_eq!(kernel.get_constant("dim").unwrap().as_u64(), 128);
}
#[test]
fn mixed_modifiers() {
let src = r#"
input cycle: u64
shared s := 0
const f := 42
plain := mod(hash(cycle), 100)
"#;
let kernel = compile_gk(src).unwrap();
assert_eq!(kernel.program().output_modifier("s"), BindingModifier::SHARED);
assert_eq!(kernel.program().output_modifier("f"), BindingModifier::CONST);
assert_eq!(kernel.program().output_modifier("plain"), BindingModifier::NONE);
}
#[test]
fn shared_outputs_list() {
let src = r#"
input cycle: u64
shared a := 0
shared b := 0
c := hash(cycle)
"#;
let kernel = compile_gk(src).unwrap();
let mut shared = kernel.program().shared_outputs();
shared.sort();
assert_eq!(shared, vec!["a", "b"]);
}
#[test]
fn final_outputs_list() {
let src = r#"
input cycle: u64
const x := 1
const y := 2
z := hash(cycle)
"#;
let kernel = compile_gk(src).unwrap();
let mut finals = kernel.program().const_outputs();
finals.sort();
assert_eq!(finals, vec!["x", "y"]);
}
#[test]
fn no_modifiers_returns_empty_lists() {
let src = r#"
input cycle: u64
h := hash(cycle)
"#;
let kernel = compile_gk(src).unwrap();
assert!(kernel.program().shared_outputs().is_empty());
assert!(kernel.program().const_outputs().is_empty());
}
#[test]
fn extern_u64_input_declared() {
let src = r#"
input cycle: u64
extern offset: u64
"#;
let kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("offset");
assert!(idx.is_some(), "extern should create a named input");
}
#[test]
fn extern_usable_as_wire_argument() {
let src = r#"
input cycle: u64
extern offset: u64
result := hash(offset)
"#;
let mut kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("offset").unwrap();
kernel.state().set_input(idx, polydat::node::Value::U64(100));
kernel.set_inputs(&[42]);
let v1 = kernel.pull("result").as_u64();
kernel.state().set_input(idx, polydat::node::Value::U64(200));
kernel.set_inputs(&[42]);
let v2 = kernel.pull("result").as_u64();
assert_ne!(v1, v2, "different extern values should hash differently");
}
#[test]
fn extern_in_arithmetic_expression() {
let src = r#"
input cycle: u64
extern scale: u64
result := cycle * scale
"#;
let mut kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("scale").unwrap();
kernel.state().set_input(idx, polydat::node::Value::U64(10));
kernel.set_inputs(&[5]);
let v = kernel.pull("result").as_u64();
assert_eq!(v, 50);
}
#[test]
fn extern_input_usable_as_output() {
let src = r#"
input cycle: u64
extern dim: u64
"#;
let mut kernel = compile_gk(src).unwrap();
let idx = kernel.program().find_input("dim").unwrap();
kernel.state().set_input(idx, polydat::node::Value::U64(128));
kernel.set_inputs(&[0]);
let val = kernel.pull("dim").as_u64();
assert_eq!(val, 128);
}
#[test]
fn extern_f64_input() {
let src = r#"
input cycle: u64
extern threshold: f64
"#;
let kernel = compile_gk(src).unwrap();
assert!(kernel.program().find_input("threshold").is_some());
}
#[test]
fn extern_string_input() {
let src = r#"
input cycle: u64
extern label: String
"#;
let kernel = compile_gk(src).unwrap();
assert!(kernel.program().find_input("label").is_some());
}
#[test]
fn materialize_wiring_from_outer_copies_constants() {
let outer_src = r#"
input cycle: u64
dim := 128
"#;
let outer = compile_gk(outer_src).unwrap();
assert_eq!(outer.get_constant("dim").unwrap().as_u64(), 128);
let inner_src = r#"
input cycle: u64
extern dim: u64
"#;
let inner_program = compile_gk(inner_src).unwrap().program().clone();
let mut inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
let idx = inner.program().find_input("dim").unwrap();
let val = inner.state().get_input(idx);
assert_eq!(val.as_u64(), 128);
inner.set_inputs(&[0]);
let pulled = inner.pull("dim").as_u64();
assert_eq!(pulled, 128);
}
#[test]
fn materialize_wiring_from_outer_ignores_nonmatching() {
let outer_src = r#"
input cycle: u64
dim := 128
"#;
let outer = compile_gk(outer_src).unwrap();
let inner_src = r#"
input cycle: u64
h := hash(cycle)
"#;
let inner_program = compile_gk(inner_src).unwrap().program().clone();
let _inner = outer.subscope(GkMatter::builder().program(inner_program).build().unwrap()).unwrap();
}
#[test]
fn deterministic_across_1000_cycles() {
let src = r#"
input cycle: u64
h := hash(cycle)
v := mod(h, 1000000)
"#;
let mut kernel = compile_gk(src).unwrap();
let mut first_pass = Vec::new();
for i in 0..1000 {
kernel.set_inputs(&[i]);
first_pass.push(kernel.pull("v").as_u64());
}
for i in 0..1000 {
kernel.set_inputs(&[i]);
let v = kernel.pull("v").as_u64();
assert_eq!(v, first_pass[i as usize],
"non-deterministic at cycle {i}: {v} != {}", first_pass[i as usize]);
}
}
#[test]
fn deterministic_with_multiple_outputs() {
let src = r#"
input cycle: u64
a := hash(cycle)
b := mod(a, 100)
c := mod(a, 1000)
"#;
let mut kernel = compile_gk(src).unwrap();
for i in 0..100 {
kernel.set_inputs(&[i]);
let a1 = kernel.pull("a").as_u64();
let b1 = kernel.pull("b").as_u64();
let c1 = kernel.pull("c").as_u64();
kernel.set_inputs(&[i]);
let a2 = kernel.pull("a").as_u64();
let b2 = kernel.pull("b").as_u64();
let c2 = kernel.pull("c").as_u64();
assert_eq!(a1, a2, "non-deterministic 'a' at cycle {i}");
assert_eq!(b1, b2, "non-deterministic 'b' at cycle {i}");
assert_eq!(c1, c2, "non-deterministic 'c' at cycle {i}");
}
}
#[test]
fn max_u64_input() {
let src = r#"
input cycle: u64
h := hash(cycle)
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[u64::MAX]);
let _ = kernel.pull("h").as_u64();
}
#[test]
fn zero_input() {
let src = r#"
input cycle: u64
h := hash(cycle)
v := mod(h, 100)
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[0]);
assert!(kernel.pull("v").as_u64() < 100);
}
#[test]
fn mod_by_one() {
let src = r#"
input cycle: u64
v := mod(hash(cycle), 1)
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[42]);
assert_eq!(kernel.pull("v").as_u64(), 0);
}
#[test]
fn constant_only_program() {
let src = r#"
x := 42
y := 3.14
s := "hello"
"#;
let kernel = compile_gk(src).unwrap();
assert_eq!(kernel.get_constant("x").unwrap().as_u64(), 42);
assert!((kernel.get_constant("y").unwrap().as_f64() - 3.14).abs() < 0.001);
assert_eq!(kernel.get_constant("s").unwrap().as_str(), "hello");
}
#[test]
fn compiled_kernel_has_source_and_context() {
let src = r#"
input cycle: u64
h := hash(cycle)
"#;
let kernel = compile_gk(src).unwrap();
assert!(kernel.program().source().contains("hash(cycle)"),
"source should contain original text");
}
#[test]
fn fuzz_random_hash_chains() {
let functions = ["hash", "hash"];
for seed in 0..200u64 {
let depth = (seed % 5) + 1;
let mut expr = "cycle".to_string();
for _ in 0..depth {
let func = functions[(seed as usize) % functions.len()];
expr = format!("{func}({expr})");
}
let src = format!("input cycle: u64\nresult := mod({expr}, 1000)");
let mut kernel = compile_gk(&src).unwrap_or_else(|e| {
panic!("seed {seed}: compile failed: {e}\nsource:\n{src}")
});
kernel.set_inputs(&[seed]);
let v = kernel.pull("result").as_u64();
assert!(v < 1000, "seed {seed}: expected < 1000, got {v}");
}
}
#[test]
fn fuzz_arithmetic_expressions() {
let ops = ["+", "-", "*"];
for (_i, op) in ops.iter().enumerate() {
for a in [0u64, 1, 42, 100, u64::MAX / 2] {
let src = format!(
"input cycle: u64\nresult := cycle {op} {a}"
);
let result = compile_gk(&src);
assert!(result.is_ok(),
"op={op} a={a}: compile failed: {:?}", result.err());
let mut kernel = result.unwrap();
kernel.set_inputs(&[10]);
let _ = kernel.pull("result");
}
}
}
#[test]
fn fuzz_many_bindings() {
for n in 1..=20 {
let mut src = "input cycle: u64\n".to_string();
let mut prev = "cycle".to_string();
for i in 0..n {
let name = format!("v{i}");
src.push_str(&format!("{name} := hash({prev})\n"));
prev = name;
}
let mut kernel = compile_gk(&src).unwrap_or_else(|e| {
panic!("n={n}: compile failed: {e}\nsource:\n{src}")
});
kernel.set_inputs(&[42]);
let _ = kernel.pull(&prev);
}
}
#[test]
fn fuzz_multi_output_destructuring() {
for n in [2, 3, 5, 10] {
let names: Vec<String> = (0..n).map(|i| format!("d{i}")).collect();
let targets = names.join(", ");
let dims: Vec<String> = (0..n).map(|i| format!("{}", 10 + i)).collect();
let dim_args = dims.join(", ");
let src = format!(
"input cycle: u64\n({targets}) := mixed_radix(cycle, {dim_args})"
);
let mut kernel = compile_gk(&src).unwrap_or_else(|e| {
panic!("n={n}: compile failed: {e}\nsource:\n{src}")
});
kernel.set_inputs(&[12345]);
for name in &names {
let _ = kernel.pull(name);
}
}
}
#[test]
fn fuzz_wide_graph() {
let mut src = "input cycle: u64\n".to_string();
for i in 0..100 {
src.push_str(&format!("out_{i} := mod(hash(cycle + {i}), 1000)\n"));
}
let mut kernel = compile_gk(&src).unwrap();
kernel.set_inputs(&[42]);
for i in 0..100 {
let v = kernel.pull(&format!("out_{i}")).as_u64();
assert!(v < 1000, "out_{i} = {v}");
}
}
#[test]
fn error_includes_line_info() {
let result = compile_gk(r#"
input cycle: u64
h := hash(cycle)
bad := completely_bogus_function(h)
"#);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("completely_bogus_function") || err.contains("unknown"),
"error should be informative: {err}");
}
#[test]
fn eval_const_integer_arithmetic() {
let v = polydat::dsl::compile::eval_const_expr("4 * 4").unwrap();
assert_eq!(v.as_u64(), 16);
}
#[test]
fn eval_const_float_arithmetic() {
let v = polydat::dsl::compile::eval_const_expr("3.0 + 0.14").unwrap();
assert!((v.as_f64() - 3.14).abs() < 0.001);
}
#[test]
fn eval_const_rejects_cycle_dependent() {
let result = polydat::dsl::compile::eval_const_expr("hash(cycle)");
assert!(result.is_err(), "cycle-dependent expr should not be const");
}
#[test]
fn eval_const_nested() {
let v = polydat::dsl::compile::eval_const_expr("(2 + 3) * (4 + 1)").unwrap();
assert_eq!(v.as_u64(), 25);
}
#[test]
fn string_eq_compiles_and_evaluates() {
let src = r#"
input cycle: u64
s := "LATENCY"
eq := s == "LATENCY"
ne := s == "RECALL"
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[0]);
assert_eq!(kernel.pull("eq").as_u64(), 1);
assert_eq!(kernel.pull("ne").as_u64(), 0);
}
#[test]
fn string_ne_compiles_and_evaluates() {
let src = r#"
input cycle: u64
s := "RECALL"
ne := s != "LATENCY"
eq := s != "RECALL"
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[0]);
assert_eq!(kernel.pull("ne").as_u64(), 1);
assert_eq!(kernel.pull("eq").as_u64(), 0);
}
#[test]
fn if_with_string_branches_picks_str() {
let src = r#"
input cycle: u64
s := "LATENCY"
out := if(s == "LATENCY", "fast", "thorough")
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[0]);
assert_eq!(kernel.pull("out").as_str(), "fast");
}
#[test]
fn if_string_cond_picks_f64_branch() {
let src = r#"
input cycle: u64
optimize_for := "LATENCY"
latency_factor := 1.5
recall_factor := 9.5
overscan := if(optimize_for == "LATENCY", latency_factor, recall_factor)
"#;
let mut kernel = compile_gk(src).unwrap();
kernel.set_inputs(&[0]);
assert!((kernel.pull("overscan").as_f64() - 1.5).abs() < 1e-9);
}
#[test]
fn string_ordered_comparison_errors() {
let result = compile_gk(r#"
input cycle: u64
s := "LATENCY"
bad := s < "RECALL"
"#);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
err.contains("String") || err.contains("not supported"),
"expected clear String-ordered-comparison error, got: {err}",
);
}
#[test]
fn type_mismatch_error_carries_binding_name() {
let result = compile_gk(r#"
input cycle: u64
s := "x"
my_named_binding := s < "y"
"#);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("String") || err.contains("not supported"),
"got: {err}");
}
#[test]
fn type_mismatch_error_names_user_binding_via_prefix() {
let result = compile_gk(r#"
input cycle: u64
s := "LATENCY"
overscan := s << 2
"#);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
err.contains("overscan"),
"expected error to mention the LHS binding name 'overscan', got:\n{err}",
);
}