1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! This module obtains the gates of a foreign field addition circuit.

use ark_ff::PrimeField;
use num_bigint::BigUint;

use crate::circuits::{
    gate::{CircuitGate, Connect, GateType},
    polynomials::foreign_field_common::BigUintForeignFieldHelpers,
    wires::Wire,
};

use super::witness::FFOps;

impl<F: PrimeField> CircuitGate<F> {
    /// Create foreign field addition gate chain without range checks (needs to wire the range check for result bound manually)
    /// - Inputs
    ///   - starting row
    ///   - operations to perform
    ///   - modulus of the foreign field
    /// - Outputs tuple (next_row, circuit_gates) where
    ///   - next_row      - next row after this gate
    ///   - circuit_gates - vector of circuit gates comprising this gate
    ///
    /// Note that the final structure of the circuit is as follows:
    /// circuit_gates = [
    ///      {
    ///        (i) ->      -> 1 ForeignFieldAdd row
    ///      } * num times
    ///      (n)           -> 1 ForeignFieldAdd row (this is where the final result goes)
    ///      (n+1)         -> 1 Zero row for bound result
    /// ]
    ///
    /// Warning:
    /// - Wire the range check for result bound manually
    /// - Connect to public input containing the 1 value for the overflow in the final bound check
    /// - If the inputs of the addition come from public input, wire it as well
    pub fn create_chain_ffadd(
        start_row: usize,
        opcodes: &[FFOps],
        foreign_field_modulus: &BigUint,
    ) -> (usize, Vec<Self>) {
        if *foreign_field_modulus > BigUint::max_foreign_field_modulus::<F>() {
            panic!(
                "foreign_field_modulus exceeds maximum: {} > {}",
                *foreign_field_modulus,
                BigUint::max_foreign_field_modulus::<F>()
            );
        }

        let next_row = start_row;
        let foreign_field_modulus = foreign_field_modulus.to_field_limbs::<F>();
        let mut circuit_gates = vec![];
        let num = opcodes.len();
        // ---------------------------
        // Foreign field addition gates
        // ---------------------------
        // First the single-addition gates
        for (i, opcode) in opcodes.iter().enumerate() {
            let mut coeffs = foreign_field_modulus.to_vec();
            coeffs.push(opcode.sign::<F>());
            circuit_gates.append(&mut vec![CircuitGate {
                typ: GateType::ForeignFieldAdd,
                wires: Wire::for_row(next_row + i),
                coeffs,
            }]);
        }
        let mut final_coeffs = foreign_field_modulus.to_vec();
        final_coeffs.push(FFOps::Add.sign::<F>());
        // Then the final bound gate and the zero gate
        circuit_gates.append(&mut vec![
            CircuitGate {
                typ: GateType::ForeignFieldAdd,
                wires: Wire::for_row(next_row + num),
                coeffs: final_coeffs,
            },
            CircuitGate {
                typ: GateType::Zero,
                wires: Wire::for_row(next_row + num + 1),
                coeffs: vec![],
            },
        ]);
        (start_row + circuit_gates.len(), circuit_gates)
    }

    /// Create a single foreign field addition gate. This is used for example in the final bound check.
    /// - Inputs
    ///   - starting row
    ///   - operation to perform
    ///   - modulus of the foreign field
    /// - Outputs tuple (next_row, circuit_gates) where
    ///   - next_row      - next row after this gate
    ///   - circuit_gates - vector of circuit gates comprising this gate
    pub fn create_single_ffadd(
        start_row: usize,
        operation: FFOps,
        foreign_field_modulus: &BigUint,
    ) -> (usize, Vec<Self>) {
        if *foreign_field_modulus > BigUint::max_foreign_field_modulus::<F>() {
            panic!(
                "foreign_field_modulus exceeds maximum: {} > {}",
                *foreign_field_modulus,
                BigUint::max_foreign_field_modulus::<F>()
            );
        }

        let foreign_field_modulus = foreign_field_modulus.to_field_limbs::<F>();
        let mut coeffs = foreign_field_modulus.to_vec();
        coeffs.push(operation.sign::<F>());
        let circuit_gates = vec![
            CircuitGate {
                typ: GateType::ForeignFieldAdd,
                wires: Wire::for_row(start_row),
                coeffs,
            },
            CircuitGate {
                typ: GateType::Zero,
                wires: Wire::for_row(start_row + 1),
                coeffs: vec![],
            },
        ];

        (start_row + circuit_gates.len(), circuit_gates)
    }

    /// Extend a chain of foreign field addition gates. It already wires 1 value to the overflow cell.
    /// - Inputs
    ///   - gates: vector of gates to extend
    ///   - pub_row: row of the public input
    ///   - curr_row: mutable reference to the current row
    ///   - opcodes: operations to perform
    ///   - foreign_field_modulus: modulus of the foreign field
    pub fn extend_chain_ffadd(
        gates: &mut Vec<Self>,
        pub_row: usize,
        curr_row: &mut usize,
        opcodes: &[FFOps],
        foreign_field_modulus: &BigUint,
    ) {
        let (next_row, add_gates) =
            Self::create_chain_ffadd(*curr_row, opcodes, foreign_field_modulus);
        gates.extend_from_slice(&add_gates);
        *curr_row = next_row;
        // check overflow flag is one
        gates.connect_cell_pair((pub_row, 0), (*curr_row - 2, 6));
    }

    /// Extend a single foreign field addition gate followed by a zero row containing the result
    pub fn extend_single_ffadd(
        gates: &mut Vec<Self>,
        curr_row: &mut usize,
        operation: FFOps,
        foreign_field_modulus: &BigUint,
    ) {
        let (next_row, add_gates) =
            Self::create_single_ffadd(*curr_row, operation, foreign_field_modulus);
        *curr_row = next_row;
        gates.extend_from_slice(&add_gates);
    }
}