Skip to content

Commit 243e875

Browse files
committed
Improve field macro
Compile time Montgomery multiplication!
1 parent ae21931 commit 243e875

File tree

13 files changed

+364
-137
lines changed

13 files changed

+364
-137
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"serialize",
55
"serialize-derive",
66

7+
"ff-macros",
78
"ff-asm",
89
"ff",
910

ff-macros/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "ark-ff-macros"
3+
version = "0.1.0"
4+
authors = [ "arkworks contributors" ]
5+
description = "A library for generating x86-64 assembly for finite field multiplication"
6+
homepage = "https://arworks.rs"
7+
repository = "https://github.com/arkworks/algebra"
8+
documentation = "https://docs.rs/ark-ff-asm/"
9+
keywords = ["cryptography", "finite fields", "assembly" ]
10+
categories = ["cryptography"]
11+
include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
12+
license = "MIT/Apache-2.0"
13+
edition = "2018"
14+
15+
[dependencies]
16+
quote = "1.0.0"
17+
syn = { version = "1.0.0", features = ["full", "parsing", "extra-traits"]}
18+
num-bigint = { version = "0.3", default-features = false }
19+
num-traits = { version = "0.2", default-features = false }
20+
21+
[lib]
22+
proc-macro = true

ff-macros/LICENSE-APACHE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-APACHE

ff-macros/LICENSE-MIT

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-MIT

