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), JitFunction(u32),
46 Option(Option<Box<Value>>), }
48
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52pub enum ValueKey {
53 Int(i64),
54 BigInt(num_bigint::BigInt),
55 Rational(num_bigint::BigInt, num_bigint::BigInt), Float(u64), Str(String),
58 Char(char),
59 Bool(bool),
60 Symbol(u32), Expr(u32), }
63
64impl ValueKey {
65 pub fn from_value(v: &Value) -> Option<ValueKey> {
68 match v {
69 Value::Number(n) => number_to_key(n),
70 Value::String(s) => Some(ValueKey::Str(s.clone())),
71 Value::Char(c) => Some(ValueKey::Char(*c)),
72 Value::Bool(b) => Some(ValueKey::Bool(*b)),
73 Value::Symbol(s) => Some(ValueKey::Symbol(*s)),
74 Value::Expr(id) => Some(ValueKey::Expr(id.as_u32())),
75 _ => None,
76 }
77 }
78
79 pub fn to_value(&self) -> Value {
82 match self {
83 ValueKey::Int(i) => Value::Number(Number::from(*i)),
84 ValueKey::BigInt(b) => Value::Number(Number::Integer(b.clone())),
85 ValueKey::Rational(n, d) => {
86 Value::Number(Number::Rational(BigRational::new(n.clone(), d.clone())))
87 }
88 ValueKey::Float(bits) => Value::Number(Number::Real(Real::F64(f64::from_bits(*bits)))),
89 ValueKey::Str(s) => Value::String(s.clone()),
90 ValueKey::Char(c) => Value::Char(*c),
91 ValueKey::Bool(b) => Value::Bool(*b),
92 ValueKey::Symbol(s) => Value::Symbol(*s),
93 ValueKey::Expr(u) => Value::Expr(ExprId::from_u32(*u)),
94 }
95 }
96}
97
98fn number_to_key(n: &Number) -> Option<ValueKey> {
103 if n.is_complex() {
104 return None;
105 }
106 match n {
107 Number::Integer(i) => match i.to_i64() {
108 Some(v) => Some(ValueKey::Int(v)),
109 None => Some(ValueKey::BigInt(i.clone())),
110 },
111 Number::Rational(r) => {
112 let r = BigRational::new(r.numer().clone(), r.denom().clone());
114 Some(ValueKey::Rational(r.numer().clone(), r.denom().clone()))
115 }
116 Number::Real(Real::F64(x)) => (!x.is_nan()).then_some(ValueKey::Float(x.to_bits())),
117 Number::Real(Real::F32(x)) => {
118 (!x.is_nan()).then_some(ValueKey::Float((*x as f64).to_bits()))
119 }
120 Number::BigFloat(f) => (!f.is_nan()).then_some(ValueKey::Float(f.to_bits())),
121 other => match other.as_i64() {
123 Some(v) => Some(ValueKey::Int(v)),
124 None => other.as_bigint().map(ValueKey::BigInt),
125 },
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use num_bigint::BigInt;
133 use num_rational::BigRational;
134
135 use crate::expr_pool::ExprPool;
136
137 fn assert_roundtrip(v: Value, key: ValueKey) {
139 let k = ValueKey::from_value(&v).unwrap_or_else(|| panic!("expected a key for {v:?}"));
140 assert_eq!(k, key);
141 assert_eq!(k.to_value(), v);
142 }
143
144 #[test]
145 fn int_key_roundtrip() {
146 assert_roundtrip(Value::Number(Number::from(1)), ValueKey::Int(1));
147 assert_roundtrip(Value::Number(Number::from(-7)), ValueKey::Int(-7));
148 }
149
150 #[test]
151 fn bigint_key_roundtrip() {
152 let big = BigInt::from(i64::MAX) + BigInt::from(1);
153 assert_roundtrip(
154 Value::Number(Number::Integer(big.clone())),
155 ValueKey::BigInt(big),
156 );
157 }
158
159 #[test]
160 fn rational_key_roundtrip() {
161 assert_roundtrip(
162 Value::Number(Number::Rational(BigRational::new(
163 BigInt::from(1),
164 BigInt::from(3),
165 ))),
166 ValueKey::Rational(BigInt::from(1), BigInt::from(3)),
167 );
168 assert_roundtrip(
170 Value::Number(Number::Rational(BigRational::new(
171 BigInt::from(2),
172 BigInt::from(-3),
173 ))),
174 ValueKey::Rational(BigInt::from(-2), BigInt::from(3)),
175 );
176 }
177
178 #[test]
179 fn float_key_roundtrip() {
180 assert_roundtrip(
181 Value::Number(Number::from(2.5)),
182 ValueKey::Float(2.5f64.to_bits()),
183 );
184 let v = Value::Number(Number::Real(Real::F32(1.5)));
186 assert_eq!(
187 ValueKey::from_value(&v),
188 Some(ValueKey::Float((1.5f32 as f64).to_bits()))
189 );
190 assert_eq!(
191 ValueKey::Float((1.5f32 as f64).to_bits()).to_value(),
192 Value::Number(Number::Real(Real::F64(1.5)))
193 );
194 }
195
196 #[test]
197 fn scalar_key_roundtrip() {
198 assert_roundtrip(
199 Value::String("hello".to_string()),
200 ValueKey::Str("hello".to_string()),
201 );
202 assert_roundtrip(Value::Char('x'), ValueKey::Char('x'));
203 assert_roundtrip(Value::Bool(true), ValueKey::Bool(true));
204 }
205
206 #[test]
207 fn symbol_and_expr_keys() {
208 assert_eq!(
209 ValueKey::from_value(&Value::Symbol(42)),
210 Some(ValueKey::Symbol(42))
211 );
212 assert_eq!(ValueKey::Symbol(42).to_value(), Value::Symbol(42));
213
214 let pool = ExprPool::new();
215 let id = pool.integer(3);
216 assert_eq!(
217 ValueKey::from_value(&Value::Expr(id)),
218 Some(ValueKey::Expr(id.as_u32()))
219 );
220 assert_eq!(ValueKey::Expr(id.as_u32()).to_value(), Value::Expr(id));
221 }
222
223 #[test]
224 fn unsupported_values_are_none() {
225 assert_eq!(
226 ValueKey::from_value(&Value::Number(Number::complex(1, 2))),
227 None
228 );
229 assert_eq!(
230 ValueKey::from_value(&Value::Array(vec![Value::Number(Number::from(1))])),
231 None
232 );
233 assert_eq!(ValueKey::from_value(&Value::Dict(HashMap::new())), None);
234 assert_eq!(ValueKey::from_value(&Value::Set(HashSet::new())), None);
235 assert_eq!(ValueKey::from_value(&Value::Undefined), None);
236 assert_eq!(ValueKey::from_value(&Value::Nil), None);
237 }
238
239 #[test]
240 fn nan_is_not_a_key() {
241 assert_eq!(
242 ValueKey::from_value(&Value::Number(Number::Real(Real::F64(f64::NAN)))),
243 None
244 );
245 assert_eq!(
246 ValueKey::from_value(&Value::Number(Number::Real(Real::F32(f32::NAN)))),
247 None
248 );
249 }
250
251 #[test]
252 fn to_value_reconstructs_values() {
253 assert_eq!(ValueKey::Int(5).to_value(), Value::Number(Number::from(5)));
254 assert_eq!(
255 ValueKey::BigInt(BigInt::from(1u64 << 40)).to_value(),
256 Value::Number(Number::Integer(BigInt::from(1u64 << 40)))
257 );
258 assert_eq!(
259 ValueKey::Float(2.5f64.to_bits()).to_value(),
260 Value::Number(Number::Real(Real::F64(2.5)))
261 );
262 assert_eq!(
263 ValueKey::Str("abc".to_string()).to_value(),
264 Value::String("abc".to_string())
265 );
266 assert_eq!(ValueKey::Char('z').to_value(), Value::Char('z'));
267 assert_eq!(ValueKey::Bool(false).to_value(), Value::Bool(false));
268 }
269
270 #[test]
271 fn dict_and_set_hold_hashable_keys() {
272 let mut m = HashMap::new();
273 m.insert(ValueKey::Str("a".into()), Value::Number(Number::from(1)));
274 let Value::Dict(d) = Value::Dict(m) else {
275 unreachable!()
276 };
277 assert_eq!(
278 d.get(&ValueKey::Str("a".into())),
279 Some(&Value::Number(Number::from(1)))
280 );
281
282 let mut s = HashSet::new();
283 s.insert(ValueKey::Int(1));
284 s.insert(ValueKey::Int(1));
285 s.insert(ValueKey::Float(2.5f64.to_bits()));
286 let Value::Set(s) = Value::Set(s) else {
287 unreachable!()
288 };
289 assert_eq!(s.len(), 2);
290 assert!(s.contains(&ValueKey::Int(1)));
291 assert!(s.contains(&ValueKey::Float(2.5f64.to_bits())));
292 }
293}