mathhook_core/functions/elementary/
rounding.rs1use crate::core::{Expression, Number};
4use num_traits::Signed;
5use num_traits::ToPrimitive;
6
7pub fn sign(arg: &Expression) -> Expression {
34 match arg {
35 Expression::Number(n) => evaluate_sign_number(n),
36 _ => Expression::function("sign", vec![arg.clone()]),
37 }
38}
39
40fn evaluate_sign_number(n: &Number) -> Expression {
41 match n {
42 Number::Integer(i) => Expression::integer(i.signum()),
43 Number::Float(f) => {
44 if *f > 0.0 {
45 Expression::integer(1)
46 } else if *f < 0.0 {
47 Expression::integer(-1)
48 } else {
49 Expression::integer(0)
50 }
51 }
52 Number::BigInteger(bi) => {
53 use num_bigint::Sign;
54 match bi.sign() {
55 Sign::Plus => Expression::integer(1),
56 Sign::Minus => Expression::integer(-1),
57 Sign::NoSign => Expression::integer(0),
58 }
59 }
60 Number::Rational(r) => {
61 if r.is_positive() {
62 Expression::integer(1)
63 } else if r.is_negative() {
64 Expression::integer(-1)
65 } else {
66 Expression::integer(0)
67 }
68 }
69 }
70}
71
72pub fn floor(arg: &Expression) -> Expression {
96 match arg {
97 Expression::Number(Number::Integer(i)) => Expression::integer(*i),
98 Expression::Number(Number::Float(f)) => Expression::integer(f.floor() as i64),
99 Expression::Number(Number::BigInteger(_)) => arg.clone(),
100 Expression::Number(Number::Rational(r)) => {
101 Expression::integer(r.to_f64().unwrap_or(0.0).floor() as i64)
102 }
103 _ => Expression::function("floor", vec![arg.clone()]),
104 }
105}
106
107pub fn ceil(arg: &Expression) -> Expression {
131 match arg {
132 Expression::Number(Number::Integer(i)) => Expression::integer(*i),
133 Expression::Number(Number::Float(f)) => Expression::integer(f.ceil() as i64),
134 Expression::Number(Number::BigInteger(_)) => arg.clone(),
135 Expression::Number(Number::Rational(r)) => {
136 Expression::integer(r.to_f64().unwrap_or(0.0).ceil() as i64)
137 }
138 _ => Expression::function("ceil", vec![arg.clone()]),
139 }
140}
141
142pub fn round(arg: &Expression) -> Expression {
167 match arg {
168 Expression::Number(Number::Integer(i)) => Expression::integer(*i),
169 Expression::Number(Number::Float(f)) => Expression::integer(f.round() as i64),
170 Expression::Number(Number::BigInteger(_)) => arg.clone(),
171 Expression::Number(Number::Rational(r)) => {
172 Expression::integer(r.to_f64().unwrap_or(0.0).round() as i64)
173 }
174 _ => Expression::function("round", vec![arg.clone()]),
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_sign() {
184 assert_eq!(sign(&Expression::integer(-5)), Expression::integer(-1));
185 assert_eq!(sign(&Expression::integer(0)), Expression::integer(0));
186 assert_eq!(sign(&Expression::integer(5)), Expression::integer(1));
187 }
188
189 #[test]
190 fn test_floor() {
191 assert_eq!(floor(&Expression::float(3.7)), Expression::integer(3));
192 assert_eq!(floor(&Expression::float(-2.3)), Expression::integer(-3));
193 assert_eq!(floor(&Expression::integer(5)), Expression::integer(5));
194 }
195
196 #[test]
197 fn test_ceil() {
198 assert_eq!(ceil(&Expression::float(3.2)), Expression::integer(4));
199 assert_eq!(ceil(&Expression::float(-2.7)), Expression::integer(-2));
200 assert_eq!(ceil(&Expression::integer(5)), Expression::integer(5));
201 }
202
203 #[test]
204 fn test_round() {
205 assert_eq!(round(&Expression::float(3.4)), Expression::integer(3));
206 assert_eq!(round(&Expression::float(3.6)), Expression::integer(4));
207 assert_eq!(round(&Expression::float(3.5)), Expression::integer(4));
208 assert_eq!(round(&Expression::integer(5)), Expression::integer(5));
209 }
210}