- 
                Notifications
    
You must be signed in to change notification settings  - Fork 363
 
Ark-ff Small Field Support #1044
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Open
      
      
            benbencik
  wants to merge
  36
  commits into
  arkworks-rs:master
  
    
      
        
          
  
    
      Choose a base branch
      
     
    
      
        
      
      
        
          
          
        
        
          
            
              
              
              
  
           
        
        
          
            
              
              
           
        
       
     
  
        
          
            
          
            
          
        
       
    
      
from
benbencik:small_fp
  
      
      
   
  
    
  
  
  
 
  
      
    base: master
Could not load branches
            
              
  
    Branch not found: {{ refName }}
  
            
                
      Loading
              
            Could not load tags
            
            
              Nothing to show
            
              
  
            
                
      Loading
              
            Are you sure you want to change the base?
            Some commits from the old base branch may be removed from the timeline,
            and old review comments may become outdated.
          
          
  
     Open
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            36 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      7b49928
              
                placeholder for SmallFp
              
              
                z-tech daa99f7
              
                migrate smallfp implementation from blendy
              
              
                benbencik 0fadbb9
              
                add tests
              
              
                benbencik 3d6636e
              
                add benchmarks
              
              
                benbencik f7de469
              
                add square root precomputation
              
              
                benbencik 4d835f4
              
                split sampling method into cases
              
              
                benbencik 4ba1448
              
                fix mul overflow issue
              
              
                benbencik d1937cc
              
                extend testing suite (some tests failing)
              
              
                benbencik a1a3343
              
                rewrite sampling function
              
              
                benbencik 9cc57f5
              
                fix overflowing bug in multiplication
              
              
                benbencik be2664d
              
                fix the computation for bit size
              
              
                benbencik e39da81
              
                consider (de)serialization of small elements
              
              
                benbencik 3c7daf9
              
                rewrite computation for two adic root of unity
              
              
                benbencik 8a907d8
              
                use safe mul to avoid overflows in compile-time
              
              
                benbencik ea4cce2
              
                update the smallfp tests
              
              
                benbencik db896d3
              
                Merge branch 'smallfp-test' into small_fp
              
              
                benbencik 2945217
              
                rewrite mul_assing to handle overflows correctly
              
              
                benbencik 582ac3d
              
                move tests and benches to test-curves
              
              
                benbencik b91c704
              
                update doccomments
              
              
                benbencik f2f3fb1
              
                use the provided bench templates for fields
              
              
                benbencik f8c81e8
              
                Merge branch 'master' into small_fp
              
              
                benbencik 176fc97
              
                add info about small fields to readme
              
              
                benbencik 0cad96a
              
                add pending PR 1044
              
              
                benbencik 742bb39
              
                fix markdown linter error
              
              
                benbencik 4ccddce
              
                add mont multiplication fastpath
              
              
                benbencik 42d99e9
              
                clean unused type
              
              
                benbencik 6976823
              
                specify mont mul impl at compile time
              
              
                benbencik 7a7f18b
              
                add inlinging for arithmetic ops
              
              
                benbencik f59fa7d
              
                replace modulo operation in addition
              
              
                benbencik 676a7c3
              
                Merge branch 'master' into small_fp
              
              
                z-tech d663208
              
                remove branching from mont multiplicaiton
              
              
                benbencik 2a6e5e3
              
                Merge branch 'master' into small_fp
              
              
                z-tech 8acdef2
              
                update utils helper functions
              
              
                benbencik 391f0ba
              
                specify supported moduli
              
              
                benbencik 234bd8f
              
                reduce duplicity in tests
              
              
                benbencik 9172802
              
                revert ff Cargo.toml changes
              
              
                benbencik File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| mod montgomery_backend; | ||
| mod standard_backend; | ||
| mod utils; | ||
| 
     | 
||
| use quote::quote; | ||
| 
     | 
