1#![allow(non_snake_case)]
2
3use crate::module::ModuleFlags;
4use crate::plugin::*;
5use crate::{def_package, Position, RhaiResultOf, ERR, INT};
6#[cfg(feature = "no_std")]
7use std::prelude::v1::*;
8
9#[cfg(not(feature = "no_float"))]
10use crate::FLOAT;
11
12#[cfg(feature = "no_std")]
13#[cfg(not(feature = "no_float"))]
14use num_traits::Float;
15
16#[cfg(feature = "decimal")]
17use rust_decimal::Decimal;
18
19#[cfg(feature = "decimal")]
20use super::arithmetic::make_err;
21
22macro_rules! gen_conversion_as_functions {
23 ($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => {
24 pub mod $root { $(pub mod $arg_type {
25 use super::super::*;
26
27 #[export_fn]
28 #[allow(clippy::missing_const_for_fn)]
29 pub fn $func_name(x: $arg_type) -> $result_type {
30 x as $result_type
31 }
32 })* }
33 }
34}
35
36#[cfg(feature = "decimal")]
37macro_rules! gen_conversion_into_functions {
38 ($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => {
39 pub mod $root { $(pub mod $arg_type {
40 use super::super::*;
41
42 #[export_fn]
43 pub fn $func_name(x: $arg_type) -> $result_type {
44 x.into()
45 }
46 })* }
47 }
48}
49
50macro_rules! reg_functions {
51 ($mod_name:ident += $root:ident :: $func_name:ident ( $($arg_type:ident),+ ) ) => { $(
52 set_exported_fn!($mod_name, stringify!($func_name), $root::$arg_type::$func_name);
53 )* }
54}
55
56def_package! {
57 pub BasicMathPackage(lib) {
59 lib.flags |= ModuleFlags::STANDARD_LIB;
60
61 combine_with_exported_module!(lib, "int", int_functions);
63
64 reg_functions!(lib += basic_to_int::to_int(char));
65
66 #[cfg(not(feature = "only_i32"))]
67 #[cfg(not(feature = "only_i64"))]
68 {
69 reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64));
70
71 #[cfg(not(target_family = "wasm"))]
72
73 reg_functions!(lib += num_128_to_int::to_int(i128, u128));
74 }
75
76 #[cfg(not(feature = "no_float"))]
77 {
78 combine_with_exported_module!(lib, "float", float_functions);
80
81 combine_with_exported_module!(lib, "trig", trig_functions);
83
84 reg_functions!(lib += basic_to_float::to_float(INT));
85
86 #[cfg(not(feature = "only_i32"))]
87 #[cfg(not(feature = "only_i64"))]
88 {
89 reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32));
90
91 #[cfg(not(target_family = "wasm"))]
92
93 reg_functions!(lib += num_128_to_float::to_float(i128, u128));
94 }
95 }
96
97 #[cfg(feature = "decimal")]
99 {
100 combine_with_exported_module!(lib, "decimal", decimal_functions);
101
102 reg_functions!(lib += basic_to_decimal::to_decimal(INT));
103
104 #[cfg(not(feature = "only_i32"))]
105 #[cfg(not(feature = "only_i64"))]
106 reg_functions!(lib += numbers_to_decimal::to_decimal(i8, u8, i16, u16, i32, u32, i64, u64));
107 }
108 }
109}
110
111#[export_module]
112mod int_functions {
113 #[rhai_fn(name = "parse_int", return_raw)]
123 pub fn parse_int(string: &str) -> RhaiResultOf<INT> {
124 parse_int_radix(string, 10)
125 }
126 #[rhai_fn(name = "parse_int", return_raw)]
142 pub fn parse_int_radix(string: &str, radix: INT) -> RhaiResultOf<INT> {
143 if !(2..=36).contains(&radix) {
144 return Err(
145 ERR::ErrorArithmetic(format!("Invalid radix: '{radix}'"), Position::NONE).into(),
146 );
147 }
148
149 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
150 INT::from_str_radix(string.trim(), radix as u32).map_err(|err| {
151 ERR::ErrorArithmetic(
152 format!("Error parsing integer number '{string}': {err}"),
153 Position::NONE,
154 )
155 .into()
156 })
157 }
158}
159
160#[cfg(not(feature = "no_float"))]
161#[export_module]
162mod trig_functions {
163 pub fn sin(x: FLOAT) -> FLOAT {
165 x.sin()
166 }
167 pub fn cos(x: FLOAT) -> FLOAT {
169 x.cos()
170 }
171 pub fn tan(x: FLOAT) -> FLOAT {
173 x.tan()
174 }
175 pub fn sinh(x: FLOAT) -> FLOAT {
177 x.sinh()
178 }
179 pub fn cosh(x: FLOAT) -> FLOAT {
181 x.cosh()
182 }
183 pub fn tanh(x: FLOAT) -> FLOAT {
185 x.tanh()
186 }
187 pub fn asin(x: FLOAT) -> FLOAT {
189 x.asin()
190 }
191 pub fn acos(x: FLOAT) -> FLOAT {
193 x.acos()
194 }
195 pub fn atan(x: FLOAT) -> FLOAT {
197 x.atan()
198 }
199 #[rhai_fn(name = "atan")]
201 pub fn atan2(x: FLOAT, y: FLOAT) -> FLOAT {
202 x.atan2(y)
203 }
204 pub fn asinh(x: FLOAT) -> FLOAT {
206 x.asinh()
207 }
208 pub fn acosh(x: FLOAT) -> FLOAT {
210 x.acosh()
211 }
212 pub fn atanh(x: FLOAT) -> FLOAT {
214 x.atanh()
215 }
216 pub fn hypot(x: FLOAT, y: FLOAT) -> FLOAT {
218 x.hypot(y)
219 }
220}
221
222#[cfg(not(feature = "no_float"))]
223#[export_module]
224mod float_functions {
225 #[rhai_fn(name = "E")]
227 pub const fn e() -> FLOAT {
228 #[cfg(not(feature = "f32_float"))]
229 return std::f64::consts::E;
230 #[cfg(feature = "f32_float")]
231 return std::f32::consts::E;
232 }
233 #[rhai_fn(name = "PI")]
235 pub const fn pi() -> FLOAT {
236 #[cfg(not(feature = "f32_float"))]
237 return std::f64::consts::PI;
238 #[cfg(feature = "f32_float")]
239 return std::f32::consts::PI;
240 }
241 pub fn to_radians(x: FLOAT) -> FLOAT {
243 x.to_radians()
244 }
245 pub fn to_degrees(x: FLOAT) -> FLOAT {
247 x.to_degrees()
248 }
249 pub fn sqrt(x: FLOAT) -> FLOAT {
251 x.sqrt()
252 }
253 pub fn exp(x: FLOAT) -> FLOAT {
255 x.exp()
256 }
257 pub fn ln(x: FLOAT) -> FLOAT {
259 x.ln()
260 }
261 pub fn log(x: FLOAT, base: FLOAT) -> FLOAT {
263 x.log(base)
264 }
265 #[rhai_fn(name = "log")]
267 pub fn log10(x: FLOAT) -> FLOAT {
268 x.log10()
269 }
270 #[rhai_fn(name = "floor", get = "floor")]
272 pub fn floor(x: FLOAT) -> FLOAT {
273 x.floor()
274 }
275 #[rhai_fn(name = "ceiling", get = "ceiling")]
277 pub fn ceiling(x: FLOAT) -> FLOAT {
278 x.ceil()
279 }
280 #[rhai_fn(name = "round", get = "round")]
283 pub fn round(x: FLOAT) -> FLOAT {
284 x.round()
285 }
286 #[rhai_fn(name = "int", get = "int")]
288 pub fn int(x: FLOAT) -> FLOAT {
289 x.trunc()
290 }
291 #[rhai_fn(name = "fraction", get = "fraction")]
293 pub fn fraction(x: FLOAT) -> FLOAT {
294 x.fract()
295 }
296 #[rhai_fn(name = "is_nan", get = "is_nan")]
298 pub fn is_nan(x: FLOAT) -> bool {
299 x.is_nan()
300 }
301 #[rhai_fn(name = "is_finite", get = "is_finite")]
303 pub fn is_finite(x: FLOAT) -> bool {
304 x.is_finite()
305 }
306 #[rhai_fn(name = "is_infinite", get = "is_infinite")]
308 pub fn is_infinite(x: FLOAT) -> bool {
309 x.is_infinite()
310 }
311 #[rhai_fn(name = "to_int", return_raw)]
313 pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> {
314 #[allow(clippy::cast_precision_loss)]
315 if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f32) || x < (INT::MIN as f32)) {
316 return Err(ERR::ErrorArithmetic(
317 format!("Integer overflow: to_int({x})"),
318 Position::NONE,
319 )
320 .into());
321 }
322
323 Ok(x.trunc() as INT)
324 }
325 #[rhai_fn(name = "to_int", return_raw)]
327 pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> {
328 #[allow(clippy::cast_precision_loss)]
329 if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f64) || x < (INT::MIN as f64)) {
330 return Err(ERR::ErrorArithmetic(
331 format!("Integer overflow: to_int({x})"),
332 Position::NONE,
333 )
334 .into());
335 }
336
337 Ok(x.trunc() as INT)
338 }
339 #[rhai_fn(return_raw)]
349 pub fn parse_float(string: &str) -> RhaiResultOf<FLOAT> {
350 string.trim().parse::<FLOAT>().map_err(|err| {
351 ERR::ErrorArithmetic(
352 format!("Error parsing floating-point number '{string}': {err}"),
353 Position::NONE,
354 )
355 .into()
356 })
357 }
358 #[cfg(not(feature = "f32_float"))]
360 #[rhai_fn(name = "to_float")]
361 pub fn f32_to_f64(x: f32) -> f64 {
362 x.into()
363 }
364}
365
366#[cfg(feature = "decimal")]
367#[export_module]
368mod decimal_functions {
369 use num_traits::ToPrimitive;
370 use rust_decimal::{
371 prelude::{FromStr, RoundingStrategy},
372 Decimal, MathematicalOps,
373 };
374 #[cfg(not(feature = "no_float"))]
375 use std::convert::TryFrom;
376
377 #[cfg(feature = "no_float")]
379 #[rhai_fn(name = "PI")]
380 pub const fn pi() -> Decimal {
381 Decimal::PI
382 }
383 #[cfg(feature = "no_float")]
385 #[rhai_fn(name = "E")]
386 pub const fn e() -> Decimal {
387 Decimal::E
388 }
389 #[cfg(feature = "no_float")]
399 #[rhai_fn(return_raw)]
400 pub fn parse_float(s: &str) -> RhaiResultOf<Decimal> {
401 parse_decimal(s)
402 }
403
404 pub fn sin(x: Decimal) -> Decimal {
406 x.sin()
407 }
408 pub fn cos(x: Decimal) -> Decimal {
410 x.cos()
411 }
412 pub fn tan(x: Decimal) -> Decimal {
414 x.tan()
415 }
416 #[rhai_fn(return_raw)]
418 pub fn sqrt(x: Decimal) -> RhaiResultOf<Decimal> {
419 x.sqrt()
420 .ok_or_else(|| make_err(format!("Error taking the square root of {x}")))
421 }
422 #[rhai_fn(return_raw)]
424 pub fn exp(x: Decimal) -> RhaiResultOf<Decimal> {
425 if cfg!(not(feature = "unchecked")) {
426 x.checked_exp()
427 .ok_or_else(|| make_err(format!("Exponential overflow: e ** {x}")))
428 } else {
429 Ok(x.exp())
430 }
431 }
432 #[rhai_fn(return_raw)]
434 pub fn ln(x: Decimal) -> RhaiResultOf<Decimal> {
435 if cfg!(not(feature = "unchecked")) {
436 x.checked_ln()
437 .ok_or_else(|| make_err(format!("Error taking the natural log of {x}")))
438 } else {
439 Ok(x.ln())
440 }
441 }
442 #[rhai_fn(name = "log", return_raw)]
444 pub fn log10(x: Decimal) -> RhaiResultOf<Decimal> {
445 if cfg!(not(feature = "unchecked")) {
446 x.checked_log10()
447 .ok_or_else(|| make_err(format!("Error taking the log of {x}")))
448 } else {
449 Ok(x.log10())
450 }
451 }
452 #[rhai_fn(name = "floor", get = "floor")]
454 pub fn floor(x: Decimal) -> Decimal {
455 x.floor()
456 }
457 #[rhai_fn(name = "ceiling", get = "ceiling")]
459 pub fn ceiling(x: Decimal) -> Decimal {
460 x.ceil()
461 }
462 #[rhai_fn(name = "round", get = "round")]
465 pub fn round(x: Decimal) -> Decimal {
466 x.round()
467 }
468 #[rhai_fn(name = "round", return_raw)]
471 pub fn round_dp(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
472 if cfg!(not(feature = "unchecked")) {
473 if digits < 0 {
474 return Err(make_err(format!(
475 "Invalid number of digits for rounding: {digits}"
476 )));
477 }
478 if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
479 return Ok(x);
480 }
481 }
482
483 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
484 Ok(x.round_dp(digits as u32))
485 }
486 #[rhai_fn(return_raw)]
489 pub fn round_up(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
490 if cfg!(not(feature = "unchecked")) {
491 if digits < 0 {
492 return Err(make_err(format!(
493 "Invalid number of digits for rounding: {digits}"
494 )));
495 }
496 if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
497 return Ok(x);
498 }
499 }
500
501 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
502 Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::AwayFromZero))
503 }
504 #[rhai_fn(return_raw)]
507 pub fn round_down(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
508 if cfg!(not(feature = "unchecked")) {
509 if digits < 0 {
510 return Err(make_err(format!(
511 "Invalid number of digits for rounding: {digits}"
512 )));
513 }
514 if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
515 return Ok(x);
516 }
517 }
518
519 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
520 Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::ToZero))
521 }
522 #[rhai_fn(return_raw)]
525 pub fn round_half_up(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
526 if cfg!(not(feature = "unchecked")) {
527 if digits < 0 {
528 return Err(make_err(format!(
529 "Invalid number of digits for rounding: {digits}"
530 )));
531 }
532 if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
533 return Ok(x);
534 }
535 }
536
537 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
538 Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointAwayFromZero))
539 }
540 #[rhai_fn(return_raw)]
543 pub fn round_half_down(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
544 if cfg!(not(feature = "unchecked")) {
545 if digits < 0 {
546 return Err(make_err(format!(
547 "Invalid number of digits for rounding: {digits}"
548 )));
549 }
550 if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
551 return Ok(x);
552 }
553 }
554
555 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
556 Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointTowardZero))
557 }
558 #[rhai_fn(return_raw)]
560 pub fn to_int(x: Decimal) -> RhaiResultOf<INT> {
561 #[allow(clippy::bind_instead_of_map)]
562 x.to_i64()
563 .and_then(|n| {
564 #[cfg(feature = "only_i32")]
565 return if n > (INT::MAX as i64) || n < (INT::MIN as i64) {
566 None
567 } else {
568 Some(n as i32)
569 };
570
571 #[cfg(not(feature = "only_i32"))]
572 return Some(n);
573 })
574 .map_or_else(
575 || {
576 Err(ERR::ErrorArithmetic(
577 format!("Integer overflow: to_int({x})"),
578 Position::NONE,
579 )
580 .into())
581 },
582 Ok,
583 )
584 }
585 #[rhai_fn(name = "int", get = "int")]
587 pub fn int(x: Decimal) -> Decimal {
588 x.trunc()
589 }
590 #[rhai_fn(name = "fraction", get = "fraction")]
592 pub fn fraction(x: Decimal) -> Decimal {
593 x.fract()
594 }
595 #[rhai_fn(return_raw)]
605 pub fn parse_decimal(string: &str) -> RhaiResultOf<Decimal> {
606 Decimal::from_str(string)
607 .or_else(|_| Decimal::from_scientific(string))
608 .map_err(|err| {
609 ERR::ErrorArithmetic(
610 format!("Error parsing decimal number '{string}': {err}"),
611 Position::NONE,
612 )
613 .into()
614 })
615 }
616
617 #[cfg(not(feature = "no_float"))]
619 #[rhai_fn(name = "to_decimal", return_raw)]
620 pub fn f32_to_decimal(x: f32) -> RhaiResultOf<Decimal> {
621 Decimal::try_from(x).map_err(|_| {
622 ERR::ErrorArithmetic(
623 format!("Cannot convert to Decimal: to_decimal({x})"),
624 Position::NONE,
625 )
626 .into()
627 })
628 }
629 #[cfg(not(feature = "no_float"))]
631 #[rhai_fn(name = "to_decimal", return_raw)]
632 pub fn f64_to_decimal(x: f64) -> RhaiResultOf<Decimal> {
633 Decimal::try_from(x).map_err(|_| {
634 ERR::ErrorArithmetic(
635 format!("Cannot convert to Decimal: to_decimal({x})"),
636 Position::NONE,
637 )
638 .into()
639 })
640 }
641 #[cfg(not(feature = "no_float"))]
643 #[rhai_fn(return_raw)]
644 pub fn to_float(x: Decimal) -> RhaiResultOf<FLOAT> {
645 FLOAT::try_from(x).map_err(|_| {
646 ERR::ErrorArithmetic(
647 format!("Cannot convert to floating-point: to_float({x})"),
648 Position::NONE,
649 )
650 .into()
651 })
652 }
653}
654
655#[cfg(not(feature = "no_float"))]
656gen_conversion_as_functions!(basic_to_float => to_float (INT) -> FLOAT);
657
658#[cfg(not(feature = "no_float"))]
659#[cfg(not(feature = "only_i32"))]
660#[cfg(not(feature = "only_i64"))]
661gen_conversion_as_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32, u32, i64, u64) -> FLOAT);
662
663#[cfg(not(feature = "no_float"))]
664#[cfg(not(feature = "only_i32"))]
665#[cfg(not(feature = "only_i64"))]
666#[cfg(not(target_family = "wasm"))]
667
668gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
669
670gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT);
671
672#[cfg(not(feature = "only_i32"))]
673#[cfg(not(feature = "only_i64"))]
674gen_conversion_as_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u32, i64, u64) -> INT);
675
676#[cfg(not(feature = "only_i32"))]
677#[cfg(not(feature = "only_i64"))]
678#[cfg(not(target_family = "wasm"))]
679
680gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT);
681
682#[cfg(feature = "decimal")]
683gen_conversion_into_functions!(basic_to_decimal => to_decimal (INT) -> Decimal);
684
685#[cfg(feature = "decimal")]
686#[cfg(not(feature = "only_i32"))]
687#[cfg(not(feature = "only_i64"))]
688gen_conversion_into_functions!(numbers_to_decimal => to_decimal (i8, u8, i16, u16, i32, u32, i64, u64) -> Decimal);