1use crate::error::CoreError;
2use std::fmt;
3
4use num_bigint::BigInt;
5use num_rational::BigRational;
6use num_traits::{One, Signed, ToPrimitive, Zero};
7
8#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum Real {
12 F32(f32),
13 F64(f64),
14}
15
16impl std::hash::Hash for Real {
17 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
18 match self {
19 Real::F32(f) => f.to_bits().hash(state),
20 Real::F64(f) => f.to_bits().hash(state),
21 }
22 }
23}
24
25#[derive(Debug, Clone, PartialEq)]
31pub enum Number {
32 Integer(BigInt),
33 Rational(BigRational),
34 Real(Real),
35 Complex { re: Box<Number>, im: Box<Number> },
36 I8(i8), I16(i16), I32(i32), I64(i64), I128(i128),
38 U8(u8), U16(u16), U32(u32), U64(u64), U128(u128),
39 Isize(isize), Usize(usize),
40 BigFloat(f64),
41}
42
43impl Number {
44 pub fn complex(re: i64, im: i64) -> Number {
45 Number::Complex {
46 re: Box::new(Number::Integer(BigInt::from(re))),
47 im: Box::new(Number::Integer(BigInt::from(im))),
48 }
49 }
50
51 pub fn is_complex(&self) -> bool {
52 matches!(self, Number::Complex { .. })
53 }
54
55 pub fn is_zero(&self) -> bool {
56 match self {
57 Number::Integer(i) => i.is_zero(),
58 Number::Rational(r) => r.is_zero(),
59 Number::Real(Real::F32(f)) => *f == 0.0,
60 Number::Real(Real::F64(f)) => *f == 0.0,
61 Number::Complex { re, im } => re.is_zero() && im.is_zero(),
62 other => normalize(other.clone()).is_zero(),
63 }
64 }
65
66 pub fn is_one(&self) -> bool {
67 match self {
68 Number::Integer(i) => i == &BigInt::from(1),
69 Number::Rational(r) => r == &BigRational::new(BigInt::from(1), BigInt::from(1)),
70 Number::Real(Real::F32(f)) => *f == 1.0,
71 Number::Real(Real::F64(f)) => *f == 1.0,
72 Number::Complex { .. } => false,
73 other => normalize(other.clone()).is_one(),
74 }
75 }
76
77 pub fn abs(&self) -> Number {
78 match self {
79 Number::Integer(i) => Number::Integer(i.abs()),
80 Number::Rational(r) => Number::Rational(r.abs()),
81 Number::Real(Real::F32(x)) => Number::Real(Real::F32(x.abs())),
82 Number::Real(Real::F64(x)) => Number::Real(Real::F64(x.abs())),
83 Number::Complex { .. } => self.clone(),
84 other => normalize(other.clone()).abs(),
85 }
86 }
87
88 pub fn sqrt(&self) -> Option<Number> {
89 match self {
90 Number::Integer(n) => isqrt(n).map(Number::Integer),
91 Number::Rational(r) => {
92 let p = isqrt(r.numer())?;
93 let q = isqrt(r.denom())?;
94 Some(Number::Rational(BigRational::new(p, q)))
95 }
96 Number::Real(Real::F32(x)) => Some(Number::Real(Real::F32(x.sqrt()))),
97 Number::Real(Real::F64(x)) => Some(Number::Real(Real::F64(x.sqrt()))),
98 Number::Complex { .. } => None,
99 other => normalize(other.clone()).sqrt(),
100 }
101 }
102
103 pub fn pow(&self, exp: &Number) -> Option<Number> {
104 let base = normalize(self.clone());
105 let exp = normalize(exp.clone());
106 match (&base, &exp) {
107 (Number::Integer(a), Number::Integer(b)) => {
108 if b.is_zero() {
109 return Some(Number::Integer(BigInt::one()));
110 }
111 let neg = *b < BigInt::zero();
112 let mag = if neg { -b } else { b.clone() };
113 let e = mag.to_u32()?;
114 if neg && a.is_zero() {
115 return None;
116 }
117 let p = a.pow(e);
118 if neg {
119 Some(normalized(BigInt::one(), p))
120 } else {
121 Some(Number::Integer(p))
122 }
123 }
124 (Number::Rational(a), Number::Integer(b)) => {
125 if b.is_zero() {
126 return Some(Number::Integer(BigInt::one()));
127 }
128 let neg = *b < BigInt::zero();
129 let mag = if neg { -b } else { b.clone() };
130 let e = mag.to_u32()?;
131 if neg && a.is_zero() {
132 return None;
133 }
134 let p = a.numer().pow(e);
135 let q = a.denom().pow(e);
136 if neg {
137 Some(normalized(q, p))
138 } else {
139 Some(normalized(p, q))
140 }
141 }
142 (Number::Real(x), Number::Integer(b)) => {
143 let n = b.to_i32()?;
144 match x {
145 Real::F32(f) => Some(Number::Real(Real::F32(f.powi(n)))),
146 Real::F64(f) => Some(Number::Real(Real::F64(f.powi(n)))),
147 }
148 }
149 (Number::Real(x), Number::Rational(r)) => {
150 let v = r.to_f64()?;
151 match x {
152 Real::F32(f) => Some(Number::Real(Real::F32(f.powf(v as f32)))),
153 Real::F64(f) => Some(Number::Real(Real::F64(f.powf(v)))),
154 }
155 }
156 (Number::Integer(a), Number::Rational(r)) => {
157 if *r.denom() == BigInt::one() {
158 return base.pow(&Number::Integer(r.numer().clone()));
159 }
160 if *r.denom() == BigInt::from(2) && *r.numer() == BigInt::one() {
162 return base.sqrt();
163 }
164 let _ = a;
165 None
166 }
167 (Number::Rational(a), Number::Rational(r)) => {
168 if *r.denom() == BigInt::one() {
169 return base.pow(&Number::Integer(r.numer().clone()));
170 }
171 if *r.denom() == BigInt::from(2) && *r.numer() == BigInt::one() {
172 return base.sqrt();
173 }
174 let _ = a;
175 None
176 }
177 _ => None,
178 }
179 }
180
181 pub fn to_f64_lossy(&self) -> f64 {
183 match self {
184 Number::Integer(i) => i.to_f64().unwrap_or(f64::NAN),
185 Number::Rational(r) => r.to_f64().unwrap_or(f64::NAN),
186 Number::Real(Real::F32(f)) => *f as f64,
187 Number::Real(Real::F64(f)) => *f,
188 Number::Complex { .. } => f64::NAN,
189 Number::I8(v) => *v as f64,
190 Number::I16(v) => *v as f64,
191 Number::I32(v) => *v as f64,
192 Number::I64(v) => *v as f64,
193 Number::I128(v) => *v as f64,
194 Number::U8(v) => *v as f64,
195 Number::U16(v) => *v as f64,
196 Number::U32(v) => *v as f64,
197 Number::U64(v) => *v as f64,
198 Number::U128(v) => *v as f64,
199 Number::Isize(v) => *v as f64,
200 Number::Usize(v) => *v as f64,
201 Number::BigFloat(f) => *f,
202 }
203 }
204
205 pub fn as_i64(&self) -> Option<i64> {
207 match self {
208 Number::Integer(i) => i.to_i64(),
209 Number::Rational(r) if *r.denom() == BigInt::one() => r.numer().to_i64(),
210 Number::Real(Real::F64(f)) if f.fract() == 0.0 && (*f as i64) as f64 == *f => Some(*f as i64),
211 Number::Real(Real::F32(f)) if f.fract() == 0.0 && (*f as i64) as f64 == *f as f64 => Some(*f as i64),
212 Number::I8(v) => Some(*v as i64),
213 Number::I16(v) => Some(*v as i64),
214 Number::I32(v) => Some(*v as i64),
215 Number::I64(v) => Some(*v),
216 Number::I128(v) => i64::try_from(*v).ok(),
217 Number::U8(v) => Some(*v as i64),
218 Number::U16(v) => Some(*v as i64),
219 Number::U32(v) => Some(*v as i64),
220 Number::U64(v) => i64::try_from(*v).ok(),
221 Number::U128(v) => i64::try_from(*v).ok(),
222 Number::Isize(v) => Some(*v as i64),
223 Number::Usize(v) => i64::try_from(*v).ok(),
224 Number::BigFloat(f) if f.fract() == 0.0 && (*f as i64) as f64 == *f => Some(*f as i64),
225 _ => None,
226 }
227 }
228
229 pub fn as_i32(&self) -> Option<i32> {
231 self.as_i64().and_then(|v| i32::try_from(v).ok())
232 }
233
234 pub fn as_u64(&self) -> Option<u64> {
236 match self {
237 Number::Integer(i) => i.to_u64(),
238 Number::Rational(r) if *r.denom() == BigInt::one() => r.numer().to_u64(),
239 Number::Real(Real::F64(f)) if f.fract() == 0.0 && f.is_sign_positive() && (*f as u64) as f64 == *f => {
240 Some(*f as u64)
241 }
242 Number::Real(Real::F32(f)) if f.fract() == 0.0 && f.is_sign_positive() && (*f as u64) as f64 == *f as f64 => {
243 Some(*f as u64)
244 }
245 Number::I8(v) if *v >= 0 => Some(*v as u64),
246 Number::I16(v) if *v >= 0 => Some(*v as u64),
247 Number::I32(v) if *v >= 0 => Some(*v as u64),
248 Number::I64(v) if *v >= 0 => Some(*v as u64),
249 Number::I128(v) => u64::try_from(*v).ok(),
250 Number::U8(v) => Some(*v as u64),
251 Number::U16(v) => Some(*v as u64),
252 Number::U32(v) => Some(*v as u64),
253 Number::U64(v) => Some(*v),
254 Number::U128(v) => u64::try_from(*v).ok(),
255 Number::Isize(v) if *v >= 0 => Some(*v as u64),
256 Number::Usize(v) => u64::try_from(*v).ok(),
257 Number::BigFloat(f) if f.fract() == 0.0 && f.is_sign_positive() && (*f as u64) as f64 == *f => {
258 Some(*f as u64)
259 }
260 _ => None,
261 }
262 }
263
264 pub fn as_bigint(&self) -> Option<BigInt> {
266 match self {
267 Number::Integer(i) => Some(i.clone()),
268 Number::Rational(r) if *r.denom() == BigInt::one() => Some(r.numer().clone()),
269 Number::Real(Real::F64(f)) if f.fract() == 0.0 => Some(BigInt::from(*f as i64)),
270 Number::Real(Real::F32(f)) if f.fract() == 0.0 => Some(BigInt::from(*f as i64)),
271 Number::I8(v) => Some(BigInt::from(*v)),
272 Number::I16(v) => Some(BigInt::from(*v)),
273 Number::I32(v) => Some(BigInt::from(*v)),
274 Number::I64(v) => Some(BigInt::from(*v)),
275 Number::I128(v) => Some(BigInt::from(*v)),
276 Number::U8(v) => Some(BigInt::from(*v)),
277 Number::U16(v) => Some(BigInt::from(*v)),
278 Number::U32(v) => Some(BigInt::from(*v)),
279 Number::U64(v) => Some(BigInt::from(*v)),
280 Number::U128(v) => Some(BigInt::from(*v)),
281 Number::Isize(v) => Some(BigInt::from(*v)),
282 Number::Usize(v) => Some(BigInt::from(*v)),
283 Number::BigFloat(f) if f.fract() == 0.0 => Some(BigInt::from(*f as i64)),
284 _ => None,
285 }
286 }
287
288 pub fn as_rational(&self) -> Option<BigRational> {
290 match self {
291 Number::Integer(i) => Some(BigRational::from_integer(i.clone())),
292 Number::Rational(r) => Some(r.clone()),
293 Number::Real(Real::F64(f)) if f.fract() == 0.0 => Some(BigRational::from_integer(BigInt::from(*f as i64))),
294 Number::Real(Real::F32(f)) if f.fract() == 0.0 => Some(BigRational::from_integer(BigInt::from(*f as i64))),
295 Number::I8(v) => Some(BigRational::from_integer(BigInt::from(*v))),
296 Number::I16(v) => Some(BigRational::from_integer(BigInt::from(*v))),
297 Number::I32(v) => Some(BigRational::from_integer(BigInt::from(*v))),
298 Number::I64(v) => Some(BigRational::from_integer(BigInt::from(*v))),
299 Number::I128(v) => Some(BigRational::from_integer(BigInt::from(*v))),
300 Number::U8(v) => Some(BigRational::from_integer(BigInt::from(*v))),
301 Number::U16(v) => Some(BigRational::from_integer(BigInt::from(*v))),
302 Number::U32(v) => Some(BigRational::from_integer(BigInt::from(*v))),
303 Number::U64(v) => Some(BigRational::from_integer(BigInt::from(*v))),
304 Number::U128(v) => Some(BigRational::from_integer(BigInt::from(*v))),
305 Number::Isize(v) => Some(BigRational::from_integer(BigInt::from(*v))),
306 Number::Usize(v) => Some(BigRational::from_integer(BigInt::from(*v))),
307 Number::BigFloat(f) if f.fract() == 0.0 => Some(BigRational::from_integer(BigInt::from(*f as i64))),
308 _ => None,
309 }
310 }
311
312 pub fn is_integer_value(&self) -> bool {
314 self.as_bigint().is_some()
315 }
316
317 pub fn as_i8(&self) -> Option<i8> {
320 exact_integer(self).and_then(|b| b.to_i8())
321 }
322
323 pub fn as_i16(&self) -> Option<i16> {
325 exact_integer(self).and_then(|b| b.to_i16())
326 }
327
328 pub fn as_i128(&self) -> Option<i128> {
330 exact_integer(self).and_then(|b| b.to_i128())
331 }
332
333 pub fn as_u8(&self) -> Option<u8> {
335 exact_integer(self).and_then(|b| b.to_u8())
336 }
337
338 pub fn as_u16(&self) -> Option<u16> {
340 exact_integer(self).and_then(|b| b.to_u16())
341 }
342
343 pub fn as_u32(&self) -> Option<u32> {
345 exact_integer(self).and_then(|b| b.to_u32())
346 }
347
348 pub fn as_u128(&self) -> Option<u128> {
350 exact_integer(self).and_then(|b| b.to_u128())
351 }
352
353 pub fn as_isize(&self) -> Option<isize> {
355 exact_integer(self).and_then(|b| b.to_isize())
356 }
357
358 pub fn as_usize(&self) -> Option<usize> {
360 exact_integer(self).and_then(|b| b.to_usize())
361 }
362
363 pub fn as_f32(&self) -> Option<f32> {
365 match self {
366 Number::Complex { .. } => None,
367 Number::Real(Real::F32(f)) => Some(*f),
368 _ => Some(self.to_f64_lossy() as f32),
369 }
370 }
371
372 pub fn truncate(&self) -> Number {
374 match self {
375 Number::Integer(_) => self.clone(),
376 Number::Rational(r) => {
377 let t = r.to_integer();
378 normalized(t, BigInt::one())
379 }
380 Number::Real(Real::F64(f)) => Number::Real(Real::F64(f.trunc())),
381 Number::Real(Real::F32(f)) => Number::Real(Real::F32(f.trunc())),
382 Number::Complex { .. } => self.clone(),
383 other => normalize(other.clone()).truncate(),
384 }
385 }
386
387 pub fn round(&self) -> Number {
389 match self {
390 Number::Integer(_) => self.clone(),
391 Number::Rational(r) => normalized(r.round().numer().clone(), BigInt::one()),
392 Number::Real(Real::F64(f)) => Number::Real(Real::F64(f.round())),
393 Number::Real(Real::F32(f)) => Number::Real(Real::F32(f.round())),
394 Number::Complex { .. } => self.clone(),
395 other => normalize(other.clone()).round(),
396 }
397 }
398
399 pub fn rounded_digits(&self, digits: i64) -> Number {
401 let mult = 10f64.powi(digits as i32);
402 let v = (self.to_f64_lossy() * mult).round() / mult;
403 Number::Real(Real::F64(v))
404 }
405
406 pub fn clamped_f64(&self, min: f64, max: f64) -> Number {
408 let v = self.to_f64_lossy();
409 Number::Real(Real::F64(v.clamp(min, max)))
410 }
411}
412
413fn isqrt(n: &BigInt) -> Option<BigInt> {
415 if n < &BigInt::zero() {
416 return None;
417 }
418 if n.is_zero() {
419 return Some(BigInt::zero());
420 }
421 let bits = n.bits();
422 let mut x = BigInt::one() << bits.div_ceil(2);
423 loop {
424 let y = (&x + n / &x) >> 1;
425 if y >= x {
426 break;
427 }
428 x = y;
429 }
430 if &x * &x == *n {
431 Some(x)
432 } else {
433 None
434 }
435}
436
437impl From<i32> for Number {
438 fn from(v: i32) -> Number {
439 Number::Integer(BigInt::from(v))
440 }
441}
442
443impl From<i64> for Number {
444 fn from(v: i64) -> Number {
445 Number::Integer(BigInt::from(v))
446 }
447}
448
449impl From<f64> for Number {
450 fn from(v: f64) -> Number {
451 Number::Real(Real::F64(v))
452 }
453}
454
455fn to_rational(n: &Number) -> Number {
456 match n {
457 Number::Integer(i) => Number::Rational(BigRational::new(i.clone(), BigInt::one())),
458 Number::Rational(_) => n.clone(),
459 _ => unreachable!("to_rational called on non-rational"),
460 }
461}
462
463fn normalized(numer: BigInt, denom: BigInt) -> Number {
464 if denom == BigInt::one() {
465 Number::Integer(numer)
466 } else {
467 Number::Rational(BigRational::new(numer, denom))
468 }
469}
470
471fn to_f64(n: &Number) -> Number {
472 match n {
473 Number::Integer(i) => Number::Real(Real::F64(i.to_f64().unwrap_or(f64::NAN))),
474 Number::Rational(r) => Number::Real(Real::F64(r.to_f64().unwrap_or(f64::NAN))),
475 Number::Real(Real::F32(f)) => Number::Real(Real::F64(*f as f64)),
476 Number::Real(Real::F64(f)) => Number::Real(Real::F64(*f)),
477 _ => unreachable!("to_f64 called on complex"),
478 }
479}
480
481fn to_real(n: &Number, like: &Real) -> Number {
482 let v = match n {
483 Number::Integer(i) => i.to_f64().unwrap_or(f64::NAN),
484 Number::Rational(r) => r.to_f64().unwrap_or(f64::NAN),
485 Number::Real(Real::F32(f)) => *f as f64,
486 Number::Real(Real::F64(f)) => *f,
487 _ => unreachable!("to_real called on complex"),
488 };
489 match like {
490 Real::F32(_) => Number::Real(Real::F32(v as f32)),
491 Real::F64(_) => Number::Real(Real::F64(v)),
492 }
493}
494
495fn convert_to(n: &Number, like: &Number) -> Number {
496 match like {
497 Number::Rational(_) => to_rational(n),
498 Number::Real(Real::F64(_)) => to_f64(n),
499 Number::Real(Real::F32(_)) => to_real(n, &Real::F32(0.0)),
500 _ => n.clone(),
501 }
502}
503
504fn zero_like(like: &Number) -> Number {
505 match like {
506 Number::Integer(_) => Number::Integer(BigInt::zero()),
507 Number::Rational(_) => Number::Rational(BigRational::new(BigInt::zero(), BigInt::one())),
508 Number::Real(Real::F32(_)) => Number::Real(Real::F32(0.0)),
509 Number::Real(Real::F64(_)) => Number::Real(Real::F64(0.0)),
510 Number::Complex { re, im } => Number::Complex { re: Box::new(zero_like(re)), im: Box::new(zero_like(im)) },
511 other => zero_like(&normalize(other.clone())),
513 }
514}
515
516fn normalize(n: Number) -> Number {
520 match n {
521 Number::I8(v) => Number::Integer(BigInt::from(v)),
522 Number::I16(v) => Number::Integer(BigInt::from(v)),
523 Number::I32(v) => Number::Integer(BigInt::from(v)),
524 Number::I64(v) => Number::Integer(BigInt::from(v)),
525 Number::I128(v) => Number::Integer(BigInt::from(v)),
526 Number::U8(v) => Number::Integer(BigInt::from(v)),
527 Number::U16(v) => Number::Integer(BigInt::from(v)),
528 Number::U32(v) => Number::Integer(BigInt::from(v)),
529 Number::U64(v) => Number::Integer(BigInt::from(v)),
530 Number::U128(v) => Number::Integer(BigInt::from(v)),
531 Number::Isize(v) => Number::Integer(BigInt::from(v)),
532 Number::Usize(v) => Number::Integer(BigInt::from(v)),
533 Number::BigFloat(f) => Number::Real(Real::F64(f)),
534 other => other,
535 }
536}
537
538fn exact_integer(n: &Number) -> Option<BigInt> {
541 match n {
542 Number::Integer(i) => Some(i.clone()),
543 Number::Rational(r) if *r.denom() == BigInt::one() => Some(r.numer().clone()),
544 Number::Real(Real::F64(f)) if f.fract() == 0.0 && (*f as i64) as f64 == *f => Some(BigInt::from(*f as i64)),
545 Number::Real(Real::F32(f)) if f.fract() == 0.0 && (*f as i64) as f64 == *f as f64 => Some(BigInt::from(*f as i64)),
546 Number::I8(v) => Some(BigInt::from(*v)),
547 Number::I16(v) => Some(BigInt::from(*v)),
548 Number::I32(v) => Some(BigInt::from(*v)),
549 Number::I64(v) => Some(BigInt::from(*v)),
550 Number::I128(v) => Some(BigInt::from(*v)),
551 Number::U8(v) => Some(BigInt::from(*v)),
552 Number::U16(v) => Some(BigInt::from(*v)),
553 Number::U32(v) => Some(BigInt::from(*v)),
554 Number::U64(v) => Some(BigInt::from(*v)),
555 Number::U128(v) => Some(BigInt::from(*v)),
556 Number::Isize(v) => Some(BigInt::from(*v)),
557 Number::Usize(v) => Some(BigInt::from(*v)),
558 Number::BigFloat(f) if f.fract() == 0.0 && (*f as i64) as f64 == *f => Some(BigInt::from(*f as i64)),
559 _ => None,
560 }
561}
562
563fn promote_real(a: &Number, b: &Number) -> (Number, Number) {
564 let a = normalize(a.clone());
565 let b = normalize(b.clone());
566 match (&a, &b) {
567 (Number::Integer(_), Number::Integer(_)) => (a.clone(), b.clone()),
568 (Number::Rational(_), Number::Rational(_)) => (a.clone(), b.clone()),
569 (Number::Integer(_), Number::Rational(_)) | (Number::Rational(_), Number::Integer(_)) => {
570 (to_rational(&a), to_rational(&b))
571 }
572 (Number::Real(Real::F32(_)), Number::Real(Real::F32(_))) => (a.clone(), b.clone()),
573 (Number::Real(Real::F64(_)), Number::Real(Real::F64(_))) => (a.clone(), b.clone()),
574 (Number::Real(Real::F64(_)), Number::Real(Real::F32(_))) | (Number::Real(Real::F32(_)), Number::Real(Real::F64(_))) => {
575 (to_f64(&a), to_f64(&b))
576 }
577 (Number::Real(x), Number::Integer(_)) | (Number::Real(x), Number::Rational(_)) => {
578 (a.clone(), to_real(&b, x))
579 }
580 (Number::Integer(_), Number::Real(x)) | (Number::Rational(_), Number::Real(x)) => {
581 (to_real(&a, x), b.clone())
582 }
583 (Number::Complex { .. }, _) | (_, Number::Complex { .. }) => unreachable!("complex promoted by caller"),
584 _ => unreachable!("fixed-width variants must be normalized before promote_real"),
586 }
587}
588
589pub fn promote(a: &Number, b: &Number) -> (Number, Number) {
594 let a = normalize(a.clone());
595 let b = normalize(b.clone());
596 use Number::*;
597 let a_complex = matches!(&a, Complex { .. });
598 let b_complex = matches!(&b, Complex { .. });
599 match (a_complex, b_complex) {
600 (false, false) => promote_real(&a, &b),
601 (true, true) => {
602 let (Complex { re: rea, im: ima }, Complex { re: reb, im: imb }) = (a, b) else {
603 unreachable!()
604 };
605 let (nrea, nreb) = promote_real(&rea, &reb);
606 let (nima, nimb) = promote_real(&ima, &imb);
607 (
608 Complex { re: Box::new(nrea), im: Box::new(nima) },
609 Complex { re: Box::new(nreb), im: Box::new(nimb) },
610 )
611 }
612 (true, false) => {
613 let Complex { re, im } = a else { unreachable!() };
614 let (nre, nb) = promote_real(&re, &b);
615 let nima = convert_to(&im, &nre);
616 let nb_c = Complex { re: Box::new(nb), im: Box::new(zero_like(&nima)) };
617 (Complex { re: Box::new(nre), im: Box::new(nima) }, nb_c)
618 }
619 (false, true) => {
620 let Complex { re, im } = b else { unreachable!() };
621 let (na, nre) = promote_real(&a, &re);
622 let nima = convert_to(&im, &nre);
623 let na_c = Complex { re: Box::new(na), im: Box::new(zero_like(&nima)) };
624 (na_c, Complex { re: Box::new(nre), im: Box::new(nima) })
625 }
626 }
627}
628
629fn add_real(a: Real, b: Real) -> Real {
630 match (a, b) {
631 (Real::F32(x), Real::F32(y)) => Real::F32(x + y),
632 _ => {
633 let x = match a {
634 Real::F32(f) => f as f64,
635 Real::F64(f) => f,
636 };
637 let y = match b {
638 Real::F32(f) => f as f64,
639 Real::F64(f) => f,
640 };
641 Real::F64(x + y)
642 }
643 }
644}
645
646fn mul_real(a: Real, b: Real) -> Real {
647 match (a, b) {
648 (Real::F32(x), Real::F32(y)) => Real::F32(x * y),
649 _ => {
650 let x = match a {
651 Real::F32(f) => f as f64,
652 Real::F64(f) => f,
653 };
654 let y = match b {
655 Real::F32(f) => f as f64,
656 Real::F64(f) => f,
657 };
658 Real::F64(x * y)
659 }
660 }
661}
662
663fn div_real(a: Real, b: Real) -> Real {
664 match (a, b) {
665 (Real::F32(x), Real::F32(y)) => Real::F32(x / y),
666 _ => {
667 let x = match a {
668 Real::F32(f) => f as f64,
669 Real::F64(f) => f,
670 };
671 let y = match b {
672 Real::F32(f) => f as f64,
673 Real::F64(f) => f,
674 };
675 Real::F64(x / y)
676 }
677 }
678}
679
680fn checked_denominator(n: &Number) -> Result<(), CoreError> {
681 if n.is_zero() {
682 Err(CoreError::DivisionByZero)
683 } else {
684 Ok(())
685 }
686}
687
688fn complex_div(a: Number, b: Number, c: Number, d: Number) -> Number {
689 let c2 = c.clone() * c.clone();
690 let d2 = d.clone() * d.clone();
691 let denom = c2 + d2;
692 checked_denominator(&denom).expect("division by zero");
693 let re = (a.clone() * c.clone() + b.clone() * d.clone()) / denom.clone();
694 let im = (b * c - a * d) / denom;
695 Number::Complex { re: Box::new(re), im: Box::new(im) }
696}
697
698impl std::ops::Add for Number {
699 type Output = Number;
700 fn add(self, rhs: Number) -> Number {
701 let (a, b) = promote(&normalize(self), &normalize(rhs));
702 use Number::*;
703 match (a, b) {
704 (Integer(x), Integer(y)) => Integer(x + y),
705 (Rational(x), Rational(y)) => { let r = x + y; normalized(r.numer().clone(), r.denom().clone()) },
706 (Real(x), Real(y)) => Real(add_real(x, y)),
707 (Complex { re, im }, Complex { re: u, im: v }) => Complex { re: Box::new(*re + *u), im: Box::new(*im + *v) },
708 _ => unreachable!("promote must align operands"),
709 }
710 }
711}
712
713impl std::ops::Sub for Number {
714 type Output = Number;
715 fn sub(self, rhs: Number) -> Number {
716 let (a, b) = promote(&normalize(self), &normalize(rhs));
717 match (a, b) {
718 (Number::Integer(x), Number::Integer(y)) => Number::Integer(x - y),
719 (Number::Rational(x), Number::Rational(y)) => {
720 let r = x - y;
721 normalized(r.numer().clone(), r.denom().clone())
722 }
723 (Number::Real(rx), Number::Real(ry)) => match (rx, ry) {
724 (Real::F32(x), Real::F32(y)) => Number::Real(Real::F32(x - y)),
725 _ => {
726 let x = match rx {
727 Real::F32(f) => f as f64,
728 Real::F64(f) => f,
729 };
730 let y = match ry {
731 Real::F32(f) => f as f64,
732 Real::F64(f) => f,
733 };
734 Number::Real(Real::F64(x - y))
735 }
736 },
737 (Number::Complex { re, im }, Number::Complex { re: u, im: v }) => {
738 Number::Complex { re: Box::new(*re - *u), im: Box::new(*im - *v) }
739 }
740 _ => unreachable!("promote must align operands"),
741 }
742 }
743}
744
745impl std::ops::Mul for Number {
746 type Output = Number;
747 fn mul(self, rhs: Number) -> Number {
748 let (a, b) = promote(&normalize(self), &normalize(rhs));
749 use Number::*;
750 match (a, b) {
751 (Integer(x), Integer(y)) => Integer(x * y),
752 (Rational(x), Rational(y)) => { let r = x * y; normalized(r.numer().clone(), r.denom().clone()) },
753 (Real(x), Real(y)) => Real(mul_real(x, y)),
754 (Complex { re, im }, Complex { re: u, im: v }) => {
755 let re_new = *re.clone() * *u.clone() - *im.clone() * *v.clone();
756 let im_new = *re * *v + *im * *u;
757 Complex { re: Box::new(re_new), im: Box::new(im_new) }
758 }
759 _ => unreachable!("promote must align operands"),
760 }
761 }
762}
763
764impl std::ops::Div for Number {
765 type Output = Number;
766 fn div(self, rhs: Number) -> Number {
767 let (a, b) = promote(&normalize(self), &normalize(rhs));
768 use Number::*;
769 match (a, b) {
770 (Integer(x), Integer(y)) => {
771 if y.is_zero() {
772 panic!("division by zero");
773 }
774 normalized(x, y)
775 }
776 (Rational(x), Rational(y)) => {
777 if y.is_zero() {
778 panic!("division by zero");
779 }
780 let r = x / y;
781 normalized(r.numer().clone(), r.denom().clone())
782 }
783 (Real(x), Real(y)) => Real(div_real(x, y)),
784 (Complex { re, im }, Complex { re: u, im: v }) => complex_div(*re, *im, *u, *v),
785 _ => unreachable!("promote must align operands"),
786 }
787 }
788}
789
790impl std::ops::Neg for Number {
791 type Output = Number;
792 fn neg(self) -> Number {
793 match normalize(self) {
794 Number::Integer(i) => Number::Integer(-i),
795 Number::Rational(r) => Number::Rational(-r),
796 Number::Real(Real::F32(f)) => Number::Real(Real::F32(-f)),
797 Number::Real(Real::F64(f)) => Number::Real(Real::F64(-f)),
798 Number::Complex { re, im } => Number::Complex { re: Box::new(-*re), im: Box::new(-*im) },
799 _ => unreachable!("normalize returns only the exact/Real/complex layer"),
800 }
801 }
802}
803
804impl fmt::Display for Real {
805 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
806 match self {
807 Real::F32(v) => write!(f, "{v}"),
808 Real::F64(v) => write!(f, "{v}"),
809 }
810 }
811}
812
813impl fmt::Display for Number {
814 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
815 match self {
816 Number::Integer(i) => write!(f, "{i}"),
817 Number::Rational(r) => write!(f, "{}/{}", r.numer(), r.denom()),
818 Number::Real(r) => write!(f, "{r}"),
819 Number::Complex { re, im } => write!(f, "{re} + {im}i"),
820 Number::I8(v) => write!(f, "{v}"),
821 Number::I16(v) => write!(f, "{v}"),
822 Number::I32(v) => write!(f, "{v}"),
823 Number::I64(v) => write!(f, "{v}"),
824 Number::I128(v) => write!(f, "{v}"),
825 Number::U8(v) => write!(f, "{v}"),
826 Number::U16(v) => write!(f, "{v}"),
827 Number::U32(v) => write!(f, "{v}"),
828 Number::U64(v) => write!(f, "{v}"),
829 Number::U128(v) => write!(f, "{v}"),
830 Number::Isize(v) => write!(f, "{v}"),
831 Number::Usize(v) => write!(f, "{v}"),
832 Number::BigFloat(x) => write!(f, "{x}"),
833 }
834 }
835}