1use factorion_math::rug::ops::AddFrom;
4#[cfg(any(feature = "serde", test))]
5use serde::{Deserialize, Serialize};
6
7use crate::calculation_results::Number;
8
9use crate::Consts;
10use crate::{
11 calculation_results::{Calculation, CalculationResult},
12 math,
13};
14
15use crate::rug::{Float, ops::Pow};
16
17pub mod recommended {
18 use factorion_math::rug::Complete;
19 use factorion_math::rug::integer::IntegerExt64;
20
21 use crate::rug::Integer;
22 pub static UPPER_CALCULATION_LIMIT: fn() -> Integer = || 1_000_000.into();
24 pub static UPPER_APPROXIMATION_LIMIT: fn() -> Integer =
26 || Integer::u64_pow_u64(10, 300).complete();
27 pub static UPPER_SUBFACTORIAL_LIMIT: fn() -> Integer = || 100_000.into();
29 pub static UPPER_TERMIAL_LIMIT: fn() -> Integer = || Integer::u64_pow_u64(10, 10000).complete();
31 pub static UPPER_TERMIAL_APPROXIMATION_LIMIT: u32 = 1073741822;
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
38#[cfg_attr(any(feature = "serde", test), derive(Serialize, Deserialize))]
39pub struct CalculationJob {
40 pub base: CalculationBase,
41 pub level: i32,
43 pub negative: u32,
45}
46#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
48#[cfg_attr(any(feature = "serde", test), derive(Serialize, Deserialize))]
49pub enum CalculationBase {
50 Num(Number),
51 Calc(Box<CalculationJob>),
52}
53
54impl CalculationJob {
55 pub fn execute(self, include_steps: bool, consts: &Consts) -> Vec<Option<Calculation>> {
58 let CalculationJob {
59 mut base,
60 mut level,
61 mut negative,
62 } = self;
63 let size = {
64 let mut n = 1;
65 let mut b = &base;
66 while let CalculationBase::Calc(inner) = b {
67 n += 1;
68 b = &inner.base;
69 }
70 n
71 };
72 let mut steps = Vec::with_capacity(size);
74 let mut calcs = loop {
75 match base {
76 CalculationBase::Num(num) => {
77 break vec![
78 Self::calculate_appropriate_factorial(num.clone(), level, negative, consts)
79 .map(|res| Calculation {
80 value: num,
81 steps: vec![(level, negative % 2 == 1)],
82 result: res,
83 }),
84 ];
85 }
86 CalculationBase::Calc(calc) => {
87 steps.push((level, negative));
88 CalculationJob {
89 base,
90 level,
91 negative,
92 } = *calc;
93 }
94 }
95 };
96 for (i, (level, negative)) in steps.into_iter().rev().enumerate() {
97 let calc = if include_steps && i < 30 {
98 calcs.last().cloned()
99 } else {
100 calcs.pop()
101 };
102 match calc {
103 Some(Some(Calculation {
104 result: res,
105 mut steps,
106 value: number,
107 })) => {
108 let factorial = Self::calculate_appropriate_factorial(
109 res, level, negative, consts,
110 )
111 .map(|res| {
112 steps.push((level, negative % 2 == 1));
113 Calculation {
114 value: number,
115 steps,
116 result: res,
117 }
118 });
119 calcs.push(factorial);
120 }
121 _ => return calcs,
122 };
123 }
124 calcs
125 }
126 fn calculate_appropriate_factorial(
127 num: Number,
128 level: i32,
129 negative: u32,
130 consts: &Consts,
131 ) -> Option<CalculationResult> {
132 let prec = consts.float_precision;
133 let calc_num = match num {
134 CalculationResult::ComplexInfinity => return Some(CalculationResult::ComplexInfinity),
135 Number::Float(num) | CalculationResult::Approximate(num, _)
136 if !num.as_float().is_finite() =>
137 {
138 return Some(CalculationResult::ComplexInfinity);
139 }
140 CalculationResult::Approximate(base, exponent) => {
141 if exponent <= consts.integer_construction_limit {
142 let x: Float = base.as_float() * Float::with_val(prec, 10).pow(&exponent);
143 x.to_integer().unwrap()
144 } else {
145 return Some(if base.as_float() < &0.0 {
146 CalculationResult::ComplexInfinity
147 } else if level < 0 {
148 let termial = math::approximate_approx_termial(
149 (Float::from(base), exponent),
150 -level as u32,
151 );
152 if termial.0 == 1 {
153 CalculationResult::ApproximateDigits(false, termial.1)
154 } else {
155 CalculationResult::Approximate(termial.0.into(), termial.1)
156 }
157 } else {
158 let mut exponent = exponent;
159 exponent.add_from(math::length(&exponent, prec));
160 CalculationResult::ApproximateDigitsTower(false, false, 1.into(), exponent)
161 });
162 }
163 }
164 CalculationResult::ApproximateDigits(was_neg, digits) => {
165 if digits <= consts.integer_construction_limit {
166 let x: Float = Float::with_val(prec, 10).pow(digits.clone() - 1);
167 x.to_integer().unwrap()
168 } else {
169 return Some(if digits.is_negative() {
170 CalculationResult::Float(Float::new(prec).into())
171 } else if was_neg {
172 CalculationResult::ComplexInfinity
173 } else if level < 0 {
174 let mut one = Float::with_val(consts.float_precision, 1);
175 if was_neg {
176 one *= -1;
177 }
178 let termial =
179 math::approximate_approx_termial((one, digits), -level as u32);
180 if termial.0 == 1 {
181 CalculationResult::ApproximateDigits(false, termial.1)
182 } else {
183 CalculationResult::Approximate(termial.0.into(), termial.1)
184 }
185 } else {
186 let mut digits = digits;
187 digits.add_from(math::length(&digits, prec));
188 CalculationResult::ApproximateDigitsTower(false, false, 1.into(), digits)
189 });
190 }
191 }
192 CalculationResult::ApproximateDigitsTower(was_neg, neg, depth, exponent) => {
193 return Some(if neg {
194 CalculationResult::Float(Float::new(prec).into())
195 } else if was_neg {
196 CalculationResult::ComplexInfinity
197 } else if level < 0 {
198 CalculationResult::ApproximateDigitsTower(false, false, depth, exponent)
199 } else {
200 CalculationResult::ApproximateDigitsTower(false, false, depth + 1, exponent)
201 });
202 }
203 Number::Float(num) => match level {
204 ..-1 => {
205 return None;
207 }
208 -1 => {
209 let res: Float = math::fractional_termial(num.as_float().clone())
210 * if !negative.is_multiple_of(2) { -1 } else { 1 };
211 if res.is_finite() {
212 return Some(CalculationResult::Float(res.into()));
213 } else {
214 num.as_float().to_integer()?
215 }
216 }
217 0 => {
218 return None;
220 }
221 1 => {
222 let res: Float = math::fractional_factorial(num.as_float().clone())
223 * if !negative.is_multiple_of(2) { -1 } else { 1 };
224 if res.is_finite() {
225 return Some(CalculationResult::Float(res.into()));
226 } else {
227 num.as_float().to_integer()?
228 }
229 }
230 2.. => {
231 let res: Float =
232 math::fractional_multifactorial(num.as_float().clone(), level as u32)
233 * if !negative.is_multiple_of(2) { -1 } else { 1 };
234 if res.is_finite() {
235 return Some(CalculationResult::Float(res.into()));
236 } else {
237 num.as_float().to_integer()?
238 }
239 }
240 },
241 Number::Exact(num) if num.significant_bits() >= math::rug::float::exp_max() as u32 => {
242 let sig_bits = num.significant_bits();
243 return Self::calculate_appropriate_factorial(
244 CalculationResult::Approximate(
245 (Float::with_val(consts.float_precision, num / consts.float_precision)
246 / (sig_bits - consts.float_precision))
247 .into(),
248 sig_bits.into(),
249 ),
250 level,
251 negative,
252 consts,
253 );
254 }
255 Number::Exact(num) => num,
256 };
257 if level > 0 {
258 Some(if calc_num < 0 && level == 1 {
259 CalculationResult::ComplexInfinity
260 } else if calc_num < 0 {
261 let factor = math::negative_multifacorial_factor(calc_num.clone(), level);
262 match (factor, -level - 1 > calc_num) {
263 (Some(factor), true) => {
264 let mut res = Self::calculate_appropriate_factorial(
265 Number::Exact(-calc_num.clone() - level),
266 level,
267 negative,
268 consts,
269 )?;
270 res = match res {
271 CalculationResult::Exact(n) => {
272 let n = Float::with_val(prec, n);
273 CalculationResult::Float((factor / n).into())
274 }
275 CalculationResult::Approximate(b, e) => {
276 let (b, e) =
277 math::adjust_approximate((factor / Float::from(b), -e));
278 CalculationResult::Approximate(b.into(), e)
279 }
280 CalculationResult::ApproximateDigits(wn, n) => {
281 CalculationResult::ApproximateDigits(wn, -n)
282 }
283 CalculationResult::ApproximateDigitsTower(
284 wn,
285 negative,
286 depth,
287 base,
288 ) => CalculationResult::ApproximateDigitsTower(
289 wn, !negative, depth, base,
290 ),
291 CalculationResult::ComplexInfinity => {
292 CalculationResult::Exact(0.into())
293 }
294 CalculationResult::Float(f) => {
295 CalculationResult::Float((factor / Float::from(f)).into())
296 }
297 };
298
299 res
300 }
301 (factor, _) => factor
302 .map(CalculationResult::Exact)
303 .unwrap_or(CalculationResult::ComplexInfinity),
304 }
305 } else if calc_num > consts.upper_approximation_limit {
307 let factorial =
308 math::approximate_multifactorial_digits(calc_num.clone(), level as u32, prec);
309 CalculationResult::ApproximateDigits(!negative.is_multiple_of(2), factorial)
310 } else if calc_num > consts.upper_calculation_limit {
312 let factorial = if level == 0 {
313 math::approximate_factorial(calc_num.clone(), prec)
314 } else {
315 math::approximate_multifactorial(calc_num.clone(), level as u32, prec)
316 };
317 CalculationResult::Approximate(
318 ((factorial.0 * if !negative.is_multiple_of(2) { -1 } else { 1 }) as Float)
319 .into(),
320 factorial.1,
321 )
322 } else {
323 let calc_num = calc_num
324 .to_u64()
325 .unwrap_or_else(|| panic!("Failed to convert BigInt to u64: {calc_num}"));
326 let factorial = math::factorial(calc_num, level as u32)
327 * if !negative.is_multiple_of(2) { -1 } else { 1 };
328 CalculationResult::Exact(factorial)
329 })
330 } else if level == 0 {
331 Some(if calc_num < 0 {
332 CalculationResult::ComplexInfinity
333 } else if calc_num > consts.upper_approximation_limit {
334 let factorial = math::approximate_multifactorial_digits(calc_num.clone(), 1, prec);
335 CalculationResult::ApproximateDigits(!negative.is_multiple_of(2), factorial)
336 } else if calc_num > consts.upper_subfactorial_limit {
337 let factorial = math::approximate_subfactorial(calc_num.clone(), prec);
338 CalculationResult::Approximate(
339 ((factorial.0 * if !negative.is_multiple_of(2) { -1 } else { 1 }) as Float)
340 .into(),
341 factorial.1,
342 )
343 } else {
344 let calc_num = calc_num
345 .to_u64()
346 .unwrap_or_else(|| panic!("Failed to convert BigInt to u64: {calc_num}"));
347 let factorial =
348 math::subfactorial(calc_num) * if !negative.is_multiple_of(2) { -1 } else { 1 };
349 CalculationResult::Exact(factorial)
350 })
351 } else if level < 0 {
352 Some(
353 if calc_num.significant_bits() > consts.upper_termial_approximation_limit {
354 let termial =
355 math::approximate_termial_digits(calc_num, level.unsigned_abs(), prec);
356 CalculationResult::ApproximateDigits(!negative.is_multiple_of(2), termial)
357 } else if *calc_num.as_abs() > consts.upper_termial_limit {
358 let termial = math::approximate_termial(calc_num, level.unsigned_abs(), prec);
359 CalculationResult::Approximate(
360 ((termial.0 * if !negative.is_multiple_of(2) { -1 } else { 1 }) as Float)
361 .into(),
362 termial.1,
363 )
364 } else {
365 let termial = if level < -1 {
366 math::multitermial(calc_num, level.unsigned_abs())
367 } else {
368 math::termial(calc_num)
369 };
370 let termial = termial * if !negative.is_multiple_of(2) { -1 } else { 1 };
371 CalculationResult::Exact(termial)
372 },
373 )
374 } else {
375 unreachable!()
376 }
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383 use factorion_math::recommended::FLOAT_PRECISION;
384
385 #[test]
386 fn test_unsupported_calcs() {
387 let consts = Consts::default();
388 let job = CalculationJob {
390 base: CalculationBase::Num(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into())),
391 level: 0,
392 negative: 0,
393 };
394 assert_eq!(job.execute(false, &consts), vec![None]);
395 let job = CalculationJob {
397 base: CalculationBase::Num(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into())),
398 level: -2,
399 negative: 0,
400 };
401 assert_eq!(job.execute(false, &consts), vec![None]);
402 let job = CalculationJob {
403 base: CalculationBase::Num(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into())),
404 level: -51,
405 negative: 0,
406 };
407 assert_eq!(job.execute(false, &consts), vec![None]);
408 }
409}