o1_utils/
array.rs

1//! This module provides different helpers in creating constant sized
2//! arrays and converting them to different formats.
3//!
4//! Functions in this module are not necessarily optimal in terms of
5//! allocations, as they tend to create intermediate vectors. For
6//! better performance, either optimise this code, or use
7//! (non-fixed-sized) vectors.
8
9// Use a generic function so that the pointer cast remains type-safe
10/// Converts a vector of elements to a boxed one. Semantically
11/// equivalent to `vector.into_boxed_slice().try_into().unwrap()`.
12pub fn vec_to_boxed_array<T, const N: usize>(vec: Vec<T>) -> Box<[T; N]> {
13    vec.into_boxed_slice()
14        .try_into()
15        .unwrap_or_else(|_| panic!("vec_to_boxed_array: length mismatch, expected {}", N))
16}
17
18// @volhovm It could potentially be more efficient with unsafe tricks.
19/// Converts a two-dimensional vector to a constant sized two-dimensional array.
20pub fn vec_to_boxed_array2<T: Clone, const N: usize, const M: usize>(
21    vec: Vec<Vec<T>>,
22) -> Box<[[T; N]; M]> {
23    let mut vec_of_slices2: Vec<[T; N]> = vec![];
24    vec.into_iter().for_each(|x: Vec<T>| {
25        let y: Box<[T]> = x.into_boxed_slice();
26        let z: Box<[T; N]> = y
27            .try_into()
28            .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch inner array"));
29        let zz: &[[T; N]] = std::slice::from_ref(z.as_ref());
30        vec_of_slices2.extend_from_slice(zz);
31    });
32
33    vec_of_slices2
34        .into_boxed_slice()
35        .try_into()
36        .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch outer array"))
37}
38
39/// Converts a three-dimensional vector to a constant sized two-dimensional array.
40pub fn vec_to_boxed_array3<T: Clone, const N: usize, const M: usize, const K: usize>(
41    vec: Vec<Vec<Vec<T>>>,
42) -> Box<[[[T; N]; M]; K]> {
43    let mut vec_of_slices2: Vec<[[T; N]; M]> = vec![];
44    vec.into_iter().for_each(|x| {
45        let r: Box<[[T; N]; M]> = vec_to_boxed_array2(x);
46        let zz: &[[[T; N]; M]] = std::slice::from_ref(r.as_ref());
47        vec_of_slices2.extend_from_slice(zz);
48    });
49
50    vec_of_slices2
51        .into_boxed_slice()
52        .try_into()
53        .unwrap_or_else(|_| panic!("vec_to_boxed_array3: length mismatch outer array"))
54}
55
56/// A macro similar to `vec![$elem; $size]` which returns a boxed
57/// array, allocated directly on the heap (via a vector, with reallocations).
58///
59/// ```rustc
60///     let _: Box<[u8; 1024]> = box_array![0; 1024];
61/// ```
62///
63/// See
64/// <https://stackoverflow.com/questions/25805174/creating-a-fixed-size-array-on-heap-in-rust/68122278#68122278>
65#[macro_export]
66macro_rules! box_array {
67    ($val:expr ; $len:expr) => {{
68        // Use a generic function so that the pointer cast remains type-safe
69        fn vec_to_boxed_array<T>(vec: Vec<T>) -> Box<[T; $len]> {
70            (vec.into_boxed_slice())
71                .try_into()
72                .unwrap_or_else(|_| panic!("box_array: length mismatch"))
73        }
74
75        vec_to_boxed_array(vec![$val; $len])
76    }};
77}
78
79/// A macro similar to `vec![vec![$elem; $size1]; $size2]` which
80/// returns a two-dimensional boxed array, allocated directly on the
81/// heap (via a vector, with reallocations).
82///
83/// ```rustc
84///     let _: Box<[[u8; 1024]; 512]> = box_array![0; 1024; 512];
85/// ```
86///
87#[macro_export]
88macro_rules! box_array2 {
89    ($val:expr; $len1:expr; $len2:expr) => {{
90        fn vec_to_boxed_array2<T: Clone, const N: usize, const M: usize>(
91            vec: Vec<Vec<T>>,
92        ) -> Box<[[T; N]; M]> {
93            let mut vec_of_slices2: Vec<[T; N]> = vec![];
94            vec.into_iter().for_each(|x: Vec<T>| {
95                let y: Box<[T]> = x.into_boxed_slice();
96                let z: Box<[T; N]> = y
97                    .try_into()
98                    .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch inner array"));
99                let zz: &[[T; N]] = std::slice::from_ref(z.as_ref());
100                vec_of_slices2.extend_from_slice(zz);
101            });
102
103            vec_of_slices2
104                .into_boxed_slice()
105                .try_into()
106                .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch outer array"))
107        }
108
109        vec_to_boxed_array2(vec![vec![$val; $len1]; $len2])
110    }};
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    use ark_ec::AffineRepr;
118    use ark_ff::{UniformRand, Zero};
119    use mina_curves::pasta::Pallas as CurvePoint;
120
121    pub type BaseField = <CurvePoint as AffineRepr>::BaseField;
122
123    #[test]
124    /// Tests whether initialising different arrays creates a stack
125    /// overflow. The usual default size of the stack is 128kB.
126    fn test_boxed_stack_overflow() {
127        // Each field element is assumed to be 256 bits, so 1.000.000
128        // elements is 30MB. This often overflows the stack if created
129        // as an array.
130        let _boxed: Box<[[BaseField; 1000000]; 1]> =
131            vec_to_boxed_array2(vec![vec![BaseField::zero(); 1000000]; 1]);
132        let _boxed: Box<[[BaseField; 250000]; 4]> =
133            vec_to_boxed_array2(vec![vec![BaseField::zero(); 250000]; 4]);
134        let _boxed: Box<[BaseField; 1000000]> = box_array![BaseField::zero(); 1000000];
135        let _boxed: Box<[[BaseField; 250000]; 4]> = box_array2![BaseField::zero(); 250000; 4];
136    }
137
138    #[test]
139    /// Tests whether boxed array tranformations preserve the elements.
140    fn test_boxed_stack_completeness() {
141        let mut rng = crate::tests::make_test_rng(None);
142
143        const ARR_SIZE: usize = 100;
144
145        let vector1: Vec<usize> = (0..ARR_SIZE).map(|i| i * i).collect();
146        let boxed1: Box<[usize; ARR_SIZE]> = vec_to_boxed_array(vector1);
147        assert!(boxed1[0] == 0);
148        assert!(boxed1[ARR_SIZE - 1] == (ARR_SIZE - 1) * (ARR_SIZE - 1));
149        let n_queries = ARR_SIZE;
150        for _ in 0..n_queries {
151            let index = usize::rand(&mut rng) % ARR_SIZE;
152            assert!(boxed1[index] == index * index);
153        }
154
155        let vector2: Vec<Vec<usize>> = (0..ARR_SIZE)
156            .map(|i| (0..ARR_SIZE).map(|j| i * j).collect())
157            .collect();
158        let boxed2: Box<[[usize; ARR_SIZE]; ARR_SIZE]> = vec_to_boxed_array2(vector2);
159        assert!(boxed2[0][0] == 0);
160        assert!(boxed2[ARR_SIZE - 1][ARR_SIZE - 1] == (ARR_SIZE - 1) * (ARR_SIZE - 1));
161        let n_queries = ARR_SIZE;
162        for _ in 0..n_queries {
163            let index1 = usize::rand(&mut rng) % ARR_SIZE;
164            let index2 = usize::rand(&mut rng) % ARR_SIZE;
165            assert!(boxed2[index1][index2] == index1 * index2);
166        }
167
168        let vector3: Vec<Vec<Vec<usize>>> = (0..ARR_SIZE)
169            .map(|i| {
170                (0..ARR_SIZE)
171                    .map(|j| (0..ARR_SIZE).map(|k| i * j + k).collect())
172                    .collect()
173            })
174            .collect();
175        let boxed3: Box<[[[usize; ARR_SIZE]; ARR_SIZE]; ARR_SIZE]> = vec_to_boxed_array3(vector3);
176        assert!(boxed3[0][0][0] == 0);
177        assert!(
178            boxed3[ARR_SIZE - 1][ARR_SIZE - 1][ARR_SIZE - 1]
179                == (ARR_SIZE - 1) * (ARR_SIZE - 1) + (ARR_SIZE - 1)
180        );
181        let n_queries = ARR_SIZE;
182        for _ in 0..n_queries {
183            let index1 = usize::rand(&mut rng) % ARR_SIZE;
184            let index2 = usize::rand(&mut rng) % ARR_SIZE;
185            let index3 = usize::rand(&mut rng) % ARR_SIZE;
186            assert!(boxed3[index1][index2][index3] == index1 * index2 + index3);
187        }
188    }
189}