||
| /// This function is called by the `#[derive(SmallFp)]` macro and generates | ||
| /// the implementation of the `SmallFpConfig` | ||
| pub(crate) fn small_fp_config_helper( | ||
| modulus: u128, | ||
| generator: u128, | ||
| backend: String, | ||
| config_name: proc_macro2::Ident, | ||
| ) -> proc_macro2::TokenStream { | ||
| let ty = match modulus { | ||
| m if m < 1u128 << 8 => quote! { u8 }, | ||
| m if m < 1u128 << 16 => quote! { u16 }, | ||
| m if m < 1u128 << 32 => quote! { u32 }, | ||
| m if m < 1u128 << 64 => quote! { u64 }, | ||
| _ => quote! { u128 }, | ||
| }; | ||
| 
     | 
||
| let backend_impl = match backend.as_str() { | ||
| "standard" => standard_backend::backend_impl(&ty, modulus, generator), | ||
| "montgomery" => { | ||
| if modulus >= 1u128 << 127 { | ||
| panic!( | ||
| "SmallFpConfig montgomery backend supports only moduli < 2^127. Use MontConfig with BigInt instead of SmallFp." | ||
| ) | ||
| } | ||
| montgomery_backend::backend_impl(&ty, modulus, generator) | ||
| }, | ||
| 
     | 
||
| _ => panic!("Unknown backend type: {}", backend), | ||
| }; | ||
| 
     | 
||
| let new_impl = match backend.as_str() { | ||
| "standard" => standard_backend::new(), | ||
| "montgomery" => montgomery_backend::new(modulus, ty), | ||
| _ => panic!("Unknown backend type: {}", backend), | ||
| }; | ||
| 
     | 
||
| quote! { | ||
| impl SmallFpConfig for #config_name { | ||
| #backend_impl | ||
| } | ||
| 
     | 
||
| impl #config_name { | ||
| #new_impl | ||
| } | ||
| } | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,258 @@ | ||
| use std::u32; | ||
| 
     | 
||
| use super::*; | ||
| use crate::small_fp::utils::{ | ||
| compute_two_adic_root_of_unity, compute_two_adicity, generate_montgomery_bigint_casts, | ||
| generate_sqrt_precomputation, mod_mul_const, | ||
| }; | ||
| 
     | 
