// File: /workspace/github_cpp_makefile_star2_O2/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, %r14
leaq -0x3c(%rbp), %r15
andl $0x0, (%r15)
movq %rsi, %r12
xorps %xmm0, %xmm0
leaq -0xb0(%rbp), %rax
movaps %xmm0, (%rax)
andq $0x0, 0x10(%rax)
leaq -0x90(%rbp), %rax
andq $0x0, 0x10(%rax)
movaps %xmm0, (%rax)
andq $0x0, -0xc0(%rbp)
leaq -0xf0(%rbp), %rax
andq $0x0, 0x10(%rax)
movaps %xmm0, -0xd0(%rbp)
movaps %xmm0, (%rax)
leaq -0x70(%rbp), %rax
movaps %xmm0, (%rax)
andq $0x0, 0x10(%rax)
addq $0x30, %rdi
movq %rdi, -0xf8(%rbp)
xorl %eax, %eax
xorl %ebx, %ebx
movq %rdx, -0x108(%rbp)
movq %rsi, -0x110(%rbp)
movq %rax, -0x50(%rbp)
cmpq $0x20, %rax
je 0x2b225
movq %rbx, -0x38(%rbp)
xorl %ebx, %ebx
movq -0xb0(%rbp), %rax
movq -0xa8(%rbp), %rcx
subq %rax, %rcx
sarq $0x2, %rcx
cmpq %rbx, %rcx
jbe 0x2aebf
movl (%rax,%rbx,4), %edi
leaq -0x70(%rbp), %rsi
leaq -0x90(%rbp), %rdx
movq %r15, %rcx
callq 0x2b304
incq %rbx
jmp 0x2ae8a
xorl %ebx, %ebx
cmpq %rbx, %r14
je 0x2af11
movq (%r12,%rbx,8), %rax
movq -0x50(%rbp), %rcx
movq 0x30(%rax,%rcx,8), %rax
movl 0x20(%rax), %edi
leal -0x2(%rdi), %ecx
cmpl $-0x4, %ecx
ja 0x2aef9
movq 0x18(%rax), %rax
movq -0x38(%rbp), %rcx
testq %rcx, %rcx
je 0x2aef5
cmpq %rcx, %rax
je 0x2aef9
jmp 0x2b270
movq %rax, -0x38(%rbp)
leaq -0x70(%rbp), %rsi
leaq -0x90(%rbp), %rdx
movq %r15, %rcx
callq 0x2b304
incq %rbx
jmp 0x2aec1
movq -0x70(%rbp), %rdi
movq -0x68(%rbp), %rsi
movq %rsi, %r13
subq %rdi, %r13
movq %r13, %rbx
sarq $0x2, %rbx
cmpq $0x1f, -0x50(%rbp)
je 0x2b098
cmpq $0x2, %rbx
jb 0x2b098
movq -0x38(%rbp), %rdi
testq %rdi, %rdi
je 0x2b28d
movl 0x70(%rdi), %eax
testl %eax, %eax
jne 0x2af55
movq (%rdi), %rax
callq *0x18(%rax)
movl %eax, %eax
movq %rax, -0x100(%rbp)
movq %rbx, %rax
testq %rax, %rax
je 0x2b11c
cmpq $0x1e, -0x50(%rbp)
setb -0x29(%rbp)
movq -0x100(%rbp), %rbx
cmpq %rbx, %rax
movq %rax, -0x78(%rbp)
cmovbq %rax, %rbx
setbe %r14b
movl -0x3c(%rbp), %r13d
cmpq $0x4, %rbx
setae %r15b
cmpq $0x3, %rbx
sete %r12b
movq -0x38(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x48(%rbp)
leaq -0x70(%rbp), %rdi
leaq -0x48(%rbp), %rsi
callq 0xe0d2
movq -0x38(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x48(%rbp)
leaq -0x70(%rbp), %rdi
leaq -0x48(%rbp), %rsi
callq 0xe0d2
andb %r13b, %r14b
andb %r14b, %r12b
orb %r12b, %r15b
movb -0x29(%rbp), %al
andb %r15b, %al
je 0x2b002
movl %eax, %r12d
movq -0x38(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x48(%rbp)
leaq -0x70(%rbp), %rdi
leaq -0x48(%rbp), %rsi
callq 0xe0d2
movl %r12d, %eax
movq -0x78(%rbp), %r12
movq %r12, %r15
subq %rbx, %r15
movzbl %r14b, %r8d
movl %eax, %r13d
movzbl %al, %ecx
orq $0x2, %rcx
movq -0x70(%rbp), %r14
leaq (%r14,%r15,4), %rsi
movq -0x38(%rbp), %rdi
movq %rbx, %rdx
xorl %r9d, %r9d
callq 0xf10c
leaq (%r14,%r12,4), %rbx
movl (%rbx), %eax
movq -0x70(%rbp), %rcx
movl %eax, (%rcx,%r15,4)
leaq 0x4(%rbx), %rsi
leaq -0x90(%rbp), %rdi
callq 0xd89e
movq -0x78(%rbp), %rax
testb %r13b, %r13b
je 0x2b06f
addq $0x8, %rbx
leaq -0xf0(%rbp), %rdi
movq %rbx, %rsi
callq 0xd89e
movq -0x78(%rbp), %rax
cmpq -0x100(%rbp), %rax
movl $0x0, %eax
jbe 0x2af61
incq %r15
leaq -0x70(%rbp), %rdi
movq %r15, %rsi
callq 0x1c4d8
movq %r15, %rax
jmp 0x2af61
movq %rbx, -0x78(%rbp)
callq 0x12036
movq -0x38(%rbp), %rbx
movq -0x70(%rbp), %rsi
movl -0x3c(%rbp), %ecx
andl $0x1, %ecx
movq %rbx, %rdi
movq -0x78(%rbp), %rdx
callq 0x2a3e0
movq %rbx, -0x38(%rbp)
leaq -0x48(%rbp), %rbx
movq %rbx, %rdi
movq -0x38(%rbp), %rsi
movl %eax, %edx
callq 0x29c34
movq -0x48(%rbp), %rax
movq -0xf8(%rbp), %rcx
movq -0x50(%rbp), %rdx
movq %rax, (%rcx,%rdx,8)
andq $0x0, -0x48(%rbp)
movq %rbx, %rdi
callq 0x1f3b0
cmpq $0x1f, -0x50(%rbp)
movq -0x38(%rbp), %rbx
je 0x2b168
cmpq $0x4, %r13
jne 0x2b168
testb $0x1, -0x3c(%rbp)
je 0x2b168
movq -0x70(%rbp), %rax
movl (%rax), %edi
leaq -0x90(%rbp), %rsi
callq 0x2b3a6
jmp 0x2b168
pushq $0x40
popq %rdi
callq 0xc2a0
movq -0x50(%rbp), %r14
movq -0x110(%rbp), %r12
movq %rax, %rbx
movq -0x70(%rbp), %rax
movl (%rax), %edx
movq %rbx, %rdi
movq -0x38(%rbp), %r15
movq %r15, %rsi
callq 0x29ca0
movq -0xf8(%rbp), %rax
leaq (%rax,%r14,8), %rdi
movq %rbx, %rsi
movq %r15, %rbx
callq 0x20b9e
movq -0x108(%rbp), %r14
leaq -0x3c(%rbp), %r15
movq -0xb0(%rbp), %rax
movq -0xa0(%rbp), %rcx
movq -0x90(%rbp), %rdx
movq -0x88(%rbp), %rsi
movq %rdx, -0xb0(%rbp)
movq %rsi, -0xa8(%rbp)
movq -0x80(%rbp), %rdx
movq %rdx, -0xa0(%rbp)
movq %rax, -0x90(%rbp)
movq %rax, -0x88(%rbp)
movq %rcx, -0x80(%rbp)
movq -0xd0(%rbp), %rdx
movq -0xc8(%rbp), %rcx
leaq -0xb0(%rbp), %rdi
callq 0x2b3ca
movq -0xd0(%rbp), %rax
movq -0xc0(%rbp), %rcx
movq -0xf0(%rbp), %rdx
movq %rdx, -0xd0(%rbp)
movups -0xe8(%rbp), %xmm0
movups %xmm0, -0xc8(%rbp)
movq %rax, -0xf0(%rbp)
movq %rax, -0xe8(%rbp)
movq %rcx, -0xe0(%rbp)
movq -0x70(%rbp), %rax
cmpq %rax, -0x68(%rbp)
je 0x2b216
movq %rax, -0x68(%rbp)
shrl -0x3c(%rbp)
movq -0x50(%rbp), %rax
incq %rax
jmp 0x2ae76
leaq -0x70(%rbp), %rdi
callq 0x12486
leaq -0xf0(%rbp), %rdi
callq 0x12486
leaq -0xd0(%rbp), %rdi
callq 0x12486
leaq -0x90(%rbp), %rdi
callq 0x12486
leaq -0xb0(%rbp), %rdi
callq 0x12486
addq $0xe8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x1159e(%rip), %rdi     # 0x3c815
leaq 0x115bb(%rip), %rsi     # 0x3c839
leaq 0x115d2(%rip), %rcx     # 0x3c857
pushq $0x75
popq %rdx
callq 0xc180
leaq 0x11673(%rip), %rdi     # 0x3c907
leaq 0x1159e(%rip), %rsi     # 0x3c839
leaq 0x115b5(%rip), %rcx     # 0x3c857
movl $0x95, %edx
callq 0xc180
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
jmp 0x2b2c0
movq %rax, %rbx
leaq -0x70(%rbp), %rdi
callq 0x12486
leaq -0xf0(%rbp), %rdi
callq 0x12486
leaq -0xd0(%rbp), %rdi
callq 0x12486
leaq -0x90(%rbp), %rdi
callq 0x12486
leaq -0xb0(%rbp), %rdi
callq 0x12486
movq %rbx, %rdi
callq 0xc4c0