// File: /workspace/github_cpp_makefile_star2_O3/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 $0x398, %rsp            # imm = 0x398
movq %rdx, -0x58(%rbp)
movq %rdi, %r12
movl 0x810(%rdi), %r15d
testl %r15d, %r15d
jle 0x97cb
movq %rcx, %r14
movq %rsi, %r13
leaq -0x2c0(%rbp), %rdi
movl $0x1, %esi
movq %r15, %rdx
callq 0x2180
leaq -0x1c0(%rbp), %rdi
movl $0x1, %esi
movq %r15, %rdx
callq 0x2180
movb (%r12), %bl
xorl %eax, %eax
cmpq $0x1, %r15
je 0x9810
movq %r13, -0x38(%rbp)
movb %bl, -0x29(%rbp)
leal -0x1(%r15), %ecx
movl %ecx, %eax
imull %r15d, %eax
shrl %eax
movq %r14, -0x78(%rbp)
addq %r14, %rax
decq %rax
movq %rax, -0x88(%rbp)
movl %ecx, -0x30(%rbp)
movl %ecx, %eax
movq %rax, -0x48(%rbp)
leaq -0x1bf(%rbp), %r14
movq %r15, %rcx
leaq -0x2bf(%rbp), %r15
leaq 0x101d(%r12), %r13
leaq 0x18(%r12), %rdx
leaq -0x1(%rcx), %rsi
movq %rcx, -0x50(%rbp)
movq %rcx, %rdi
negq %rdi
xorl %r10d, %r10d
movq $-0x1, %r8
leaq 0x6da5(%rip), %rbx      # 0x10300
xorl %r9d, %r9d
movq %r12, -0x90(%rbp)
movq %rdi, -0x80(%rbp)
movq %rdx, -0x70(%rbp)
movq %r13, -0x68(%rbp)
movl %r9d, -0x3c(%rbp)
movq %r8, -0xb0(%rbp)
movq 0x10(%r12,%r10,8), %rax
movb 0x8(%rax), %al
movb 0x101c(%r12,%r10), %cl
movl %ecx, %edx
xorb %al, %dl
movzbl -0x2c0(%rbp,%r10), %edi
movq %rsi, %r13
movzbl %dl, %esi
movl %esi, %r8d
shll $0x8, %r8d
orq %r8, %rdi
movzbl 0x12000(%rbx,%rdi), %edx
movzbl -0x1c0(%rbp,%r10), %edi
orq %r8, %rdi
movzbl 0x12000(%rbx,%rdi), %edi
movl %ecx, %r8d
xorb -0x29(%rbp), %r8b
movzbl %r8b, %r8d
shll $0x8, %r8d
orq %rdi, %r8
movzbl 0x2000(%rbx,%r8), %edi
movq %rdi, -0xa0(%rbp)
shll $0x8, %edi
movq %rdx, -0x98(%rbp)
orq %rdx, %rdi
movzbl 0x2000(%rbx,%rdi), %edi
shll $0x8, %edi
orq %rsi, %rdi
movb 0x2000(%rbx,%rdi), %sil
movq -0x58(%rbp), %rdi
movb %sil, (%rdi,%r10)
movq %r10, -0xa8(%rbp)
leaq 0x1(%r10), %rdx
movq %rdx, -0x60(%rbp)
xorl %r12d, %r12d
movq -0x38(%rbp), %rdx
movq -0x70(%rbp), %rsi
movq (%rsi,%r12,8), %rsi
movb 0x8(%rsi), %r9b
movq -0x68(%rbp), %rsi
movb (%rsi,%r12), %sil
movzbl (%r15,%r12), %edi
movl %r9d, %r8d
xorb %cl, %r8b
movzbl %r8b, %r10d
shll $0x8, %r10d
orq %r10, %rdi
movb 0x12000(%rbx,%rdi), %r11b
movzbl (%r14,%r12), %edi
movl %esi, %r8d
xorb %al, %r8b
movzbl %r8b, %r8d
shll $0x8, %r8d
orq %r8, %rdi
movb 0x12000(%rbx,%rdi), %dil
movb %r11b, (%rdx,%r12)
xorb %al, %r9b
movzbl %r9b, %r9d
orq %r10, %r9
movzbl (%r15,%r12), %r10d
movzbl 0x12000(%rbx,%r9), %r9d
shll $0x8, %r9d
orq %r10, %r9
xorb %cl, %sil
movzbl %sil, %esi
orq %r8, %rsi
movzbl (%r14,%r12), %r8d
movzbl 0x12000(%rbx,%rsi), %esi
shll $0x8, %esi
orq %r8, %rsi
movb %dil, -0x3c0(%rbp,%r12)
movb 0x2000(%rbx,%r9), %dil
movb %dil, (%r15,%r12)
movb 0x2000(%rbx,%rsi), %sil
movb %sil, (%r14,%r12)
incq %r12
cmpq %r12, %r13
jne 0x9628
movq %r13, -0xb8(%rbp)
movb $0x1, %al
movq -0x98(%rbp), %rcx
cmpl $0x1, %ecx
je 0x9700
movb 0x22000(%rbx,%rcx), %al
movq -0x50(%rbp), %rcx
subl -0x60(%rbp), %ecx
movzbl %al, %edx
movq -0x38(%rbp), %rdi
movq %rdi, %rsi
movl %ecx, %r13d
callq 0xa41e
movq -0xa0(%rbp), %rax
cmpl $0x1, %eax
movb $0x1, %cl
je 0x972e
movb 0x22000(%rbx,%rax), %cl
addq %r12, -0x38(%rbp)
movzbl %cl, %edx
leaq -0x3c0(%rbp), %r12
movq %r12, %rdi
movq %r12, %rsi
movl %r13d, %ecx
callq 0xa41e
movl -0x3c(%rbp), %r9d
movslq %r9d, %rax
addq -0x88(%rbp), %rax
movq -0xb0(%rbp), %r8
movq %r8, %rcx
movq -0x80(%rbp), %rdi
movb (%r12), %sil
incq %r12
movb %sil, (%rax)
addq %rcx, %rax
decq %rcx
cmpq %rcx, %rdi
jne 0x9766
subl -0xa8(%rbp), %r9d
addl $-0x2, %r9d
incq %r14
incq %r15
movq -0x68(%rbp), %r13
incq %r13
movq -0x70(%rbp), %rdx
addq $0x8, %rdx
movq -0xb8(%rbp), %rsi
decq %rsi
decq %r8
movq -0x60(%rbp), %rax
movq %rax, %r10
cmpq -0x48(%rbp), %rax
movq -0x90(%rbp), %r12
jne 0x9569
cmpl $0x1, -0x50(%rbp)
jne 0x97d7
movb -0x29(%rbp), %bl
jmp 0x980d
decl %r15d
movb (%r12), %bl
movl %r15d, %eax
jmp 0x9810
movb -0x29(%rbp), %bl
movq -0x78(%rbp), %r14
movq -0x48(%rbp), %r15
movb 0x101c(%r12,%r15), %al
xorb %bl, %al
movzbl %al, %edx
movq %r14, %rdi
movq %r14, %rsi
movl %r15d, %ecx
callq 0xa41e
addq %r15, %r14
leaq -0x1(%r15), %rax
cmpq $0x1, %r15
movq %rax, %r15
ja 0x97e2
movl -0x30(%rbp), %eax
cltq
movq 0x10(%r12,%rax,8), %rcx
movb 0x101c(%r12,%rax), %dl
movzbl -0x2c0(%rbp,%rax), %esi
movzbl -0x1c0(%rbp,%rax), %edi
xorb %dl, %bl
movzbl %bl, %r8d
shll $0x8, %r8d
orq %rdi, %r8
leaq 0x6abd(%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 -0x58(%rbp), %rdx
movb %cl, (%rdx,%rax)
addq $0x398, %rsp            # imm = 0x398
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq