1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! This module provides different helpers in creating constant sized
//! arrays and converting them to different formats.
//!
//! Functions in this module are not necessarily optimal in terms of
//! allocations, as they tend to create intermediate vectors. For
//! better performance, either optimise this code, or use
//! (non-fixed-sized) vectors.

// Use a generic function so that the pointer cast remains type-safe
/// Converts a vector of elements to a boxed one. Semantically
/// equivalent to `vector.into_boxed_slice().try_into().unwrap()`.
pub fn vec_to_boxed_array<T, const N: usize>(vec: Vec<T>) -> Box<[T; N]> {
    vec.into_boxed_slice()
        .try_into()
        .unwrap_or_else(|_| panic!("vec_to_boxed_array: length mismatch, expected {}", N))
}

// @volhovm It could potentially be more efficient with unsafe tricks.
/// Converts a two-dimensional vector to a constant sized two-dimensional array.
pub fn vec_to_boxed_array2<T: Clone, const N: usize, const M: usize>(
    vec: Vec<Vec<T>>,
) -> Box<[[T; N]; M]> {
    let mut vec_of_slices2: Vec<[T; N]> = vec![];
    vec.into_iter().for_each(|x: Vec<T>| {
        let y: Box<[T]> = x.into_boxed_slice();
        let z: Box<[T; N]> = y
            .try_into()
            .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch inner array"));
        let zz: &[[T; N]] = std::slice::from_ref(z.as_ref());
        vec_of_slices2.extend_from_slice(zz);
    });

    vec_of_slices2
        .into_boxed_slice()
        .try_into()
        .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch outer array"))
}

/// Converts a three-dimensional vector to a constant sized two-dimensional array.
pub fn vec_to_boxed_array3<T: Clone, const N: usize, const M: usize, const K: usize>(
    vec: Vec<Vec<Vec<T>>>,
) -> Box<[[[T; N]; M]; K]> {
    let mut vec_of_slices2: Vec<[[T; N]; M]> = vec![];
    vec.into_iter().for_each(|x| {
        let r: Box<[[T; N]; M]> = vec_to_boxed_array2(x);
        let zz: &[[[T; N]; M]] = std::slice::from_ref(r.as_ref());
        vec_of_slices2.extend_from_slice(zz);
    });

    vec_of_slices2
        .into_boxed_slice()
        .try_into()
        .unwrap_or_else(|_| panic!("vec_to_boxed_array3: length mismatch outer array"))
}

/// A macro similar to `vec![$elem; $size]` which returns a boxed
/// array, allocated directly on the heap (via a vector, with reallocations).
///
/// ```rustc
///     let _: Box<[u8; 1024]> = box_array![0; 1024];
/// ```
///
/// See
/// <https://stackoverflow.com/questions/25805174/creating-a-fixed-size-array-on-heap-in-rust/68122278#68122278>
#[macro_export]
macro_rules! box_array {
    ($val:expr ; $len:expr) => {{
        // Use a generic function so that the pointer cast remains type-safe
        fn vec_to_boxed_array<T>(vec: Vec<T>) -> Box<[T; $len]> {
            (vec.into_boxed_slice())
                .try_into()
                .unwrap_or_else(|_| panic!("box_array: length mismatch"))
        }

        vec_to_boxed_array(vec![$val; $len])
    }};
}

/// A macro similar to `vec![vec![$elem; $size1]; $size2]` which
/// returns a two-dimensional boxed array, allocated directly on the
/// heap (via a vector, with reallocations).
///
/// ```rustc
///     let _: Box<[[u8; 1024]; 512]> = box_array![0; 1024; 512];
/// ```
///
#[macro_export]
macro_rules! box_array2 {
    ($val:expr; $len1:expr; $len2:expr) => {{
        fn vec_to_boxed_array2<T: Clone, const N: usize, const M: usize>(
            vec: Vec<Vec<T>>,
        ) -> Box<[[T; N]; M]> {
            let mut vec_of_slices2: Vec<[T; N]> = vec![];
            vec.into_iter().for_each(|x: Vec<T>| {
                let y: Box<[T]> = x.into_boxed_slice();
                let z: Box<[T; N]> = y
                    .try_into()
                    .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch inner array"));
                let zz: &[[T; N]] = std::slice::from_ref(z.as_ref());
                vec_of_slices2.extend_from_slice(zz);
            });

            vec_of_slices2
                .into_boxed_slice()
                .try_into()
                .unwrap_or_else(|_| panic!("vec_to_boxed_array2: length mismatch outer array"))
        }

        vec_to_boxed_array2(vec![vec![$val; $len1]; $len2])
    }};
}

