// File: /workspace/github_cpp_makefile_star2_O1/xocoder[P]m_kcptun/vendor/cm256/cm256.cpp
// Function: CM256Decoder::GenerateLDUDecomposition(unsigned char*, unsigned char*, unsigned char*)
void CM256Decoder::GenerateLDUDecomposition(uint8_t* matrix_L, uint8_t* diag_D, uint8_t* matrix_U)
{
    // Schur-type-direct-Cauchy algorithm 2.5 from
    // "Pivoting and Backward Stability of Fast Algorithms for Solving Cauchy Linear Equations"
    // T. Boros, T. Kailath, V. Olshevsky
    // Modified for practical use.  I folded the diagonal parts of U/L matrices into the
    // diagonal one to reduce the number of multiplications to perform against the input data,
    // and organized the triangle matrices in memory to allow for faster SSE3 GF multiplications.

    // Matrix size NxN
    const int N = RecoveryCount;

    // Generators
    uint8_t g[256], b[256];
    for (int i = 0; i < N; ++i)
    {
        g[i] = 1;
        b[i] = 1;
    }

    // Temporary buffer for rotated row of U matrix
    // This allows for faster GF bulk multiplication
    uint8_t rotated_row_U[256];
    uint8_t* last_U = matrix_U + ((N - 1) * N) / 2 - 1;
    int firstOffset_U = 0;

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

    // Unrolling k = 0 just makes it slower for some reason.
    for (int k = 0; k < N - 1; ++k)
    {
        const uint8_t x_k = Recovery[k]->Index;
        const uint8_t y_k = ErasuresIndices[k];

        // D_kk = (x_k + y_k)
        // L_kk = g[k] / (x_k + y_k)
        // U_kk = b[k] * (x_0 + y_k) / (x_k + y_k)
        const uint8_t D_kk = gf256_add(x_k, y_k);
        const uint8_t L_kk = gf256_div(g[k], D_kk);
        const uint8_t U_kk = gf256_mul(gf256_div(b[k], D_kk), gf256_add(x_0, y_k));

        // diag_D[k] = D_kk * L_kk * U_kk
        diag_D[k] = gf256_mul(D_kk, gf256_mul(L_kk, U_kk));

        // Computing the k-th row of L and U
        uint8_t* row_L = matrix_L;
        uint8_t* row_U = rotated_row_U;
        for (int j = k + 1; j < N; ++j)
        {
            const uint8_t x_j = Recovery[j]->Index;
            const uint8_t y_j = ErasuresIndices[j];

            // L_jk = g[j] / (x_j + y_k)
            // U_kj = b[j] / (x_k + y_j)
            const uint8_t L_jk = gf256_div(g[j], gf256_add(x_j, y_k));
            const uint8_t U_kj = gf256_div(b[j], gf256_add(x_k, y_j));

            *matrix_L++ = L_jk;
            *row_U++ = U_kj;

            // g[j] = g[j] * (x_j + x_k) / (x_j + y_k)
            // b[j] = b[j] * (y_j + y_k) / (y_j + x_k)
            g[j] = gf256_mul(g[j], gf256_div(gf256_add(x_j, x_k), gf256_add(x_j, y_k)));
            b[j] = gf256_mul(b[j], gf256_div(gf256_add(y_j, y_k), gf256_add(y_j, x_k)));
        }

        // Do these row/column divisions in bulk for speed.
        // L_jk /= L_kk
        // U_kj /= U_kk
        const int count = N - (k + 1);
        gf256_div_mem(row_L, row_L, L_kk, count);
        gf256_div_mem(rotated_row_U, rotated_row_U, U_kk, count);

        // Copy U matrix row into place in memory.
        uint8_t* output_U = last_U + firstOffset_U;
        row_U = rotated_row_U;
        for (int j = k + 1; j < N; ++j)
        {
            *output_U = *row_U++;
            output_U -= j;
        }
        firstOffset_U -= k + 2;
    }

    // Multiply diagonal matrix into U
    uint8_t* row_U = matrix_U;
    for (int j = N - 1; j > 0; --j)
    {
        const uint8_t y_j = ErasuresIndices[j];
        const int count = j;

        gf256_mul_mem(row_U, row_U, gf256_add(x_0, y_j), count);
        row_U += count;
    }

    const uint8_t x_n = Recovery[N - 1]->Index;
    const uint8_t y_n = ErasuresIndices[N - 1];

    // D_nn = 1 / (x_n + y_n)
    // L_nn = g[N-1]
    // U_nn = b[N-1] * (x_0 + y_n)
    const uint8_t L_nn = g[N - 1];
    const uint8_t U_nn = gf256_mul(b[N - 1], gf256_add(x_0, y_n));

    // diag_D[N-1] = L_nn * D_nn * U_nn
    diag_D[N - 1] = gf256_div(gf256_mul(L_nn, U_nn), gf256_add(x_n, y_n));
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x388, %rsp            # imm = 0x388
movq %rcx, -0x50(%rbp)
movq %rdx, -0x60(%rbp)
movq %rsi, %r11
movq %rdi, %r13
movslq 0x810(%rdi), %rbx
testq %rbx, %rbx
jle 0x96da
leaq -0x2b0(%rbp), %rdi
movl $0x1, %esi
movq %rbx, %rdx
movq %r11, %r14
callq 0x2180
leaq -0x1b0(%rbp), %rdi
movl $0x1, %esi
movq %rbx, %rdx
callq 0x2180
movq %r14, %r11
leal -0x1(%rbx), %ecx
movb (%r13), %al
movb %al, -0x29(%rbp)
movl %ecx, -0x30(%rbp)
movl %ecx, %eax
movq %rax, -0x58(%rbp)
movq %rbx, -0x38(%rbp)
cmpl $0x2, %ebx
jl 0x99c2
movl -0x30(%rbp), %eax
movq -0x38(%rbp), %rdx
imull %edx, %eax
movl %eax, %ecx
shrl $0x1f, %ecx
addl %eax, %ecx
sarl %ecx
movslq %ecx, %rax
movq -0x50(%rbp), %rcx
addq %rcx, %rax
decq %rax
movq %rax, -0x88(%rbp)
leaq -0x1af(%rbp), %r14
leaq -0x2af(%rbp), %r15
leaq 0x101d(%r13), %rax
leaq 0x18(%r13), %rcx
leaq -0x1(%rdx), %rsi
movq %rdx, %rdi
negq %rdi
xorl %r10d, %r10d
movq $-0x1, %r8
leaq 0x6bab(%rip), %r12      # 0x10300
xorl %r9d, %r9d
movq %r13, -0x90(%rbp)
movq %rdi, -0x80(%rbp)
movq %rcx, -0x70(%rbp)
movq %rax, -0x78(%rbp)
movl %r9d, -0x44(%rbp)
movq %r8, -0xa8(%rbp)
movq 0x10(%r13,%r10,8), %rax
movb 0x8(%rax), %al
movb 0x101c(%r13,%r10), %cl
movq %rsi, %rdx
movl %ecx, %esi
xorb %al, %sil
movzbl -0x2b0(%rbp,%r10), %edi
movzbl %sil, %esi
movl %esi, %r8d
shll $0x8, %r8d
orq %r8, %rdi
movzbl 0x12000(%r12,%rdi), %r9d
movzbl -0x1b0(%rbp,%r10), %edi
orq %r8, %rdi
movzbl 0x12000(%r12,%rdi), %edi
movl %ecx, %r8d
xorb -0x29(%rbp), %r8b
movzbl %r8b, %r8d
shll $0x8, %r8d
orq %rdi, %r8
movzbl 0x2000(%r12,%r8), %edi
movq %rdi, -0x98(%rbp)
shll $0x8, %edi
movq %r9, -0x40(%rbp)
orq %r9, %rdi
movzbl 0x2000(%r12,%rdi), %edi
shll $0x8, %edi
orq %rsi, %rdi
movb 0x2000(%r12,%rdi), %sil
movq -0x60(%rbp), %rdi
movb %sil, (%rdi,%r10)
movq %r10, -0xa0(%rbp)
leaq 0x1(%r10), %rsi
movq %rsi, -0x68(%rbp)
xorl %r13d, %r13d
movq -0x70(%rbp), %rsi
movq (%rsi,%r13,8), %rsi
movb 0x8(%rsi), %r9b
movq -0x78(%rbp), %rsi
movb (%rsi,%r13), %sil
movzbl (%r15,%r13), %edi
movl %r9d, %r8d
xorb %cl, %r8b
movzbl %r8b, %r10d
shll $0x8, %r10d
orq %r10, %rdi
movq %r11, %rbx
movb 0x12000(%r12,%rdi), %r11b
movzbl (%r14,%r13), %edi
movl %esi, %r8d
xorb %al, %r8b
movzbl %r8b, %r8d
shll $0x8, %r8d
orq %r8, %rdi
movb 0x12000(%r12,%rdi), %dil
movb %r11b, (%rbx,%r13)
movq %rbx, %r11
xorb %al, %r9b
movzbl %r9b, %r9d
orq %r10, %r9
movzbl (%r15,%r13), %r10d
movzbl 0x12000(%r12,%r9), %r9d
shll $0x8, %r9d
orq %r10, %r9
xorb %cl, %sil
movzbl %sil, %esi
orq %r8, %rsi
movzbl (%r14,%r13), %r8d
movzbl 0x12000(%r12,%rsi), %esi
shll $0x8, %esi
orq %r8, %rsi
movb %dil, -0x3b0(%rbp,%r13)
movb 0x2000(%r12,%r9), %dil
movb %dil, (%r15,%r13)
movb 0x2000(%r12,%rsi), %sil
movb %sil, (%r14,%r13)
incq %r13
cmpq %r13, %rdx
jne 0x9820
movq %rdx, -0xb0(%rbp)
movb $0x1, %al
movq -0x40(%rbp), %rcx
cmpl $0x1, %ecx
je 0x98fd
movb 0x22000(%r12,%rcx), %al
movq -0x38(%rbp), %rcx
subq -0x68(%rbp), %rcx
movzbl %al, %edx
movq %r11, %rdi
movq %r11, %rsi
movq %rcx, -0x40(%rbp)
callq 0xa6ad
movq -0x98(%rbp), %rax
cmpl $0x1, %eax
movb $0x1, %cl
je 0x992d
movb 0x22000(%r12,%rax), %cl
addq %r13, %rbx
movzbl %cl, %edx
leaq -0x3b0(%rbp), %r13
movq %r13, %rdi
movq %r13, %rsi
movq -0x40(%rbp), %rcx
callq 0xa6ad
movl -0x44(%rbp), %r9d
movslq %r9d, %rax
addq -0x88(%rbp), %rax
movq -0xa8(%rbp), %r8
movq %r8, %rcx
movq -0x80(%rbp), %rdi
movb (%r13), %sil
incq %r13
movb %sil, (%rax)
addq %rcx, %rax
decq %rcx
cmpq %rcx, %rdi
jne 0x9965
subl -0xa0(%rbp), %r9d
addl $-0x2, %r9d
incq %r14
incq %r15
movq -0x78(%rbp), %rax
incq %rax
movq -0x70(%rbp), %rcx
addq $0x8, %rcx
movq -0xb0(%rbp), %rsi
decq %rsi
decq %r8
movq -0x68(%rbp), %rdx
movq %rdx, %r10
cmpq -0x58(%rbp), %rdx
movq -0x90(%rbp), %r13
movq %rbx, %r11
jne 0x9763
cmpl $0x2, -0x38(%rbp)
movq -0x50(%rbp), %rbx
movb -0x29(%rbp), %r14b
movq -0x58(%rbp), %r15
jl 0x9a00
movb 0x101c(%r13,%r15), %al
xorb %r14b, %al
movzbl %al, %edx
movq %rbx, %rdi
movq %rbx, %rsi
movl %r15d, %ecx
callq 0xa6ad
addq %r15, %rbx
leaq -0x1(%r15), %rax
cmpq $0x1, %r15
movq %rax, %r15
jg 0x99d4
movslq -0x30(%rbp), %rax
movq 0x10(%r13,%rax,8), %rcx
movb 0x101c(%r13,%rax), %dl
movzbl -0x2b0(%rbp,%rax), %esi
movzbl -0x1b0(%rbp,%rax), %edi
xorb %dl, %r14b
movzbl %r14b, %r8d
shll $0x8, %r8d
orq %rdi, %r8
leaq 0x68ca(%rip), %rdi      # 0x10300
movzbl 0x2000(%rdi,%r8), %r8d
shll $0x8, %r8d
orq %rsi, %r8
movzbl 0x2000(%rdi,%r8), %esi
xorb 0x8(%rcx), %dl
movzbl %dl, %ecx
shll $0x8, %ecx
orq %rsi, %rcx
movb 0x12000(%rdi,%rcx), %cl
movq -0x60(%rbp), %rdx
movb %cl, (%rdx,%rax)
addq $0x388, %rsp            # imm = 0x388
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
nop