1
1
//! Calculations and processing of numeric values.
2
2
3
+ use std:: cmp;
3
4
use std:: cmp:: Ordering ;
4
5
use std:: ops:: Rem ;
5
6
@@ -23,6 +24,9 @@ pub fn module() -> Module {
23
24
scope. define ( "cosh" , cosh) ;
24
25
scope. define ( "tanh" , tanh) ;
25
26
scope. define ( "log" , log) ;
27
+ scope. define ( "fact" , fact) ;
28
+ scope. define ( "perm" , perm) ;
29
+ scope. define ( "binom" , binom) ;
26
30
scope. define ( "floor" , floor) ;
27
31
scope. define ( "ceil" , ceil) ;
28
32
scope. define ( "round" , round) ;
@@ -404,6 +408,128 @@ pub fn log(
404
408
Value :: Float ( result)
405
409
}
406
410
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
+
407
533
/// Round a number down to the nearest integer.
408
534
///
409
535
/// If the number is already an integer, it is returned unchanged.
0 commit comments