use crate::constants::RING_ELEMENT_OF_T0S_SIZE;
use crate::helper::cloop;
use crate::ntt::ntt;
use crate::polynomial::PolynomialRingElement;
use crate::simd::traits::Operations;
const OUTPUT_BYTES_PER_SIMD_UNIT: usize = 13;
#[cfg_attr(tarpaulin, inline(never))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn serialize<SIMDUnit: Operations>(
re: &PolynomialRingElement<SIMDUnit>,
serialized: &mut [u8], ) {
cloop! {
for (i, simd_unit) in re.simd_units.iter().enumerate() {
SIMDUnit::t0_serialize(simd_unit, &mut serialized[i * OUTPUT_BYTES_PER_SIMD_UNIT..(i + 1) * OUTPUT_BYTES_PER_SIMD_UNIT]);
}
}
}
#[cfg_attr(tarpaulin, inline(never))]
#[cfg_attr(not(tarpaulin), inline(always))]
fn deserialize<SIMDUnit: Operations>(
serialized: &[u8],
result: &mut PolynomialRingElement<SIMDUnit>,
) {
for i in 0..result.simd_units.len() {
SIMDUnit::t0_deserialize(
&serialized[i * OUTPUT_BYTES_PER_SIMD_UNIT..(i + 1) * OUTPUT_BYTES_PER_SIMD_UNIT],
&mut result.simd_units[i],
);
}
}
#[cfg_attr(tarpaulin, inline(never))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn deserialize_to_vector_then_ntt<SIMDUnit: Operations>(
serialized: &[u8],
ring_elements: &mut [PolynomialRingElement<SIMDUnit>],
) {
cloop! {
for (i, bytes) in serialized.chunks_exact(RING_ELEMENT_OF_T0S_SIZE).enumerate() {
deserialize::<SIMDUnit>(bytes, &mut ring_elements[i]);
ntt(&mut ring_elements[i]);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::simd::traits::Operations;
use crate::simd::{
self,
};
fn test_deserialize_generic<SIMDUnit: Operations>() {
let serialized = [
142, 115, 136, 74, 18, 206, 88, 7, 0, 22, 20, 228, 219, 113, 49, 227, 242, 177, 86, 8,
110, 150, 82, 137, 103, 225, 186, 160, 235, 159, 98, 45, 123, 187, 93, 112, 177, 99,
251, 129, 207, 135, 162, 175, 115, 126, 16, 1, 68, 214, 247, 203, 33, 148, 238, 24, 92,
61, 61, 70, 127, 17, 66, 65, 162, 196, 167, 28, 225, 232, 40, 224, 246, 214, 32, 44, 0,
64, 182, 68, 10, 16, 127, 154, 193, 64, 220, 171, 165, 110, 54, 86, 243, 191, 193, 96,
102, 104, 85, 97, 195, 220, 185, 8, 98, 225, 29, 111, 9, 154, 159, 243, 83, 167, 78,
106, 106, 46, 37, 117, 135, 86, 12, 164, 2, 139, 19, 89, 160, 108, 163, 85, 44, 92,
165, 163, 89, 231, 204, 238, 154, 211, 104, 62, 245, 69, 55, 19, 240, 91, 3, 107, 179,
195, 198, 23, 104, 95, 134, 200, 100, 224, 188, 54, 149, 209, 120, 104, 162, 62, 251,
175, 105, 37, 2, 241, 62, 147, 210, 96, 89, 232, 131, 193, 167, 154, 122, 85, 23, 17,
130, 227, 120, 89, 120, 5, 76, 28, 116, 125, 92, 136, 19, 239, 246, 150, 215, 151, 153,
79, 157, 252, 136, 86, 115, 251, 95, 170, 181, 223, 2, 210, 134, 84, 40, 177, 151, 148,
82, 254, 195, 81, 161, 173, 141, 161, 65, 254, 179, 54, 53, 243, 145, 27, 157, 62, 39,
161, 234, 177, 25, 47, 82, 228, 236, 162, 68, 252, 94, 90, 4, 137, 43, 183, 221, 79,
218, 218, 78, 243, 237, 180, 32, 92, 75, 15, 210, 71, 59, 254, 113, 145, 98, 26, 99,
79, 204, 24, 150, 162, 219, 250, 92, 252, 112, 109, 203, 75, 20, 133, 166, 243, 231,
120, 220, 28, 149, 7, 77, 128, 3, 48, 203, 190, 8, 116, 79, 149, 166, 187, 60, 34, 221,
241, 217, 2, 38, 57, 118, 243, 26, 174, 47, 4, 240, 77, 188, 119, 126, 239, 235, 207,
105, 14, 59, 223, 155, 108, 56, 53, 39, 134, 181, 79, 78, 189, 98, 123, 52, 69, 242,
124, 194, 30, 190, 206, 2, 185, 8, 150, 250, 186, 47, 147, 129, 27, 67, 45, 124, 165,
37, 165, 223, 215, 169, 175, 63, 43, 16, 181, 202, 134, 66, 162, 246, 48, 30, 235, 124,
145, 86, 76, 50, 247, 213, 157, 68, 112, 162, 228, 14, 164, 240, 198, 232, 176,
];
let expected_coefficients = [
-910, -1091, 2926, -412, 3979, 1280, -80, -2940, -369, -1817, 900, -173, 2336, 1717,
-3621, -3116, 3910, -3933, -2215, -1626, -2999, -2094, 315, -3948, 127, -1086, 1048,
-3303, -263, 3584, -3929, -2430, -1057, 2188, -1798, -2682, -1123, 1857, 2808, -1096,
2108, 1819, -2616, 4015, 146, -107, 3920, 2048, 2890, 4014, -4036, 3276, 3060, -1518,
-2710, 2355, -854, 513, -2096, -204, -1366, 3664, 2189, 3817, 3742, -2287, 3493, -3892,
-3897, -937, 1734, 691, 2770, -2985, -1441, 2024, -42, 1595, 3740, 620, -1443, 3742,
1705, -839, 395, -1894, 405, 742, -1342, -2607, 2867, -2016, -53, -2485, -2830, 3336,
-3944, 3022, -2354, -2496, -875, 1846, 3613, -1101, -2878, 641, 1702, 3580, -1007,
1719, 2685, -3339, 3709, -1342, -3750, 342, 3823, -449, 2589, 245, 1019, 3870, -3933,
-184, -312, -2935, -3675, -762, 103, 2838, 3521, 2387, -4023, -1327, -3798, 4005, 2350,
3420, 950, 1745, 2775, 3585, 2745, -1460, 3699, -525, 769, 1427, -3891, 568, -2676,
2841, 1375, 625, 1082, 1884, 306, 3503, -3057, 1205, 1788, -2396, -1901, -1183, 595,
-2471, -951, 3050, 1188, -122, -500, -3190, -1823, -328, 919, 1556, -2252, -1200,
-1768, -2549, 59, -1720, 211, 3447, 2427, -3997, -3641, -2488, -2385, 2429, 511, 2560,
-3787, 4027, -989, 726, 1094, -286, 2188, -2878, 2558, -457, -3293, -3125, 3334, -2050,
-311, 265, 130, -3935, -2675, -1564, -3571, -1613, -1249, 2842, -1414, -637, 173,
-1733, -839, -2338, 1549, 3112, 322, 2026, 3538, -1324, -2991, 1641, 506, 1949, -3117,
725, 1719, 65, -2717, -4055, 3924, -1698, 2358, -532, -3496, -3169, 335, 1858, -346,
2487, -1527, 2834, -3089, 1724, 3858, -2130, 3301, -1565,
];
let mut deserialized = PolynomialRingElement::<SIMDUnit>::zero();
deserialize::<SIMDUnit>(&serialized, &mut deserialized);
assert_eq!(deserialized.to_i32_array(), expected_coefficients);
}
#[test]
fn test_serialize_portable() {
let coefficients = [
-1072, -3712, -3423, -27, 1995, 3750, -922, 3806, 2356, 3801, -1709, -2709, 1191, 108,
-593, -3081, -949, -926, 3107, -3820, 379, 3747, -2910, -2370, 939, 3218, -3190, 1311,
1110, -2717, -1191, -1019, -2478, -1860, -4018, 2615, -3208, 337, -3406, -1225, -261,
-329, -3624, -726, -3159, 3407, 4042, 2124, 2921, 1507, 279, -2830, -2850, -4011, 402,
1510, -2648, -168, 18, 652, 3443, 1723, 3112, -1605, -3885, 3174, 832, -3424, 2886,
3815, 2064, 1757, 3298, 3365, -1489, -1021, 1594, 3630, -3352, 1055, -2914, -816, 864,
-1251, 2628, -3199, 549, -1966, 419, 685, -3414, -3673, -3939, -1422, -3994, 4073, 86,
-1703, 1179, 758, -3588, 3427, -1798, -2139, -456, -547, -3741, 3191, -2432, 1213,
-3415, -3825, -1993, -763, -1757, 887, 1587, -1995, -887, -873, 1152, -1897, 2738,
2867, 1952, 3834, 3562, 3118, -768, 1400, 3883, 2636, 456, -3884, -1726, -3232, 2373,
-1039, 591, 1975, 1634, 459, -595, 2864, 3619, 3288, -2180, 4048, -2469, 1826, 1764,
-1345, 3761, 2320, 3935, -1219, -1397, 214, -1008, 299, -3270, -2628, 1070, 2904, 1597,
3471, 2383, -417, -3456, 327, 3997, 1662, -3363, 2033, 1180, 1625, 923, -1911, -3511,
-41, 1525, -3882, -3104, 3023, 3794, -1028, 3818, -3216, -2875, -1755, -354, -3137,
-1546, -3535, -1156, 1802, -1081, 3726, 3067, 773, 2408, 72, 810, 3607, -1524, 3478,
3409, 3377, 3159, 159, -706, -60, 1462, 2224, 2279, 2373, -3027, -78, 405, -4078, 2697,
3474, -3611, 3632, 1229, 2396, -3729, -1110, 290, -2861, 3018, 122, 1177, -3123, -3583,
2683, 2743, 2888, -2104, 874, -1150, -2453, -125, -2561, -2011, -2384, 2259, -10, 836,
-2773, 2487, -2292, -201, -3235, 1232, -3197,
];
let re = PolynomialRingElement::<simd::portable::PortableSIMDUnit>::from_i32_array_test(
&coefficients,
);
let mut result = [0u8; RING_ELEMENT_OF_T0S_SIZE];
serialize::<simd::portable::PortableSIMDUnit>(&re, &mut result);
let mut result2 = [0u8; RING_ELEMENT_OF_T0S_SIZE];
serialize::<simd::portable::PortableSIMDUnit>(&re, &mut result2);
assert_eq!(result, result2, "Serialization must be deterministic");
assert!(
!result.iter().all(|&x| x == 0),
"Serialization should produce non-zero output"
);
assert_eq!(
result.len(),
RING_ELEMENT_OF_T0S_SIZE,
"Output size must match expected"
);
}
#[test]
fn test_deserialize_portable() {
test_deserialize_generic::<simd::portable::PortableSIMDUnit>();
}
#[cfg(feature = "simd256")]
#[test]
fn test_serialize_simd256() {
let coefficients = [
-1072, -3712, -3423, -27, 1995, 3750, -922, 3806, 2356, 3801, -1709, -2709, 1191, 108,
-593, -3081, -949, -926, 3107, -3820, 379, 3747, -2910, -2370, 939, 3218, -3190, 1311,
1110, -2717, -1191, -1019, -2478, -1860, -4018, 2615, -3208, 337, -3406, -1225, -261,
-329, -3624, -726, -3159, 3407, 4042, 2124, 2921, 1507, 279, -2830, -2850, -4011, 402,
1510, -2648, -168, 18, 652, 3443, 1723, 3112, -1605, -3885, 3174, 832, -3424, 2886,
3815, 2064, 1757, 3298, 3365, -1489, -1021, 1594, 3630, -3352, 1055, -2914, -816, 864,
-1251, 2628, -3199, 549, -1966, 419, 685, -3414, -3673, -3939, -1422, -3994, 4073, 86,
-1703, 1179, 758, -3588, 3427, -1798, -2139, -456, -547, -3741, 3191, -2432, 1213,
-3415, -3825, -1993, -763, -1757, 887, 1587, -1995, -887, -873, 1152, -1897, 2738,
2867, 1952, 3834, 3562, 3118, -768, 1400, 3883, 2636, 456, -3884, -1726, -3232, 2373,
-1039, 591, 1975, 1634, 459, -595, 2864, 3619, 3288, -2180, 4048, -2469, 1826, 1764,
-1345, 3761, 2320, 3935, -1219, -1397, 214, -1008, 299, -3270, -2628, 1070, 2904, 1597,
3471, 2383, -417, -3456, 327, 3997, 1662, -3363, 2033, 1180, 1625, 923, -1911, -3511,
-41, 1525, -3882, -3104, 3023, 3794, -1028, 3818, -3216, -2875, -1755, -354, -3137,
-1546, -3535, -1156, 1802, -1081, 3726, 3067, 773, 2408, 72, 810, 3607, -1524, 3478,
3409, 3377, 3159, 159, -706, -60, 1462, 2224, 2279, 2373, -3027, -78, 405, -4078, 2697,
3474, -3611, 3632, 1229, 2396, -3729, -1110, 290, -2861, 3018, 122, 1177, -3123, -3583,
2683, 2743, 2888, -2104, 874, -1150, -2453, -125, -2561, -2011, -2384, 2259, -10, 836,
-2773, 2487, -2292, -201, -3235, 1232, -3197,
];
let re =
PolynomialRingElement::<simd::avx2::AVX2SIMDUnit>::from_i32_array_test(&coefficients);
let mut result = [0u8; RING_ELEMENT_OF_T0S_SIZE];
serialize::<simd::avx2::AVX2SIMDUnit>(&re, &mut result);
let mut result2 = [0u8; RING_ELEMENT_OF_T0S_SIZE];
serialize::<simd::avx2::AVX2SIMDUnit>(&re, &mut result2);
assert_eq!(result, result2, "SIMD serialization must be deterministic");
assert!(
!result.iter().all(|&x| x == 0),
"SIMD serialization should produce non-zero output"
);
assert_eq!(
result.len(),
RING_ELEMENT_OF_T0S_SIZE,
"SIMD output size must match expected"
);
let re_portable =
PolynomialRingElement::<simd::portable::PortableSIMDUnit>::from_i32_array_test(
&coefficients,
);
let mut result_portable = [0u8; RING_ELEMENT_OF_T0S_SIZE];
serialize::<simd::portable::PortableSIMDUnit>(&re_portable, &mut result_portable);
assert_eq!(
result, result_portable,
"SIMD and portable must produce identical serialization for same input"
);
}
#[cfg(feature = "simd256")]
#[test]
fn debug_simd_portable_mismatch() {
let coefficients = [
-1072, -3712, -3423, -27, 1995, 3750, -922, 3806, 2356, 3801, -1709, -2709, 1191, 108,
-593, -3081, -949, -926, 3107, -3820, 379, 3747, -2910, -2370, 939, 3218, -3190, 1311,
1110, -2717, -1191, -1019, -2478, -1860, -4018, 2615, -3208, 337, -3406, -1225, -261,
-329, -3624, -726, -3159, 3407, 4042, 2124, 2921, 1507, 279, -2830, -2850, -4011, 402,
1510, -2648, -168, 18, 652, 3443, 1723, 3112, -1605, -3885, 3174, 832, -3424, 2886,
3815, 2064, 1757, 3298, 3365, -1489, -1021, 1594, 3630, -3352, 1055, -2914, -816, 864,
-1251, 2628, -3199, 549, -1966, 419, 685, -3414, -3673, -3939, -1422, -3994, 4073, 86,
-1703, 1179, 758, -3588, 3427, -1798, -2139, -456, -547, -3741, 3191, -2432, 1213,
-3415, -3825, -1993, -763, -1757, 887, 1587, -1995, -887, -873, 1152, -1897, 2738,
2867, 1952, 3834, 3562, 3118, -768, 1400, 3883, 2636, 456, -3884, -1726, -3232, 2373,
-1039, 591, 1975, 1634, 459, -595, 2864, 3619, 3288, -2180, 4048, -2469, 1826, 1764,
-1345, 3761, 2320, 3935, -1219, -1397, 214, -1008, 299, -3270, -2628, 1070, 2904, 1597,
3471, 2383, -417, -3456, 327, 3997, 1662, -3363, 2033, 1180, 1625, 923, -1911, -3511,
-41, 1525, -3882, -3104, 3023, 3794, -1028, 3818, -3216, -2875, -1755, -354, -3137,
-1546, -3535, -1156, 1802, -1081, 3726, 3067, 773, 2408, 72, 810, 3607, -1524, 3478,
3409, 3377, 3159, 159, -706, -60, 1462, 2224, 2279, 2373, -3027, -78, 405, -4078, 2697,
3474, -3611, 3632, 1229, 2396, -3729, -1110, 290, -2861, 3018, 122, 1177, -3123, -3583,
2683, 2743, 2888, -2104, 874, -1150, -2453, -125, -2561, -2011, -2384, 2259, -10, 836,
-2773, 2487, -2292, -201, -3235, 1232, -3197,
];
let re_portable =
PolynomialRingElement::<simd::portable::PortableSIMDUnit>::from_i32_array_test(
&coefficients,
);
let re_simd =
PolynomialRingElement::<simd::avx2::AVX2SIMDUnit>::from_i32_array_test(&coefficients);
let mut bytes_portable = [0u8; RING_ELEMENT_OF_T0S_SIZE];
let mut bytes_simd = [0u8; RING_ELEMENT_OF_T0S_SIZE];
serialize::<simd::portable::PortableSIMDUnit>(&re_portable, &mut bytes_portable);
serialize::<simd::avx2::AVX2SIMDUnit>(&re_simd, &mut bytes_simd);
let mut first_mismatch = None;
for (idx, (p, s)) in bytes_portable.iter().zip(bytes_simd.iter()).enumerate() {
if p != s {
first_mismatch = Some((idx, *p, *s));
break;
}
}
if let Some((idx, p, s)) = first_mismatch {
let start = idx.saturating_sub(8);
let end = (idx + 8).min(RING_ELEMENT_OF_T0S_SIZE);
let portable_context = &bytes_portable[start..end];
let simd_context = &bytes_simd[start..end];
panic!(
"First mismatch at byte {}: portable={:02x}, simd={:02x}\nContext portable: {:02x?}\nContext simd: {:02x?}",
idx, p, s, portable_context, simd_context
);
}
assert_eq!(
bytes_portable, bytes_simd,
"SIMD and portable serialization must be identical"
);
}
#[cfg(feature = "simd256")]
#[test]
fn debug_simd_portable_detailed_analysis() {
let mut simple_coeffs = [0i32; 256];
for i in 0..8 {
simple_coeffs[i] = i as i32;
}
let re_portable =
PolynomialRingElement::<simd::portable::PortableSIMDUnit>::from_i32_array_test(
&simple_coeffs,
);
let re_simd =
PolynomialRingElement::<simd::avx2::AVX2SIMDUnit>::from_i32_array_test(&simple_coeffs);
let mut bytes_portable = [0u8; RING_ELEMENT_OF_T0S_SIZE];
let mut bytes_simd = [0u8; RING_ELEMENT_OF_T0S_SIZE];
serialize::<simd::portable::PortableSIMDUnit>(&re_portable, &mut bytes_portable);
serialize::<simd::avx2::AVX2SIMDUnit>(&re_simd, &mut bytes_simd);
let portable_first_unit = &bytes_portable[0..13];
let simd_first_unit = &bytes_simd[0..13];
if portable_first_unit != simd_first_unit {
panic!(
"SIMD unit mismatch:\nPortable: {:02x?}\nSIMD: {:02x?}",
portable_first_unit, simd_first_unit
);
}
}
#[cfg(feature = "simd256")]
#[test]
fn test_deserialize_simd256() {
test_deserialize_generic::<simd::avx2::AVX2SIMDUnit>();
}
}