Skip to main content

rhai_sci/
patterns.rs

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