// File: 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
subq $0x440, %rsp            # imm = 0x440
movq %rdi, -0x70(%rbp)
movq %rsi, -0x78(%rbp)
movq %rdx, -0x80(%rbp)
movq %rcx, -0x88(%rbp)
movq -0x70(%rbp), %rax
movq %rax, -0x410(%rbp)
movl 0x810(%rax), %eax
movl %eax, -0x8c(%rbp)
movl $0x0, -0x294(%rbp)
movl -0x294(%rbp), %eax
cmpl -0x8c(%rbp), %eax
jge 0xefbc
movslq -0x294(%rbp), %rax
movb $0x1, -0x190(%rbp,%rax)
movslq -0x294(%rbp), %rax
movb $0x1, -0x290(%rbp,%rax)
movl -0x294(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x294(%rbp)
jmp 0xef7f
movq -0x88(%rbp), %rcx
movl -0x8c(%rbp), %eax
subl $0x1, %eax
imull -0x8c(%rbp), %eax
movl $0x2, %esi
cltd
idivl %esi
movl %eax, %edx
movq -0x410(%rbp), %rax
movslq %edx, %rdx
addq %rdx, %rcx
addq $-0x1, %rcx
movq %rcx, -0x3a8(%rbp)
movl $0x0, -0x3ac(%rbp)
movl (%rax), %eax
movb %al, -0x3ad(%rbp)
movl $0x0, -0x3b4(%rbp)
movl -0x3b4(%rbp), %eax
movl -0x8c(%rbp), %ecx
subl $0x1, %ecx
cmpl %ecx, %eax
jge 0xf62c
movq -0x410(%rbp), %rax
movslq -0x3b4(%rbp), %rcx
movq 0x10(%rax,%rcx,8), %rcx
movb 0x8(%rcx), %cl
movb %cl, -0x3b5(%rbp)
movslq -0x3b4(%rbp), %rcx
movb 0x101c(%rax,%rcx), %al
movb %al, -0x3b6(%rbp)
movb -0x3b5(%rbp), %cl
movb -0x3b6(%rbp), %al
movb %cl, -0x4d(%rbp)
movb %al, -0x4e(%rbp)
movzbl -0x4d(%rbp), %eax
movzbl -0x4e(%rbp), %ecx
xorl %ecx, %eax
movb %al, -0x3b7(%rbp)
movslq -0x3b4(%rbp), %rax
movb -0x190(%rbp,%rax), %cl
movb -0x3b7(%rbp), %al
movb %cl, -0x3f(%rbp)
movb %al, -0x40(%rbp)
movzbl -0x40(%rbp), %eax
shll $0x8, %eax
movzbl -0x3f(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x92e4(%rip), %rax      # 0x18390
movb 0x12000(%rax,%rcx), %al
movb %al, -0x3b8(%rbp)
movslq -0x3b4(%rbp), %rax
movb -0x290(%rbp,%rax), %cl
movb -0x3b7(%rbp), %al
movb %cl, -0x41(%rbp)
movb %al, -0x42(%rbp)
movzbl -0x42(%rbp), %eax
shll $0x8, %eax
movzbl -0x41(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x92a5(%rip), %rax      # 0x18390
movb 0x12000(%rax,%rcx), %cl
movb -0x3ad(%rbp), %dl
movb -0x3b6(%rbp), %al
movb %dl, -0x4f(%rbp)
movb %al, -0x50(%rbp)
movzbl -0x4f(%rbp), %eax
movzbl -0x50(%rbp), %edx
xorl %edx, %eax
movb %cl, -0x31(%rbp)
movb %al, -0x32(%rbp)
movzbl -0x32(%rbp), %eax
shll $0x8, %eax
movzbl -0x31(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x9264(%rip), %rax      # 0x18390
movb 0x2000(%rax,%rcx), %al
movb %al, -0x3b9(%rbp)
movb -0x3b7(%rbp), %cl
movb -0x3b8(%rbp), %dl
movb -0x3b9(%rbp), %al
movb %dl, -0x33(%rbp)
movb %al, -0x34(%rbp)
movzbl -0x34(%rbp), %eax
shll $0x8, %eax
movzbl -0x33(%rbp), %edx
addl %edx, %eax
movl %eax, %eax
movl %eax, %edx
leaq 0x9227(%rip), %rax      # 0x18390
movb 0x2000(%rax,%rdx), %al
movb %cl, -0x35(%rbp)
movb %al, -0x36(%rbp)
movzbl -0x36(%rbp), %eax
shll $0x8, %eax
movzbl -0x35(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x9202(%rip), %rax      # 0x18390
movb 0x2000(%rax,%rcx), %dl
movq -0x80(%rbp), %rax
movslq -0x3b4(%rbp), %rcx
movb %dl, (%rax,%rcx)
movq -0x78(%rbp), %rax
movq %rax, -0x3c8(%rbp)
leaq -0x3a0(%rbp), %rax
movq %rax, -0x3d0(%rbp)
movl -0x3b4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3d4(%rbp)
movl -0x3d4(%rbp), %eax
cmpl -0x8c(%rbp), %eax
jge 0xf443
movq -0x410(%rbp), %rax
movslq -0x3d4(%rbp), %rcx
movq 0x10(%rax,%rcx,8), %rcx
movb 0x8(%rcx), %cl
movb %cl, -0x3d5(%rbp)
movslq -0x3d4(%rbp), %rcx
movb 0x101c(%rax,%rcx), %al
movb %al, -0x3d6(%rbp)
movslq -0x3d4(%rbp), %rax
movb -0x190(%rbp,%rax), %cl
movb -0x3d5(%rbp), %dl
movb -0x3b6(%rbp), %al
movb %dl, -0x51(%rbp)
movb %al, -0x52(%rbp)
movzbl -0x51(%rbp), %eax
movzbl -0x52(%rbp), %edx
xorl %edx, %eax
movb %cl, -0x43(%rbp)
movb %al, -0x44(%rbp)
movzbl -0x44(%rbp), %eax
shll $0x8, %eax
movzbl -0x43(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x913b(%rip), %rax      # 0x18390
movb 0x12000(%rax,%rcx), %al
movb %al, -0x3d7(%rbp)
movslq -0x3d4(%rbp), %rax
movb -0x290(%rbp,%rax), %cl
movb -0x3b5(%rbp), %dl
movb -0x3d6(%rbp), %al
movb %dl, -0x53(%rbp)
movb %al, -0x54(%rbp)
movzbl -0x53(%rbp), %eax
movzbl -0x54(%rbp), %edx
xorl %edx, %eax
movb %cl, -0x45(%rbp)
movb %al, -0x46(%rbp)
movzbl -0x46(%rbp), %eax
shll $0x8, %eax
movzbl -0x45(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x90e6(%rip), %rax      # 0x18390
movb 0x12000(%rax,%rcx), %al
movb %al, -0x3d8(%rbp)
movb -0x3d7(%rbp), %cl
movq -0x78(%rbp), %rax
movq %rax, %rdx
addq $0x1, %rdx
movq %rdx, -0x78(%rbp)
movb %cl, (%rax)
movb -0x3d8(%rbp), %cl
movq -0x3d0(%rbp), %rax
movq %rax, %rdx
addq $0x1, %rdx
movq %rdx, -0x3d0(%rbp)
movb %cl, (%rax)
movslq -0x3d4(%rbp), %rax
movb -0x190(%rbp,%rax), %cl
movb -0x3d5(%rbp), %dl
movb -0x3b5(%rbp), %al
movb %dl, -0x55(%rbp)
movb %al, -0x56(%rbp)
movzbl -0x55(%rbp), %eax
movzbl -0x56(%rbp), %edx
xorl %edx, %eax
movb %al, %dl
movb -0x3d5(%rbp), %sil
movb -0x3b6(%rbp), %al
movb %sil, -0x57(%rbp)
movb %al, -0x58(%rbp)
movzbl -0x57(%rbp), %eax
movzbl -0x58(%rbp), %esi
xorl %esi, %eax
movb %dl, -0x47(%rbp)
movb %al, -0x48(%rbp)
movzbl -0x48(%rbp), %eax
shll $0x8, %eax
movzbl -0x47(%rbp), %edx
addl %edx, %eax
movl %eax, %eax
movl %eax, %edx
leaq 0x903d(%rip), %rax      # 0x18390
movb 0x12000(%rax,%rdx), %al
movb %cl, -0x37(%rbp)
movb %al, -0x38(%rbp)
movzbl -0x38(%rbp), %eax
shll $0x8, %eax
movzbl -0x37(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x9018(%rip), %rax      # 0x18390
movb 0x2000(%rax,%rcx), %cl
movslq -0x3d4(%rbp), %rax
movb %cl, -0x190(%rbp,%rax)
movslq -0x3d4(%rbp), %rax
movb -0x290(%rbp,%rax), %cl
movb -0x3d6(%rbp), %dl
movb -0x3b6(%rbp), %al
movb %dl, -0x59(%rbp)
movb %al, -0x5a(%rbp)
movzbl -0x59(%rbp), %eax
movzbl -0x5a(%rbp), %edx
xorl %edx, %eax
movb %al, %dl
movb -0x3d6(%rbp), %sil
movb -0x3b5(%rbp), %al
movb %sil, -0x5b(%rbp)
movb %al, -0x5c(%rbp)
movzbl -0x5b(%rbp), %eax
movzbl -0x5c(%rbp), %esi
xorl %esi, %eax
movb %dl, -0x49(%rbp)
movb %al, -0x4a(%rbp)
movzbl -0x4a(%rbp), %eax
shll $0x8, %eax
movzbl -0x49(%rbp), %edx
addl %edx, %eax
movl %eax, %eax
movl %eax, %edx
leaq 0x8f9b(%rip), %rax      # 0x18390
movb 0x12000(%rax,%rdx), %al
movb %cl, -0x39(%rbp)
movb %al, -0x3a(%rbp)
movzbl -0x3a(%rbp), %eax
shll $0x8, %eax
movzbl -0x39(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x8f76(%rip), %rax      # 0x18390
movb 0x2000(%rax,%rcx), %cl
movslq -0x3d4(%rbp), %rax
movb %cl, -0x290(%rbp,%rax)
movl -0x3d4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3d4(%rbp)
jmp 0xf1cb
movl -0x8c(%rbp), %eax
movl -0x3b4(%rbp), %ecx
addl $0x1, %ecx
subl %ecx, %eax
movl %eax, -0x3dc(%rbp)
movq -0x3c8(%rbp), %rsi
movq -0x3c8(%rbp), %rdx
movb -0x3b8(%rbp), %cl
movl -0x3dc(%rbp), %eax
movq %rsi, -0x8(%rbp)
movq %rdx, -0x10(%rbp)
movb %cl, -0x11(%rbp)
movl %eax, -0x18(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0x420(%rbp)
movq -0x10(%rbp), %rax
movq %rax, -0x418(%rbp)
movzbl -0x11(%rbp), %eax
cmpl $0x1, %eax
jne 0xf4ab
movb $0x1, %al
movb %al, -0x421(%rbp)
jmp 0xf4c5
movzbl -0x11(%rbp), %eax
movl %eax, %ecx
leaq 0x8ed8(%rip), %rax      # 0x18390
movb 0x22000(%rax,%rcx), %al
movb %al, -0x421(%rbp)
movq -0x418(%rbp), %rsi
movq -0x420(%rbp), %rdi
movb -0x421(%rbp), %al
movl -0x18(%rbp), %ecx
movzbl %al, %edx
callq 0x11890
leaq -0x3a0(%rbp), %rsi
leaq -0x3a0(%rbp), %rdx
movb -0x3b9(%rbp), %cl
movl -0x3dc(%rbp), %eax
movq %rsi, -0x20(%rbp)
movq %rdx, -0x28(%rbp)
movb %cl, -0x29(%rbp)
movl %eax, -0x30(%rbp)
movq -0x20(%rbp), %rax
movq %rax, -0x438(%rbp)
movq -0x28(%rbp), %rax
movq %rax, -0x430(%rbp)
movzbl -0x29(%rbp), %eax
cmpl $0x1, %eax
jne 0xf535
movb $0x1, %al
movb %al, -0x439(%rbp)
jmp 0xf54f
movzbl -0x29(%rbp), %eax
movl %eax, %ecx
leaq 0x8e4e(%rip), %rax      # 0x18390
movb 0x22000(%rax,%rcx), %al
movb %al, -0x439(%rbp)
movq -0x430(%rbp), %rsi
movq -0x438(%rbp), %rdi
movb -0x439(%rbp), %al
movl -0x30(%rbp), %ecx
movzbl %al, %edx
callq 0x11890
movq -0x3a8(%rbp), %rax
movslq -0x3ac(%rbp), %rcx
addq %rcx, %rax
movq %rax, -0x3e8(%rbp)
leaq -0x3a0(%rbp), %rax
movq %rax, -0x3d0(%rbp)
movl -0x3b4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3ec(%rbp)
movl -0x3ec(%rbp), %eax
cmpl -0x8c(%rbp), %eax
jge 0xf601
movq -0x3d0(%rbp), %rax
movq %rax, %rcx
addq $0x1, %rcx
movq %rcx, -0x3d0(%rbp)
movb (%rax), %cl
movq -0x3e8(%rbp), %rax
movb %cl, (%rax)
movl -0x3ec(%rbp), %ecx
movq -0x3e8(%rbp), %rax
movslq %ecx, %rdx
xorl %ecx, %ecx
subq %rdx, %rcx
addq %rcx, %rax
movq %rax, -0x3e8(%rbp)
movl -0x3ec(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3ec(%rbp)
jmp 0xf5a3
movl -0x3b4(%rbp), %ecx
addl $0x2, %ecx
movl -0x3ac(%rbp), %eax
subl %ecx, %eax
movl %eax, -0x3ac(%rbp)
movl -0x3b4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3b4(%rbp)
jmp 0xf011
movq -0x88(%rbp), %rax
movq %rax, -0x3f8(%rbp)
movl -0x8c(%rbp), %eax
subl $0x1, %eax
movl %eax, -0x3fc(%rbp)
cmpl $0x0, -0x3fc(%rbp)
jle 0xf6e3
movq -0x410(%rbp), %rax
movslq -0x3fc(%rbp), %rcx
movb 0x101c(%rax,%rcx), %al
movb %al, -0x3fd(%rbp)
movl -0x3fc(%rbp), %eax
movl %eax, -0x404(%rbp)
movq -0x3f8(%rbp), %rdi
movq -0x3f8(%rbp), %rsi
movb -0x3ad(%rbp), %cl
movb -0x3fd(%rbp), %al
movb %cl, -0x5d(%rbp)
movb %al, -0x5e(%rbp)
movzbl -0x5d(%rbp), %eax
movzbl -0x5e(%rbp), %ecx
xorl %ecx, %eax
movl -0x404(%rbp), %ecx
movzbl %al, %edx
callq 0x11890
movl -0x404(%rbp), %ecx
movq -0x3f8(%rbp), %rax
movslq %ecx, %rcx
addq %rcx, %rax
movq %rax, -0x3f8(%rbp)
movl -0x3fc(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0x3fc(%rbp)
jmp 0xf649
movq -0x410(%rbp), %rax
movl -0x8c(%rbp), %ecx
subl $0x1, %ecx
movslq %ecx, %rcx
movq 0x10(%rax,%rcx,8), %rcx
movb 0x8(%rcx), %cl
movb %cl, -0x405(%rbp)
movl -0x8c(%rbp), %ecx
subl $0x1, %ecx
movslq %ecx, %rcx
movb 0x101c(%rax,%rcx), %al
movb %al, -0x406(%rbp)
movl -0x8c(%rbp), %eax
subl $0x1, %eax
cltq
movb -0x190(%rbp,%rax), %al
movb %al, -0x407(%rbp)
movl -0x8c(%rbp), %eax
subl $0x1, %eax
cltq
movb -0x290(%rbp,%rax), %cl
movb -0x3ad(%rbp), %dl
movb -0x406(%rbp), %al
movb %dl, -0x5f(%rbp)
movb %al, -0x60(%rbp)
movzbl -0x5f(%rbp), %eax
movzbl -0x60(%rbp), %edx
xorl %edx, %eax
movb %cl, -0x3b(%rbp)
movb %al, -0x3c(%rbp)
movzbl -0x3c(%rbp), %eax
shll $0x8, %eax
movzbl -0x3b(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x8c0f(%rip), %rax      # 0x18390
movb 0x2000(%rax,%rcx), %al
movb %al, -0x408(%rbp)
movb -0x407(%rbp), %cl
movb -0x408(%rbp), %al
movb %cl, -0x3d(%rbp)
movb %al, -0x3e(%rbp)
movzbl -0x3e(%rbp), %eax
shll $0x8, %eax
movzbl -0x3d(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x8bd8(%rip), %rax      # 0x18390
movb 0x2000(%rax,%rcx), %cl
movb -0x405(%rbp), %dl
movb -0x406(%rbp), %al
movb %dl, -0x61(%rbp)
movb %al, -0x62(%rbp)
movzbl -0x61(%rbp), %eax
movzbl -0x62(%rbp), %edx
xorl %edx, %eax
movb %cl, -0x4b(%rbp)
movb %al, -0x4c(%rbp)
movzbl -0x4c(%rbp), %eax
shll $0x8, %eax
movzbl -0x4b(%rbp), %ecx
addl %ecx, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x8b97(%rip), %rax      # 0x18390
movb 0x12000(%rax,%rcx), %dl
movq -0x80(%rbp), %rax
movl -0x8c(%rbp), %ecx
subl $0x1, %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
addq $0x440, %rsp            # imm = 0x440
popq %rbp
retq
nopl (%rax)