// File: /workspace/github_cpp_makefile_star2_O3/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 $0xd8, %rsp
movq %rdx, %r15
movq %rsi, -0x100(%rbp)
xorps %xmm0, %xmm0
leaq -0xd0(%rbp), %rax
movaps %xmm0, (%rax)
xorl %ecx, %ecx
movq %rcx, 0x10(%rax)
leaq -0x80(%rbp), %rax
movq %rcx, 0x10(%rax)
movaps %xmm0, (%rax)
leaq -0xb0(%rbp), %rax
movq %rcx, 0x10(%rax)
movaps %xmm0, (%rax)
leaq -0x50(%rbp), %rax
movq $0x0, -0x88(%rbp)
movq %rcx, 0x10(%rax)
movaps %xmm0, (%rax)
addq $0x30, %rdi
movq %rdi, -0xd8(%rbp)
movq $0x0, -0x58(%rbp)
movl $0x0, -0x30(%rbp)
xorl %ebx, %ebx
movq $0x0, -0xe0(%rbp)
movq $0x0, -0xe8(%rbp)
movq %rdx, -0xf8(%rbp)
movq %rbx, -0x90(%rbp)
movq -0xd0(%rbp), %rax
cmpq %rax, -0xc8(%rbp)
je 0x356d5
xorl %ebx, %ebx
movl (%rax,%rbx,4), %edi
movl %edi, -0x5c(%rbp)
cmpl $0x2, %edi
jae 0x355f7
xorl %eax, %eax
cmpl $0x1, %edi
sete %al
addl %eax, -0x30(%rbp)
jmp 0x356b4
movq -0x50(%rbp), %rcx
movq -0x48(%rbp), %rax
movq %rax, %rsi
subq %rcx, %rsi
movq %rax, %rdx
subq %rcx, %rdx
je 0x35634
sarq $0x2, %rsi
cmpq $0x1, %rsi
movq %rsi, %r8
adcq $0x0, %r8
xorl %r14d, %r14d
movl (%rcx,%r14,4), %r9d
xorl %edi, %r9d
cmpl $0x1, %r9d
jbe 0x35646
incq %r14
cmpq %r14, %r8
jne 0x3561f
cmpq -0x40(%rbp), %rax
je 0x3564d
movl %edi, (%rax)
addq $0x4, %rax
movq %rax, -0x48(%rbp)
jmp 0x356b4
jne 0x3565f
incl -0x30(%rbp)
jmp 0x3569a
leaq -0x50(%rbp), %rdi
movq %rax, %rsi
leaq -0x5c(%rbp), %rdx
callq 0xe912
jmp 0x356b4
movl %edi, -0x38(%rbp)
movq -0x78(%rbp), %rax
cmpq -0x70(%rbp), %rax
je 0x35678
movl %edi, (%rax)
addq $0x4, %rax
movq %rax, -0x78(%rbp)
jmp 0x3569a
leaq -0x80(%rbp), %rdi
movq %rax, %rsi
leaq -0x38(%rbp), %rdx
callq 0xe912
movq -0x50(%rbp), %rcx
movq -0x48(%rbp), %rdx
subq %rcx, %rdx
movq %rdx, %rsi
sarq $0x2, %rsi
cmpq $0x2, %rsi
jb 0x356a8
movl -0x4(%rcx,%rdx), %eax
movl %eax, (%rcx,%r14,4)
decq %rsi
leaq -0x50(%rbp), %rdi
callq 0x219d2
incq %rbx
movq -0xd0(%rbp), %rax
movq -0xc8(%rbp), %rcx
subq %rax, %rcx
sarq $0x2, %rcx
cmpq %rbx, %rcx
ja 0x355dc
testq %r15, %r15
je 0x35801
xorl %ebx, %ebx
movq -0x100(%rbp), %rax
movq (%rax,%rbx,8), %rax
movq -0x88(%rbp), %rcx
movq 0x30(%rax,%rcx,8), %rax
movl 0x20(%rax), %edx
leal -0x2(%rdx), %ecx
cmpl $-0x4, %ecx
ja 0x35719
movq 0x18(%rax), %rax
movq -0x58(%rbp), %rcx
testq %rcx, %rcx
je 0x35731
cmpq %rcx, %rax
je 0x35735
jmp 0x35c13
movl %edx, -0x5c(%rbp)
cmpl $0x2, %edx
jae 0x35738
xorl %eax, %eax
cmpl $0x1, %edx
sete %al
addl %eax, -0x30(%rbp)
jmp 0x357f5
movq %rax, -0x58(%rbp)
movl %edx, -0x5c(%rbp)
movq -0x50(%rbp), %rcx
movq -0x48(%rbp), %rax
movq %rax, %rsi
subq %rcx, %rsi
movq %rax, %rdi
subq %rcx, %rdi
je 0x35775
sarq $0x2, %rsi
cmpq $0x1, %rsi
movq %rsi, %r8
adcq $0x0, %r8
xorl %r14d, %r14d
movl (%rcx,%r14,4), %r9d
xorl %edx, %r9d
cmpl $0x1, %r9d
jbe 0x35787
incq %r14
cmpq %r14, %r8
jne 0x35760
cmpq -0x40(%rbp), %rax
je 0x3578e
movl %edx, (%rax)
addq $0x4, %rax
movq %rax, -0x48(%rbp)
jmp 0x357f5
jne 0x357a0
incl -0x30(%rbp)
jmp 0x357db
leaq -0x50(%rbp), %rdi
movq %rax, %rsi
leaq -0x5c(%rbp), %rdx
callq 0xe912
jmp 0x357f5
movl %edx, -0x38(%rbp)
movq -0x78(%rbp), %rax
cmpq -0x70(%rbp), %rax
je 0x357b9
movl %edx, (%rax)
addq $0x4, %rax
movq %rax, -0x78(%rbp)
jmp 0x357db
leaq -0x80(%rbp), %rdi
movq %rax, %rsi
leaq -0x38(%rbp), %rdx
callq 0xe912
movq -0x50(%rbp), %rcx
movq -0x48(%rbp), %rdi
subq %rcx, %rdi
movq %rdi, %rsi
sarq $0x2, %rsi
cmpq $0x2, %rsi
jb 0x357e9
movl -0x4(%rcx,%rdi), %eax
movl %eax, (%rcx,%r14,4)
decq %rsi
leaq -0x50(%rbp), %rdi
callq 0x219d2
incq %rbx
cmpq %r15, %rbx
jne 0x356e0
movq -0x50(%rbp), %rbx
movq -0x48(%rbp), %r12
movq %r12, %r14
subq %rbx, %r14
movq %r14, %r13
sarq $0x2, %r13
cmpq $0x1f, -0x88(%rbp)
je 0x35a2d
cmpq $0x2, %r13
jb 0x35a2d
movq -0x58(%rbp), %rax
testq %rax, %rax
je 0x35c32
movl 0x70(%rax), %eax
testl %eax, %eax
jne 0x3584c
movq -0x58(%rbp), %rdi
movq (%rdi), %rax
callq *0x18(%rax)
cmpq %rbx, %r12
je 0x359ef
movl %eax, %eax
movq %rax, -0xf0(%rbp)
cmpq $0x1e, -0x88(%rbp)
setb -0x29(%rbp)
movq -0xf0(%rbp), %r12
cmpq %r12, %r13
cmovbq %r13, %r12
setbe %r14b
cmpq $0x4, %r12
setae %bl
cmpq $0x3, %r12
sete %r15b
movq -0x58(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x38(%rbp)
movq -0x48(%rbp), %rsi
cmpq -0x40(%rbp), %rsi
je 0x358ae
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x48(%rbp)
jmp 0x358bb
leaq -0x50(%rbp), %rdi
leaq -0x38(%rbp), %rdx
callq 0xe7da
movq -0x58(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x38(%rbp)
movq -0x48(%rbp), %rsi
cmpq -0x40(%rbp), %rsi
je 0x358de
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x48(%rbp)
jmp 0x358eb
leaq -0x50(%rbp), %rdi
leaq -0x38(%rbp), %rdx
callq 0xe7da
andb -0x30(%rbp), %r14b
andb %r14b, %r15b
orb %r15b, %bl
andb %bl, -0x29(%rbp)
movq -0xf8(%rbp), %r15
je 0x35931
movq -0x58(%rbp), %rdi
movq (%rdi), %rax
callq *0x8(%rax)
movl %eax, -0x38(%rbp)
movq -0x48(%rbp), %rsi
cmpq -0x40(%rbp), %rsi
je 0x35924
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x48(%rbp)
jmp 0x35931
leaq -0x50(%rbp), %rdi
leaq -0x38(%rbp), %rdx
callq 0xe7da
movq %r13, %rbx
subq %r12, %rbx
movzbl %r14b, %r8d
movzbl -0x29(%rbp), %ecx
orq $0x2, %rcx
movq -0x50(%rbp), %r14
leaq (%r14,%rbx,4), %rsi
movq -0x58(%rbp), %rdi
movq %r12, %rdx
xorl %r9d, %r9d
callq 0xfb35
leaq (%r14,%r13,4), %r12
movl (%r12), %eax
movq -0x50(%rbp), %rcx
movl %eax, (%rcx,%rbx,4)
leaq 0x4(%r12), %rdx
movq -0x78(%rbp), %rsi
cmpq -0x70(%rbp), %rsi
je 0x35986
movl (%rdx), %eax
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x78(%rbp)
jmp 0x3598f
leaq -0x80(%rbp), %rdi
callq 0xe912
cmpb $0x0, -0x29(%rbp)
je 0x359cb
addq $0x8, %r12
movq -0xa8(%rbp), %rsi
cmpq -0xa0(%rbp), %rsi
je 0x359bc
movl (%r12), %eax
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0xa8(%rbp)
jmp 0x359cb
leaq -0xb0(%rbp), %rdi
movq %r12, %rdx
callq 0xe912
cmpq -0xf0(%rbp), %r13
jbe 0x359ef
incq %rbx
leaq -0x50(%rbp), %rdi
movq %rbx, %rsi
callq 0x219d2
movq %rbx, %r13
testq %rbx, %rbx
jne 0x3585e
movl $0x40, %edi
callq 0xb2c0
movq %rax, %rbx
movq -0x50(%rbp), %rax
movl (%rax), %edx
movq %rbx, %rdi
movq -0x58(%rbp), %rsi
callq 0x3444c
movq -0xd8(%rbp), %rax
movq -0x88(%rbp), %rcx
leaq (%rax,%rcx,8), %rdi
movq %rbx, %rsi
callq 0x28666
jmp 0x35ae9
cmpq %r12, %rbx
je 0x35a5e
bsrq %r13, %rdx
xorl $0x3f, %edx
addl %edx, %edx
xorq $0x7e, %rdx
movq %rbx, %rdi
movq %r12, %rsi
callq 0x13771
movq %rbx, %rdi
movq %r12, %rsi
callq 0x13852
movq %r13, %rdx
movq -0x50(%rbp), %rbx
jmp 0x35a61
movq %r13, %rdx
movl -0x30(%rbp), %r12d
andl $0x1, %r12d
movq -0x58(%rbp), %r13
movq %r13, %rdi
movq %rbx, %rsi
movl %r12d, %ecx
callq 0x34b80
leaq -0x38(%rbp), %rdi
movq %r13, %rsi
movl %eax, %edx
callq 0x343d8
movq -0x88(%rbp), %rsi
cmpq $0x1f, %rsi
sete %al
movq -0x38(%rbp), %rcx
movq -0xd8(%rbp), %rdx
movq %rcx, (%rdx,%rsi,8)
cmpq $0x4, %r14
setne %cl
testl %r12d, %r12d
sete %dl
orb %al, %dl
orb %cl, %dl
jne 0x35ae9
movq -0x50(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x38(%rbp)
testl %eax, %eax
je 0x35ae9
movq -0x78(%rbp), %rsi
cmpq -0x70(%rbp), %rsi
je 0x35adc
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, -0x78(%rbp)
jmp 0x35ae9
leaq -0x80(%rbp), %rdi
leaq -0x38(%rbp), %rdx
callq 0xe912
movq -0xd0(%rbp), %rax
movq -0xc0(%rbp), %rcx
movq -0x80(%rbp), %rdx
movq -0x78(%rbp), %rsi
movq %rdx, -0xd0(%rbp)
movq %rsi, -0xc8(%rbp)
movq -0x70(%rbp), %rdx
movq %rdx, -0xc0(%rbp)
movq %rax, -0x80(%rbp)
movq %rax, -0x78(%rbp)
movq %rcx, -0x70(%rbp)
leaq -0xd0(%rbp), %rdi
movq -0x90(%rbp), %r14
movq %r14, %rdx
movq -0xe8(%rbp), %rcx
callq 0x35cc0
movq -0xb0(%rbp), %rbx
movq -0xa8(%rbp), %rax
movq %rax, -0xe8(%rbp)
movq -0xa0(%rbp), %rax
movq %r14, -0xb0(%rbp)
movq %r14, -0xa8(%rbp)
movq -0xe0(%rbp), %rcx
movq %rcx, -0xa0(%rbp)
movq -0x50(%rbp), %rdi
cmpq %rdi, -0x48(%rbp)
je 0x35b87
movq %rdi, -0x48(%rbp)
shrl -0x30(%rbp)
movq -0x88(%rbp), %rcx
incq %rcx
movq %rax, -0xe0(%rbp)
movq %rcx, -0x88(%rbp)
cmpq $0x20, %rcx
jne 0x355bf
testq %rdi, %rdi
je 0x35bc4
callq 0xb280
movq -0xb0(%rbp), %rax
movq %rax, -0x90(%rbp)
movq -0x90(%rbp), %rdi
testq %rdi, %rdi
je 0x35bd5
callq 0xb280
testq %rbx, %rbx
je 0x35be2
movq %rbx, %rdi
callq 0xb280
movq -0x80(%rbp), %rdi
testq %rdi, %rdi
je 0x35bf0
callq 0xb280
movq -0xd0(%rbp), %rdi
testq %rdi, %rdi
je 0x35c01
callq 0xb280
addq $0xd8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x149f4(%rip), %rdi     # 0x4a60e
leaq 0x14a11(%rip), %rsi     # 0x4a632
leaq 0x14a28(%rip), %rcx     # 0x4a650
movl $0x75, %edx
callq 0xb180
leaq 0x14ac7(%rip), %rdi     # 0x4a700
leaq 0x149f2(%rip), %rsi     # 0x4a632
leaq 0x14a09(%rip), %rcx     # 0x4a650
movl $0x95, %edx
callq 0xb180
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
jmp 0x35c65
movq %rax, %rbx
movq -0x50(%rbp), %rdi
testq %rdi, %rdi
je 0x35c76
callq 0xb280
movq -0xb0(%rbp), %rdi
testq %rdi, %rdi
je 0x35c87
callq 0xb280
movq -0x90(%rbp), %rdi
testq %rdi, %rdi
je 0x35c98
callq 0xb280
movq -0x80(%rbp), %rdi
testq %rdi, %rdi
je 0x35ca6
callq 0xb280
movq -0xd0(%rbp), %rdi
testq %rdi, %rdi
je 0x35cb7
callq 0xb280
movq %rbx, %rdi
callq 0xb4e0
nop