1use 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 pub static UPPER_CALCULATION_LIMIT: fn() -> Integer = || 1_000_000.into();
28 pub static UPPER_APPROXIMATION_LIMIT: fn() -> Integer =
30 || Integer::u64_pow_u64(10, 300).complete();
31 pub static UPPER_SUBFACTORIAL_LIMIT: fn() -> Integer = || 100_000.into();
33 pub static UPPER_TERMIAL_LIMIT: fn() -> Integer = || Integer::u64_pow_u64(10, 10000).complete();
35 pub static UPPER_TERMIAL_APPROXIMATION_LIMIT: u32 = 1073741822;
38}
39
40#[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 pub level: i32,
47 pub negative: u32,
49}
50#[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 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 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 } 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 } 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 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 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 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 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}