circomspect_circom_algebra/
modular_arithmetic.rs1use num_bigint::{BigInt, ModInverse, Sign};
2use num_traits::ToPrimitive;
3
4pub enum ArithmeticError {
5 DivisionByZero,
6 BitOverFlowInShift,
7}
8
9fn modulus(a: &BigInt, b: &BigInt) -> BigInt {
10 ((a % b) + b) % b
11}
12fn bit_representation(elem: &BigInt) -> (Sign, Vec<u8>) {
15 elem.to_radix_le(2)
16}
17fn mask(field: &BigInt) -> BigInt {
19 let two = BigInt::from(2);
20 let b = bit_representation(field).1.len();
21 let mask = num_traits::pow::pow(two, b);
22 mask - 1
23}
24
25pub fn add(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
27 modulus(&(left + right), field)
30}
31pub fn mul(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
32 modulus(&(left * right), field)
35}
36pub fn sub(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
37 modulus(&(left - right), field)
40}
41pub fn div(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
42 let right_inverse =
43 right.mod_inverse(field).map_or(Err(ArithmeticError::DivisionByZero), Ok)?;
44 let res = mul(left, &right_inverse, field);
45 Ok(res)
46}
47pub fn idiv(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
48 let zero = BigInt::from(0);
49 let left = modulus(left, field);
50 let right = modulus(right, field);
51 if right == zero {
52 Err(ArithmeticError::DivisionByZero)
53 } else {
54 Ok(left / right)
55 }
56}
57pub fn mod_op(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
58 let left = modulus(left, field);
59 let right = modulus(right, field);
60 Ok(modulus(&left, &right))
61}
62pub fn pow(base: &BigInt, exp: &BigInt, field: &BigInt) -> BigInt {
63 base.modpow(exp, field)
64}
65pub fn prefix_sub(elem: &BigInt, field: &BigInt) -> BigInt {
66 let minus_one = BigInt::from(-1);
67 mul(elem, &minus_one, field)
68}
69
70pub fn complement_256(elem: &BigInt, field: &BigInt) -> BigInt {
74 let (sign, mut bit_repr) = bit_representation(elem);
75 while bit_repr.len() > 256 {
76 bit_repr.pop();
77 }
78 for _i in bit_repr.len()..256 {
79 bit_repr.push(0);
80 }
81 for bit in &mut bit_repr {
82 *bit = u8::from(*bit == 0);
83 }
84 let cp = BigInt::from_radix_le(sign, &bit_repr, 2).unwrap();
85 modulus(&cp, field)
86}
87
88pub fn shift_l(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
89 let two = BigInt::from(2);
90 let top = field / &two;
91 if right <= &top {
92 let usize_repr = right.to_usize().map_or(Err(ArithmeticError::DivisionByZero), Ok)?;
93 let value = modulus(&((left * &num_traits::pow(two, usize_repr)) & &mask(field)), field);
94 Ok(value)
95 } else {
96 shift_r(left, &(field - right), field)
97 }
98}
99pub fn shift_r(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
100 let two = BigInt::from(2);
101 let top = field / &two;
102 if right <= &top {
103 let usize_repr = right.to_usize().map_or(Err(ArithmeticError::DivisionByZero), Ok)?;
104 let value = left / &num_traits::pow(two, usize_repr);
105 Ok(value)
106 } else {
107 shift_l(left, &(field - right), field)
108 }
109}
110pub fn bit_or(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
111 modulus(&(left | right), field)
112}
113pub fn bit_and(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
114 modulus(&(left & right), field)
115}
116pub fn bit_xor(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
117 modulus(&(left ^ right), field)
118}
119
120fn constant_true() -> BigInt {
122 BigInt::from(1)
123}
124fn constant_false() -> BigInt {
125 BigInt::from(0)
126}
127fn val(elem: &BigInt, field: &BigInt) -> BigInt {
128 let c = (field / &BigInt::from(2)) + 1;
129 if &c <= elem && elem < field {
130 elem - field
131 } else {
132 elem.clone()
133 }
134}
135fn comparable_element(elem: &BigInt, field: &BigInt) -> BigInt {
136 val(&modulus(elem, field), field)
137}
138fn normalize(elem: &BigInt, field: &BigInt) -> BigInt {
139 let f = constant_false();
140 let t = constant_true();
141 if comparable_element(elem, field) == f {
142 f
143 } else {
144 t
145 }
146}
147pub fn as_bool(elem: &BigInt, field: &BigInt) -> bool {
148 normalize(elem, field) != constant_false()
149}
150pub fn not(elem: &BigInt, field: &BigInt) -> BigInt {
151 (normalize(elem, field) + 1) % 2
152}
153pub fn bool_or(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
154 (normalize(left, field) + normalize(right, field) + bool_and(left, right, field)) % 2
155}
156pub fn bool_and(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
157 normalize(left, field) * normalize(right, field)
158}
159pub fn eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
160 let left = modulus(left, field);
161 let right = modulus(right, field);
162 if left == right {
163 constant_true()
164 } else {
165 constant_false()
166 }
167}
168pub fn lesser(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
169 let left = comparable_element(left, field);
170 let right = comparable_element(right, field);
171 if left < right {
172 constant_true()
173 } else {
174 constant_false()
175 }
176}
177pub fn not_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
178 not(&eq(left, right, field), field)
179}
180pub fn lesser_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
181 bool_or(&lesser(left, right, field), &eq(left, right, field), field)
182}
183pub fn greater(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
184 not(&lesser_eq(left, right, field), field)
185}
186pub fn greater_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
187 bool_or(&greater(left, right, field), &eq(left, right, field), field)
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193 const FIELD: &str = "257";
194 #[test]
195 fn mod_check() {
196 let a = BigInt::from(-8);
197 let b = BigInt::from(5);
198 let res = super::modulus(&a, &b);
199 assert_eq!(res, BigInt::from(2));
200 }
201 #[test]
202 fn comparison_check() {
203 let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
204 .expect("generating the big int was not possible");
205 let a = sub(&BigInt::from(2), &BigInt::from(1), &field);
206 let b = BigInt::from(-1);
207 let res = not_eq(&a, &b, &field);
208 assert!(as_bool(&res, &field));
209 }
210 #[test]
211 fn mod_operation_check() {
212 let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
213 .expect("generating the big int was not possible");
214 let a = BigInt::from(17);
215 let b = BigInt::from(32);
216 if let Ok(res) = mod_op(&a, &b, &field) {
217 assert_eq!(a, res)
218 } else {
219 unreachable!();
220 }
221 }
222 #[test]
223 fn complement_of_complement_is_the_original_test() {
224 let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
225 .expect("generating the big int was not possible");
226 let big_num = BigInt::parse_bytes("1234".as_bytes(), 10)
227 .expect("generating the big int was not possible");
228 let big_num_complement = complement_256(&big_num, &field);
229 let big_num_complement_complement = complement_256(&big_num_complement, &field);
230 let big_num_modulus = modulus(&big_num, &field);
231 assert_eq!(big_num_complement_complement, big_num_modulus);
232 }
233 #[test]
234 fn lesser_eq_test() {
235 let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
236 .expect("generating the big int was not possible");
237 let zero = BigInt::from(0);
238 let two = BigInt::from(2);
239 assert!(zero < two);
240 assert!(as_bool(&lesser_eq(&zero, &two, &field), &field));
241 }
242}