2  *****************************************************************************
 
    4  Implementation of interfaces for the TinyRAM ALU arithmetic gadgets.
 
    6  See alu_arithmetic.hpp .
 
    8  *****************************************************************************
 
    9  * @author     This file is part of libsnark, developed by SCIPR Lab
 
   10  *             and contributors (see AUTHORS).
 
   11  * @copyright  MIT license (see LICENSE file)
 
   12  *****************************************************************************/
 
   14 #ifndef ALU_ARITHMETIC_TCC_
 
   15 #define ALU_ARITHMETIC_TCC_
 
   18 #include <libff/common/profiling.hpp>
 
   19 #include <libff/common/utils.hpp>
 
   24 /* the code here is full of template lambda magic, but it is better to
 
   25    have limited presence of such code than to have code duplication in
 
   26    testing functions, which basically do the same thing: brute force
 
   27    the range of inputs which different success predicates */
 
   29 template<class T, typename FieldT>
 
   30 using initializer_fn = std::function<T *(
 
   31     tinyram_protoboard<FieldT> &,   // pb
 
   32     pb_variable_array<FieldT> &,    // opcode_indicators
 
   33     word_variable_gadget<FieldT> &, // desval
 
   34     word_variable_gadget<FieldT> &, // arg1val
 
   35     word_variable_gadget<FieldT> &, // arg2val
 
   36     pb_variable<FieldT> &,          // flag
 
   37     pb_variable<FieldT> &,          // result
 
   38     pb_variable<FieldT> &           // result_flag
 
   41 template<class T, typename FieldT>
 
   42 void brute_force_arithmetic_gadget(
 
   45     initializer_fn<T, FieldT> initializer,
 
   46     std::function<size_t(size_t, bool, size_t, size_t)> res_function,
 
   47     std::function<bool(size_t, bool, size_t, size_t)> flag_function)
 
   48 /* parameters for res_function and flag_function are both desval, flag, arg1val,
 
   51     printf("testing on all %zu bit inputs\n", w);
 
   53     tinyram_architecture_params ap(w, 16);
 
   55     P.instructions = generate_tinyram_prelude(ap);
 
   56     tinyram_protoboard<FieldT> pb(ap, P.size(), 0, 10);
 
   58     pb_variable_array<FieldT> opcode_indicators;
 
   59     opcode_indicators.allocate(
 
   60         pb, 1ul << ap.opcode_width(), "opcode_indicators");
 
   61     for (size_t i = 0; i < 1ul << ap.opcode_width(); ++i) {
 
   62         pb.val(opcode_indicators[i]) =
 
   63             (i == opcode ? FieldT::one() : FieldT::zero());
 
   66     word_variable_gadget<FieldT> desval(pb, "desval");
 
   67     desval.generate_r1cs_constraints(true);
 
   68     word_variable_gadget<FieldT> arg1val(pb, "arg1val");
 
   69     arg1val.generate_r1cs_constraints(true);
 
   70     word_variable_gadget<FieldT> arg2val(pb, "arg2val");
 
   71     arg2val.generate_r1cs_constraints(true);
 
   72     pb_variable<FieldT> flag;
 
   73     flag.allocate(pb, "flag");
 
   74     pb_variable<FieldT> result;
 
   75     result.allocate(pb, "result");
 
   76     pb_variable<FieldT> result_flag;
 
   77     result_flag.allocate(pb, "result_flag");
 
   89     g->generate_r1cs_constraints();
 
   91     for (size_t des = 0; des < (1u << w); ++des) {
 
   92         pb.val(desval.packed) = FieldT(des);
 
   93         desval.generate_r1cs_witness_from_packed();
 
   95         for (char f = 0; f <= 1; ++f) {
 
   96             pb.val(flag) = (f ? FieldT::one() : FieldT::zero());
 
   98             for (size_t arg1 = 0; arg1 < (1u << w); ++arg1) {
 
   99                 pb.val(arg1val.packed) = FieldT(arg1);
 
  100                 arg1val.generate_r1cs_witness_from_packed();
 
  102                 for (size_t arg2 = 0; arg2 < (1u << w); ++arg2) {
 
  103                     pb.val(arg2val.packed) = FieldT(arg2);
 
  104                     arg2val.generate_r1cs_witness_from_packed();
 
  106                     size_t res = res_function(des, f, arg1, arg2);
 
  107                     bool res_f = flag_function(des, f, arg1, arg2);
 
  110                         "with the following parameters: flag = %d"
 
  111                         ", desval = %zu (%d)"
 
  112                         ", arg1val = %zu (%d)"
 
  113                         ", arg2val = %zu (%d)"
 
  114                         ". expected result: %zu (%d), expected flag: %d\n",
 
  117                         libff::from_twos_complement(des, w),
 
  119                         libff::from_twos_complement(arg1, w),
 
  121                         libff::from_twos_complement(arg2, w),
 
  123                         libff::from_twos_complement(res, w),
 
  127                     libff::UNUSED(res_f);
 
  129                     g->generate_r1cs_witness();
 
  132                     pb.val(result).print();
 
  134                     pb.val(result_flag).print();
 
  136                     assert(pb.is_satisfied());
 
  137                     assert(pb.val(result) == FieldT(res));
 
  139                         pb.val(result_flag) ==
 
  140                         (res_f ? FieldT::one() : FieldT::zero()));
 
  148 template<typename FieldT>
 
  149 void ALU_and_gadget<FieldT>::generate_r1cs_constraints()
 
  151     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  152         this->pb.add_r1cs_constraint(
 
  153             r1cs_constraint<FieldT>(
 
  154                 {this->arg1val.bits[i]},
 
  155                 {this->arg2val.bits[i]},
 
  156                 {this->res_word[i]}),
 
  157             FMT(this->annotation_prefix, " res_word_%zu", i));
 
  160     /* generate result */
 
  161     pack_result->generate_r1cs_constraints(false);
 
  162     not_all_zeros->generate_r1cs_constraints();
 
  164     /* result_flag = 1 - not_all_zeros = result is 0^w */
 
  165     this->pb.add_r1cs_constraint(
 
  166         r1cs_constraint<FieldT>(
 
  168             {ONE, this->not_all_zeros_result * (-1)},
 
  169             {this->result_flag}),
 
  170         FMT(this->annotation_prefix, " result_flag"));
 
  173 template<typename FieldT> void ALU_and_gadget<FieldT>::generate_r1cs_witness()
 
  175     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  176         bool b1 = this->pb.val(this->arg1val.bits[i]) == FieldT::one();
 
  177         bool b2 = this->pb.val(this->arg2val.bits[i]) == FieldT::one();
 
  179         this->pb.val(this->res_word[i]) =
 
  180             (b1 && b2 ? FieldT::one() : FieldT::zero());
 
  183     pack_result->generate_r1cs_witness_from_bits();
 
  184     not_all_zeros->generate_r1cs_witness();
 
  185     this->pb.val(this->result_flag) =
 
  186         FieldT::one() - this->pb.val(not_all_zeros_result);
 
  189 template<typename FieldT> void test_ALU_and_gadget(const size_t w)
 
  191     libff::print_time("starting and test");
 
  192     brute_force_arithmetic_gadget<ALU_and_gadget<FieldT>, FieldT>(
 
  195         [](tinyram_protoboard<FieldT> &pb,
 
  196            pb_variable_array<FieldT> &opcode_indicators,
 
  197            word_variable_gadget<FieldT> &desval,
 
  198            word_variable_gadget<FieldT> &arg1val,
 
  199            word_variable_gadget<FieldT> &arg2val,
 
  200            pb_variable<FieldT> &flag,
 
  201            pb_variable<FieldT> &result,
 
  202            pb_variable<FieldT> &result_flag) -> ALU_and_gadget<FieldT> * {
 
  203             return new ALU_and_gadget<FieldT>(
 
  214         [w](size_t, bool, size_t x, size_t y) -> size_t { return x & y; },
 
  215         [w](size_t, bool, size_t x, size_t y) -> bool { return (x & y) == 0; });
 
  216     libff::print_time("and tests successful");
 
  220 template<typename FieldT>
 
  221 void ALU_or_gadget<FieldT>::generate_r1cs_constraints()
 
  223     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  224         this->pb.add_r1cs_constraint(
 
  225             r1cs_constraint<FieldT>(
 
  226                 {ONE, this->arg1val.bits[i] * (-1)},
 
  227                 {ONE, this->arg2val.bits[i] * (-1)},
 
  228                 {ONE, this->res_word[i] * (-1)}),
 
  229             FMT(this->annotation_prefix, " res_word_%zu", i));
 
  232     /* generate result */
 
  233     pack_result->generate_r1cs_constraints(false);
 
  234     not_all_zeros->generate_r1cs_constraints();
 
  236     /* result_flag = 1 - not_all_zeros = result is 0^w */
 
  237     this->pb.add_r1cs_constraint(
 
  238         r1cs_constraint<FieldT>(
 
  240             {ONE, this->not_all_zeros_result * (-1)},
 
  241             {this->result_flag}),
 
  242         FMT(this->annotation_prefix, " result_flag"));
 
  245 template<typename FieldT> void ALU_or_gadget<FieldT>::generate_r1cs_witness()
 
  247     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  248         bool b1 = this->pb.val(this->arg1val.bits[i]) == FieldT::one();
 
  249         bool b2 = this->pb.val(this->arg2val.bits[i]) == FieldT::one();
 
  251         this->pb.val(this->res_word[i]) =
 
  252             (b1 || b2 ? FieldT::one() : FieldT::zero());
 
  255     pack_result->generate_r1cs_witness_from_bits();
 
  256     not_all_zeros->generate_r1cs_witness();
 
  257     this->pb.val(this->result_flag) =
 
  258         FieldT::one() - this->pb.val(this->not_all_zeros_result);
 
  261 template<typename FieldT> void test_ALU_or_gadget(const size_t w)
 
  263     libff::print_time("starting or test");
 
  264     brute_force_arithmetic_gadget<ALU_or_gadget<FieldT>, FieldT>(
 
  267         [](tinyram_protoboard<FieldT> &pb,
 
  268            pb_variable_array<FieldT> &opcode_indicators,
 
  269            word_variable_gadget<FieldT> &desval,
 
  270            word_variable_gadget<FieldT> &arg1val,
 
  271            word_variable_gadget<FieldT> &arg2val,
 
  272            pb_variable<FieldT> &flag,
 
  273            pb_variable<FieldT> &result,
 
  274            pb_variable<FieldT> &result_flag) -> ALU_or_gadget<FieldT> * {
 
  275             return new ALU_or_gadget<FieldT>(
 
  286         [w](size_t, bool, size_t x, size_t y) -> size_t { return x | y; },
 
  287         [w](size_t, bool, size_t x, size_t y) -> bool { return (x | y) == 0; });
 
  288     libff::print_time("or tests successful");
 
  292 template<typename FieldT>
 
  293 void ALU_xor_gadget<FieldT>::generate_r1cs_constraints()
 
  295     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  296         /* a = b ^ c <=> a = b + c - 2*b*c, (2*b)*c = b+c - a */
 
  297         this->pb.add_r1cs_constraint(
 
  298             r1cs_constraint<FieldT>(
 
  299                 {this->arg1val.bits[i] * 2},
 
  300                 {this->arg2val.bits[i]},
 
  301                 {this->arg1val.bits[i],
 
  302                  this->arg2val.bits[i],
 
  303                  this->res_word[i] * (-1)}),
 
  304             FMT(this->annotation_prefix, " res_word_%zu", i));
 
  307     /* generate result */
 
  308     pack_result->generate_r1cs_constraints(false);
 
  309     not_all_zeros->generate_r1cs_constraints();
 
  311     /* result_flag = 1 - not_all_zeros = result is 0^w */
 
  312     this->pb.add_r1cs_constraint(
 
  313         r1cs_constraint<FieldT>(
 
  315             {ONE, this->not_all_zeros_result * (-1)},
 
  316             {this->result_flag}),
 
  317         FMT(this->annotation_prefix, " result_flag"));
 
  320 template<typename FieldT> void ALU_xor_gadget<FieldT>::generate_r1cs_witness()
 
  322     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  323         bool b1 = this->pb.val(this->arg1val.bits[i]) == FieldT::one();
 
  324         bool b2 = this->pb.val(this->arg2val.bits[i]) == FieldT::one();
 
  326         this->pb.val(this->res_word[i]) =
 
  327             (b1 ^ b2 ? FieldT::one() : FieldT::zero());
 
  330     pack_result->generate_r1cs_witness_from_bits();
 
  331     not_all_zeros->generate_r1cs_witness();
 
  332     this->pb.val(this->result_flag) =
 
  333         FieldT::one() - this->pb.val(this->not_all_zeros_result);
 
  336 template<typename FieldT> void test_ALU_xor_gadget(const size_t w)
 
  338     libff::print_time("starting xor test");
 
  339     brute_force_arithmetic_gadget<ALU_xor_gadget<FieldT>, FieldT>(
 
  342         [](tinyram_protoboard<FieldT> &pb,
 
  343            pb_variable_array<FieldT> &opcode_indicators,
 
  344            word_variable_gadget<FieldT> &desval,
 
  345            word_variable_gadget<FieldT> &arg1val,
 
  346            word_variable_gadget<FieldT> &arg2val,
 
  347            pb_variable<FieldT> &flag,
 
  348            pb_variable<FieldT> &result,
 
  349            pb_variable<FieldT> &result_flag) -> ALU_xor_gadget<FieldT> * {
 
  350             return new ALU_xor_gadget<FieldT>(
 
  361         [w](size_t, bool, size_t x, size_t y) -> size_t { return x ^ y; },
 
  362         [w](size_t, bool, size_t x, size_t y) -> bool { return (x ^ y) == 0; });
 
  363     libff::print_time("xor tests successful");
 
  367 template<typename FieldT>
 
  368 void ALU_not_gadget<FieldT>::generate_r1cs_constraints()
 
  370     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  371         this->pb.add_r1cs_constraint(
 
  372             r1cs_constraint<FieldT>(
 
  374                 {ONE, this->arg2val.bits[i] * (-1)},
 
  375                 {this->res_word[i]}),
 
  376             FMT(this->annotation_prefix, " res_word_%zu", i));
 
  379     /* generate result */
 
  380     pack_result->generate_r1cs_constraints(false);
 
  381     not_all_zeros->generate_r1cs_constraints();
 
  383     /* result_flag = 1 - not_all_zeros = result is 0^w */
 
  384     this->pb.add_r1cs_constraint(
 
  385         r1cs_constraint<FieldT>(
 
  387             {ONE, this->not_all_zeros_result * (-1)},
 
  388             {this->result_flag}),
 
  389         FMT(this->annotation_prefix, " result_flag"));
 
  392 template<typename FieldT> void ALU_not_gadget<FieldT>::generate_r1cs_witness()
 
  394     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  395         bool b2 = this->pb.val(this->arg2val.bits[i]) == FieldT::one();
 
  397         this->pb.val(this->res_word[i]) =
 
  398             (!b2 ? FieldT::one() : FieldT::zero());
 
  401     pack_result->generate_r1cs_witness_from_bits();
 
  402     not_all_zeros->generate_r1cs_witness();
 
  403     this->pb.val(this->result_flag) =
 
  404         FieldT::one() - this->pb.val(this->not_all_zeros_result);
 
  407 template<typename FieldT> void test_ALU_not_gadget(const size_t w)
 
  409     libff::print_time("starting not test");
 
  410     brute_force_arithmetic_gadget<ALU_not_gadget<FieldT>, FieldT>(
 
  413         [](tinyram_protoboard<FieldT> &pb,
 
  414            pb_variable_array<FieldT> &opcode_indicators,
 
  415            word_variable_gadget<FieldT> &desval,
 
  416            word_variable_gadget<FieldT> &arg1val,
 
  417            word_variable_gadget<FieldT> &arg2val,
 
  418            pb_variable<FieldT> &flag,
 
  419            pb_variable<FieldT> &result,
 
  420            pb_variable<FieldT> &result_flag) -> ALU_not_gadget<FieldT> * {
 
  421             return new ALU_not_gadget<FieldT>(
 
  432         [w](size_t, bool, size_t, size_t y) -> size_t {
 
  433             return (1ul << w) - 1 - y;
 
  435         [w](size_t, bool, size_t, size_t y) -> bool {
 
  436             return ((1ul << w) - 1 - y) == 0;
 
  438     libff::print_time("not tests successful");
 
  442 template<typename FieldT>
 
  443 void ALU_add_gadget<FieldT>::generate_r1cs_constraints()
 
  445     /* addition_result = 1 * (arg1val + arg2val) */
 
  446     this->pb.add_r1cs_constraint(
 
  447         r1cs_constraint<FieldT>(
 
  449             {this->arg1val.packed, this->arg2val.packed},
 
  450             {this->addition_result}),
 
  451         FMT(this->annotation_prefix, " addition_result"));
 
  453     /* unpack into bits */
 
  454     unpack_addition->generate_r1cs_constraints(true);
 
  456     /* generate result */
 
  457     pack_result->generate_r1cs_constraints(false);
 
  460 template<typename FieldT> void ALU_add_gadget<FieldT>::generate_r1cs_witness()
 
  462     this->pb.val(addition_result) =
 
  463         this->pb.val(this->arg1val.packed) + this->pb.val(this->arg2val.packed);
 
  464     unpack_addition->generate_r1cs_witness_from_packed();
 
  465     pack_result->generate_r1cs_witness_from_bits();
 
  468 template<typename FieldT> void test_ALU_add_gadget(const size_t w)
 
  470     libff::print_time("starting add test");
 
  471     brute_force_arithmetic_gadget<ALU_add_gadget<FieldT>, FieldT>(
 
  474         [](tinyram_protoboard<FieldT> &pb,
 
  475            pb_variable_array<FieldT> &opcode_indicators,
 
  476            word_variable_gadget<FieldT> &desval,
 
  477            word_variable_gadget<FieldT> &arg1val,
 
  478            word_variable_gadget<FieldT> &arg2val,
 
  479            pb_variable<FieldT> &flag,
 
  480            pb_variable<FieldT> &result,
 
  481            pb_variable<FieldT> &result_flag) -> ALU_add_gadget<FieldT> * {
 
  482             return new ALU_add_gadget<FieldT>(
 
  493         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
  494             return (x + y) % (1ul << w);
 
  496         [w](size_t, bool, size_t x, size_t y) -> bool {
 
  497             return (x + y) >= (1ul << w);
 
  499     libff::print_time("add tests successful");
 
  503 template<typename FieldT>
 
  504 void ALU_sub_gadget<FieldT>::generate_r1cs_constraints()
 
  506     /* intermediate_result = 2^w + (arg1val - arg2val) */
 
  507     FieldT twoi = FieldT::one();
 
  509     linear_combination<FieldT> a, b, c;
 
  512     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  516     b.add_term(this->arg1val.packed, 1);
 
  517     b.add_term(this->arg2val.packed, -1);
 
  518     c.add_term(intermediate_result, 1);
 
  520     this->pb.add_r1cs_constraint(
 
  521         r1cs_constraint<FieldT>(a, b, c),
 
  522         FMT(this->annotation_prefix, " main_constraint"));
 
  524     /* unpack into bits */
 
  525     unpack_intermediate->generate_r1cs_constraints(true);
 
  527     /* generate result */
 
  528     pack_result->generate_r1cs_constraints(false);
 
  529     this->pb.add_r1cs_constraint(
 
  530         r1cs_constraint<FieldT>(
 
  531             {ONE}, {ONE, this->negated_flag * (-1)}, {this->result_flag}),
 
  532         FMT(this->annotation_prefix, " result_flag"));
 
  535 template<typename FieldT> void ALU_sub_gadget<FieldT>::generate_r1cs_witness()
 
  537     FieldT twoi = FieldT::one();
 
  538     for (size_t i = 0; i < this->pb.ap.w; ++i) {
 
  542     this->pb.val(intermediate_result) = twoi +
 
  543                                         this->pb.val(this->arg1val.packed) -
 
  544                                         this->pb.val(this->arg2val.packed);
 
  545     unpack_intermediate->generate_r1cs_witness_from_packed();
 
  546     pack_result->generate_r1cs_witness_from_bits();
 
  547     this->pb.val(this->result_flag) =
 
  548         FieldT::one() - this->pb.val(this->negated_flag);
 
  551 template<typename FieldT> void test_ALU_sub_gadget(const size_t w)
 
  553     libff::print_time("starting sub test");
 
  554     brute_force_arithmetic_gadget<ALU_sub_gadget<FieldT>, FieldT>(
 
  557         [](tinyram_protoboard<FieldT> &pb,
 
  558            pb_variable_array<FieldT> &opcode_indicators,
 
  559            word_variable_gadget<FieldT> &desval,
 
  560            word_variable_gadget<FieldT> &arg1val,
 
  561            word_variable_gadget<FieldT> &arg2val,
 
  562            pb_variable<FieldT> &flag,
 
  563            pb_variable<FieldT> &result,
 
  564            pb_variable<FieldT> &result_flag) -> ALU_sub_gadget<FieldT> * {
 
  565             return new ALU_sub_gadget<FieldT>(
 
  576         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
  577             const size_t unsigned_result = ((1ul << w) + x - y) % (1ul << w);
 
  578             return unsigned_result;
 
  580         [w](size_t, bool, size_t x, size_t y) -> bool {
 
  581             const size_t msb = ((1ul << w) + x - y) >> w;
 
  584     libff::print_time("sub tests successful");
 
  588 template<typename FieldT>
 
  589 void ALU_mov_gadget<FieldT>::generate_r1cs_constraints()
 
  591     this->pb.add_r1cs_constraint(
 
  592         r1cs_constraint<FieldT>({ONE}, {this->arg2val.packed}, {this->result}),
 
  593         FMT(this->annotation_prefix, " mov_result"));
 
  595     this->pb.add_r1cs_constraint(
 
  596         r1cs_constraint<FieldT>({ONE}, {this->flag}, {this->result_flag}),
 
  597         FMT(this->annotation_prefix, " mov_result_flag"));
 
  600 template<typename FieldT> void ALU_mov_gadget<FieldT>::generate_r1cs_witness()
 
  602     this->pb.val(this->result) = this->pb.val(this->arg2val.packed);
 
  603     this->pb.val(this->result_flag) = this->pb.val(this->flag);
 
  606 template<typename FieldT> void test_ALU_mov_gadget(const size_t w)
 
  608     libff::print_time("starting mov test");
 
  609     brute_force_arithmetic_gadget<ALU_mov_gadget<FieldT>, FieldT>(
 
  612         [](tinyram_protoboard<FieldT> &pb,
 
  613            pb_variable_array<FieldT> &opcode_indicators,
 
  614            word_variable_gadget<FieldT> &desval,
 
  615            word_variable_gadget<FieldT> &arg1val,
 
  616            word_variable_gadget<FieldT> &arg2val,
 
  617            pb_variable<FieldT> &flag,
 
  618            pb_variable<FieldT> &result,
 
  619            pb_variable<FieldT> &result_flag) -> ALU_mov_gadget<FieldT> * {
 
  620             return new ALU_mov_gadget<FieldT>(
 
  631         [w](size_t, bool, size_t, size_t y) -> size_t { return y; },
 
  632         [w](size_t, bool f, size_t, size_t) -> bool { return f; });
 
  633     libff::print_time("mov tests successful");
 
  637 template<typename FieldT>
 
  638 void ALU_cmov_gadget<FieldT>::generate_r1cs_constraints()
 
  641       flag1 * arg2val + (1-flag1) * desval = result
 
  642       flag1 * (arg2val - desval) = result - desval
 
  644     this->pb.add_r1cs_constraint(
 
  645         r1cs_constraint<FieldT>(
 
  647             {this->arg2val.packed, this->desval.packed * (-1)},
 
  648             {this->result, this->desval.packed * (-1)}),
 
  649         FMT(this->annotation_prefix, " cmov_result"));
 
  651     this->pb.add_r1cs_constraint(
 
  652         r1cs_constraint<FieldT>({ONE}, {this->flag}, {this->result_flag}),
 
  653         FMT(this->annotation_prefix, " cmov_result_flag"));
 
  656 template<typename FieldT> void ALU_cmov_gadget<FieldT>::generate_r1cs_witness()
 
  658     this->pb.val(this->result) =
 
  659         ((this->pb.val(this->flag) == FieldT::one())
 
  660              ? this->pb.val(this->arg2val.packed)
 
  661              : this->pb.val(this->desval.packed));
 
  662     this->pb.val(this->result_flag) = this->pb.val(this->flag);
 
  665 template<typename FieldT> void test_ALU_cmov_gadget(const size_t w)
 
  667     libff::print_time("starting cmov test");
 
  668     brute_force_arithmetic_gadget<ALU_cmov_gadget<FieldT>, FieldT>(
 
  671         [](tinyram_protoboard<FieldT> &pb,
 
  672            pb_variable_array<FieldT> &opcode_indicators,
 
  673            word_variable_gadget<FieldT> &desval,
 
  674            word_variable_gadget<FieldT> &arg1val,
 
  675            word_variable_gadget<FieldT> &arg2val,
 
  676            pb_variable<FieldT> &flag,
 
  677            pb_variable<FieldT> &result,
 
  678            pb_variable<FieldT> &result_flag) -> ALU_cmov_gadget<FieldT> * {
 
  679             return new ALU_cmov_gadget<FieldT>(
 
  690         [w](size_t des, bool f, size_t, size_t y) -> size_t {
 
  693         [w](size_t, bool f, size_t, size_t) -> bool { return f; });
 
  694     libff::print_time("cmov tests successful");
 
  697 /* unsigned comparison */
 
  698 template<typename FieldT>
 
  699 void ALU_cmp_gadget<FieldT>::generate_r1cs_constraints()
 
  701     comparator.generate_r1cs_constraints();
 
  703       cmpe = cmpae * (1-cmpa)
 
  705     this->pb.add_r1cs_constraint(
 
  706         r1cs_constraint<FieldT>(
 
  708             {ONE, cmpa_result_flag * (-1)},
 
  710         FMT(this->annotation_prefix, " cmpa_result_flag"));
 
  712     /* copy over results */
 
  713     this->pb.add_r1cs_constraint(
 
  714         r1cs_constraint<FieldT>({ONE}, {this->desval.packed}, {cmpe_result}),
 
  715         FMT(this->annotation_prefix, " cmpe_result"));
 
  717     this->pb.add_r1cs_constraint(
 
  718         r1cs_constraint<FieldT>({ONE}, {this->desval.packed}, {cmpa_result}),
 
  719         FMT(this->annotation_prefix, " cmpa_result"));
 
  721     this->pb.add_r1cs_constraint(
 
  722         r1cs_constraint<FieldT>({ONE}, {this->desval.packed}, {cmpae_result}),
 
  723         FMT(this->annotation_prefix, " cmpae_result"));
 
  726 template<typename FieldT> void ALU_cmp_gadget<FieldT>::generate_r1cs_witness()
 
  728     comparator.generate_r1cs_witness();
 
  730     this->pb.val(cmpe_result) = this->pb.val(this->desval.packed);
 
  731     this->pb.val(cmpa_result) = this->pb.val(this->desval.packed);
 
  732     this->pb.val(cmpae_result) = this->pb.val(this->desval.packed);
 
  734     this->pb.val(cmpe_result_flag) =
 
  735         ((this->pb.val(cmpae_result_flag) == FieldT::one()) &&
 
  736                  (this->pb.val(cmpa_result_flag) == FieldT::zero())
 
  741 template<typename FieldT> void test_ALU_cmpe_gadget(const size_t w)
 
  743     libff::print_time("starting cmpe test");
 
  744     brute_force_arithmetic_gadget<ALU_cmp_gadget<FieldT>, FieldT>(
 
  747         [](tinyram_protoboard<FieldT> &pb,
 
  748            pb_variable_array<FieldT> &opcode_indicators,
 
  749            word_variable_gadget<FieldT> &desval,
 
  750            word_variable_gadget<FieldT> &arg1val,
 
  751            word_variable_gadget<FieldT> &arg2val,
 
  752            pb_variable<FieldT> &flag,
 
  753            pb_variable<FieldT> &result,
 
  754            pb_variable<FieldT> &result_flag) -> ALU_cmp_gadget<FieldT> * {
 
  755             pb_variable<FieldT> cmpa_result;
 
  756             cmpa_result.allocate(pb, "cmpa_result");
 
  757             pb_variable<FieldT> cmpa_result_flag;
 
  758             cmpa_result_flag.allocate(pb, "cmpa_result_flag");
 
  759             pb_variable<FieldT> cmpae_result;
 
  760             cmpae_result.allocate(pb, "cmpae_result");
 
  761             pb_variable<FieldT> cmpae_result_flag;
 
  762             cmpae_result_flag.allocate(pb, "cmpae_result_flag");
 
  763             return new ALU_cmp_gadget<FieldT>(
 
  778         [w](size_t des, bool, size_t, size_t) -> size_t { return des; },
 
  779         [w](size_t, bool, size_t x, size_t y) -> bool { return x == y; });
 
  780     libff::print_time("cmpe tests successful");
 
  783 template<typename FieldT> void test_ALU_cmpa_gadget(const size_t w)
 
  785     libff::print_time("starting cmpa test");
 
  786     brute_force_arithmetic_gadget<ALU_cmp_gadget<FieldT>, FieldT>(
 
  789         [](tinyram_protoboard<FieldT> &pb,
 
  790            pb_variable_array<FieldT> &opcode_indicators,
 
  791            word_variable_gadget<FieldT> &desval,
 
  792            word_variable_gadget<FieldT> &arg1val,
 
  793            word_variable_gadget<FieldT> &arg2val,
 
  794            pb_variable<FieldT> &flag,
 
  795            pb_variable<FieldT> &result,
 
  796            pb_variable<FieldT> &result_flag) -> ALU_cmp_gadget<FieldT> * {
 
  797             pb_variable<FieldT> cmpe_result;
 
  798             cmpe_result.allocate(pb, "cmpe_result");
 
  799             pb_variable<FieldT> cmpe_result_flag;
 
  800             cmpe_result_flag.allocate(pb, "cmpe_result_flag");
 
  801             pb_variable<FieldT> cmpae_result;
 
  802             cmpae_result.allocate(pb, "cmpae_result");
 
  803             pb_variable<FieldT> cmpae_result_flag;
 
  804             cmpae_result_flag.allocate(pb, "cmpae_result_flag");
 
  805             return new ALU_cmp_gadget<FieldT>(
 
  820         [w](size_t des, bool, size_t, size_t) -> size_t { return des; },
 
  821         [w](size_t, bool, size_t x, size_t y) -> bool { return x > y; });
 
  822     libff::print_time("cmpa tests successful");
 
  825 template<typename FieldT> void test_ALU_cmpae_gadget(const size_t w)
 
  827     libff::print_time("starting cmpae test");
 
  828     brute_force_arithmetic_gadget<ALU_cmp_gadget<FieldT>, FieldT>(
 
  830         tinyram_opcode_CMPAE,
 
  831         [](tinyram_protoboard<FieldT> &pb,
 
  832            pb_variable_array<FieldT> &opcode_indicators,
 
  833            word_variable_gadget<FieldT> &desval,
 
  834            word_variable_gadget<FieldT> &arg1val,
 
  835            word_variable_gadget<FieldT> &arg2val,
 
  836            pb_variable<FieldT> &flag,
 
  837            pb_variable<FieldT> &result,
 
  838            pb_variable<FieldT> &result_flag) -> ALU_cmp_gadget<FieldT> * {
 
  839             pb_variable<FieldT> cmpe_result;
 
  840             cmpe_result.allocate(pb, "cmpe_result");
 
  841             pb_variable<FieldT> cmpe_result_flag;
 
  842             cmpe_result_flag.allocate(pb, "cmpe_result_flag");
 
  843             pb_variable<FieldT> cmpa_result;
 
  844             cmpa_result.allocate(pb, "cmpa_result");
 
  845             pb_variable<FieldT> cmpa_result_flag;
 
  846             cmpa_result_flag.allocate(pb, "cmpa_result_flag");
 
  847             return new ALU_cmp_gadget<FieldT>(
 
  862         [w](size_t des, bool, size_t, size_t) -> size_t { return des; },
 
  863         [w](size_t, bool, size_t x, size_t y) -> bool { return x >= y; });
 
  864     libff::print_time("cmpae tests successful");
 
  867 /* signed comparison */
 
  868 template<typename FieldT>
 
  869 void ALU_cmps_gadget<FieldT>::generate_r1cs_constraints()
 
  871     /* negate sign bits */
 
  872     this->pb.add_r1cs_constraint(
 
  873         r1cs_constraint<FieldT>(
 
  875             {ONE, this->arg1val.bits[this->pb.ap.w - 1] * (-1)},
 
  876             {negated_arg1val_sign}),
 
  877         FMT(this->annotation_prefix, " negated_arg1val_sign"));
 
  878     this->pb.add_r1cs_constraint(
 
  879         r1cs_constraint<FieldT>(
 
  881             {ONE, this->arg2val.bits[this->pb.ap.w - 1] * (-1)},
 
  882             {negated_arg2val_sign}),
 
  883         FMT(this->annotation_prefix, " negated_arg2val_sign"));
 
  886     pack_modified_arg1->generate_r1cs_constraints(false);
 
  887     pack_modified_arg2->generate_r1cs_constraints(false);
 
  890     comparator->generate_r1cs_constraints();
 
  892     /* copy over results */
 
  893     this->pb.add_r1cs_constraint(
 
  894         r1cs_constraint<FieldT>({ONE}, {this->desval.packed}, {cmpg_result}),
 
  895         FMT(this->annotation_prefix, " cmpg_result"));
 
  897     this->pb.add_r1cs_constraint(
 
  898         r1cs_constraint<FieldT>({ONE}, {this->desval.packed}, {cmpge_result}),
 
  899         FMT(this->annotation_prefix, " cmpge_result"));
 
  902 template<typename FieldT> void ALU_cmps_gadget<FieldT>::generate_r1cs_witness()
 
  904     /* negate sign bits */
 
  905     this->pb.val(negated_arg1val_sign) =
 
  906         FieldT::one() - this->pb.val(this->arg1val.bits[this->pb.ap.w - 1]);
 
  907     this->pb.val(negated_arg2val_sign) =
 
  908         FieldT::one() - this->pb.val(this->arg2val.bits[this->pb.ap.w - 1]);
 
  911     pack_modified_arg1->generate_r1cs_witness_from_bits();
 
  912     pack_modified_arg2->generate_r1cs_witness_from_bits();
 
  915     comparator->generate_r1cs_witness();
 
  917     this->pb.val(cmpg_result) = this->pb.val(this->desval.packed);
 
  918     this->pb.val(cmpge_result) = this->pb.val(this->desval.packed);
 
  921 template<typename FieldT> void test_ALU_cmpg_gadget(const size_t w)
 
  923     libff::print_time("starting cmpg test");
 
  924     brute_force_arithmetic_gadget<ALU_cmps_gadget<FieldT>, FieldT>(
 
  927         [](tinyram_protoboard<FieldT> &pb,
 
  928            pb_variable_array<FieldT> &opcode_indicators,
 
  929            word_variable_gadget<FieldT> &desval,
 
  930            word_variable_gadget<FieldT> &arg1val,
 
  931            word_variable_gadget<FieldT> &arg2val,
 
  932            pb_variable<FieldT> &flag,
 
  933            pb_variable<FieldT> &result,
 
  934            pb_variable<FieldT> &result_flag) -> ALU_cmps_gadget<FieldT> * {
 
  935             pb_variable<FieldT> cmpge_result;
 
  936             cmpge_result.allocate(pb, "cmpge_result");
 
  937             pb_variable<FieldT> cmpge_result_flag;
 
  938             cmpge_result_flag.allocate(pb, "cmpge_result_flag");
 
  939             return new ALU_cmps_gadget<FieldT>(
 
  952         [w](size_t des, bool, size_t, size_t) -> size_t { return des; },
 
  953         [w](size_t, bool, size_t x, size_t y) -> bool {
 
  955                 libff::from_twos_complement(x, w) >
 
  956                 libff::from_twos_complement(y, w));
 
  958     libff::print_time("cmpg tests successful");
 
  961 template<typename FieldT> void test_ALU_cmpge_gadget(const size_t w)
 
  963     libff::print_time("starting cmpge test");
 
  964     brute_force_arithmetic_gadget<ALU_cmps_gadget<FieldT>, FieldT>(
 
  966         tinyram_opcode_CMPGE,
 
  967         [](tinyram_protoboard<FieldT> &pb,
 
  968            pb_variable_array<FieldT> &opcode_indicators,
 
  969            word_variable_gadget<FieldT> &desval,
 
  970            word_variable_gadget<FieldT> &arg1val,
 
  971            word_variable_gadget<FieldT> &arg2val,
 
  972            pb_variable<FieldT> &flag,
 
  973            pb_variable<FieldT> &result,
 
  974            pb_variable<FieldT> &result_flag) -> ALU_cmps_gadget<FieldT> * {
 
  975             pb_variable<FieldT> cmpg_result;
 
  976             cmpg_result.allocate(pb, "cmpg_result");
 
  977             pb_variable<FieldT> cmpg_result_flag;
 
  978             cmpg_result_flag.allocate(pb, "cmpg_result_flag");
 
  979             return new ALU_cmps_gadget<FieldT>(
 
  992         [w](size_t des, bool, size_t, size_t) -> size_t { return des; },
 
  993         [w](size_t, bool, size_t x, size_t y) -> bool {
 
  995                 libff::from_twos_complement(x, w) >=
 
  996                 libff::from_twos_complement(y, w));
 
  998     libff::print_time("cmpge tests successful");
 
 1001 template<typename FieldT>
 
 1002 void ALU_umul_gadget<FieldT>::generate_r1cs_constraints()
 
 1004     /* do multiplication */
 
 1005     this->pb.add_r1cs_constraint(
 
 1006         r1cs_constraint<FieldT>(
 
 1007             {this->arg1val.packed},
 
 1008             {this->arg2val.packed},
 
 1009             {mul_result.packed}),
 
 1010         FMT(this->annotation_prefix, " main_constraint"));
 
 1011     mul_result.generate_r1cs_constraints(true);
 
 1014     pack_mull_result->generate_r1cs_constraints(false);
 
 1015     pack_umulh_result->generate_r1cs_constraints(false);
 
 1018     compute_flag->generate_r1cs_constraints();
 
 1020     this->pb.add_r1cs_constraint(
 
 1021         r1cs_constraint<FieldT>({ONE}, {this->result_flag}, {mull_flag}),
 
 1022         FMT(this->annotation_prefix, " mull_flag"));
 
 1024     this->pb.add_r1cs_constraint(
 
 1025         r1cs_constraint<FieldT>({ONE}, {this->result_flag}, {umulh_flag}),
 
 1026         FMT(this->annotation_prefix, " umulh_flag"));
 
 1029 template<typename FieldT> void ALU_umul_gadget<FieldT>::generate_r1cs_witness()
 
 1031     /* do multiplication */
 
 1032     this->pb.val(mul_result.packed) =
 
 1033         this->pb.val(this->arg1val.packed) * this->pb.val(this->arg2val.packed);
 
 1034     mul_result.generate_r1cs_witness_from_packed();
 
 1037     pack_mull_result->generate_r1cs_witness_from_bits();
 
 1038     pack_umulh_result->generate_r1cs_witness_from_bits();
 
 1041     compute_flag->generate_r1cs_witness();
 
 1043     this->pb.val(mull_flag) = this->pb.val(this->result_flag);
 
 1044     this->pb.val(umulh_flag) = this->pb.val(this->result_flag);
 
 1047 template<typename FieldT> void test_ALU_mull_gadget(const size_t w)
 
 1049     libff::print_time("starting mull test");
 
 1050     brute_force_arithmetic_gadget<ALU_umul_gadget<FieldT>, FieldT>(
 
 1052         tinyram_opcode_MULL,
 
 1053         [](tinyram_protoboard<FieldT> &pb,
 
 1054            pb_variable_array<FieldT> &opcode_indicators,
 
 1055            word_variable_gadget<FieldT> &desval,
 
 1056            word_variable_gadget<FieldT> &arg1val,
 
 1057            word_variable_gadget<FieldT> &arg2val,
 
 1058            pb_variable<FieldT> &flag,
 
 1059            pb_variable<FieldT> &result,
 
 1060            pb_variable<FieldT> &result_flag) -> ALU_umul_gadget<FieldT> * {
 
 1061             pb_variable<FieldT> umulh_result;
 
 1062             umulh_result.allocate(pb, "umulh_result");
 
 1063             pb_variable<FieldT> umulh_flag;
 
 1064             umulh_flag.allocate(pb, "umulh_flag");
 
 1065             return new ALU_umul_gadget<FieldT>(
 
 1078         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
 1079             return (x * y) % (1ul << w);
 
 1081         [w](size_t, bool, size_t x, size_t y) -> bool {
 
 1082             return ((x * y) >> w) != 0;
 
 1084     libff::print_time("mull tests successful");
 
 1087 template<typename FieldT> void test_ALU_umulh_gadget(const size_t w)
 
 1089     libff::print_time("starting umulh test");
 
 1090     brute_force_arithmetic_gadget<ALU_umul_gadget<FieldT>, FieldT>(
 
 1092         tinyram_opcode_UMULH,
 
 1093         [](tinyram_protoboard<FieldT> &pb,
 
 1094            pb_variable_array<FieldT> &opcode_indicators,
 
 1095            word_variable_gadget<FieldT> &desval,
 
 1096            word_variable_gadget<FieldT> &arg1val,
 
 1097            word_variable_gadget<FieldT> &arg2val,
 
 1098            pb_variable<FieldT> &flag,
 
 1099            pb_variable<FieldT> &result,
 
 1100            pb_variable<FieldT> &result_flag) -> ALU_umul_gadget<FieldT> * {
 
 1101             pb_variable<FieldT> mull_result;
 
 1102             mull_result.allocate(pb, "mull_result");
 
 1103             pb_variable<FieldT> mull_flag;
 
 1104             mull_flag.allocate(pb, "mull_flag");
 
 1105             return new ALU_umul_gadget<FieldT>(
 
 1118         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
 1119             return (x * y) >> w;
 
 1121         [w](size_t, bool, size_t x, size_t y) -> bool {
 
 1122             return ((x * y) >> w) != 0;
 
 1124     libff::print_time("umulh tests successful");
 
 1127 template<typename FieldT>
 
 1128 void ALU_smul_gadget<FieldT>::generate_r1cs_constraints()
 
 1130     /* do multiplication */
 
 1132       from two's complement: (packed - 2^w * bits[w-1])
 
 1133       to two's complement: lower order bits of 2^{2w} + result_of_*
 
 1136     linear_combination<FieldT> a, b, c;
 
 1137     a.add_term(this->arg1val.packed, 1);
 
 1139         this->arg1val.bits[this->pb.ap.w - 1], -(FieldT(2) ^ this->pb.ap.w));
 
 1140     b.add_term(this->arg2val.packed, 1);
 
 1142         this->arg2val.bits[this->pb.ap.w - 1], -(FieldT(2) ^ this->pb.ap.w));
 
 1143     c.add_term(mul_result.packed, 1);
 
 1144     c.add_term(ONE, -(FieldT(2) ^ (2 * this->pb.ap.w)));
 
 1145     this->pb.add_r1cs_constraint(
 
 1146         r1cs_constraint<FieldT>(a, b, c),
 
 1147         FMT(this->annotation_prefix, " main_constraint"));
 
 1149     mul_result.generate_r1cs_constraints(true);
 
 1152     pack_smulh_result->generate_r1cs_constraints(false);
 
 1155     pack_top->generate_r1cs_constraints(false);
 
 1158       the gadgets below are FieldT specific:
 
 1163       if X != 0 then R = 0 and I = X^{-1}
 
 1165     this->pb.add_r1cs_constraint(
 
 1166         r1cs_constraint<FieldT>(
 
 1167             {is_top_empty_aux}, {top}, {ONE, is_top_empty * (-1)}),
 
 1168         FMT(this->annotation_prefix, " I*X=1-R (is_top_empty)"));
 
 1169     this->pb.add_r1cs_constraint(
 
 1170         r1cs_constraint<FieldT>({is_top_empty}, {top}, {ONE * 0}),
 
 1171         FMT(this->annotation_prefix, " R*X=0 (is_top_full)"));
 
 1173     this->pb.add_r1cs_constraint(
 
 1174         r1cs_constraint<FieldT>(
 
 1176             {top, ONE * (1l - (1ul << (this->pb.ap.w + 1)))},
 
 1177             {ONE, is_top_full * (-1)}),
 
 1178         FMT(this->annotation_prefix, " I*X=1-R (is_top_full)"));
 
 1179     this->pb.add_r1cs_constraint(
 
 1180         r1cs_constraint<FieldT>(
 
 1182             {top, ONE * (1l - (1ul << (this->pb.ap.w + 1)))},
 
 1184         FMT(this->annotation_prefix, " R*X=0 (is_top_full)"));
 
 1186     /* smulh_flag = 1 - (is_top_full + is_top_empty) */
 
 1187     this->pb.add_r1cs_constraint(
 
 1188         r1cs_constraint<FieldT>(
 
 1190             {ONE, is_top_full * (-1), is_top_empty * (-1)},
 
 1192         FMT(this->annotation_prefix, " smulh_flag"));
 
 1195 template<typename FieldT> void ALU_smul_gadget<FieldT>::generate_r1cs_witness()
 
 1197     /* do multiplication */
 
 1199       from two's complement: (packed - 2^w * bits[w-1])
 
 1200       to two's complement: lower order bits of (2^{2w} + result_of_mul)
 
 1202     this->pb.val(mul_result.packed) =
 
 1203         (this->pb.val(this->arg1val.packed) -
 
 1204          (this->pb.val(this->arg1val.bits[this->pb.ap.w - 1]) *
 
 1205           (FieldT(2) ^ this->pb.ap.w))) *
 
 1206             (this->pb.val(this->arg2val.packed) -
 
 1207              (this->pb.val(this->arg2val.bits[this->pb.ap.w - 1]) *
 
 1208               (FieldT(2) ^ this->pb.ap.w))) +
 
 1209         (FieldT(2) ^ (2 * this->pb.ap.w));
 
 1211     mul_result.generate_r1cs_witness_from_packed();
 
 1214     pack_smulh_result->generate_r1cs_witness_from_bits();
 
 1217     pack_top->generate_r1cs_witness_from_bits();
 
 1218     size_t topval = this->pb.val(top).as_ulong();
 
 1221         this->pb.val(is_top_empty) = FieldT::one();
 
 1222         this->pb.val(is_top_empty_aux) = FieldT::zero();
 
 1224         this->pb.val(is_top_empty) = FieldT::zero();
 
 1225         this->pb.val(is_top_empty_aux) = this->pb.val(top).inverse();
 
 1228     if (topval == ((1ul << (this->pb.ap.w + 1)) - 1)) {
 
 1229         this->pb.val(is_top_full) = FieldT::one();
 
 1230         this->pb.val(is_top_full_aux) = FieldT::zero();
 
 1232         this->pb.val(is_top_full) = FieldT::zero();
 
 1233         this->pb.val(is_top_full_aux) =
 
 1234             (this->pb.val(top) - FieldT((1ul << (this->pb.ap.w + 1)) - 1))
 
 1238     /* smulh_flag = 1 - (is_top_full + is_top_empty) */
 
 1239     this->pb.val(smulh_flag) = FieldT::one() - (this->pb.val(is_top_full) +
 
 1240                                                 this->pb.val(is_top_empty));
 
 1243 template<typename FieldT> void test_ALU_smulh_gadget(const size_t w)
 
 1245     libff::print_time("starting smulh test");
 
 1246     brute_force_arithmetic_gadget<ALU_smul_gadget<FieldT>, FieldT>(
 
 1248         tinyram_opcode_SMULH,
 
 1249         [](tinyram_protoboard<FieldT> &pb,
 
 1250            pb_variable_array<FieldT> &opcode_indicators,
 
 1251            word_variable_gadget<FieldT> &desval,
 
 1252            word_variable_gadget<FieldT> &arg1val,
 
 1253            word_variable_gadget<FieldT> &arg2val,
 
 1254            pb_variable<FieldT> &flag,
 
 1255            pb_variable<FieldT> &result,
 
 1256            pb_variable<FieldT> &result_flag) -> ALU_smul_gadget<FieldT> * {
 
 1257             return new ALU_smul_gadget<FieldT>(
 
 1268         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
 1269             const size_t res = libff::to_twos_complement(
 
 1270                 (libff::from_twos_complement(x, w) *
 
 1271                  libff::from_twos_complement(y, w)),
 
 1275         [w](size_t, bool, size_t x, size_t y) -> bool {
 
 1276             const int res = libff::from_twos_complement(x, w) *
 
 1277                             libff::from_twos_complement(y, w);
 
 1278             const int truncated_res = libff::from_twos_complement(
 
 1279                 libff::to_twos_complement(res, 2 * w) & ((1ul << w) - 1), w);
 
 1280             return (res != truncated_res);
 
 1282     libff::print_time("smulh tests successful");
 
 1285 template<typename FieldT>
 
 1286 void ALU_divmod_gadget<FieldT>::generate_r1cs_constraints()
 
 1288     /* B_inv * B = B_nonzero */
 
 1289     linear_combination<FieldT> a1, b1, c1;
 
 1290     a1.add_term(B_inv, 1);
 
 1291     b1.add_term(this->arg2val.packed, 1);
 
 1292     c1.add_term(B_nonzero, 1);
 
 1294     this->pb.add_r1cs_constraint(
 
 1295         r1cs_constraint<FieldT>(a1, b1, c1),
 
 1296         FMT(this->annotation_prefix, " B_inv*B=B_nonzero"));
 
 1298     /* (1-B_nonzero) * B = 0 */
 
 1299     linear_combination<FieldT> a2, b2, c2;
 
 1300     a2.add_term(ONE, 1);
 
 1301     a2.add_term(B_nonzero, -1);
 
 1302     b2.add_term(this->arg2val.packed, 1);
 
 1303     c2.add_term(ONE, 0);
 
 1305     this->pb.add_r1cs_constraint(
 
 1306         r1cs_constraint<FieldT>(a2, b2, c2),
 
 1307         FMT(this->annotation_prefix, " (1-B_nonzero)*B=0"));
 
 1309     /* B * q + r = A_aux = A * B_nonzero */
 
 1310     linear_combination<FieldT> a3, b3, c3;
 
 1311     a3.add_term(this->arg2val.packed, 1);
 
 1312     b3.add_term(udiv_result, 1);
 
 1313     c3.add_term(A_aux, 1);
 
 1314     c3.add_term(umod_result, -1);
 
 1316     this->pb.add_r1cs_constraint(
 
 1317         r1cs_constraint<FieldT>(a3, b3, c3),
 
 1318         FMT(this->annotation_prefix, " B*q+r=A_aux"));
 
 1320     linear_combination<FieldT> a4, b4, c4;
 
 1321     a4.add_term(this->arg1val.packed, 1);
 
 1322     b4.add_term(B_nonzero, 1);
 
 1323     c4.add_term(A_aux, 1);
 
 1325     this->pb.add_r1cs_constraint(
 
 1326         r1cs_constraint<FieldT>(a4, b4, c4),
 
 1327         FMT(this->annotation_prefix, " A_aux=A*B_nonzero"));
 
 1329     /* q * (1-B_nonzero) = 0 */
 
 1330     linear_combination<FieldT> a5, b5, c5;
 
 1331     a5.add_term(udiv_result, 1);
 
 1332     b5.add_term(ONE, 1);
 
 1333     b5.add_term(B_nonzero, -1);
 
 1334     c5.add_term(ONE, 0);
 
 1336     this->pb.add_r1cs_constraint(
 
 1337         r1cs_constraint<FieldT>(a5, b5, c5),
 
 1338         FMT(this->annotation_prefix, " q*B_nonzero=0"));
 
 1340     /* A<B_gadget<FieldT>(B, r, less=B_nonzero, leq=ONE) */
 
 1341     r_less_B->generate_r1cs_constraints();
 
 1344 template<typename FieldT>
 
 1345 void ALU_divmod_gadget<FieldT>::generate_r1cs_witness()
 
 1347     if (this->pb.val(this->arg2val.packed) == FieldT::zero()) {
 
 1348         this->pb.val(B_inv) = FieldT::zero();
 
 1349         this->pb.val(B_nonzero) = FieldT::zero();
 
 1351         this->pb.val(A_aux) = FieldT::zero();
 
 1353         this->pb.val(udiv_result) = FieldT::zero();
 
 1354         this->pb.val(umod_result) = FieldT::zero();
 
 1356         this->pb.val(udiv_flag) = FieldT::one();
 
 1357         this->pb.val(umod_flag) = FieldT::one();
 
 1359         this->pb.val(B_inv) = this->pb.val(this->arg2val.packed).inverse();
 
 1360         this->pb.val(B_nonzero) = FieldT::one();
 
 1362         const size_t A = this->pb.val(this->arg1val.packed).as_ulong();
 
 1363         const size_t B = this->pb.val(this->arg2val.packed).as_ulong();
 
 1365         this->pb.val(A_aux) = this->pb.val(this->arg1val.packed);
 
 1367         this->pb.val(udiv_result) = FieldT(A / B);
 
 1368         this->pb.val(umod_result) = FieldT(A % B);
 
 1370         this->pb.val(udiv_flag) = FieldT::zero();
 
 1371         this->pb.val(umod_flag) = FieldT::zero();
 
 1374     r_less_B->generate_r1cs_witness();
 
 1377 template<typename FieldT> void test_ALU_udiv_gadget(const size_t w)
 
 1379     libff::print_time("starting udiv test");
 
 1380     brute_force_arithmetic_gadget<ALU_divmod_gadget<FieldT>, FieldT>(
 
 1382         tinyram_opcode_UDIV,
 
 1383         [](tinyram_protoboard<FieldT> &pb,
 
 1384            pb_variable_array<FieldT> &opcode_indicators,
 
 1385            word_variable_gadget<FieldT> &desval,
 
 1386            word_variable_gadget<FieldT> &arg1val,
 
 1387            word_variable_gadget<FieldT> &arg2val,
 
 1388            pb_variable<FieldT> &flag,
 
 1389            pb_variable<FieldT> &result,
 
 1390            pb_variable<FieldT> &result_flag) -> ALU_divmod_gadget<FieldT> * {
 
 1391             pb_variable<FieldT> umod_result;
 
 1392             umod_result.allocate(pb, "umod_result");
 
 1393             pb_variable<FieldT> umod_flag;
 
 1394             umod_flag.allocate(pb, "umod_flag");
 
 1395             return new ALU_divmod_gadget<FieldT>(
 
 1406                 "ALU_divmod_gadget");
 
 1408         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
 1409             return (y == 0 ? 0 : x / y);
 
 1411         [w](size_t, bool, size_t, size_t y) -> bool { return (y == 0); });
 
 1412     libff::print_time("udiv tests successful");
 
 1415 template<typename FieldT> void test_ALU_umod_gadget(const size_t w)
 
 1417     libff::print_time("starting umod test");
 
 1418     brute_force_arithmetic_gadget<ALU_divmod_gadget<FieldT>, FieldT>(
 
 1420         tinyram_opcode_UMOD,
 
 1421         [](tinyram_protoboard<FieldT> &pb,
 
 1422            pb_variable_array<FieldT> &opcode_indicators,
 
 1423            word_variable_gadget<FieldT> &desval,
 
 1424            word_variable_gadget<FieldT> &arg1val,
 
 1425            word_variable_gadget<FieldT> &arg2val,
 
 1426            pb_variable<FieldT> &flag,
 
 1427            pb_variable<FieldT> &result,
 
 1428            pb_variable<FieldT> &result_flag) -> ALU_divmod_gadget<FieldT> * {
 
 1429             pb_variable<FieldT> udiv_result;
 
 1430             udiv_result.allocate(pb, "udiv_result");
 
 1431             pb_variable<FieldT> udiv_flag;
 
 1432             udiv_flag.allocate(pb, "udiv_flag");
 
 1433             return new ALU_divmod_gadget<FieldT>(
 
 1444                 "ALU_divmod_gadget");
 
 1446         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
 1447             return (y == 0 ? 0 : x % y);
 
 1449         [w](size_t, bool, size_t, size_t y) -> bool { return (y == 0); });
 
 1450     libff::print_time("umod tests successful");
 
 1453 template<typename FieldT>
 
 1454 void ALU_shr_shl_gadget<FieldT>::generate_r1cs_constraints()
 
 1457       select the input for barrel shifter:
 
 1459       r = arg1val * opcode_indicators[SHR] + reverse(arg1val) *
 
 1460       (1-opcode_indicators[SHR]) r - reverse(arg1val) = (arg1val -
 
 1461       reverse(arg1val)) * opcode_indicators[SHR]
 
 1463     pack_reversed_input->generate_r1cs_constraints(false);
 
 1465     this->pb.add_r1cs_constraint(
 
 1466         r1cs_constraint<FieldT>(
 
 1467             {this->arg1val.packed, reversed_input * (-1)},
 
 1468             {this->opcode_indicators[tinyram_opcode_SHR]},
 
 1469             {barrel_right_internal[0], reversed_input * (-1)}),
 
 1470         FMT(this->annotation_prefix, " select_arg1val_or_reversed"));
 
 1473       do logw iterations of barrel shifts
 
 1475     for (size_t i = 0; i < logw; ++i) {
 
 1476         /* assert that shifted out part is bits */
 
 1477         for (size_t j = 0; j < 1ul << i; ++j) {
 
 1478             generate_boolean_r1cs_constraint<FieldT>(
 
 1480                 shifted_out_bits[i][j],
 
 1481                 FMT(this->annotation_prefix,
 
 1482                     " shifted_out_bits_%zu_%zu",
 
 1488           add main shifting constraint
 
 1492           (shifted_result * 2^(i+1) + shifted_out_part) * need_to_shift +
 
 1493           (shfited_result) * (1-need_to_shift)
 
 1495           old_result - shifted_result = (shifted_result * (2^(i+1) - 1) +
 
 1496           shifted_out_part) * need_to_shift
 
 1498         linear_combination<FieldT> a, b, c;
 
 1501             barrel_right_internal[i + 1],
 
 1502             (FieldT(2) ^ (i + 1)) - FieldT::one());
 
 1503         for (size_t j = 0; j < 1ul << i; ++j) {
 
 1504             a.add_term(shifted_out_bits[i][j], (FieldT(2) ^ j));
 
 1507         b.add_term(this->arg2val.bits[i], 1);
 
 1509         c.add_term(barrel_right_internal[i], 1);
 
 1510         c.add_term(barrel_right_internal[i + 1], -1);
 
 1512         this->pb.add_r1cs_constraint(
 
 1513             r1cs_constraint<FieldT>(a, b, c),
 
 1514             FMT(this->annotation_prefix, " barrel_shift_%zu", i));
 
 1518       get result as the logw iterations or zero if shift was oversized
 
 1520       result = (1-is_oversize_shift) * barrel_right_internal[logw]
 
 1522     check_oversize_shift->generate_r1cs_constraints();
 
 1523     this->pb.add_r1cs_constraint(
 
 1524         r1cs_constraint<FieldT>(
 
 1525             {ONE, is_oversize_shift * (-1)},
 
 1526             {barrel_right_internal[logw]},
 
 1528         FMT(this->annotation_prefix, " result"));
 
 1531       get reversed result for SHL
 
 1533     unpack_result->generate_r1cs_constraints(true);
 
 1534     pack_reversed_result->generate_r1cs_constraints(false);
 
 1537       select the correct output:
 
 1538       r = result * opcode_indicators[SHR] + reverse(result) *
 
 1539       (1-opcode_indicators[SHR]) r - reverse(result) = (result -
 
 1540       reverse(result)) * opcode_indicators[SHR]
 
 1542     this->pb.add_r1cs_constraint(
 
 1543         r1cs_constraint<FieldT>(
 
 1544             {this->result, reversed_result * (-1)},
 
 1545             {this->opcode_indicators[tinyram_opcode_SHR]},
 
 1546             {shr_result, reversed_result * (-1)}),
 
 1547         FMT(this->annotation_prefix, " shr_result"));
 
 1549     this->pb.add_r1cs_constraint(
 
 1550         r1cs_constraint<FieldT>(
 
 1551             {this->result, reversed_result * (-1)},
 
 1552             {this->opcode_indicators[tinyram_opcode_SHR]},
 
 1553             {shr_result, reversed_result * (-1)}),
 
 1554         FMT(this->annotation_prefix, " shl_result"));
 
 1556     this->pb.add_r1cs_constraint(
 
 1557         r1cs_constraint<FieldT>({ONE}, {this->arg1val.bits[0]}, {shr_flag}),
 
 1558         FMT(this->annotation_prefix, " shr_flag"));
 
 1560     this->pb.add_r1cs_constraint(
 
 1561         r1cs_constraint<FieldT>(
 
 1562             {ONE}, {this->arg1val.bits[this->pb.ap.w - 1]}, {shl_flag}),
 
 1563         FMT(this->annotation_prefix, " shl_flag"));
 
 1566 template<typename FieldT>
 
 1567 void ALU_shr_shl_gadget<FieldT>::generate_r1cs_witness()
 
 1569     /* select the input for barrel shifter */
 
 1570     pack_reversed_input->generate_r1cs_witness_from_bits();
 
 1572     this->pb.val(barrel_right_internal[0]) =
 
 1573         (this->pb.val(this->opcode_indicators[tinyram_opcode_SHR]) ==
 
 1575              ? this->pb.val(this->arg1val.packed)
 
 1576              : this->pb.val(reversed_input));
 
 1579       do logw iterations of barrel shifts.
 
 1582       (shifted_result * 2^i + shifted_out_part) * need_to_shift +
 
 1583       (shfited_result) * (1-need_to_shift)
 
 1586     for (size_t i = 0; i < logw; ++i) {
 
 1587         this->pb.val(barrel_right_internal[i + 1]) =
 
 1588             (this->pb.val(this->arg2val.bits[i]) == FieldT::zero())
 
 1589                 ? this->pb.val(barrel_right_internal[i])
 
 1591                       this->pb.val(barrel_right_internal[i]).as_ulong() >>
 
 1594         shifted_out_bits[i].fill_with_bits_of_ulong(
 
 1596             this->pb.val(barrel_right_internal[i]).as_ulong() % (2u << i));
 
 1600       get result as the logw iterations or zero if shift was oversized
 
 1602       result = (1-is_oversize_shift) * barrel_right_internal[logw]
 
 1604     check_oversize_shift->generate_r1cs_witness();
 
 1605     this->pb.val(this->result) =
 
 1606         (FieldT::one() - this->pb.val(is_oversize_shift)) *
 
 1607         this->pb.val(barrel_right_internal[logw]);
 
 1610       get reversed result for SHL
 
 1612     unpack_result->generate_r1cs_witness_from_packed();
 
 1613     pack_reversed_result->generate_r1cs_witness_from_bits();
 
 1616       select the correct output:
 
 1617       r = result * opcode_indicators[SHR] + reverse(result) *
 
 1618       (1-opcode_indicators[SHR]) r - reverse(result) = (result -
 
 1619       reverse(result)) * opcode_indicators[SHR]
 
 1621     this->pb.val(shr_result) =
 
 1622         (this->pb.val(this->opcode_indicators[tinyram_opcode_SHR]) ==
 
 1624             ? this->pb.val(this->result)
 
 1625             : this->pb.val(reversed_result);
 
 1627     this->pb.val(shl_result) = this->pb.val(shr_result);
 
 1628     this->pb.val(shr_flag) = this->pb.val(this->arg1val.bits[0]);
 
 1629     this->pb.val(shl_flag) =
 
 1630         this->pb.val(this->arg1val.bits[this->pb.ap.w - 1]);
 
 1633 template<typename FieldT> void test_ALU_shr_gadget(const size_t w)
 
 1635     libff::print_time("starting shr test");
 
 1636     brute_force_arithmetic_gadget<ALU_shr_shl_gadget<FieldT>, FieldT>(
 
 1639         [](tinyram_protoboard<FieldT> &pb,
 
 1640            pb_variable_array<FieldT> &opcode_indicators,
 
 1641            word_variable_gadget<FieldT> &desval,
 
 1642            word_variable_gadget<FieldT> &arg1val,
 
 1643            word_variable_gadget<FieldT> &arg2val,
 
 1644            pb_variable<FieldT> &flag,
 
 1645            pb_variable<FieldT> &result,
 
 1646            pb_variable<FieldT> &result_flag) -> ALU_shr_shl_gadget<FieldT> * {
 
 1647             pb_variable<FieldT> shl_result;
 
 1648             shl_result.allocate(pb, "shl_result");
 
 1649             pb_variable<FieldT> shl_flag;
 
 1650             shl_flag.allocate(pb, "shl_flag");
 
 1651             return new ALU_shr_shl_gadget<FieldT>(
 
 1662                 "ALU_shr_shl_gadget");
 
 1664         [w](size_t, bool, size_t x, size_t y) -> size_t { return (x >> y); },
 
 1665         [w](size_t, bool, size_t x, size_t) -> bool { return (x & 1); });
 
 1666     libff::print_time("shr tests successful");
 
 1669 template<typename FieldT> void test_ALU_shl_gadget(const size_t w)
 
 1671     libff::print_time("starting shl test");
 
 1672     brute_force_arithmetic_gadget<ALU_shr_shl_gadget<FieldT>, FieldT>(
 
 1675         [](tinyram_protoboard<FieldT> &pb,
 
 1676            pb_variable_array<FieldT> &opcode_indicators,
 
 1677            word_variable_gadget<FieldT> &desval,
 
 1678            word_variable_gadget<FieldT> &arg1val,
 
 1679            word_variable_gadget<FieldT> &arg2val,
 
 1680            pb_variable<FieldT> &flag,
 
 1681            pb_variable<FieldT> &result,
 
 1682            pb_variable<FieldT> &result_flag) -> ALU_shr_shl_gadget<FieldT> * {
 
 1683             pb_variable<FieldT> shr_result;
 
 1684             shr_result.allocate(pb, "shr_result");
 
 1685             pb_variable<FieldT> shr_flag;
 
 1686             shr_flag.allocate(pb, "shr_flag");
 
 1687             return new ALU_shr_shl_gadget<FieldT>(
 
 1698                 "ALU_shr_shl_gadget");
 
 1700         [w](size_t, bool, size_t x, size_t y) -> size_t {
 
 1701             return (x << y) & ((1ul << w) - 1);
 
 1703         [w](size_t, bool, size_t x, size_t) -> bool { return (x >> (w - 1)); });
 
 1704     libff::print_time("shl tests successful");
 
 1707 } // namespace libsnark