rhai_sci/
patterns.rs

1use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT, INT};
2
3/// Matrix compatibility conditions
4#[allow(dead_code)]
5pub enum FOIL {
6    /// Height of first matrix must match height of second matrix
7    First,
8    /// Height of first matrix must match width of second matrix
9    Outside,
10    /// Width of first matrix must match height of second matrix
11    Inside,
12    /// Width of first matrix must match width of second matrix
13    Last,
14}
15
16pub fn int_and_float_totals(arr: &mut Array) -> (INT, INT, INT) {
17    crate::matrix_functions::flatten(arr)
18        .iter()
19        .fold((0, 0, 0), |(i, f, t), x| {
20            if x.is_int() {
21                (i + 1, f, t + 1)
22            } else if x.is_float() {
23                (i, f + 1, t + 1)
24            } else {
25                (i, f, t + 1)
26            }
27        })
28}
29
30pub fn if_list_do_int_or_do_float<FA, FB, T>(
31    arr: &mut Array,
32    mut f_int: FA,
33    mut f_float: FB,
34) -> Result<T, Box<EvalAltResult>>
35where
36    FA: FnMut(&mut Array) -> Result<T, Box<EvalAltResult>>,
37    FB: FnMut(&mut Array) -> Result<T, Box<EvalAltResult>>,
38{
39    let (int, float, total) = int_and_float_totals(arr);
40    if int == total {
41        f_int(arr)
42    } else if float == total {
43        f_float(arr)
44    } else if float + int == total {
45        let mut arr_of_float = arr
46            .iter()
47            .map(|el| {
48                Dynamic::from_float(if el.is_int() {
49                    el.as_int().unwrap() as FLOAT
50                } else if el.is_float() {
51                    el.as_float().unwrap()
52                } else {
53                    unreachable!("This should never happen!");
54                })
55            })
56            .collect::<Array>();
57        f_float(&mut arr_of_float)
58    } else {
59        Err(EvalAltResult::ErrorArithmetic(
60            "The elements of the input array must either be INT or FLOAT".to_string(),
61            Position::NONE,
62        )
63        .into())
64    }
65}
66
67/// Does a function if the input is a list, otherwise throws an error.
68pub fn if_list_do<F, T>(arr: &mut Array, mut f: F) -> Result<T, Box<EvalAltResult>>
69where
70    F: FnMut(&mut Array) -> Result<T, Box<EvalAltResult>>,
71{
72    crate::validation_functions::is_numeric_list(arr)
73        .then(|| f(arr))
74        .unwrap_or(Err(EvalAltResult::ErrorArithmetic(
75            format!("The elements of the input array must either be INT or FLOAT."),
76            Position::NONE,
77        )
78        .into()))
79}
80
81pub fn if_list_convert_to_vec_float_and_do<F, T>(
82    arr: &mut Array,
83    f: F,
84) -> Result<T, Box<EvalAltResult>>
85where
86    F: FnMut(Vec<FLOAT>) -> Result<T, Box<EvalAltResult>>,
87{
88    if_list_do_int_or_do_float(
89        arr,
90        |arr: &mut Array| Ok(arr.iter().map(|el| el.as_int().unwrap() as FLOAT).collect()),
91        |arr: &mut Array| Ok(arr.iter().map(|el| el.as_float().unwrap()).collect()),
92    )
93    .and_then(f)
94}
95
96/// If the input is an int, convert to a float and do the function. if the input is a float already,
97/// the function is still performed.
98pub fn if_int_convert_to_float_and_do<F, T>(x: Dynamic, mut f: F) -> Result<T, Box<EvalAltResult>>
99where
100    F: FnMut(FLOAT) -> Result<T, Box<EvalAltResult>>,
101{
102    let new_x: FLOAT = if x.is_float() {
103        x.as_float().unwrap()
104    } else if x.is_int() {
105        x.as_int().unwrap() as FLOAT
106    } else {
107        return Err(EvalAltResult::ErrorArithmetic(
108            "The input must either be INT or FLOAT".to_string(),
109            Position::NONE,
110        )
111        .into());
112    };
113    f(new_x)
114}
115
116#[cfg(feature = "nalgebra")]
117pub fn if_matrix_do<T, F>(matrix: &mut Array, mut f: F) -> Result<T, Box<EvalAltResult>>
118where
119    F: FnMut(&mut Array) -> Result<T, Box<EvalAltResult>>,
120{
121    crate::validation_functions::is_matrix(matrix)
122        .then(|| f(matrix))
123        .unwrap_or(Err(EvalAltResult::ErrorArithmetic(
124            format!("The input must be a matrix."),
125            Position::NONE,
126        )
127        .into()))
128}
129
130#[cfg(feature = "nalgebra")]
131pub fn if_matrices_and_compatible_convert_to_vec_array_and_do<T, F>(
132    compatibility_condition: FOIL,
133    matrix1: &mut Array,
134    matrix2: &mut Array,
135    mut f: F,
136) -> Result<T, Box<EvalAltResult>>
137where
138    F: FnMut(Vec<Array>, Vec<Array>) -> Result<T, Box<EvalAltResult>>,
139{
140    if crate::validation_functions::is_matrix(matrix1) {
141        if crate::validation_functions::is_matrix(matrix2) {
142            let s1 = crate::matrix_functions::matrix_size_by_reference(matrix1);
143            let s2 = crate::matrix_functions::matrix_size_by_reference(matrix2);
144            if match compatibility_condition {
145                FOIL::First => s1[0].as_int().unwrap() == s2[0].as_int().unwrap(),
146                FOIL::Outside => s1[0].as_int().unwrap() == s2[1].as_int().unwrap(),
147                FOIL::Inside => s1[1].as_int().unwrap() == s2[0].as_int().unwrap(),
148                FOIL::Last => s1[1].as_int().unwrap() == s2[1].as_int().unwrap(),
149            } {
150                // Turn into Vec<Array>
151                let matrix_as_vec1 = matrix1
152                    .into_iter()
153                    .map(|x| x.clone().into_array().unwrap())
154                    .collect::<Vec<Array>>();
155                // Turn into Vec<Array>
156                let matrix_as_vec2 = matrix2
157                    .into_iter()
158                    .map(|x| x.clone().into_array().unwrap())
159                    .collect::<Vec<Array>>();
160                f(matrix_as_vec1, matrix_as_vec2)
161            } else {
162                Err(EvalAltResult::ErrorArithmetic(
163                    "The input matrices are not compatible for this operation".to_string(),
164                    Position::NONE,
165                )
166                .into())
167            }
168        } else {
169            Err(EvalAltResult::ErrorArithmetic(
170                "The second input must be a matrix".to_string(),
171                Position::NONE,
172            )
173            .into())
174        }
175    } else {
176        Err(EvalAltResult::ErrorArithmetic(
177            "The first input must be a matrix".to_string(),
178            Position::NONE,
179        )
180        .into())
181    }
182}
183
184/// If the input is a
185pub fn if_matrix_convert_to_vec_array_and_do<F, T>(
186    matrix: &mut Array,
187    mut f: F,
188) -> Result<T, Box<EvalAltResult>>
189where
190    F: FnMut(Vec<Array>) -> Result<T, Box<EvalAltResult>>,
191{
192    let matrix_as_vec = matrix
193        .into_iter()
194        .map(|x| x.clone().into_array().unwrap())
195        .collect::<Vec<Array>>();
196    if crate::validation_functions::is_matrix(matrix) {
197        f(matrix_as_vec)
198    } else {
199        Err(EvalAltResult::ErrorArithmetic(
200            "The input must be a matrix".to_string(),
201            Position::NONE,
202        )
203        .into())
204    }
205}
206
207pub fn if_int_do_else_if_array_do<FA, FB, T>(
208    d: Dynamic,
209    mut f_int: FA,
210    mut f_array: FB,
211) -> Result<T, Box<EvalAltResult>>
212where
213    FA: FnMut(INT) -> Result<T, Box<EvalAltResult>>,
214    FB: FnMut(&mut Array) -> Result<T, Box<EvalAltResult>>,
215{
216    if d.is_int() {
217        f_int(d.as_int().unwrap())
218    } else if d.is_array() {
219        if_list_do(&mut d.into_array().unwrap(), |arr| f_array(arr))
220    } else {
221        Err(EvalAltResult::ErrorArithmetic(
222            "The input must be either an INT or an numeric array".to_string(),
223            Position::NONE,
224        )
225        .into())
226    }
227}
228
229pub fn array_to_vec_int(arr: &mut Array) -> Vec<INT> {
230    arr.iter()
231        .map(|el| el.as_int().unwrap())
232        .collect::<Vec<INT>>()
233}
234
235pub fn array_to_vec_float(arr: &mut Array) -> Vec<FLOAT> {
236    arr.into_iter()
237        .map(|el| el.as_float().unwrap())
238        .collect::<Vec<FLOAT>>()
239}
240
241#[cfg(feature = "nalgebra")]
242pub fn omatrix_to_vec_dynamic(
243    mat: nalgebralib::OMatrix<FLOAT, nalgebralib::Dyn, nalgebralib::Dyn>,
244) -> Vec<Dynamic> {
245    let mut out = vec![];
246    for idx in 0..mat.shape().0 {
247        let mut new_row = vec![];
248        for jdx in 0..mat.shape().1 {
249            new_row.push(Dynamic::from_float(mat[(idx, jdx)]));
250        }
251        out.push(Dynamic::from_array(new_row));
252    }
253    out
254}
255
256#[cfg(feature = "nalgebra")]
257pub fn ovector_to_vec_dynamic(mat: nalgebralib::OVector<FLOAT, nalgebralib::Dyn>) -> Vec<Dynamic> {
258    let mut out = vec![];
259    for idx in 0..mat.shape().0 {
260        out.push(Dynamic::from_float(mat[idx]));
261    }
262    out
263}