1#[cfg(feature = "exact")]
4use core::cmp::Ordering;
5
6#[cfg(feature = "decimal")]
7use crate::Decimal;
8#[cfg(feature = "exact")]
9use crate::Exact;
10#[cfg(feature = "fraction")]
11use crate::Fraction;
12use crate::{Context, Error, ErrorKind, Evaluator, Number};
13
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
72pub struct Math;
73
74const FUNCTIONS: [&str; 32] = [
76 "sqrt", "cbrt", "abs", "sign", "exp", "ln", "log2", "log10", "log", "sin", "cos", "tan",
77 "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "floor",
78 "ceil", "round", "trunc", "pow", "hypot", "min", "max", "clamp", "fact",
79];
80
81#[cfg(feature = "libm")]
83fn finite<T: Into<f64>>(value: T) -> bool {
84 value.into().is_finite()
85}
86
87#[cfg(feature = "libm")]
92macro_rules! math {
93 (
94 $type:ident in $module:ident,
95 unary: [$($unary:ident = $unary_libm:ident),* $(,)?],
96 binary: [$($binary:ident = $binary_libm:ident),* $(,)?] $(,)?
97 ) => {
98 #[doc = concat!("`libm` functions for `", stringify!($type), "`, under the names expressions use.")]
99 mod $module {
100 $(
101 #[doc = concat!("Calls `libm::", stringify!($unary_libm), "`.")]
102 pub(super) fn $unary(x: $type) -> $type {
103 libm::$unary_libm(x)
104 })*
105 $(
106 #[doc = concat!("Calls `libm::", stringify!($binary_libm), "`.")]
107 pub(super) fn $binary(x: $type, y: $type) -> $type {
108 libm::$binary_libm(x, y)
109 })*
110 }
111
112 impl Context<$type> for Math {
113 fn variable(&self, name: &str) -> Option<$type> {
114 use core::$type::consts;
115 match name {
116 "pi" | "PI" => Some(consts::PI),
117 "tau" => Some(consts::TAU),
118 "e" | "E" => Some(consts::E),
119 _ => None,
120 }
121 }
122
123 fn call(&self, name: &str, arguments: &[$type]) -> Option<Result<$type, ErrorKind>> {
124 use $module::*;
125 let domain = |valid: bool, value: $type| if valid { Ok(value) } else { Err(ErrorKind::Domain) };
126 let result = match (name, arguments) {
127 ("sqrt", &[x]) => domain(x >= 0.0, sqrt(x)),
128 ("cbrt", &[x]) => Ok(cbrt(x)),
129 ("abs", &[x]) => Ok(x.abs()),
130 ("sign", &[x]) => domain(!x.is_nan(), if x == 0.0 { 0.0 } else { x.signum() }),
131 ("exp", &[x]) => Ok(exp(x)),
132 ("ln", &[x]) => domain(x > 0.0, ln(x)),
133 ("log2", &[x]) => domain(x > 0.0, log2(x)),
134 ("log10" | "log", &[x]) => domain(x > 0.0, log10(x)),
135 ("log", &[x, base]) => domain(x > 0.0 && base > 0.0 && base != 1.0, ln(x) / ln(base)),
136 ("sin", &[x]) => Ok(sin(x)),
137 ("cos", &[x]) => Ok(cos(x)),
138 ("tan", &[x]) => Ok(tan(x)),
139 ("asin", &[x]) => domain((-1.0..=1.0).contains(&x), asin(x)),
140 ("acos", &[x]) => domain((-1.0..=1.0).contains(&x), acos(x)),
141 ("atan", &[x]) => Ok(atan(x)),
142 ("atan2", &[y, x]) => Ok(atan2(y, x)),
143 ("sinh", &[x]) => Ok(sinh(x)),
144 ("cosh", &[x]) => Ok(cosh(x)),
145 ("tanh", &[x]) => Ok(tanh(x)),
146 ("asinh", &[x]) => Ok(asinh(x)),
147 ("acosh", &[x]) => domain(x >= 1.0, acosh(x)),
148 ("atanh", &[x]) => domain(x > -1.0 && x < 1.0, atanh(x)),
149 ("floor", &[x]) => Ok(floor(x)),
150 ("ceil", &[x]) => Ok(ceil(x)),
151 ("round", &[x]) => Ok(round(x)),
152 ("trunc", &[x]) => Ok(trunc(x)),
153 ("pow", &[x, y]) => return Some(x.power(y)),
154 ("fact", &[x]) => return Some(x.factorial()),
155 ("hypot", &[x, y]) => Ok(hypot(x, y)),
156 ("min", &[first, ref rest @ ..]) => Ok(rest.iter().fold(first, |a, b| a.min(*b))),
157 ("max", &[first, ref rest @ ..]) => Ok(rest.iter().fold(first, |a, b| a.max(*b))),
158 ("clamp", &[x, low, high]) => domain(low <= high, x.max(low).min(high)),
159 _ if FUNCTIONS.contains(&name) => Err(ErrorKind::ArgumentCount),
160 _ => return None,
161 };
162 Some(result.and_then(|value| {
163 if !finite(value) && arguments.iter().all(|argument| finite(*argument)) {
164 Err(if value.is_nan() { ErrorKind::Domain } else { ErrorKind::Overflow })
165 } else {
166 Ok(value)
167 }
168 }))
169 }
170 }
171 };
172}
173
174#[cfg(feature = "libm")]
175math!(
176 f32 in single,
177 unary: [
178 sqrt = sqrtf, cbrt = cbrtf, exp = expf, ln = logf, log2 = log2f, log10 = log10f,
179 sin = sinf, cos = cosf, tan = tanf, asin = asinf, acos = acosf, atan = atanf,
180 sinh = sinhf, cosh = coshf, tanh = tanhf, asinh = asinhf, acosh = acoshf, atanh = atanhf,
181 floor = floorf, ceil = ceilf, round = roundf, trunc = truncf,
182 ],
183 binary: [atan2 = atan2f, hypot = hypotf],
184);
185
186#[cfg(feature = "libm")]
187math!(
188 f64 in double,
189 unary: [
190 sqrt = sqrt, cbrt = cbrt, exp = exp, ln = log, log2 = log2, log10 = log10,
191 sin = sin, cos = cos, tan = tan, asin = asin, acos = acos, atan = atan,
192 sinh = sinh, cosh = cosh, tanh = tanh, asinh = asinh, acosh = acosh, atanh = atanh,
193 floor = floor, ceil = ceil, round = round, trunc = trunc,
194 ],
195 binary: [atan2 = atan2, hypot = hypot],
196);
197
198pub fn evaluate<N: Number>(input: &str) -> Result<N, Error>
207where
208 Math: Context<N>,
209{
210 Evaluator::<Math>::new(&Math).evaluate(input)
211}
212
213#[cfg(feature = "decimal")]
214impl<const DIGITS: u32, const MAX_EXPONENT: i32> Context<Decimal<DIGITS, MAX_EXPONENT>> for Math {
215 fn variable(&self, name: &str) -> Option<Decimal<DIGITS, MAX_EXPONENT>> {
216 let literal = match name {
217 "pi" | "PI" => "3.14159265358979323846264338327950288",
218 "tau" => "6.28318530717958647692528676655900577",
219 "e" | "E" => "2.71828182845904523536028747135266250",
220 _ => return None,
221 };
222 Number::parse(literal)
223 }
224
225 fn call(
226 &self,
227 name: &str,
228 arguments: &[Decimal<DIGITS, MAX_EXPONENT>],
229 ) -> Option<Result<Decimal<DIGITS, MAX_EXPONENT>, ErrorKind>> {
230 let result = match (name, arguments) {
231 ("abs", &[x]) => Ok(x.abs()),
232 ("sign", &[x]) if x.is_zero() => Ok(x),
233 ("sign", &[x]) => Decimal::new(if x.is_negative() { -1 } else { 1 }, 0),
234 ("floor", &[x]) => x.floor(),
235 ("ceil", &[x]) => x.ceil(),
236 ("round", &[x]) => x.round(),
237 ("trunc", &[x]) => x.trunc(),
238 ("pow", &[x, y]) => x.power(y),
239 ("fact", &[x]) => x.factorial(),
240 ("sqrt", &[x]) => x.sqrt(),
241 ("cbrt", &[x]) => x.cbrt(),
242 ("exp", &[x]) => x.exp(),
243 ("ln", &[x]) => x.ln(),
244 ("log2", &[x]) => x.log2(),
245 ("log10" | "log", &[x]) => x.log10(),
246 ("log", &[x, base]) => x.log(base),
247 ("sin", &[x]) => x.sin(),
248 ("cos", &[x]) => x.cos(),
249 ("tan", &[x]) => x.tan(),
250 ("asin", &[x]) => x.asin(),
251 ("acos", &[x]) => x.acos(),
252 ("atan", &[x]) => x.atan(),
253 ("atan2", &[y, x]) => y.atan2(x),
254 ("sinh", &[x]) => x.sinh(),
255 ("cosh", &[x]) => x.cosh(),
256 ("tanh", &[x]) => x.tanh(),
257 ("asinh", &[x]) => x.asinh(),
258 ("acosh", &[x]) => x.acosh(),
259 ("atanh", &[x]) => x.atanh(),
260 ("hypot", &[x, y]) => x.hypot(y),
261 ("min", &[first, ref rest @ ..]) => Ok(rest.iter().fold(first, |a, b| a.min(*b))),
262 ("max", &[first, ref rest @ ..]) => Ok(rest.iter().fold(first, |a, b| a.max(*b))),
263 ("clamp", &[x, low, high]) if low <= high => Ok(x.clamp(low, high)),
264 ("clamp", &[_, _, _]) => Err(ErrorKind::Domain),
265 _ if FUNCTIONS.contains(&name) => Err(ErrorKind::ArgumentCount),
266 _ => return None,
267 };
268 Some(result)
269 }
270}
271
272#[cfg(feature = "fraction")]
275const INEXACT: [(&str, usize); 21] = [
276 ("exp", 1),
277 ("ln", 1),
278 ("log2", 1),
279 ("log10", 1),
280 ("log", 1),
281 ("log", 2),
282 ("sin", 1),
283 ("cos", 1),
284 ("tan", 1),
285 ("asin", 1),
286 ("acos", 1),
287 ("atan", 1),
288 ("atan2", 2),
289 ("sinh", 1),
290 ("cosh", 1),
291 ("tanh", 1),
292 ("asinh", 1),
293 ("acosh", 1),
294 ("atanh", 1),
295 ("sqrt", 1),
296 ("cbrt", 1),
297];
298
299#[cfg(feature = "fraction")]
300impl Context<Fraction> for Math {
301 fn call(&self, name: &str, arguments: &[Fraction]) -> Option<Result<Fraction, ErrorKind>> {
302 let result = match (name, arguments) {
303 ("abs", &[x]) => Ok(x.abs()),
304 ("sign", &[x]) => Ok(x.sign()),
305 ("floor", &[x]) => Ok(x.floor()),
306 ("ceil", &[x]) => Ok(x.ceil()),
307 ("round", &[x]) => Ok(x.round()),
308 ("trunc", &[x]) => Ok(x.trunc()),
309 ("pow", &[x, y]) => x.power(y),
310 ("fact", &[x]) => x.factorial(),
311 ("sqrt", &[x]) => x.root(2),
312 ("cbrt", &[x]) => x.root(3),
313 ("hypot", &[x, y]) => x
314 .multiply(x)
315 .and_then(|square| square.add(y.multiply(y)?))
316 .and_then(|sum| sum.root(2)),
317 ("min", &[first, ref rest @ ..]) => Ok(rest.iter().fold(first, |a, b| a.min(*b))),
318 ("max", &[first, ref rest @ ..]) => Ok(rest.iter().fold(first, |a, b| a.max(*b))),
319 ("clamp", &[x, low, high]) if low <= high => Ok(x.clamp(low, high)),
320 ("clamp", &[_, _, _]) => Err(ErrorKind::Domain),
321 _ if INEXACT.contains(&(name, arguments.len())) => Err(ErrorKind::Inexact),
322 _ if FUNCTIONS.contains(&name) => Err(ErrorKind::ArgumentCount),
323 _ => return None,
324 };
325 Some(result)
326 }
327}
328
329#[cfg(feature = "exact")]
330impl<const DIGITS: u32, const MAX_EXPONENT: i32> Context<Exact<DIGITS, MAX_EXPONENT>> for Math {
331 fn variable(&self, name: &str) -> Option<Exact<DIGITS, MAX_EXPONENT>> {
332 Context::<Decimal<DIGITS, MAX_EXPONENT>>::variable(self, name).map(Exact::from)
333 }
334
335 fn call(
336 &self,
337 name: &str,
338 arguments: &[Exact<DIGITS, MAX_EXPONENT>],
339 ) -> Option<Result<Exact<DIGITS, MAX_EXPONENT>, ErrorKind>> {
340 let pick = |first, rest: &[Exact<DIGITS, MAX_EXPONENT>], keep: Ordering| {
341 rest.iter().fold(first, |chosen, candidate| {
342 if candidate.cmp_value(chosen) == keep {
343 *candidate
344 } else {
345 chosen
346 }
347 })
348 };
349 match (name, arguments) {
350 ("min", &[first, ref rest @ ..]) => return Some(Ok(pick(first, rest, Ordering::Less))),
351 ("max", &[first, ref rest @ ..]) => {
352 return Some(Ok(pick(first, rest, Ordering::Greater)));
353 }
354 ("clamp", &[x, low, high]) => {
355 return Some(
356 match (low.cmp_value(high), x.cmp_value(low), x.cmp_value(high)) {
357 (Ordering::Greater, _, _) => Err(ErrorKind::Domain),
358 (_, Ordering::Less, _) => Ok(low),
359 (_, _, Ordering::Greater) => Ok(high),
360 _ => Ok(x),
361 },
362 );
363 }
364 _ => {}
365 }
366 if arguments.len() > 3 {
367 return FUNCTIONS
368 .contains(&name)
369 .then_some(Err(ErrorKind::ArgumentCount));
370 }
371 let mut fractions = [Fraction::ZERO; 3];
372 let mut exact = true;
373 for (slot, argument) in fractions.iter_mut().zip(arguments) {
374 match argument.as_fraction() {
375 Some(fraction) => *slot = fraction,
376 None => exact = false,
377 }
378 }
379 if exact {
380 match Context::<Fraction>::call(self, name, &fractions[..arguments.len()]) {
381 Some(Err(ErrorKind::Overflow | ErrorKind::Inexact)) | None => {}
382 result => return result.map(|result| result.map(Exact::from)),
383 }
384 }
385 let mut decimals = [Decimal::ZERO; 3];
386 for (slot, argument) in decimals.iter_mut().zip(arguments) {
387 *slot = argument.to_decimal();
388 }
389 Context::<Decimal<DIGITS, MAX_EXPONENT>>::call(self, name, &decimals[..arguments.len()])
390 .map(|result| result.map(Exact::from))
391 }
392}