Skip to main content

factorion_lib/
calculation_tasks.rs

1//! This module handles the calculation of pending calculation tasks
2
3use std::ops::ControlFlow;
4
5use factorion_math::rug::Integer;
6use factorion_math::rug::float::OrdFloat;
7use factorion_math::rug::ops::AddFrom;
8#[cfg(any(feature = "serde", test))]
9use serde::{Deserialize, Serialize};
10
11use crate::calculation_results::Number;
12
13use crate::Consts;
14use crate::{
15    calculation_results::{Calculation, CalculationResult},
16    math,
17};
18
19use crate::rug::{Float, ops::Pow};
20
21pub mod recommended {
22    use factorion_math::rug::Complete;
23    use factorion_math::rug::integer::IntegerExt64;
24
25    use crate::rug::Integer;
26    // Limit for exact calculation, set to limit calculation time
27    pub static UPPER_CALCULATION_LIMIT: fn() -> Integer = || 1_000_000.into();
28    // Limit for approximation, set to ensure enough accuracy (5 decimals)
29    pub static UPPER_APPROXIMATION_LIMIT: fn() -> Integer =
30        || Integer::u64_pow_u64(10, 300).complete();
31    // Limit for exact subfactorial calculation, set to limit calculation time
32    pub static UPPER_SUBFACTORIAL_LIMIT: fn() -> Integer = || 100_000.into();
33    // Limit for exact termial calculation, set to limit calculation time (absurdly high)
34    pub static UPPER_TERMIAL_LIMIT: fn() -> Integer = || Integer::u64_pow_u64(10, 10000).complete();
35    // Limit for approximation, set to ensure enough accuracy (5 decimals)
36    // Based on max float. (bits)
37    pub static UPPER_TERMIAL_APPROXIMATION_LIMIT: u32 = 1073741822;
38}
39
40/// Representation of the calculation to be done
41#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
42#[cfg_attr(any(feature = "serde", test), derive(Serialize, Deserialize))]
43pub struct CalculationJob {
44    pub base: CalculationBase,
45    /// Type of the calculation
46    pub level: i32,
47    /// Number of negations encountered
48    pub negative: u32,
49}
50/// The basis of a calculation, whether [Number] or [CalculationJob].
51#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
52#[cfg_attr(any(feature = "serde", test), derive(Serialize, Deserialize))]
53pub enum CalculationBase {
54    Num(Number),
55    Calc(Box<CalculationJob>),
56}
57
58impl CalculationJob {
59    /// Execute the calculation. \
60    /// If include_steps is enabled, will return all intermediate results.
61    pub fn execute(self, include_steps: bool, consts: &Consts) -> Vec<Option<Calculation>> {
62        let CalculationJob {
63            mut base,
64            mut level,
65            mut negative,
66        } = self;
67        let size = {
68            let mut n = 1;
69            let mut b = &base;
70            while let CalculationBase::Calc(inner) = b {
71                n += 1;
72                b = &inner.base;
73            }
74            n
75        };
76        // TODO: Maybe ignore include steps if size is too big (we can't respond properly anyway)
77        let mut steps = Vec::with_capacity(size);
78        let mut calcs = loop {
79            match base {
80                CalculationBase::Num(num) => {
81                    break vec![
82                        calculate_appropriate_factorial(num.clone(), level, negative, consts).map(
83                            |res| Calculation {
84                                value: num,
85                                steps: vec![(level, negative % 2 == 1)],
86                                result: res,
87                            },
88                        ),
89                    ];
90                }
91                CalculationBase::Calc(calc) => {
92                    steps.push((level, negative));
93                    CalculationJob {
94                        base,
95                        level,
96                        negative,
97                    } = *calc;
98                }
99            }
100        };
101        for (i, (level, negative)) in steps.into_iter().rev().enumerate() {
102            let calc = if include_steps && i < 30 {
103                calcs.last().cloned()
104            } else {
105                calcs.pop()
106            };
107            match calc {
108                Some(Some(Calculation {
109                    result: res,
110                    mut steps,
111                    value: number,
112                })) => {
113                    let factorial = calculate_appropriate_factorial(res, level, negative, consts)
114                        .map(|res| {
115                            steps.push((level, negative % 2 == 1));
116                            Calculation {
117                                value: number,
118                                steps,
119                                result: res,
120                            }
121                        });
122                    calcs.push(factorial);
123                }
124                _ => return calcs,
125            };
126        }
127        calcs
128    }
129}
130
131fn calculate_appropriate_factorial(
132    num: Number,
133    level: i32,
134    negative: u32,
135    consts: &Consts,
136) -> Option<CalculationResult> {
137    let prec = consts.float_precision;
138    let calc_num = match num {
139        CalculationResult::ComplexInfinity => return Some(CalculationResult::ComplexInfinity),
140        Number::Float(num) | CalculationResult::Approximate(num, _)
141            if !num.as_float().is_finite() =>
142        {
143            return Some(CalculationResult::ComplexInfinity);
144        }
145        CalculationResult::Approximate(base, exponent) => {
146            match calculate_or_extract_approximate(level, consts, prec, base, exponent) {
147                ControlFlow::Continue(value) => value,
148                ControlFlow::Break(value) => return value,
149            }
150        }
151
152        CalculationResult::ApproximateDigits(was_neg, digits) => {
153            match calculate_or_extract_approximate_digits(level, consts, prec, was_neg, digits) {
154                ControlFlow::Continue(value) => value,
155                ControlFlow::Break(value) => return value,
156            }
157        }
158        CalculationResult::ApproximateDigitsTower(was_neg, neg, depth, exponent) => {
159            return calculate_approximate_digits_tower(level, prec, was_neg, neg, depth, exponent);
160        }
161        Number::Float(num) => match calculate_or_extract_float(level, negative, num) {
162            ControlFlow::Continue(num) => num,
163            ControlFlow::Break(val) => return val,
164        },
165        Number::Exact(num) if num.significant_bits() >= math::rug::float::exp_max() as u32 => {
166            return calculate_exact_as_approximate(level, negative, consts, num);
167        }
168        Number::Exact(num) => num,
169    };
170    Some(if level > 0 {
171        calculate_k_factorial(level, negative, consts, prec, &calc_num)?
172    } else if level == 0 {
173        calculate_subfactorial(negative, consts, prec, &calc_num)
174    } else if level < 0 {
175        calculate_termial(level, negative, consts, prec, calc_num)
176    } else {
177        unreachable!()
178    })
179}
180
181fn calculate_termial(
182    level: i32,
183    negative: u32,
184    consts: &Consts<'_>,
185    prec: u32,
186    calc_num: Integer,
187) -> CalculationResult {
188    if calc_num.significant_bits() > consts.upper_termial_approximation_limit {
189        let termial = math::approximate_termial_digits(calc_num, level.unsigned_abs(), prec);
190        CalculationResult::ApproximateDigits(!negative.is_multiple_of(2), termial)
191    } else if *calc_num.as_abs() > consts.upper_termial_limit {
192        let termial = math::approximate_termial(calc_num, level.unsigned_abs(), prec);
193        CalculationResult::Approximate(
194            ((termial.0 * if !negative.is_multiple_of(2) { -1 } else { 1 }) as Float).into(),
195            termial.1,
196        )
197    } else {
198        let termial = if level < -1 {
199            math::multitermial(calc_num, level.unsigned_abs())
200        } else {
201            math::termial(calc_num)
202        };
203        let termial = termial * if !negative.is_multiple_of(2) { -1 } else { 1 };
204        CalculationResult::Exact(termial)
205    }
206}
207
208fn calculate_subfactorial(
209    negative: u32,
210    consts: &Consts<'_>,
211    prec: u32,
212    calc_num: &Integer,
213) -> CalculationResult {
214    if *calc_num < 0 {
215        CalculationResult::ComplexInfinity
216    } else if *calc_num > consts.upper_approximation_limit {
217        let factorial = math::approximate_multifactorial_digits(calc_num.clone(), 1, prec);
218        CalculationResult::ApproximateDigits(!negative.is_multiple_of(2), factorial)
219    } else if *calc_num > consts.upper_subfactorial_limit {
220        let factorial = math::approximate_subfactorial(calc_num.clone(), prec);
221        CalculationResult::Approximate(
222            ((factorial.0 * if !negative.is_multiple_of(2) { -1 } else { 1 }) as Float).into(),
223            factorial.1,
224        )
225    } else {
226        let calc_num = calc_num
227            .to_u64()
228            .unwrap_or_else(|| panic!("Failed to convert BigInt to u64: {calc_num}"));
229        let factorial =
230            math::subfactorial(calc_num) * if !negative.is_multiple_of(2) { -1 } else { 1 };
231        CalculationResult::Exact(factorial)
232    }
233}
234
235fn calculate_k_factorial(
236    level: i32,
237    negative: u32,
238    consts: &Consts<'_>,
239    prec: u32,
240    calc_num: &Integer,
241) -> Option<CalculationResult> {
242    Some(if *calc_num < 0 && level == 1 {
243        CalculationResult::ComplexInfinity
244    } else if *calc_num < 0 {
245        let factor = math::negative_multifacorial_factor(calc_num.clone(), level);
246        match (factor, -level - 1 > *calc_num) {
247            (Some(factor), true) => {
248                let mut res = calculate_appropriate_factorial(
249                    Number::Exact(-calc_num.clone() - level),
250                    level,
251                    negative,
252                    consts,
253                )?;
254                res = match res {
255                    CalculationResult::Exact(n) => {
256                        let n = Float::with_val(prec, n);
257                        CalculationResult::Float((factor / n).into())
258                    }
259                    CalculationResult::Approximate(b, e) => {
260                        let (b, e) = math::adjust_approximate((factor / Float::from(b), -e));
261                        CalculationResult::Approximate(b.into(), e)
262                    }
263                    CalculationResult::ApproximateDigits(wn, n) => {
264                        CalculationResult::ApproximateDigits(wn, -n)
265                    }
266                    CalculationResult::ApproximateDigitsTower(wn, negative, depth, base) => {
267                        CalculationResult::ApproximateDigitsTower(wn, !negative, depth, base)
268                    }
269                    CalculationResult::ComplexInfinity => CalculationResult::Exact(0.into()),
270                    CalculationResult::Float(f) => {
271                        CalculationResult::Float((factor / Float::from(f)).into())
272                    }
273                };
274
275                res
276            }
277            (factor, _) => factor
278                .map(CalculationResult::Exact)
279                .unwrap_or(CalculationResult::ComplexInfinity),
280        }
281        // Check if we can approximate the number of digits
282    } else if *calc_num > consts.upper_approximation_limit {
283        let factorial =
284            math::approximate_multifactorial_digits(calc_num.clone(), level as u32, prec);
285        CalculationResult::ApproximateDigits(!negative.is_multiple_of(2), factorial)
286    // Check if the number is within a reasonable range to compute
287    } else if *calc_num > consts.upper_calculation_limit {
288        let factorial = if level == 0 {
289            math::approximate_factorial(calc_num.clone(), prec)
290        } else {
291            math::approximate_multifactorial(calc_num.clone(), level as u32, prec)
292        };
293        CalculationResult::Approximate(
294            ((factorial.0 * if !negative.is_multiple_of(2) { -1 } else { 1 }) as Float).into(),
295            factorial.1,
296        )
297    } else {
298        let calc_num = calc_num
299            .to_u64()
300            .unwrap_or_else(|| panic!("Failed to convert BigInt to u64: {calc_num}"));
301        let factorial = math::factorial(calc_num, level as u32)
302            * if !negative.is_multiple_of(2) { -1 } else { 1 };
303        CalculationResult::Exact(factorial)
304    })
305}
306
307fn calculate_exact_as_approximate(
308    level: i32,
309    negative: u32,
310    consts: &Consts<'_>,
311    num: Integer,
312) -> Option<CalculationResult> {
313    let sig_bits = num.significant_bits();
314    calculate_appropriate_factorial(
315        CalculationResult::Approximate(
316            (Float::with_val(consts.float_precision, num / consts.float_precision)
317                / (sig_bits - consts.float_precision))
318                .into(),
319            sig_bits.into(),
320        ),
321        level,
322        negative,
323        consts,
324    )
325}
326
327fn calculate_or_extract_float(
328    level: i32,
329    negative: u32,
330    num: OrdFloat,
331) -> ControlFlow<Option<CalculationResult>, Integer> {
332    ControlFlow::Continue(match level {
333        ..-1 => {
334            // We don't support multitermials of decimals
335            return ControlFlow::Break(None);
336        }
337        -1 => {
338            let res: Float = math::fractional_termial(num.as_float().clone())
339                * if !negative.is_multiple_of(2) { -1 } else { 1 };
340            if res.is_finite() {
341                return ControlFlow::Break(Some(CalculationResult::Float(res.into())));
342            } else {
343                let Some(num) = num.as_float().to_integer() else {
344                    return ControlFlow::Break(None);
345                };
346                num
347            }
348        }
349        0 => {
350            // We don't support subfactorials of deciamals
351            return ControlFlow::Break(None);
352        }
353        1 => {
354            let res: Float = math::fractional_factorial(num.as_float().clone())
355                * if !negative.is_multiple_of(2) { -1 } else { 1 };
356            if res.is_finite() {
357                return ControlFlow::Break(Some(CalculationResult::Float(res.into())));
358            } else {
359                let Some(num) = num.as_float().to_integer() else {
360                    return ControlFlow::Break(None);
361                };
362                num
363            }
364        }
365        2.. => {
366            let res: Float = math::fractional_multifactorial(num.as_float().clone(), level as u32)
367                * if !negative.is_multiple_of(2) { -1 } else { 1 };
368            if res.is_finite() {
369                return ControlFlow::Break(Some(CalculationResult::Float(res.into())));
370            } else {
371                let Some(num) = num.as_float().to_integer() else {
372                    return ControlFlow::Break(None);
373                };
374                num
375            }
376        }
377    })
378}
379
380fn calculate_approximate_digits_tower(
381    level: i32,
382    prec: u32,
383    was_neg: bool,
384    neg: bool,
385    depth: Integer,
386    exponent: Integer,
387) -> Option<CalculationResult> {
388    Some(if neg {
389        CalculationResult::Float(Float::new(prec).into())
390    } else if was_neg {
391        CalculationResult::ComplexInfinity
392    } else if level < 0 {
393        CalculationResult::ApproximateDigitsTower(false, false, depth, exponent)
394    } else {
395        CalculationResult::ApproximateDigitsTower(false, false, depth + 1, exponent)
396    })
397}
398
399fn calculate_or_extract_approximate_digits(
400    level: i32,
401    consts: &Consts<'_>,
402    prec: u32,
403    was_neg: bool,
404    digits: Integer,
405) -> ControlFlow<Option<CalculationResult>, Integer> {
406    ControlFlow::Continue(if digits <= consts.integer_construction_limit {
407        let x: Float = Float::with_val(prec, 10).pow(digits.clone() - 1);
408        x.to_integer().unwrap()
409    } else {
410        return ControlFlow::Break(Some(if digits.is_negative() {
411            CalculationResult::Float(Float::new(prec).into())
412        } else if was_neg {
413            CalculationResult::ComplexInfinity
414        } else if level < 0 {
415            let mut one = Float::with_val(consts.float_precision, 1);
416            if was_neg {
417                one *= -1;
418            }
419            let termial = math::approximate_approx_termial((one, digits), -level as u32);
420            if termial.0 == 1 {
421                CalculationResult::ApproximateDigits(false, termial.1)
422            } else {
423                CalculationResult::Approximate(termial.0.into(), termial.1)
424            }
425        } else {
426            let mut digits = digits;
427            digits.add_from(math::length(&digits, prec));
428            CalculationResult::ApproximateDigitsTower(false, false, 1.into(), digits)
429        }));
430    })
431}
432
433fn calculate_or_extract_approximate(
434    level: i32,
435    consts: &Consts<'_>,
436    prec: u32,
437    base: OrdFloat,
438    exponent: Integer,
439) -> ControlFlow<Option<CalculationResult>, Integer> {
440    ControlFlow::Continue(if exponent <= consts.integer_construction_limit {
441        let x: Float = base.as_float() * Float::with_val(prec, 10).pow(&exponent);
442        x.to_integer().unwrap()
443    } else {
444        return ControlFlow::Break(Some(if base.as_float() < &0.0 {
445            CalculationResult::ComplexInfinity
446        } else if level < 0 {
447            let termial =
448                math::approximate_approx_termial((Float::from(base), exponent), -level as u32);
449            if termial.0 == 1 {
450                CalculationResult::ApproximateDigits(false, termial.1)
451            } else {
452                CalculationResult::Approximate(termial.0.into(), termial.1)
453            }
454        } else {
455            let mut exponent = exponent;
456            exponent.add_from(math::length(&exponent, prec));
457            CalculationResult::ApproximateDigitsTower(false, false, 1.into(), exponent)
458        }));
459    })
460}
461#[cfg(test)]
462mod tests {
463    use super::*;
464    use factorion_math::recommended::FLOAT_PRECISION;
465
466    #[test]
467    fn test_unsupported_calcs() {
468        let consts = Consts::default();
469        // Subfactorial
470        let job = CalculationJob {
471            base: CalculationBase::Num(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into())),
472            level: 0,
473            negative: 0,
474        };
475        assert_eq!(job.execute(false, &consts), vec![None]);
476        // Multitermial
477        let job = CalculationJob {
478            base: CalculationBase::Num(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into())),
479            level: -2,
480            negative: 0,
481        };
482        assert_eq!(job.execute(false, &consts), vec![None]);
483        let job = CalculationJob {
484            base: CalculationBase::Num(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into())),
485            level: -51,
486            negative: 0,
487        };
488        assert_eq!(job.execute(false, &consts), vec![None]);
489    }
490}