@@ -4,9 +4,12 @@ use crate::felt::Felt;
4
4
5
5
pub const STWO_PRIME : u64 = ( 1 << 31 ) - 1 ;
6
6
const STWO_PRIME_U128 : u128 = STWO_PRIME as u128 ;
7
+ const MASK_36 : u64 = ( 1 << 36 ) - 1 ;
8
+ const MASK_8 : u64 = ( 1 << 8 ) - 1 ;
7
9
8
10
#[ derive( Debug ) ]
9
11
pub enum QM31Error {
12
+ UnreducedFelt ( Felt ) ,
10
13
FeltTooBig ( Felt ) ,
11
14
InvalidInversion ,
12
15
}
@@ -17,6 +20,10 @@ impl std::error::Error for QM31Error {}
17
20
impl fmt:: Display for QM31Error {
18
21
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
19
22
match self {
23
+ QM31Error :: UnreducedFelt ( felt) => writeln ! (
24
+ f,
25
+ "Number is not a packing of a QM31 in reduced form: {felt})"
26
+ ) ,
20
27
QM31Error :: FeltTooBig ( felt) => writeln ! (
21
28
f,
22
29
"Number used as QM31 since it's more than 144 bits long: {felt}"
@@ -231,7 +238,21 @@ impl TryFrom<Felt> for QM31Felt {
231
238
return Err ( QM31Error :: FeltTooBig ( value) ) ;
232
239
}
233
240
234
- Ok ( Self :: from_raw ( limbs) )
241
+ let coordinates = [
242
+ ( limbs[ 0 ] & MASK_36 ) ,
243
+ ( ( limbs[ 0 ] >> 36 ) + ( ( limbs[ 1 ] & MASK_8 ) << 28 ) ) ,
244
+ ( ( limbs[ 1 ] >> 8 ) & MASK_36 ) ,
245
+ ( ( limbs[ 1 ] >> 44 ) + ( limbs[ 2 ] << 20 ) ) ,
246
+ ] ;
247
+
248
+ // Check if the coordinates were reduced before.
249
+ for x in coordinates. iter ( ) {
250
+ if * x >= STWO_PRIME {
251
+ return Err ( QM31Error :: UnreducedFelt ( value) ) ;
252
+ }
253
+ }
254
+
255
+ Ok ( Self ( coordinates) )
235
256
}
236
257
}
237
258
@@ -248,13 +269,27 @@ impl TryFrom<&Felt> for QM31Felt {
248
269
return Err ( QM31Error :: FeltTooBig ( * value) ) ;
249
270
}
250
271
251
- Ok ( Self :: from_raw ( limbs) )
272
+ // Check if the coordinates were reduced before.
273
+ let coordinates = [
274
+ ( limbs[ 0 ] & MASK_36 ) ,
275
+ ( ( limbs[ 0 ] >> 36 ) + ( ( limbs[ 1 ] & MASK_8 ) << 28 ) ) ,
276
+ ( ( limbs[ 1 ] >> 8 ) & MASK_36 ) ,
277
+ ( ( limbs[ 1 ] >> 44 ) + ( limbs[ 2 ] << 20 ) ) ,
278
+ ] ;
279
+
280
+ for x in coordinates. iter ( ) {
281
+ if * x >= STWO_PRIME {
282
+ return Err ( QM31Error :: UnreducedFelt ( * value) ) ;
283
+ }
284
+ }
285
+
286
+ Ok ( Self ( coordinates) )
252
287
}
253
288
}
254
289
255
290
#[ cfg( test) ]
256
291
mod test {
257
- use core:: { u128 , u16 , u8 } ;
292
+ use core:: u64 ;
258
293
259
294
use proptest:: {
260
295
array:: uniform4,
@@ -268,47 +303,35 @@ mod test {
268
303
} ;
269
304
270
305
#[ test]
271
- fn qm31_to_felt_packed ( ) {
272
- let u64_max_reduced = u64:: MAX % STWO_PRIME ;
273
-
274
- let value = u8:: MAX ;
275
- let felt = Felt :: from ( value) ;
276
- let qm31: QM31Felt = felt. try_into ( ) . unwrap ( ) ;
277
- let qm31_to_felt = qm31. pack_into_felt ( ) ;
278
-
279
- assert_eq ! ( qm31_to_felt, Felt :: from( value) ) ;
280
-
281
- let value = u16:: MAX ;
282
- let felt = Felt :: from ( value) ;
283
- let qm31: QM31Felt = felt. try_into ( ) . unwrap ( ) ;
284
- let qm31_to_felt = qm31. pack_into_felt ( ) ;
306
+ fn qm31_to_felt ( ) {
307
+ let coordinates = QM31Felt :: from_raw ( [ 1 , 2 , 3 , 4 ] ) ;
308
+ let packed_coordinates = Felt :: from ( coordinates) ;
309
+ let unpacked_coordinates = QM31Felt :: try_from ( packed_coordinates) . unwrap ( ) ;
310
+ assert_eq ! ( coordinates, unpacked_coordinates) ;
285
311
286
- assert_eq ! ( qm31_to_felt, Felt :: from( value) ) ;
312
+ let qm31 = QM31Felt :: from_raw ( [ u64:: MAX , 0 , 0 , 0 ] ) ;
313
+ let felt: Felt = qm31. try_into ( ) . unwrap ( ) ;
314
+ let felt_to_qm31 = QM31Felt :: try_from ( felt) . unwrap ( ) ;
287
315
288
- let value = u32:: MAX ;
289
- let felt = Felt :: from ( value) ;
290
- let qm31: QM31Felt = felt. try_into ( ) . unwrap ( ) ;
291
- let qm31_to_felt = qm31. pack_into_felt ( ) ;
316
+ assert_eq ! ( felt_to_qm31, qm31) ;
292
317
293
- assert_eq ! ( qm31_to_felt, Felt :: from( value as u64 % STWO_PRIME ) ) ;
318
+ let qm31 = QM31Felt :: from_raw ( [ u64:: MAX , u64:: MAX , 0 , 0 ] ) ;
319
+ let felt: Felt = qm31. try_into ( ) . unwrap ( ) ;
320
+ let felt_to_qm31 = QM31Felt :: try_from ( felt) . unwrap ( ) ;
294
321
295
- let felt = Felt :: from ( u64:: MAX ) ;
296
- let qm31: QM31Felt = felt. try_into ( ) . unwrap ( ) ;
297
- let qm31_to_felt = qm31. pack_into_felt ( ) ;
298
- dbg ! ( felt. to_le_digits( ) ) ;
322
+ assert_eq ! ( felt_to_qm31, qm31) ;
299
323
300
- assert_eq ! ( qm31_to_felt, Felt :: from( u64_max_reduced) ) ;
324
+ let qm31 = QM31Felt :: from_raw ( [ u64:: MAX , u64:: MAX , u64:: MAX , 0 ] ) ;
325
+ let felt: Felt = qm31. try_into ( ) . unwrap ( ) ;
326
+ let felt_to_qm31 = QM31Felt :: try_from ( felt) . unwrap ( ) ;
301
327
302
- let felt = Felt :: from ( u128:: MAX ) ;
303
- let qm31: QM31Felt = felt. try_into ( ) . unwrap ( ) ;
304
- let qm31_to_felt = qm31. pack_into_felt ( ) ;
328
+ assert_eq ! ( felt_to_qm31, qm31) ;
305
329
306
- let mut bytes = [ 0u8 ; 32 ] ;
307
- let bytes_part1 =
308
- ( ( u64_max_reduced) as u128 + ( ( ( u64_max_reduced) as u128 ) << 36 ) ) . to_le_bytes ( ) ;
309
- bytes[ 0 ..9 ] . copy_from_slice ( & bytes_part1[ 0 ..9 ] ) ;
330
+ let qm31 = QM31Felt :: from_raw ( [ u64:: MAX , u64:: MAX , u64:: MAX , u64:: MAX ] ) ;
331
+ let felt: Felt = qm31. try_into ( ) . unwrap ( ) ;
332
+ let felt_to_qm31 = QM31Felt :: try_from ( felt) . unwrap ( ) ;
310
333
311
- assert_eq ! ( qm31_to_felt , Felt :: from_bytes_le ( & bytes ) ) ;
334
+ assert_eq ! ( felt_to_qm31 , qm31 ) ;
312
335
}
313
336
314
337
#[ test]
0 commit comments