suan_core/
subject.rs

1use num_bigint::{BigInt, BigUint};
2use num_complex::Complex64;
3use num_irrational::{BigQuadratic, Quadratic64};
4use num_rational::{BigRational, Rational64};
5use num_traits::{FromPrimitive, ToPrimitive};
6use std::convert::TryInto;
7
8/// A general `suan` object (su-bject) containing all possible numeric types involved in the calculation
9#[derive(Clone)]
10pub enum Subject {
11    /// Positive infinity (true) or negative infinity (false).
12    /// This number should only be used as a sentinel in comparison,
13    /// arithmetic operators will panic if one of the operands is infinite.
14    Infinity(bool),
15    /// Small integer
16    Int(i64),
17    /// Multi-precision integer
18    BInt(BigInt),
19    /// Small real number
20    Real(f64),
21    /// Multi-precision real number
22    #[cfg(feature = "bigdecimal")]
23    BReal(bigdecimal::BigDecimal),
24    #[cfg(feature = "rug")]
25    BReal(rug::Real),
26    /// Rational number
27    Rational(Rational64),
28    /// Multi-precision Rational number
29    BRational(BigRational),
30    /// Complex number
31    Complex(Complex64),
32    /// Multi-precision Complex number
33    #[cfg(feature = "rug")]
34    BComplex(rug::Complex),
35    /// Quadratic number
36    Quad(Quadratic64),
37    /// Multi-precision Quadratic number
38    BQuad(BigQuadratic),
39    /// Quadratic integer
40    QuadInt(), // TODO: Quadratic Integer
41    /// Multi-precision Quadratic INteger
42    BQuadInt(),
43}
44
45// These converters will automatically downcast to smaller representation if possible.
46
47impl From<i8> for Subject {
48    fn from(v: i8) -> Self {
49        Subject::Int(v as i64)
50    }
51}
52impl From<i64> for Subject {
53    fn from(v: i64) -> Self {
54        Subject::Int(v)
55    }
56}
57impl From<u64> for Subject {
58    fn from(v: u64) -> Self {
59        match v.try_into() {
60            Ok(u) => Subject::Int(u),
61            Err(_) => Subject::BInt(BigInt::from_u64(v).unwrap()),
62        }
63    }
64}
65impl From<usize> for Subject {
66    fn from(v: usize) -> Self {
67        match v.try_into() {
68            Ok(u) => Subject::Int(u),
69            Err(_) => Subject::BInt(BigInt::from_usize(v).unwrap()),
70        }
71    }
72}
73impl From<f64> for Subject {
74    fn from(v: f64) -> Self {
75        if v.is_nan() {
76            panic!("nan is not accepted as a suan::Subject")
77        } else if v.is_infinite() {
78            Subject::Infinity(v.is_sign_positive())
79        } else {
80            Subject::Real(v)
81        }
82    }
83}
84#[cfg(feature = "bigdecimal")]
85impl From<bigdecimal::BigDecimal> for Subject {
86    fn from(v: bigdecimal::BigDecimal) -> Self {
87        // TODO: try downcasting or should we allow precision lower than f64?
88        // see MPFR and boost::multiprecision for design
89        Subject::BReal(v)
90    }
91}
92impl From<BigInt> for Subject {
93    fn from(v: BigInt) -> Self {
94        match v.to_i64() {
95            Some(s) => Subject::Int(s),
96            None => Subject::BInt(v),
97        }
98    }
99}
100impl From<BigUint> for Subject {
101    fn from(v: BigUint) -> Self {
102        match v.to_i64() {
103            Some(s) => Subject::Int(s),
104            None => Subject::BInt(BigInt::from(v)),
105        }
106    }
107}
108impl From<Rational64> for Subject {
109    fn from(v: Rational64) -> Self {
110        if v.is_integer() {
111            Subject::Int(v.to_integer())
112        } else {
113            Subject::Rational(v)
114        }
115    }
116}
117impl From<BigRational> for Subject {
118    fn from(v: BigRational) -> Self {
119        if v.is_integer() {
120            v.to_integer().into()
121        } else {
122            match (v.numer().to_i64(), v.denom().to_i64()) {
123                (Some(n), Some(d)) => Subject::Rational(Rational64::new(n, d)),
124                _ => Subject::BRational(v),
125            }
126        }
127    }
128}
129impl From<Quadratic64> for Subject {
130    fn from(v: Quadratic64) -> Self {
131        if v.is_integer() {
132            Subject::Int(v.to_integer().value())
133        } else if v.is_rational() {
134            Subject::Rational(v.to_rational().value())
135        } else {
136            Subject::Quad(v)
137        }
138    }
139}
140impl From<BigQuadratic> for Subject {
141    fn from(v: BigQuadratic) -> Self {
142        let (ra, rb, rc, rr) = v.parts();
143        match (ra.to_i64(), rb.to_i64(), rc.to_i64(), rr.to_i64()) {
144            (Some(sa), Some(sb), Some(sc), Some(sr)) => {
145                Subject::Quad(Quadratic64::new(sa, sb, sc, sr))
146            }
147            _ => Subject::BQuad(v),
148        }
149    }
150}
151impl From<Complex64> for Subject {
152    fn from(v: Complex64) -> Self {
153        if v.re.is_nan() || v.im.is_nan() {
154            panic!("nan is not accepted as a suan::Subject")
155        } else if v.re.is_infinite() {
156            // first consider infinite real part
157            Subject::Infinity(v.re.is_sign_positive())
158        } else if v.im.is_infinite() {
159            // then consider infinite image part
160            Subject::Infinity(v.im.is_sign_positive())
161        } else {
162            Subject::Complex(v)
163        }
164    }
165}