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