primeorder/tables/
radix16.rs1use crate::PrimeFieldExt;
4use core::ops::{Add, Index};
5use elliptic_curve::{
6 FieldBytesSize,
7 array::{Array, ArraySize, sizes::U1},
8};
9
10pub type Radix16Digits<C> = <<FieldBytesSize<C> as Add>::Output as Add<U1>>::Output;
14
15#[derive(Clone, Debug, Default)]
17pub struct Radix16Decomposition<Digits: ArraySize> {
18 digits: Array<i8, Digits>,
19}
20
21impl<Digits: ArraySize> Radix16Decomposition<Digits> {
22 pub fn new<Scalar: PrimeFieldExt>(scalar: &Scalar) -> Self {
32 let mut ret = Self::default();
34
35 let repr = scalar.to_be_repr();
38 let bytes = repr.as_ref();
39
40 for i in 0..(Digits::USIZE - 1) / 2 {
41 let b = bytes[bytes.len() - 1 - i];
42 ret.digits[2 * i] = (b & 0xf) as i8;
43 ret.digits[2 * i + 1] = ((b >> 4) & 0xf) as i8;
44 }
45
46 for i in 0..(Digits::USIZE - 1) {
48 let carry = (ret.digits[i] + 8) >> 4;
49 ret.digits[i] -= carry << 4;
50 ret.digits[i + 1] += carry;
51 }
52
53 ret
54 }
55}
56
57impl<Digits: ArraySize> Index<usize> for Radix16Decomposition<Digits> {
58 type Output = i8;
59
60 #[inline]
61 fn index(&self, index: usize) -> &i8 {
62 &self.digits[index]
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use elliptic_curve::array::sizes::{U57, U65, U133};
70
71 fn decompose_from_padded_be_uint<Digits: ArraySize>(
73 bytes: &[u8],
74 byte_len: usize,
75 ) -> Radix16Decomposition<Digits> {
76 let uint_byte_len = bytes.len();
77 assert!(uint_byte_len >= byte_len);
78
79 let len = 2 * byte_len + 1;
80 let mut digits = Array::<i8, Digits>::default();
81
82 for i in 0..byte_len {
83 let b = bytes[uint_byte_len - 1 - i];
84 digits[2 * i] = (b & 0xf) as i8;
85 digits[2 * i + 1] = ((b >> 4) & 0xf) as i8;
86 }
87
88 for i in 0..(len - 1) {
89 let carry = (digits[i] + 8) >> 4;
90 digits[i] -= carry << 4;
91 digits[i + 1] += carry;
92 }
93
94 Radix16Decomposition { digits }
95 }
96
97 fn decompose_be_bytes<Digits: ArraySize>(bytes: &[u8]) -> Radix16Decomposition<Digits> {
98 decompose_from_padded_be_uint(bytes, bytes.len())
99 }
100
101 fn assert_digits_in_range<Digits: ArraySize>(d: &Radix16Decomposition<Digits>) {
102 for i in 0..Digits::USIZE {
103 let digit = d[i];
104 assert!((-8..=8).contains(&digit), "digit[{i}] = {digit}");
105 }
106 }
107
108 #[test]
109 fn digit_range() {
110 let d = decompose_be_bytes::<U65>(&[0xabu8; 32]);
111 assert_digits_in_range(&d);
112 }
113
114 #[test]
116 fn p224_padded_uint_ignores_high_prefix() {
117 let mut bytes = [0xffu8; 32];
118 bytes[4..32].fill(0);
119 bytes[31] = 2;
120
121 let d = decompose_from_padded_be_uint::<U57>(&bytes, 28);
122 assert_eq!(d[0], 2);
123 assert_digits_in_range(&d);
124 for i in 1..57 {
125 assert_eq!(d[i], 0);
126 }
127 }
128
129 #[test]
131 fn p521_padded_uint_ignores_high_prefix() {
132 let mut bytes = [0xffu8; 72];
133 bytes[6..72].fill(0);
134 bytes[71] = 3;
135
136 let d = decompose_from_padded_be_uint::<U133>(&bytes, 66);
137 assert_eq!(d[0], 3);
138 assert_digits_in_range(&d);
139 for i in 1..133 {
140 assert_eq!(d[i], 0);
141 }
142 }
143
144 #[test]
145 fn reconstruction() {
146 let bytes: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
147 let d = decompose_be_bytes::<U65>(&bytes);
148
149 let mut acc: i128 = 0;
150 let mut radix: i128 = 1;
151 for i in 0..17 {
152 acc += i128::from(d[i]) * radix;
153 radix *= 16;
154 }
155
156 let mut expected: i128 = 0;
157 for b in bytes {
158 expected = (expected << 8) | i128::from(b);
159 }
160
161 assert_eq!(acc, expected);
162 }
163
164 #[test]
165 fn zero_scalar() {
166 let d = decompose_be_bytes::<U65>(&[0u8; 32]);
167 for i in 0..65 {
168 assert_eq!(d[i], 0);
169 }
170 }
171}