ark_r1cs_std/select.rs
1use crate::prelude::*;
2use ark_ff::Field;
3use ark_relations::r1cs::SynthesisError;
4use ark_std::vec::Vec;
5/// Generates constraints for selecting between one of two values.
6pub trait CondSelectGadget<ConstraintF: Field>: Sized + Clone {
7 /// If `cond == &Boolean::TRUE`, then this returns `true_value`; else,
8 /// returns `false_value`.
9 ///
10 /// # Note
11 /// `Self::conditionally_select(cond, true_value, false_value)?` can be more
12 /// succinctly written as `cond.select(&true_value, &false_value)?`.
13 fn conditionally_select(
14 cond: &Boolean<ConstraintF>,
15 true_value: &Self,
16 false_value: &Self,
17 ) -> Result<Self, SynthesisError>;
18
19 /// Returns an element of `values` whose index in represented by `position`.
20 /// `position` is an array of boolean that represents an unsigned integer in
21 /// big endian order.
22 ///
23 /// # Example
24 /// To get the 6th element of `values`, convert unsigned integer 6 (`0b110`)
25 /// to `position = [True, True, False]`,
26 /// and call `conditionally_select_power_of_two_vector(position, values)`.
27 fn conditionally_select_power_of_two_vector(
28 position: &[Boolean<ConstraintF>],
29 values: &[Self],
30 ) -> Result<Self, SynthesisError> {
31 let m = values.len();
32 let n = position.len();
33
34 // Assert m is a power of 2, and n = log(m)
35 assert!(m.is_power_of_two());
36 assert_eq!(1 << n, m);
37
38 let mut cur_mux_values = values.to_vec();
39
40 // Traverse the evaluation tree from bottom to top in level order traversal.
41 // This is method 5.1 from https://github.com/mir-protocol/r1cs-workshop/blob/master/workshop.pdf
42 // TODO: Add method 5.2/5.3
43 for i in 0..n {
44 // Size of current layer.
45 let cur_size = 1 << (n - i);
46 assert_eq!(cur_mux_values.len(), cur_size);
47
48 let mut next_mux_values = Vec::new();
49 for j in (0..cur_size).step_by(2) {
50 let cur = Self::conditionally_select(
51 &position[n - 1 - i],
52 // true case
53 &cur_mux_values[j + 1],
54 // false case
55 &cur_mux_values[j],
56 )?;
57 next_mux_values.push(cur);
58 }
59 cur_mux_values = next_mux_values;
60 }
61
62 Ok(cur_mux_values[0].clone())
63 }
64}
65
66/// Performs a lookup in a 4-element table using two bits.
67pub trait TwoBitLookupGadget<ConstraintF: Field>: Sized {
68 /// The type of values being looked up.
69 type TableConstant;
70
71 /// Interprets the slice `bits` as a two-bit integer `b = bits[0] + (bits[1]
72 /// << 1)`, and then outputs `constants[b]`.
73 ///
74 /// For example, if `bits == [0, 1]`, and `constants == [0, 1, 2, 3]`, this
75 /// method should output a variable corresponding to `2`.
76 ///
77 /// # Panics
78 ///
79 /// This method panics if `bits.len() != 2` or `constants.len() != 4`.
80 fn two_bit_lookup(
81 bits: &[Boolean<ConstraintF>],
82 constants: &[Self::TableConstant],
83 ) -> Result<Self, SynthesisError>;
84}
85
86/// Uses three bits to perform a lookup into a table, where the last bit
87/// conditionally negates the looked-up value.
88pub trait ThreeBitCondNegLookupGadget<ConstraintF: Field>: Sized {
89 /// The type of values being looked up.
90 type TableConstant;
91
92 /// Interprets the slice `bits` as a two-bit integer `b = bits[0] + (bits[1]
93 /// << 1)`, and then outputs `constants[b] * c`, where `c = if bits[2] {
94 /// -1 } else { 1 };`.
95 ///
96 /// That is, `bits[2]` conditionally negates the looked-up value.
97 ///
98 /// For example, if `bits == [1, 0, 1]`, and `constants == [0, 1, 2, 3]`,
99 /// this method should output a variable corresponding to `-1`.
100 ///
101 /// # Panics
102 ///
103 /// This method panics if `bits.len() != 3` or `constants.len() != 4`.
104 fn three_bit_cond_neg_lookup(
105 bits: &[Boolean<ConstraintF>],
106 b0b1: &Boolean<ConstraintF>,
107 constants: &[Self::TableConstant],
108 ) -> Result<Self, SynthesisError>;
109}