11//! Calculations and processing of numeric values.
22
3+ use std:: cmp;
34use std:: cmp:: Ordering ;
45use std:: ops:: Rem ;
56
@@ -23,6 +24,9 @@ pub fn module() -> Module {
2324 scope. define ( "cosh" , cosh) ;
2425 scope. define ( "tanh" , tanh) ;
2526 scope. define ( "log" , log) ;
27+ scope. define ( "fact" , fact) ;
28+ scope. define ( "perm" , perm) ;
29+ scope. define ( "binom" , binom) ;
2630 scope. define ( "floor" , floor) ;
2731 scope. define ( "ceil" , ceil) ;
2832 scope. define ( "round" , round) ;
@@ -404,6 +408,128 @@ pub fn log(
404408 Value :: Float ( result)
405409}
406410
411+ /// Calculate the factorial of a number.
412+ ///
413+ /// ## Example
414+ /// ```example
415+ /// #calc.fact(5)
416+ /// ```
417+ ///
418+ /// Display: Factorial
419+ /// Category: calculate
420+ /// Returns: integer
421+ #[ func]
422+ pub fn fact (
423+ /// The number whose factorial to calculate. Must be positive.
424+ number : Spanned < u64 > ,
425+ ) -> Value {
426+ let result = factorial_range ( 1 , number. v ) . and_then ( |r| i64:: try_from ( r) . ok ( ) ) ;
427+
428+ match result {
429+ None => bail ! ( number. span, "the factorial result is too large" ) ,
430+ Some ( s) => Value :: Int ( s) ,
431+ }
432+ }
433+
434+ /// Calculates the product of a range of numbers. Used to calculate permutations.
435+ /// Returns None if the result is larger than `u64::MAX`
436+ fn factorial_range ( start : u64 , end : u64 ) -> Option < u64 > {
437+ // By convention
438+ if end + 1 < start {
439+ return Some ( 0 ) ;
440+ }
441+
442+ let mut count: u64 = 1 ;
443+ let real_start: u64 = cmp:: max ( 1 , start) ;
444+
445+ for i in real_start..=end {
446+ count = count. checked_mul ( i) ?;
447+ }
448+ Some ( count)
449+ }
450+
451+ /// Calculate a permutation.
452+ ///
453+ /// ## Example
454+ /// ```example
455+ /// #calc.perm(10,5)
456+ /// ```
457+ ///
458+ /// Display: Permutation
459+ /// Category: calculate
460+ /// Returns: integer
461+ #[ func]
462+ pub fn perm (
463+ /// The base number. Must be positive.
464+ base : Spanned < u64 > ,
465+ /// The number of permutations. Must be positive.
466+ numbers : Spanned < u64 > ,
467+ ) -> Value {
468+ let base_parsed = base. v ;
469+ let numbers_parsed = numbers. v ;
470+
471+ let result = if base_parsed + 1 > numbers_parsed {
472+ factorial_range ( base_parsed - numbers_parsed + 1 , base_parsed)
473+ . and_then ( |value| i64:: try_from ( value) . ok ( ) )
474+ } else {
475+ // By convention
476+ Some ( 0 )
477+ } ;
478+
479+ match result {
480+ None => bail ! ( base. span, "the permutation result is too large" ) ,
481+ Some ( s) => Value :: Int ( s) ,
482+ }
483+ }
484+
485+ /// Calculate a binomial coefficient.
486+ ///
487+ /// ## Example
488+ /// ```example
489+ /// #calc.binom(10,5)
490+ /// ```
491+ ///
492+ /// Display: Permutation
493+ /// Category: calculate
494+ /// Returns: integer
495+ #[ func]
496+ pub fn binom (
497+ /// The upper coefficient. Must be positive
498+ n : Spanned < u64 > ,
499+ /// The lower coefficient. Must be positive.
500+ k : Spanned < u64 > ,
501+ ) -> Value {
502+ let result = binomial ( n. v , k. v ) . and_then ( |raw| i64:: try_from ( raw) . ok ( ) ) ;
503+
504+ match result {
505+ None => bail ! ( n. span, "the binomial result is too large" ) ,
506+ Some ( r) => Value :: Int ( r) ,
507+ }
508+ }
509+
510+ /// Calculates a binomial coefficient, with `n` the upper coefficient and `k` the lower coefficient.
511+ /// Returns `None` if the result is larger than `u64::MAX`
512+ fn binomial ( n : u64 , k : u64 ) -> Option < u64 > {
513+ if k > n {
514+ return Some ( 0 ) ;
515+ }
516+
517+ // By symmetry
518+ let real_k = cmp:: min ( n - k, k) ;
519+
520+ if real_k == 0 {
521+ return Some ( 1 ) ;
522+ }
523+
524+ let mut result: u64 = 1 ;
525+
526+ for i in 0 ..real_k {
527+ result = result. checked_mul ( n - i) . and_then ( |r| r. checked_div ( i + 1 ) ) ?;
528+ }
529+
530+ Some ( result)
531+ }
532+
407533/// Round a number down to the nearest integer.
408534///
409535/// If the number is already an integer, it is returned unchanged.
0 commit comments