use std::sync::Arc;
#[crate::polydat_node(category = Conversions)]
fn __u64_to_u128(n: u64) -> u128 { n as u128 }
#[crate::polydat_node(category = Conversions)]
fn __u64_to_i128(n: u64) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __i64_to_i128(n: i64) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __u128_to_f64(n: u128) -> f64 { n as f64 }
#[crate::polydat_node(category = Conversions)]
fn __i128_to_f64(n: i128) -> f64 { n as f64 }
#[crate::polydat_node(category = Conversions)]
fn __u8_to_u128(n: u8) -> u128 { n as u128 }
#[crate::polydat_node(category = Conversions)]
fn __u8_to_i128(n: u8) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __u16_to_u128(n: u16) -> u128 { n as u128 }
#[crate::polydat_node(category = Conversions)]
fn __u16_to_i128(n: u16) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __u32_to_u128(n: u32) -> u128 { n as u128 }
#[crate::polydat_node(category = Conversions)]
fn __u32_to_i128(n: u32) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __i8_to_i128(n: i8) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __i16_to_i128(n: i16) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __i32_to_i128(n: i32) -> i128 { n as i128 }
#[crate::polydat_node(category = Conversions)]
fn __bool_to_u128(b: bool) -> u128 { b as u128 }
#[crate::polydat_node(category = Conversions)]
fn __bool_to_i128(b: bool) -> i128 { b as i128 }
#[crate::polydat_node(category = Conversions)]
fn __u128_to_bool(n: u128) -> bool { n != 0 }
#[crate::polydat_node(category = Conversions)]
fn __i128_to_bool(n: i128) -> bool { n != 0 }
#[crate::polydat_node(category = Conversions)]
fn __u128_to_u64(n: u128) -> u64 {
if n > u64::MAX as u128 {
panic!("__u128_to_u64: value {n} exceeds u64::MAX ({})", u64::MAX);
}
n as u64
}
#[crate::polydat_node(category = Conversions)]
fn __i128_to_i64(n: i128) -> i64 {
if n < i64::MIN as i128 || n > i64::MAX as i128 {
panic!("__i128_to_i64: value {n} out of i64 range [{}, {}]", i64::MIN, i64::MAX);
}
n as i64
}
#[crate::polydat_node(category = Conversions)]
fn __i64_to_u128(n: i64) -> u128 {
if n < 0 {
panic!("__i64_to_u128: negative value {n} cannot be represented as u128");
}
n as u128
}
#[crate::polydat_node(category = Conversions)]
fn __u128_to_i128(n: u128) -> i128 {
if n > i128::MAX as u128 {
panic!("__u128_to_i128: value {n} exceeds i128::MAX ({})", i128::MAX);
}
n as i128
}
#[crate::polydat_node(category = Conversions)]
fn __i128_to_u128(n: i128) -> u128 {
if n < 0 {
panic!("__i128_to_u128: negative value {n} cannot be represented as u128");
}
n as u128
}
#[crate::polydat_node(category = Conversions)]
fn __f64_to_u128(f: f64) -> u128 {
if !f.is_finite() {
panic!("__f64_to_u128: non-finite value {f} cannot be represented as u128");
}
let n = f.trunc();
if n < 0.0 || n > u128::MAX as f64 {
panic!("__f64_to_u128: value {f} out of u128 range");
}
n as u128
}
#[crate::polydat_node(category = Conversions)]
fn __f64_to_i128(f: f64) -> i128 {
if !f.is_finite() {
panic!("__f64_to_i128: non-finite value {f} cannot be represented as i128");
}
let n = f.trunc();
if n < i128::MIN as f64 || n > i128::MAX as f64 {
panic!("__f64_to_i128: value {f} out of i128 range");
}
n as i128
}
#[crate::polydat_node(category = Conversions)]
fn __u128_to_string(n: u128) -> String { n.to_string() }
#[crate::polydat_node(category = Conversions)]
fn __i128_to_string(n: i128) -> String { n.to_string() }
#[crate::polydat_node(category = Conversions)]
fn __str_to_u128(input: &str) -> u128 {
let raw = input.trim();
raw.parse::<u128>()
.unwrap_or_else(|e| panic!("__str_to_u128: cannot parse {raw:?} as u128: {e}"))
}
#[crate::polydat_node(category = Conversions)]
fn __str_to_i128(input: &str) -> i128 {
let raw = input.trim();
raw.parse::<i128>()
.unwrap_or_else(|e| panic!("__str_to_i128: cannot parse {raw:?} as i128: {e}"))
}
#[crate::polydat_node(category = Conversions)]
fn __u128_to_bytes(n: u128) -> Vec<u8> { n.to_le_bytes().to_vec() }
#[crate::polydat_node(category = Conversions)]
fn __i128_to_bytes(n: i128) -> Vec<u8> { n.to_le_bytes().to_vec() }
#[crate::polydat_node(category = Conversions)]
fn __bytes_to_u128(b: &[u8]) -> u128 {
if b.len() != 16 {
panic!("__bytes_to_u128: expected exactly 16 bytes for u128, got {}", b.len());
}
u128::from_le_bytes(b.try_into().unwrap())
}
#[crate::polydat_node(category = Conversions)]
fn __bytes_to_i128(b: &[u8]) -> i128 {
if b.len() != 16 {
panic!("__bytes_to_i128: expected exactly 16 bytes for i128, got {}", b.len());
}
i128::from_le_bytes(b.try_into().unwrap())
}
#[crate::polydat_node(category = Conversions)]
fn __u128_to_json(n: u128) -> Arc<serde_json::Value> {
Arc::new(serde_json::Value::String(n.to_string()))
}
#[crate::polydat_node(category = Conversions)]
fn __i128_to_json(n: i128) -> Arc<serde_json::Value> {
Arc::new(serde_json::Value::String(n.to_string()))
}
#[crate::polydat_node(category = Conversions)]
fn __json_to_u128(j: &serde_json::Value) -> u128 {
match j {
serde_json::Value::String(s) => s.trim().parse::<u128>().unwrap_or_else(|e| {
panic!("__json_to_u128: cannot parse {s:?} as u128: {e}")
}),
serde_json::Value::Number(n) => n
.as_u64()
.map(|u| u as u128)
.unwrap_or_else(|| panic!("__json_to_u128: JSON number {n} is not a u64")),
other => panic!("__json_to_u128: JSON value {other} is not a string or number"),
}
}
#[crate::polydat_node(category = Conversions)]
fn __json_to_i128(j: &serde_json::Value) -> i128 {
match j {
serde_json::Value::String(s) => s.trim().parse::<i128>().unwrap_or_else(|e| {
panic!("__json_to_i128: cannot parse {s:?} as i128: {e}")
}),
serde_json::Value::Number(n) => n
.as_i64()
.map(|i| i as i128)
.or_else(|| n.as_u64().map(|u| u as i128))
.unwrap_or_else(|| panic!("__json_to_i128: JSON number {n} is not an integer")),
other => panic!("__json_to_i128: JSON value {other} is not a string or number"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Bits128, PolydatNode, Value};
fn u128v(v: u128) -> Value { Value::U128(Bits128::from_u128(v)) }
fn i128v(v: i128) -> Value { Value::I128(Bits128::from_i128(v)) }
fn check<N: PolydatNode>(node: &N, input: Value, expected: Value) {
let mut out = [Value::None];
node.eval(&[input], &mut out);
assert_eq!(out[0], expected, "{} produced wrong output", node.meta().name);
}
fn check_panics<N: PolydatNode>(node: &N, input: Value, msg_substring: &str) {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut out = [Value::None];
node.eval(&[input], &mut out);
}));
match result {
Ok(_) => panic!("{} did not panic as expected", node.meta().name),
Err(payload) => {
let s = payload
.downcast_ref::<String>()
.cloned()
.or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
.unwrap_or_default();
assert!(
s.contains(msg_substring),
"{} panicked but message didn't contain {msg_substring:?}: {s}",
node.meta().name
);
}
}
}
const BIG: u128 = 0xDEAD_BEEF_CAFE_BABE_0123_4567_89AB_CDEF;
#[test]
fn limb_round_trip_preserves_all_bits() {
assert_eq!(Bits128::from_u128(BIG).as_u128(), BIG);
assert_eq!(Bits128::from_i128(-1).as_i128(), -1);
assert_eq!(Bits128::from_i128(i128::MIN).as_i128(), i128::MIN);
}
#[test]
fn widenings_and_narrowings() {
check(&U64ToU128::new(), Value::U64(u64::MAX), u128v(u64::MAX as u128));
check(&U64ToI128::new(), Value::U64(u64::MAX), i128v(u64::MAX as i128));
check(&I64ToI128::new(), Value::I64(-5), i128v(-5));
check(&U128ToU64::new(), u128v(42), Value::U64(42));
check_panics(&U128ToU64::new(), u128v(u64::MAX as u128 + 1), "exceeds u64::MAX");
check(&I128ToI64::new(), i128v(-5), Value::I64(-5));
check_panics(&I128ToI64::new(), i128v(i64::MAX as i128 + 1), "out of i64 range");
check_panics(&I64ToU128::new(), Value::I64(-1), "negative");
check_panics(&I128ToU128::new(), i128v(-1), "negative");
check(&U128ToI128::new(), u128v(42), i128v(42));
}
#[test]
fn string_and_json_round_trip() {
check(&U128ToString::new(), u128v(BIG), Value::Str(BIG.to_string().into()));
check(&StrToU128::new(), Value::Str(BIG.to_string().into()), u128v(BIG));
check(&I128ToString::new(), i128v(-7), Value::Str("-7".into()));
check(&StrToI128::new(), Value::Str(" -7 ".into()), i128v(-7));
check(&U128ToJson::new(), u128v(BIG),
Value::Json(Arc::new(serde_json::Value::String(BIG.to_string()))));
check(&JsonToU128::new(),
Value::Json(Arc::new(serde_json::Value::String(BIG.to_string()))), u128v(BIG));
check(&JsonToI128::new(),
Value::Json(Arc::new(serde_json::Value::from(-5_i64))), i128v(-5));
}
#[test]
fn bytes_round_trip() {
let bytes = BIG.to_le_bytes().to_vec();
check(&U128ToBytes::new(), u128v(BIG), Value::Bytes(bytes.clone().into()));
check(&BytesToU128::new(), Value::Bytes(bytes.into()), u128v(BIG));
check_panics(&BytesToU128::new(), Value::Bytes(vec![0; 8].into()), "expected exactly 16 bytes");
}
#[test]
fn display_and_json_projection_render_signed() {
assert_eq!(i128v(-5).to_display_string(), "-5");
assert_eq!(
i128v(-5).to_json_value(),
serde_json::Value::String("-5".to_string())
);
assert_eq!(u128v(BIG).to_display_string(), BIG.to_string());
}
}