// File: /workspace/github_cpp_makefile_star2_O1/vsklad[P]cgen/./bal/encoding/literaladd.hpp
// Function: void bal::literal_word_add<32ul, bal::Literal<bal::Cnf>, &bal::add(bal::Cnf&, unsigned int*, unsigned long, unsigned long, unsigned int, bool)>(bal::GF2NElement<32ul, bal::Literal<bal::Cnf>>*, bal::GF2NElement<32ul, bal::Literal<bal::Cnf>> const* const*, unsigned long)
inline void literal_word_add(GF2NElement<N, GF2E>* result,
                                 const GF2NElement<N, GF2E>* const args[], const std::size_t args_size) {
        // formula instance to generate the clauses for
        // determined from the first variable argument and must be the same for all
        typename GF2E::Formula* p_formula = nullptr;
        
        // there is a constant carry and variable carry, for each carry bit
        // constant is a mix of constant operand values and constant carry
        unsigned constant = 0;
        // lists to keep carry
        std::vector<literalid_t> carry_in;
        std::vector<literalid_t> carry_out_1;
        std::vector<literalid_t> carry_out_21;
        std::vector<literalid_t> carry_out_22;
        
        // variables that are added
        std::vector<literalid_t> variables;
        
        for (auto i = 0; i < N; i++) {
            // determine number of variables
            // take carry out from the previous bit
            for (auto j = 0; j < carry_in.size(); j++) {
                add_append_variable_(carry_in[j], variables, carry_out_1, constant);
            };
            // filter out variables from args
            for (auto j = 0; j < args_size; j++) {
                const literalid_t args_ji = (*args[j])[i]->value;
                if (literal_t__is_variable(args_ji)) {
                    if (p_formula == nullptr) {
                        p_formula = (*args[j])[i]->formula;
                    } else {
                        _assert_level_1((*args[j])[i]->formula == p_formula);
                    };
                };
                
                add_append_variable_(args_ji, variables, carry_out_1, constant);
            };
            
            std::size_t input_size = variables.size();
            
            if ((i == N - 1) || input_size < 2) {
                // no need for carry for the last bit, only assign result bit
                // also, eor takes care of zero or single variable arguments;
                // make sure the args are sorted however
#if defined(XOR_ARGS_ORDER_ASCENDING)
                std::sort(variables.begin(), variables.end());
#elif defined(XOR_ARGS_ORDER_DESCENDING)
                std::sort(variables.begin(), variables.end(), [](const literalid_t a, const literalid_t b) { return a > b; });
#endif
                (*result)[i] = GF2E::from_literal(p_formula, eor(p_formula, variables.data(), input_size, constant & 0b1));
                
                if ((i < N - 1) && (input_size == 1) && (constant & 0b1)) {
                    add_append_carry_(variables[0], carry_out_1);
                };
            } else {
                // for SHA1/SHA256, 6 variables is the max possible
                // addition of 4 variables + 1 constant
                // i.e. 4 input + 2 carry in
                // for other purposes, ADD_MAP may be extended
                
                // determine number of variable to add at once
                // there are at least 2 variables
                // formula must always be assigned for a variable
                _assert_level_1(p_formula != nullptr);
                const std::size_t batch_size = p_formula->get_add_max_args();
#ifdef __ADD_NETWORK_REDUCE_DIAMETER
                // now take batches of variables and add them up
                // from low to high indexes, result is appended to the end
                std::size_t current_index = 0;
                
                while (current_index <  input_size) {
                    const std::size_t current_batch_size = (input_size - current_index) > batch_size ? batch_size : input_size - current_index;
                    
                    // add constant to the last batch
                    WordValue c0b1 = ((current_index + current_batch_size == input_size) && (constant & 0x1));
                    bool is_2nd_carry = (((current_batch_size > 3) || (current_batch_size == 3 && c0b1)) && (i < N - 2));
                    const addition_map_entry_t& map = ADD_MAP[current_batch_size][c0b1][is_2nd_carry];
                    
                    const std::size_t output_size = is_2nd_carry ? 3 : 2;
                    
                    std::vector<BIT*> input;
                    for (auto i = 0; i < current_batch_size; i++) {
                        input.push_back(variables[current_index + i]);
                    };
                    
                    r.new_instance(); // make new result bit
                    input.push_back(r);
                    carry_out_1.push_back(Ref<BIT>()); // make new carry out 1
                    carry_out_1.back().new_instance();
                    input.push_back(carry_out_1.back());
                    
                    if (is_2nd_carry) {
                        carry_out_22.push_back(Ref<BIT>()); // make new carry out 2
                        carry_out_22.back().new_instance();
                        input.push_back(carry_out_22.back());
                    };
                    
                    BIT::record_clauses(map.map, map.map_size, input.data(), current_batch_size, output_size);

                    if (current_index + current_batch_size < input_size) {
                        // more batches - append r to input variables list
                        add_append_carry_(r, carry_in); // to keep reference
                        variables.push_back(r);
                        input_size++;
                    };
                    
                    current_index += current_batch_size;
                };
#else
                // now take batches of variables and add them up
                // from high to low indexes, i.e. (input_size-batch_size, input_size-1) first
                std::size_t remaining_size = input_size;
                
                while (remaining_size > 0) {
                    input_size = batch_size > remaining_size ? remaining_size : batch_size;
                    remaining_size -= input_size;
                    
                    // add constant to the last batch
                    unsigned constant_bit = (remaining_size == 0 && constant & 0x1);
                    bool b_c2 = (((input_size > 3) || (input_size == 3 && constant_bit)) && (i < N - 2));
                    bool b_2nd_c1 = false;

                    const std::size_t output_size = b_c2 ? 3 : 2;
                    
                    variables.push_back(p_formula->new_variable_literal()); // make the result bit
                    variables.push_back(p_formula->new_variable_literal()); // mace the c1 bit
                    
                    if (b_c2) {
                        variables.push_back(p_formula->new_variable_literal()); // make the c2 bit
                    };
                    
                    literalid_t* const p_args = variables.data() + remaining_size;
                    literalid_t* const p_output = p_args + input_size;
                    
                    encode_method(*p_formula, p_args, input_size, output_size, constant_bit, b_2nd_c1);
                    
                    variables[remaining_size] = p_output[0];
                    carry_out_1.push_back(p_output[1]);
                    if (b_c2) {
                        carry_out_22.push_back(p_output[2]);
                    };
                    
                    if (remaining_size > 0) {
                        remaining_size++; // +1 since the first element is the adder's result
                        variables.resize(remaining_size);
                    };
                };
#endif
                // assign the result bit
                // this will be the first variables element remaining
                (*result)[i] = new GF2E(p_formula, variables[0]);
            };
            
            // shift carry for the next iteration; move references to keep them
            carry_in.clear();
            carry_in.swap(carry_out_1);
            carry_in.insert(carry_in.end(), carry_out_21.begin(), carry_out_21.end());
            carry_out_21.clear();
            carry_out_21.swap(carry_out_22);
            
            // variables from the current iteration
            variables.clear();
            
            // remaining bits of the constant passed to the next round
            constant >>= 1;
        };
    }

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xe8, %rsp
movq %rdx, -0xf8(%rbp)
movq %rsi, %r15
leaq -0x30(%rbp), %r12
movl $0x0, (%r12)
xorl %eax, %eax
leaq -0xb0(%rbp), %rcx
movq %rax, 0x10(%rcx)
xorps %xmm0, %xmm0
movaps %xmm0, (%rcx)
leaq -0x90(%rbp), %rcx
movq %rax, 0x10(%rcx)
movaps %xmm0, (%rcx)
leaq -0xd0(%rbp), %rcx
movq %rax, 0x10(%rcx)
movaps %xmm0, (%rcx)
leaq -0x60(%rbp), %rcx
movq $0x0, -0x68(%rbp)
movq %rax, 0x10(%rcx)
movaps %xmm0, (%rcx)
movq %rdi, -0xf0(%rbp)
leaq 0x30(%rdi), %rax
movq %rax, -0xe8(%rbp)
movq $0x0, -0x38(%rbp)
xorl %ecx, %ecx
xorl %esi, %esi
xorl %edi, %edi
movq %r15, -0x110(%rbp)
movq %rdi, -0x100(%rbp)
movq %rsi, -0x108(%rbp)
movq %rcx, -0xd8(%rbp)
movq -0xb0(%rbp), %rax
cmpq %rax, -0xa8(%rbp)
movq -0xf8(%rbp), %r14
je 0x3750d
xorl %ebx, %ebx
movl (%rax,%rbx,4), %edi
leaq -0x60(%rbp), %rsi
leaq -0x90(%rbp), %rdx
movq %r12, %rcx
callq 0x37a43
incq %rbx
movq -0xb0(%rbp), %rax
movq -0xa8(%rbp), %rcx
subq %rax, %rcx
sarq $0x2, %rcx
cmpq %rbx, %rcx
ja 0x374da
testq %r14, %r14
je 0x37562
xorl %ebx, %ebx
movq (%r15,%rbx,8), %rax
movq -0x68(%rbp), %rcx
movq 0x30(%rax,%rcx,8), %rax
movl 0x20(%rax), %edi
leal -0x2(%rdi), %ecx
cmpl $-0x4, %ecx
ja 0x37547
movq 0x18(%rax), %rax
movq -0x38(%rbp), %rcx
testq %rcx, %rcx
je 0x37543
cmpq %rcx, %rax
je 0x37547
jmp 0x37994
movq %rax, -0x38(%rbp)
leaq -0x60(%rbp), %rsi
leaq -0x90(%rbp), %rdx
movq %r12, %rcx
callq 0x37a43
incq %rbx
cmpq %rbx, %r14
jne 0x37514
movq -0x60(%rbp), %r13
movq -0x58(%rbp), %r14
movq %r14, %rbx
subq %r13, %rbx
movq %rbx, %rax
sarq $0x2, %rax
cmpq $0x1f, -0x68(%rbp)
je 0x377a8
cmpq $0x2, %rax
jb 0x377a8
movq -0x38(%rbp), %rdi
testq %rdi, %rdi
je 0x379b3
movq %rax, %rbx
movl 0x70(%rdi), %eax
testl %eax, %eax
jne 0x375a9
movq (%rdi), %rax
callq *0x18(%rax)
cmpq %r13, %r14
je 0x37769
movq %rbx, %r13
movl %eax, %eax
movq %rax, -0xe0(%rbp)
cmpq $0x1e, -0x68(%rbp)
setb -0x29(%rbp)
movq -0xe0(%rbp), %r14
cmpq %r14, %r13
movq %r13, -0x70(%rbp)
cmovbq %r13, %r14
setbe %r15b
movl -0x30(%rbp), %r12d
cmpq $0x4, %r14
setae %r13b
cmpq $0x3, %r14
sete %bl
movq -0x38(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x40(%rbp)
movq -0x58(%rbp), %rsi
cmpq -0x50(%rbp), %rsi
je 0x37613
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x58(%rbp)
jmp 0x37620
leaq -0x60(%rbp), %rdi
leaq -0x40(%rbp), %rdx
callq 0xfb44
movq -0x38(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x40(%rbp)
movq -0x58(%rbp), %rsi
cmpq -0x50(%rbp), %rsi
je 0x37643
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x58(%rbp)
jmp 0x37650
leaq -0x60(%rbp), %rdi
leaq -0x40(%rbp), %rdx
callq 0xfb44
andb %r12b, %r15b
andb %r15b, %bl
orb %bl, %r13b
movb -0x29(%rbp), %bl
andb %r13b, %bl
movq -0x70(%rbp), %r13
je 0x37695
movq -0x38(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x40(%rbp)
movq -0x58(%rbp), %rsi
cmpq -0x50(%rbp), %rsi
je 0x37688
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x58(%rbp)
jmp 0x37695
leaq -0x60(%rbp), %rdi
leaq -0x40(%rbp), %rdx
callq 0xfb44
subq %r14, %r13
movzbl %r15b, %r8d
movl %ebx, %r12d
movzbl %bl, %ecx
orq $0x2, %rcx
movq -0x60(%rbp), %rbx
leaq (%rbx,%r13,4), %rsi
movq -0x38(%rbp), %rdi
movq %r14, %rdx
xorl %r9d, %r9d
callq 0x10ea9
movq -0x110(%rbp), %r15
movq -0x70(%rbp), %rax
leaq (%rbx,%rax,4), %r14
movl (%r14), %eax
movq -0x60(%rbp), %rcx
movl %eax, (%rcx,%r13,4)
leaq 0x4(%r14), %rdx
movq -0x88(%rbp), %rsi
cmpq -0x80(%rbp), %rsi
je 0x376f9
movl (%rdx), %eax
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x88(%rbp)
jmp 0x37705
leaq -0x90(%rbp), %rdi
callq 0xfc7c
testb %r12b, %r12b
je 0x3773f
addq $0x8, %r14
movq -0xc8(%rbp), %rsi
cmpq -0xc0(%rbp), %rsi
je 0x37730
movl (%r14), %eax
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0xc8(%rbp)
jmp 0x3773f
leaq -0xd0(%rbp), %rdi
movq %r14, %rdx
callq 0xfc7c
movq -0xe0(%rbp), %rax
cmpq %rax, -0x70(%rbp)
jbe 0x3775d
incq %r13
leaq -0x60(%rbp), %rdi
movq %r13, %rsi
callq 0x23056
jmp 0x37760
xorl %r13d, %r13d
testq %r13, %r13
jne 0x375be
movl $0x40, %edi
callq 0xc2c0
movq %rax, %r14
movq -0x60(%rbp), %rax
movl (%rax), %edx
movq %r14, %rdi
movq -0x38(%rbp), %rsi
callq 0x36252
movq -0x68(%rbp), %rax
movq -0xe8(%rbp), %rcx
leaq (%rcx,%rax,8), %rdi
movq %r14, %rsi
callq 0x2a030
leaq -0x30(%rbp), %r12
jmp 0x3785c
movq %rax, -0x70(%rbp)
cmpq %r14, %r13
je 0x377d5
bsrq -0x70(%rbp), %rdx
xorl $0x3f, %edx
addl %edx, %edx
xorq $0x7e, %rdx
movq %r13, %rdi
movq %r14, %rsi
callq 0x14bb8
movq %r13, %rdi
movq %r14, %rsi
callq 0x14c99
movq -0x60(%rbp), %rsi
movl -0x30(%rbp), %ecx
andl $0x1, %ecx
movq -0x38(%rbp), %r14
movq %r14, %rdi
movq -0x70(%rbp), %rdx
callq 0x369bf
leaq -0x40(%rbp), %rdi
movq %r14, %rsi
movl %eax, %edx
callq 0x361de
movq -0x40(%rbp), %rax
movq -0xf0(%rbp), %rcx
movq -0x68(%rbp), %rdx
movq %rax, 0x30(%rcx,%rdx,8)
cmpq $0x1f, %rdx
je 0x3785c
cmpq $0x4, %rbx
jne 0x3785c
testb $0x1, -0x30(%rbp)
je 0x3785c
movq -0x60(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x40(%rbp)
testl %eax, %eax
je 0x3785c
movq -0x88(%rbp), %rsi
cmpq -0x80(%rbp), %rsi
je 0x3784c
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x88(%rbp)
jmp 0x3785c
leaq -0x90(%rbp), %rdi
leaq -0x40(%rbp), %rdx
callq 0xfc7c
movq -0xb0(%rbp), %rax
cmpq %rax, -0xa8(%rbp)
je 0x37873
movq %rax, -0xa8(%rbp)
movq -0xa8(%rbp), %rcx
movq -0xa0(%rbp), %rdx
movq -0x90(%rbp), %rdi
movq -0x88(%rbp), %rsi
movq %rdi, -0xb0(%rbp)
movq %rsi, -0xa8(%rbp)
movq -0x80(%rbp), %rdi
movq %rdi, -0xa0(%rbp)
movq %rax, -0x90(%rbp)
movq %rcx, -0x88(%rbp)
movq %rdx, -0x80(%rbp)
leaq -0xb0(%rbp), %rdi
movq -0xd8(%rbp), %rbx
movq %rbx, %rdx
movq -0x108(%rbp), %rcx
callq 0x37b50
movq -0xd0(%rbp), %r14
movq -0xc8(%rbp), %rsi
movq -0xc0(%rbp), %rax
movq %rbx, -0xd0(%rbp)
movq %rbx, -0xc8(%rbp)
movq -0x100(%rbp), %rcx
movq %rcx, -0xc0(%rbp)
movq -0x60(%rbp), %rcx
cmpq %rcx, -0x58(%rbp)
je 0x37916
movq %rcx, -0x58(%rbp)
shrl -0x30(%rbp)
movq -0x68(%rbp), %rdx
incq %rdx
movq %r14, %rcx
movq %rax, %rdi
movq %rdx, -0x68(%rbp)
cmpq $0x20, %rdx
jne 0x374ac
movq -0x60(%rbp), %rdi
testq %rdi, %rdi
je 0x37942
callq 0xc280
movq -0xd0(%rbp), %rdi
testq %rdi, %rdi
je 0x37953
callq 0xc280
testq %r14, %r14
je 0x37960
movq %r14, %rdi
callq 0xc280
movq -0x90(%rbp), %rdi
testq %rdi, %rdi
je 0x37971
callq 0xc280
movq -0xb0(%rbp), %rdi
testq %rdi, %rdi
je 0x37982
callq 0xc280
addq $0xe8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x14d71(%rip), %rdi     # 0x4c70c
leaq 0x14d8e(%rip), %rsi     # 0x4c730
leaq 0x14da5(%rip), %rcx     # 0x4c74e
movl $0x75, %edx
callq 0xc180
leaq 0x14e44(%rip), %rdi     # 0x4c7fe
leaq 0x14d6f(%rip), %rsi     # 0x4c730
leaq 0x14d86(%rip), %rcx     # 0x4c74e
movl $0x95, %edx
callq 0xc180
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
jmp 0x379e6
movq %rax, %rbx
movq -0x60(%rbp), %rdi
testq %rdi, %rdi
je 0x379f7
callq 0xc280
movq -0xd0(%rbp), %rdi
testq %rdi, %rdi
je 0x37a08
callq 0xc280
movq -0xd8(%rbp), %rdi
testq %rdi, %rdi
je 0x37a19
callq 0xc280
movq -0x90(%rbp), %rdi
testq %rdi, %rdi
je 0x37a2a
callq 0xc280
movq -0xb0(%rbp), %rdi
testq %rdi, %rdi
je 0x37a3b
callq 0xc280
movq %rbx, %rdi
callq 0xc4e0