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) => Value::Number(Number::Rational(BigRational::new(n.clone(), d.clone()))),
86 ValueKey::Float(bits) => Value::Number(Number::Real(Real::F64(f64::from_bits(*bits)))),
87 ValueKey::Str(s) => Value::String(s.clone()),
88 ValueKey::Char(c) => Value::Char(*c),
89 ValueKey::Bool(b) => Value::Bool(*b),
90 ValueKey::Symbol(s) => Value::Symbol(*s),
91 ValueKey::Expr(u) => Value::Expr(ExprId::from_u32(*u)),
92 }
93 }
94}
95
96fn number_to_key(n: &Number) -> Option<ValueKey> {
101 if n.is_complex() {
102 return None;
103 }
104 match n {
105 Number::Integer(i) => match i.to_i64() {
106 Some(v) => Some(ValueKey::Int(v)),
107 None => Some(ValueKey::BigInt(i.clone())),
108 },
109 Number::Rational(r) => {
110 let r = BigRational::new(r.numer().clone(), r.denom().clone());
112 Some(ValueKey::Rational(r.numer().clone(), r.denom().clone()))
113 }
114 Number::Real(Real::F64(x)) => (!x.is_nan()).then_some(ValueKey::Float(x.to_bits())),
115 Number::Real(Real::F32(x)) => (!x.is_nan()).then_some(ValueKey::Float((*x as f64).to_bits())),
116 Number::BigFloat(f) => (!f.is_nan()).then_some(ValueKey::Float(f.to_bits())),
117 other => match other.as_i64() {
119 Some(v) => Some(ValueKey::Int(v)),
120 None => other.as_bigint().map(ValueKey::BigInt),
121 },
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use num_bigint::BigInt;
129 use num_rational::BigRational;
130
131 use crate::expr_pool::ExprPool;
132
133 fn assert_roundtrip(v: Value, key: ValueKey) {
135 let k = ValueKey::from_value(&v).unwrap_or_else(|| panic!("expected a key for {v:?}"));
136 assert_eq!(k, key);
137 assert_eq!(k.to_value(), v);
138 }
139
140 #[test]
141 fn int_key_roundtrip() {
142 assert_roundtrip(Value::Number(Number::from(1)), ValueKey::Int(1));
143 assert_roundtrip(Value::Number(Number::from(-7)), ValueKey::Int(-7));
144 }
145
146 #[test]
147 fn bigint_key_roundtrip() {
148 let big = BigInt::from(i64::MAX) + BigInt::from(1);
149 assert_roundtrip(Value::Number(Number::Integer(big.clone())), ValueKey::BigInt(big));
150 }
151
152 #[test]
153 fn rational_key_roundtrip() {
154 assert_roundtrip(
155 Value::Number(Number::Rational(BigRational::new(BigInt::from(1), BigInt::from(3)))),
156 ValueKey::Rational(BigInt::from(1), BigInt::from(3)),
157 );
158 assert_roundtrip(
160 Value::Number(Number::Rational(BigRational::new(BigInt::from(2), BigInt::from(-3)))),
161 ValueKey::Rational(BigInt::from(-2), BigInt::from(3)),
162 );
163 }
164
165 #[test]
166 fn float_key_roundtrip() {
167 assert_roundtrip(Value::Number(Number::from(2.5)), ValueKey::Float(2.5f64.to_bits()));
168 let v = Value::Number(Number::Real(Real::F32(1.5)));
170 assert_eq!(ValueKey::from_value(&v), Some(ValueKey::Float((1.5f32 as f64).to_bits())));
171 assert_eq!(
172 ValueKey::Float((1.5f32 as f64).to_bits()).to_value(),
173 Value::Number(Number::Real(Real::F64(1.5)))
174 );
175 }
176
177 #[test]
178 fn scalar_key_roundtrip() {
179 assert_roundtrip(Value::String("hello".to_string()), ValueKey::Str("hello".to_string()));
180 assert_roundtrip(Value::Char('x'), ValueKey::Char('x'));
181 assert_roundtrip(Value::Bool(true), ValueKey::Bool(true));
182 }
183
184 #[test]
185 fn symbol_and_expr_keys() {
186 assert_eq!(ValueKey::from_value(&Value::Symbol(42)), Some(ValueKey::Symbol(42)));
187 assert_eq!(ValueKey::Symbol(42).to_value(), Value::Symbol(42));
188
189 let pool = ExprPool::new();
190 let id = pool.integer(3);
191 assert_eq!(ValueKey::from_value(&Value::Expr(id)), Some(ValueKey::Expr(id.as_u32())));
192 assert_eq!(ValueKey::Expr(id.as_u32()).to_value(), Value::Expr(id));
193 }
194
195 #[test]
196 fn unsupported_values_are_none() {
197 assert_eq!(ValueKey::from_value(&Value::Number(Number::complex(1, 2))), None);
198 assert_eq!(ValueKey::from_value(&Value::Array(vec![Value::Number(Number::from(1))])), None);
199 assert_eq!(ValueKey::from_value(&Value::Dict(HashMap::new())), None);
200 assert_eq!(ValueKey::from_value(&Value::Set(HashSet::new())), None);
201 assert_eq!(ValueKey::from_value(&Value::Undefined), None);
202 assert_eq!(ValueKey::from_value(&Value::Nil), None);
203 }
204
205 #[test]
206 fn nan_is_not_a_key() {
207 assert_eq!(ValueKey::from_value(&Value::Number(Number::Real(Real::F64(f64::NAN)))), None);
208 assert_eq!(ValueKey::from_value(&Value::Number(Number::Real(Real::F32(f32::NAN)))), None);
209 }
210
211 #[test]
212 fn to_value_reconstructs_values() {
213 assert_eq!(ValueKey::Int(5).to_value(), Value::Number(Number::from(5)));
214 assert_eq!(
215 ValueKey::BigInt(BigInt::from(1u64 << 40)).to_value(),
216 Value::Number(Number::Integer(BigInt::from(1u64 << 40)))
217 );
218 assert_eq!(ValueKey::Float(2.5f64.to_bits()).to_value(), Value::Number(Number::Real(Real::F64(2.5))));
219 assert_eq!(ValueKey::Str("abc".to_string()).to_value(), Value::String("abc".to_string()));
220 assert_eq!(ValueKey::Char('z').to_value(), Value::Char('z'));
221 assert_eq!(ValueKey::Bool(false).to_value(), Value::Bool(false));
222 }
223
224 #[test]
225 fn dict_and_set_hold_hashable_keys() {
226 let mut m = HashMap::new();
227 m.insert(ValueKey::Str("a".into()), Value::Number(Number::from(1)));
228 let Value::Dict(d) = Value::Dict(m) else { unreachable!() };
229 assert_eq!(d.get(&ValueKey::Str("a".into())), Some(&Value::Number(Number::from(1))));
230
231 let mut s = HashSet::new();
232 s.insert(ValueKey::Int(1));
233 s.insert(ValueKey::Int(1));
234 s.insert(ValueKey::Float(2.5f64.to_bits()));
235 let Value::Set(s) = Value::Set(s) else { unreachable!() };
236 assert_eq!(s.len(), 2);
237 assert!(s.contains(&ValueKey::Int(1)));
238 assert!(s.contains(&ValueKey::Float(2.5f64.to_bits())));
239 }
240}