// File: 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
subq $0x1c0, %rsp            # imm = 0x1C0
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq %rdx, -0x18(%rbp)
movq $0x0, -0x20(%rbp)
movl $0x0, -0x24(%rbp)
leaq -0x40(%rbp), %rdi
callq 0x17e70
leaq -0x58(%rbp), %rdi
callq 0x17e70
leaq -0x70(%rbp), %rdi
callq 0x17e70
leaq -0x88(%rbp), %rdi
callq 0x17e70
leaq -0xa0(%rbp), %rdi
callq 0x17e70
movl $0x0, -0xa4(%rbp)
movslq -0xa4(%rbp), %rax
cmpq $0x20, %rax
jae 0x4f5fc
movl $0x0, -0xa8(%rbp)
movslq -0xa8(%rbp), %rax
movq %rax, -0x150(%rbp)
leaq -0x40(%rbp), %rdi
callq 0xf9f0
movq %rax, %rcx
movq -0x150(%rbp), %rax
cmpq %rcx, %rax
jae 0x4ef3d
movslq -0xa8(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xfd50
movl (%rax), %edi
leaq -0xa0(%rbp), %rsi
leaq -0x58(%rbp), %rdx
leaq -0x24(%rbp), %rcx
callq 0x4f650
jmp 0x4eee0
jmp 0x4eee2
movl -0xa8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xa8(%rbp)
jmp 0x4ee8e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xb0(%rbp)
movl %eax, -0xb4(%rbp)
leaq -0xa0(%rbp), %rdi
callq 0x18050
leaq -0x88(%rbp), %rdi
callq 0x18050
leaq -0x70(%rbp), %rdi
callq 0x18050
leaq -0x58(%rbp), %rdi
callq 0x18050
leaq -0x40(%rbp), %rdi
callq 0x18050
jmp 0x4f638
movl $0x0, -0xb8(%rbp)
movslq -0xb8(%rbp), %rax
cmpq -0x18(%rbp), %rax
jae 0x4f06c
movq -0x10(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movq (%rax,%rcx,8), %rdi
movslq -0xa4(%rbp), %rsi
callq 0x4d5a0
movq %rax, %rdi
callq 0x39290
movl 0x20(%rax), %eax
movl %eax, -0xbc(%rbp)
xorl %eax, %eax
cmpl $0x1, -0xbc(%rbp)
movb %al, -0x151(%rbp)
jbe 0x4efa5
cmpl $-0x2, -0xbc(%rbp)
setbe %al
movb %al, -0x151(%rbp)
movb -0x151(%rbp), %al
testb $0x1, %al
jne 0x4efb4
jmp 0x4f03a
cmpq $0x0, -0x20(%rbp)
jne 0x4efe8
movq -0x10(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movq (%rax,%rcx,8), %rdi
movslq -0xa4(%rbp), %rsi
callq 0x4d5a0
movq %rax, %rdi
callq 0x39290
movq 0x18(%rax), %rax
movq %rax, -0x20(%rbp)
jmp 0x4f038
movq -0x10(%rbp), %rax
movslq -0xb8(%rbp), %rcx
movq (%rax,%rcx,8), %rdi
movslq -0xa4(%rbp), %rsi
callq 0x4d5a0
movq %rax, %rdi
callq 0x39290
movq 0x18(%rax), %rax
cmpq -0x20(%rbp), %rax
jne 0x4f017
jmp 0x4f036
leaq 0x17cea(%rip), %rdi     # 0x66d08
leaq 0x17d07(%rip), %rsi     # 0x66d2c
movl $0x75, %edx
leaq 0x17d19(%rip), %rcx     # 0x66d4a
callq 0xd1c0
jmp 0x4f038
jmp 0x4f03a
movl -0xbc(%rbp), %edi
leaq -0xa0(%rbp), %rsi
leaq -0x58(%rbp), %rdx
leaq -0x24(%rbp), %rcx
callq 0x4f650
jmp 0x4f056
jmp 0x4f058
movl -0xb8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xb8(%rbp)
jmp 0x4ef47
leaq -0xa0(%rbp), %rdi
callq 0xf9f0
movq %rax, -0xc8(%rbp)
movslq -0xa4(%rbp), %rax
cmpq $0x1f, %rax
je 0x4f09a
cmpq $0x2, -0xc8(%rbp)
jae 0x4f1a3
leaq -0xa0(%rbp), %rdi
movq %rdi, -0x160(%rbp)
callq 0x10a10
movq -0x160(%rbp), %rdi
movq %rax, -0xd0(%rbp)
callq 0x10ce0
movq %rax, -0xd8(%rbp)
movq -0xd0(%rbp), %rdi
movq -0xd8(%rbp), %rsi
callq 0x18010
jmp 0x4f0dc
movq -0x20(%rbp), %rax
movq %rax, -0x170(%rbp)
leaq -0xa0(%rbp), %rdi
callq 0x4f7a0
movq -0x170(%rbp), %rdi
movq %rax, %rsi
movq -0xc8(%rbp), %rdx
movl -0x24(%rbp), %ecx
andl $0x1, %ecx
callq 0x4db50
movl %eax, -0x164(%rbp)
jmp 0x4f117
movl -0x164(%rbp), %edx
movq -0x170(%rbp), %rsi
leaq -0xe0(%rbp), %rdi
callq 0x4c9d0
jmp 0x4f132
movq -0x8(%rbp), %rdi
movslq -0xa4(%rbp), %rsi
callq 0x4caa0
movq %rax, %rdi
leaq -0xe0(%rbp), %rsi
callq 0x4cad0
leaq -0xe0(%rbp), %rdi
callq 0x34700
movslq -0xa4(%rbp), %rax
cmpq $0x1f, %rax
jae 0x4f19e
cmpq $0x1, -0xc8(%rbp)
jne 0x4f19e
movl -0x24(%rbp), %eax
andl $0x1, %eax
cmpl $0x0, %eax
je 0x4f19e
xorl %eax, %eax
movl %eax, %esi
leaq -0xa0(%rbp), %rdi
callq 0xfd50
movl (%rax), %edi
leaq -0x58(%rbp), %rsi
callq 0x4f7c0
jmp 0x4f19c
jmp 0x4f19e
jmp 0x4f513
cmpq $0x0, -0x20(%rbp)
je 0x4f1ac
jmp 0x4f1cb
leaq 0x17c47(%rip), %rdi     # 0x66dfa
leaq 0x17b72(%rip), %rsi     # 0x66d2c
movl $0x95, %edx
leaq 0x17b84(%rip), %rcx     # 0x66d4a
callq 0xd1c0
movq -0x20(%rbp), %rdi
callq 0x40a80
movl %eax, -0x174(%rbp)
jmp 0x4f1dc
movl -0x174(%rbp), %eax
movl %eax, %eax
movq %rax, -0xe8(%rbp)
movq -0xc8(%rbp), %rax
movq %rax, -0xf0(%rbp)
cmpq $0x0, -0xf0(%rbp)
jbe 0x4f4ad
movq -0xe8(%rbp), %rax
cmpq -0xf0(%rbp), %rax
jbe 0x4f227
movq -0xf0(%rbp), %rax
movq %rax, -0x180(%rbp)
jmp 0x4f235
movq -0xe8(%rbp), %rax
movq %rax, -0x180(%rbp)
movq -0x180(%rbp), %rax
movq %rax, -0xc8(%rbp)
movq -0xc8(%rbp), %rcx
movq -0xf0(%rbp), %rax
subq %rcx, %rax
movq %rax, -0xf0(%rbp)
xorl %eax, %eax
cmpq $0x0, -0xf0(%rbp)
movb %al, -0x181(%rbp)
jne 0x4f27f
movl -0x24(%rbp), %eax
andl $0x1, %eax
cmpl $0x0, %eax
setne %al
movb %al, -0x181(%rbp)
movb -0x181(%rbp), %al
andb $0x1, %al
movzbl %al, %eax
movl %eax, -0xf4(%rbp)
cmpq $0x3, -0xc8(%rbp)
ja 0x4f2bd
xorl %eax, %eax
cmpq $0x3, -0xc8(%rbp)
movb %al, -0x182(%rbp)
jne 0x4f2d1
xorl %eax, %eax
cmpl $0x0, -0xf4(%rbp)
movb %al, -0x182(%rbp)
je 0x4f2d1
movslq -0xa4(%rbp), %rax
cmpq $0x1e, %rax
setb %al
movb %al, -0x182(%rbp)
movb -0x182(%rbp), %al
andb $0x1, %al
movb %al, -0xf5(%rbp)
movb $0x0, -0xf6(%rbp)
movzbl -0xf5(%rbp), %eax
andl $0x1, %eax
orq $0x2, %rax
movq %rax, -0x100(%rbp)
movq -0x20(%rbp), %rdi
movq (%rdi), %rax
movq 0x8(%rax), %rax
callq *%rax
movl %eax, -0x188(%rbp)
jmp 0x4f310
movl -0x188(%rbp), %eax
movl %eax, -0x104(%rbp)
leaq -0xa0(%rbp), %rdi
leaq -0x104(%rbp), %rsi
callq 0xfa10
jmp 0x4f331
movq -0x20(%rbp), %rdi
movq (%rdi), %rax
movq 0x8(%rax), %rax
callq *%rax
movl %eax, -0x18c(%rbp)
jmp 0x4f346
movl -0x18c(%rbp), %eax
movl %eax, -0x108(%rbp)
leaq -0xa0(%rbp), %rdi
leaq -0x108(%rbp), %rsi
callq 0xfa10
jmp 0x4f367
testb $0x1, -0xf5(%rbp)
je 0x4f3a8
movq -0x20(%rbp), %rdi
movq (%rdi), %rax
movq 0x8(%rax), %rax
callq *%rax
movl %eax, -0x190(%rbp)
jmp 0x4f385
movl -0x190(%rbp), %eax
movl %eax, -0x10c(%rbp)
leaq -0xa0(%rbp), %rdi
leaq -0x10c(%rbp), %rsi
callq 0xfa10
jmp 0x4f3a6
jmp 0x4f3a8
leaq -0xa0(%rbp), %rdi
callq 0x4f7a0
movq -0xf0(%rbp), %rcx
leaq (%rax,%rcx,4), %rax
movq %rax, -0x118(%rbp)
movq -0x118(%rbp), %rax
movq -0xc8(%rbp), %rcx
leaq (%rax,%rcx,4), %rax
movq %rax, -0x120(%rbp)
movq -0x20(%rbp), %rdi
movq -0x118(%rbp), %rsi
movq -0xc8(%rbp), %rdx
movq -0x100(%rbp), %rcx
movl -0xf4(%rbp), %r8d
movzbl -0xf6(%rbp), %r9d
andl $0x1, %r9d
callq 0x13760
jmp 0x4f412
movq -0x120(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x194(%rbp)
movq -0xf0(%rbp), %rsi
leaq -0xa0(%rbp), %rdi
callq 0xfd50
movl -0x194(%rbp), %ecx
movl %ecx, (%rax)
movq -0x120(%rbp), %rsi
addq $0x4, %rsi
leaq -0x58(%rbp), %rdi
callq 0xfa40
jmp 0x4f452
testb $0x1, -0xf5(%rbp)
je 0x4f476
movq -0x120(%rbp), %rsi
addq $0x8, %rsi
leaq -0x88(%rbp), %rdi
callq 0xfa40
jmp 0x4f474
jmp 0x4f476
cmpq $0x0, -0xf0(%rbp)
jbe 0x4f4a8
movq -0xf0(%rbp), %rax
incq %rax
movq %rax, -0xf0(%rbp)
movq -0xf0(%rbp), %rsi
leaq -0xa0(%rbp), %rdi
callq 0x2e270
jmp 0x4f4a6
jmp 0x4f4a8
jmp 0x4f1f9
movl $0x40, %edi
callq 0xd330
movq %rax, -0x1a0(%rbp)
jmp 0x4f4c0
movq -0x20(%rbp), %rax
movq %rax, -0x1a8(%rbp)
xorl %eax, %eax
movl %eax, %esi
leaq -0xa0(%rbp), %rdi
callq 0xfd50
movq -0x1a8(%rbp), %rsi
movq -0x1a0(%rbp), %rdi
movl (%rax), %edx
callq 0x4cb00
movq -0x8(%rbp), %rdi
movslq -0xa4(%rbp), %rsi
callq 0x4caa0
movq -0x1a0(%rbp), %rsi
movq %rax, %rdi
callq 0x38cd0
jmp 0x4f511
jmp 0x4f513
leaq -0x40(%rbp), %rdi
movq %rdi, -0x1b8(%rbp)
callq 0xf9d0
movq -0x1b8(%rbp), %rdi
leaq -0x58(%rbp), %rsi
callq 0x4f7f0
movq -0x1b8(%rbp), %rdi
callq 0x10ce0
movq %rax, -0x130(%rbp)
leaq -0x128(%rbp), %rdi
leaq -0x130(%rbp), %rsi
callq 0x10a80
leaq -0x70(%rbp), %rdi
movq %rdi, -0x1c0(%rbp)
callq 0x10a10
movq -0x1c0(%rbp), %rdi
movq %rax, -0x138(%rbp)
callq 0x10ce0
movq -0x1b8(%rbp), %rdi
movq %rax, -0x140(%rbp)
movq -0x128(%rbp), %rsi
movq -0x138(%rbp), %rdx
movq -0x140(%rbp), %rcx
callq 0x4f850
movq %rax, -0x1b0(%rbp)
jmp 0x4f5ad
movq -0x1b0(%rbp), %rax
movq %rax, -0x148(%rbp)
leaq -0x70(%rbp), %rdi
callq 0xf9d0
leaq -0x70(%rbp), %rdi
leaq -0x88(%rbp), %rsi
callq 0x4f7f0
leaq -0xa0(%rbp), %rdi
callq 0xf9d0
movl -0x24(%rbp), %eax
shrl %eax
movl %eax, -0x24(%rbp)
movl -0xa4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xa4(%rbp)
jmp 0x4ee73
leaq -0xa0(%rbp), %rdi
callq 0x18050
leaq -0x88(%rbp), %rdi
callq 0x18050
leaq -0x70(%rbp), %rdi
callq 0x18050
leaq -0x58(%rbp), %rdi
callq 0x18050
leaq -0x40(%rbp), %rdi
callq 0x18050
addq $0x1c0, %rsp            # imm = 0x1C0
popq %rbp
retq
movq -0xb0(%rbp), %rdi
callq 0xd5a0
nopw %cs:(%rax,%rax)
nop