kimchi_msm/fec/
interpreter.rs

1use crate::{
2    circuit_design::{
3        capabilities::{read_column_array, write_column_array_const, write_column_const},
4        ColAccessCap, ColWriteCap, LookupCap,
5    },
6    fec::{
7        columns::{FECColumn, FECColumnInput, FECColumnInter, FECColumnOutput},
8        lookups::LookupTable,
9    },
10    serialization::interpreter::{
11        bigint_to_biguint_f, combine_carry, combine_small_to_large, fold_choice2,
12        limb_decompose_biguint, limb_decompose_ff, LIMB_BITSIZE_LARGE, LIMB_BITSIZE_SMALL,
13        N_LIMBS_LARGE, N_LIMBS_SMALL,
14    },
15};
16use ark_ff::{PrimeField, Zero};
17use core::marker::PhantomData;
18use num_bigint::{BigInt, BigUint, ToBigInt};
19use num_integer::Integer;
20use o1_utils::field_helpers::FieldHelpers;
21
22/// Convenience function for printing.
23pub fn limbs_to_bigints<F: PrimeField, const N: usize>(input: [F; N]) -> Vec<BigInt> {
24    input
25        .iter()
26        .map(|f| f.to_bigint_positive())
27        .collect::<Vec<_>>()
28}
29
30/// When P = (xP,yP) and Q = (xQ,yQ) are not negative of each other, thus function ensures
31///
32/// P + Q = R where
33///
34/// s = (yP - yQ) / (xP - xQ)
35///
36/// xR = s^2 - xP - xQ and yR = -yP + s(xP - xR)
37///
38///
39/// Equations that we check:
40///   1. s (xP - xQ) - (yP - yQ) - q_1 f =  0
41///   2. xR - s^2 + xP + xQ - q_2 f = 0
42///   3. yR + yP - s (xP - xR) - q_3 f = 0
43///
44/// We will use several different "packing" format.
45///
46/// === Limb equations
47///
48/// The standard (small limb) one, using 17 limbs of 15 bits each, is
49/// mostly helpful for range-checking the element, because 15-bit
50/// range checks are easy to perform. Additionally, this format is
51/// helpful for checking that the value is ∈ [0,f), where f is a
52/// foreign field modulus.
53///
54/// We will additionally use a "large limb" format, where each limb is
55/// 75 bits, so fitting exactly 5 small limbs. This format is
56/// effective for trusted values that we do not need to range check.
57/// Additionally, verifying equations on 75 bits is more effective in
58/// terms of minimising constraints.
59///
60/// Regarding our /concrete limb/ equations, they are different from
61/// the generic ones above in that they have carries. The carries are
62/// stored in a third format. Let us illustrate the design on the
63/// first equation of the three. Its concrete final form is as follows:
64///
65/// for i ∈ [0..2L-2]:
66///    \sum_{j,k < L | k+j = i} s_j (xP_k - xQ_k)
67///       - ((yP_i - yQ_i) if i < L else 0)
68///       - q_1_sign * \sum_{j,k < L | k+j = i} q_1_j f_k
69///       - (c_i * 2^B if i < 2L-2 else 0)
70///       + (c_{i-1} if i > 0 else 0) = 0
71///
72/// First, note that the equation has turned into 2L-2 equations. This
73/// is because the form of multiplication (and there are two
74/// multiplications here, s*(xP-xQ) and q*f) implies quadratic number
75/// of limb multiplications, but because our operations are modulo f,
76/// every single element except for q in this equation is in the
77/// field.
78///
79/// Instead of having one limb-multiplication per row (e.g.
80/// q_1_5*f_6), which would lead to quadratic number of constraints,
81/// and quadratic number of intermediate-representation columns, we
82/// "batch" all multiplications for degree $i$ in one constraint as
83/// above.
84///
85/// Second, note that the carries are non-uniform in the loop: for the
86/// first limb, we only subtract c_0*2^B, while for the last limb we
87/// only add the previous carry c_{2L-3}. This means that, as usual,
88/// the number of carries is one less than the number of
89/// limb-equations. In our case, every equation relies on 2L-2 "large"
90/// carries.
91///
92/// Finally, small remark is that for simplicity we carry the sign of
93/// q separately from its absolute value. Note that in the original
94/// generic equation s (xP - xQ) - (yP - yQ) - q_1 f = 0 that holds
95/// over the integers, the only value (except for f) that can actually
96/// be outside of the field range [0,f-1) is q_1. In fact, while every
97/// other value is strictly positive, q_1 can be actually negative.
98/// Carrying its sign separately greatly simplifies modelling limbs at
99/// the expense of just 3 extra columns per circuit. So q_1 limbs
100/// actually contains the absolute value of q_1, while q_1_sign is in
101/// {-1,1}.
102///
103/// === Data layout
104///
105/// Now we are ready to present the data layout and to discuss the
106/// representation modes.
107///
108/// Let
109/// L := N_LIMBS_LARGE
110/// S := N_LIMBS_SMALL
111///
112/// variable    offset      length        comment
113/// ---------------------------------------------------------------
114/// xP:         0                 L          Always trusted, not range checked
115/// yP:         1*L               L          Always trusted, not range checked
116/// xQ:         2*L               L          Always trusted, not range checked
117/// yQ:         3*L               L          Alawys trusted, not range checked
118/// f:          4*L               L          Always trusted, not range checked
119/// xR:         5*L               S
120/// yR:         5*L + 1*S         S
121/// s:          5*L + 2*S         S
122/// q_1:        5*L + 3*S         S
123/// q_2:        5*L + 4*S         S
124/// q_3:        5*L + 5*S         S
125/// q_2_sign:   5*L + 6*S         1
126/// q_1_sign:   5*L + 6*S + 1     1
127/// q_3_sign:   5*L + 6*S + 2     1
128/// carry_1:    5*L + 6*S + 3     2*S+2
129/// carry_2:    5*L + 8*S + 5     2*S+2
130/// carry_3:    5*L + 10*S + 7    2*S+2
131///----------------------------------------------------------------
132///
133///
134/// As we said before, all elements that are either S small limbs or 1
135/// are for range-checking. The only unusual part here is that the
136/// carries are represented in 2*S+2 limbs. Let us explain.
137///
138/// As we said, we need 2*L-2 carries, which in 6. Because our
139/// operations contain not just one limb multiplication, but several
140/// limb multiplication and extra additions, our carries will /not/
141/// fit into 75 bits. But we can prove (below) that they always fit
142/// into 79 limbs. Therefore, every large carry will be represented
143/// not by 5 15-bit chunks, but by 6 15-bit chunks. This gives us 6
144/// bits * 6 carries = 36 chunks, and every 6th chunk is 4 bits only.
145/// This matches the 2*S+2 = 36, since S = 17.
146///
147/// Note however since 79-bit carry is signed, we will store it as a list of
148/// [15 15 15 15 15 9]-bit limbs, where limbs are signed.
149/// E.g. 15-bit limbs are in [-2^14, 2^14-1]. This allows us to use
150/// 14abs range checks.
151///
152/// === Ranges
153///
154/// Carries for our three equations have the following generic range
155/// form (inclusive over integers). Note that all three equations look
156/// exactly the same for i >= n _except_ the carry from the previous
157/// limbs.
158///
159///
160/// Eq1.
161/// - i ∈ [0,n-1]:  c1_i ∈ [-((i+1)*2^(b+1) - 2*i - 3),
162///                           (i+1)*2^(b+1) - 2*i - 3] (symmetric)
163/// - i ∈ [n,2n-2]: c1_i ∈ [-((2*n-i-1)*2^(b+1) - 2*(2*n-i) + 3),
164///                           (2*n-i-1)*2^(b+1) - 2*(2*n-i) + 3] (symmetric)
165///
166/// Eq2.
167/// - i ∈ [0,n-1]:  c2_i ∈ [-((i+1)*2^(b+1) - 2*i - 4),
168///                          if i == 0 2^b else (i+1)*2^b - i]
169/// - i ∈ [n,2n-2]: c2_i ∈ [-((2*n-i-1)*2^(b+1) - 2*(2*n-i) + 3),
170///                           (2*n-i-1)*2^(b+1) - 2*(2*n-i) + 3 - (if i == n { n-1 } else 0) ]
171///
172/// Eq3.
173/// - i ∈ [0,n-1]:  c3_i ∈ [-((i+1)*2^(b+1) - 2*i - 4),
174///                           (i+1)*2^b - i - 1]
175/// - i ∈ [n,2n-2]: c3_i ∈ [-((2*n-i-1)*2^(b+1) - 2*(2*n-i) + 3),
176///                           (2*n-i-1)*2^(b+1) - 2*(2*n-i) + 3 - (if i == n { n-1 } else 0) ]
177///
178/// Absolute maximum values for all carries:
179/// Eq1.
180/// * Upper bound = -lower bound is achieved at i = n-1, n*2^(b+1) - 2*(n-1) - 3
181///   * (+-) 302231454903657293676535
182///
183/// Eq2 and Eq3:
184/// * Upper bound is achieved at i = n, (n-1)*2^(b+1) - 2*n + 3 - n -1
185///   * 226673591177742970257400
186/// * Lower bound is achieved at i = n-1, n*2^(b+1) - 2*(n-1) - 4
187///   * (-) 302231454903657293676534
188///
189/// As we can see, the values are about 2*n=8 times bigger than 2^b,
190/// so concretely 4 extra bits per carry will be enough. This implies
191/// that we can /definitely/ fit a large carry into 6 small limbs,
192/// since it has 15 "free" bits of which we will use 4 at most.
193///
194/// @volhovm: Soundness-wise I am not convinced that we need to
195/// enforce these more precise ranges as compared to enforcing just 4
196/// bit more for the highest limb. Even checking that highest limb is
197/// 15 bits could be quite sound.
198pub fn constrain_ec_addition<
199    F: PrimeField,
200    Ff: PrimeField,
201    Env: ColAccessCap<F, FECColumn> + LookupCap<F, FECColumn, LookupTable<Ff>>,
202>(
203    env: &mut Env,
204) {
205    let xp_limbs_large: [_; N_LIMBS_LARGE] =
206        read_column_array(env, |i| FECColumn::Input(FECColumnInput::XP(i)));
207    let yp_limbs_large: [_; N_LIMBS_LARGE] =
208        read_column_array(env, |i| FECColumn::Input(FECColumnInput::YP(i)));
209    let xq_limbs_large: [_; N_LIMBS_LARGE] =
210        read_column_array(env, |i| FECColumn::Input(FECColumnInput::XQ(i)));
211    let yq_limbs_large: [_; N_LIMBS_LARGE] =
212        read_column_array(env, |i| FECColumn::Input(FECColumnInput::YQ(i)));
213    let f_limbs_large: [_; N_LIMBS_LARGE] =
214        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::F(i)));
215    let xr_limbs_small: [_; N_LIMBS_SMALL] =
216        read_column_array(env, |i| FECColumn::Output(FECColumnOutput::XR(i)));
217    let yr_limbs_small: [_; N_LIMBS_SMALL] =
218        read_column_array(env, |i| FECColumn::Output(FECColumnOutput::YR(i)));
219    let s_limbs_small: [_; N_LIMBS_SMALL] =
220        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::S(i)));
221
222    let q1_limbs_small: [_; N_LIMBS_SMALL] =
223        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Q1(i)));
224    let q2_limbs_small: [_; N_LIMBS_SMALL] =
225        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Q2(i)));
226    let q3_limbs_small: [_; N_LIMBS_SMALL] =
227        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Q3(i)));
228    let q1_limbs_large: [_; N_LIMBS_LARGE] =
229        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Q1L(i)));
230    let q2_limbs_large: [_; N_LIMBS_LARGE] =
231        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Q2L(i)));
232    let q3_limbs_large: [_; N_LIMBS_LARGE] =
233        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Q3L(i)));
234
235    let q1_sign = env.read_column(FECColumn::Inter(FECColumnInter::Q1Sign));
236    let q2_sign = env.read_column(FECColumn::Inter(FECColumnInter::Q2Sign));
237    let q3_sign = env.read_column(FECColumn::Inter(FECColumnInter::Q3Sign));
238
239    let carry1_limbs_small: [_; 2 * N_LIMBS_SMALL + 2] =
240        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Carry1(i)));
241    let carry2_limbs_small: [_; 2 * N_LIMBS_SMALL + 2] =
242        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Carry2(i)));
243    let carry3_limbs_small: [_; 2 * N_LIMBS_SMALL + 2] =
244        read_column_array(env, |i| FECColumn::Inter(FECColumnInter::Carry3(i)));
245
246    // FIXME get rid of cloning
247
248    // u128 covers our limb sizes shifts which is good
249    let constant_u128 = |x: u128| -> Env::Variable { Env::constant(From::from(x)) };
250
251    // Slope and result variables must be in the field.
252    for (i, x) in s_limbs_small
253        .iter()
254        .chain(xr_limbs_small.iter())
255        .chain(yr_limbs_small.iter())
256        .enumerate()
257    {
258        if i % N_LIMBS_SMALL == N_LIMBS_SMALL - 1 {
259            // If it's the highest limb, we need to check that it's representing a field element.
260            env.lookup(
261                LookupTable::RangeCheckFfHighest(PhantomData),
262                vec![x.clone()],
263            );
264        } else {
265            env.lookup(LookupTable::RangeCheck15, vec![x.clone()]);
266        }
267    }
268
269    // Quotient limbs must fit into 15 bits, but we don't care if they're in the field.
270    for x in q1_limbs_small
271        .iter()
272        .chain(q2_limbs_small.iter())
273        .chain(q3_limbs_small.iter())
274    {
275        env.lookup(LookupTable::RangeCheck15, vec![x.clone()]);
276    }
277
278    // Signs must be -1 or 1.
279    for x in [&q1_sign, &q2_sign, &q3_sign] {
280        env.assert_zero(x.clone() * x.clone() - Env::constant(F::one()));
281    }
282
283    // Carry limbs need to be in particular ranges.
284    for (i, x) in carry1_limbs_small
285        .iter()
286        .chain(carry2_limbs_small.iter())
287        .chain(carry3_limbs_small.iter())
288        .enumerate()
289    {
290        if i % 6 == 5 {
291            // This should be a different range check depending on which big-limb we're processing?
292            // So instead of one type of lookup we will have 5 different ones?
293            env.lookup(LookupTable::RangeCheck9Abs, vec![x.clone()]);
294        } else {
295            env.lookup(LookupTable::RangeCheck14Abs, vec![x.clone()]);
296        }
297    }
298
299    // Make sure qi_limbs_large are properly constructed from qi_limbs_small and qi_sign
300    {
301        let q1_limbs_large_abs_expected =
302            combine_small_to_large::<_, _, Env>(q1_limbs_small.clone());
303        for j in 0..N_LIMBS_LARGE {
304            env.assert_zero(
305                q1_limbs_large[j].clone()
306                    - q1_sign.clone() * q1_limbs_large_abs_expected[j].clone(),
307            );
308        }
309        let q2_limbs_large_abs_expected =
310            combine_small_to_large::<_, _, Env>(q2_limbs_small.clone());
311        for j in 0..N_LIMBS_LARGE {
312            env.assert_zero(
313                q2_limbs_large[j].clone()
314                    - q2_sign.clone() * q2_limbs_large_abs_expected[j].clone(),
315            );
316        }
317        let q3_limbs_large_abs_expected =
318            combine_small_to_large::<_, _, Env>(q3_limbs_small.clone());
319        for j in 0..N_LIMBS_LARGE {
320            env.assert_zero(
321                q3_limbs_large[j].clone()
322                    - q3_sign.clone() * q3_limbs_large_abs_expected[j].clone(),
323            );
324        }
325    }
326
327    let xr_limbs_large = combine_small_to_large::<_, _, Env>(xr_limbs_small.clone());
328    let yr_limbs_large = combine_small_to_large::<_, _, Env>(yr_limbs_small.clone());
329    let s_limbs_large = combine_small_to_large::<_, _, Env>(s_limbs_small.clone());
330
331    let carry1_limbs_large: [_; 2 * N_LIMBS_LARGE - 2] =
332        combine_carry::<F, _, Env>(carry1_limbs_small.clone());
333    let carry2_limbs_large: [_; 2 * N_LIMBS_LARGE - 2] =
334        combine_carry::<F, _, Env>(carry2_limbs_small.clone());
335    let carry3_limbs_large: [_; 2 * N_LIMBS_LARGE - 2] =
336        combine_carry::<F, _, Env>(carry3_limbs_small.clone());
337
338    let limb_size_large = constant_u128(1u128 << LIMB_BITSIZE_LARGE);
339    let add_extra_carries =
340        |i: usize, carry_limbs_large: &[Env::Variable; 2 * N_LIMBS_LARGE - 2]| -> Env::Variable {
341            if i == 0 {
342                -(carry_limbs_large[0].clone() * limb_size_large.clone())
343            } else if i < 2 * N_LIMBS_LARGE - 2 {
344                carry_limbs_large[i - 1].clone()
345                    - carry_limbs_large[i].clone() * limb_size_large.clone()
346            } else if i == 2 * N_LIMBS_LARGE - 2 {
347                carry_limbs_large[i - 1].clone()
348            } else {
349                panic!("add_extra_carries: the index {i:?} is too high")
350            }
351        };
352
353    // Equation 1
354    // General form:
355    // \sum_{k,j | k+j = i} s_j (xP_k - xQ_k) - (yP_i - yQ_i) - \sum_{k,j} q_1_k f_j - c_i * 2^B + c_{i-1} =  0
356    for i in 0..2 * N_LIMBS_LARGE - 1 {
357        let mut constraint1 = fold_choice2(N_LIMBS_LARGE, i, |j, k| {
358            s_limbs_large[j].clone() * (xp_limbs_large[k].clone() - xq_limbs_large[k].clone())
359        });
360        if i < N_LIMBS_LARGE {
361            constraint1 = constraint1 - (yp_limbs_large[i].clone() - yq_limbs_large[i].clone());
362        }
363        constraint1 = constraint1
364            - fold_choice2(N_LIMBS_LARGE, i, |j, k| {
365                q1_limbs_large[j].clone() * f_limbs_large[k].clone()
366            });
367        constraint1 = constraint1 + add_extra_carries(i, &carry1_limbs_large);
368        env.assert_zero(constraint1);
369    }
370
371    // Equation 2
372    // General form: xR_i - \sum s_j s_k + xP_i + xQ_i - \sum q_2_j f_k - c_i * 2^B + c_{i-1} =  0
373    for i in 0..2 * N_LIMBS_LARGE - 1 {
374        let mut constraint2 = -fold_choice2(N_LIMBS_LARGE, i, |j, k| {
375            s_limbs_large[j].clone() * s_limbs_large[k].clone()
376        });
377        if i < N_LIMBS_LARGE {
378            constraint2 = constraint2
379                + xr_limbs_large[i].clone()
380                + xp_limbs_large[i].clone()
381                + xq_limbs_large[i].clone();
382        }
383        constraint2 = constraint2
384            - fold_choice2(N_LIMBS_LARGE, i, |j, k| {
385                q2_limbs_large[j].clone() * f_limbs_large[k].clone()
386            });
387        constraint2 = constraint2 + add_extra_carries(i, &carry2_limbs_large);
388        env.assert_zero(constraint2);
389    }
390
391    // Equation 3
392    // General form: yR_i + yP_i - \sum s_j (xP_k - xR_k) - \sum q_3_j f_k - c_i * 2^B + c_{i-1} = 0
393    for i in 0..2 * N_LIMBS_LARGE - 1 {
394        let mut constraint3 = -fold_choice2(N_LIMBS_LARGE, i, |j, k| {
395            s_limbs_large[j].clone() * (xp_limbs_large[k].clone() - xr_limbs_large[k].clone())
396        });
397        if i < N_LIMBS_LARGE {
398            constraint3 = constraint3 + yr_limbs_large[i].clone() + yp_limbs_large[i].clone();
399        }
400        constraint3 = constraint3
401            - fold_choice2(N_LIMBS_LARGE, i, |j, k| {
402                q3_limbs_large[j].clone() * f_limbs_large[k].clone()
403            });
404        constraint3 = constraint3 + add_extra_carries(i, &carry3_limbs_large);
405        env.assert_zero(constraint3)
406    }
407}
408
409/// Creates a witness for adding two points, p and q, each represented
410/// as a pair of foreign field elements. Returns a point.
411///
412/// This function is witness-generation counterpart (called by the prover) of
413/// `constrain_ec_addition` -- see the documentation of the latter.
414pub fn ec_add_circuit<
415    F: PrimeField,
416    Ff: PrimeField,
417    Env: ColWriteCap<F, FECColumn> + LookupCap<F, FECColumn, LookupTable<Ff>>,
418>(
419    env: &mut Env,
420    xp: Ff,
421    yp: Ff,
422    xq: Ff,
423    yq: Ff,
424) -> (Ff, Ff) {
425    let slope: Ff = (yq - yp) / (xq - xp);
426    let xr: Ff = slope * slope - xp - xq;
427    let yr: Ff = slope * (xp - xr) - yp;
428
429    let two_bi: BigInt = BigInt::from(2);
430
431    let large_limb_size: F = From::from(1u128 << LIMB_BITSIZE_LARGE);
432
433    // Foreign field modulus
434    let f_bui: BigUint = TryFrom::try_from(Ff::MODULUS).unwrap();
435    let f_bi: BigInt = f_bui.to_bigint().unwrap();
436
437    // Native field modulus (prime)
438    let n_bui: BigUint = TryFrom::try_from(F::MODULUS).unwrap();
439    let n_bi: BigInt = n_bui.to_bigint().unwrap();
440    let n_half_bi = &n_bi / &two_bi;
441
442    let xp_limbs_large: [F; N_LIMBS_LARGE] =
443        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(&xp);
444    let yp_limbs_large: [F; N_LIMBS_LARGE] =
445        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(&yp);
446    let xq_limbs_large: [F; N_LIMBS_LARGE] =
447        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(&xq);
448    let yq_limbs_large: [F; N_LIMBS_LARGE] =
449        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(&yq);
450    let f_limbs_large: [F; N_LIMBS_LARGE] =
451        limb_decompose_biguint::<F, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(f_bui.clone());
452    let xr_limbs_large: [F; N_LIMBS_LARGE] =
453        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(&xr);
454    let yr_limbs_large: [F; N_LIMBS_LARGE] =
455        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(&yr);
456
457    let xr_limbs_small: [F; N_LIMBS_SMALL] =
458        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_SMALL, N_LIMBS_SMALL>(&xr);
459    let yr_limbs_small: [F; N_LIMBS_SMALL] =
460        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_SMALL, N_LIMBS_SMALL>(&yr);
461    let slope_limbs_small: [F; N_LIMBS_SMALL] =
462        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_SMALL, N_LIMBS_SMALL>(&slope);
463    let slope_limbs_large: [F; N_LIMBS_LARGE] =
464        limb_decompose_ff::<F, Ff, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(&slope);
465
466    write_column_array_const(env, &xp_limbs_large, |i| {
467        FECColumn::Input(FECColumnInput::XP(i))
468    });
469    write_column_array_const(env, &yp_limbs_large, |i| {
470        FECColumn::Input(FECColumnInput::YP(i))
471    });
472    write_column_array_const(env, &xq_limbs_large, |i| {
473        FECColumn::Input(FECColumnInput::XQ(i))
474    });
475    write_column_array_const(env, &yq_limbs_large, |i| {
476        FECColumn::Input(FECColumnInput::YQ(i))
477    });
478    write_column_array_const(env, &f_limbs_large, |i| {
479        FECColumn::Inter(FECColumnInter::F(i))
480    });
481    write_column_array_const(env, &xr_limbs_small, |i| {
482        FECColumn::Output(FECColumnOutput::XR(i))
483    });
484    write_column_array_const(env, &yr_limbs_small, |i| {
485        FECColumn::Output(FECColumnOutput::YR(i))
486    });
487    write_column_array_const(env, &slope_limbs_small, |i| {
488        FECColumn::Inter(FECColumnInter::S(i))
489    });
490
491    let xp_bi: BigInt = FieldHelpers::to_bigint_positive(&xp);
492    let yp_bi: BigInt = FieldHelpers::to_bigint_positive(&yp);
493    let xq_bi: BigInt = FieldHelpers::to_bigint_positive(&xq);
494    let yq_bi: BigInt = FieldHelpers::to_bigint_positive(&yq);
495    let slope_bi: BigInt = FieldHelpers::to_bigint_positive(&slope);
496    let xr_bi: BigInt = FieldHelpers::to_bigint_positive(&xr);
497    let yr_bi: BigInt = FieldHelpers::to_bigint_positive(&yr);
498
499    // Equation 1: s (xP - xQ) - (yP - yQ) - q_1 f =  0
500    let (q1_bi, r1_bi) = (&slope_bi * (&xp_bi - &xq_bi) - (&yp_bi - &yq_bi)).div_rem(&f_bi);
501    assert!(r1_bi.is_zero());
502    // Storing negative numbers is a mess.
503    let (q1_bi, q1_sign): (BigInt, F) = if q1_bi < BigInt::zero() {
504        (-q1_bi, -F::one())
505    } else {
506        (q1_bi, F::one())
507    };
508
509    // Equation 2: xR - s^2 + xP + xQ - q_2 f = 0
510    let (q2_bi, r2_bi) = (&xr_bi - &slope_bi * &slope_bi + &xp_bi + &xq_bi).div_rem(&f_bi);
511    assert!(r2_bi.is_zero());
512    let (q2_bi, q2_sign): (BigInt, F) = if q2_bi < BigInt::zero() {
513        (-q2_bi, -F::one())
514    } else {
515        (q2_bi, F::one())
516    };
517
518    // Equation 3: yR + yP - s (xP - xR) - q_3 f = 0
519    let (q3_bi, r3_bi) = (&yr_bi + &yp_bi - &slope_bi * (&xp_bi - &xr_bi)).div_rem(&f_bi);
520    assert!(r3_bi.is_zero());
521    let (q3_bi, q3_sign): (BigInt, F) = if q3_bi < BigInt::zero() {
522        (-q3_bi, -F::one())
523    } else {
524        (q3_bi, F::one())
525    };
526
527    // TODO can this be better?
528    // Used for witness computation
529    // Big limbs /have/ sign in them.
530    let q1_limbs_large: [F; N_LIMBS_LARGE] =
531        limb_decompose_biguint::<F, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(q1_bi.to_biguint().unwrap())
532            .into_iter()
533            .map(|v| v * q1_sign)
534            .collect::<Vec<_>>()
535            .try_into()
536            .unwrap();
537    let q2_limbs_large: [F; N_LIMBS_LARGE] =
538        limb_decompose_biguint::<F, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(q2_bi.to_biguint().unwrap())
539            .into_iter()
540            .map(|v| v * q2_sign)
541            .collect::<Vec<_>>()
542            .try_into()
543            .unwrap();
544    let q3_limbs_large: [F; N_LIMBS_LARGE] =
545        limb_decompose_biguint::<F, LIMB_BITSIZE_LARGE, N_LIMBS_LARGE>(q3_bi.to_biguint().unwrap())
546            .into_iter()
547            .map(|v| v * q3_sign)
548            .collect::<Vec<_>>()
549            .try_into()
550            .unwrap();
551
552    // Written into the columns
553    // small limbs are signless 15-bit
554    let q1_limbs_small: [F; N_LIMBS_SMALL] =
555        limb_decompose_biguint::<F, LIMB_BITSIZE_SMALL, N_LIMBS_SMALL>(q1_bi.to_biguint().unwrap());
556    let q2_limbs_small: [F; N_LIMBS_SMALL] =
557        limb_decompose_biguint::<F, LIMB_BITSIZE_SMALL, N_LIMBS_SMALL>(q2_bi.to_biguint().unwrap());
558    let q3_limbs_small: [F; N_LIMBS_SMALL] =
559        limb_decompose_biguint::<F, LIMB_BITSIZE_SMALL, N_LIMBS_SMALL>(q3_bi.to_biguint().unwrap());
560
561    write_column_array_const(env, &q1_limbs_small, |i| {
562        FECColumn::Inter(FECColumnInter::Q1(i))
563    });
564    write_column_array_const(env, &q2_limbs_small, |i| {
565        FECColumn::Inter(FECColumnInter::Q2(i))
566    });
567    write_column_array_const(env, &q3_limbs_small, |i| {
568        FECColumn::Inter(FECColumnInter::Q3(i))
569    });
570
571    write_column_const(env, FECColumn::Inter(FECColumnInter::Q1Sign), &q1_sign);
572    write_column_const(env, FECColumn::Inter(FECColumnInter::Q2Sign), &q2_sign);
573    write_column_const(env, FECColumn::Inter(FECColumnInter::Q3Sign), &q3_sign);
574
575    write_column_array_const(env, &q1_limbs_large, |i| {
576        FECColumn::Inter(FECColumnInter::Q1L(i))
577    });
578    write_column_array_const(env, &q2_limbs_large, |i| {
579        FECColumn::Inter(FECColumnInter::Q2L(i))
580    });
581    write_column_array_const(env, &q3_limbs_large, |i| {
582        FECColumn::Inter(FECColumnInter::Q3L(i))
583    });
584
585    let mut carry1: F = From::from(0u64);
586    let mut carry2: F = From::from(0u64);
587    let mut carry3: F = From::from(0u64);
588
589    for i in 0..N_LIMBS_LARGE * 2 - 1 {
590        let compute_carry = |res: F| -> F {
591            // TODO enforce this as an integer division
592            let mut res_bi = res.to_bigint_positive();
593            if res_bi > n_half_bi {
594                res_bi -= &n_bi;
595            }
596            let (div, rem) = res_bi.div_rem(&large_limb_size.to_bigint_positive());
597            assert!(
598                rem.is_zero(),
599                "Cannot compute carry for step {i:?}: div {div:?}, rem {rem:?}"
600            );
601            let carry_f: BigUint = bigint_to_biguint_f(div, &n_bi);
602            F::from_biguint(&carry_f).unwrap()
603        };
604
605        fn assign_carry<F, Env, ColMap>(
606            env: &mut Env,
607            n_half_bi: &BigInt,
608            i: usize,
609            newcarry: F,
610            carryvar: &mut F,
611            column_mapper: ColMap,
612        ) where
613            F: PrimeField,
614            Env: ColWriteCap<F, FECColumn>,
615            ColMap: Fn(usize) -> FECColumn,
616        {
617            // Last carry should be zero, otherwise we record it
618            if i < N_LIMBS_LARGE * 2 - 2 {
619                // Carries will often not fit into 5 limbs, but they /should/ fit in 6 limbs I think.
620                let newcarry_sign = if &newcarry.to_bigint_positive() > n_half_bi {
621                    F::zero() - F::one()
622                } else {
623                    F::one()
624                };
625                let newcarry_abs_bui = (newcarry * newcarry_sign).to_biguint();
626                // Our big carries are at most 79 bits, so we need 6 small limbs per each.
627                // But limbs are signed, so we split into 14-bit /signed/ limbs. + last chunk is signed 9 bit.
628                let newcarry_limbs: [F; 6] =
629                    limb_decompose_biguint::<F, { LIMB_BITSIZE_SMALL - 1 }, 6>(
630                        newcarry_abs_bui.clone(),
631                    );
632
633                for (j, limb) in newcarry_limbs.iter().enumerate() {
634                    write_column_const(env, column_mapper(6 * i + j), &(newcarry_sign * limb));
635                }
636
637                *carryvar = newcarry;
638            } else {
639                // should this be in circiut?
640                assert!(newcarry.is_zero(), "Last carry is non-zero");
641            }
642        }
643
644        // Equation 1: s (xP - xQ) - (yP - yQ) - q_1 f =  0
645        let mut res1 = fold_choice2(N_LIMBS_LARGE, i, |j, k| {
646            slope_limbs_large[j] * (xp_limbs_large[k] - xq_limbs_large[k])
647        });
648        if i < N_LIMBS_LARGE {
649            res1 -= yp_limbs_large[i] - yq_limbs_large[i];
650        }
651        res1 -= fold_choice2(N_LIMBS_LARGE, i, |j, k| {
652            q1_limbs_large[j] * f_limbs_large[k]
653        });
654        res1 += carry1;
655        let newcarry1 = compute_carry(res1);
656        assign_carry(env, &n_half_bi, i, newcarry1, &mut carry1, |i| {
657            FECColumn::Inter(FECColumnInter::Carry1(i))
658        });
659
660        // Equation 2: xR - s^2 + xP + xQ - q_2 f = 0
661        let mut res2 = F::zero();
662        res2 -= fold_choice2(N_LIMBS_LARGE, i, |j, k| {
663            slope_limbs_large[j] * slope_limbs_large[k]
664        });
665        if i < N_LIMBS_LARGE {
666            res2 += xr_limbs_large[i] + xp_limbs_large[i] + xq_limbs_large[i];
667        }
668        res2 -= fold_choice2(N_LIMBS_LARGE, i, |j, k| {
669            q2_limbs_large[j] * f_limbs_large[k]
670        });
671        res2 += carry2;
672        let newcarry2 = compute_carry(res2);
673        assign_carry(env, &n_half_bi, i, newcarry2, &mut carry2, |i| {
674            FECColumn::Inter(FECColumnInter::Carry2(i))
675        });
676
677        // Equation 3: yR + yP - s (xP - xR) - q_3 f = 0
678        let mut res3 = F::zero();
679        res3 -= fold_choice2(N_LIMBS_LARGE, i, |j, k| {
680            slope_limbs_large[j] * (xp_limbs_large[k] - xr_limbs_large[k])
681        });
682        if i < N_LIMBS_LARGE {
683            res3 += yr_limbs_large[i] + yp_limbs_large[i];
684        }
685        res3 -= fold_choice2(N_LIMBS_LARGE, i, |j, k| {
686            q3_limbs_large[j] * f_limbs_large[k]
687        });
688        res3 += carry3;
689        let newcarry3 = compute_carry(res3);
690        assign_carry(env, &n_half_bi, i, newcarry3, &mut carry3, |i| {
691            FECColumn::Inter(FECColumnInter::Carry3(i))
692        });
693    }
694
695    constrain_ec_addition::<F, Ff, Env>(env);
696
697    (xr, yr)
698}