Skip to content

Commit cd70ab9

Browse files
committed
refactor: reduce duplication in Boolean::xor
Signed-off-by: ljedrz <ljedrz@users.noreply.github.com>
1 parent 5ac9d67 commit cd70ab9

File tree

1 file changed

+48
-228
lines changed
  • circuit/types/boolean/src

1 file changed

+48
-228
lines changed

circuit/types/boolean/src/xor.rs

Lines changed: 48 additions & 228 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@
1515

1616
use super::*;
1717

18+
fn boolean_witness<E: Environment>(first: &Boolean<E>, second: &Boolean<E>) -> Boolean<E> {
19+
Boolean(
20+
E::new_variable(Mode::Private, match first.eject_value() ^ second.eject_value() {
21+
true => E::BaseField::one(),
22+
false => E::BaseField::zero(),
23+
})
24+
.into(),
25+
)
26+
}
27+
1828
impl<E: Environment> BitXor<Boolean<E>> for Boolean<E> {
1929
type Output = Boolean<E>;
2030

@@ -39,13 +49,7 @@ impl<E: Environment> BitXor<Boolean<E>> for Boolean<E> {
3949
// Declare a new variable with the expected output as witness.
4050
// Note: The constraint below will ensure `output` is either 0 or 1,
4151
// assuming `self` and `other` are well-formed (they are either 0 or 1).
42-
let output = Boolean(
43-
E::new_variable(Mode::Private, match self.eject_value() ^ other.eject_value() {
44-
true => E::BaseField::one(),
45-
false => E::BaseField::zero(),
46-
})
47-
.into(),
48-
);
52+
let output = boolean_witness(&self, &other);
4953

5054
//
5155
// Ensure (`self` + `self`) * (`other`) = (`self` + `other` - `output`)
@@ -77,55 +81,19 @@ impl<E: Environment> BitXor<Boolean<E>> for Boolean<E> {
7781
impl<E: Environment> BitXor<Boolean<E>> for &Boolean<E> {
7882
type Output = Boolean<E>;
7983

80-
/// Returns `(self != other)`.
8184
fn bitxor(self, other: Boolean<E>) -> Self::Output {
82-
// Constant `self`
8385
if self.is_constant() {
8486
match self.eject_value() {
8587
true => !other,
8688
false => other,
8789
}
88-
}
89-
// Constant `other`
90-
else if other.is_constant() {
90+
} else if other.is_constant() {
9191
match other.eject_value() {
9292
true => !self.clone(),
9393
false => self.clone(),
9494
}
95-
}
96-
// Variable != Variable
97-
else {
98-
// Declare a new variable with the expected output as witness.
99-
// Note: The constraint below will ensure `output` is either 0 or 1,
100-
// assuming `self` and `other` are well-formed (they are either 0 or 1).
101-
let output = Boolean(
102-
E::new_variable(Mode::Private, match self.eject_value() ^ other.eject_value() {
103-
true => E::BaseField::one(),
104-
false => E::BaseField::zero(),
105-
})
106-
.into(),
107-
);
108-
109-
//
110-
// Ensure (`self` + `self`) * (`other`) = (`self` + `other` - `output`)
111-
// `output` is `1` iff `self` != `other`.
112-
//
113-
// As `self` and `other` are enforced to be `Boolean` types,
114-
// if they are equal, then the `output` is 0,
115-
// and if they are different, then `output` must be 1.
116-
//
117-
// ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c
118-
//
119-
// (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c
120-
// (1 - ab) * (1 - (1 - a - b + ab)) = c
121-
// (1 - ab) * (a + b - ab) = c
122-
// a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c
123-
// a + b - ab - ab - ab + ab = c
124-
// a + b - 2ab = c
125-
// -2a * b = c - a - b
126-
// 2a * b = a + b - c
127-
// (a + a) * b = a + b - c
128-
//
95+
} else {
96+
let output = boolean_witness(self, &other);
12997
E::enforce(|| ((&self.0 + &self.0), &other, (&self.0 + &other.0 - &output.0)));
13098

13199
output
@@ -136,55 +104,19 @@ impl<E: Environment> BitXor<Boolean<E>> for &Boolean<E> {
136104
impl<E: Environment> BitXor<&Boolean<E>> for Boolean<E> {
137105
type Output = Boolean<E>;
138106

139-
/// Returns `(self != other)`.
140107
fn bitxor(self, other: &Boolean<E>) -> Self::Output {
141-
// Constant `self`
142108
if self.is_constant() {
143109
match self.eject_value() {
144110
true => !other.clone(),
145111
false => other.clone(),
146112
}
147-
}
148-
// Constant `other`
149-
else if other.is_constant() {
113+
} else if other.is_constant() {
150114
match other.eject_value() {
151115
true => !self,
152116
false => self,
153117
}
154-
}
155-
// Variable != Variable
156-
else {
157-
// Declare a new variable with the expected output as witness.
158-
// Note: The constraint below will ensure `output` is either 0 or 1,
159-
// assuming `self` and `other` are well-formed (they are either 0 or 1).
160-
let output = Boolean(
161-
E::new_variable(Mode::Private, match self.eject_value() ^ other.eject_value() {
162-
true => E::BaseField::one(),
163-
false => E::BaseField::zero(),
164-
})
165-
.into(),
166-
);
167-
168-
//
169-
// Ensure (`self` + `self`) * (`other`) = (`self` + `other` - `output`)
170-
// `output` is `1` iff `self` != `other`.
171-
//
172-
// As `self` and `other` are enforced to be `Boolean` types,
173-
// if they are equal, then the `output` is 0,
174-
// and if they are different, then `output` must be 1.
175-
//
176-
// ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c
177-
//
178-
// (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c
179-
// (1 - ab) * (1 - (1 - a - b + ab)) = c
180-
// (1 - ab) * (a + b - ab) = c
181-
// a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c
182-
// a + b - ab - ab - ab + ab = c
183-
// a + b - 2ab = c
184-
// -2a * b = c - a - b
185-
// 2a * b = a + b - c
186-
// (a + a) * b = a + b - c
187-
//
118+
} else {
119+
let output = boolean_witness(&self, other);
188120
E::enforce(|| ((&self.0 + &self.0), other, (self.0 + &other.0 - &output.0)));
189121

190122
output
@@ -195,55 +127,19 @@ impl<E: Environment> BitXor<&Boolean<E>> for Boolean<E> {
195127
impl<E: Environment> BitXor<&Boolean<E>> for &Boolean<E> {
196128
type Output = Boolean<E>;
197129

198-
/// Returns `(self != other)`.
199130
fn bitxor(self, other: &Boolean<E>) -> Self::Output {
200-
// Constant `self`
201131
if self.is_constant() {
202132
match self.eject_value() {
203133
true => !other.clone(),
204134
false => other.clone(),
205135
}
206-
}
207-
// Constant `other`
208-
else if other.is_constant() {
136+
} else if other.is_constant() {
209137
match other.eject_value() {
210138
true => !self.clone(),
211139
false => self.clone(),
212140
}
213-
}
214-
// Variable != Variable
215-
else {
216-
// Declare a new variable with the expected output as witness.
217-
// Note: The constraint below will ensure `output` is either 0 or 1,
218-
// assuming `self` and `other` are well-formed (they are either 0 or 1).
219-
let output = Boolean(
220-
E::new_variable(Mode::Private, match self.eject_value() ^ other.eject_value() {
221-
true => E::BaseField::one(),
222-
false => E::BaseField::zero(),
223-
})
224-
.into(),
225-
);
226-
227-
//
228-
// Ensure (`self` + `self`) * (`other`) = (`self` + `other` - `output`)
229-
// `output` is `1` iff `self` != `other`.
230-
//
231-
// As `self` and `other` are enforced to be `Boolean` types,
232-
// if they are equal, then the `output` is 0,
233-
// and if they are different, then `output` must be 1.
234-
//
235-
// ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c
236-
//
237-
// (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c
238-
// (1 - ab) * (1 - (1 - a - b + ab)) = c
239-
// (1 - ab) * (a + b - ab) = c
240-
// a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c
241-
// a + b - ab - ab - ab + ab = c
242-
// a + b - 2ab = c
243-
// -2a * b = c - a - b
244-
// 2a * b = a + b - c
245-
// (a + a) * b = a + b - c
246-
//
141+
} else {
142+
let output = boolean_witness(self, other);
247143
E::enforce(|| ((&self.0 + &self.0), other, (&self.0 + &other.0 - &output.0)));
248144

249145
output
@@ -252,120 +148,44 @@ impl<E: Environment> BitXor<&Boolean<E>> for &Boolean<E> {
252148
}
253149

254150
impl<E: Environment> BitXorAssign<Boolean<E>> for Boolean<E> {
255-
/// Sets `self` as `(self != other)`.
256151
fn bitxor_assign(&mut self, other: Boolean<E>) {
257-
// Stores the bitwise XOR of `self` and `other` in `self`.
258-
*self =
259-
// Constant `self`
260-
if self.is_constant() {
261-
match self.eject_value() {
262-
true => !other,
263-
false => other,
264-
}
265-
}
266-
// Constant `other`
267-
else if other.is_constant() {
268-
match other.eject_value() {
269-
true => !self.clone(),
270-
false => self.clone(),
271-
}
152+
*self = if self.is_constant() {
153+
match self.eject_value() {
154+
true => !other,
155+
false => other,
272156
}
273-
// Variable != Variable
274-
else {
275-
// Declare a new variable with the expected output as witness.
276-
// Note: The constraint below will ensure `output` is either 0 or 1,
277-
// assuming `self` and `other` are well-formed (they are either 0 or 1).
278-
let output = Boolean(
279-
E::new_variable(Mode::Private, match self.eject_value() ^ other.eject_value() {
280-
true => E::BaseField::one(),
281-
false => E::BaseField::zero(),
282-
})
283-
.into(),
284-
);
285-
286-
//
287-
// Ensure (`self` + `self`) * (`other`) = (`self` + `other` - `output`)
288-
// `output` is `1` iff `self` != `other`.
289-
//
290-
// As `self` and `other` are enforced to be `Boolean` types,
291-
// if they are equal, then the `output` is 0,
292-
// and if they are different, then `output` must be 1.
293-
//
294-
// ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c
295-
//
296-
// (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c
297-
// (1 - ab) * (1 - (1 - a - b + ab)) = c
298-
// (1 - ab) * (a + b - ab) = c
299-
// a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c
300-
// a + b - ab - ab - ab + ab = c
301-
// a + b - 2ab = c
302-
// -2a * b = c - a - b
303-
// 2a * b = a + b - c
304-
// (a + a) * b = a + b - c
305-
//
306-
E::enforce(|| ((&self.0 + &self.0), &other, (&self.0 + &other.0 - &output.0)));
307-
308-
output
157+
} else if other.is_constant() {
158+
match other.eject_value() {
159+
true => !self.clone(),
160+
false => self.clone(),
309161
}
162+
} else {
163+
let output = boolean_witness(self, &other);
164+
E::enforce(|| ((&self.0 + &self.0), &other, (&self.0 + &other.0 - &output.0)));
165+
166+
output
167+
}
310168
}
311169
}
312170

313171
impl<E: Environment> BitXorAssign<&Boolean<E>> for Boolean<E> {
314-
/// Sets `self` as `(self != other)`.
315172
fn bitxor_assign(&mut self, other: &Boolean<E>) {
316-
// Stores the bitwise XOR of `self` and `other` in `self`.
317-
*self =
318-
// Constant `self`
319-
if self.is_constant() {
320-
match self.eject_value() {
321-
true => !other.clone(),
322-
false => other.clone(),
323-
}
324-
}
325-
// Constant `other`
326-
else if other.is_constant() {
327-
match other.eject_value() {
328-
true => !self.clone(),
329-
false => self.clone(),
330-
}
173+
*self = if self.is_constant() {
174+
match self.eject_value() {
175+
true => !other.clone(),
176+
false => other.clone(),
331177
}
332-
// Variable != Variable
333-
else {
334-
// Declare a new variable with the expected output as witness.
335-
// Note: The constraint below will ensure `output` is either 0 or 1,
336-
// assuming `self` and `other` are well-formed (they are either 0 or 1).
337-
let output = Boolean(
338-
E::new_variable(Mode::Private, match self.eject_value() ^ other.eject_value() {
339-
true => E::BaseField::one(),
340-
false => E::BaseField::zero(),
341-
})
342-
.into(),
343-
);
344-
345-
//
346-
// Ensure (`self` + `self`) * (`other`) = (`self` + `other` - `output`)
347-
// `output` is `1` iff `self` != `other`.
348-
//
349-
// As `self` and `other` are enforced to be `Boolean` types,
350-
// if they are equal, then the `output` is 0,
351-
// and if they are different, then `output` must be 1.
352-
//
353-
// ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c
354-
//
355-
// (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c
356-
// (1 - ab) * (1 - (1 - a - b + ab)) = c
357-
// (1 - ab) * (a + b - ab) = c
358-
// a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c
359-
// a + b - ab - ab - ab + ab = c
360-
// a + b - 2ab = c
361-
// -2a * b = c - a - b
362-
// 2a * b = a + b - c
363-
// (a + a) * b = a + b - c
364-
//
365-
E::enforce(|| ((&self.0 + &self.0), other, (&self.0 + &other.0 - &output.0)));
366-
367-
output
178+
} else if other.is_constant() {
179+
match other.eject_value() {
180+
true => !self.clone(),
181+
false => self.clone(),
368182
}
183+
} else {
184+
let output = boolean_witness(self, other);
185+
E::enforce(|| ((&self.0 + &self.0), other, (&self.0 + &other.0 - &output.0)));
186+
187+
output
188+
}
369189
}
370190
}
371191

0 commit comments

Comments
 (0)