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
160
161
//! Range check gate

use ark_ff::{FftField, PrimeField};

use crate::{
    alphas::Alphas,
    circuits::{
        argument::Argument,
        berkeley_columns::E,
        expr::Cache,
        gate::{CircuitGate, Connect, GateType},
        lookup::{
            self,
            tables::{GateLookupTable, LookupTable},
        },
        wires::Wire,
    },
};

use super::circuitgates::{RangeCheck0, RangeCheck1};

pub const GATE_COUNT: usize = 2;

impl<F: PrimeField> CircuitGate<F> {
    /// Create range check gate for constraining three 88-bit values.
    ///     Inputs the starting row
    ///     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_multi_range_check(start_row: usize) -> (usize, Vec<Self>) {
        Self::create_multi_range_check_gadget(start_row, false)
    }

    /// Create range check gate for constraining compact limbs.
    ///     Inputs the starting row
    ///     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_compact_multi_range_check(start_row: usize) -> (usize, Vec<Self>) {
        Self::create_multi_range_check_gadget(start_row, true)
    }

    /// Create foreign field muti-range-check gadget by extending the existing gates
    pub fn extend_multi_range_check(gates: &mut Vec<Self>, curr_row: &mut usize) {
        let (next_row, circuit_gates) = Self::create_multi_range_check(*curr_row);
        *curr_row = next_row;
        gates.extend_from_slice(&circuit_gates);
    }

    /// Create foreign field muti-range-check gadget by extending the existing gates
    pub fn extend_compact_multi_range_check(gates: &mut Vec<Self>, curr_row: &mut usize) {
        let (next_row, circuit_gates) = Self::create_compact_multi_range_check(*curr_row);
        *curr_row = next_row;
        gates.extend_from_slice(&circuit_gates);
    }

    /// Create single range check gate
    ///     Inputs the starting row
    ///     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_range_check(start_row: usize) -> (usize, Vec<Self>) {
        let gate = CircuitGate::new(
            GateType::RangeCheck0,
            Wire::for_row(start_row),
            vec![F::zero()],
        );
        (start_row + 1, vec![gate])
    }

    /// Create foreign field range-check gate by extending the existing gates
    pub fn extend_range_check(gates: &mut Vec<Self>, curr_row: &mut usize) {
        let (next_row, circuit_gates) = Self::create_range_check(*curr_row);
        *curr_row = next_row;
        gates.extend_from_slice(&circuit_gates);
    }

    // Create range check gate for constraining three 88-bit values.
    //     Inputs the starting row and whether the limbs are in compact format
    //     Outputs tuple (`next_row`, `circuit_gates`) where
    //       `next_row`      - next row after this gate
    //       `circuit_gates` - vector of circuit gates comprising this gate
    fn create_multi_range_check_gadget(start_row: usize, compact: bool) -> (usize, Vec<Self>) {
        let coeff = if compact { F::one() } else { F::zero() };

        let mut circuit_gates = vec![
            CircuitGate::new(
                GateType::RangeCheck0,
                Wire::for_row(start_row),
                vec![F::zero()],
            ),
            CircuitGate::new(
                GateType::RangeCheck0,
                Wire::for_row(start_row + 1),
                vec![coeff],
            ),
            CircuitGate::new(GateType::RangeCheck1, Wire::for_row(start_row + 2), vec![]),
            CircuitGate::new(GateType::Zero, Wire::for_row(start_row + 3), vec![]),
        ];

        // copy v0p0
        circuit_gates.connect_cell_pair((0, 1), (3, 3));

        // copy v0p1
        circuit_gates.connect_cell_pair((0, 2), (3, 4));

        // copy v1p0
        circuit_gates.connect_cell_pair((1, 1), (3, 5));

        // copy v1p1
        circuit_gates.connect_cell_pair((1, 2), (3, 6));

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

/// Get vector of range check circuit gate types
pub fn circuit_gates() -> [GateType; GATE_COUNT] {
    [GateType::RangeCheck0, GateType::RangeCheck1]
}

/// Number of constraints for a given range check circuit gate type
///
/// # Panics
///
/// Will panic if `typ` is not `RangeCheck`-related gate type.
pub fn circuit_gate_constraint_count<F: PrimeField>(typ: GateType) -> u32 {
    match typ {
        GateType::RangeCheck0 => RangeCheck0::<F>::CONSTRAINTS,
        GateType::RangeCheck1 => RangeCheck1::<F>::CONSTRAINTS,
        _ => panic!("invalid gate type"),
    }
}

/// Get combined constraints for a given range check circuit gate type
///
/// # Panics
///
/// Will panic if `typ` is not `RangeCheck`-related gate type.
pub fn circuit_gate_constraints<F: PrimeField>(
    typ: GateType,
    alphas: &Alphas<F>,
    cache: &mut Cache,
) -> E<F> {
    match typ {
        GateType::RangeCheck0 => RangeCheck0::combined_constraints(alphas, cache),
        GateType::RangeCheck1 => RangeCheck1::combined_constraints(alphas, cache),
        _ => panic!("invalid gate type"),
    }
}

/// Get the combined constraints for all range check circuit gate types
pub fn combined_constraints<F: PrimeField>(alphas: &Alphas<F>, cache: &mut Cache) -> E<F> {
    RangeCheck0::combined_constraints(alphas, cache)
        + RangeCheck1::combined_constraints(alphas, cache)
}

/// Get the range check lookup table
pub fn lookup_table<F: FftField>() -> LookupTable<F> {
    lookup::tables::get_table::<F>(GateLookupTable::RangeCheck)
}