1use crate::base::{
2 scalar::Scalar,
3 standard_serializations::limbs::{deserialize_to_limbs, serialize_limbs},
4};
5use ark_ff::BigInteger;
6use core::ops::Neg;
7use serde::{Deserialize, Serialize};
8
9#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Copy)]
13pub struct I256(
14 #[serde(
15 serialize_with = "serialize_limbs",
16 deserialize_with = "deserialize_to_limbs"
17 )]
18 [u64; 4],
19);
20
21impl Neg for I256 {
22 type Output = Self;
23 fn neg(self) -> Self::Output {
25 let mut res = ark_ff::BigInt([0; 4]);
26 res.sub_with_borrow(&ark_ff::BigInt(self.0));
27 Self(res.0)
28 }
29}
30
31impl I256 {
32 #[must_use]
34 pub fn new(limbs: [u64; 4]) -> Self {
35 Self(limbs)
36 }
37
38 #[must_use]
40 #[cfg(test)]
41 pub(crate) fn limbs(&self) -> [u64; 4] {
42 self.0
43 }
44 #[must_use]
45 pub fn into_scalar<S: Scalar>(self) -> S {
51 if self.0[3] & 0x8000_0000_0000_0000 == 0 {
52 self.0.into()
53 } else {
54 (Into::<S>::into(self.neg().0)).neg()
55 }
56 }
57
58 #[must_use]
59 pub fn from_num_bigint(value: &num_bigint::BigInt) -> Self {
64 let (sign, limbs_vec) = value.to_u64_digits();
65 let num_limbs = limbs_vec.len().min(4);
66 let mut limbs = [0u64; 4];
67 limbs[..num_limbs].copy_from_slice(&limbs_vec[..num_limbs]);
68 limbs[3] &= 0x7FFF_FFFF_FFFF_FFFF;
69 match sign {
70 num_bigint::Sign::Minus => Self(limbs).neg(),
71 num_bigint::Sign::Plus | num_bigint::Sign::NoSign => Self(limbs),
72 }
73 }
74}
75impl From<i32> for I256 {
76 fn from(value: i32) -> Self {
77 let abs = Self([value.unsigned_abs().into(), 0, 0, 0]);
78 if value >= 0 {
79 abs
80 } else {
81 abs.neg()
82 }
83 }
84}
85
86#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
87impl From<i128> for I256 {
88 fn from(value: i128) -> Self {
89 Self([
90 value as u64,
91 (value >> 64) as u64,
92 (value >> 127) as u64,
93 (value >> 127) as u64,
94 ])
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::base::scalar::{test_scalar::TestScalar, MontScalar, Scalar};
102 use ark_ff::MontFp;
103 use num_bigint::BigInt;
104 use rand::{thread_rng, Rng};
105
106 const ZERO: I256 = I256([0, 0, 0, 0]);
107 const ONE: I256 = I256([1, 0, 0, 0]);
108 const TWO: I256 = I256([2, 0, 0, 0]);
109 const NEG_ONE: I256 = I256([
110 0xFFFF_FFFF_FFFF_FFFF,
111 0xFFFF_FFFF_FFFF_FFFF,
112 0xFFFF_FFFF_FFFF_FFFF,
113 0xFFFF_FFFF_FFFF_FFFF,
114 ]);
115 const NEG_TWO: I256 = I256([
116 0xFFFF_FFFF_FFFF_FFFE,
117 0xFFFF_FFFF_FFFF_FFFF,
118 0xFFFF_FFFF_FFFF_FFFF,
119 0xFFFF_FFFF_FFFF_FFFF,
120 ]);
121 const A_STR: &str =
122 "57896044618658097705508390768957273162799202909612615603626436559492530307074";
123 const A: I256 = I256([2, 0, 0, 0x7FFF_FFFF_FFFF_FFFF]);
124 const NEG_A: I256 = I256([
125 0xFFFF_FFFF_FFFF_FFFE,
126 0xFFFF_FFFF_FFFF_FFFF,
127 0xFFFF_FFFF_FFFF_FFFF,
128 0x8000_0000_0000_0000,
129 ]);
130 const B_STR: &str =
131 "44514458406356786875149426309623179975904669798901350226660343085647800511238";
132 const B: I256 = I256([
133 0x12DE_4D02_71BF_2B06,
134 0x2686_80A2_B415_EE31,
135 0xBCF3_35CF_A69C_DBE3,
136 0x626A_4A65_275E_1D88,
137 ]);
138 const NEG_B: I256 = I256([
139 0xED21_B2FD_8E40_D4FA,
140 0xD979_7F5D_4BEA_11CE,
141 0x430C_CA30_5963_241C,
142 0x9D95_B59A_D8A1_E277,
143 ]);
144 const C_STR: &str =
145 "452312848583266388373324160190187140051835877600158453279131187530910662656";
146 const C_SCALAR: TestScalar = MontScalar(MontFp!(
147 "452312848583266388373324160190187140051835877600158453279131187530910662656"
148 ));
149 const C: I256 = I256([
150 0x0000_0000_0000_0000,
151 0x0000_0000_0000_0000,
152 0x0000_0000_0000_0000,
153 0x0100_0000_0000_0000,
154 ]);
155 const NEG_C: I256 = I256([
156 0x0000_0000_0000_0000,
157 0x0000_0000_0000_0000,
158 0x0000_0000_0000_0000,
159 0xFF00_0000_0000_0000,
160 ]);
161 const MODULUS_MINUS_ONE: I256 = I256([
162 0x5812_631A_5CF5_D3EC,
163 0x14DE_F9DE_A2F7_9CD6,
164 0x0000_0000_0000_0000,
165 0x1000_0000_0000_0000,
166 ]);
167 const NEG_MODULUS_PLUS_ONE: I256 = I256([
168 0xa7ed_9ce5_a30a_2c14,
169 0xEB21_0621_5D08_6329,
170 0xFFFF_FFFF_FFFF_FFFF,
171 0xEFFF_FFFF_FFFF_FFFF,
172 ]);
173 const MODULUS_MINUS_TWO: I256 = I256([
174 0x5812_631A_5CF5_D3EB,
175 0x14DE_F9DE_A2F7_9CD6,
176 0x0000_0000_0000_0000,
177 0x1000_0000_0000_0000,
178 ]);
179 const NEG_MODULUS_PLUS_TWO: I256 = I256([
180 0xa7ed_9ce5_a30a_2c15,
181 0xEB21_0621_5D08_6329,
182 0xFFFF_FFFF_FFFF_FFFF,
183 0xEFFF_FFFF_FFFF_FFFF,
184 ]);
185
186 #[test]
187 fn we_can_compute_the_negative_of_i256() {
188 assert_eq!(ZERO.neg(), ZERO);
189 assert_eq!(ONE.neg(), NEG_ONE);
190 assert_eq!(NEG_ONE.neg(), ONE);
191 assert_eq!(TWO.neg(), NEG_TWO);
192 assert_eq!(NEG_TWO.neg(), TWO);
193 assert_eq!(A.neg(), NEG_A);
194 assert_eq!(NEG_A.neg(), A);
195 assert_eq!(B.neg(), NEG_B);
196 assert_eq!(NEG_B.neg(), B);
197 assert_eq!(C.neg(), NEG_C);
198 assert_eq!(NEG_C.neg(), C);
199 assert_eq!(MODULUS_MINUS_ONE.neg(), NEG_MODULUS_PLUS_ONE);
200 assert_eq!(NEG_MODULUS_PLUS_ONE.neg(), MODULUS_MINUS_ONE);
201 assert_eq!(MODULUS_MINUS_TWO.neg(), NEG_MODULUS_PLUS_TWO);
202 assert_eq!(NEG_MODULUS_PLUS_TWO.neg(), MODULUS_MINUS_TWO);
203
204 let mut rng = thread_rng();
205 for _ in 0..10 {
206 let x = I256([rng.gen(), rng.gen(), rng.gen(), rng.gen()]);
207 assert_eq!(x.neg().neg(), x);
208 }
209 }
210 #[test]
211 fn we_can_convert_i256_into_scalar() {
212 assert_eq!(ZERO.into_scalar::<TestScalar>(), TestScalar::ZERO);
213 assert_eq!(ONE.into_scalar::<TestScalar>(), TestScalar::ONE);
214 assert_eq!(NEG_ONE.into_scalar::<TestScalar>(), -TestScalar::ONE);
215 assert_eq!(TWO.into_scalar::<TestScalar>(), TestScalar::TWO);
216 assert_eq!(NEG_TWO.into_scalar::<TestScalar>(), -TestScalar::TWO);
217 assert_eq!(C.into_scalar::<TestScalar>(), C_SCALAR);
218 assert_eq!(NEG_C.into_scalar::<TestScalar>(), -C_SCALAR);
219 assert_eq!(
220 MODULUS_MINUS_ONE.into_scalar::<TestScalar>(),
221 -TestScalar::ONE
222 );
223 assert_eq!(
224 NEG_MODULUS_PLUS_ONE.into_scalar::<TestScalar>(),
225 TestScalar::ONE
226 );
227 assert_eq!(
228 MODULUS_MINUS_TWO.into_scalar::<TestScalar>(),
229 -TestScalar::TWO
230 );
231 assert_eq!(
232 NEG_MODULUS_PLUS_TWO.into_scalar::<TestScalar>(),
233 TestScalar::TWO
234 );
235
236 let mut rng = thread_rng();
237 for _ in 0..10 {
238 let x = I256([rng.gen(), rng.gen(), rng.gen(), rng.gen()]);
239 assert_eq!(
240 x.neg().into_scalar::<TestScalar>(),
241 -(x.into_scalar::<TestScalar>())
242 );
243 }
244 }
245 #[test]
246 fn we_can_convert_i256_from_num_bigint() {
247 assert_eq!(I256::from_num_bigint(&"0".parse().unwrap()), ZERO);
248 assert_eq!(I256::from_num_bigint(&"1".parse().unwrap()), ONE);
249 assert_eq!(I256::from_num_bigint(&"-1".parse().unwrap()), NEG_ONE);
250 assert_eq!(I256::from_num_bigint(&"2".parse().unwrap()), TWO);
251 assert_eq!(I256::from_num_bigint(&"-2".parse().unwrap()), NEG_TWO);
252 assert_eq!(I256::from_num_bigint(&A_STR.parse().unwrap()), A);
253 assert_eq!(
254 I256::from_num_bigint(&-A_STR.parse::<BigInt>().unwrap()),
255 NEG_A
256 );
257 assert_eq!(I256::from_num_bigint(&B_STR.parse().unwrap()), B);
258 assert_eq!(
259 I256::from_num_bigint(&-B_STR.parse::<BigInt>().unwrap()),
260 NEG_B
261 );
262 assert_eq!(I256::from_num_bigint(&C_STR.parse().unwrap()), C);
263 assert_eq!(
264 I256::from_num_bigint(&-C_STR.parse::<BigInt>().unwrap()),
265 NEG_C
266 );
267
268 let mut rng = thread_rng();
269 for _ in 0..10 {
270 let x =
271 (BigInt::from(rng.gen::<i128>().abs()) << 128) + BigInt::from(rng.gen::<u128>());
272 let y = &x + (BigInt::from(rng.gen::<u128>()) << 255);
273 assert_eq!(I256::from_num_bigint(&y), I256::from_num_bigint(&x));
274 assert_eq!(I256::from_num_bigint(&-&y), I256::from_num_bigint(&-x));
275 assert_eq!(I256::from_num_bigint(&y), I256::from_num_bigint(&-y).neg());
276 }
277 }
278 #[test]
279 fn we_can_convert_i256_from_i32() {
280 assert_eq!(I256::from(0), ZERO);
281 assert_eq!(I256::from(1), ONE);
282 assert_eq!(I256::from(-1), NEG_ONE);
283 assert_eq!(I256::from(2), TWO);
284 assert_eq!(I256::from(-2), NEG_TWO);
285 }
286 #[test]
287 fn we_can_convert_i256_between_type_compatibly() {
288 let mut rng = thread_rng();
289 for _ in 0..10 {
290 let int32: i32 = rng.gen();
291 let neg_int32 = -int32;
292 let scalar = TestScalar::from(int32);
293 let neg_scalar = -scalar;
294 let bigint = BigInt::from(int32);
295 let neg_bigint = -&bigint;
296 let int256_from_i32 = I256::from(int32);
297 let neg_int256_from_i32 = I256::from(neg_int32);
298 let int256_from_bigint = I256::from_num_bigint(&bigint);
299 let neg_int256_from_bigint = I256::from_num_bigint(&neg_bigint);
300 assert_eq!(int256_from_i32, int256_from_bigint);
301 assert_eq!(neg_int256_from_i32, neg_int256_from_bigint);
302 assert_eq!(neg_int256_from_i32, int256_from_i32.neg());
303 assert_eq!(int256_from_i32.into_scalar::<TestScalar>(), scalar);
304 assert_eq!(neg_int256_from_i32.into_scalar::<TestScalar>(), neg_scalar);
305 }
306 }
307
308 #[expect(clippy::cast_sign_loss)]
309 #[test]
310 fn test_i128_to_i256_conversion() {
311 assert_eq!(I256::from(0i128), ZERO);
313
314 assert_eq!(I256::from(1i128), ONE);
316 assert_eq!(I256::from(2i128), TWO);
317
318 assert_eq!(I256::from(-1i128), NEG_ONE);
320 assert_eq!(I256::from(-2i128), NEG_TWO);
321
322 assert_eq!(
324 I256::from(i128::MIN),
325 I256([0, 0x8000_0000_0000_0000, 0, 0]).neg()
326 );
327 assert_eq!(
328 I256::from(i128::MAX),
329 I256([0xFFFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_FFFF_FFFF, 0, 0])
330 );
331
332 let test_values = [
334 42i128,
335 -42i128,
336 1_234_567_890i128,
337 -1_234_567_890i128,
338 i128::from(i64::MAX),
339 i128::from(i64::MIN),
340 ];
341
342 for &value in &test_values {
343 let converted = I256::from(value);
344 if value >= 0 {
345 assert_eq!(
346 converted.0[0],
347 (value as u128 & 0xFFFF_FFFF_FFFF_FFFF) as u64
348 );
349 assert_eq!(
350 converted.0[1],
351 ((value as u128 >> 64) & 0xFFFF_FFFF_FFFF_FFFF) as u64
352 );
353 assert_eq!(converted.0[2], 0);
354 assert_eq!(converted.0[3], 0);
355 } else {
356 let abs_value = value.unsigned_abs();
357 let expected = I256([
358 (abs_value & 0xFFFF_FFFF_FFFF_FFFF) as u64,
359 ((abs_value >> 64) & 0xFFFF_FFFF_FFFF_FFFF) as u64,
360 0,
361 0,
362 ])
363 .neg();
364 assert_eq!(converted, expected);
365 }
366 }
367 }
368}