ff-macros/src/lib.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#![deny(
2+
warnings,
3+
unused,
4+
future_incompatible,
5+
nonstandard_style,
6+
rust_2018_idioms
7+
)]
8+
#![forbid(unsafe_code)]
9+
#![recursion_limit = "128"]
10+
11+
use num_bigint::{BigInt, Sign};
12+
use proc_macro::TokenStream;
13+
use std::str::FromStr;
14+
use syn::{Expr, Lit};
15+
16+
fn parse_string(input: TokenStream) -> Option<String> {
17+
let input: Expr = syn::parse(input).unwrap();
18+
let input = if let Expr::Group(syn::ExprGroup { expr, .. }) = input {
19+
expr
20+
} else {
21+
panic!("could not parse");
22+
};
23+
match *input {
24+
Expr::Lit(expr_lit) => match expr_lit.lit {
25+
Lit::Str(s) => Some(s.value()),
26+
_ => None,
27+
},
28+
_ => None,
29+
}
30+
}
31+
32+
fn str_to_limbs(num: &str) -> (bool, Vec<String>) {
33+
let (sign, digits) = BigInt::from_str(num)
34+
.expect("could not parse to bigint")
35+
.to_radix_le(16);
36+
let limbs = digits
37+
.chunks(16)
38+
.map(|chunk| {
39+
let mut this = 0u64;
40+
for (i, hexit) in chunk.iter().enumerate() {
41+
this += (*hexit as u64) << (4 * i);
42+
}
43+
format!("{}u64", this)
44+
})
45+
.collect::<Vec<_>>();
46+
47+
let sign_is_positive = sign != Sign::Minus;
48+
(sign_is_positive, limbs)
49+
}
50+
51+
#[proc_macro]
52+
pub fn to_sign_and_limbs(input: TokenStream) -> TokenStream {
53+
let num = parse_string(input).expect("expected decimal string");
54+
let (is_positive, limbs) = str_to_limbs(&num);
55+
56+
let limbs: String = limbs.join(", ");
57+
let limbs_and_sign = format!("({}", is_positive) + ", [" + &limbs + "])";
58+
let tuple: Expr = syn::parse_str(&limbs_and_sign).unwrap();
59+
quote::quote!(#tuple).into()
60+
}
61+
62+
#[test]
63+
fn test_str_to_limbs() {
64+
let (is_positive, limbs) = str_to_limbs("-5");
65+
assert!(!is_positive);
66+
assert_eq!(&limbs, &["5u64".to_string()]);
67+
68+
let (is_positive, limbs) = str_to_limbs("100");
69+
assert!(is_positive);
70+
assert_eq!(&limbs, &["100u64".to_string()]);
71+
72+
let large_num = -((1i128 << 64) + 101234001234i128);
73+
let (is_positive, limbs) = str_to_limbs(&large_num.to_string());
74+
assert!(!is_positive);
75+
assert_eq!(&limbs, &["101234001234u64".to_string(), "1u64".to_string()]);
76+
77+
let num = "80949648264912719408558363140637477264845294720710499478137287262712535938301461879813459410946";
78+
let (is_positive, limbs) = str_to_limbs(&num.to_string());
79+
assert!(is_positive);
80+
let expected_limbs = [
81+
format!("{}u64", 0x8508c00000000002u64),
82+
format!("{}u64", 0x452217cc90000000u64),
83+
format!("{}u64", 0xc5ed1347970dec00u64),
84+
format!("{}u64", 0x619aaf7d34594aabu64),
85+
format!("{}u64", 0x9b3af05dd14f6ecu64),
86+
];
87+
assert_eq!(&limbs, &expected_limbs);
88+
}

ff/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ build = "build.rs"
1515

1616
[dependencies]
1717
ark-ff-asm = { path = "../ff-asm" }
18+
ark-ff-macros = { path = "../ff-macros" }
1819
ark-std = { git = "https://github.com/arkworks-rs/utils", default-features = false }
1920
ark-serialize = { path = "../serialize", default-features = false }
2021
derivative = { version = "2", features = ["use_core"] }

ff/src/biginteger/arithmetic.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use ark_std::vec::Vec;
2+
3+
/// Calculate a + b + carry, returning the sum and modifying the
4+
/// carry value.
5+
macro_rules! adc {
6+
($a:expr, $b:expr, $carry:expr$(,)?) => {{
7+
let tmp = ($a as u128) + ($b as u128) + ($carry as u128);
8+
9+
$carry = (tmp >> 64) as u64;
10+
11+
tmp as u64
12+
}};
13+
}
14+
15+
/// Calculate a + (b * c) + carry, returning the least significant digit
16+
/// and setting carry to the most significant digit.
17+
macro_rules! mac_with_carry {
18+
($a:expr, $b:expr, $c:expr, $carry:expr$(,)?) => {{
19+
let tmp = ($a as u128) + ($b as u128 * $c as u128) + ($carry as u128);
20+
21+
$carry = (tmp >> 64) as u64;
22+
23+
tmp as u64
24+
}};
25+
}
26+
27+
/// Calculate a - b - borrow, returning the result and modifying
28+
/// the borrow value.
29+
macro_rules! sbb {
30+
($a:expr, $b:expr, $borrow:expr$(,)?) => {{
31+
let tmp = (1u128 << 64) + ($a as u128) - ($b as u128) - ($borrow as u128);
32+
33+
$borrow = if tmp >> 64 == 0 { 1 } else { 0 };
34+
35+
tmp as u64
36+
}};
37+
}
38+
39+
#[inline(always)]
40+
pub(crate) fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
41+
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
42+
43+
*carry = (tmp >> 64) as u64;
44+
45+
tmp as u64
46+
}
47+
48+
#[inline(always)]
49+
pub(crate) fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) {
50+
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
51+
52+
*carry = (tmp >> 64) as u64;
53+
}
54+
55+
pub fn find_wnaf(num: &[u64]) -> Vec<i64> {
56+
let is_zero = |num: &[u64]| num.iter().all(|x| *x == 0u64);
57+
let is_odd = |num: &[u64]| num[0] & 1 == 1;
58+
let sub_noborrow = |num: &mut [u64], z: u64| {
59+
let mut other = vec![0u64; num.len()];
60+
other[0] = z;
61+
let mut borrow = 0;
62+
63+
for (a, b) in num.iter_mut().zip(other) {
64+
*a = sbb!(*a, b, borrow);
65+
}
66+
};
67+
let add_nocarry = |num: &mut [u64], z: u64| {
68+
let mut other = vec![0u64; num.len()];
69+
other[0] = z;
70+
let mut carry = 0;
71+
72+
for (a, b) in num.iter_mut().zip(other) {
73+
*a = adc!(*a, b, carry);
74+
}
75+
};
76+
let div2 = |num: &mut [u64]| {
77+
let mut t = 0;
78+
for i in num.iter_mut().rev() {
79+
let t2 = *i << 63;
80+
*i >>= 1;
81+
*i |= t;
82+
t = t2;
83+
}
84+
};
85+
86+
let mut num = num.to_vec();
87+
let mut res = vec![];
88+
89+
while !is_zero(&num) {
90+
let z: i64;
91+
if is_odd(&num) {
92+
z = 2 - (num[0] % 4) as i64;
93+
if z >= 0 {
94+
sub_noborrow(&mut num, z as u64)
95+
} else {
96+
add_nocarry(&mut num, (-z) as u64)
97+
}
98+
} else {
99+
z = 0;
100+
}
101+
res.push(z);
102+
div2(&mut num);
103+
}
104+
105+
res
106+
}

