1use std::collections::{HashMap, HashSet};
2
3use num_rational::BigRational;
4use num_traits::ToPrimitive;
5
6use crate::expr_pool::ExprId;
7use crate::number::{Number, Real};
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub enum IndeterminateForm {
13 ZeroOverZero,
14 InfOverInf,
15 ZeroTimesInf,
16 InfMinusInf,
17}
18
19#[derive(Debug, Clone, PartialEq)]
25pub enum Value {
26 Nil,
27 Number(Number),
28 Bool(bool),
29 Char(char),
30 String(String),
31 Array(Vec<Value>),
32 Dict(HashMap<ValueKey, Value>),
33 Set(HashSet<ValueKey>),
34 Expr(ExprId),
35 Symbol(u32),
36 Indeterminate(IndeterminateForm),
37 Undefined,
38 Error(String),
39 Tuple(Vec<Value>),
40 Result(std::result::Result<Box<Value>, String>),
41 Class(u32), Option(Option<Box<Value>>), }
44
45#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48pub enum ValueKey {
49 Int(i64),
50 BigInt(num_bigint::BigInt),
51 Rational(num_bigint::BigInt, num_bigint::BigInt), Float(u64), Str(String),
54 Char(char),
55 Bool(bool),
56 Symbol(u32), Expr(u32), }
59
60impl ValueKey {
61 pub fn from_value(v: &Value) -> Option<ValueKey> {
64 match v {
65 Value::Number(n) => number_to_key(n),
66 Value::String(s) => Some(ValueKey::Str(s.clone())),
67 Value::Char(c) => Some(ValueKey::Char(*c)),
68 Value::Bool(b) => Some(ValueKey::Bool(*b)),
69 Value::Symbol(s) => Some(ValueKey::Symbol(*s)),
70 Value::Expr(id) => Some(ValueKey::Expr(id.as_u32())),
71 _ => None,
72 }
73 }
74
75 pub fn to_value(&self) -> Value {
78 match self {
79 ValueKey::Int(i) => Value::Number(Number::from(*i)),
80 ValueKey::BigInt(b) => Value::Number(Number::Integer(b.clone())),
81 ValueKey::Rational(n, d) => Value::Number(Number::Rational(BigRational::new(n.clone(), d.clone()))),
82 ValueKey::Float(bits) => Value::Number(Number::Real(Real::F64(f64::from_bits(*bits)))),
83 ValueKey::Str(s) => Value::String(s.clone()),
84 ValueKey::Char(c) => Value::Char(*c),
85 ValueKey::Bool(b) => Value::Bool(*b),
86 ValueKey::Symbol(s) => Value::Symbol(*s),
87 ValueKey::Expr(u) => Value::Expr(ExprId::from_u32(*u)),
88 }
89 }
90}
91
92fn number_to_key(n: &Number) -> Option<ValueKey> {
97 if n.is_complex() {
98 return None;
99 }
100 match n {
101 Number::Integer(i) => match i.to_i64() {
102 Some(v) => Some(ValueKey::Int(v)),
103 None => Some(ValueKey::BigInt(i.clone())),
104 },
105 Number::Rational(r) => {
106 let r = BigRational::new(r.numer().clone(), r.denom().clone());
108 Some(ValueKey::Rational(r.numer().clone(), r.denom().clone()))
109 }
110 Number::Real(Real::F64(x)) => (!x.is_nan()).then_some(ValueKey::Float(x.to_bits())),
111 Number::Real(Real::F32(x)) => (!x.is_nan()).then_some(ValueKey::Float((*x as f64).to_bits())),
112 Number::BigFloat(f) => (!f.is_nan()).then_some(ValueKey::Float(f.to_bits())),
113 other => match other.as_i64() {
115 Some(v) => Some(ValueKey::Int(v)),
116 None => other.as_bigint().map(ValueKey::BigInt),
117 },
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use num_bigint::BigInt;
125 use num_rational::BigRational;
126
127 use crate::expr_pool::ExprPool;
128
129 fn assert_roundtrip(v: Value, key: ValueKey) {
131 let k = ValueKey::from_value(&v).unwrap_or_else(|| panic!("expected a key for {v:?}"));
132 assert_eq!(k, key);
133 assert_eq!(k.to_value(), v);
134 }
135
136 #[test]
137 fn int_key_roundtrip() {
138 assert_roundtrip(Value::Number(Number::from(1)), ValueKey::Int(1));
139 assert_roundtrip(Value::Number(Number::from(-7)), ValueKey::Int(-7));
140 }
141
142 #[test]
143 fn bigint_key_roundtrip() {
144 let big = BigInt::from(i64::MAX) + BigInt::from(1);
145 assert_roundtrip(Value::Number(Number::Integer(big.clone())), ValueKey::BigInt(big));
146 }
147
148 #[test]
149 fn rational_key_roundtrip() {
150 assert_roundtrip(
151 Value::Number(Number::Rational(BigRational::new(BigInt::from(1), BigInt::from(3)))),
152 ValueKey::Rational(BigInt::from(1), BigInt::from(3)),
153 );
154 assert_roundtrip(
156 Value::Number(Number::Rational(BigRational::new(BigInt::from(2), BigInt::from(-3)))),
157 ValueKey::Rational(BigInt::from(-2), BigInt::from(3)),
158 );
159 }
160
161 #[test]
162 fn float_key_roundtrip() {
163 assert_roundtrip(Value::Number(Number::from(2.5)), ValueKey::Float(2.5f64.to_bits()));
164 let v = Value::Number(Number::Real(Real::F32(1.5)));
166 assert_eq!(ValueKey::from_value(&v), Some(ValueKey::Float((1.5f32 as f64).to_bits())));
167 assert_eq!(
168 ValueKey::Float((1.5f32 as f64).to_bits()).to_value(),
169 Value::Number(Number::Real(Real::F64(1.5)))
170 );
171 }
172
173 #[test]
174 fn scalar_key_roundtrip() {
175 assert_roundtrip(Value::String("hello".to_string()), ValueKey::Str("hello".to_string()));
176 assert_roundtrip(Value::Char('x'), ValueKey::Char('x'));
177 assert_roundtrip(Value::Bool(true), ValueKey::Bool(true));
178 }
179
180 #[test]
181 fn symbol_and_expr_keys() {
182 assert_eq!(ValueKey::from_value(&Value::Symbol(42)), Some(ValueKey::Symbol(42)));
183 assert_eq!(ValueKey::Symbol(42).to_value(), Value::Symbol(42));
184
185 let pool = ExprPool::new();
186 let id = pool.integer(3);
187 assert_eq!(ValueKey::from_value(&Value::Expr(id)), Some(ValueKey::Expr(id.as_u32())));
188 assert_eq!(ValueKey::Expr(id.as_u32()).to_value(), Value::Expr(id));
189 }
190
191 #[test]
192 fn unsupported_values_are_none() {
193 assert_eq!(ValueKey::from_value(&Value::Number(Number::complex(1, 2))), None);
194 assert_eq!(ValueKey::from_value(&Value::Array(vec![Value::Number(Number::from(1))])), None);
195 assert_eq!(ValueKey::from_value(&Value::Dict(HashMap::new())), None);
196 assert_eq!(ValueKey::from_value(&Value::Set(HashSet::new())), None);
197 assert_eq!(ValueKey::from_value(&Value::Undefined), None);
198 assert_eq!(ValueKey::from_value(&Value::Nil), None);
199 }
200
201 #[test]
202 fn nan_is_not_a_key() {
203 assert_eq!(ValueKey::from_value(&Value::Number(Number::Real(Real::F64(f64::NAN)))), None);
204 assert_eq!(ValueKey::from_value(&Value::Number(Number::Real(Real::F32(f32::NAN)))), None);
205 }
206
207 #[test]
208 fn to_value_reconstructs_values() {
209 assert_eq!(ValueKey::Int(5).to_value(), Value::Number(Number::from(5)));
210 assert_eq!(
211 ValueKey::BigInt(BigInt::from(1u64 << 40)).to_value(),
212 Value::Number(Number::Integer(BigInt::from(1u64 << 40)))
213 );
214 assert_eq!(ValueKey::Float(2.5f64.to_bits()).to_value(), Value::Number(Number::Real(Real::F64(2.5))));
215 assert_eq!(ValueKey::Str("abc".to_string()).to_value(), Value::String("abc".to_string()));
216 assert_eq!(ValueKey::Char('z').to_value(), Value::Char('z'));
217 assert_eq!(ValueKey::Bool(false).to_value(), Value::Bool(false));
218 }
219
220 #[test]
221 fn dict_and_set_hold_hashable_keys() {
222 let mut m = HashMap::new();
223 m.insert(ValueKey::Str("a".into()), Value::Number(Number::from(1)));
224 let Value::Dict(d) = Value::Dict(m) else { unreachable!() };
225 assert_eq!(d.get(&ValueKey::Str("a".into())), Some(&Value::Number(Number::from(1))));
226
227 let mut s = HashSet::new();
228 s.insert(ValueKey::Int(1));
229 s.insert(ValueKey::Int(1));
230 s.insert(ValueKey::Float(2.5f64.to_bits()));
231 let Value::Set(s) = Value::Set(s) else { unreachable!() };
232 assert_eq!(s.len(), 2);
233 assert!(s.contains(&ValueKey::Int(1)));
234 assert!(s.contains(&ValueKey::Float(2.5f64.to_bits())));
235 }
236}