||
| pub(crate) fn backend_impl( | ||
| ty: &proc_macro2::TokenStream, | ||
| modulus: u128, | ||
| generator: u128, | ||
| ) -> proc_macro2::TokenStream { | ||
| let k_bits = 128 - modulus.leading_zeros(); | ||
| let r: u128 = 1u128 << k_bits; | ||
| let r_mod_n = r % modulus; | ||
| let r_mask = r - 1; | ||
| 
     | 
||
| let n_prime = mod_inverse_pow2(modulus, k_bits); | ||
| let one_mont = r_mod_n; | ||
| let generator_mont = mod_mul_const(generator % modulus, r_mod_n % modulus, modulus); | ||
| 
     | 
||
| let two_adicity = compute_two_adicity(modulus); | ||
| let two_adic_root = compute_two_adic_root_of_unity(modulus, two_adicity, generator); | ||
| let two_adic_root_mont = mod_mul_const(two_adic_root, r_mod_n, modulus); | ||
| 
     | 
||
| let neg_one_mont = mod_mul_const(modulus - 1, r_mod_n, modulus); | ||
| 
     | 
||
| let (from_bigint_impl, into_bigint_impl) = | ||
| generate_montgomery_bigint_casts(modulus, k_bits, r_mod_n); | ||
| let sqrt_precomp_impl = generate_sqrt_precomputation(modulus, two_adicity, Some(r_mod_n)); | ||
| 
     | 
||
| // Generate multiplication implementation based on type | ||
| let mul_impl = generate_mul_impl(ty, modulus, k_bits, r_mask, n_prime); | ||
| 
     | 
||
| quote! { | ||
| type T = #ty; | ||
| const MODULUS: Self::T = #modulus as Self::T; | ||
| const MODULUS_128: u128 = #modulus; | ||
| const GENERATOR: SmallFp<Self> = SmallFp::new(#generator_mont as Self::T); | ||
| const ZERO: SmallFp<Self> = SmallFp::new(0 as Self::T); | ||
| const ONE: SmallFp<Self> = SmallFp::new(#one_mont as Self::T); | ||
| const NEG_ONE: SmallFp<Self> = SmallFp::new(#neg_one_mont as Self::T); | ||
| 
     | 
||
| 
     | 
||
| const TWO_ADICITY: u32 = #two_adicity; | ||
| const TWO_ADIC_ROOT_OF_UNITY: SmallFp<Self> = SmallFp::new(#two_adic_root_mont as Self::T); | ||
| #sqrt_precomp_impl | ||
| 
     | 
||
| #[inline(always)] | ||
| fn add_assign(a: &mut SmallFp<Self>, b: &SmallFp<Self>) { | ||
| let (mut val, overflow) = a.value.overflowing_add(b.value); | ||
| 
     | 
||
| if overflow { | ||
| val = Self::T::MAX - Self::MODULUS + 1 + val | ||
| } | ||
| 
     | 
||
| if val >= Self::MODULUS { | ||
| val -= Self::MODULUS; | ||
| } | ||
| a.value = val; | ||
| } | ||
| 
     | 
||
| #[inline(always)] | ||
| fn sub_assign(a: &mut SmallFp<Self>, b: &SmallFp<Self>) { | ||
| if a.value >= b.value { | ||
| a.value -= b.value; | ||
| } else { | ||
| a.value = Self::MODULUS - (b.value - a.value); | ||
| } | ||
| } | ||
| 
     | 
||
| #[inline(always)] | ||
| fn double_in_place(a: &mut SmallFp<Self>) { | ||
| let tmp = *a; | ||
| Self::add_assign(a, &tmp); | ||
| } | ||
| 
     | 
||
| #[inline(always)] | ||
| fn neg_in_place(a: &mut SmallFp<Self>) { | ||
| if a.value != (0 as Self::T) { | ||
| a.value = Self::MODULUS - a.value; | ||
| } | ||
| } | ||
| 
     | 
||
| #mul_impl | ||
| 
     | 
||
| #[inline(always)] | ||
| fn sum_of_products<const T: usize>( | ||
| a: &[SmallFp<Self>; T], | ||
| b: &[SmallFp<Self>; T],) -> SmallFp<Self> { | ||
| let mut acc = SmallFp::new(0 as Self::T); | ||
| for (x, y) in a.iter().zip(b.iter()) { | ||
| let mut prod = *x; | ||
| Self::mul_assign(&mut prod, y); | ||
| Self::add_assign(&mut acc, &prod); | ||
| } | ||
| acc | ||
| } | ||
| 
     | 
||
| #[inline(always)] | ||
| fn square_in_place(a: &mut SmallFp<Self>) { | ||
| let tmp = *a; | ||
| Self::mul_assign(a, &tmp); | ||
| } | ||
| 
     | 
||
| fn inverse(a: &SmallFp<Self>) -> Option<SmallFp<Self>> { | ||
| if a.value == 0 { | ||
| return None; | ||
| } | ||
| 
     | 
||
| let mut result = Self::ONE; | ||
| let mut base = *a; | ||
| let mut exp = Self::MODULUS - 2; | ||
| 
     | 
||
| while exp > 0 { | ||
| if exp & 1 == 1 { | ||
| Self::mul_assign(&mut result, &base); | ||
| } | ||
| 
     | 
||
| let mut sq = base; | ||
| Self::square_in_place(&mut sq); | ||
| base = sq; | ||
| exp >>= 1; | ||
| } | ||
| 
     | 
||
| Some(result) | ||
| } | ||
| 
     | 
||
| #from_bigint_impl | ||
| 
     | 
||
| #into_bigint_impl | ||
| } | ||
| } | ||
| 
     | 
||
| // Selects the appropriate multiplication algorithm at compile time: | ||
| // if modulus <= u64, multiply by casting to the next largest primitive | ||
| // otherwise, multiply in parts to form a 256-bit product before reduction | ||
| fn generate_mul_impl( | ||
| ty: &proc_macro2::TokenStream, | ||
| modulus: u128, | ||
| k_bits: u32, | ||
| r_mask: u128, | ||
| n_prime: u128, | ||
| ) -> proc_macro2::TokenStream { | ||
| let ty_str = ty.to_string(); | ||
| 
     | 
||
| if ty_str == "u128" { | ||
| quote! { | ||
| #[inline(always)] | ||
| fn mul_assign(a: &mut SmallFp<Self>, b: &SmallFp<Self>) { | ||
| // 256-bit result stored as lo, hi | ||
| // t = a * b | ||
| let lolo = (a.value & 0xFFFFFFFFFFFFFFFF) * (b.value & 0xFFFFFFFFFFFFFFFF); | ||
| let lohi = (a.value & 0xFFFFFFFFFFFFFFFF) * (b.value >> 64); | ||
| let hilo = (a.value >> 64) * (b.value & 0xFFFFFFFFFFFFFFFF); | ||
| let hihi = (a.value >> 64) * (b.value >> 64); | ||
| 
     | 
||
| let (cross_sum, cross_carry) = lohi.overflowing_add(hilo); | ||
| let (mid, mid_carry) = lolo.overflowing_add(cross_sum << 64); | ||
| let t_lo = mid; | ||
| let t_hi = hihi + (cross_sum >> 64) + ((cross_carry as u128) << 64) + (mid_carry as u128); | ||
| 
     | 
||
| // m = t_lo * n_prime & r_mask | ||
| let m = t_lo.wrapping_mul(#n_prime) & #r_mask; | ||
| 
     | 
||
| // mn = m * modulus | ||
| let lolo = (m & 0xFFFFFFFFFFFFFFFF) * (#modulus & 0xFFFFFFFFFFFFFFFF); | ||
| let lohi = (m & 0xFFFFFFFFFFFFFFFF) * (#modulus >> 64); | ||
| let hilo = (m >> 64) * (#modulus & 0xFFFFFFFFFFFFFFFF); | ||
| let hihi = (m >> 64) * (#modulus >> 64); | ||
| 
     | 
||
| let (cross_sum, cross_carry) = lohi.overflowing_add(hilo); | ||
| let (mid, mid_carry) = lolo.overflowing_add(cross_sum << 64); | ||
| let mn_lo = mid; | ||
| let mn_hi = hihi + (cross_sum >> 64) + ((cross_carry as u128) << 64) + (mid_carry as u128); | ||
| 
     | 
||
| // (t + mn) / R | ||
| let (sum_lo, carry) = t_lo.overflowing_add(mn_lo); | ||
| let sum_hi = t_hi + mn_hi + (carry as u128); | ||
| 
     | 
||
| let mut u = (sum_lo >> #k_bits) | (sum_hi << (128 - #k_bits)); | ||
| u -= #modulus * (u >= #modulus) as u128; | ||
| a.value = u as Self::T; | ||
| } | ||
| } | ||
| } else { | ||
| let (mul_ty, bits) = match ty_str.as_str() { | ||
| "u8" => (quote! {u16}, 16u32), | ||
| "u16" => (quote! {u32}, 32u32), | ||
| "u32" => (quote! {u64}, 64u32), | ||
| _ => (quote! {u128}, 128u32), | ||
| }; | ||
| 
     | 
||
| let r_mask_downcast = quote! { #r_mask as #mul_ty }; | ||
| let n_prime_downcast = quote! { #n_prime as #mul_ty }; | ||
| let modulus_downcast = quote! { #modulus as #mul_ty }; | ||
| let one = quote! { 1 as #mul_ty }; | ||
| 
     | 
||
| quote! { | ||
| #[inline(always)] | ||
| fn mul_assign(a: &mut SmallFp<Self>, b: &SmallFp<Self>) { | ||
                
      
                  z-tech marked this conversation as resolved.
               
          
            Show resolved
            Hide resolved
         | 
||
| let a_val = a.value as #mul_ty; | ||
| let b_val = b.value as #mul_ty; | ||
| 
     | 
||
| let t = a_val * b_val; | ||
| let t_low = t & #r_mask_downcast; | ||
| 
     | 
||
| // m = t_lo * n_prime & r_mask | ||
| let m = t_low.wrapping_mul(#n_prime_downcast) & #r_mask_downcast; | ||
| 
     | 
||
| // mn = m * modulus | ||
| let mn = m * #modulus_downcast; | ||
| 
     | 
||
| // (t + mn) / R | ||
| let (sum, overflow) = t.overflowing_add(mn); | ||
| let mut u = sum >> #k_bits; | ||
| 
     | 
||
| u += ((#one) << (#bits - #k_bits)) * (overflow as #mul_ty); | ||
| u -= #modulus_downcast * ((u >= #modulus_downcast) as #mul_ty); | ||
| a.value = u as Self::T; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| 
     | 
||
| fn mod_inverse_pow2(n: u128, k_bits: u32) -> u128 { | ||
| let mut inv = 1u128; | ||
| for _ in 0..k_bits { | ||
| inv = inv.wrapping_mul(2u128.wrapping_sub(n.wrapping_mul(inv))); | ||
| } | ||
| let mask = (1u128 << k_bits) - 1; | ||
| inv.wrapping_neg() & mask | ||
| } | ||
| 
     | 
||
| pub(crate) fn new(modulus: u128, _ty: proc_macro2::TokenStream) -> proc_macro2::TokenStream { | ||
| let k_bits = 128 - modulus.leading_zeros(); | ||
| let r: u128 = 1u128 << k_bits; | ||
| let r_mod_n = r % modulus; | ||
| let r2 = mod_mul_const(r_mod_n, r_mod_n, modulus); | ||
| 
     | 
||
| quote! { | ||
| pub fn new(value: <Self as SmallFpConfig>::T) -> SmallFp<Self> { | ||
| let reduced_value = value % <Self as SmallFpConfig>::MODULUS; | ||
| let mut tmp = SmallFp::new(reduced_value); | ||
| let r2_elem = SmallFp::new(#r2 as <Self as SmallFpConfig>::T); | ||
| <Self as SmallFpConfig>::mul_assign(&mut tmp, &r2_elem); | ||
| tmp | ||
| } | ||
| 
     | 
||
| pub fn exit(a: &mut SmallFp<Self>) { | ||
| let mut tmp = *a; | ||
| let one = SmallFp::new(1 as <Self as SmallFpConfig>::T); | ||
| <Self as SmallFpConfig>::mul_assign(&mut tmp, &one); | ||
| a.value = tmp.value; | ||
| } | ||
| } | ||
| } | ||
      
      Oops, something went wrong.
        
    
  
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.