polydat_core/library/
fixed.rs1#[crate::polydat_node(category = Math)]
14fn const_f64(#[poly_default(0.0f64)] value: crate::derive_support::Const<f64>) -> f64 {
15 *value
16}
17
18#[crate::polydat_node(category = Math)]
20fn const_bool(#[poly_default(false)] value: crate::derive_support::Const<bool>) -> bool {
21 *value
22}
23
24#[crate::polydat_node(category = Math)]
40fn fixed_values_u64(input: u64, values: crate::derive_support::Const<Vec<u64>>) -> u64 {
41 assert!(
42 !values.is_empty(),
43 "fixed_values_u64: value list must not be empty"
44 );
45 let idx = (input as usize) % values.len();
46 values[idx]
47}
48
49#[crate::polydat_node(category = Math)]
51fn fixed_values_f64(input: u64, values: crate::derive_support::Const<Vec<f64>>) -> f64 {
52 assert!(
53 !values.is_empty(),
54 "fixed_values_f64: value list must not be empty"
55 );
56 let idx = (input as usize) % values.len();
57 values[idx]
58}
59
60#[crate::polydat_node(category = Math)]
62fn fixed_values_str(input: u64, values: crate::derive_support::Const<Vec<String>>) -> String {
63 assert!(
64 !values.is_empty(),
65 "fixed_values_str: value list must not be empty"
66 );
67 let idx = (input as usize) % values.len();
68 values[idx].clone()
69}
70
71pub(crate) fn compute_threshold(probability: f64) -> u64 {
82 (probability.clamp(0.0, 1.0) * u64::MAX as f64) as u64
83}
84
85fn coin_flip_jit_constants(node: &CoinFlip) -> Vec<u64> {
88 vec![node.threshold]
89}
90
91#[crate::polydat_node(category = Probability, jit_constants = coin_flip_jit_constants)]
94fn coin_flip(
95 input: u64,
96 #[poly_default(0.5f64)] probability: crate::derive_support::Const<f64>,
97 #[poly_const(compute_threshold, from = probability)] threshold: &u64,
98) -> bool {
99 input < *threshold
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::ast::{PolydatNode, Value};
106
107 #[test]
108 fn const_f64() {
109 let node = ConstF64::new(3.14);
110 let mut out = [Value::None];
111 node.eval(&[], &mut out);
112 assert_eq!(out[0].as_f64(), 3.14);
113 }
114
115 #[test]
116 fn const_bool() {
117 let node = ConstBool::new(true);
118 let mut out = [Value::None];
119 node.eval(&[], &mut out);
120 assert!(out[0].as_bool());
121 }
122
123 #[test]
124 fn fixed_values_u64_cycles() {
125 let node = FixedValuesU64::new(vec![10, 20, 30]);
126 let mut out = [Value::None];
127 node.eval(&[Value::U64(0)], &mut out);
128 assert_eq!(out[0].as_u64(), 10);
129 node.eval(&[Value::U64(1)], &mut out);
130 assert_eq!(out[0].as_u64(), 20);
131 node.eval(&[Value::U64(2)], &mut out);
132 assert_eq!(out[0].as_u64(), 30);
133 node.eval(&[Value::U64(3)], &mut out);
134 assert_eq!(out[0].as_u64(), 10); }
136
137 #[test]
145 fn fixed_values_f64() {
146 let node = FixedValuesF64::new(vec![1.1, 2.2, 3.3]);
147 let mut out = [Value::None];
148 node.eval(&[Value::U64(1)], &mut out);
149 assert_eq!(out[0].as_f64(), 2.2);
150 }
151
152 #[test]
153 fn fixed_values_str() {
154 let node = FixedValuesStr::new(vec!["alpha".into(), "beta".into(), "gamma".into()]);
155 let mut out = [Value::None];
156 node.eval(&[Value::U64(2)], &mut out);
157 assert_eq!(out[0].as_str(), "gamma");
158 }
159
160 #[test]
161 fn coin_flip_always_true() {
162 let node = CoinFlip::new(1.0);
163 let mut out = [Value::None];
164 for i in 0..100 {
165 node.eval(&[Value::U64(i)], &mut out);
166 assert!(out[0].as_bool());
167 }
168 }
169
170 #[test]
171 fn coin_flip_always_false() {
172 let node = CoinFlip::new(0.0);
173 let mut out = [Value::None];
174 for i in 0..100 {
175 node.eval(&[Value::U64(i)], &mut out);
176 assert!(!out[0].as_bool());
177 }
178 }
179
180 #[test]
181 fn coin_flip_roughly_half() {
182 use xxhash_rust::xxh3::xxh3_64;
183 let node = CoinFlip::new(0.5);
184 let mut true_count = 0;
185 let n = 10_000u64;
186 let mut out = [Value::None];
187 for i in 0..n {
188 let hashed = xxh3_64(&i.to_le_bytes());
189 node.eval(&[Value::U64(hashed)], &mut out);
190 if out[0].as_bool() {
191 true_count += 1;
192 }
193 }
194 let ratio = true_count as f64 / n as f64;
195 assert!((ratio - 0.5).abs() < 0.05, "expected ~50%, got {ratio}");
196 }
197}