1use crate::{
5 FancyArithmetic, FancyBinary, check_binary, derive_binary,
6 dummy::{Dummy, DummyVal},
7 errors::{CircuitBuilderError, DummyError, FancyError},
8 fancy::{BinaryBundle, CrtBundle, Fancy, FancyInput, HasModulus},
9 informer::Informer,
10};
11use itertools::Itertools;
12use std::{collections::HashMap, fmt::Display};
13
14#[derive(Clone, Copy, Debug, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct CircuitRef {
18 pub(crate) ix: usize,
19 pub(crate) modulus: u16,
20}
21
22impl std::fmt::Display for CircuitRef {
23 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
24 write!(f, "[{} | {}]", self.ix, self.modulus)
25 }
26}
27
28impl HasModulus for CircuitRef {
29 fn modulus(&self) -> u16 {
30 self.modulus
31 }
32}
33
34#[derive(Clone, Debug, PartialEq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct ArithmeticCircuit {
38 pub(crate) gates: Vec<ArithmeticGate>,
39 pub(crate) gate_moduli: Vec<u16>,
40 pub(crate) garbler_input_refs: Vec<CircuitRef>,
41 pub(crate) evaluator_input_refs: Vec<CircuitRef>,
42 pub(crate) const_refs: Vec<CircuitRef>,
43 pub(crate) output_refs: Vec<CircuitRef>,
44 pub(crate) num_nonfree_gates: usize,
45}
46
47#[derive(Clone, Debug, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct BinaryCircuit {
51 pub(crate) gates: Vec<BinaryGate>,
52 pub(crate) garbler_input_refs: Vec<CircuitRef>,
53 pub(crate) evaluator_input_refs: Vec<CircuitRef>,
54 pub(crate) const_refs: Vec<CircuitRef>,
55 pub(crate) output_refs: Vec<CircuitRef>,
56 pub(crate) num_nonfree_gates: usize,
57}
58
59#[derive(Clone, Debug, PartialEq)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
65pub enum ArithmeticGate {
66 GarblerInput {
68 id: usize,
70 },
71 EvaluatorInput {
73 id: usize,
75 },
76 Constant {
78 val: u16,
80 },
81 Add {
83 xref: CircuitRef,
85
86 yref: CircuitRef,
88
89 out: Option<usize>,
91 },
92 Sub {
94 xref: CircuitRef,
96
97 yref: CircuitRef,
99
100 out: Option<usize>,
102 },
103 Cmul {
105 xref: CircuitRef,
107
108 c: u16,
110
111 out: Option<usize>,
113 },
114 Mul {
116 xref: CircuitRef,
118
119 yref: CircuitRef,
121
122 id: usize,
124
125 out: Option<usize>,
127 },
128 Proj {
130 xref: CircuitRef,
132
133 tt: Vec<u16>,
135
136 id: usize,
138
139 out: Option<usize>,
141 },
142}
143
144#[derive(Clone, Debug, PartialEq)]
149#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
150pub enum BinaryGate {
151 GarblerInput {
153 id: usize,
155 },
156 EvaluatorInput {
158 id: usize,
160 },
161 Constant {
163 val: u16,
165 },
166
167 Xor {
169 xref: CircuitRef,
171
172 yref: CircuitRef,
174
175 out: Option<usize>,
177 },
178 And {
180 xref: CircuitRef,
182
183 yref: CircuitRef,
185
186 id: usize,
188
189 out: Option<usize>,
191 },
192 Inv {
194 xref: CircuitRef,
196
197 out: Option<usize>,
199 },
200}
201
202impl std::fmt::Display for ArithmeticGate {
203 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
204 match self {
205 Self::GarblerInput { id } => write!(f, "GarblerInput {}", id),
206 Self::EvaluatorInput { id } => write!(f, "EvaluatorInput {}", id),
207 Self::Constant { val } => write!(f, "Constant {}", val),
208 Self::Add { xref, yref, out } => write!(f, "Add ( {}, {}, {:?} )", xref, yref, out),
209 Self::Sub { xref, yref, out } => write!(f, "Sub ( {}, {}, {:?} )", xref, yref, out),
210 Self::Cmul { xref, c, out } => write!(f, "Cmul ( {}, {}, {:?} )", xref, c, out),
211 Self::Mul {
212 xref,
213 yref,
214 id,
215 out,
216 } => write!(f, "Mul ( {}, {}, {}, {:?} )", xref, yref, id, out),
217 Self::Proj { xref, tt, id, out } => {
218 write!(f, "Proj ( {}, {:?}, {}, {:?} )", xref, tt, id, out)
219 }
220 }
221 }
222}
223
224impl std::fmt::Display for BinaryGate {
225 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
226 match self {
227 Self::GarblerInput { id } => write!(f, "GarblerInput {}", id),
228 Self::EvaluatorInput { id } => write!(f, "EvaluatorInput {}", id),
229 Self::Constant { val } => write!(f, "Constant {}", val),
230 Self::Xor { xref, yref, out } => write!(f, "Xor ( {}, {}, {:?} )", xref, yref, out),
231 Self::And {
232 xref,
233 yref,
234 id,
235 out,
236 } => write!(f, "And ( {}, {}, {}, {:?} )", xref, yref, id, out),
237 Self::Inv { xref, out } => write!(f, "Inv ( {}, {:?} )", xref, out),
238 }
239 }
240}
241
242pub trait CircuitInfo {
247 fn print_info(&self) -> Result<(), DummyError>;
249}
250
251impl<C: EvaluableCircuit<Informer<Dummy>>> CircuitInfo for C {
252 fn print_info(&self) -> Result<(), DummyError> {
253 let mut informer = crate::informer::Informer::new(Dummy::new());
254
255 let gb = self
257 .get_garbler_input_refs()
258 .iter()
259 .map(|r| informer.encode(0, r.modulus()))
260 .collect::<Result<Vec<DummyVal>, DummyError>>()?;
261 let ev = self
262 .get_evaluator_input_refs()
263 .iter()
264 .map(|r| informer.encode(0, r.modulus()))
265 .collect::<Result<Vec<DummyVal>, DummyError>>()?;
266
267 let _outputs = self.eval(&mut informer, &gb, &ev)?;
268 println!("{}", informer.stats());
269 Ok(())
270 }
271}
272
273pub trait EvaluableCircuit<F: Fancy>: CircuitType {
277 fn eval(
279 &self,
280 f: &mut F,
281 garbler_inputs: &[F::Item],
282 evaluator_inputs: &[F::Item],
283 ) -> Result<Option<Vec<u16>>, F::Error>;
284}
285
286impl<F: FancyArithmetic> EvaluableCircuit<F> for ArithmeticCircuit {
287 fn eval(
288 &self,
289 f: &mut F,
290 garbler_inputs: &[F::Item],
291 evaluator_inputs: &[F::Item],
292 ) -> Result<Option<Vec<u16>>, F::Error> {
293 let mut cache: Vec<Option<F::Item>> = vec![None; self.gates.len()];
294 for (i, gate) in self.gates.iter().enumerate() {
295 let q = self.modulus(i);
296 let (zref_, val) = match *gate {
297 ArithmeticGate::GarblerInput { id } => (None, garbler_inputs[id].clone()),
298 ArithmeticGate::EvaluatorInput { id } => {
299 assert!(
300 id < evaluator_inputs.len(),
301 "id={} ev_inps.len()={}",
302 id,
303 evaluator_inputs.len()
304 );
305 (None, evaluator_inputs[id].clone())
306 }
307 ArithmeticGate::Constant { val } => (None, f.constant(val, q)?),
308 ArithmeticGate::Add { xref, yref, out } => (
309 out,
310 f.add(
311 cache[xref.ix]
312 .as_ref()
313 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
314 cache[yref.ix]
315 .as_ref()
316 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
317 )?,
318 ),
319 ArithmeticGate::Sub { xref, yref, out } => (
320 out,
321 f.sub(
322 cache[xref.ix]
323 .as_ref()
324 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
325 cache[yref.ix]
326 .as_ref()
327 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
328 )?,
329 ),
330 ArithmeticGate::Cmul { xref, c, out } => (
331 out,
332 f.cmul(
333 cache[xref.ix]
334 .as_ref()
335 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
336 c,
337 )?,
338 ),
339 ArithmeticGate::Proj {
340 xref, ref tt, out, ..
341 } => (
342 out,
343 f.proj(
344 cache[xref.ix]
345 .as_ref()
346 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
347 q,
348 Some(tt.to_vec()),
349 )?,
350 ),
351 ArithmeticGate::Mul {
352 xref, yref, out, ..
353 } => (
354 out,
355 f.mul(
356 cache[xref.ix]
357 .as_ref()
358 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
359 cache[yref.ix]
360 .as_ref()
361 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
362 )?,
363 ),
364 };
365 cache[zref_.unwrap_or(i)] = Some(val);
366 }
367 let mut outputs = Vec::with_capacity(self.noutputs());
368 for r in self.get_output_refs().iter() {
369 let r = cache[r.ix]
370 .as_ref()
371 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?;
372 let out = f.output(r)?;
373 outputs.push(out);
374 }
375 Ok(outputs.into_iter().collect())
376 }
377}
378
379impl<F: FancyBinary> EvaluableCircuit<F> for BinaryCircuit {
380 fn eval(
381 &self,
382 f: &mut F,
383 garbler_inputs: &[F::Item],
384 evaluator_inputs: &[F::Item],
385 ) -> Result<Option<Vec<u16>>, F::Error> {
386 let mut cache: Vec<Option<F::Item>> = vec![None; self.gates.len()];
387 for (i, gate) in self.gates.iter().enumerate() {
388 let q = 2;
389 let (zref_, val) = match *gate {
390 BinaryGate::GarblerInput { id } => (None, garbler_inputs[id].clone()),
391 BinaryGate::EvaluatorInput { id } => {
392 assert!(
393 id < evaluator_inputs.len(),
394 "id={} ev_inps.len()={}",
395 id,
396 evaluator_inputs.len()
397 );
398 (None, evaluator_inputs[id].clone())
399 }
400 BinaryGate::Constant { val } => (None, f.constant(val, q)?),
401 BinaryGate::Inv { xref, out } => (
402 out,
403 f.negate(
404 cache[xref.ix]
405 .as_ref()
406 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
407 )?,
408 ),
409 BinaryGate::Xor { xref, yref, out } => (
410 out,
411 f.xor(
412 cache[xref.ix]
413 .as_ref()
414 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
415 cache[yref.ix]
416 .as_ref()
417 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
418 )?,
419 ),
420 BinaryGate::And {
421 xref, yref, out, ..
422 } => (
423 out,
424 f.and(
425 cache[xref.ix]
426 .as_ref()
427 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
428 cache[yref.ix]
429 .as_ref()
430 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?,
431 )?,
432 ),
433 };
434 cache[zref_.unwrap_or(i)] = Some(val);
435 }
436 let mut outputs = Vec::with_capacity(self.noutputs());
437 for r in self.get_output_refs().iter() {
438 let r = cache[r.ix]
439 .as_ref()
440 .ok_or_else(|| F::Error::from(FancyError::UninitializedValue))?;
441 let out = f.output(r)?;
442 outputs.push(out);
443 }
444 Ok(outputs.into_iter().collect())
445 }
446}
447
448pub trait GateType: Display {
450 fn make_constant(val: u16) -> Self;
452
453 fn make_garbler_input(id: usize) -> Self;
455
456 fn make_evaluator_input(id: usize) -> Self;
458}
459
460impl GateType for BinaryGate {
461 fn make_constant(val: u16) -> Self {
462 Self::Constant { val }
463 }
464
465 fn make_garbler_input(id: usize) -> Self {
466 Self::GarblerInput { id }
467 }
468
469 fn make_evaluator_input(id: usize) -> Self {
470 Self::EvaluatorInput { id }
471 }
472}
473
474impl GateType for ArithmeticGate {
475 fn make_constant(val: u16) -> Self {
476 Self::Constant { val }
477 }
478
479 fn make_garbler_input(id: usize) -> Self {
480 Self::GarblerInput { id }
481 }
482
483 fn make_evaluator_input(id: usize) -> Self {
484 Self::EvaluatorInput { id }
485 }
486}
487
488pub trait CircuitType: Clone {
490 type Gate: GateType;
492
493 fn increment_nonfree_gates(&mut self);
495
496 fn new(ngates: Option<usize>) -> Self;
498
499 fn get_output_refs(&self) -> &[CircuitRef];
501
502 fn get_garbler_input_refs(&self) -> &[CircuitRef];
504
505 fn get_evaluator_input_refs(&self) -> &[CircuitRef];
507
508 fn get_num_nonfree_gates(&self) -> usize;
510
511 fn push_gates(&mut self, gate: Self::Gate);
513
514 fn push_const_ref(&mut self, xref: CircuitRef);
516
517 fn push_output_ref(&mut self, xref: CircuitRef);
519
520 fn push_garbler_input_ref(&mut self, xref: CircuitRef);
522
523 fn push_evaluator_input_ref(&mut self, xref: CircuitRef);
525
526 fn push_modulus(&mut self, modulus: u16);
528
529 fn garbler_input_mod(&self, i: usize) -> u16;
531
532 fn evaluator_input_mod(&self, i: usize) -> u16;
534
535 #[inline]
537 fn num_garbler_inputs(&self) -> usize {
538 self.get_garbler_input_refs().len()
539 }
540
541 #[inline]
543 fn num_evaluator_inputs(&self) -> usize {
544 self.get_evaluator_input_refs().len()
545 }
546
547 #[inline]
549 fn noutputs(&self) -> usize {
550 self.get_output_refs().len()
551 }
552}
553
554pub fn eval_plain<C: EvaluableCircuit<Dummy>>(
556 circuit: &C,
557 garbler_inputs: &[u16],
558 evaluator_inputs: &[u16],
559) -> Result<Vec<u16>, DummyError> {
560 let mut dummy = crate::dummy::Dummy::new();
561
562 if garbler_inputs.len() != circuit.num_garbler_inputs() {
563 return Err(DummyError::NotEnoughGarblerInputs);
564 }
565
566 if evaluator_inputs.len() != circuit.num_evaluator_inputs() {
567 return Err(DummyError::NotEnoughEvaluatorInputs);
568 }
569
570 let gb = garbler_inputs
572 .iter()
573 .zip(circuit.get_garbler_input_refs().iter())
574 .map(|(x, r)| DummyVal::new(*x, r.modulus()))
575 .collect_vec();
576 let ev = evaluator_inputs
577 .iter()
578 .zip(circuit.get_evaluator_input_refs().iter())
579 .map(|(x, r)| DummyVal::new(*x, r.modulus()))
580 .collect_vec();
581
582 let outputs = circuit.eval(&mut dummy, &gb, &ev)?;
583 Ok(outputs.expect("dummy will always return Some(u16) output"))
584}
585
586impl CircuitType for BinaryCircuit {
587 type Gate = BinaryGate;
588
589 fn new(ngates: Option<usize>) -> Self {
590 let gates = Vec::with_capacity(ngates.unwrap_or(0));
591 Self {
592 gates,
593 garbler_input_refs: Vec::new(),
594 evaluator_input_refs: Vec::new(),
595 const_refs: Vec::new(),
596 output_refs: Vec::new(),
597 num_nonfree_gates: 0,
598 }
599 }
600
601 fn push_gates(&mut self, gate: Self::Gate) {
602 self.gates.push(gate)
603 }
604
605 fn push_const_ref(&mut self, xref: CircuitRef) {
606 self.const_refs.push(xref)
607 }
608
609 fn push_output_ref(&mut self, xref: CircuitRef) {
610 self.output_refs.push(xref)
611 }
612
613 fn push_garbler_input_ref(&mut self, xref: CircuitRef) {
614 self.garbler_input_refs.push(xref)
615 }
616
617 fn push_modulus(&mut self, modulus: u16) {
618 assert_eq!(modulus, 2);
619 }
620
621 fn push_evaluator_input_ref(&mut self, xref: CircuitRef) {
622 self.evaluator_input_refs.push(xref)
623 }
624
625 fn increment_nonfree_gates(&mut self) {
626 self.num_nonfree_gates += 1;
627 }
628
629 fn get_num_nonfree_gates(&self) -> usize {
630 self.num_nonfree_gates
631 }
632
633 fn get_output_refs(&self) -> &[CircuitRef] {
634 &self.output_refs
635 }
636
637 fn get_garbler_input_refs(&self) -> &[CircuitRef] {
638 &self.garbler_input_refs
639 }
640
641 fn get_evaluator_input_refs(&self) -> &[CircuitRef] {
642 &self.evaluator_input_refs
643 }
644
645 fn garbler_input_mod(&self, _: usize) -> u16 {
646 2
647 }
648
649 fn evaluator_input_mod(&self, _: usize) -> u16 {
650 2
651 }
652}
653
654impl CircuitType for ArithmeticCircuit {
655 type Gate = ArithmeticGate;
656
657 fn new(ngates: Option<usize>) -> ArithmeticCircuit {
658 let gates = Vec::with_capacity(ngates.unwrap_or(0));
659 ArithmeticCircuit {
660 gates,
661 garbler_input_refs: Vec::new(),
662 evaluator_input_refs: Vec::new(),
663 const_refs: Vec::new(),
664 output_refs: Vec::new(),
665 gate_moduli: Vec::new(),
666 num_nonfree_gates: 0,
667 }
668 }
669
670 fn push_gates(&mut self, gate: Self::Gate) {
671 self.gates.push(gate)
672 }
673
674 fn push_const_ref(&mut self, xref: CircuitRef) {
675 self.const_refs.push(xref)
676 }
677
678 fn push_output_ref(&mut self, xref: CircuitRef) {
679 self.output_refs.push(xref)
680 }
681
682 fn push_garbler_input_ref(&mut self, xref: CircuitRef) {
683 self.garbler_input_refs.push(xref)
684 }
685
686 fn push_modulus(&mut self, modulus: u16) {
687 self.gate_moduli.push(modulus)
688 }
689
690 fn push_evaluator_input_ref(&mut self, xref: CircuitRef) {
691 self.evaluator_input_refs.push(xref)
692 }
693
694 fn increment_nonfree_gates(&mut self) {
695 self.num_nonfree_gates += 1;
696 }
697
698 fn get_num_nonfree_gates(&self) -> usize {
699 self.num_nonfree_gates
700 }
701
702 fn get_output_refs(&self) -> &[CircuitRef] {
703 &self.output_refs
704 }
705
706 fn get_garbler_input_refs(&self) -> &[CircuitRef] {
707 &self.garbler_input_refs
708 }
709
710 fn get_evaluator_input_refs(&self) -> &[CircuitRef] {
711 &self.evaluator_input_refs
712 }
713
714 fn garbler_input_mod(&self, i: usize) -> u16 {
715 let r = self.garbler_input_refs[i];
716 r.modulus()
717 }
718
719 fn evaluator_input_mod(&self, i: usize) -> u16 {
720 let r = self.evaluator_input_refs[i];
721 r.modulus()
722 }
723}
724
725impl ArithmeticCircuit {
726 #[inline]
728 pub fn modulus(&self, i: usize) -> u16 {
729 self.gate_moduli[i]
730 }
731}
732
733pub struct CircuitBuilder<Circuit> {
735 next_ref_ix: usize,
736 next_garbler_input_id: usize,
737 next_evaluator_input_id: usize,
738 const_map: HashMap<(u16, u16), CircuitRef>,
739 circ: Circuit,
740}
741
742impl FancyBinary for CircuitBuilder<BinaryCircuit> {
743 fn xor(&mut self, xref: &Self::Item, yref: &Self::Item) -> Result<Self::Item, Self::Error> {
744 let gate = BinaryGate::Xor {
745 xref: *xref,
746 yref: *yref,
747 out: None,
748 };
749
750 Ok(self.gate(gate, xref.modulus()))
751 }
752
753 fn negate(&mut self, xref: &Self::Item) -> Result<Self::Item, Self::Error> {
754 let gate = BinaryGate::Inv {
755 xref: *xref,
756 out: None,
757 };
758 Ok(self.gate(gate, xref.modulus()))
759 }
760
761 fn and(&mut self, xref: &Self::Item, yref: &Self::Item) -> Result<Self::Item, Self::Error> {
762 let gate = BinaryGate::And {
763 xref: *xref,
764 yref: *yref,
765 id: self.get_next_ciphertext_id(),
766 out: None,
767 };
768
769 Ok(self.gate(gate, xref.modulus()))
770 }
771}
772
773derive_binary!(CircuitBuilder<ArithmeticCircuit>);
775
776impl FancyArithmetic for CircuitBuilder<ArithmeticCircuit> {
777 fn add(&mut self, xref: &CircuitRef, yref: &CircuitRef) -> Result<CircuitRef, Self::Error> {
778 if xref.modulus() != yref.modulus() {
779 return Err(Self::Error::from(FancyError::UnequalModuli));
780 }
781 let gate = ArithmeticGate::Add {
782 xref: *xref,
783 yref: *yref,
784 out: None,
785 };
786 Ok(self.gate(gate, xref.modulus()))
787 }
788
789 fn sub(&mut self, xref: &CircuitRef, yref: &CircuitRef) -> Result<CircuitRef, Self::Error> {
790 if xref.modulus() != yref.modulus() {
791 return Err(Self::Error::from(FancyError::UnequalModuli));
792 }
793 let gate = ArithmeticGate::Sub {
794 xref: *xref,
795 yref: *yref,
796 out: None,
797 };
798 Ok(self.gate(gate, xref.modulus()))
799 }
800
801 fn cmul(&mut self, xref: &CircuitRef, c: u16) -> Result<CircuitRef, Self::Error> {
802 Ok(self.gate(
803 ArithmeticGate::Cmul {
804 xref: *xref,
805 c,
806 out: None,
807 },
808 xref.modulus(),
809 ))
810 }
811
812 fn proj(
813 &mut self,
814 xref: &CircuitRef,
815 output_modulus: u16,
816 tt: Option<Vec<u16>>,
817 ) -> Result<CircuitRef, Self::Error> {
818 let tt = tt.ok_or_else(|| Self::Error::from(FancyError::NoTruthTable))?;
819 if tt.len() < xref.modulus() as usize || !tt.iter().all(|&x| x < output_modulus) {
820 return Err(Self::Error::from(FancyError::InvalidTruthTable));
821 }
822 let gate = ArithmeticGate::Proj {
823 xref: *xref,
824 tt: tt.to_vec(),
825 id: self.get_next_ciphertext_id(),
826 out: None,
827 };
828 Ok(self.gate(gate, output_modulus))
829 }
830
831 fn mul(&mut self, xref: &CircuitRef, yref: &CircuitRef) -> Result<CircuitRef, Self::Error> {
832 if xref.modulus() < yref.modulus() {
833 return self.mul(yref, xref);
834 }
835
836 let gate = ArithmeticGate::Mul {
837 xref: *xref,
838 yref: *yref,
839 id: self.get_next_ciphertext_id(),
840 out: None,
841 };
842
843 Ok(self.gate(gate, xref.modulus()))
844 }
845}
846
847impl<Circuit: CircuitType> Fancy for CircuitBuilder<Circuit> {
848 type Item = CircuitRef;
849 type Error = CircuitBuilderError;
850
851 fn constant(&mut self, val: u16, modulus: u16) -> Result<CircuitRef, Self::Error> {
852 match self.const_map.get(&(val, modulus)) {
853 Some(&r) => Ok(r),
854 None => {
855 let gate = Circuit::Gate::make_constant(val);
856 let r = self.gate(gate, modulus);
857 self.const_map.insert((val, modulus), r);
858 self.circ.push_const_ref(r);
859 Ok(r)
860 }
861 }
862 }
863
864 fn output(&mut self, xref: &CircuitRef) -> Result<Option<u16>, Self::Error> {
865 println!("output called");
866 self.circ.push_output_ref(*xref);
867 Ok(None)
868 }
869}
870
871impl<Circuit: CircuitType> CircuitBuilder<Circuit> {
872 pub fn new() -> Self {
874 CircuitBuilder {
875 next_ref_ix: 0,
876 next_garbler_input_id: 0,
877 next_evaluator_input_id: 0,
878 const_map: HashMap::new(),
879 circ: Circuit::new(None),
880 }
881 }
882
883 pub fn finish(self) -> Circuit {
885 self.circ
886 }
887
888 fn get_next_garbler_input_id(&mut self) -> usize {
889 let current = self.next_garbler_input_id;
890 self.next_garbler_input_id += 1;
891 current
892 }
893
894 fn get_next_evaluator_input_id(&mut self) -> usize {
895 let current = self.next_evaluator_input_id;
896 self.next_evaluator_input_id += 1;
897 current
898 }
899
900 fn get_next_ciphertext_id(&mut self) -> usize {
901 let current = self.circ.get_num_nonfree_gates();
902 self.circ.increment_nonfree_gates();
903 current
904 }
905
906 fn get_next_ref_ix(&mut self) -> usize {
907 let current = self.next_ref_ix;
908 self.next_ref_ix += 1;
909 current
910 }
911
912 fn gate(&mut self, gate: Circuit::Gate, modulus: u16) -> CircuitRef {
913 self.circ.push_gates(gate);
914 self.circ.push_modulus(modulus);
915 let ix = self.get_next_ref_ix();
916 CircuitRef { ix, modulus }
917 }
918
919 pub fn garbler_input(&mut self, modulus: u16) -> CircuitRef {
921 let id = self.get_next_garbler_input_id();
922 let r = self.gate(Circuit::Gate::make_garbler_input(id), modulus);
923 self.circ.push_garbler_input_ref(r);
924 r
925 }
926
927 pub fn evaluator_input(&mut self, modulus: u16) -> CircuitRef {
929 let id = self.get_next_evaluator_input_id();
930 let r = self.gate(Circuit::Gate::make_evaluator_input(id), modulus);
931 self.circ.push_evaluator_input_ref(r);
932 r
933 }
934
935 pub fn garbler_inputs(&mut self, mods: &[u16]) -> Vec<CircuitRef> {
937 mods.iter().map(|q| self.garbler_input(*q)).collect()
938 }
939
940 pub fn evaluator_inputs(&mut self, mods: &[u16]) -> Vec<CircuitRef> {
942 mods.iter().map(|q| self.evaluator_input(*q)).collect()
943 }
944
945 pub fn crt_garbler_input(&mut self, modulus: u128) -> CrtBundle<CircuitRef> {
947 CrtBundle::new(self.garbler_inputs(&crate::util::factor(modulus)))
948 }
949
950 pub fn crt_evaluator_input(&mut self, modulus: u128) -> CrtBundle<CircuitRef> {
952 CrtBundle::new(self.evaluator_inputs(&crate::util::factor(modulus)))
953 }
954
955 pub fn bin_garbler_input(&mut self, nbits: usize) -> BinaryBundle<CircuitRef> {
957 BinaryBundle::new(self.garbler_inputs(&vec![2; nbits]))
958 }
959
960 pub fn bin_evaluator_input(&mut self, nbits: usize) -> BinaryBundle<CircuitRef> {
962 BinaryBundle::new(self.evaluator_inputs(&vec![2; nbits]))
963 }
964}
965
966#[cfg(test)]
967mod plaintext {
968 use super::*;
969 use crate::util::RngExt;
970 use itertools::Itertools;
971 use rand::thread_rng;
972
973 #[test] fn and_gate_fan_n() {
975 let mut rng = thread_rng();
976
977 let mut b = CircuitBuilder::<BinaryCircuit>::new();
978 let n = 2 + (rng.gen_usize() % 200);
979 let inps = b.evaluator_inputs(&vec![2; n]);
980 let z = b.and_many(&inps).unwrap();
981 b.output(&z).unwrap();
982 let c = b.finish();
983
984 for _ in 0..16 {
985 let mut inps: Vec<u16> = Vec::new();
986 for _ in 0..n {
987 inps.push(rng.gen_bool() as u16);
988 }
989 let res = inps.iter().fold(1, |acc, &x| x & acc);
990 let out = eval_plain(&c, &[], &inps).unwrap()[0];
991 if out != res {
992 println!("{:?} {} {}", inps, out, res);
993 panic!("incorrect output n={}", n);
994 }
995 }
996 }
997 #[test] fn or_gate_fan_n() {
1000 let mut rng = thread_rng();
1001 let mut b: CircuitBuilder<BinaryCircuit> = CircuitBuilder::new();
1002 let n = 2 + (rng.gen_usize() % 200);
1003 let inps = b.evaluator_inputs(&vec![2; n]);
1004 let z = b.or_many(&inps).unwrap();
1005 b.output(&z).unwrap();
1006 let c = b.finish();
1007
1008 for _ in 0..16 {
1009 let mut inps: Vec<u16> = Vec::new();
1010 for _ in 0..n {
1011 inps.push(rng.gen_bool() as u16);
1012 }
1013 let res = inps.iter().fold(0, |acc, &x| x | acc);
1014 let out = eval_plain(&c, &[], &inps).unwrap()[0];
1015 if out != res {
1016 println!("{:?} {} {}", inps, out, res);
1017 panic!();
1018 }
1019 }
1020 }
1021
1022 #[test] fn or_gate_fan_n_arithmetic() {
1024 let mut rng = thread_rng();
1025 let mut b: CircuitBuilder<ArithmeticCircuit> = CircuitBuilder::new();
1026 let n = 2 + (rng.gen_usize() % 200);
1027 let inps = b.evaluator_inputs(&vec![2; n]);
1028 let z = b.or_many(&inps).unwrap();
1029 b.output(&z).unwrap();
1030 let c = b.finish();
1031
1032 for _ in 0..16 {
1033 let mut inps: Vec<u16> = Vec::new();
1034 for _ in 0..n {
1035 inps.push(rng.gen_bool() as u16);
1036 }
1037 let res = inps.iter().fold(0, |acc, &x| x | acc);
1038 let out = eval_plain(&c, &[], &inps).unwrap()[0];
1039 if out != res {
1040 println!("{:?} {} {}", inps, out, res);
1041 panic!();
1042 }
1043 }
1044 }
1045 #[test] fn binary_half_gate() {
1048 let mut rng = thread_rng();
1049 let mut b = CircuitBuilder::<BinaryCircuit>::new();
1050 let q = 2;
1051 let x = b.garbler_input(q);
1052 let y = b.evaluator_input(q);
1053 let z = b.and(&x, &y).unwrap();
1054 b.output(&z).unwrap();
1055 let c = b.finish();
1056 for _ in 0..16 {
1057 let x = rng.gen_u16() % q;
1058 let y = rng.gen_u16() % q;
1059 let out = eval_plain(&c, &[x], &[y]).unwrap();
1060 assert_eq!(out[0], x * y % q);
1061 }
1062 }
1063 #[test] fn arithmetic_half_gate() {
1065 let mut rng = thread_rng();
1066 let mut b = CircuitBuilder::new();
1067 let q = rng.gen_prime();
1068 let x = b.garbler_input(q);
1069 let y = b.evaluator_input(q);
1070 let z = b.mul(&x, &y).unwrap();
1071 b.output(&z).unwrap();
1072 let c = b.finish();
1073 for _ in 0..16 {
1074 let x = rng.gen_u16() % q;
1075 let y = rng.gen_u16() % q;
1076 let out = eval_plain(&c, &[x], &[y]).unwrap();
1077 assert_eq!(out[0], x * y % q);
1078 }
1079 }
1080 #[test] fn mod_change() {
1083 let mut rng = thread_rng();
1084 let mut b = CircuitBuilder::new();
1085 let p = rng.gen_prime();
1086 let q = rng.gen_prime();
1087 let x = b.garbler_input(p);
1088 let y = b.mod_change(&x, q).unwrap();
1089 let z = b.mod_change(&y, p).unwrap();
1090 b.output(&z).unwrap();
1091 let c = b.finish();
1092 for _ in 0..16 {
1093 let x = rng.gen_u16() % p;
1094 let out = eval_plain(&c, &[x], &[]).unwrap();
1095 assert_eq!(out[0], x % q);
1096 }
1097 }
1098 #[test] fn add_many_mod_change() {
1101 let mut b = CircuitBuilder::new();
1102 let n = 113;
1103 let args = b.garbler_inputs(&vec![2; n]);
1104 let wires = args
1105 .iter()
1106 .map(|x| b.mod_change(x, n as u16 + 1).unwrap())
1107 .collect_vec();
1108 let s = b.add_many(&wires).unwrap();
1109 b.output(&s).unwrap();
1110 let c = b.finish();
1111
1112 let mut rng = thread_rng();
1113 for _ in 0..64 {
1114 let inps = (0..c.num_garbler_inputs())
1115 .map(|i| rng.gen_u16() % c.garbler_input_mod(i))
1116 .collect_vec();
1117 let s: u16 = inps.iter().sum();
1118 println!("{:?}, sum={}", inps, s);
1119 let out = eval_plain(&c, &inps, &[]).unwrap();
1120 assert_eq!(out[0], s);
1121 }
1122 }
1123 #[test] fn constants() {
1126 let mut b = CircuitBuilder::new();
1127 let mut rng = thread_rng();
1128
1129 let q = rng.gen_modulus();
1130 let c = rng.gen_u16() % q;
1131
1132 let x = b.evaluator_input(q);
1133 let y = b.constant(c, q).unwrap();
1134 let z = b.add(&x, &y).unwrap();
1135 b.output(&z).unwrap();
1136
1137 let circ = b.finish();
1138
1139 for _ in 0..64 {
1140 let x = rng.gen_u16() % q;
1141 let z = eval_plain(&circ, &[], &[x]).unwrap();
1142 assert_eq!(z[0], (x + c) % q);
1143 }
1144 }
1145 }
1147
1148#[cfg(test)]
1149mod bundle {
1150 use super::*;
1151 use crate::{
1152 fancy::{ArithmeticBundleGadgets, BinaryGadgets, BundleGadgets, CrtGadgets},
1153 util::{self, RngExt, crt_factor, crt_inv_factor},
1154 };
1155 use itertools::Itertools;
1156 use rand::thread_rng;
1157
1158 #[test] fn test_bundle_input_output() {
1160 let mut rng = thread_rng();
1161 let q = rng.gen_usable_composite_modulus();
1162
1163 let mut b = CircuitBuilder::new();
1164 let x = b.crt_garbler_input(q);
1165 println!("{:?} wires", x.wires().len());
1166 b.output_bundle(&x).unwrap();
1167
1168 let c: ArithmeticCircuit = b.finish();
1169
1170 println!("{:?}", c.output_refs);
1171
1172 for _ in 0..16 {
1173 let x = rng.gen_u128() % q;
1174 let res = eval_plain(&c, &crt_factor(x, q), &[]).unwrap();
1175 println!("{:?}", res);
1176 let z = crt_inv_factor(&res, q);
1177 assert_eq!(x, z);
1178 }
1179 }
1180
1181 #[test] fn test_addition() {
1184 let mut rng = thread_rng();
1185 let q = rng.gen_usable_composite_modulus();
1186
1187 let mut b = CircuitBuilder::new();
1188 let x = b.crt_garbler_input(q);
1189 let y = b.crt_evaluator_input(q);
1190 let z = b.crt_add(&x, &y).unwrap();
1191 b.output_bundle(&z).unwrap();
1192 let c = b.finish();
1193
1194 for _ in 0..16 {
1195 let x = rng.gen_u128() % q;
1196 let y = rng.gen_u128() % q;
1197 let res = eval_plain(&c, &crt_factor(x, q), &crt_factor(y, q)).unwrap();
1198 let z = crt_inv_factor(&res, q);
1199 assert_eq!(z, (x + y) % q);
1200 }
1201 }
1202 #[test] fn test_subtraction() {
1205 let mut rng = thread_rng();
1206 let q = rng.gen_usable_composite_modulus();
1207
1208 let mut b = CircuitBuilder::new();
1209 let x = b.crt_garbler_input(q);
1210 let y = b.crt_evaluator_input(q);
1211 let z = b.sub_bundles(&x, &y).unwrap();
1212 b.output_bundle(&z).unwrap();
1213 let c = b.finish();
1214
1215 for _ in 0..16 {
1216 let x = rng.gen_u128() % q;
1217 let y = rng.gen_u128() % q;
1218 let res = eval_plain(&c, &crt_factor(x, q), &crt_factor(y, q)).unwrap();
1219 let z = crt_inv_factor(&res, q);
1220 assert_eq!(z, (x + q - y) % q);
1221 }
1222 }
1223 #[test] fn test_cmul() {
1226 let mut rng = thread_rng();
1227 let q = util::modulus_with_width(16);
1228
1229 let mut b = CircuitBuilder::new();
1230 let x = b.crt_garbler_input(q);
1231 let y = rng.gen_u128() % q;
1232 let z = b.crt_cmul(&x, y).unwrap();
1233 b.output_bundle(&z).unwrap();
1234 let c = b.finish();
1235
1236 for _ in 0..16 {
1237 let x = rng.gen_u128() % q;
1238 let res = eval_plain(&c, &crt_factor(x, q), &[]).unwrap();
1239 let z = crt_inv_factor(&res, q);
1240 assert_eq!(z, (x * y) % q);
1241 }
1242 }
1243 #[test] fn test_multiplication() {
1246 let mut rng = thread_rng();
1247 let q = rng.gen_usable_composite_modulus();
1248
1249 let mut b = CircuitBuilder::new();
1250 let x = b.crt_garbler_input(q);
1251 let y = b.crt_evaluator_input(q);
1252 let z = b.mul_bundles(&x, &y).unwrap();
1253 b.output_bundle(&z).unwrap();
1254 let c = b.finish();
1255
1256 for _ in 0..16 {
1257 let x = rng.gen_u64() as u128 % q;
1258 let y = rng.gen_u64() as u128 % q;
1259 let res = eval_plain(&c, &crt_factor(x, q), &crt_factor(y, q)).unwrap();
1260 let z = crt_inv_factor(&res, q);
1261 assert_eq!(z, (x * y) % q);
1262 }
1263 }
1264 #[test] fn test_cexp() {
1267 let mut rng = thread_rng();
1268 let q = util::modulus_with_width(10);
1269 let y = rng.gen_u16() % 10;
1270
1271 let mut b = CircuitBuilder::new();
1272 let x = b.crt_garbler_input(q);
1273 let z = b.crt_cexp(&x, y).unwrap();
1274 b.output_bundle(&z).unwrap();
1275 let c = b.finish();
1276
1277 for _ in 0..64 {
1278 let x = rng.gen_u16() as u128 % q;
1279 let should_be = x.pow(y as u32) % q;
1280 let res = eval_plain(&c, &crt_factor(x, q), &[]).unwrap();
1281 let z = crt_inv_factor(&res, q);
1282 assert_eq!(z, should_be);
1283 }
1284 }
1285 #[test] fn test_remainder() {
1288 let mut rng = thread_rng();
1289 let ps = rng.gen_usable_factors();
1290 let q = ps.iter().fold(1, |acc, &x| (x as u128) * acc);
1291 let p = ps[rng.gen_u16() as usize % ps.len()];
1292
1293 let mut b = CircuitBuilder::new();
1294 let x = b.crt_garbler_input(q);
1295 let z = b.crt_rem(&x, p).unwrap();
1296 b.output_bundle(&z).unwrap();
1297 let c = b.finish();
1298
1299 for _ in 0..64 {
1300 let x = rng.gen_u128() % q;
1301 let should_be = x % p as u128;
1302 let res = eval_plain(&c, &crt_factor(x, q), &[]).unwrap();
1303 let z = crt_inv_factor(&res, q);
1304 assert_eq!(z, should_be);
1305 }
1306 }
1307 #[test] fn test_equality() {
1310 let mut rng = thread_rng();
1311 let q = rng.gen_usable_composite_modulus();
1312
1313 let mut b = CircuitBuilder::new();
1314 let x = b.crt_garbler_input(q);
1315 let y = b.crt_evaluator_input(q);
1316 let z = b.eq_bundles(&x, &y).unwrap();
1317 b.output(&z).unwrap();
1318 let c = b.finish();
1319
1320 let x = rng.gen_u128() % q;
1322 let res = eval_plain(&c, &crt_factor(x, q), &crt_factor(x, q)).unwrap();
1323 assert_eq!(res, &[(x == x) as u16]);
1324
1325 for _ in 0..64 {
1326 let x = rng.gen_u128() % q;
1327 let y = rng.gen_u128() % q;
1328 let res = eval_plain(&c, &crt_factor(x, q), &crt_factor(y, q)).unwrap();
1329 assert_eq!(res, &[(x == y) as u16]);
1330 }
1331 }
1332 #[test] fn test_mixed_radix_addition() {
1335 let mut rng = thread_rng();
1336
1337 let nargs = 2 + rng.gen_usize() % 100;
1338 let mods = (0..7).map(|_| rng.gen_modulus()).collect_vec();
1339
1340 let mut b = CircuitBuilder::new();
1341 let xs = (0..nargs)
1342 .map(|_| crate::fancy::Bundle::new(b.evaluator_inputs(&mods)))
1343 .collect_vec();
1344 let z = b.mixed_radix_addition(&xs).unwrap();
1345 b.output_bundle(&z).unwrap();
1346 let circ = b.finish();
1347
1348 let Q: u128 = mods.iter().map(|&q| q as u128).product();
1349
1350 let mut ds = Vec::new();
1352 for _ in 0..nargs {
1353 ds.extend(util::as_mixed_radix(Q - 1, &mods).iter());
1354 }
1355 let res = eval_plain(&circ, &[], &ds).unwrap();
1356 assert_eq!(
1357 util::from_mixed_radix(&res, &mods),
1358 (Q - 1) * (nargs as u128) % Q
1359 );
1360
1361 for _ in 0..4 {
1363 let mut should_be = 0;
1364 let mut ds = Vec::new();
1365 for _ in 0..nargs {
1366 let x = rng.gen_u128() % Q;
1367 should_be = (should_be + x) % Q;
1368 ds.extend(util::as_mixed_radix(x, &mods).iter());
1369 }
1370 let res = eval_plain(&circ, &[], &ds).unwrap();
1371 assert_eq!(util::from_mixed_radix(&res, &mods), should_be);
1372 }
1373 }
1374 #[test] fn test_relu() {
1377 let mut rng = thread_rng();
1378 let q = util::modulus_with_width(10);
1379 println!("q={}", q);
1380
1381 let mut b = CircuitBuilder::new();
1382 let x = b.crt_garbler_input(q);
1383 let z = b.crt_relu(&x, "100%", None).unwrap();
1384 b.output_bundle(&z).unwrap();
1385 let c = b.finish();
1386
1387 for _ in 0..128 {
1388 let pt = rng.gen_u128() % q;
1389 let should_be = if pt < q / 2 { pt } else { 0 };
1390 let res = eval_plain(&c, &crt_factor(pt, q), &[]).unwrap();
1391 let z = crt_inv_factor(&res, q);
1392 assert_eq!(z, should_be);
1393 }
1394 }
1395 #[test] fn test_sgn() {
1398 let mut rng = thread_rng();
1399 let q = util::modulus_with_width(10);
1400 println!("q={}", q);
1401
1402 let mut b = CircuitBuilder::new();
1403 let x = b.crt_garbler_input(q);
1404 let z = b.crt_sgn(&x, "100%", None).unwrap();
1405 b.output_bundle(&z).unwrap();
1406 let c = b.finish();
1407
1408 for _ in 0..128 {
1409 let pt = rng.gen_u128() % q;
1410 let should_be = if pt < q / 2 { 1 } else { q - 1 };
1411 let res = eval_plain(&c, &crt_factor(pt, q), &[]).unwrap();
1412 let z = crt_inv_factor(&res, q);
1413 assert_eq!(z, should_be);
1414 }
1415 }
1416 #[test] fn test_leq() {
1419 let mut rng = thread_rng();
1420 let q = util::modulus_with_width(10);
1421
1422 let mut b = CircuitBuilder::new();
1423 let x = b.crt_garbler_input(q);
1424 let y = b.crt_evaluator_input(q);
1425 let z = b.crt_lt(&x, &y, "100%").unwrap();
1426 b.output(&z).unwrap();
1427 let c = b.finish();
1428
1429 let x = rng.gen_u128() % q / 2;
1431 let res = eval_plain(&c, &crt_factor(x, q), &crt_factor(x, q)).unwrap();
1432 assert_eq!(res, &[(x < x) as u16], "x={}", x);
1433
1434 for _ in 0..64 {
1435 let x = rng.gen_u128() % q / 2;
1436 let y = rng.gen_u128() % q / 2;
1437 let res = eval_plain(&c, &crt_factor(x, q), &crt_factor(y, q)).unwrap();
1438 assert_eq!(res, &[(x < y) as u16], "x={} y={}", x, y);
1439 }
1440 }
1441 #[test] fn test_max() {
1444 let mut rng = thread_rng();
1445 let q = util::modulus_with_width(10);
1446 let n = 10;
1447 println!("n={} q={}", n, q);
1448
1449 let mut b = CircuitBuilder::new();
1450 let xs = (0..n).map(|_| b.crt_garbler_input(q)).collect_vec();
1451 let z = b.crt_max(&xs, "100%").unwrap();
1452 b.output_bundle(&z).unwrap();
1453 let c = b.finish();
1454
1455 for _ in 0..16 {
1456 let inps = (0..n).map(|_| rng.gen_u128() % (q / 2)).collect_vec();
1457 println!("{:?}", inps);
1458 let should_be = *inps.iter().max().unwrap();
1459
1460 let enc_inps = inps
1461 .into_iter()
1462 .flat_map(|x| crt_factor(x, q))
1463 .collect_vec();
1464 let res = eval_plain(&c, &enc_inps, &[]).unwrap();
1465 let z = crt_inv_factor(&res, q);
1466 assert_eq!(z, should_be);
1467 }
1468 }
1469 #[test] fn test_binary_addition() {
1472 let mut rng = thread_rng();
1473 let n = 2 + (rng.gen_usize() % 10);
1474 let q = 2;
1475 let Q = util::product(&vec![q; n]);
1476 println!("n={} q={} Q={}", n, q, Q);
1477
1478 let mut b = CircuitBuilder::<BinaryCircuit>::new();
1479 let x = b.bin_garbler_input(n);
1480 let y = b.bin_evaluator_input(n);
1481 let (zs, carry) = b.bin_addition(&x, &y).unwrap();
1482 b.output(&carry).unwrap();
1483 b.output_bundle(&zs).unwrap();
1484 let c = b.finish();
1485
1486 for _ in 0..16 {
1487 let x = rng.gen_u128() % Q;
1488 let y = rng.gen_u128() % Q;
1489 println!("x={} y={}", x, y);
1490 let res_should_be = (x + y) % Q;
1491 let carry_should_be = (x + y >= Q) as u16;
1492 let res = eval_plain(&c, &util::u128_to_bits(x, n), &util::u128_to_bits(y, n)).unwrap();
1493 assert_eq!(util::u128_from_bits(&res[1..]), res_should_be);
1494 assert_eq!(res[0], carry_should_be);
1495 }
1496 }
1497 #[test] fn test_bin_demux() {
1500 let mut rng = thread_rng();
1501 let nbits = 1 + (rng.gen_usize() % 7);
1502 let Q = 1 << nbits as u128;
1503
1504 let mut b = CircuitBuilder::<BinaryCircuit>::new();
1505 let x = b.bin_garbler_input(nbits);
1506 let d = b.bin_demux(&x).unwrap();
1507 b.outputs(&d).unwrap();
1508 let c = b.finish();
1509
1510 for _ in 0..16 {
1511 let x = rng.gen_u128() % Q;
1512 println!("x={}", x);
1513 let mut should_be = vec![0; Q as usize];
1514 should_be[x as usize] = 1;
1515
1516 let res = eval_plain(&c, &util::u128_to_bits(x, nbits), &[]).unwrap();
1517
1518 for (i, y) in res.into_iter().enumerate() {
1519 if i as u128 == x {
1520 assert_eq!(y, 1);
1521 } else {
1522 assert_eq!(y, 0);
1523 }
1524 }
1525 }
1526 }
1527 }