// File: /workspace/github_cpp_makefile_star2_O2/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 %rdx, -0x68(%rbp)
movq %rsi, -0x50(%rbp)
movq %rdi, -0x40(%rbp)
movslq 0x810(%rdi), %rdx
xorl %eax, %eax
testl %edx, %edx
movl $0x0, %esi
movq %rdx, -0x38(%rbp)
cmovgl %edx, %esi
movb $0x1, %dl
cmpq %rax, %rsi
je 0x8583
movb %dl, -0x2b0(%rbp,%rax)
movb %dl, -0x1b0(%rbp,%rax)
incq %rax
jmp 0x856b
movq -0x38(%rbp), %rdx
leal -0x1(%rdx), %edi
movl %edi, %eax
imull %edx, %eax
pushq $0x2
popq %rsi
cltd
idivl %esi
cltq
movq %rcx, -0x80(%rbp)
addq %rcx, %rax
decq %rax
movq %rax, -0x88(%rbp)
movq -0x40(%rbp), %rax
movb (%rax), %cl
movb %cl, -0x29(%rbp)
xorl %r9d, %r9d
testl %edi, %edi
movl $0x0, %ecx
movl %edi, -0x54(%rbp)
cmovgl %edi, %ecx
movq %rcx, -0x90(%rbp)
leaq -0x1af(%rbp), %r14
leaq -0x2af(%rbp), %rbx
leaq 0x101d(%rax), %rcx
movq %rcx, -0x78(%rbp)
addq $0x18, %rax
movq %rax, -0x70(%rbp)
pushq $0x1
popq %rdi
pushq $-0x1
popq %r8
leaq 0x6cf9(%rip), %r15      # 0xf2f0
xorl %r10d, %r10d
cmpq -0x90(%rbp), %r9
je 0x8849
movl %r10d, -0x5c(%rbp)
movq %r8, -0xa0(%rbp)
movq %rdi, -0xa8(%rbp)
movq -0x40(%rbp), %rcx
movq 0x10(%rcx,%r9,8), %rax
movb 0x8(%rax), %al
movb 0x101c(%rcx,%r9), %cl
movl %ecx, %edx
xorb %al, %dl
movzbl -0x2b0(%rbp,%r9), %edi
movzbl %dl, %esi
movl %esi, %r8d
shll $0x8, %r8d
orq %r8, %rdi
movzbl 0x12000(%r15,%rdi), %edx
movzbl -0x1b0(%rbp,%r9), %edi
orq %r8, %rdi
movzbl 0x12000(%r15,%rdi), %edi
movl %ecx, %r8d
xorb -0x29(%rbp), %r8b
movzbl %r8b, %r8d
shll $0x8, %r8d
orq %rdi, %r8
movzbl 0x2000(%r15,%r8), %edi
movq %rdi, -0x98(%rbp)
shll $0x8, %edi
movq %rdx, -0x48(%rbp)
orq %rdx, %rdi
movzbl 0x2000(%r15,%rdi), %edi
shll $0x8, %edi
orq %rsi, %rdi
movb 0x2000(%r15,%rdi), %sil
movq -0x68(%rbp), %rdi
movb %sil, (%rdi,%r9)
xorl %r12d, %r12d
movq %r9, %r13
leal (%r9,%r12), %esi
incl %esi
cmpl %esi, -0x38(%rbp)
jle 0x8783
movq -0x70(%rbp), %rdx
movq (%rdx,%r12,8), %rsi
movb 0x8(%rsi), %r8b
movq -0x78(%rbp), %rdx
movb (%rdx,%r12), %sil
movzbl (%rbx,%r12), %edi
movl %r8d, %r9d
xorb %cl, %r9b
movzbl %r9b, %r9d
shll $0x8, %r9d
orq %r9, %rdi
movb 0x12000(%r15,%rdi), %r10b
movzbl (%r14,%r12), %r11d
movl %esi, %edi
xorb %al, %dil
movzbl %dil, %edi
shll $0x8, %edi
orq %rdi, %r11
movb 0x12000(%r15,%r11), %r11b
movq -0x50(%rbp), %rdx
movb %r10b, (%rdx,%r12)
movb %r11b, -0x3b0(%rbp,%r12)
movzbl (%rbx,%r12), %r10d
xorb %al, %r8b
movzbl %r8b, %r8d
orq %r9, %r8
movq %r13, %r9
movzbl 0x12000(%r15,%r8), %r8d
shll $0x8, %r8d
orq %r10, %r8
movb 0x2000(%r15,%r8), %r8b
movb %r8b, (%rbx,%r12)
movzbl (%r14,%r12), %r8d
xorb %cl, %sil
movzbl %sil, %esi
orq %rdi, %rsi
movzbl 0x12000(%r15,%rsi), %esi
shll $0x8, %esi
orq %r8, %rsi
movb 0x2000(%r15,%rsi), %sil
movb %sil, (%r14,%r12)
incq %r12
jmp 0x86b6
movb $0x1, %al
movq -0x48(%rbp), %rcx
cmpl $0x1, %ecx
je 0x8796
movb 0x22000(%r15,%rcx), %al
leaq 0x1(%r9), %rdx
movq -0x38(%rbp), %rcx
movq %rdx, -0x48(%rbp)
subl %edx, %ecx
movzbl %al, %edx
movq -0x50(%rbp), %rdi
movq %rdi, %rsi
movl %ecx, -0x58(%rbp)
callq 0x93b6
movq -0x98(%rbp), %rax
cmpl $0x1, %eax
movb $0x1, %cl
je 0x87cc
movb 0x22000(%r15,%rax), %cl
addq %r12, -0x50(%rbp)
movzbl %cl, %edx
leaq -0x3b0(%rbp), %rdi
movq %rdi, %rsi
movl -0x58(%rbp), %ecx
callq 0x93b6
movl -0x5c(%rbp), %r10d
movslq %r10d, %rax
addq -0x88(%rbp), %rax
xorl %ecx, %ecx
movq -0xa0(%rbp), %r8
movq %r8, %rdx
movq -0xa8(%rbp), %rdi
leal (%rdi,%rcx), %esi
cmpl %esi, -0x38(%rbp)
jle 0x8824
movb -0x3b0(%rbp,%rcx), %sil
movb %sil, (%rax)
addq %rdx, %rax
decq %rdx
incq %rcx
jmp 0x8806
subl %r13d, %r10d
addl $-0x2, %r10d
incq %rdi
incq %r14
incq %rbx
incq -0x78(%rbp)
addq $0x8, -0x70(%rbp)
decq %r8
movq -0x48(%rbp), %r9
jmp 0x85fa
movl -0x54(%rbp), %r12d
movq -0x80(%rbp), %rbx
testl %r12d, %r12d
jle 0x887e
movq -0x40(%rbp), %rax
movb 0x101c(%rax,%r12), %al
xorb -0x29(%rbp), %al
movzbl %al, %edx
movq %rbx, %rdi
movq %rbx, %rsi
movl %r12d, %ecx
callq 0x93b6
addq %r12, %rbx
decq %r12
jmp 0x8851
movq -0x40(%rbp), %rcx
movq -0x38(%rbp), %r8
movq 0x8(%rcx,%r8,8), %rax
movb 0x101b(%rcx,%r8), %cl
movzbl -0x2b1(%rbp,%r8), %edx
movzbl -0x1b1(%rbp,%r8), %esi
movb -0x29(%rbp), %dil
xorb %cl, %dil
movzbl %dil, %edi
shll $0x8, %edi
orq %rsi, %rdi
movzbl 0x2000(%r15,%rdi), %esi
shll $0x8, %esi
orq %rdx, %rsi
movzbl 0x2000(%r15,%rsi), %edx
xorb 0x8(%rax), %cl
movzbl %cl, %eax
shll $0x8, %eax
orq %rdx, %rax
movb 0x12000(%r15,%rax), %al
movq -0x68(%rbp), %rcx
movb %al, -0x1(%rcx,%r8)
addq $0x388, %rsp            # imm = 0x388
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
nop