suan_core/
property.rs

1use crate::{subject::Subject::{self, *}, modular::ModularSubject, cont_frac::CfracSubject};
2use bigdecimal::ToPrimitive;
3use num_bigint::BigInt;
4use num_integer::Integer;
5use num_irrational::Quadratic64;
6use num_traits::{One, Pow, Zero};
7use std::fmt::{Debug, Display, Formatter};
8
9// short print int to first 8 digits
10fn sprinti(f: &mut Formatter<'_>, i: &i64) -> std::fmt::Result {
11    if i.abs() >= 100000000 {
12        write!(f, "{:.7E}", i)
13    } else {
14        write!(f, "{}", i)
15    }
16}
17
18// short print bigint to first 8 digits
19fn sprintbi(f: &mut Formatter<'_>, i: &BigInt) -> std::fmt::Result {
20    // FIXME: use standard formatting after https://github.com/rust-num/num-bigint/pull/214 is merged
21    // TODO: print as `XXXX..XXXX`
22    let decimal_digits = (i.bits() as f64) / 10f64.log2();
23    if decimal_digits > 7. {
24        let digits = decimal_digits.floor() as u32;
25        write!(
26            f,
27            "{}E{:+}",
28            (i / BigInt::from(10u8).pow(digits - 7)).to_f64().unwrap() / 10f64.pow(7),
29            digits
30        )
31    } else {
32        write!(f, "{}", i)
33    }
34}
35
36// short print float to first 8 digits
37fn sprintf(f: &mut Formatter<'_>, v: &f64) -> std::fmt::Result {
38    if v.abs() >= 1e7 || v.abs() <= 1e-4 {
39        write!(f, "{:.7E}", v)
40    } else {
41        let s = v.to_string();
42        if s.len() > 9 {
43            f.write_str(&s[..9])
44        } else {
45            f.write_str(&s)
46        }
47    }
48}
49
50// short print big float to first 8 digits
51#[cfg(feature = "bigdecimal")]
52fn sprintbf(f: &mut Formatter<'_>, v: &bigdecimal::BigDecimal) -> std::fmt::Result {
53    // TODO: print as `XXXX..XXXX`
54    let (mantissa, exp) = v.as_bigint_and_exponent();
55    let mantissa_digits = (mantissa.bits() as f64) / 10f64.log2();
56    let decimal_digits = mantissa_digits - exp as f64;
57    if decimal_digits >= 7. || decimal_digits < -4. {
58        let digits = decimal_digits.floor() as i32;
59        write!(
60            f,
61            "{}E{:+}",
62            (mantissa / BigInt::from(10u8).pow(mantissa_digits.floor() as u32 - 7))
63                .to_f64()
64                .unwrap()
65                / 10f64.pow(7),
66            digits
67        )
68    } else {
69        let s = v.to_string();
70        if s.len() > 9 {
71            f.write_str(&s[..9])
72        } else {
73            f.write_str(&s)
74        }
75    }
76}
77
78impl Debug for Subject {
79    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
80        match self {
81            Infinity(v) => {
82                if *v {
83                    write!(f, "INF+")
84                } else {
85                    write!(f, "INF-")
86                }
87            }
88            Int(v) => {
89                write!(f, "INT ")?;
90                sprinti(f, v)
91            }
92            BInt(v) => {
93                write!(f, "INT* ")?;
94                sprintbi(f, v)
95            }
96            Real(v) => {
97                write!(f, "REAL ")?;
98                sprintf(f, v)
99            }
100            #[cfg(feature = "bigdecimal")]
101            BReal(v) => {
102                write!(f, "REAL* ")?;
103                sprintbf(f, v)
104            }
105            Rational(v) => {
106                if v.denom().is_one() {
107                    write!(f, "RATIONAL ")?;
108                    sprinti(f, v.numer())
109                } else {
110                    write!(f, "RATIONAL ")?;
111                    sprinti(f, v.numer())?;
112                    write!(f, " / ")?;
113                    sprinti(f, v.denom())
114                }
115            }
116            Complex(v) => match (v.re == 0., v.im == 0.) {
117                (true, true) => write!(f, "COMPLEX 0"),
118                (true, false) => {
119                    write!(f, "COMPLEX ")?;
120                    sprintf(f, &v.im)?;
121                    write!(f, "i")
122                }
123                (false, true) => {
124                    write!(f, "COMPLEX ")?;
125                    sprintf(f, &v.re)
126                }
127                (false, false) => {
128                    write!(f, "COMPLEX ")?;
129                    sprintf(f, &v.re)?;
130                    if v.im > 0. {
131                        write!(f, " + ")?;
132                        if v.im != 1. {
133                            sprintf(f, &v.im)?
134                        }
135                    } else {
136                        write!(f, " - ")?;
137                        if v.im != -1. {
138                            sprintf(f, &-v.im)?
139                        }
140                    }
141                    write!(f, "i")
142                }
143            },
144            Quad(v) => {
145                let (a, b, c, r) = v.parts();
146                write!(f, "QUAD ")?;
147
148                match (a.is_zero(), b.is_zero()) {
149                    (true, true) => write!(f, "0"),
150                    (false, false) => {
151                        // numerator
152                        write!(f, "(")?;
153                        sprinti(f, a)?;
154
155                        if b > &0 {
156                            write!(f, " + ")?;
157                            sprinti(f, b)?;
158                        } else {
159                            write!(f, " - ")?;
160                            sprinti(f, &-*b)?;
161                        }
162                        write!(f, "√")?;
163                        sprinti(f, r)?;
164
165                        // denumerator
166                        if c.is_one() {
167                            Ok(())
168                        } else {
169                            write!(f, " / ")?;
170                            sprinti(f, c)
171                        }
172                    }
173                    (az, _) => {
174                        // numerator
175                        if az {
176                            match b {
177                                1 => {}
178                                -1 => write!(f, "-")?,
179                                b => sprinti(f, b)?,
180                            }
181                            write!(f, "√")?;
182                            sprinti(f, r)?;
183                        } else {
184                            sprinti(f, a)?;
185                        }
186
187                        // denumerator
188                        if c.is_one() {
189                            Ok(())
190                        } else {
191                            write!(f, " / ")?;
192                            sprinti(f, c)
193                        }
194                    }
195                }
196            }
197            _ => {
198                // TODO: currently delegate to Display, should implement with sprint functions
199                Display::fmt(self, f)
200            }
201        }
202    }
203}
204
205impl Display for Subject {
206    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
207        match self {
208            Infinity(v) => {
209                if *v {
210                    write!(f, "+inf")
211                } else {
212                    write!(f, "-inf")
213                }
214            }
215            Int(v) => Display::fmt(v, f),
216            BInt(v) => Display::fmt(v, f),
217            Real(v) => Display::fmt(v, f),
218            #[cfg(feature = "bigdecimal")]
219            BReal(v) => Display::fmt(v, f),
220            Rational(v) => Display::fmt(v, f),
221            BRational(v) => Display::fmt(v, f),
222            Quad(v) => Display::fmt(v, f),
223            BQuad(v) => Display::fmt(v, f),
224            Complex(v) => Display::fmt(v, f),
225            _ => write!(f, "..."),
226        }
227    }
228}
229
230impl Zero for Subject {
231    fn zero() -> Self {
232        Subject::Int(0)
233    }
234
235    fn is_zero(&self) -> bool {
236        match self {
237            Subject::Infinity(_) => false,
238            Subject::Int(v) => v.is_zero(),
239            Subject::BInt(v) => v.is_zero(),
240            Subject::Rational(v) => v.is_zero(),
241            Subject::BRational(v) => v.is_zero(),
242            Subject::Real(v) => v.is_zero(),
243            Subject::BReal(v) => v.is_zero(),
244            _ => unimplemented!()
245        }
246    }
247}
248
249impl One for Subject {
250    fn one() -> Self {
251        Subject::Int(1)
252    }
253
254    fn is_one(&self) -> bool {
255        match self {
256            Subject::Infinity(_) => false,
257            Subject::Int(v) => v.is_one(),
258            Subject::BInt(v) => v.is_one(),
259            Subject::Rational(v) => v.is_one(),
260            Subject::BRational(v) => v.is_one(),
261            Subject::Real(v) => v.is_one(),
262            Subject::BReal(v) => v.is_one(),
263            _ => unimplemented!()
264        }
265    }
266}
267
268pub enum Rounding {
269    Nearest,
270    Up,
271    Down,
272    ToZero,
273    AwayZero,
274}
275
276pub enum Precision {
277    /// Precision in binary bits
278    Bits(usize),
279    /// Precision in decimal digits
280    Digits(usize),
281}
282
283// conversions, precision will ensure the result is exact to at least the given position
284impl Subject {
285    // Convert the number to an integer by the specified rounding mode if necessary
286    pub fn int(self, rounding: Option<Rounding>) -> Self {
287        use std::cmp::Ordering::*;
288        use Rounding::*;
289
290        match self {
291            Int(v) => Int(v),
292            BInt(v) => BInt(v),
293            Rational(v) => {
294                let (quo, rem) = v.numer().div_rem(v.denom());
295                match (rounding.unwrap_or(Nearest), rem.cmp(&0)) {
296                    (_, Equal) | (ToZero, _) => Int(quo),
297                    (Nearest, Greater) => Int(if rem * 2 > *v.denom() { quo + 1 } else { quo }),
298                    (Nearest, Less) => Int(if rem * 2 + v.denom() > 0 { quo + 1 } else { quo }),
299                    (Up, Greater) => Int(quo + 1),
300                    (Up, Less) | (Down, Greater) => Int(quo),
301                    (Down, Less) => Int(quo - 1),
302                    (AwayZero, Greater) => Int(quo + 1),
303                    (AwayZero, Less) => Int(quo - 1),
304                }
305            },
306            _ => unimplemented!()
307        }
308    }
309    pub fn real(self, precision: Option<Precision>, rounding: Option<Rounding>) -> Self {
310        unimplemented!() // TODO: default precision from integer is f64
311    }
312    pub fn rational(self, precision: Option<Precision>, rounding: Option<Rounding>) -> Self {
313        unimplemented!() // TODO: precision of rational is the scale of the denominator
314    }
315    pub fn complex(self, precision: Option<Precision>, rounding: Option<Rounding>) -> Self {
316        unimplemented!() // TODO: panics when convert from complex to normal number
317    }
318    pub fn modulo(self, modulus: Self) -> ModularSubject { unimplemented!() }
319    pub fn quad(self, precision: Option<Precision>, rounding: Option<Rounding>) -> Self { unimplemented!() }
320    pub fn quadint(self, base: Self, rounding: Option<Rounding>) -> Self { unimplemented!() }
321    pub fn cfrac(self) -> CfracSubject { unimplemented!() }
322}
323
324impl Subject {
325    /// Get the precision of the number, the return variant depends on the underlying implementation
326    pub fn prec(&self) -> Precision {
327        unimplemented!()
328    }
329    /// Get the integral part of the number
330    pub fn trunc(&self) -> Subject {
331        unimplemented!()
332    }
333    /// Get the fractional part of the number
334    pub fn frac(&self) -> Subject {
335        unimplemented!()
336    }
337}
338
339fn abs() {}
340fn conj() {}
341fn arg() {}