ff/src/biginteger/macros.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ macro_rules! bigint_impl {
1717
let mut carry = 0;
1818

1919
for (a, b) in self.0.iter_mut().zip(other.0.iter()) {
20-
*a = arithmetic::adc(*a, *b, &mut carry);
20+
*a = adc!(*a, *b, carry);
2121
}
2222

2323
carry != 0
@@ -28,7 +28,7 @@ macro_rules! bigint_impl {
2828
let mut borrow = 0;
2929

3030
for (a, b) in self.0.iter_mut().zip(other.0.iter()) {
31-
*a = arithmetic::sbb(*a, *b, &mut borrow);
31+
*a = sbb!(*a, *b, borrow);
3232
}
3333

3434
borrow != 0

ff/src/biginteger/mod.rs

Lines changed: 2 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use rand::{
1515
distributions::{Distribution, Standard},
1616
Rng,
1717
};
18-
18+
#[macro_use]
19+
pub mod arithmetic;
1920
#[macro_use]
2021
mod macros;
2122

@@ -117,108 +118,3 @@ pub trait BigInteger:
117118
Ok(())
118119
}
119120
}
120-
121-
pub mod arithmetic {
122-
use ark_std::vec::Vec;
123-
pub fn find_wnaf(num: &[u64]) -> Vec<i64> {
124-
let is_zero = |num: &[u64]| num.iter().all(|x| *x == 0u64);
125-
let is_odd = |num: &[u64]| num[0] & 1 == 1;
126-
let sub_noborrow = |num: &mut [u64], z: u64| {
127-
let mut other = vec![0u64; num.len()];
128-
other[0] = z;
129-
let mut borrow = 0;
130-
131-
for (a, b) in num.iter_mut().zip(other) {
132-
*a = sbb(*a, b, &mut borrow);
133-
}
134-
};
135-
let add_nocarry = |num: &mut [u64], z: u64| {
136-
let mut other = vec![0u64; num.len()];
137-
other[0] = z;
138-
let mut carry = 0;
139-
140-
for (a, b) in num.iter_mut().zip(other) {
141-
*a = adc(*a, b, &mut carry);
142-
}
143-
};
144-
let div2 = |num: &mut [u64]| {
145-
let mut t = 0;
146-
for i in num.iter_mut().rev() {
147-
let t2 = *i << 63;
148-
*i >>= 1;
149-
*i |= t;
150-
t = t2;
151-
}
152-
};
153-
154-
let mut num = num.to_vec();
155-
let mut res = vec![];
156-
157-
while !is_zero(&num) {
158-
let z: i64;
159-
if is_odd(&num) {
160-
z = 2 - (num[0] % 4) as i64;
161-
if z >= 0 {
162-
sub_noborrow(&mut num, z as u64)
163-
} else {
164-
add_nocarry(&mut num, (-z) as u64)
165-
}
166-
} else {
167-
z = 0;
168-
}
169-
res.push(z);
170-
div2(&mut num);
171-
}
172-
173-
res
174-
}
175-
176-
/// Calculate a + b + carry, returning the sum and modifying the
177-
/// carry value.
178-
#[inline(always)]
179-
pub(crate) fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
180-
let tmp = u128::from(a) + u128::from(b) + u128::from(*carry);
181-
182-
*carry = (tmp >> 64) as u64;
183-
184-
tmp as u64
185-
}
186-
187-
/// Calculate a - b - borrow, returning the result and modifying
188-
/// the borrow value.
189-
#[inline(always)]
190-
pub(crate) fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
191-
let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow);
192-
193-
*borrow = if tmp >> 64 == 0 { 1 } else { 0 };
194-
195-
tmp as u64
196-
}
197-
198-
/// Calculate a + (b * c) + carry, returning the least significant digit
199-
/// and setting carry to the most significant digit.
200-
#[inline(always)]
201-
pub(crate) fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
202-
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry);
203-
204-
*carry = (tmp >> 64) as u64;
205-
206-
tmp as u64
207-
}
208-
209-
#[inline(always)]
210-
pub(crate) fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
211-
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
212-
213-
*carry = (tmp >> 64) as u64;
214-
215-
tmp as u64
216-
}
217-
218-
#[inline(always)]
219-
pub(crate) fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) {
220-
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c);
221-
222-
*carry = (tmp >> 64) as u64;
223-
}
224-
}

0 commit comments

Comments
 (0)