rhai/packages/
array_basic.rs

1#![cfg(not(feature = "no_index"))]
2
3use crate::api::deprecated::deprecated_array_functions;
4use crate::engine::OP_EQUALS;
5use crate::eval::{calc_index, calc_offset_len};
6use crate::plugin::*;
7use crate::types::fn_ptr::FnPtrType;
8use crate::{
9    def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext,
10    Position, RhaiResultOf, ERR, INT,
11};
12#[cfg(feature = "no_std")]
13use std::prelude::v1::*;
14use std::{any::TypeId, cmp::Ordering, convert::TryFrom, mem};
15
16def_package! {
17    /// Package of basic array utilities.
18    pub BasicArrayPackage(lib) {
19        lib.set_standard_lib(true);
20
21        combine_with_exported_module!(lib, "array", array_functions);
22        combine_with_exported_module!(lib, "deprecated_array", deprecated_array_functions);
23
24        // Register array iterator
25        lib.set_iterable::<Array>();
26    }
27}
28
29#[export_module]
30pub mod array_functions {
31    /// Number of elements in the array.
32    #[rhai_fn(name = "len", get = "len", pure)]
33    pub fn len(array: &mut Array) -> INT {
34        INT::try_from(array.len()).unwrap_or(INT::MAX)
35    }
36    /// Return true if the array is empty.
37    #[rhai_fn(name = "is_empty", get = "is_empty", pure)]
38    pub fn is_empty(array: &mut Array) -> bool {
39        array.len() == 0
40    }
41    /// Get a copy of the element at the `index` position in the array.
42    ///
43    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
44    /// * If `index` < -length of array, `()` is returned.
45    /// * If `index` ≥ length of array, `()` is returned.
46    ///
47    /// # Example
48    ///
49    /// ```rhai
50    /// let x = [1, 2, 3];
51    ///
52    /// print(x.get(0));        // prints 1
53    ///
54    /// print(x.get(-1));       // prints 3
55    ///
56    /// print(x.get(99));       // prints empty (for '()')
57    /// ```
58    pub fn get(array: &mut Array, index: INT) -> Dynamic {
59        if array.is_empty() {
60            return Dynamic::UNIT;
61        }
62
63        let (index, ..) = calc_offset_len(array.len(), index, 0);
64
65        if index >= array.len() {
66            return Dynamic::UNIT;
67        }
68
69        array[index].clone()
70    }
71    /// Set the element at the `index` position in the array to a new `value`.
72    ///
73    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
74    /// * If `index` < -length of array, the array is not modified.
75    /// * If `index` ≥ length of array, the array is not modified.
76    ///
77    /// # Example
78    ///
79    /// ```rhai
80    /// let x = [1, 2, 3];
81    ///
82    /// x.set(0, 42);
83    ///
84    /// print(x);           // prints "[42, 2, 3]"
85    ///
86    /// x.set(-3, 0);
87    ///
88    /// print(x);           // prints "[0, 2, 3]"
89    ///
90    /// x.set(99, 123);
91    ///
92    /// print(x);           // prints "[0, 2, 3]"
93    /// ```
94    pub fn set(array: &mut Array, index: INT, value: Dynamic) {
95        if array.is_empty() {
96            return;
97        }
98
99        let (index, ..) = calc_offset_len(array.len(), index, 0);
100
101        if index >= array.len() {
102            return;
103        }
104
105        array[index] = value;
106    }
107    /// Add a new element, which is not another array, to the end of the array.
108    ///
109    /// If `item` is `Array`, then `append` is more specific and will be called instead.
110    ///
111    /// # Example
112    ///
113    /// ```rhai
114    /// let x = [1, 2, 3];
115    ///
116    /// x.push("hello");
117    ///
118    /// print(x);       // prints [1, 2, 3, "hello"]
119    /// ```
120    pub fn push(array: &mut Array, item: Dynamic) {
121        array.push(item);
122    }
123    /// Add all the elements of another array to the end of the array.
124    ///
125    /// # Example
126    ///
127    /// ```rhai
128    /// let x = [1, 2, 3];
129    /// let y = [true, 'x'];
130    ///
131    /// x.append(y);
132    ///
133    /// print(x);       // prints "[1, 2, 3, true, 'x']"
134    /// ```
135    pub fn append(array: &mut Array, new_array: Array) {
136        if new_array.is_empty() {
137            return;
138        }
139
140        if array.is_empty() {
141            *array = new_array;
142        } else {
143            array.extend(new_array);
144        }
145    }
146    /// Combine two arrays into a new array and return it.
147    ///
148    /// # Example
149    ///
150    /// ```rhai
151    /// let x = [1, 2, 3];
152    /// let y = [true, 'x'];
153    ///
154    /// print(x + y);   // prints "[1, 2, 3, true, 'x']"
155    ///
156    /// print(x);       // prints "[1, 2, 3"
157    /// ```
158    #[rhai_fn(name = "+")]
159    pub fn concat(array1: Array, array2: Array) -> Array {
160        if array2.is_empty() {
161            return array1;
162        }
163
164        if array1.is_empty() {
165            return array2;
166        }
167
168        let mut array = array1;
169        array.extend(array2);
170        array
171    }
172    /// Add a new element into the array at a particular `index` position.
173    ///
174    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
175    /// * If `index` < -length of array, the element is added to the beginning of the array.
176    /// * If `index` ≥ length of array, the element is appended to the end of the array.
177    ///
178    /// # Example
179    ///
180    /// ```rhai
181    /// let x = [1, 2, 3];
182    ///
183    /// x.insert(0, "hello");
184    ///
185    /// x.insert(2, true);
186    ///
187    /// x.insert(-2, 42);
188    ///
189    /// print(x);       // prints ["hello", 1, true, 2, 42, 3]
190    /// ```
191    pub fn insert(array: &mut Array, index: INT, item: Dynamic) {
192        if array.is_empty() {
193            array.push(item);
194            return;
195        }
196
197        let (index, ..) = calc_offset_len(array.len(), index, 0);
198
199        if index >= array.len() {
200            array.push(item);
201        } else {
202            array.insert(index, item);
203        }
204    }
205    /// Pad the array to at least the specified length with copies of a specified element.
206    ///
207    /// If `len` ≤ length of array, no padding is done.
208    ///
209    /// # Example
210    ///
211    /// ```rhai
212    /// let x = [1, 2, 3];
213    ///
214    /// x.pad(5, 42);
215    ///
216    /// print(x);       // prints "[1, 2, 3, 42, 42]"
217    ///
218    /// x.pad(3, 123);
219    ///
220    /// print(x);       // prints "[1, 2, 3, 42, 42]"
221    /// ```
222    #[rhai_fn(return_raw)]
223    pub fn pad(
224        ctx: NativeCallContext,
225        array: &mut Array,
226        len: INT,
227        item: Dynamic,
228    ) -> RhaiResultOf<()> {
229        let Ok(len) = usize::try_from(len) else {
230            return Ok(());
231        };
232        if len <= array.len() {
233            return Ok(());
234        }
235
236        let _ctx = ctx;
237
238        // Check if array will be over max size limit
239        #[cfg(not(feature = "unchecked"))]
240        if _ctx.engine().max_array_size() > 0 {
241            let pad = len - array.len();
242            let (a, m, s) = crate::eval::calc_array_sizes(array);
243            let (ax, mx, sx) = crate::eval::calc_data_sizes(&item, true);
244
245            _ctx.engine()
246                .throw_on_size((a + pad + ax * pad, m + mx * pad, s + sx * pad))?;
247        }
248
249        array.resize(len, item);
250
251        Ok(())
252    }
253    /// Remove the last element from the array and return it.
254    ///
255    /// If the array is empty, `()` is returned.
256    ///
257    /// # Example
258    ///
259    /// ```rhai
260    /// let x = [1, 2, 3];
261    ///
262    /// print(x.pop());     // prints 3
263    ///
264    /// print(x);           // prints "[1, 2]"
265    /// ```
266    pub fn pop(array: &mut Array) -> Dynamic {
267        if array.is_empty() {
268            return Dynamic::UNIT;
269        }
270
271        array.pop().unwrap_or(Dynamic::UNIT)
272    }
273    /// Remove the first element from the array and return it.
274    ///
275    /// If the array is empty, `()` is returned.
276    ///
277    /// # Example
278    ///
279    /// ```rhai
280    /// let x = [1, 2, 3];
281    ///
282    /// print(x.shift());   // prints 1
283    ///
284    /// print(x);           // prints "[2, 3]"
285    /// ```
286    pub fn shift(array: &mut Array) -> Dynamic {
287        if array.is_empty() {
288            return Dynamic::UNIT;
289        }
290
291        array.remove(0)
292    }
293    /// Remove the element at the specified `index` from the array and return it.
294    ///
295    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
296    /// * If `index` < -length of array, `()` is returned.
297    /// * If `index` ≥ length of array, `()` is returned.
298    ///
299    /// # Example
300    ///
301    /// ```rhai
302    /// let x = [1, 2, 3];
303    ///
304    /// print(x.remove(1));     // prints 2
305    ///
306    /// print(x);               // prints "[1, 3]"
307    ///
308    /// print(x.remove(-2));    // prints 1
309    ///
310    /// print(x);               // prints "[3]"
311    /// ```
312    pub fn remove(array: &mut Array, index: INT) -> Dynamic {
313        let Ok(index) = calc_index(array.len(), index, true, || Err(())) else {
314            return Dynamic::UNIT;
315        };
316
317        array.remove(index)
318    }
319    /// Clear the array.
320    pub fn clear(array: &mut Array) {
321        if array.is_empty() {
322            return;
323        }
324
325        array.clear();
326    }
327    /// Cut off the array at the specified length.
328    ///
329    /// * If `len` ≤ 0, the array is cleared.
330    /// * If `len` ≥ length of array, the array is not truncated.
331    ///
332    /// # Example
333    ///
334    /// ```rhai
335    /// let x = [1, 2, 3, 4, 5];
336    ///
337    /// x.truncate(3);
338    ///
339    /// print(x);       // prints "[1, 2, 3]"
340    ///
341    /// x.truncate(10);
342    ///
343    /// print(x);       // prints "[1, 2, 3]"
344    /// ```
345    pub fn truncate(array: &mut Array, len: INT) {
346        if array.is_empty() {
347            return;
348        }
349        if len <= 0 {
350            array.clear();
351            return;
352        }
353
354        let Ok(len) = usize::try_from(len) else {
355            return;
356        };
357
358        if len > 0 {
359            array.truncate(len);
360        } else {
361            array.clear();
362        }
363    }
364    /// Cut off the head of the array, leaving a tail of the specified length.
365    ///
366    /// * If `len` ≤ 0, the array is cleared.
367    /// * If `len` ≥ length of array, the array is not modified.
368    ///
369    /// # Example
370    ///
371    /// ```rhai
372    /// let x = [1, 2, 3, 4, 5];
373    ///
374    /// x.chop(3);
375    ///
376    /// print(x);       // prints "[3, 4, 5]"
377    ///
378    /// x.chop(10);
379    ///
380    /// print(x);       // prints "[3, 4, 5]"
381    /// ```
382    pub fn chop(array: &mut Array, len: INT) {
383        if array.is_empty() {
384            return;
385        }
386        if len <= 0 {
387            array.clear();
388            return;
389        }
390
391        let Ok(len) = usize::try_from(len) else {
392            return;
393        };
394
395        if len < array.len() {
396            array.drain(0..array.len() - len);
397        }
398    }
399    /// Reverse all the elements in the array.
400    ///
401    /// # Example
402    ///
403    /// ```rhai
404    /// let x = [1, 2, 3, 4, 5];
405    ///
406    /// x.reverse();
407    ///
408    /// print(x);       // prints "[5, 4, 3, 2, 1]"
409    /// ```
410    pub fn reverse(array: &mut Array) {
411        if array.is_empty() {
412            return;
413        }
414
415        array.reverse();
416    }
417    /// Replace an exclusive range of the array with another array.
418    ///
419    /// # Example
420    ///
421    /// ```rhai
422    /// let x = [1, 2, 3, 4, 5];
423    /// let y = [7, 8, 9, 10];
424    ///
425    /// x.splice(1..3, y);
426    ///
427    /// print(x);       // prints "[1, 7, 8, 9, 10, 4, 5]"
428    /// ```
429    #[rhai_fn(name = "splice")]
430    pub fn splice_range(array: &mut Array, range: ExclusiveRange, replace: Array) {
431        let start = INT::max(range.start, 0);
432        let end = INT::max(range.end, start);
433        splice(array, start, end - start, replace);
434    }
435    /// Replace an inclusive range of the array with another array.
436    ///
437    /// # Example
438    ///
439    /// ```rhai
440    /// let x = [1, 2, 3, 4, 5];
441    /// let y = [7, 8, 9, 10];
442    ///
443    /// x.splice(1..=3, y);
444    ///
445    /// print(x);       // prints "[1, 7, 8, 9, 10, 5]"
446    /// ```
447    #[rhai_fn(name = "splice")]
448    pub fn splice_inclusive_range(array: &mut Array, range: InclusiveRange, replace: Array) {
449        let start = INT::max(*range.start(), 0);
450        let end = INT::min(INT::max(*range.end(), start), INT::MAX - 1);
451        splice(array, start, end - start + 1, replace);
452    }
453    /// Replace a portion of the array with another array.
454    ///
455    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
456    /// * If `start` < -length of array, position counts from the beginning of the array.
457    /// * If `start` ≥ length of array, the other array is appended to the end of the array.
458    /// * If `len` ≤ 0, the other array is inserted into the array at the `start` position without replacing any element.
459    /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is replaced.
460    ///
461    /// # Example
462    ///
463    /// ```rhai
464    /// let x = [1, 2, 3, 4, 5];
465    /// let y = [7, 8, 9, 10];
466    ///
467    /// x.splice(1, 2, y);
468    ///
469    /// print(x);       // prints "[1, 7, 8, 9, 10, 4, 5]"
470    ///
471    /// x.splice(-5, 4, y);
472    ///
473    /// print(x);       // prints "[1, 7, 7, 8, 9, 10, 5]"
474    /// ```
475    pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
476        if array.is_empty() {
477            *array = replace;
478            return;
479        }
480
481        let (start, len) = calc_offset_len(array.len(), start, len);
482
483        if start >= array.len() {
484            array.extend(replace);
485        } else {
486            array.splice(start..start + len, replace);
487        }
488    }
489    /// Copy an exclusive range of the array and return it as a new array.
490    ///
491    /// # Example
492    ///
493    /// ```rhai
494    /// let x = [1, 2, 3, 4, 5];
495    ///
496    /// print(x.extract(1..3));     // prints "[2, 3]"
497    ///
498    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
499    /// ```
500    #[rhai_fn(name = "extract")]
501    pub fn extract_range(array: &mut Array, range: ExclusiveRange) -> Array {
502        let start = INT::max(range.start, 0);
503        let end = INT::max(range.end, start);
504        extract(array, start, end - start)
505    }
506    /// Copy an inclusive range of the array and return it as a new array.
507    ///
508    /// # Example
509    ///
510    /// ```rhai
511    /// let x = [1, 2, 3, 4, 5];
512    ///
513    /// print(x.extract(1..=3));    // prints "[2, 3, 4]"
514    ///
515    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
516    /// ```
517    #[rhai_fn(name = "extract")]
518    pub fn extract_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
519        let start = INT::max(*range.start(), 0);
520        let end = INT::min(INT::max(*range.end(), start), INT::MAX - 1);
521        extract(array, start, end - start + 1)
522    }
523    /// Copy a portion of the array and return it as a new array.
524    ///
525    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
526    /// * If `start` < -length of array, position counts from the beginning of the array.
527    /// * If `start` ≥ length of array, an empty array is returned.
528    /// * If `len` ≤ 0, an empty array is returned.
529    /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is copied and returned.
530    ///
531    /// # Example
532    ///
533    /// ```rhai
534    /// let x = [1, 2, 3, 4, 5];
535    ///
536    /// print(x.extract(1, 3));     // prints "[2, 3, 4]"
537    ///
538    /// print(x.extract(-3, 2));    // prints "[3, 4]"
539    ///
540    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
541    /// ```
542    pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
543        if array.is_empty() || len <= 0 {
544            return Array::new();
545        }
546
547        let (start, len) = calc_offset_len(array.len(), start, len);
548
549        if len == 0 {
550            return Array::new();
551        }
552
553        array[start..start + len].to_vec()
554    }
555    /// Copy a portion of the array beginning at the `start` position till the end and return it as
556    /// a new array.
557    ///
558    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
559    /// * If `start` < -length of array, the entire array is copied and returned.
560    /// * If `start` ≥ length of array, an empty array is returned.
561    ///
562    /// # Example
563    ///
564    /// ```rhai
565    /// let x = [1, 2, 3, 4, 5];
566    ///
567    /// print(x.extract(2));        // prints "[3, 4, 5]"
568    ///
569    /// print(x.extract(-3));       // prints "[3, 4, 5]"
570    ///
571    /// print(x);                   // prints "[1, 2, 3, 4, 5]"
572    /// ```
573    #[rhai_fn(name = "extract")]
574    pub fn extract_tail(array: &mut Array, start: INT) -> Array {
575        extract(array, start, INT::MAX)
576    }
577    /// Cut off the array at `index` and return it as a new array.
578    ///
579    /// * If `index` < 0, position counts from the end of the array (`-1` is the last element).
580    /// * If `index` is zero, the entire array is cut and returned.
581    /// * If `index` < -length of array, the entire array is cut and returned.
582    /// * If `index` ≥ length of array, nothing is cut from the array and an empty array is returned.
583    ///
584    /// # Example
585    ///
586    /// ```rhai
587    /// let x = [1, 2, 3, 4, 5];
588    ///
589    /// let y = x.split(2);
590    ///
591    /// print(y);           // prints "[3, 4, 5]"
592    ///
593    /// print(x);           // prints "[1, 2]"
594    /// ```
595    #[rhai_fn(name = "split")]
596    pub fn split_at(array: &mut Array, index: INT) -> Array {
597        if array.is_empty() {
598            return Array::new();
599        }
600
601        let (start, len) = calc_offset_len(array.len(), index, INT::MAX);
602
603        if start >= array.len() {
604            return Array::new();
605        }
606
607        if start == 0 {
608            if len >= array.len() {
609                return mem::take(array);
610            }
611
612            let mut result = Array::new();
613            result.extend(array.drain(array.len() - len..));
614            return result;
615        }
616
617        let mut result = Array::new();
618        result.extend(array.drain(start..));
619        result
620    }
621
622    /// Iterate through all the elements in the array, applying a `process` function to each element in turn.
623    /// Each element is bound to `this` before calling the function.
624    ///
625    /// # Function Parameters
626    ///
627    /// * `this`: bound to array element (mutable)
628    /// * `index` _(optional)_: current index in the array
629    ///
630    /// # Example
631    ///
632    /// ```rhai
633    /// let x = [1, 2, 3, 4, 5];
634    ///
635    /// x.for_each(|| this *= this);
636    ///
637    /// print(x);       // prints "[1, 4, 9, 16, 25]"
638    ///
639    /// x.for_each(|i| this *= i);
640    ///
641    /// print(x);       // prints "[0, 2, 6, 12, 20]"
642    /// ```
643    #[rhai_fn(return_raw)]
644    pub fn for_each(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<()> {
645        if array.is_empty() {
646            return Ok(());
647        }
648
649        for (i, item) in array.iter_mut().enumerate() {
650            let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
651
652            let _ = map.call_raw_with_extra_args("for_each", &ctx, Some(item), [], ex, None)?;
653        }
654
655        Ok(())
656    }
657
658    /// Iterate through all the elements in the array, applying a `mapper` function to each element
659    /// in turn, and return the results as a new array.
660    ///
661    /// # No Function Parameter
662    ///
663    /// Array element (mutable) is bound to `this`.
664    ///
665    /// This method is marked _pure_; the `mapper` function should not mutate array elements.
666    ///
667    /// # Function Parameters
668    ///
669    /// * `element`: copy of array element
670    /// * `index` _(optional)_: current index in the array
671    ///
672    /// # Example
673    ///
674    /// ```rhai
675    /// let x = [1, 2, 3, 4, 5];
676    ///
677    /// let y = x.map(|v| v * v);
678    ///
679    /// print(y);       // prints "[1, 4, 9, 16, 25]"
680    ///
681    /// let y = x.map(|v, i| v * i);
682    ///
683    /// print(y);       // prints "[0, 2, 6, 12, 20]"
684    /// ```
685    #[rhai_fn(return_raw, pure)]
686    pub fn map(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<Array> {
687        if array.is_empty() {
688            return Ok(Array::new());
689        }
690
691        let mut ar = Array::with_capacity(array.len());
692
693        for (i, item) in array.iter_mut().enumerate() {
694            let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
695            ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, Some(0))?);
696        }
697
698        Ok(ar)
699    }
700
701    /// Iterate through all the elements in the array, applying a `filter` function to each element
702    /// in turn, and return a copy of all elements (in order) that return `true` as a new array.
703    ///
704    /// # No Function Parameter
705    ///
706    /// Array element (mutable) is bound to `this`.
707    ///
708    /// This method is marked _pure_; the `filter` function should not mutate array elements.
709    ///
710    /// # Function Parameters
711    ///
712    /// * `element`: copy of array element
713    /// * `index` _(optional)_: current index in the array
714    ///
715    /// # Example
716    ///
717    /// ```rhai
718    /// let x = [1, 2, 3, 4, 5];
719    ///
720    /// let y = x.filter(|v| v >= 3);
721    ///
722    /// print(y);       // prints "[3, 4, 5]"
723    ///
724    /// let y = x.filter(|v, i| v * i >= 10);
725    ///
726    /// print(y);       // prints "[12, 20]"
727    /// ```
728    #[rhai_fn(return_raw, pure)]
729    pub fn filter(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
730        if array.is_empty() {
731            return Ok(Array::new());
732        }
733
734        let mut ar = Array::new();
735
736        for (i, item) in array.iter_mut().enumerate() {
737            let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
738
739            if filter
740                .call_raw_with_extra_args("filter", &ctx, Some(item), [], ex, Some(0))?
741                .as_bool()
742                .unwrap_or(false)
743            {
744                ar.push(item.clone());
745            }
746        }
747
748        Ok(ar)
749    }
750    /// Return `true` if the array contains an element that equals `value`.
751    ///
752    /// The operator `==` is used to compare elements with `value` and must be defined,
753    /// otherwise `false` is assumed.
754    ///
755    /// This function also drives the `in` operator.
756    ///
757    /// # Example
758    ///
759    /// ```rhai
760    /// let x = [1, 2, 3, 4, 5];
761    ///
762    /// // The 'in' operator calls 'contains' in the background
763    /// if 4 in x {
764    ///     print("found!");
765    /// }
766    /// ```
767    #[rhai_fn(return_raw, pure)]
768    pub fn contains(
769        ctx: NativeCallContext,
770        array: &mut Array,
771        value: Dynamic,
772    ) -> RhaiResultOf<bool> {
773        if array.is_empty() {
774            return Ok(false);
775        }
776
777        for item in array {
778            if ctx
779                .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
780                .or_else(|err| match *err {
781                    ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
782                        if item.type_id() == value.type_id() {
783                            // No default when comparing same type
784                            Err(err)
785                        } else {
786                            Ok(Dynamic::FALSE)
787                        }
788                    }
789                    _ => Err(err),
790                })?
791                .as_bool()
792                .unwrap_or(false)
793            {
794                return Ok(true);
795            }
796        }
797
798        Ok(false)
799    }
800    /// Find the first element in the array that equals a particular `value` and return its index.
801    /// If no element equals `value`, `-1` is returned.
802    ///
803    /// The operator `==` is used to compare elements with `value` and must be defined,
804    /// otherwise `false` is assumed.
805    ///
806    /// # Example
807    ///
808    /// ```rhai
809    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
810    ///
811    /// print(x.index_of(4));       // prints 3 (first index)
812    ///
813    /// print(x.index_of(9));       // prints -1
814    ///
815    /// print(x.index_of("foo"));   // prints -1: strings do not equal numbers
816    /// ```
817    #[rhai_fn(return_raw, pure)]
818    pub fn index_of(
819        ctx: NativeCallContext,
820        array: &mut Array,
821        value: Dynamic,
822    ) -> RhaiResultOf<INT> {
823        if array.is_empty() {
824            return Ok(-1);
825        }
826
827        index_of_starting_from(ctx, array, value, 0)
828    }
829    /// Find the first element in the array, starting from a particular `start` position, that
830    /// equals a particular `value` and return its index. If no element equals `value`, `-1` is returned.
831    ///
832    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
833    /// * If `start` < -length of array, position counts from the beginning of the array.
834    /// * If `start` ≥ length of array, `-1` is returned.
835    ///
836    /// The operator `==` is used to compare elements with `value` and must be defined,
837    /// otherwise `false` is assumed.
838    ///
839    /// # Example
840    ///
841    /// ```rhai
842    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
843    ///
844    /// print(x.index_of(4, 2));        // prints 3
845    ///
846    /// print(x.index_of(4, 5));        // prints 7
847    ///
848    /// print(x.index_of(4, 15));       // prints -1: nothing found past end of array
849    ///
850    /// print(x.index_of(4, -5));       // prints 11: -5 = start from index 8
851    ///
852    /// print(x.index_of(9, 1));        // prints -1: nothing equals 9
853    ///
854    /// print(x.index_of("foo", 1));    // prints -1: strings do not equal numbers
855    /// ```
856    #[rhai_fn(name = "index_of", return_raw, pure)]
857    pub fn index_of_starting_from(
858        ctx: NativeCallContext,
859        array: &mut Array,
860        value: Dynamic,
861        start: INT,
862    ) -> RhaiResultOf<INT> {
863        if array.is_empty() {
864            return Ok(-1);
865        }
866
867        let (start, ..) = calc_offset_len(array.len(), start, 0);
868
869        for (i, item) in array.iter_mut().enumerate().skip(start) {
870            if ctx
871                .call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
872                .or_else(|err| match *err {
873                    ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
874                        if item.type_id() == value.type_id() {
875                            // No default when comparing same type
876                            Err(err)
877                        } else {
878                            Ok(Dynamic::FALSE)
879                        }
880                    }
881                    _ => Err(err),
882                })?
883                .as_bool()
884                .unwrap_or(false)
885            {
886                return Ok(INT::try_from(i).unwrap_or(INT::MAX));
887            }
888        }
889
890        Ok(-1 as INT)
891    }
892    /// Iterate through all the elements in the array, applying a `filter` function to each element
893    /// in turn, and return the index of the first element that returns `true`.
894    /// If no element returns `true`, `-1` is returned.
895    ///
896    /// # No Function Parameter
897    ///
898    /// Array element (mutable) is bound to `this`.
899    ///
900    /// This method is marked _pure_; the `filter` function should not mutate array elements.
901    ///
902    /// # Function Parameters
903    ///
904    /// * `element`: copy of array element
905    /// * `index` _(optional)_: current index in the array
906    ///
907    /// # Example
908    ///
909    /// ```rhai
910    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
911    ///
912    /// print(x.index_of(|v| v > 3));           // prints 3: 4 > 3
913    ///
914    /// print(x.index_of(|v| v > 8));           // prints -1: nothing is > 8
915    ///
916    /// print(x.index_of(|v, i| v * i > 20));   // prints 7: 4 * 7 > 20
917    /// ```
918    #[rhai_fn(name = "index_of", return_raw, pure)]
919    pub fn index_of_filter(
920        ctx: NativeCallContext,
921        array: &mut Array,
922        filter: FnPtr,
923    ) -> RhaiResultOf<INT> {
924        if array.is_empty() {
925            return Ok(-1);
926        }
927
928        index_of_filter_starting_from(ctx, array, filter, 0)
929    }
930    /// Iterate through all the elements in the array, starting from a particular `start` position,
931    /// applying a `filter` function to each element in turn, and return the index of the first
932    /// element that returns `true`. If no element returns `true`, `-1` is returned.
933    ///
934    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
935    /// * If `start` < -length of array, position counts from the beginning of the array.
936    /// * If `start` ≥ length of array, `-1` is returned.
937    ///
938    /// # No Function Parameter
939    ///
940    /// Array element (mutable) is bound to `this`.
941    ///
942    /// This method is marked _pure_; the `filter` function should not mutate array elements.
943    ///
944    /// # Function Parameters
945    ///
946    /// * `element`: copy of array element
947    /// * `index` _(optional)_: current index in the array
948    ///
949    /// # Example
950    ///
951    /// ```rhai
952    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
953    ///
954    /// print(x.index_of(|v| v > 1, 3));    // prints 5: 2 > 1
955    ///
956    /// print(x.index_of(|v| v < 2, 9));    // prints -1: nothing < 2 past index 9
957    ///
958    /// print(x.index_of(|v| v > 1, 15));   // prints -1: nothing found past end of array
959    ///
960    /// print(x.index_of(|v| v > 1, -5));   // prints 9: -5 = start from index 8
961    ///
962    /// print(x.index_of(|v| v > 1, -99));  // prints 1: -99 = start from beginning
963    ///
964    /// print(x.index_of(|v, i| v * i > 20, 8));    // prints 10: 3 * 10 > 20
965    /// ```
966    #[rhai_fn(name = "index_of", return_raw, pure)]
967    pub fn index_of_filter_starting_from(
968        ctx: NativeCallContext,
969        array: &mut Array,
970        filter: FnPtr,
971        start: INT,
972    ) -> RhaiResultOf<INT> {
973        if array.is_empty() {
974            return Ok(-1);
975        }
976
977        let (start, ..) = calc_offset_len(array.len(), start, 0);
978
979        for (i, item) in array.iter_mut().enumerate().skip(start) {
980            let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
981
982            if filter
983                .call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex, Some(0))?
984                .as_bool()
985                .unwrap_or(false)
986            {
987                return Ok(INT::try_from(i).unwrap_or(INT::MAX));
988            }
989        }
990
991        Ok(-1 as INT)
992    }
993    /// Iterate through all the elements in the array, applying a `filter` function to each element
994    /// in turn, and return a copy of the first element that returns `true`. If no element returns
995    /// `true`, `()` is returned.
996    ///
997    /// # No Function Parameter
998    ///
999    /// Array element (mutable) is bound to `this`.
1000    ///
1001    /// # Function Parameters
1002    ///
1003    /// * `element`: copy of array element
1004    /// * `index` _(optional)_: current index in the array
1005    ///
1006    /// # Example
1007    ///
1008    /// ```rhai
1009    /// let x = [1, 2, 3, 5, 8, 13];
1010    ///
1011    /// print(x.find(|v| v > 3));                    // prints 5: 5 > 3
1012    ///
1013    /// print(x.find(|v| v > 13) ?? "not found");    // prints "not found": nothing is > 13
1014    ///
1015    /// print(x.find(|v, i| v * i > 13));            // prints 5: 3 * 5 > 13
1016    /// ```
1017    #[rhai_fn(return_raw, pure)]
1018    pub fn find(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult {
1019        find_starting_from(ctx, array, filter, 0)
1020    }
1021    /// Iterate through all the elements in the array, starting from a particular `start` position,
1022    /// applying a `filter` function to each element in turn, and return a copy of the first element
1023    /// that returns `true`. If no element returns `true`, `()` is returned.
1024    ///
1025    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
1026    /// * If `start` < -length of array, position counts from the beginning of the array.
1027    /// * If `start` ≥ length of array, `-1` is returned.
1028    ///
1029    /// # No Function Parameter
1030    ///
1031    /// Array element (mutable) is bound to `this`.
1032    ///
1033    /// This method is marked _pure_; the `filter` function should not mutate array elements.
1034    ///
1035    /// # Function Parameters
1036    ///
1037    /// * `element`: copy of array element
1038    /// * `index` _(optional)_: current index in the array
1039    ///
1040    /// # Example
1041    ///
1042    /// ```rhai
1043    /// let x = [1, 2, 3, 5, 8, 13];
1044    ///
1045    /// print(x.find(|v| v > 1, 2));                     // prints 3: 3 > 1
1046    ///
1047    /// print(x.find(|v| v < 2, 3) ?? "not found");      // prints "not found": nothing < 2 past index 3
1048    ///
1049    /// print(x.find(|v| v > 1, 8) ?? "not found");      // prints "not found": nothing found past end of array
1050    ///
1051    /// print(x.find(|v| v > 1, -3));                    // prints 5: -3 = start from index 4
1052    ///
1053    /// print(x.find(|v| v > 0, -99));                   // prints 1: -99 = start from beginning
1054    ///
1055    /// print(x.find(|v, i| v * i > 6, 3));              // prints 5: 5 * 4 > 6
1056    /// ```
1057    #[rhai_fn(name = "find", return_raw, pure)]
1058    pub fn find_starting_from(
1059        ctx: NativeCallContext,
1060        array: &mut Array,
1061        filter: FnPtr,
1062        start: INT,
1063    ) -> RhaiResult {
1064        let index = index_of_filter_starting_from(ctx, array, filter, start)?;
1065
1066        if index < 0 {
1067            return Ok(Dynamic::UNIT);
1068        }
1069
1070        Ok(get(array, index))
1071    }
1072    /// Iterate through all the elements in the array, applying a `mapper` function to each element
1073    /// in turn, and return the first result that is not `()`. Otherwise, `()` is returned.
1074    ///
1075    /// # No Function Parameter
1076    ///
1077    /// Array element (mutable) is bound to `this`.
1078    ///
1079    /// This method is marked _pure_; the `mapper` function should not mutate array elements.
1080    ///
1081    /// # Function Parameters
1082    ///
1083    /// * `element`: copy of array element
1084    /// * `index` _(optional)_: current index in the array
1085    ///
1086    /// # Example
1087    ///
1088    /// ```rhai
1089    /// let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}];
1090    ///
1091    /// print(x.find_map(|v| v.alice));                  // prints 1
1092    ///
1093    /// print(x.find_map(|v| v.dave) ?? "not found");    // prints "not found"
1094    ///
1095    /// print(x.find_map(|| this.dave) ?? "not found");  // prints "not found"
1096    /// ```
1097    #[rhai_fn(return_raw, pure)]
1098    pub fn find_map(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResult {
1099        find_map_starting_from(ctx, array, filter, 0)
1100    }
1101    /// Iterate through all the elements in the array, starting from a particular `start` position,
1102    /// applying a `mapper` function to each element in turn, and return the first result that is not `()`.
1103    /// Otherwise, `()` is returned.
1104    ///
1105    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
1106    /// * If `start` < -length of array, position counts from the beginning of the array.
1107    /// * If `start` ≥ length of array, `-1` is returned.
1108    ///
1109    /// # No Function Parameter
1110    ///
1111    /// Array element (mutable) is bound to `this`.
1112    ///
1113    /// This method is marked _pure_; the `mapper` function should not mutate array elements.
1114    ///
1115    /// # Function Parameters
1116    ///
1117    /// * `element`: copy of array element
1118    /// * `index` _(optional)_: current index in the array
1119    ///
1120    /// # Example
1121    ///
1122    /// ```rhai
1123    /// let x = [#{alice: 1}, #{bob: 2}, #{bob: 3}, #{clara: 3}, #{alice: 0}, #{clara: 5}];
1124    ///
1125    /// print(x.find_map(|v| v.alice, 2));                   // prints 0
1126    ///
1127    /// print(x.find_map(|v| v.bob, 4) ?? "not found");      // prints "not found"
1128    ///
1129    /// print(x.find_map(|v| v.alice, 8) ?? "not found");    // prints "not found"
1130    ///
1131    /// print(x.find_map(|| this.alice, 8) ?? "not found");  // prints "not found"
1132    ///
1133    /// print(x.find_map(|v| v.bob, -4));                    // prints 3: -4 = start from index 2
1134    ///
1135    /// print(x.find_map(|v| v.alice, -99));                 // prints 1: -99 = start from beginning
1136    ///
1137    /// print(x.find_map(|| this.alice, -99));               // prints 1: -99 = start from beginning
1138    /// ```
1139    #[rhai_fn(name = "find_map", return_raw, pure)]
1140    pub fn find_map_starting_from(
1141        ctx: NativeCallContext,
1142        array: &mut Array,
1143        filter: FnPtr,
1144        start: INT,
1145    ) -> RhaiResult {
1146        if array.is_empty() {
1147            return Ok(Dynamic::UNIT);
1148        }
1149
1150        let (start, ..) = calc_offset_len(array.len(), start, 0);
1151
1152        for (i, item) in array.iter_mut().enumerate().skip(start) {
1153            let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
1154
1155            let value =
1156                filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex, Some(0))?;
1157
1158            if !value.is_unit() {
1159                return Ok(value);
1160            }
1161        }
1162
1163        Ok(Dynamic::UNIT)
1164    }
1165    /// Return `true` if any element in the array that returns `true` when applied the `filter` function.
1166    ///
1167    /// # No Function Parameter
1168    ///
1169    /// Array element (mutable) is bound to `this`.
1170    ///
1171    /// This method is marked _pure_; the `filter` function should not mutate array elements.
1172    ///
1173    /// # Function Parameters
1174    ///
1175    /// * `element`: copy of array element
1176    /// * `index` _(optional)_: current index in the array
1177    ///
1178    /// # Example
1179    ///
1180    /// ```rhai
1181    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
1182    ///
1183    /// print(x.some(|v| v > 3));       // prints true
1184    ///
1185    /// print(x.some(|v| v > 10));      // prints false
1186    ///
1187    /// print(x.some(|v, i| i > v));    // prints true
1188    /// ```
1189    #[rhai_fn(return_raw, pure)]
1190    pub fn some(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
1191        if array.is_empty() {
1192            return Ok(false);
1193        }
1194
1195        for (i, item) in array.iter_mut().enumerate() {
1196            let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
1197
1198            if filter
1199                .call_raw_with_extra_args("some", &ctx, Some(item), [], ex, Some(0))?
1200                .as_bool()
1201                .unwrap_or(false)
1202            {
1203                return Ok(true);
1204            }
1205        }
1206
1207        Ok(false)
1208    }
1209    /// Return `true` if all elements in the array return `true` when applied the `filter` function.
1210    ///
1211    /// # No Function Parameter
1212    ///
1213    /// Array element (mutable) is bound to `this`.
1214    ///
1215    /// This method is marked _pure_; the `filter` function should not mutate array elements.
1216    ///
1217    /// # Function Parameters
1218    ///
1219    /// * `element`: copy of array element
1220    /// * `index` _(optional)_: current index in the array
1221    ///
1222    /// # Example
1223    ///
1224    /// ```rhai
1225    /// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
1226    ///
1227    /// print(x.all(|v| v > 3));        // prints false
1228    ///
1229    /// print(x.all(|v| v > 1));        // prints true
1230    ///
1231    /// print(x.all(|v, i| i > v));     // prints false
1232    /// ```
1233    #[rhai_fn(return_raw, pure)]
1234    pub fn all(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
1235        if array.is_empty() {
1236            return Ok(true);
1237        }
1238
1239        for (i, item) in array.iter_mut().enumerate() {
1240            let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
1241
1242            if !filter
1243                .call_raw_with_extra_args("all", &ctx, Some(item), [], ex, Some(0))?
1244                .as_bool()
1245                .unwrap_or(false)
1246            {
1247                return Ok(false);
1248            }
1249        }
1250
1251        Ok(true)
1252    }
1253    /// Remove duplicated _consecutive_ elements from the array.
1254    ///
1255    /// The operator `==` is used to compare elements and must be defined,
1256    /// otherwise `false` is assumed.
1257    ///
1258    /// # Example
1259    ///
1260    /// ```rhai
1261    /// let x = [1, 2, 2, 2, 3, 4, 3, 3, 2, 1];
1262    ///
1263    /// x.dedup();
1264    ///
1265    /// print(x);       // prints "[1, 2, 3, 4, 3, 2, 1]"
1266    /// ```
1267    pub fn dedup(ctx: NativeCallContext, array: &mut Array) {
1268        let comparer = FnPtr {
1269            name: ctx.engine().get_interned_string(OP_EQUALS),
1270            curry: <_>::default(),
1271            #[cfg(not(feature = "no_function"))]
1272            env: None,
1273            typ: FnPtrType::Normal,
1274        };
1275        dedup_by_comparer(ctx, array, comparer);
1276    }
1277    /// Remove duplicated _consecutive_ elements from the array that return `true` when applied the
1278    /// `comparer` function.
1279    ///
1280    /// No element is removed if the correct `comparer` function does not exist.
1281    ///
1282    /// # Function Parameters
1283    ///
1284    /// * `element1`: copy of the current array element to compare
1285    /// * `element2`: copy of the next array element to compare
1286    ///
1287    /// ## Return Value
1288    ///
1289    /// `true` if `element1 == element2`, otherwise `false`.
1290    ///
1291    /// # Example
1292    ///
1293    /// ```rhai
1294    /// let x = [1, 2, 2, 2, 3, 1, 2, 3, 4, 3, 3, 2, 1];
1295    ///
1296    /// x.dedup(|a, b| a >= b);
1297    ///
1298    /// print(x);       // prints "[1, 2, 3, 4]"
1299    /// ```
1300    #[rhai_fn(name = "dedup")]
1301    pub fn dedup_by_comparer(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) {
1302        if array.is_empty() {
1303            return;
1304        }
1305
1306        array.dedup_by(|x, y| {
1307            comparer
1308                .call_raw(&ctx, None, [y.clone(), x.clone()])
1309                .unwrap_or(Dynamic::FALSE)
1310                .as_bool()
1311                .unwrap_or(false)
1312        });
1313    }
1314    /// Reduce an array by iterating through all elements while applying the `reducer` function.
1315    ///
1316    /// # Function Parameters
1317    ///
1318    /// * `result`: accumulated result, initially `()`
1319    /// * `element`: copy of array element, or bound to `this` if omitted
1320    /// * `index` _(optional)_: current index in the array
1321    ///
1322    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1323    ///
1324    /// # Example
1325    ///
1326    /// ```rhai
1327    /// let x = [1, 2, 3, 4, 5];
1328    ///
1329    /// let y = x.reduce(|r, v| v + (r ?? 0));
1330    ///
1331    /// print(y);       // prints 15
1332    ///
1333    /// let y = x.reduce(|r, v, i| v + i + (r ?? 0));
1334    ///
1335    /// print(y);       // prints 25
1336    /// ```
1337    #[rhai_fn(return_raw, pure)]
1338    pub fn reduce(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
1339        reduce_with_initial(ctx, array, reducer, Dynamic::UNIT)
1340    }
1341    /// Reduce an array by iterating through all elements while applying the `reducer` function.
1342    ///
1343    /// # Function Parameters
1344    ///
1345    /// * `result`: accumulated result, starting with the value of `initial`
1346    /// * `element`: copy of array element, or bound to `this` if omitted
1347    /// * `index` _(optional)_: current index in the array
1348    ///
1349    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1350    ///
1351    /// # Example
1352    ///
1353    /// ```rhai
1354    /// let x = [1, 2, 3, 4, 5];
1355    ///
1356    /// let y = x.reduce(|r, v| v + r, 5);
1357    ///
1358    /// print(y);       // prints 20
1359    ///
1360    /// let y = x.reduce(|r, v, i| v + i + r, 5);
1361    ///
1362    /// print(y);       // prints 30
1363    /// ```
1364    #[rhai_fn(name = "reduce", return_raw, pure)]
1365    pub fn reduce_with_initial(
1366        ctx: NativeCallContext,
1367        array: &mut Array,
1368        reducer: FnPtr,
1369        initial: Dynamic,
1370    ) -> RhaiResult {
1371        if array.is_empty() {
1372            return Ok(initial);
1373        }
1374
1375        array
1376            .iter_mut()
1377            .enumerate()
1378            .try_fold(initial, |result, (i, item)| {
1379                let ex = [INT::try_from(i).unwrap_or(INT::MAX).into()];
1380                reducer.call_raw_with_extra_args("reduce", &ctx, Some(item), [result], ex, Some(1))
1381            })
1382    }
1383    /// Reduce an array by iterating through all elements, in _reverse_ order,
1384    /// while applying the `reducer` function.
1385    ///
1386    /// # Function Parameters
1387    ///
1388    /// * `result`: accumulated result, initially `()`
1389    /// * `element`: copy of array element, or bound to `this` if omitted
1390    /// * `index` _(optional)_: current index in the array
1391    ///
1392    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1393    ///
1394    /// # Example
1395    ///
1396    /// ```rhai
1397    /// let x = [1, 2, 3, 4, 5];
1398    ///
1399    /// let y = x.reduce_rev(|r, v| v + (r ?? 0));
1400    ///
1401    /// print(y);       // prints 15
1402    ///
1403    /// let y = x.reduce_rev(|r, v, i| v + i + (r ?? 0));
1404    ///
1405    /// print(y);       // prints 25
1406    /// ```
1407    #[rhai_fn(return_raw, pure)]
1408    pub fn reduce_rev(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
1409        reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT)
1410    }
1411    /// Reduce an array by iterating through all elements, in _reverse_ order,
1412    /// while applying the `reducer` function.
1413    ///
1414    /// # Function Parameters
1415    ///
1416    /// * `result`: accumulated result, starting with the value of `initial`
1417    /// * `element`: copy of array element, or bound to `this` if omitted
1418    /// * `index` _(optional)_: current index in the array
1419    ///
1420    /// This method is marked _pure_; the `reducer` function should not mutate array elements.
1421    ///
1422    /// # Example
1423    ///
1424    /// ```rhai
1425    /// let x = [1, 2, 3, 4, 5];
1426    ///
1427    /// let y = x.reduce_rev(|r, v| v + r, 5);
1428    ///
1429    /// print(y);       // prints 20
1430    ///
1431    /// let y = x.reduce_rev(|r, v, i| v + i + r, 5);
1432    ///
1433    /// print(y);       // prints 30
1434    /// ```
1435    #[rhai_fn(name = "reduce_rev", return_raw, pure)]
1436    pub fn reduce_rev_with_initial(
1437        ctx: NativeCallContext,
1438        array: &mut Array,
1439        reducer: FnPtr,
1440        initial: Dynamic,
1441    ) -> RhaiResult {
1442        if array.is_empty() {
1443            return Ok(initial);
1444        }
1445
1446        let len = array.len();
1447
1448        array
1449            .iter_mut()
1450            .rev()
1451            .enumerate()
1452            .try_fold(initial, |result, (i, item)| {
1453                let ex = [INT::try_from(len - 1 - i).unwrap_or(INT::MAX).into()];
1454
1455                reducer.call_raw_with_extra_args(
1456                    "reduce_rev",
1457                    &ctx,
1458                    Some(item),
1459                    [result],
1460                    ex,
1461                    Some(1),
1462                )
1463            })
1464    }
1465    /// Iterate through all elements in two arrays, applying a `mapper` function to them,
1466    /// and return a new array containing the results.
1467    ///
1468    /// # Function Parameters
1469    ///
1470    /// * `array1`: First array
1471    /// * `array2`: Second array
1472    /// * `index` _(optional)_: current index in the array
1473    ///
1474    /// # Example
1475    ///
1476    /// ```rhai
1477    /// let x = [1, 2, 3, 4, 5];
1478    /// let y = [9, 8, 7, 6];
1479    ///
1480    /// let z = x.zip(y, |a, b| a + b);
1481    ///
1482    /// print(z);       // prints [10, 10, 10, 10]
1483    ///
1484    /// let z = x.zip(y, |a, b, i| a + b + i);
1485    ///
1486    /// print(z);       // prints [10, 11, 12, 13]
1487    /// ```
1488    #[rhai_fn(return_raw, pure)]
1489    pub fn zip(
1490        ctx: NativeCallContext,
1491        array1: &mut Array,
1492        array2: Array,
1493        map: FnPtr,
1494    ) -> RhaiResultOf<Array> {
1495        if array1.is_empty() && array2.is_empty() {
1496            return Ok(Array::new());
1497        }
1498
1499        array1
1500            .iter_mut()
1501            .zip(array2)
1502            .enumerate()
1503            .map(|(i, (x, y))| {
1504                map.call_raw_with_extra_args(
1505                    "zip",
1506                    &ctx,
1507                    None,
1508                    [x.clone(), y],
1509                    [INT::try_from(i).unwrap_or(INT::MAX).into()],
1510                    None,
1511                )
1512            })
1513            .collect()
1514    }
1515    /// Sort the array based on applying the `comparer` function.
1516    ///
1517    /// The `comparer` function must implement a [total order](https://en.wikipedia.org/wiki/Total_order).
1518    ///
1519    /// # Function Parameters
1520    ///
1521    /// * `element1`: copy of the current array element to compare
1522    /// * `element2`: copy of the next array element to compare
1523    ///
1524    /// ## Return Value
1525    ///
1526    /// An integer number:
1527    ///
1528    /// * Any positive integer if `element1 > element2`
1529    /// * 0 if `element1 == element2`
1530    /// * Any negative integer if `element1 < element2`
1531    ///
1532    /// or a boolean value:
1533    ///
1534    /// * `true` if `element1 <= element2`
1535    /// * `false` if `element1 > element2`
1536    ///
1537    /// Any other return value type will yield unpredictable order.
1538    ///
1539    /// # Errors
1540    ///
1541    /// If the `comparer` function does not implement a [total order](https://en.wikipedia.org/wiki/Total_order),
1542    /// an error is returned.
1543    ///
1544    /// # Example
1545    ///
1546    /// ```rhai
1547    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1548    ///
1549    /// // Do comparisons in reverse
1550    /// x.sort_by(|a, b| if a > b { -1 } else if a < b { 1 } else { 0 });
1551    ///
1552    /// print(x);       // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
1553    /// ```
1554    #[rhai_fn(name = "sort", name = "sort_by", return_raw)]
1555    pub fn sort_by(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) -> RhaiResultOf<()> {
1556        if array.len() <= 1 {
1557            return Ok(());
1558        }
1559
1560        let closure = || {
1561            array.sort_by(|x, y| {
1562                comparer
1563                    .call_raw(&ctx, None, [x.clone(), y.clone()])
1564                    .ok()
1565                    .and_then(|v| {
1566                        v.as_int()
1567                            .or_else(|_| v.as_bool().map(|v| if v { -1 } else { 1 }))
1568                            .ok()
1569                    })
1570                    .map_or_else(
1571                        || x.type_id().cmp(&y.type_id()),
1572                        |v| match v {
1573                            v if v > 0 => Ordering::Greater,
1574                            v if v < 0 => Ordering::Less,
1575                            0 => Ordering::Equal,
1576                            _ => unreachable!("v is {}", v),
1577                        },
1578                    )
1579            });
1580        };
1581
1582        // `slice::sort_by` may panic if the comparer function does not implement a total order.
1583        // Catch this instead.
1584        #[cfg(not(feature = "no_std"))]
1585        return std::panic::catch_unwind(std::panic::AssertUnwindSafe(closure)).map_err(|_| {
1586            ERR::ErrorRuntime("error in comparer for sorting".into(), Position::NONE).into()
1587        });
1588
1589        #[cfg(feature = "no_std")]
1590        {
1591            let mut closure = closure;
1592            closure();
1593            Ok(())
1594        }
1595    }
1596    /// Sort the array.
1597    ///
1598    /// All elements in the array must be of the same data type.
1599    ///
1600    /// # Supported Data Types
1601    ///
1602    /// * integer numbers
1603    /// * floating-point numbers
1604    /// * decimal numbers
1605    /// * characters
1606    /// * strings
1607    /// * booleans
1608    /// * `()`
1609    ///
1610    /// # Example
1611    ///
1612    /// ```rhai
1613    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1614    ///
1615    /// x.sort();
1616    ///
1617    /// print(x);       // prints "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
1618    /// ```
1619    #[rhai_fn(name = "sort", return_raw)]
1620    pub fn sort_with_builtin(array: &mut Array) -> RhaiResultOf<()> {
1621        if array.len() <= 1 {
1622            return Ok(());
1623        }
1624
1625        let type_id = array[0].type_id();
1626
1627        if array.iter().any(|a| a.type_id() != type_id) {
1628            return Err(ERR::ErrorFunctionNotFound(
1629                "elements of different types cannot be sorted".into(),
1630                Position::NONE,
1631            )
1632            .into());
1633        }
1634
1635        if type_id == TypeId::of::<INT>() {
1636            array.sort_by(|a, b| {
1637                let a = a.as_int().unwrap();
1638                let b = b.as_int().unwrap();
1639                a.cmp(&b)
1640            });
1641            return Ok(());
1642        }
1643        if type_id == TypeId::of::<char>() {
1644            array.sort_by(|a, b| {
1645                let a = a.as_char().unwrap();
1646                let b = b.as_char().unwrap();
1647                a.cmp(&b)
1648            });
1649            return Ok(());
1650        }
1651        #[cfg(not(feature = "no_float"))]
1652        if type_id == TypeId::of::<crate::FLOAT>() {
1653            array.sort_by(|a, b| {
1654                let a = a.as_float().unwrap();
1655                let b = b.as_float().unwrap();
1656                a.partial_cmp(&b).unwrap_or(Ordering::Equal)
1657            });
1658            return Ok(());
1659        }
1660        if type_id == TypeId::of::<ImmutableString>() {
1661            array.sort_by(|a, b| {
1662                let a = &*a.as_immutable_string_ref().unwrap();
1663                let b = &*b.as_immutable_string_ref().unwrap();
1664                a.cmp(b)
1665            });
1666            return Ok(());
1667        }
1668        #[cfg(feature = "decimal")]
1669        if type_id == TypeId::of::<rust_decimal::Decimal>() {
1670            array.sort_by(|a, b| {
1671                let a = a.as_decimal().unwrap();
1672                let b = b.as_decimal().unwrap();
1673                a.cmp(&b)
1674            });
1675            return Ok(());
1676        }
1677        if type_id == TypeId::of::<bool>() {
1678            array.sort_by(|a, b| {
1679                let a = a.as_bool().unwrap();
1680                let b = b.as_bool().unwrap();
1681                a.cmp(&b)
1682            });
1683            return Ok(());
1684        }
1685        if type_id == TypeId::of::<()>() {
1686            return Ok(());
1687        }
1688
1689        Err(ERR::ErrorFunctionNotFound(
1690            format!("elements of {} cannot be sorted", array[0].type_name()),
1691            Position::NONE,
1692        )
1693        .into())
1694    }
1695    /// Sort the array in descending order.
1696    ///
1697    /// All elements in the array must be of the same data type.
1698    ///
1699    /// # Supported Data Types
1700    ///
1701    /// * integer numbers
1702    /// * floating-point numbers
1703    /// * decimal numbers
1704    /// * characters
1705    /// * strings
1706    /// * booleans
1707    /// * `()`
1708    ///
1709    /// # Example
1710    ///
1711    /// ```rhai
1712    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1713    ///
1714    /// x.sort_desc();
1715    ///
1716    /// print(x);       // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
1717    /// ```
1718    #[rhai_fn(name = "sort_desc", return_raw)]
1719    pub fn sort_with_builtin_desc(array: &mut Array) -> RhaiResultOf<()> {
1720        if array.len() <= 1 {
1721            return Ok(());
1722        }
1723
1724        let type_id = array[0].type_id();
1725
1726        if array.iter().any(|a| a.type_id() != type_id) {
1727            return Err(ERR::ErrorFunctionNotFound(
1728                "elements of different types cannot be sorted".into(),
1729                Position::NONE,
1730            )
1731            .into());
1732        }
1733
1734        if type_id == TypeId::of::<INT>() {
1735            array.sort_by(|a, b| {
1736                let a = a.as_int().unwrap();
1737                let b = b.as_int().unwrap();
1738                match a.cmp(&b) {
1739                    Ordering::Less => Ordering::Greater,
1740                    Ordering::Equal => Ordering::Equal,
1741                    Ordering::Greater => Ordering::Less,
1742                }
1743            });
1744            return Ok(());
1745        }
1746        if type_id == TypeId::of::<char>() {
1747            array.sort_by(|a, b| {
1748                let a = a.as_char().unwrap();
1749                let b = b.as_char().unwrap();
1750                match a.cmp(&b) {
1751                    Ordering::Less => Ordering::Greater,
1752                    Ordering::Equal => Ordering::Equal,
1753                    Ordering::Greater => Ordering::Less,
1754                }
1755            });
1756            return Ok(());
1757        }
1758        #[cfg(not(feature = "no_float"))]
1759        if type_id == TypeId::of::<crate::FLOAT>() {
1760            array.sort_by(|a, b| {
1761                let a = a.as_float().unwrap();
1762                let b = b.as_float().unwrap();
1763                match a.partial_cmp(&b).unwrap_or(Ordering::Equal) {
1764                    Ordering::Less => Ordering::Greater,
1765                    Ordering::Equal => Ordering::Equal,
1766                    Ordering::Greater => Ordering::Less,
1767                }
1768            });
1769            return Ok(());
1770        }
1771        if type_id == TypeId::of::<ImmutableString>() {
1772            array.sort_by(|a, b| {
1773                let a = &*a.as_immutable_string_ref().unwrap();
1774                let b = &*b.as_immutable_string_ref().unwrap();
1775                match a.cmp(b) {
1776                    Ordering::Less => Ordering::Greater,
1777                    Ordering::Equal => Ordering::Equal,
1778                    Ordering::Greater => Ordering::Less,
1779                }
1780            });
1781            return Ok(());
1782        }
1783        #[cfg(feature = "decimal")]
1784        if type_id == TypeId::of::<rust_decimal::Decimal>() {
1785            array.sort_by(|a, b| {
1786                let a = a.as_decimal().unwrap();
1787                let b = b.as_decimal().unwrap();
1788                match a.cmp(&b) {
1789                    Ordering::Less => Ordering::Greater,
1790                    Ordering::Equal => Ordering::Equal,
1791                    Ordering::Greater => Ordering::Less,
1792                }
1793            });
1794            return Ok(());
1795        }
1796        if type_id == TypeId::of::<bool>() {
1797            array.sort_by(|a, b| {
1798                let a = a.as_bool().unwrap();
1799                let b = b.as_bool().unwrap();
1800                match a.cmp(&b) {
1801                    Ordering::Less => Ordering::Greater,
1802                    Ordering::Equal => Ordering::Equal,
1803                    Ordering::Greater => Ordering::Less,
1804                }
1805            });
1806            return Ok(());
1807        }
1808        if type_id == TypeId::of::<()>() {
1809            return Ok(());
1810        }
1811
1812        Err(ERR::ErrorFunctionNotFound(
1813            format!("elements of {} cannot be sorted", array[0].type_name()),
1814            Position::NONE,
1815        )
1816        .into())
1817    }
1818    /// Sort the array based on applying the `comparer` function and return it as a new array.
1819    ///
1820    /// The `comparer` function must implement a [total order](https://en.wikipedia.org/wiki/Total_order).
1821    ///
1822    /// # Function Parameters
1823    ///
1824    /// * `element1`: copy of the current array element to compare
1825    /// * `element2`: copy of the next array element to compare
1826    ///
1827    /// ## Return Value
1828    ///
1829    /// An integer number:
1830    ///
1831    /// * Any positive integer if `element1 > element2`
1832    /// * 0 if `element1 == element2`
1833    /// * Any negative integer if `element1 < element2`
1834    ///
1835    /// or a boolean value:
1836    ///
1837    /// * `true` if `element1 <= element2`
1838    /// * `false` if `element1 > element2`
1839    ///
1840    /// Any other return value type will yield unpredictable order.
1841    ///
1842    /// # Errors
1843    ///
1844    /// If the `comparer` function does not implement a [total order](https://en.wikipedia.org/wiki/Total_order),
1845    /// an error is returned.
1846    ///
1847    /// # Example
1848    ///
1849    /// ```rhai
1850    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1851    ///
1852    /// // Do comparisons in reverse
1853    /// let y = x.order_by(|a, b| if a > b { -1 } else if a < b { 1 } else { 0 });
1854    ///
1855    /// print(y);       // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
1856    /// ```
1857    #[rhai_fn(name = "order", name = "order_by", return_raw)]
1858    pub fn order_by(
1859        ctx: NativeCallContext,
1860        array: &mut Array,
1861        comparer: FnPtr,
1862    ) -> RhaiResultOf<Array> {
1863        let mut array = array.clone();
1864        sort_by(ctx, &mut array, comparer)?;
1865        Ok(array)
1866    }
1867    /// Sort the array and return it as a new array.
1868    ///
1869    /// All elements in the array must be of the same data type.
1870    ///
1871    /// # Supported Data Types
1872    ///
1873    /// * integer numbers
1874    /// * floating-point numbers
1875    /// * decimal numbers
1876    /// * characters
1877    /// * strings
1878    /// * booleans
1879    /// * `()`
1880    ///
1881    /// # Example
1882    ///
1883    /// ```rhai
1884    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1885    ///
1886    /// let y = x.order();
1887    ///
1888    /// print(y);       // prints "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
1889    /// ```
1890    #[rhai_fn(name = "order", return_raw)]
1891    pub fn order_with_builtin(array: &mut Array) -> RhaiResultOf<Array> {
1892        let mut array = array.clone();
1893        sort_with_builtin(&mut array)?;
1894        Ok(array)
1895    }
1896    /// Sort the array in descending order and return it as a new array.
1897    ///
1898    /// All elements in the array must be of the same data type.
1899    ///
1900    /// # Supported Data Types
1901    ///
1902    /// * integer numbers
1903    /// * floating-point numbers
1904    /// * decimal numbers
1905    /// * characters
1906    /// * strings
1907    /// * booleans
1908    /// * `()`
1909    ///
1910    /// # Example
1911    ///
1912    /// ```rhai
1913    /// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
1914    ///
1915    /// let y = x.order_desc();
1916    ///
1917    /// print(y);       // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
1918    /// ```
1919    #[rhai_fn(name = "order_desc", return_raw)]
1920    pub fn order_with_builtin_desc(array: &mut Array) -> RhaiResultOf<Array> {
1921        let mut array = array.clone();
1922        sort_with_builtin_desc(&mut array)?;
1923        Ok(array)
1924    }
1925    /// Remove all elements in the array that returns `true` when applied the `filter` function and
1926    /// return them as a new array.
1927    ///
1928    /// # No Function Parameter
1929    ///
1930    /// Array element (mutable) is bound to `this`.
1931    ///
1932    /// # Function Parameters
1933    ///
1934    /// * `element`: copy of array element
1935    /// * `index` _(optional)_: current index in the array
1936    ///
1937    /// # Example
1938    ///
1939    /// ```rhai
1940    /// let x = [1, 2, 3, 4, 5];
1941    ///
1942    /// let y = x.drain(|v| v < 3);
1943    ///
1944    /// print(x);       // prints "[3, 4, 5]"
1945    ///
1946    /// print(y);       // prints "[1, 2]"
1947    ///
1948    /// let z = x.drain(|v, i| v + i > 5);
1949    ///
1950    /// print(x);       // prints "[3, 4]"
1951    ///
1952    /// print(z);       // prints "[5]"
1953    /// ```
1954    #[rhai_fn(return_raw)]
1955    pub fn drain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
1956        if array.is_empty() {
1957            return Ok(Array::new());
1958        }
1959
1960        let mut drained = Array::with_capacity(array.len());
1961
1962        let mut i = 0;
1963        let mut x = 0;
1964
1965        while x < array.len() {
1966            let ex = [(i as INT).into()];
1967
1968            if filter
1969                .call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
1970                .as_bool()
1971                .unwrap_or(false)
1972            {
1973                drained.push(array.remove(x));
1974            } else {
1975                x += 1;
1976            }
1977
1978            i += 1;
1979        }
1980
1981        Ok(drained)
1982    }
1983    /// Remove all elements in the array within an exclusive `range` and return them as a new array.
1984    ///
1985    /// # Example
1986    ///
1987    /// ```rhai
1988    /// let x = [1, 2, 3, 4, 5];
1989    ///
1990    /// let y = x.drain(1..3);
1991    ///
1992    /// print(x);       // prints "[1, 4, 5]"
1993    ///
1994    /// print(y);       // prints "[2, 3]"
1995    ///
1996    /// let z = x.drain(2..3);
1997    ///
1998    /// print(x);       // prints "[1, 4]"
1999    ///
2000    /// print(z);       // prints "[5]"
2001    /// ```
2002    #[rhai_fn(name = "drain")]
2003    pub fn drain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
2004        let start = INT::max(range.start, 0);
2005        let end = INT::max(range.end, start);
2006        drain_range(array, start, end - start)
2007    }
2008    /// Remove all elements in the array within an inclusive `range` and return them as a new array.
2009    ///
2010    /// # Example
2011    ///
2012    /// ```rhai
2013    /// let x = [1, 2, 3, 4, 5];
2014    ///
2015    /// let y = x.drain(1..=2);
2016    ///
2017    /// print(x);       // prints "[1, 4, 5]"
2018    ///
2019    /// print(y);       // prints "[2, 3]"
2020    ///
2021    /// let z = x.drain(2..=2);
2022    ///
2023    /// print(x);       // prints "[1, 4]"
2024    ///
2025    /// print(z);       // prints "[5]"
2026    /// ```
2027    #[rhai_fn(name = "drain")]
2028    pub fn drain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
2029        let start = INT::max(*range.start(), 0);
2030        let end = INT::min(INT::max(*range.end(), start), INT::MAX - 1);
2031        drain_range(array, start, end - start + 1)
2032    }
2033    /// Remove all elements within a portion of the array and return them as a new array.
2034    ///
2035    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
2036    /// * If `start` < -length of array, position counts from the beginning of the array.
2037    /// * If `start` ≥ length of array, no element is removed and an empty array is returned.
2038    /// * If `len` ≤ 0, no element is removed and an empty array is returned.
2039    /// * If `start` position + `len` ≥ length of array, entire portion of the array after the `start` position is removed and returned.
2040    ///
2041    /// # Example
2042    ///
2043    /// ```rhai
2044    /// let x = [1, 2, 3, 4, 5];
2045    ///
2046    /// let y = x.drain(1, 2);
2047    ///
2048    /// print(x);       // prints "[1, 4, 5]"
2049    ///
2050    /// print(y);       // prints "[2, 3]"
2051    ///
2052    /// let z = x.drain(-1, 1);
2053    ///
2054    /// print(x);       // prints "[1, 4]"
2055    ///
2056    /// print(z);       // prints "[5]"
2057    /// ```
2058    #[rhai_fn(name = "drain")]
2059    pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
2060        if array.is_empty() || len <= 0 {
2061            return Array::new();
2062        }
2063
2064        let (start, len) = calc_offset_len(array.len(), start, len);
2065
2066        if len == 0 {
2067            return Array::new();
2068        }
2069
2070        array.drain(start..start + len).collect()
2071    }
2072    /// Remove all elements in the array that do not return `true` when applied the `filter`
2073    /// function and return them as a new array.
2074    ///
2075    /// # No Function Parameter
2076    ///
2077    /// Array element (mutable) is bound to `this`.
2078    ///
2079    /// # Function Parameters
2080    ///
2081    /// * `element`: copy of array element
2082    /// * `index` _(optional)_: current index in the array
2083    ///
2084    /// # Example
2085    ///
2086    /// ```rhai
2087    /// let x = [1, 2, 3, 4, 5];
2088    ///
2089    /// let y = x.retain(|v| v >= 3);
2090    ///
2091    /// print(x);       // prints "[3, 4, 5]"
2092    ///
2093    /// print(y);       // prints "[1, 2]"
2094    ///
2095    /// let z = x.retain(|v, i| v + i <= 5);
2096    ///
2097    /// print(x);       // prints "[3, 4]"
2098    ///
2099    /// print(z);       // prints "[5]"
2100    /// ```
2101    #[rhai_fn(return_raw)]
2102    pub fn retain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
2103        if array.is_empty() {
2104            return Ok(Array::new());
2105        }
2106
2107        let mut drained = Array::new();
2108
2109        let mut i = 0;
2110        let mut x = 0;
2111
2112        while x < array.len() {
2113            let ex = [(i as INT).into()];
2114
2115            if filter
2116                .call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
2117                .as_bool()
2118                .unwrap_or(false)
2119            {
2120                x += 1;
2121            } else {
2122                drained.push(array.remove(x));
2123            }
2124
2125            i += 1;
2126        }
2127
2128        Ok(drained)
2129    }
2130    /// Remove all elements in the array not within an exclusive `range` and return them as a new array.
2131    ///
2132    /// # Example
2133    ///
2134    /// ```rhai
2135    /// let x = [1, 2, 3, 4, 5];
2136    ///
2137    /// let y = x.retain(1..4);
2138    ///
2139    /// print(x);       // prints "[2, 3, 4]"
2140    ///
2141    /// print(y);       // prints "[1, 5]"
2142    ///
2143    /// let z = x.retain(1..3);
2144    ///
2145    /// print(x);       // prints "[3, 4]"
2146    ///
2147    /// print(z);       // prints "[1]"
2148    /// ```
2149    #[rhai_fn(name = "retain")]
2150    pub fn retain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
2151        let start = INT::max(range.start, 0);
2152        let end = INT::max(range.end, start);
2153        retain_range(array, start, end - start)
2154    }
2155    /// Remove all elements in the array not within an inclusive `range` and return them as a new array.
2156    ///
2157    /// # Example
2158    ///
2159    /// ```rhai
2160    /// let x = [1, 2, 3, 4, 5];
2161    ///
2162    /// let y = x.retain(1..=3);
2163    ///
2164    /// print(x);       // prints "[2, 3, 4]"
2165    ///
2166    /// print(y);       // prints "[1, 5]"
2167    ///
2168    /// let z = x.retain(1..=2);
2169    ///
2170    /// print(x);       // prints "[3, 4]"
2171    ///
2172    /// print(z);       // prints "[1]"
2173    /// ```
2174    #[rhai_fn(name = "retain")]
2175    pub fn retain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
2176        let start = INT::max(*range.start(), 0);
2177        let end = INT::min(INT::max(*range.end(), start), INT::MAX - 1);
2178        retain_range(array, start, end - start + 1)
2179    }
2180    /// Remove all elements not within a portion of the array and return them as a new array.
2181    ///
2182    /// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
2183    /// * If `start` < -length of array, position counts from the beginning of the array.
2184    /// * If `start` ≥ length of array, all elements are removed returned.
2185    /// * If `len` ≤ 0, all elements are removed and returned.
2186    /// * If `start` position + `len` ≥ length of array, entire portion of the array before the `start` position is removed and returned.
2187    ///
2188    /// # Example
2189    ///
2190    /// ```rhai
2191    /// let x = [1, 2, 3, 4, 5];
2192    ///
2193    /// let y = x.retain(1, 2);
2194    ///
2195    /// print(x);       // prints "[2, 3]"
2196    ///
2197    /// print(y);       // prints "[1, 4, 5]"
2198    ///
2199    /// let z = x.retain(-1, 1);
2200    ///
2201    /// print(x);       // prints "[3]"
2202    ///
2203    /// print(z);       // prints "[2]"
2204    /// ```
2205    #[rhai_fn(name = "retain")]
2206    pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
2207        if array.is_empty() || len <= 0 {
2208            return Array::new();
2209        }
2210
2211        let (start, len) = calc_offset_len(array.len(), start, len);
2212
2213        if len == 0 {
2214            return Array::new();
2215        }
2216
2217        let mut drained: Array = array.drain(..start).collect();
2218        drained.extend(array.drain(len..));
2219
2220        drained
2221    }
2222    /// Return `true` if two arrays are equal (i.e. all elements are equal and in the same order).
2223    ///
2224    /// The operator `==` is used to compare elements and must be defined,
2225    /// otherwise `false` is assumed.
2226    ///
2227    /// # Example
2228    ///
2229    /// ```rhai
2230    /// let x = [1, 2, 3, 4, 5];
2231    /// let y = [1, 2, 3, 4, 5];
2232    /// let z = [1, 2, 3, 4];
2233    ///
2234    /// print(x == y);      // prints true
2235    ///
2236    /// print(x == z);      // prints false
2237    /// ```
2238    #[rhai_fn(name = "==", return_raw, pure)]
2239    pub fn equals(ctx: NativeCallContext, array1: &mut Array, array2: Array) -> RhaiResultOf<bool> {
2240        if array1.len() != array2.len() {
2241            return Ok(false);
2242        }
2243        if array1.is_empty() {
2244            return Ok(true);
2245        }
2246
2247        let mut array2 = array2;
2248
2249        for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
2250            if !ctx
2251                .call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2])
2252                .or_else(|err| match *err {
2253                    ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
2254                        if a1.type_id() == a2.type_id() {
2255                            // No default when comparing same type
2256                            Err(err)
2257                        } else {
2258                            Ok(Dynamic::FALSE)
2259                        }
2260                    }
2261                    _ => Err(err),
2262                })?
2263                .as_bool()
2264                .unwrap_or(false)
2265            {
2266                return Ok(false);
2267            }
2268        }
2269
2270        Ok(true)
2271    }
2272    /// Return `true` if two arrays are not-equal (i.e. any element not equal or not in the same order).
2273    ///
2274    /// The operator `==` is used to compare elements and must be defined,
2275    /// otherwise `false` is assumed.
2276    ///
2277    /// # Example
2278    ///
2279    /// ```rhai
2280    /// let x = [1, 2, 3, 4, 5];
2281    /// let y = [1, 2, 3, 4, 5];
2282    /// let z = [1, 2, 3, 4];
2283    ///
2284    /// print(x != y);      // prints false
2285    ///
2286    /// print(x != z);      // prints true
2287    /// ```
2288    #[rhai_fn(name = "!=", return_raw, pure)]
2289    pub fn not_equals(
2290        ctx: NativeCallContext,
2291        array1: &mut Array,
2292        array2: Array,
2293    ) -> RhaiResultOf<bool> {
2294        equals(ctx, array1, array2).map(|r| !r)
2295    }
2296}