/src/fips203/src/helpers.rs
Line | Count | Source |
1 | | use crate::ntt::multiply_ntts; |
2 | | use crate::types::Z; |
3 | | use crate::Q; |
4 | | use sha3::digest::{ExtendableOutput, Update, XofReader}; |
5 | | use sha3::{Digest, Sha3_256, Sha3_512, Shake128, Shake256}; |
6 | | |
7 | | |
8 | | /// If the condition is not met, return an error message. Borrowed from the `anyhow` crate. |
9 | | macro_rules! ensure { |
10 | | ($cond:expr, $msg:literal $(,)?) => { |
11 | | if !$cond { |
12 | | return Err($msg); |
13 | | } |
14 | | }; |
15 | | } |
16 | | |
17 | | pub(crate) use ensure; // make available throughout crate |
18 | | |
19 | | |
20 | | /// Vector addition; See commentary on 2.11 page 10: `z_hat` = `u_hat` + `v_hat` |
21 | | /// |
22 | | /// # Arguments |
23 | | /// * `vec_a` - First vector of size K×256 |
24 | | /// * `vec_b` - Second vector of size K×256 |
25 | | /// |
26 | | /// # Returns |
27 | | /// Sum of the two vectors element-wise |
28 | | #[must_use] |
29 | 71.0k | pub(crate) fn add_vecs<const K: usize>( |
30 | 71.0k | vec_a: &[[Z; 256]; K], vec_b: &[[Z; 256]; K], |
31 | 71.0k | ) -> [[Z; 256]; K] { |
32 | 20.6M | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) fips203::helpers::add_vecs::<1>::{closure#0}Line | Count | Source | 32 | 66.1k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
fips203::helpers::add_vecs::<2>::{closure#0}Line | Count | Source | 32 | 3.10k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
fips203::helpers::add_vecs::<3>::{closure#0}Line | Count | Source | 32 | 4.60k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
fips203::helpers::add_vecs::<4>::{closure#0}Line | Count | Source | 32 | 6.92k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
fips203::helpers::add_vecs::<1>::{closure#0}::{closure#0}Line | Count | Source | 32 | 16.9M | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
fips203::helpers::add_vecs::<2>::{closure#0}::{closure#0}Line | Count | Source | 32 | 795k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
fips203::helpers::add_vecs::<3>::{closure#0}::{closure#0}Line | Count | Source | 32 | 1.17M | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
fips203::helpers::add_vecs::<4>::{closure#0}::{closure#0}Line | Count | Source | 32 | 1.77M | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) |
|
33 | 71.0k | } fips203::helpers::add_vecs::<1> Line | Count | Source | 29 | 66.1k | pub(crate) fn add_vecs<const K: usize>( | 30 | 66.1k | vec_a: &[[Z; 256]; K], vec_b: &[[Z; 256]; K], | 31 | 66.1k | ) -> [[Z; 256]; K] { | 32 | 66.1k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) | 33 | 66.1k | } |
fips203::helpers::add_vecs::<2> Line | Count | Source | 29 | 1.55k | pub(crate) fn add_vecs<const K: usize>( | 30 | 1.55k | vec_a: &[[Z; 256]; K], vec_b: &[[Z; 256]; K], | 31 | 1.55k | ) -> [[Z; 256]; K] { | 32 | 1.55k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) | 33 | 1.55k | } |
fips203::helpers::add_vecs::<3> Line | Count | Source | 29 | 1.53k | pub(crate) fn add_vecs<const K: usize>( | 30 | 1.53k | vec_a: &[[Z; 256]; K], vec_b: &[[Z; 256]; K], | 31 | 1.53k | ) -> [[Z; 256]; K] { | 32 | 1.53k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) | 33 | 1.53k | } |
fips203::helpers::add_vecs::<4> Line | Count | Source | 29 | 1.73k | pub(crate) fn add_vecs<const K: usize>( | 30 | 1.73k | vec_a: &[[Z; 256]; K], vec_b: &[[Z; 256]; K], | 31 | 1.73k | ) -> [[Z; 256]; K] { | 32 | 1.73k | core::array::from_fn(|k| core::array::from_fn(|n| vec_a[k][n].add(vec_b[k][n]))) | 33 | 1.73k | } |
|
34 | | |
35 | | |
36 | | /// Matrix by vector multiplication; See commentary on 2.12 page 10: `w_hat` = `A_hat` mul `u_hat` |
37 | | /// |
38 | | /// # Arguments |
39 | | /// * `a_hat` - Matrix of size K×K×256 |
40 | | /// * `u_hat` - Vector of size K×256 |
41 | | /// |
42 | | /// # Returns |
43 | | /// Result of matrix multiplication `A_hat * u_hat` |
44 | | #[must_use] |
45 | 1.94k | pub(crate) fn mul_mat_vec<const K: usize>( |
46 | 1.94k | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], |
47 | 1.94k | ) -> [[Z; 256]; K] { |
48 | 1.94k | let mut w_hat = [[Z::default(); 256]; K]; |
49 | 7.77k | for i in 0..K { |
50 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand |
51 | 24.6k | for j in 0..K { |
52 | 18.7k | let tmp = multiply_ntts(&a_hat[i][j], &u_hat[j]); |
53 | 18.7k | w_hat[i] = add_vecs(&[w_hat[i]], &[tmp])[0]; |
54 | 18.7k | } |
55 | | } |
56 | 1.94k | w_hat |
57 | 1.94k | } fips203::helpers::mul_mat_vec::<2> Line | Count | Source | 45 | 648 | pub(crate) fn mul_mat_vec<const K: usize>( | 46 | 648 | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], | 47 | 648 | ) -> [[Z; 256]; K] { | 48 | 648 | let mut w_hat = [[Z::default(); 256]; K]; | 49 | 1.94k | for i in 0..K { | 50 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 51 | 3.88k | for j in 0..K { | 52 | 2.59k | let tmp = multiply_ntts(&a_hat[i][j], &u_hat[j]); | 53 | 2.59k | w_hat[i] = add_vecs(&[w_hat[i]], &[tmp])[0]; | 54 | 2.59k | } | 55 | | } | 56 | 648 | w_hat | 57 | 648 | } |
fips203::helpers::mul_mat_vec::<3> Line | Count | Source | 45 | 648 | pub(crate) fn mul_mat_vec<const K: usize>( | 46 | 648 | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], | 47 | 648 | ) -> [[Z; 256]; K] { | 48 | 648 | let mut w_hat = [[Z::default(); 256]; K]; | 49 | 2.59k | for i in 0..K { | 50 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 51 | 7.77k | for j in 0..K { | 52 | 5.83k | let tmp = multiply_ntts(&a_hat[i][j], &u_hat[j]); | 53 | 5.83k | w_hat[i] = add_vecs(&[w_hat[i]], &[tmp])[0]; | 54 | 5.83k | } | 55 | | } | 56 | 648 | w_hat | 57 | 648 | } |
fips203::helpers::mul_mat_vec::<4> Line | Count | Source | 45 | 648 | pub(crate) fn mul_mat_vec<const K: usize>( | 46 | 648 | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], | 47 | 648 | ) -> [[Z; 256]; K] { | 48 | 648 | let mut w_hat = [[Z::default(); 256]; K]; | 49 | 3.24k | for i in 0..K { | 50 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 51 | 12.9k | for j in 0..K { | 52 | 10.3k | let tmp = multiply_ntts(&a_hat[i][j], &u_hat[j]); | 53 | 10.3k | w_hat[i] = add_vecs(&[w_hat[i]], &[tmp])[0]; | 54 | 10.3k | } | 55 | | } | 56 | 648 | w_hat | 57 | 648 | } |
|
58 | | |
59 | | |
60 | | /// Matrix transpose by vector multiplication; See commentary on 2.13 page 10: `y_hat` = `A_hat^T` mul `u_hat` |
61 | | /// |
62 | | /// # Arguments |
63 | | /// * `a_hat` - Matrix of size K×K×256 to be transposed before multiplication |
64 | | /// * `u_hat` - Vector of size K×256 |
65 | | /// |
66 | | /// # Returns |
67 | | /// Result of matrix multiplication `A_hat^T * u_hat`, where `^T` denotes transpose |
68 | | #[must_use] |
69 | 2.87k | pub(crate) fn mul_mat_t_vec<const K: usize>( |
70 | 2.87k | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], |
71 | 2.87k | ) -> [[Z; 256]; K] { |
72 | 2.87k | let mut y_hat = [[Z::default(); 256]; K]; |
73 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand |
74 | 11.6k | for i in 0..K { |
75 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand |
76 | 37.7k | for j in 0..K { |
77 | 28.9k | let tmp = multiply_ntts(&a_hat[j][i], &u_hat[j]); // i,j swapped vs above fn |
78 | 28.9k | y_hat[i] = add_vecs(&[y_hat[i]], &[tmp])[0]; |
79 | 28.9k | } |
80 | | } |
81 | 2.87k | y_hat |
82 | 2.87k | } fips203::helpers::mul_mat_t_vec::<2> Line | Count | Source | 69 | 905 | pub(crate) fn mul_mat_t_vec<const K: usize>( | 70 | 905 | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], | 71 | 905 | ) -> [[Z; 256]; K] { | 72 | 905 | let mut y_hat = [[Z::default(); 256]; K]; | 73 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 74 | 2.71k | for i in 0..K { | 75 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 76 | 5.43k | for j in 0..K { | 77 | 3.62k | let tmp = multiply_ntts(&a_hat[j][i], &u_hat[j]); // i,j swapped vs above fn | 78 | 3.62k | y_hat[i] = add_vecs(&[y_hat[i]], &[tmp])[0]; | 79 | 3.62k | } | 80 | | } | 81 | 905 | y_hat | 82 | 905 | } |
fips203::helpers::mul_mat_t_vec::<3> Line | Count | Source | 69 | 888 | pub(crate) fn mul_mat_t_vec<const K: usize>( | 70 | 888 | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], | 71 | 888 | ) -> [[Z; 256]; K] { | 72 | 888 | let mut y_hat = [[Z::default(); 256]; K]; | 73 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 74 | 3.55k | for i in 0..K { | 75 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 76 | 10.6k | for j in 0..K { | 77 | 7.99k | let tmp = multiply_ntts(&a_hat[j][i], &u_hat[j]); // i,j swapped vs above fn | 78 | 7.99k | y_hat[i] = add_vecs(&[y_hat[i]], &[tmp])[0]; | 79 | 7.99k | } | 80 | | } | 81 | 888 | y_hat | 82 | 888 | } |
fips203::helpers::mul_mat_t_vec::<4> Line | Count | Source | 69 | 1.08k | pub(crate) fn mul_mat_t_vec<const K: usize>( | 70 | 1.08k | a_hat: &[[[Z; 256]; K]; K], u_hat: &[[Z; 256]; K], | 71 | 1.08k | ) -> [[Z; 256]; K] { | 72 | 1.08k | let mut y_hat = [[Z::default(); 256]; K]; | 73 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 74 | 5.42k | for i in 0..K { | 75 | | #[allow(clippy::needless_range_loop)] // alternative is harder to understand | 76 | 21.6k | for j in 0..K { | 77 | 17.3k | let tmp = multiply_ntts(&a_hat[j][i], &u_hat[j]); // i,j swapped vs above fn | 78 | 17.3k | y_hat[i] = add_vecs(&[y_hat[i]], &[tmp])[0]; | 79 | 17.3k | } | 80 | | } | 81 | 1.08k | y_hat | 82 | 1.08k | } |
|
83 | | |
84 | | |
85 | | /// Vector dot product; See commentary on 2.14 page 10: `z_hat` = `u_hat^T` mul `v_hat` |
86 | | /// |
87 | | /// # Arguments |
88 | | /// * `u_hat` - First vector of size K×256 |
89 | | /// * `v_hat` - Second vector of size K×256 |
90 | | /// |
91 | | /// # Returns |
92 | | /// Dot product result as a 256-element array, computed as sum of element-wise products |
93 | | #[must_use] |
94 | 4.18k | pub(crate) fn dot_t_prod<const K: usize>(u_hat: &[[Z; 256]; K], v_hat: &[[Z; 256]; K]) -> [Z; 256] { |
95 | 4.18k | let mut result = [Z::default(); 256]; |
96 | 16.8k | for j in 0..K { |
97 | 12.6k | let tmp = multiply_ntts(&u_hat[j], &v_hat[j]); |
98 | 12.6k | result = add_vecs(&[result], &[tmp])[0]; |
99 | 12.6k | } |
100 | 4.18k | result |
101 | 4.18k | } fips203::helpers::dot_t_prod::<2> Line | Count | Source | 94 | 1.35k | pub(crate) fn dot_t_prod<const K: usize>(u_hat: &[[Z; 256]; K], v_hat: &[[Z; 256]; K]) -> [Z; 256] { | 95 | 1.35k | let mut result = [Z::default(); 256]; | 96 | 4.06k | for j in 0..K { | 97 | 2.71k | let tmp = multiply_ntts(&u_hat[j], &v_hat[j]); | 98 | 2.71k | result = add_vecs(&[result], &[tmp])[0]; | 99 | 2.71k | } | 100 | 1.35k | result | 101 | 1.35k | } |
fips203::helpers::dot_t_prod::<3> Line | Count | Source | 94 | 1.32k | pub(crate) fn dot_t_prod<const K: usize>(u_hat: &[[Z; 256]; K], v_hat: &[[Z; 256]; K]) -> [Z; 256] { | 95 | 1.32k | let mut result = [Z::default(); 256]; | 96 | 5.28k | for j in 0..K { | 97 | 3.96k | let tmp = multiply_ntts(&u_hat[j], &v_hat[j]); | 98 | 3.96k | result = add_vecs(&[result], &[tmp])[0]; | 99 | 3.96k | } | 100 | 1.32k | result | 101 | 1.32k | } |
fips203::helpers::dot_t_prod::<4> Line | Count | Source | 94 | 1.50k | pub(crate) fn dot_t_prod<const K: usize>(u_hat: &[[Z; 256]; K], v_hat: &[[Z; 256]; K]) -> [Z; 256] { | 95 | 1.50k | let mut result = [Z::default(); 256]; | 96 | 7.52k | for j in 0..K { | 97 | 6.01k | let tmp = multiply_ntts(&u_hat[j], &v_hat[j]); | 98 | 6.01k | result = add_vecs(&[result], &[tmp])[0]; | 99 | 6.01k | } | 100 | 1.50k | result | 101 | 1.50k | } |
|
102 | | |
103 | | |
104 | | /// Function PRF on page 18 (4.3). |
105 | | /// Pseudorandom function that generates `ETA_64` bytes of output using SHAKE256 |
106 | | /// |
107 | | /// # Arguments |
108 | | /// * `s` - 32-byte seed |
109 | | /// * `b` - Single byte domain separator |
110 | | #[must_use] |
111 | 32.1k | pub(crate) fn prf<const ETA_64: usize>(s: &[u8; 32], b: u8) -> [u8; ETA_64] { |
112 | 32.1k | let mut hasher = Shake256::default(); |
113 | 32.1k | hasher.update(s); |
114 | 32.1k | hasher.update(&[b]); |
115 | 32.1k | let mut reader = hasher.finalize_xof(); |
116 | 32.1k | let mut result = [0u8; ETA_64]; |
117 | 32.1k | reader.read(&mut result); |
118 | 32.1k | result |
119 | 32.1k | } fips203::helpers::prf::<128> Line | Count | Source | 111 | 27.7k | pub(crate) fn prf<const ETA_64: usize>(s: &[u8; 32], b: u8) -> [u8; ETA_64] { | 112 | 27.7k | let mut hasher = Shake256::default(); | 113 | 27.7k | hasher.update(s); | 114 | 27.7k | hasher.update(&[b]); | 115 | 27.7k | let mut reader = hasher.finalize_xof(); | 116 | 27.7k | let mut result = [0u8; ETA_64]; | 117 | 27.7k | reader.read(&mut result); | 118 | 27.7k | result | 119 | 27.7k | } |
fips203::helpers::prf::<192> Line | Count | Source | 111 | 4.40k | pub(crate) fn prf<const ETA_64: usize>(s: &[u8; 32], b: u8) -> [u8; ETA_64] { | 112 | 4.40k | let mut hasher = Shake256::default(); | 113 | 4.40k | hasher.update(s); | 114 | 4.40k | hasher.update(&[b]); | 115 | 4.40k | let mut reader = hasher.finalize_xof(); | 116 | 4.40k | let mut result = [0u8; ETA_64]; | 117 | 4.40k | reader.read(&mut result); | 118 | 4.40k | result | 119 | 4.40k | } |
|
120 | | |
121 | | |
122 | | /// Function XOF on page 19 (4.6), used with 32-byte `rho` |
123 | | /// Expandable output function based on SHAKE128 for generating matrix elements |
124 | | /// |
125 | | /// # Arguments |
126 | | /// * `rho` - 32-byte seed for randomness |
127 | | /// * `i` - Row index for matrix generation |
128 | | /// * `j` - Column index for matrix generation |
129 | | /// |
130 | | /// # Returns |
131 | | /// An extendable output reader that can generate arbitrary length output |
132 | | #[must_use] |
133 | 47.7k | pub(crate) fn xof(rho: &[u8; 32], i: u8, j: u8) -> impl XofReader { |
134 | | //debug_assert_eq!(rho.len(), 32); |
135 | 47.7k | let mut hasher = Shake128::default(); |
136 | 47.7k | hasher.update(rho); |
137 | 47.7k | hasher.update(&[i]); |
138 | 47.7k | hasher.update(&[j]); |
139 | 47.7k | hasher.finalize_xof() |
140 | 47.7k | } |
141 | | |
142 | | |
143 | | /// Function G on page 19 (4.5). |
144 | | /// Hash function that produces two 32-byte outputs from variable input |
145 | | /// |
146 | | /// # Arguments |
147 | | /// * `bytes` - Slice of byte slices to be hashed together |
148 | | /// |
149 | | /// # Returns |
150 | | /// Tuple of two 32-byte arrays (tr, K) as specified in the protocol |
151 | 4.82k | pub(crate) fn g(bytes: &[&[u8]]) -> ([u8; 32], [u8; 32]) { |
152 | 4.82k | let mut hasher = Sha3_512::new(); |
153 | 7.69k | bytes.iter().for_each(|b| Digest::update(&mut hasher, b)); |
154 | 4.82k | let digest = hasher.finalize(); |
155 | 4.82k | let a = digest[0..32].try_into().expect("g_a fail"); |
156 | 4.82k | let b = digest[32..64].try_into().expect("g_b fail"); |
157 | 4.82k | (a, b) |
158 | 4.82k | } |
159 | | |
160 | | |
161 | | /// Function H on page 18 (4.4). |
162 | | /// Hash function that produces a single 32-byte output |
163 | | /// |
164 | | /// # Arguments |
165 | | /// * `bytes` - Input bytes to hash (typically public key) |
166 | | /// |
167 | | /// # Returns |
168 | | /// 32-byte array representing the hash |
169 | | #[must_use] |
170 | 5.75k | pub(crate) fn h(bytes: &[u8]) -> [u8; 32] { |
171 | 5.75k | let mut hasher = Sha3_256::new(); |
172 | 5.75k | Digest::update(&mut hasher, bytes); |
173 | 5.75k | let digest = hasher.finalize(); |
174 | 5.75k | digest.into() |
175 | 5.75k | } |
176 | | |
177 | | |
178 | | /// Function J on page 18 (4.4). |
179 | | /// XOF-based hash function for challenge generation |
180 | | /// |
181 | | /// # Arguments |
182 | | /// * `z` - 32-byte seed |
183 | | /// * `ct` - Variable length ciphertext |
184 | | /// |
185 | | /// # Returns |
186 | | /// 32-byte challenge value derived from inputs |
187 | | #[must_use] |
188 | 1.30k | pub(crate) fn j(z: &[u8; 32], ct: &[u8]) -> [u8; 32] { |
189 | 1.30k | let mut hasher = Shake256::default(); |
190 | 1.30k | hasher.update(z); |
191 | 1.30k | hasher.update(ct); |
192 | 1.30k | let mut reader = hasher.finalize_xof(); |
193 | 1.30k | let mut result = [0u8; 32]; |
194 | 1.30k | reader.read(&mut result); |
195 | 1.30k | result |
196 | 1.30k | } |
197 | | |
198 | | |
199 | | /// Compress<d> from page 21 (4.7). |
200 | | /// x → ⌈(2^d/q) · x⌋ |
201 | | /// |
202 | | /// This function compresses elements from `Z_q` to a smaller range by scaling them down. |
203 | | /// The compression is lossy but maintains approximate ratios between elements. |
204 | | /// |
205 | | /// # Arguments |
206 | | /// * `d` - Compression parameter that determines output range (0 to 11) |
207 | | /// * `inout` - Vector of elements to compress in-place |
208 | | /// |
209 | | /// # Implementation Notes |
210 | | /// * Works for all odd q values from 17 to 6307 |
211 | | /// * Input x must be in range 0 to q-1 |
212 | | /// * Uses pre-computed multiplier M to avoid floating-point arithmetic |
213 | | #[allow(clippy::cast_possible_truncation)] |
214 | 12.9k | pub(crate) fn compress_vector(d: u32, inout: &mut [Z]) { |
215 | | const M: u32 = (((1u64 << 36) + Q as u64 - 1) / Q as u64) as u32; |
216 | 3.33M | for x_ref in &mut *inout { |
217 | 3.32M | let y = (x_ref.get_u32() << d) + (u32::from(Q) >> 1); |
218 | 3.32M | let result = (u64::from(y) * u64::from(M)) >> 36; |
219 | 3.32M | x_ref.set_u16(result as u16); |
220 | 3.32M | } |
221 | 12.9k | } |
222 | | |
223 | | |
224 | | /// Decompress<d> from page 21 (4.8). |
225 | | /// y → ⌈(q/2^d) · y⌋ |
226 | | /// |
227 | | /// Inverse operation of `compress_vector` that expands compressed elements back to `Z_q`. |
228 | | /// While not perfect due to lossy compression, attempts to restore original ratios. |
229 | | /// |
230 | | /// # Arguments |
231 | | /// * `d` - Same compression parameter used in `compress_vector` |
232 | | /// * `inout` - Vector of compressed elements to decompress in-place |
233 | | #[allow(clippy::cast_possible_truncation)] |
234 | 8.21k | pub(crate) fn decompress_vector(d: u32, inout: &mut [Z]) { |
235 | 2.11M | for y_ref in &mut *inout { |
236 | 2.10M | let qy = u32::from(Q) * y_ref.get_u32() + (1 << d) - 1; |
237 | 2.10M | y_ref.set_u16((qy >> d) as u16); |
238 | 2.10M | } |
239 | 8.21k | } |