rhai_sci/
misc.rs

1use rhai::plugin::*;
2
3#[export_module]
4pub mod misc_functions {
5    use crate::{if_list_convert_to_vec_float_and_do, if_list_do_int_or_do_float};
6    use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT};
7
8    /// Infinity
9    #[allow(non_upper_case_globals)]
10    pub const inf: FLOAT = FLOAT::INFINITY;
11
12    /// Returns a random number between zero and one.
13    /// ```typescript
14    /// let r = rand();
15    /// assert(r >= 0.0 && r <= 1.0);
16    /// ```
17    #[cfg(feature = "rand")]
18    #[rhai_fn(name = "rand")]
19    pub fn rand_float() -> FLOAT {
20        randlib::random()
21    }
22
23    /// Returns an array of the unique elements in an array.
24    /// ```typescript
25    /// let data = [1, 2, 2, 2, 5, 4, 4, 2, 5, 8];
26    /// let u = unique(data);
27    /// assert_eq(u, [1, 2, 4, 5, 8]);
28    /// ```
29    #[rhai_fn(name = "unique", return_raw, pure)]
30    pub fn unique(arr: &mut Array) -> Result<Array, Box<EvalAltResult>> {
31        if_list_do_int_or_do_float(
32            arr,
33            |arr| {
34                let mut x = crate::array_to_vec_int(arr);
35                x.sort();
36                x.dedup();
37                Ok(x.iter().map(|el| Dynamic::from_int(*el)).collect())
38            },
39            |arr| {
40                let mut x = crate::array_to_vec_float(arr);
41                x.sort_by(|a, b| a.partial_cmp(b).unwrap());
42                x.dedup();
43                Ok(x.iter().map(|el| Dynamic::from_float(*el)).collect())
44            },
45        )
46    }
47
48    /// Given reference data, perform linear interpolation.
49    ///
50    /// Both arrays must be sorted and have the same length.
51    ///
52    /// Out-of-bound xq values are clamped to the minimum and maximum values of y respectively.
53    /// ```typescript
54    /// let x = [0, 1];
55    /// let y = [1, 2];
56    /// let xq = 0.5;
57    /// let yq = interp1(x, y, xq);
58    /// assert_eq(yq, 1.5);
59    /// ```
60    #[rhai_fn(name = "interp1", return_raw)]
61    pub fn interp1(x: &mut Array, y: Array, xq: Dynamic) -> Result<FLOAT, Box<EvalAltResult>> {
62        let new_xq = if xq.is_int() {
63            xq.as_int().unwrap() as FLOAT
64        } else if xq.is_float() {
65            xq.as_float().unwrap()
66        } else {
67            return Err(EvalAltResult::ErrorArithmetic(
68                "xq must be either INT or FLOAT".to_string(),
69                Position::NONE,
70            )
71            .into());
72        };
73
74        if x.len() < 2 {
75            return Err(EvalAltResult::ErrorArithmetic(
76                "The arrays must have at least 2 elements".to_string(),
77                Position::NONE,
78            )
79            .into());
80        }
81        if x.len() != y.len() {
82            return Err(EvalAltResult::ErrorArithmetic(
83                "The arrays must have the same length".to_string(),
84                Position::NONE,
85            )
86            .into());
87        }
88
89        let mut y = y;
90
91        if_list_convert_to_vec_float_and_do(&mut y, |new_y| {
92            if_list_convert_to_vec_float_and_do(x, |new_x| {
93                if new_xq >= *new_x.last().unwrap() {
94                    return Ok(*new_y.last().unwrap());
95                } else if new_xq <= *new_x.first().unwrap() {
96                    return Ok(*new_y.first().unwrap());
97                }
98
99                // Identify the right index
100                let b = new_x
101                    .iter()
102                    .enumerate()
103                    .find_map(|(i, &el)| (el >= new_xq).then(|| i))
104                    .unwrap();
105
106                let a = b - 1;
107                Ok(new_y[a] + (new_xq - new_x[a]) * (new_y[b] - new_y[a]) / (new_x[b] - new_x[a]))
108            })
109        })
110    }
111}