1use crate::value::Value;
6use num_bigint::BigInt;
7#[cfg(feature = "complex_numbers")]
8use num_complex::Complex64;
9use num_rational::BigRational;
10use num_traits::ToPrimitive;
11
12#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
22enum NumericType {
23 Int32, Integer, Rational, #[cfg(feature = "complex_numbers")]
27 GaussianInt, Number, #[cfg(feature = "complex_numbers")]
30 Complex, }
32
33fn numeric_type(val: &Value) -> Option<NumericType> {
35 match val {
36 Value::Int32(_) => Some(NumericType::Int32),
37 Value::Integer(_) => Some(NumericType::Integer),
38 Value::Rational(_) => Some(NumericType::Rational),
39 Value::Number(_) => Some(NumericType::Number),
40 #[cfg(feature = "complex_numbers")]
41 Value::GaussianInt(_, _) => Some(NumericType::GaussianInt),
42 #[cfg(feature = "complex_numbers")]
43 Value::Complex(_) => Some(NumericType::Complex),
44 _ => None,
45 }
46}
47
48fn promote_to(val: &Value, target: NumericType) -> Value {
50 match (val, target) {
51 (Value::Int32(_), NumericType::Int32) => val.clone(),
53 (Value::Integer(_), NumericType::Integer) => val.clone(),
54 (Value::Rational(_), NumericType::Rational) => val.clone(),
55 (Value::Number(_), NumericType::Number) => val.clone(),
56 #[cfg(feature = "complex_numbers")]
57 (Value::GaussianInt(_, _), NumericType::GaussianInt) => val.clone(),
58 #[cfg(feature = "complex_numbers")]
59 (Value::Complex(_), NumericType::Complex) => val.clone(),
60
61 (Value::Int32(i), NumericType::Integer) => Value::Integer(BigInt::from(*i)),
63 (Value::Int32(i), NumericType::Rational) => {
64 Value::Rational(BigRational::from(BigInt::from(*i)))
65 }
66 (Value::Int32(i), NumericType::Number) => Value::Number(*i as f64),
67 #[cfg(feature = "complex_numbers")]
68 (Value::Int32(i), NumericType::GaussianInt) => {
69 Value::GaussianInt(BigInt::from(*i), BigInt::from(0))
70 }
71 #[cfg(feature = "complex_numbers")]
72 (Value::Int32(i), NumericType::Complex) => Value::Complex(Complex64::new(*i as f64, 0.0)),
73
74 (Value::Integer(i), NumericType::Rational) => {
76 Value::Rational(BigRational::from(i.clone()))
77 }
78 (Value::Integer(i), NumericType::Number) => {
79 Value::Number(i.to_f64().unwrap_or(f64::INFINITY))
80 }
81 #[cfg(feature = "complex_numbers")]
82 (Value::Integer(i), NumericType::GaussianInt) => {
83 Value::GaussianInt(i.clone(), BigInt::from(0))
84 }
85 #[cfg(feature = "complex_numbers")]
86 (Value::Integer(i), NumericType::Complex) => {
87 let n = i.to_f64().unwrap_or(f64::INFINITY);
88 Value::Complex(Complex64::new(n, 0.0))
89 }
90
91 (Value::Rational(r), NumericType::Number) => {
93 let numer = r.numer().to_f64().unwrap_or(0.0);
94 let denom = r.denom().to_f64().unwrap_or(1.0);
95 Value::Number(numer / denom)
96 }
97 #[cfg(feature = "complex_numbers")]
98 (Value::Rational(r), NumericType::Complex) => {
99 let numer = r.numer().to_f64().unwrap_or(0.0);
100 let denom = r.denom().to_f64().unwrap_or(1.0);
101 Value::Complex(Complex64::new(numer / denom, 0.0))
102 }
103
104 #[cfg(feature = "complex_numbers")]
106 (Value::Number(n), NumericType::Complex) => Value::Complex(Complex64::new(*n, 0.0)),
107
108 #[cfg(feature = "complex_numbers")]
110 (Value::GaussianInt(re, im), NumericType::Complex) => {
111 let re_f = re.to_f64().unwrap_or(f64::INFINITY);
112 let im_f = im.to_f64().unwrap_or(f64::INFINITY);
113 Value::Complex(Complex64::new(re_f, im_f))
114 }
115
116 _ => val.clone(), }
119}
120
121pub fn promote_pair(a: &Value, b: &Value) -> (Value, Value) {
124 let type_a = numeric_type(a);
125 let type_b = numeric_type(b);
126
127 match (type_a, type_b) {
128 (Some(ta), Some(tb)) => {
129 let target = if ta >= tb { ta.clone() } else { tb.clone() };
131
132 #[cfg(feature = "complex_numbers")]
134 let target = match (&ta, &tb) {
135 (NumericType::GaussianInt, NumericType::Number)
136 | (NumericType::Number, NumericType::GaussianInt) => NumericType::Complex,
137 (NumericType::GaussianInt, NumericType::Rational)
138 | (NumericType::Rational, NumericType::GaussianInt) => NumericType::Complex,
139 _ => target,
140 };
141
142 #[cfg(not(feature = "complex_numbers"))]
143 let target = target;
144
145 (promote_to(a, target.clone()), promote_to(b, target))
146 }
147 _ => (a.clone(), b.clone()),
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_promote_same_types() {
158 let a = Value::Integer(BigInt::from(5));
159 let b = Value::Integer(BigInt::from(3));
160 let (pa, pb) = promote_pair(&a, &b);
161 assert!(matches!(pa, Value::Integer(_)));
162 assert!(matches!(pb, Value::Integer(_)));
163 }
164
165 #[test]
166 fn test_promote_integer_to_rational() {
167 let a = Value::Integer(BigInt::from(5));
168 let b = Value::Rational(BigRational::from(BigInt::from(3)));
169 let (pa, pb) = promote_pair(&a, &b);
170 assert!(matches!(pa, Value::Rational(_)));
171 assert!(matches!(pb, Value::Rational(_)));
172 }
173
174 #[test]
175 fn test_promote_integer_to_number() {
176 let a = Value::Integer(BigInt::from(5));
177 let b = Value::Number(3.14);
178 let (pa, pb) = promote_pair(&a, &b);
179 assert!(matches!(pa, Value::Number(_)));
180 assert!(matches!(pb, Value::Number(_)));
181 }
182
183 #[test]
184 #[cfg(feature = "complex_numbers")]
185 fn test_promote_number_to_complex() {
186 let a = Value::Number(5.0);
187 let b = Value::Complex(Complex64::new(3.0, 4.0));
188 let (pa, pb) = promote_pair(&a, &b);
189 assert!(matches!(pa, Value::Complex(_)));
190 assert!(matches!(pb, Value::Complex(_)));
191 }
192
193 #[test]
194 #[cfg(feature = "complex_numbers")]
195 fn test_promote_gaussian_to_complex() {
196 let a = Value::GaussianInt(BigInt::from(5), BigInt::from(2));
197 let b = Value::Number(3.14);
198 let (pa, pb) = promote_pair(&a, &b);
199 assert!(matches!(pa, Value::Complex(_)));
200 assert!(matches!(pb, Value::Complex(_)));
201 }
202}