#[cfg(test)]
mod tests {
    use super::*;

    use ark_ec::AffineRepr;
    use ark_ff::{UniformRand, Zero};
    use mina_curves::pasta::Pallas as CurvePoint;

    pub type BaseField = <CurvePoint as AffineRepr>::BaseField;

    #[test]
    /// Tests whether initialising different arrays creates a stack
    /// overflow. The usual default size of the stack is 128kB.
    fn test_boxed_stack_overflow() {
        // Each field element is assumed to be 256 bits, so 1.000.000
        // elements is 30MB. This often overflows the stack if created
        // as an array.
        let _boxed: Box<[[BaseField; 1000000]; 1]> =
            vec_to_boxed_array2(vec![vec![BaseField::zero(); 1000000]; 1]);
        let _boxed: Box<[[BaseField; 250000]; 4]> =
            vec_to_boxed_array2(vec![vec![BaseField::zero(); 250000]; 4]);
        let _boxed: Box<[BaseField; 1000000]> = box_array![BaseField::zero(); 1000000];
        let _boxed: Box<[[BaseField; 250000]; 4]> = box_array2![BaseField::zero(); 250000; 4];
    }

    #[test]
    /// Tests whether boxed array tranformations preserve the elements.
    fn test_boxed_stack_completeness() {
        let mut rng = crate::tests::make_test_rng(None);

        const ARR_SIZE: usize = 100;

        let vector1: Vec<usize> = (0..ARR_SIZE).map(|i| i * i).collect();
        let boxed1: Box<[usize; ARR_SIZE]> = vec_to_boxed_array(vector1);
        assert!(boxed1[0] == 0);
        assert!(boxed1[ARR_SIZE - 1] == (ARR_SIZE - 1) * (ARR_SIZE - 1));
        let n_queries = ARR_SIZE;
        for _ in 0..n_queries {
            let index = usize::rand(&mut rng) % ARR_SIZE;
            assert!(boxed1[index] == index * index);
        }

        let vector2: Vec<Vec<usize>> = (0..ARR_SIZE)
            .map(|i| (0..ARR_SIZE).map(|j| i * j).collect())
            .collect();
        let boxed2: Box<[[usize; ARR_SIZE]; ARR_SIZE]> = vec_to_boxed_array2(vector2);
        assert!(boxed2[0][0] == 0);
        assert!(boxed2[ARR_SIZE - 1][ARR_SIZE - 1] == (ARR_SIZE - 1) * (ARR_SIZE - 1));
        let n_queries = ARR_SIZE;
        for _ in 0..n_queries {
            let index1 = usize::rand(&mut rng) % ARR_SIZE;
            let index2 = usize::rand(&mut rng) % ARR_SIZE;
            assert!(boxed2[index1][index2] == index1 * index2);
        }

        let vector3: Vec<Vec<Vec<usize>>> = (0..ARR_SIZE)
            .map(|i| {
                (0..ARR_SIZE)
                    .map(|j| (0..ARR_SIZE).map(|k| i * j + k).collect())
                    .collect()
            })
            .collect();
        let boxed3: Box<[[[usize; ARR_SIZE]; ARR_SIZE]; ARR_SIZE]> = vec_to_boxed_array3(vector3);
        assert!(boxed3[0][0][0] == 0);
        assert!(
            boxed3[ARR_SIZE - 1][ARR_SIZE - 1][ARR_SIZE - 1]
                == (ARR_SIZE - 1) * (ARR_SIZE - 1) + (ARR_SIZE - 1)
        );
        let n_queries = ARR_SIZE;
        for _ in 0..n_queries {
            let index1 = usize::rand(&mut rng) % ARR_SIZE;
            let index2 = usize::rand(&mut rng) % ARR_SIZE;
            let index3 = usize::rand(&mut rng) % ARR_SIZE;
            assert!(boxed3[index1][index2][index3] == index1 * index2 + index3);
        }
    }
}