use polydat::assembly::{GkAssembler, WireRef};
use polydat::nodes::arithmetic::{AddU64, DivU64, Interleave, MixedRadix, ModU64};
use polydat::nodes::convert::U64ToString;
use polydat::nodes::hash::{Hash64, HashRange};
#[test]
fn simple_hash_mod_chain() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node("h", Box::new(Hash64::new()), vec![WireRef::input("cycle")]);
asm.add_node("m", Box::new(ModU64::new(1000)), vec![WireRef::node("h")]);
asm.add_output("result", WireRef::node("m"));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[42]);
let v = kernel.pull("result").as_u64();
assert!(v < 1000, "expected < 1000, got {v}");
kernel.set_inputs(&[42]);
assert_eq!(kernel.pull("result").as_u64(), v);
kernel.set_inputs(&[43]);
let v2 = kernel.pull("result").as_u64();
assert!(v2 < 1000);
}
#[test]
fn mixed_radix_decomposition() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node(
"decompose",
Box::new(MixedRadix::new(vec![100, 1000, 0])),
vec![WireRef::input("cycle")],
);
asm.add_output("tenant", WireRef::node_port("decompose", 0));
asm.add_output("device", WireRef::node_port("decompose", 1));
asm.add_output("reading", WireRef::node_port("decompose", 2));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[4_201_337]);
assert_eq!(kernel.pull("tenant").as_u64(), 37);
assert_eq!(kernel.pull("device").as_u64(), 13);
assert_eq!(kernel.pull("reading").as_u64(), 42);
}
#[test]
fn shared_intermediate() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node(
"decompose",
Box::new(MixedRadix::new(vec![100, 1000, 0])),
vec![WireRef::input("cycle")],
);
asm.add_node(
"tenant_h",
Box::new(Hash64::new()),
vec![WireRef::node_port("decompose", 0)],
);
asm.add_node(
"tenant_code",
Box::new(ModU64::new(10000)),
vec![WireRef::node("tenant_h")],
);
asm.add_node(
"tenant_bucket",
Box::new(ModU64::new(10)),
vec![WireRef::node("tenant_h")],
);
asm.add_output("code", WireRef::node("tenant_code"));
asm.add_output("bucket", WireRef::node("tenant_bucket"));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[42]);
let code = kernel.pull("code").as_u64();
let bucket = kernel.pull("bucket").as_u64();
assert!(code < 10000);
assert!(bucket < 10);
assert_eq!(bucket, code % 10);
}
#[test]
fn auto_edge_adapter_u64_to_string() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node(
"val",
Box::new(ModU64::new(100)),
vec![WireRef::input("cycle")],
);
asm.add_node(
"str_val",
Box::new(U64ToString::new()),
vec![WireRef::node("val")],
);
asm.add_output("result", WireRef::node("str_val"));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[542]);
let result = kernel.pull("result");
assert_eq!(result.as_str(), "42"); }
#[test]
fn two_input_interleave() {
let mut asm = GkAssembler::new(vec!["a".into(), "b".into()]);
asm.add_node(
"mixed",
Box::new(Interleave::new()),
vec![WireRef::input("a"), WireRef::input("b")],
);
asm.add_node(
"hashed",
Box::new(Hash64::new()),
vec![WireRef::node("mixed")],
);
asm.add_node(
"bounded",
Box::new(ModU64::new(1000)),
vec![WireRef::node("hashed")],
);
asm.add_output("result", WireRef::node("bounded"));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[5, 10]);
let v1 = kernel.pull("result").as_u64();
assert!(v1 < 1000);
kernel.set_inputs(&[10, 5]);
let v2 = kernel.pull("result").as_u64();
assert!(v2 < 1000);
assert_ne!(v1, v2, "interleave(5,10) should differ from interleave(10,5)");
}
#[test]
fn memoization_within_context() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node("h", Box::new(Hash64::new()), vec![WireRef::input("cycle")]);
asm.add_node("m", Box::new(ModU64::new(1000)), vec![WireRef::node("h")]);
asm.add_output("result", WireRef::node("m"));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[99]);
let v1 = kernel.pull("result").as_u64();
let v2 = kernel.pull("result").as_u64();
assert_eq!(v1, v2, "same context, same output");
}
#[test]
fn context_change_invalidates() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node("h", Box::new(Hash64::new()), vec![WireRef::input("cycle")]);
asm.add_output("result", WireRef::node("h"));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[1]);
let v1 = kernel.pull("result").as_u64();
kernel.set_inputs(&[2]);
let v2 = kernel.pull("result").as_u64();
assert_ne!(v1, v2, "different coordinates must produce different hashes");
}
#[test]
fn error_unknown_wire() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node(
"h",
Box::new(Hash64::new()),
vec![WireRef::input("nonexistent")],
);
asm.add_output("result", WireRef::node("h"));
let result = asm.compile();
assert!(result.is_err());
}
#[test]
fn error_arity_mismatch() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node(
"h",
Box::new(Hash64::new()),
vec![WireRef::input("cycle"), WireRef::input("cycle")],
);
asm.add_output("result", WireRef::node("h"));
let result = asm.compile();
assert!(result.is_err());
}
#[test]
fn timeseries_workload_sketch() {
let mut asm = GkAssembler::new(vec!["cycle".into()]);
asm.add_node(
"decompose",
Box::new(MixedRadix::new(vec![100, 1000, 0])),
vec![WireRef::input("cycle")],
);
asm.add_node(
"tenant_h",
Box::new(Hash64::new()),
vec![WireRef::node_port("decompose", 0)],
);
asm.add_node(
"tenant_code",
Box::new(ModU64::new(10000)),
vec![WireRef::node("tenant_h")],
);
asm.add_node(
"td_interleave",
Box::new(Interleave::new()),
vec![
WireRef::node_port("decompose", 0),
WireRef::node_port("decompose", 1),
],
);
asm.add_node(
"device_h",
Box::new(Hash64::new()),
vec![WireRef::node("td_interleave")],
);
asm.add_node(
"device_seq",
Box::new(ModU64::new(100000)),
vec![WireRef::node("device_h")],
);
asm.add_node(
"time_bucket",
Box::new(DivU64::new(1000)),
vec![WireRef::node_port("decompose", 2)],
);
asm.add_node(
"timestamp",
Box::new(AddU64::new(1_710_000_000_000)),
vec![WireRef::node_port("decompose", 2)],
);
asm.add_node(
"dr_interleave",
Box::new(Interleave::new()),
vec![
WireRef::node("device_h"),
WireRef::node_port("decompose", 2),
],
);
asm.add_node(
"reading_h",
Box::new(HashRange::new(1_000_000)),
vec![WireRef::node("dr_interleave")],
);
asm.add_output("tenant_code", WireRef::node("tenant_code"));
asm.add_output("device_seq", WireRef::node("device_seq"));
asm.add_output("time_bucket", WireRef::node("time_bucket"));
asm.add_output("timestamp", WireRef::node("timestamp"));
asm.add_output("reading_h", WireRef::node("reading_h"));
let mut kernel = asm.compile().unwrap();
kernel.set_inputs(&[4_201_337]);
let tenant_code = kernel.pull("tenant_code").as_u64();
let device_seq = kernel.pull("device_seq").as_u64();
let time_bucket = kernel.pull("time_bucket").as_u64();
let timestamp = kernel.pull("timestamp").as_u64();
let reading_h = kernel.pull("reading_h").as_u64();
assert!(tenant_code < 10000, "tenant_code={tenant_code}");
assert!(device_seq < 100000, "device_seq={device_seq}");
assert_eq!(time_bucket, 0); assert_eq!(timestamp, 1_710_000_000_042); assert!(reading_h < 1_000_000, "reading_h={reading_h}");
kernel.set_inputs(&[4_201_337]);
assert_eq!(kernel.pull("tenant_code").as_u64(), tenant_code);
assert_eq!(kernel.pull("device_seq").as_u64(), device_seq);
assert_eq!(kernel.pull("reading_h").as_u64(), reading_h);
kernel.set_inputs(&[4_201_338]);
let tc2 = kernel.pull("tenant_code").as_u64();
let ds2 = kernel.pull("device_seq").as_u64();
assert_ne!(tenant_code, tc2);
assert_ne!(device_seq, ds2);
}