// File: /workspace/github_cpp_makefile_star2_O2/xocoder[P]m_kcptun/vendor/cm256/cm256.cpp
// Function: CM256Decoder::Decode()
void CM256Decoder::Decode()
{
    // Matrix size is NxN, where N is the number of recovery blocks used.
    const int N = RecoveryCount;

    // Start the x_0 values arbitrarily from the original count.
    const uint8_t x_0 = static_cast<uint8_t>(Params.OriginalCount);

    // Eliminate original data from the the recovery rows
    for (int originalIndex = 0; originalIndex < OriginalCount; ++originalIndex)
    {
        const uint8_t* inBlock = static_cast<const uint8_t*>(Original[originalIndex]->Block);
        const uint8_t inRow = Original[originalIndex]->Index;

        for (int recoveryIndex = 0; recoveryIndex < N; ++recoveryIndex)
        {
            uint8_t* outBlock = static_cast<uint8_t*>(Recovery[recoveryIndex]->Block);
            const uint8_t x_i = Recovery[recoveryIndex]->Index;
            const uint8_t y_j = inRow;
            const uint8_t matrixElement = GetMatrixElement(x_i, x_0, y_j);

            gf256_muladd_mem(outBlock, matrixElement, inBlock, Params.BlockBytes);
        }
    }

    // Allocate matrix
    static const int StackAllocSize = 2048;
    uint8_t stackMatrix[StackAllocSize];
    uint8_t* dynamicMatrix = 0;
    uint8_t* matrix = stackMatrix;
    const int requiredSpace = N * N;
    if (requiredSpace > StackAllocSize)
    {
        dynamicMatrix = new uint8_t[requiredSpace];
        matrix = dynamicMatrix;
    }

    /*
        Compute matrix decomposition:

            G = L * D * U

        L is lower-triangular, diagonal is all ones.
        D is a diagonal matrix.
        U is upper-triangular, diagonal is all ones.
    */
    uint8_t* matrix_U = matrix;
    uint8_t* diag_D = matrix_U + (N - 1) * N / 2;
    uint8_t* matrix_L = diag_D + N;
    GenerateLDUDecomposition(matrix_L, diag_D, matrix_U);

    /*
        Eliminate lower left triangle.
    */
    // For each column,
    for (int j = 0; j < N - 1; ++j)
    {
        const void* block_j = Recovery[j]->Block;

        // For each row,
        for (int i = j + 1; i < N; ++i)
        {
            void* block_i = Recovery[i]->Block;
            const uint8_t c_ij = *matrix_L++; // Matrix elements are stored column-first, top-down.

            gf256_muladd_mem(block_i, c_ij, block_j, Params.BlockBytes);
        }
    }

    /*
        Eliminate diagonal.
    */
    for (int i = 0; i < N; ++i)
    {
        void* block = Recovery[i]->Block;

        Recovery[i]->Index = ErasuresIndices[i];

        gf256_div_mem(block, block, diag_D[i], Params.BlockBytes);
    }

    /*
        Eliminate upper right triangle.
    */
    for (int j = N - 1; j >= 1; --j)
    {
        const void* block_j = Recovery[j]->Block;

        for (int i = j - 1; i >= 0; --i)
        {
            void* block_i = Recovery[i]->Block;
            const uint8_t c_ij = *matrix_U++; // Matrix elements are stored column-first, bottom-up.

            gf256_muladd_mem(block_i, c_ij, block_j, Params.BlockBytes);
        }
    }

    delete[] dynamicMatrix;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x848, %rsp            # imm = 0x848
movq %rdi, %rbx
movslq 0x810(%rdi), %rcx
movb (%rdi), %al
movb %al, -0x30(%rbp)
xorl %edx, %edx
testl %ecx, %ecx
movl $0x0, %eax
movq %rcx, -0x40(%rbp)
cmovgl %ecx, %eax
movq %rax, -0x58(%rbp)
movslq 0x1018(%rbx), %rax
cmpq %rax, %rdx
jge 0x89a6
movq %rdx, -0x38(%rbp)
movq 0x818(%rbx,%rdx,8), %rax
movq (%rax), %r14
movb 0x8(%rax), %r13b
movl %r13d, %eax
xorb -0x30(%rbp), %al
movzbl %al, %r15d
xorl %r12d, %r12d
cmpq %r12, -0x58(%rbp)
je 0x899d
movq 0x10(%rbx,%r12,8), %rax
movq (%rax), %rdi
movb 0x8(%rax), %al
xorb %r13b, %al
movzbl %al, %eax
shll $0x8, %eax
orq %r15, %rax
leaq 0x696b(%rip), %rcx      # 0xf2f0
movzbl 0x12000(%rcx,%rax), %esi
movl 0x8(%rbx), %ecx
movq %r14, %rdx
callq 0x958e
incq %r12
jmp 0x8961
movq -0x38(%rbp), %rdx
incq %rdx
jmp 0x8935
movq -0x40(%rbp), %rax
imull %eax, %eax
cmpl $0x800, %eax            # imm = 0x800
jbe 0x89c4
movl %eax, %edi
callq 0x2030
movq %rax, %r15
movq %rax, -0x50(%rbp)
jmp 0x89d3
leaq -0x870(%rbp), %r15
movq $0x0, -0x50(%rbp)
movq -0x40(%rbp), %rsi
leal -0x1(%rsi), %r14d
movl %r14d, %eax
imull %esi, %eax
pushq $0x2
popq %rcx
cltd
idivl %ecx
movslq %eax, %rdx
addq %r15, %rdx
addq %rdx, %rsi
movq %rbx, %rdi
movq %rsi, -0x38(%rbp)
movq %rdx, -0x60(%rbp)
movq %r15, %rcx
callq 0x8532
testl %r14d, %r14d
movl $0x0, %eax
movl %r14d, -0x44(%rbp)
cmovgl %r14d, %eax
movq %rax, -0x68(%rbp)
leaq 0x18(%rbx), %rax
movq %rax, -0x30(%rbp)
pushq $0x1
popq %r13
xorl %eax, %eax
cmpq -0x68(%rbp), %rax
je 0x8a7c
movq %rax, -0x70(%rbp)
movq 0x10(%rbx,%rax,8), %rax
movq (%rax), %r14
xorl %r12d, %r12d
leal (%r12,%r13), %eax
cmpl %eax, -0x40(%rbp)
jle 0x8a67
movq -0x30(%rbp), %rax
movq (%rax,%r12,8), %rax
movq (%rax), %rdi
movq -0x38(%rbp), %rax
movzbl (%rax,%r12), %esi
movl 0x8(%rbx), %ecx
movq %r14, %rdx
callq 0x958e
incq %r12
jmp 0x8a3a
movq -0x70(%rbp), %rax
incq %rax
incq %r13
addq $0x8, -0x30(%rbp)
addq %r12, -0x38(%rbp)
jmp 0x8a25
xorl %r14d, %r14d
movq -0x60(%rbp), %r12
cmpq %r14, -0x58(%rbp)
je 0x8aca
movq 0x10(%rbx,%r14,8), %rax
movq (%rax), %rdi
movb 0x101c(%rbx,%r14), %cl
movb %cl, 0x8(%rax)
movzbl (%r12,%r14), %ecx
movb $0x1, %al
cmpq $0x1, %rcx
je 0x8ab7
leaq 0x6840(%rip), %rax      # 0xf2f0
movb 0x22000(%rax,%rcx), %al
movl 0x8(%rbx), %ecx
movzbl %al, %edx
movq %rdi, %rsi
callq 0x93b6
incq %r14
jmp 0x8a83
movl -0x44(%rbp), %r12d
testl %r12d, %r12d
jle 0x8b07
movq 0x10(%rbx,%r12,8), %rax
movq (%rax), %r14
movq %r12, %r13
testl %r13d, %r13d
jle 0x8b02
movq 0x8(%rbx,%r13,8), %rax
decq %r13
movq (%rax), %rdi
movzbl (%r15), %esi
incq %r15
movl 0x8(%rbx), %ecx
movq %r14, %rdx
callq 0x958e
jmp 0x8ade
decq %r12
jmp 0x8ace
movq -0x50(%rbp), %rdi
testq %rdi, %rdi
je 0x8b15
callq 0x2320
addq $0x848, %rsp            # imm = 0x848
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq