mod field;
pub use field::{F, P};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Field(F),
Word(u64),
Hash([u8; 32]),
Bytes(Vec<u8>),
List(Vec<Value>),
}
pub type Tuple = Vec<Value>;
pub type Relation = std::collections::BTreeSet<Tuple>;
impl Value {
pub fn int(i: i64) -> Value {
Value::Int(i)
}
pub fn field(x: u64) -> Value {
Value::Field(F::from_u64(x))
}
pub fn hash(h: [u8; 32]) -> Value {
Value::Hash(h)
}
pub fn str(s: &str) -> Value {
Value::Bytes(s.as_bytes().to_vec())
}
pub fn as_int(&self) -> Option<i64> {
match self {
Value::Int(i) => Some(*i),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn truthy(&self) -> bool {
!matches!(self, Value::Null | Value::Bool(false))
}
}
pub fn tag_hash(tag: &str) -> [u8; 32] {
let mut h = [0u8; 32];
let b = tag.as_bytes();
for (i, &c) in b.iter().enumerate() {
h[i % 32] ^= c.wrapping_add(i as u8);
}
h[31] = b.len() as u8;
h
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_semantics_dedup_and_order() {
let mut r: Relation = Relation::new();
r.insert(vec![Value::int(2)]);
r.insert(vec![Value::int(1)]);
r.insert(vec![Value::int(2)]); assert_eq!(r.len(), 2);
let order: Vec<i64> = r.iter().map(|t| t[0].as_int().unwrap()).collect();
assert_eq!(order, vec![1, 2]); }
#[test]
fn cross_type_total_order_is_stable() {
let mut v = vec![
Value::List(vec![Value::int(1)]),
Value::Int(5),
Value::Null,
Value::Bool(true),
Value::field(7),
];
v.sort();
assert_eq!(v[0], Value::Null);
assert_eq!(v[1], Value::Bool(true));
assert_eq!(v[2], Value::Int(5));
assert_eq!(v[3], Value::field(7));
assert!(matches!(v[4], Value::List(_)));
}
#[test]
fn truthiness() {
assert!(Value::int(0).truthy());
assert!(Value::Bool(true).truthy());
assert!(!Value::Bool(false).truthy());
assert!(!Value::Null.truthy());
}
#[test]
fn tag_hash_is_deterministic_and_distinct() {
assert_eq!(tag_hash("seed"), tag_hash("seed"));
assert_ne!(tag_hash("seed"), tag_hash("goal"));
}
}