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