computable_real/
real.rs

1use super::{Approx, Primitive};
2use num::bigint::Sign;
3use num::{BigInt, One, Signed, ToPrimitive};
4use std::cmp::Ordering;
5use std::fmt::{self, Debug, Formatter};
6
7/// The core idea here is that we represent a real number as a function
8///  f(n: i32) -> BigInt, where n is the desired precision (typically
9///  negative).
10///
11/// The function is constructed such that |f(n)*2^n - x| < 2^n. By using
12///  an increasingly negative n, we can approximate x to arbitrary precision.
13#[derive(Clone)]
14pub struct Real {
15    pub prim: Box<Primitive>,
16    pub approx: Option<Approx>,
17}
18
19impl Debug for Real {
20    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
21        write!(f, "{:?}", self.prim)
22    }
23}
24
25const LN_MIN: i32 = 8; // 0.5
26const LN_MAX: i32 = 24; // 1.5
27
28impl Real {
29    pub fn new(prim: Primitive) -> Self {
30        Real {
31            prim: Box::new(prim),
32            approx: None,
33        }
34    }
35
36    /// Returns the most significant digit based on the current approximation.
37    /// If msd is n, then 2^(n-1) < |x| < 2^(n+1).
38    /// Returns None if no approximation has been made.
39    pub fn known_msd(&self) -> Option<i32> {
40        let res = self.approx.as_ref()?;
41        let size = res.value.abs().bits() as i32;
42        Some(res.precision + size - 1)
43    }
44
45    // TODO: understand the comparision with 1
46    /// Evaluates to the desired precision and returns the msd.
47    /// Returns None if the correct msd < precision
48    pub fn msd(&mut self, precision: i32) -> Option<i32> {
49        if let Some(res) = &self.approx {
50            if res.value.abs() > BigInt::one() {
51                return self.known_msd();
52            }
53        }
54        let res = self.appr(precision - 1);
55        if res.value.abs() <= BigInt::one() {
56            // msd could still be arbitrarily far to the right
57            None
58        } else {
59            self.known_msd()
60        }
61    }
62
63    /// Iteratively lowers the precision until the msd is found, or the
64    ///  provided precision is reached.
65    pub fn iter_msd_n(&mut self, min_precision: i32) -> Option<i32> {
66        let mut curr = 0;
67        loop {
68            let msd = self.msd(curr);
69            if msd.is_some() {
70                return msd;
71            }
72            curr = (curr * 3) / 2 - 16;
73            if curr <= min_precision + 30 {
74                break;
75            }
76        }
77        self.msd(min_precision)
78    }
79
80    /// Iteratively lowers the precision until the msd is found.
81    /// This will create an infinite loop if the value is zero.
82    pub fn iter_msd(&mut self) -> Option<i32> {
83        self.iter_msd_n(i32::MIN)
84    }
85
86    /// Generates an approximation of the real number.
87    /// If we have already approximated the number with a sufficient
88    ///  precision, we simply scale the approximation.
89    /// Otherwise we call the underlying approximate method.
90    pub fn appr(&mut self, precision: i32) -> Approx {
91        if let Some(res) = &self.approx {
92            if precision >= res.precision {
93                return res.scale_to(precision);
94            }
95        }
96        let res = self.prim.approximate(precision, self.clone());
97        self.approx = Some(res.clone());
98        res
99    }
100
101    pub fn render_base(&self, n: u32, base: u32) -> String {
102        let scaled = match base {
103            16 => self.clone().shifted(n as i32 * 4),
104            _ => self.clone() * Real::int(BigInt::from(base).pow(n)),
105        }
106        .appr(0);
107
108        let value_str = scaled.value.abs().to_str_radix(base);
109
110        let padded_str = match value_str.len() as u32 {
111            len if len <= n => format!("{}{}", "0".repeat((n - len + 1) as usize), value_str),
112            _ => value_str,
113        };
114
115        let len = padded_str.len() as u32;
116        let (whole, frac) = padded_str.split_at((len - n) as usize);
117
118        let sign = if scaled.value.sign() == Sign::Minus {
119            "-"
120        } else {
121            ""
122        };
123        let dot = if n == 0 { "" } else { "." };
124        format!("{}{}{}{}", sign, whole, dot, frac)
125    }
126
127    pub fn render(&self, n: u32) -> String {
128        self.render_base(n, 10)
129    }
130
131    pub fn cmp_ra(&mut self, other: &mut Real, rel_tol: i32, abs_tol: i32) -> Ordering {
132        let msd1 = self.iter_msd_n(abs_tol).unwrap_or(i32::MIN);
133        let msd2 = other
134            .iter_msd_n(if msd1 > abs_tol { msd1 } else { abs_tol })
135            .unwrap_or(i32::MIN);
136        let msd = msd1.max(msd2);
137        if msd == i32::MIN {
138            return Ordering::Equal;
139        }
140        let rel = msd + rel_tol;
141        self.cmp_a(other, rel.max(abs_tol))
142    }
143
144    pub fn cmp_a(&mut self, other: &mut Real, abs_tol: i32) -> Ordering {
145        let prec = abs_tol - 1;
146        let a = self.appr(prec).value;
147        let b = other.appr(prec).value;
148        a.cmp(&b)
149    }
150
151    pub fn cmp_until(&self, other: &Real, min_precision: i32) -> Ordering {
152        let mut a = -16;
153        let mut clone = self.clone();
154        let mut other = other.clone();
155        loop {
156            let cmp = clone.cmp_a(&mut other, a);
157            if cmp != Ordering::Equal || a < min_precision {
158                return cmp;
159            }
160            a *= 2;
161        }
162    }
163
164    pub fn signum_a(&self, a: i32) -> i32 {
165        if let Some(res) = &self.approx {
166            if res.value.signum() != BigInt::ZERO {
167                return res.value.signum().to_i32().unwrap();
168            }
169        }
170        let prec = a - 1;
171        self.clone().appr(prec).value.signum().to_i32().unwrap()
172    }
173}
174
175impl Real {
176    pub fn int(value: BigInt) -> Self {
177        Self::new(Primitive::Int(value))
178    }
179
180    pub fn squared(self) -> Self {
181        Self::new(Primitive::Square(self))
182    }
183
184    pub fn atan(value: BigInt) -> Self {
185        Self::new(Primitive::Atan(value))
186    }
187
188    pub fn exp(mut self) -> Self {
189        let rough_appr = self.appr(-10).value;
190        let two = BigInt::from(2);
191        if rough_appr.abs() > two {
192            self.shifted(-1).exp().squared()
193        } else {
194            Self::new(Primitive::Exp(self))
195        }
196    }
197
198    pub fn cos(mut self) -> Self {
199        let half_pi_multiples = (self.clone() / Self::pi()).appr(-1).value;
200        let two = BigInt::from(2);
201
202        if half_pi_multiples.abs() >= two {
203            let pi_multiples: BigInt = (half_pi_multiples + 1) / 2;
204            let adj = Self::pi() * Real::int(pi_multiples.clone());
205            if (pi_multiples & BigInt::one()).signum() != BigInt::ZERO {
206                -(self - adj).cos()
207            } else {
208                (self - adj).cos()
209            }
210        } else if self.appr(-1).value.abs() >= two {
211            self.shifted(-1).cos().squared().shifted(1) - Self::one()
212        } else {
213            Self::new(Primitive::Cos(self))
214        }
215    }
216
217    pub fn sin(self) -> Self {
218        (Self::pi().shifted(-1) - self).cos()
219    }
220
221    pub fn asin(mut self) -> Self {
222        let rough_appr = self.appr(-10).value;
223        if rough_appr > 750.into() {
224            Self::one() - self.squared().sqrt().acos()
225        } else if rough_appr < (-750).into() {
226            -(-self.asin())
227        } else {
228            Self::new(Primitive::Asin(self))
229        }
230    }
231
232    pub fn acos(self) -> Self {
233        Self::pi().shifted(-1) - self.asin()
234    }
235
236    pub fn ln2(self) -> Self {
237        Self::new(Primitive::Ln(self - Self::one()))
238    }
239
240    pub fn ln(mut self) -> Self {
241        let rough_appr = self.appr(-4).value;
242        if rough_appr <= LN_MIN.into() {
243            -((Self::one() / self).ln())
244        } else if rough_appr >= LN_MAX.into() {
245            if rough_appr <= 64.into() {
246                self.sqrt().sqrt().ln().shifted(2)
247            } else {
248                let ln2_1 =
249                    Real::int(7.into()) * (Real::int(10.into()) / Real::int(9.into())).ln2();
250                let ln2_2 =
251                    Real::int(2.into()) * (Real::int(25.into()) / Real::int(24.into())).ln2();
252                let ln2_3 =
253                    Real::int(3.into()) * (Real::int(81.into()) / Real::int(80.into())).ln2();
254                let ln2 = ln2_1 - ln2_2 + ln2_3;
255                let extra_bits = rough_appr.bits() as i32 - 3;
256                self.shifted(-extra_bits).ln() + Real::int(extra_bits.into()) * ln2
257            }
258        } else {
259            self.ln2()
260        }
261    }
262
263    pub fn sqrt(self) -> Self {
264        Self::new(Primitive::Sqrt(self))
265    }
266
267    pub fn shifted(self, bits: i32) -> Self {
268        Self::new(Primitive::Shift(self, bits))
269    }
270
271    /// John Machin's formula from 1706:
272    /// pi/4 = 4 * atan(1/5) - atan(1/239)
273    pub fn pi() -> Self {
274        let atan5 = Real::atan(BigInt::from(5));
275        let atan239 = Real::atan(BigInt::from(239));
276        let four = Real::int(BigInt::from(4));
277        let four_atan5 = four.clone() * atan5;
278        let diff = four_atan5 - atan239;
279        diff * four
280    }
281
282    pub fn inv(self) -> Self {
283        Self::new(Primitive::Inv(self))
284    }
285}