mathhook_core/functions/
number_theory_eval.rs

1//! Number theory function implementations
2
3use crate::core::constants::EPSILON;
4use crate::core::{Expression, Number};
5use num_integer::Integer;
6
7/// Least common multiple (LCM)
8///
9/// # Mathematical Definition
10///
11/// lcm(a, b) = |a × b| / gcd(a, b)
12///
13/// # Arguments
14///
15/// * `a` - First expression
16/// * `b` - Second expression
17///
18/// # Returns
19///
20/// LCM expression
21///
22/// # Examples
23///
24/// ```
25/// use mathhook_core::functions::number_theory_eval::lcm;
26/// use mathhook_core::expr;
27///
28/// let result = lcm(&expr!(12), &expr!(18));
29/// assert_eq!(result, expr!(36));
30/// ```
31pub fn lcm(a: &Expression, b: &Expression) -> Expression {
32    match (a, b) {
33        (
34            Expression::Number(Number::Integer(a_val)),
35            Expression::Number(Number::Integer(b_val)),
36        ) => {
37            if *a_val == 0 || *b_val == 0 {
38                Expression::integer(0)
39            } else {
40                let gcd_val = a_val.gcd(b_val);
41                Expression::integer((a_val * b_val).abs() / gcd_val)
42            }
43        }
44        _ => Expression::function("lcm", vec![a.clone(), b.clone()]),
45    }
46}
47
48/// Modulo operation
49///
50/// # Mathematical Definition
51///
52/// a mod b = remainder when a is divided by b
53///
54/// # Arguments
55///
56/// * `a` - Dividend expression
57/// * `b` - Divisor expression
58///
59/// # Returns
60///
61/// Modulo expression
62///
63/// # Examples
64///
65/// ```
66/// use mathhook_core::functions::number_theory_eval::modulo;
67/// use mathhook_core::expr;
68///
69/// let result = modulo(&expr!(17), &expr!(5));
70/// assert_eq!(result, expr!(2));
71/// ```
72pub fn modulo(a: &Expression, b: &Expression) -> Expression {
73    match (a, b) {
74        (
75            Expression::Number(Number::Integer(a_val)),
76            Expression::Number(Number::Integer(b_val)),
77        ) if *b_val != 0 => Expression::integer(a_val % b_val),
78        (Expression::Number(Number::Float(a_val)), Expression::Number(Number::Float(b_val)))
79            if b_val.abs() >= EPSILON =>
80        {
81            Expression::float(a_val % b_val)
82        }
83        _ => Expression::function("mod", vec![a.clone(), b.clone()]),
84    }
85}
86
87/// Primality test
88///
89/// # Mathematical Definition
90///
91/// isprime(n) = true if n is prime, false otherwise
92///
93/// # Arguments
94///
95/// * `arg` - Expression to test for primality
96///
97/// # Returns
98///
99/// Boolean expression (1 for true, 0 for false) or symbolic
100///
101/// # Examples
102///
103/// ```
104/// use mathhook_core::functions::number_theory_eval::isprime;
105/// use mathhook_core::expr;
106///
107/// assert_eq!(isprime(&expr!(2)), expr!(1));
108/// assert_eq!(isprime(&expr!(17)), expr!(1));
109/// assert_eq!(isprime(&expr!(4)), expr!(0));
110/// ```
111pub fn isprime(arg: &Expression) -> Expression {
112    match arg {
113        Expression::Number(Number::Integer(n)) if *n <= 1 => Expression::integer(0),
114        Expression::Number(Number::Integer(2)) => Expression::integer(1),
115        Expression::Number(Number::Integer(n)) if n % 2 == 0 => Expression::integer(0),
116        Expression::Number(Number::Integer(n)) if *n > 0 => {
117            Expression::integer(if is_prime_trial_division(*n) { 1 } else { 0 })
118        }
119        _ => Expression::function("isprime", vec![arg.clone()]),
120    }
121}
122
123fn is_prime_trial_division(n: i64) -> bool {
124    if n <= 1 {
125        return false;
126    }
127    if n == 2 {
128        return true;
129    }
130    if n % 2 == 0 {
131        return false;
132    }
133
134    let sqrt_n = (n as f64).sqrt() as i64 + 1;
135    for i in (3..=sqrt_n).step_by(2) {
136        if n % i == 0 {
137            return false;
138        }
139    }
140    true
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_lcm() {
149        assert_eq!(
150            lcm(&Expression::integer(12), &Expression::integer(18)),
151            Expression::integer(36)
152        );
153        assert_eq!(
154            lcm(&Expression::integer(4), &Expression::integer(6)),
155            Expression::integer(12)
156        );
157    }
158
159    #[test]
160    fn test_modulo() {
161        assert_eq!(
162            modulo(&Expression::integer(17), &Expression::integer(5)),
163            Expression::integer(2)
164        );
165        assert_eq!(
166            modulo(&Expression::integer(10), &Expression::integer(3)),
167            Expression::integer(1)
168        );
169    }
170
171    #[test]
172    fn test_isprime() {
173        assert_eq!(isprime(&Expression::integer(2)), Expression::integer(1));
174        assert_eq!(isprime(&Expression::integer(17)), Expression::integer(1));
175        assert_eq!(isprime(&Expression::integer(19)), Expression::integer(1));
176        assert_eq!(isprime(&Expression::integer(4)), Expression::integer(0));
177        assert_eq!(isprime(&Expression::integer(9)), Expression::integer(0));
178        assert_eq!(isprime(&Expression::integer(1)), Expression::integer(0));
179    }
180}