// File: /workspace/github_c_makefile_star2_O2/samtools[P]htslib/htscodecs/htscodecs/rANS_static32x16pr_avx512.c
// Function: rans_uncompress_O0_32x16_avx512
unsigned char *rans_uncompress_O0_32x16_avx512(unsigned char *in,
                                               unsigned int in_size,
                                               unsigned char *out,
                                               unsigned int out_sz) {
    if (in_size < 32*4) // 32-states at least
        return NULL;

    if (out_sz >= INT_MAX)
        return NULL; // protect against some overflow cases

    /* Load in the static tables */
    unsigned char *cp = in, *out_free = NULL;
    unsigned char *cp_end = in + in_size;
    int i;
    uint32_t s3[TOTFREQ]  __attribute__((aligned(64))); // For TF_SHIFT <= 12

    if (!out)
        out_free = out = malloc(out_sz);
    if (!out)
        return NULL;

    // Precompute reverse lookup of frequency.
    uint32_t F[256] = {0}, fsum;
    int fsz = decode_freq(cp, cp_end, F, &fsum);
    if (!fsz)
        goto err;
    cp += fsz;

    normalise_freq_shift(F, fsum, TOTFREQ);

    // Build symbols; fixme, do as part of decode, see the _d variant
    if (rans_F_to_s3(F, TF_SHIFT, s3))
        goto err;

    if (cp_end - cp < 32 * 4)
        goto err;

    int z;
    RansState Rv[32] __attribute__((aligned(64)));
    for (z = 0; z < 32; z++) {
        RansDecInit(&Rv[z], &cp);
        if (Rv[z] < RANS_BYTE_L)
            goto err;
    }

    uint16_t *sp = (uint16_t *)cp;

    int out_end = (out_sz&~(32-1));
    const uint32_t mask = (1u << TF_SHIFT)-1;

    __m512i maskv = _mm512_set1_epi32(mask); // set mask in all lanes
    __m512i R1 = _mm512_load_epi32(&Rv[0]);
    __m512i R2 = _mm512_load_epi32(&Rv[16]);

    // Start of the first loop iteration, which we do move to the end of the
    // loop for the next cycle so we can remove some of the instr. latency.
    __m512i masked1 = _mm512_and_epi32(R1, maskv);
    __m512i masked2 = _mm512_and_epi32(R2, maskv);
    __m512i S1 = _mm512_i32gather_epi32x(masked1, (int *)s3, sizeof(*s3));
    __m512i S2 = _mm512_i32gather_epi32x(masked2, (int *)s3, sizeof(*s3));

    uint8_t overflow[64+64] = {0};
    for (i=0; i < out_end; i+=32) {
      //for (z = 0; z < 16; z++) {

      // Protect against running off the end of in buffer.
      // We copy it to a worst-case local buffer when near the end.
      if ((uint8_t *)sp+64 > cp_end) {
        memmove(overflow, sp, cp_end - (uint8_t *)sp);
        sp = (uint16_t *)overflow;
        cp_end = overflow + sizeof(overflow);
      }

      //uint32_t S = s3[R[z] & mask];
      __m512i renorm_words1 = _mm512_cvtepu16_epi32(_mm256_loadu_si256((const __m256i *)sp)); // next 16 words

      //uint16_t f = S>>(TF_SHIFT+8), b = (S>>8) & mask;
      __m512i f1 = _mm512_srli_epi32(S1, TF_SHIFT+8);
      __m512i f2 = _mm512_srli_epi32(S2, TF_SHIFT+8);
      __m512i b1 = _mm512_and_epi32(_mm512_srli_epi32(S1, 8), maskv);
      __m512i b2 = _mm512_and_epi32(_mm512_srli_epi32(S2, 8), maskv);

      //R[z] = f * (R[z] >> TF_SHIFT) + b;
      // approx 10 cycle latency on mullo.
      R1 = _mm512_add_epi32(
               _mm512_mullo_epi32(
                   _mm512_srli_epi32(R1, TF_SHIFT), f1), b1);
      __mmask16 renorm_mask1, renorm_mask2;
      renorm_mask1=_mm512_cmplt_epu32_mask(R1, _mm512_set1_epi32(RANS_BYTE_L));
      R2 = _mm512_add_epi32(
               _mm512_mullo_epi32(
                   _mm512_srli_epi32(R2, TF_SHIFT), f2), b2);

      // renorm. this is the interesting part:
      renorm_mask2=_mm512_cmplt_epu32_mask(R2, _mm512_set1_epi32(RANS_BYTE_L));
      // advance by however many words we actually read
      sp += _mm_popcnt_u32(renorm_mask1);
      __m512i renorm_words2 = _mm512_cvtepu16_epi32(_mm256_loadu_si256(
                                      (const __m256i *)sp));

      // select masked only
      __m512i renorm_vals1, renorm_vals2;

      renorm_vals1 = _mm512_mask_expand_epi32(R1, renorm_mask1, renorm_words1);
      renorm_vals2 = _mm512_mask_expand_epi32(R2, renorm_mask2, renorm_words2);

      // For start of next loop iteration.  This has been moved here
      // (and duplicated to before the loop starts) so we can do something
      // with the latency period of gather, such as finishing up the
      // renorm offset and writing the results.
      __m512i S1_ = S1; // temporary copy for use in out[]=S later
      __m512i S2_ = S2;

      masked1 = _mm512_and_epi32(renorm_vals1, maskv);
      S1 = _mm512_i32gather_epi32x(masked1, (int *)s3, sizeof(*s3));
      masked2 = _mm512_and_epi32(renorm_vals2, maskv);
      S2 = _mm512_i32gather_epi32x(masked2, (int *)s3, sizeof(*s3));

      R1 = _mm512_mask_slli_epi32(R1, renorm_mask1, R1, 16);
      R2 = _mm512_mask_slli_epi32(R2, renorm_mask2, R2, 16);

      __m512i m16 = _mm512_set1_epi32(0xffff);
      renorm_vals1 = _mm512_maskz_and_epi32(renorm_mask1, renorm_vals1, m16);
      renorm_vals2 = _mm512_maskz_and_epi32(renorm_mask2, renorm_vals2, m16);

      // advance by however many words we actually read
      sp += _mm_popcnt_u32(renorm_mask2);

      R1 = _mm512_add_epi32(R1, renorm_vals1);
      R2 = _mm512_add_epi32(R2, renorm_vals2);

      //out[i+z] = S;
      _mm_storeu_si128((__m128i *)(out+i),    _mm512_cvtepi32_epi8(S1_));
      _mm_storeu_si128((__m128i *)(out+i+16), _mm512_cvtepi32_epi8(S2_));
    }

    _mm512_store_epi32(&Rv[ 0], R1);
    _mm512_store_epi32(&Rv[16], R2);

    for (z = out_sz & (32-1); z-- > 0; )
      out[out_end + z] = s3[Rv[z] & mask];

    return out;

 err:
    free(out_free);
    return NULL;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
andq $-0x40, %rsp
subq $0x4740, %rsp           # imm = 0x4740
movl %esi, %ebx
cmpl $0x80, %esi
setb %al
cmpl $0x7fffffff, %ecx       # imm = 0x7FFFFFFF
setae %sil
orb %al, %sil
jne 0x5736d
movq %rdi, %r12
movq %rcx, 0x28(%rsp)
xorl %r13d, %r13d
testq %rdx, %rdx
je 0x57215
xorl %eax, %eax
jmp 0x5722a
movl 0x28(%rsp), %edi
callq 0x4680
movq %rax, %rdx
testq %rax, %rax
je 0x5736d
movq %rax, 0xc0(%rsp)
movq %rdx, 0x38(%rsp)
movl %ebx, %eax
movq %rax, 0x40(%rsp)
leaq (%r12,%rax), %r14
leaq 0x300(%rsp), %rbx
movl $0x400, %edx            # imm = 0x400
movq %rbx, %rdi
xorl %esi, %esi
callq 0x4260
movq %r12, %rdi
movq %r14, 0x30(%rsp)
movq %r14, %rsi
movq %rbx, %rdx
callq 0x59769
movslq %eax, %rbx
addq %r12, %rbx
xorl %r15d, %r15d
cmpq $0x400, %r15            # imm = 0x400
je 0x572b2
cmpl $0x0, 0x300(%rsp,%r15)
je 0x572ac
leaq (%rsp,%r15), %r14
addq $0x300, %r14            # imm = 0x300
movq %rbx, %rdi
movq 0x30(%rsp), %rsi
movq %r14, %rdx
callq 0x5938d
cltq
addq %rax, %rbx
addl (%r14), %r13d
addq $0x4, %r15
jmp 0x57275
subq %r12, %rbx
testl %ebx, %ebx
movq 0x38(%rsp), %r15
je 0x57360
movslq %ebx, %rbx
leaq 0x300(%rsp), %rdi
movl %r13d, %esi
movl $0x1000, %edx           # imm = 0x1000
callq 0x5767d
xorl %eax, %eax
xorl %ecx, %ecx
cmpq $0x100, %rax            # imm = 0x100
je 0x57320
movl 0x300(%rsp,%rax,4), %edx
movl $0x1000, %esi           # imm = 0x1000
subl %ecx, %esi
leal -0x1(%rdx), %edi
cmpl %esi, %edi
jae 0x5731b
movl %edx, %esi
shll $0x14, %esi
addl %eax, %esi
subl $0x1, %edx
jb 0x5731b
movslq %ecx, %rcx
movl %esi, 0x700(%rsp,%rcx,4)
incl %ecx
addl $0x100, %esi            # imm = 0x100
jmp 0x57302
incq %rax
jmp 0x572de
cmpl $0x1000, %ecx           # imm = 0x1000
jne 0x57360
movq 0x40(%rsp), %rax
subq %rbx, %rax
cmpq $0x80, %rax
jl 0x57360
addq %rbx, %r12
xorl %r14d, %r14d
cmpq $0x80, %r14
je 0x57385
movl (%r12,%r14), %eax
movl %eax, 0x180(%rsp,%r14)
cmpl $0x8000, %eax           # imm = 0x8000
jb 0x57360
addq $0x4, %r14
jmp 0x5733e
movq 0xc0(%rsp), %rdi
callq 0x45c0
xorl %r15d, %r15d
movq %r15, %rax
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
vzeroupper
retq
movq 0x28(%rsp), %rbx
andl $0x7fffffe0, %ebx       # imm = 0x7FFFFFE0
vmovdqa64 0x180(%rsp), %zmm0
vmovdqa64 %zmm0, 0x80(%rsp)
vmovdqa64 0x1c0(%rsp), %zmm2
vmovdqa64 %zmm2, 0x40(%rsp)
vpbroadcastq 0x2691e(%rip), %zmm1 # 0x7dcd8
vpandq %zmm1, %zmm0, %zmm0
vpandq %zmm1, %zmm2, %zmm1
vmovdqa64 %zmm1, 0x100(%rsp)
leaq 0x700(%rsp), %r13
movq %r13, %rdi
callq 0x570b9
vmovdqa64 %zmm0, 0xc0(%rsp)
vmovdqa64 0x100(%rsp), %zmm0
movq %r13, %rdi
callq 0x570b9
vmovdqa64 0x80(%rsp), %zmm5
vmovdqa64 0x40(%rsp), %zmm6
vpxor %xmm1, %xmm1, %xmm1
leaq 0x300(%rsp), %rax
vmovdqa64 %zmm1, -0x80(%rax)
vmovdqa64 %zmm1, -0x40(%rax)
addq %r14, %r12
xorl %r14d, %r14d
cmpq %rbx, %r14
movq 0x30(%rsp), %rdx
jae 0x57638
leaq 0x40(%r12), %rax
cmpq %rdx, %rax
vmovdqa64 %zmm0, 0x140(%rsp)
jbe 0x57494
subq %r12, %rdx
movq %r12, %rsi
leaq 0x280(%rsp), %r12
movq %r12, %rdi
vmovdqa64 %zmm6, 0x40(%rsp)
vmovdqa64 %zmm5, 0x80(%rsp)
vzeroupper
callq 0x4830
vmovdqa64 0x140(%rsp), %zmm0
vmovdqa64 0x80(%rsp), %zmm5
vmovdqa64 0x40(%rsp), %zmm6
leaq 0x300(%rsp), %rax
movq %rax, 0x30(%rsp)
vpmovzxwd (%r12), %zmm7
vmovdqa64 0xc0(%rsp), %zmm3
vpsrld $0x14, %zmm3, %zmm1
vpsrld $0x14, %zmm0, %zmm2
vpsrld $0x8, %zmm3, %zmm3
vpsrld $0x8, %zmm0, %zmm4
vpsrld $0xc, %zmm5, %zmm5
vpmulld %zmm1, %zmm5, %zmm1
vpbroadcastd 0x267ee(%rip), %zmm5 # 0x7dcc4
vpandd %zmm5, %zmm3, %zmm3
vpaddd %zmm3, %zmm1, %zmm0
vpbroadcastd 0x2a878(%rip), %zmm3 # 0x81d64
vpcmpltud %zmm3, %zmm0, %k1
vmovdqa64 %zmm0, 0x80(%rsp)
kmovw %k1, 0x26(%rsp)
vpsrld $0xc, %zmm6, %zmm1
vpmulld %zmm2, %zmm1, %zmm1
vpandd %zmm5, %zmm4, %zmm2
vpaddd %zmm2, %zmm1, %zmm4
vmovdqa64 %zmm4, 0x40(%rsp)
vpcmpltud %zmm3, %zmm4, %k2
kmovw %k2, 0x24(%rsp)
kmovw %k1, %eax
popcntl %eax, %eax
leaq (%r12,%rax,2), %r12
vpmovzxwd (%r12), %zmm1
vmovdqa64 %zmm0, %zmm2
vpexpandd %zmm7, %zmm2 {%k1}
vmovdqa64 %zmm2, 0x200(%rsp)
vmovdqa64 %zmm4, %zmm0
vpexpandd %zmm1, %zmm0 {%k2}
vmovdqa64 %zmm0, 0x100(%rsp)
vpbroadcastq 0x26764(%rip), %zmm0 # 0x7dcd8
vpandq %zmm0, %zmm2, %zmm0
movq %r13, %rdi
callq 0x570b9
vmovdqa64 %zmm0, 0x240(%rsp)
vpbroadcastq 0x26744(%rip), %zmm0 # 0x7dcd8
vpandq 0x100(%rsp), %zmm0, %zmm0
movq %r13, %rdi
callq 0x570b9
vmovdqa64 0x80(%rsp), %zmm5
vmovdqa64 0x40(%rsp), %zmm6
vmovdqa64 0xc0(%rsp), %zmm1
vpmovdb %zmm1, (%r15,%r14)
vmovdqa64 0x140(%rsp), %zmm1
vpmovdb %zmm1, 0x10(%r15,%r14)
vpslld $0x10, %zmm5, %zmm1
vpslld $0x10, %zmm6, %zmm2
vpbroadcastd 0x2a775(%rip), %zmm4 # 0x81d60
vpandq 0x200(%rsp), %zmm4, %zmm3
vpandq 0x100(%rsp), %zmm4, %zmm4
kmovw 0x24(%rsp), %k1
kmovw %k1, %eax
popcntl %eax, %eax
leaq (%r12,%rax,2), %r12
kmovw 0x26(%rsp), %k2
vpord %zmm1, %zmm3, %zmm5 {%k2}
vpord %zmm2, %zmm4, %zmm6 {%k1}
addq $0x20, %r14
vmovdqa64 0x240(%rsp), %zmm1
vmovdqa64 %zmm1, 0xc0(%rsp)
jmp 0x57426
vmovdqa64 %zmm5, 0x180(%rsp)
vmovdqa64 %zmm6, 0x1c0(%rsp)
movq 0x28(%rsp), %rsi
andl $0x1f, %esi
leaq (%rbx,%r15), %rax
decq %rax
movl $0xfff, %ecx            # imm = 0xFFF
testq %rsi, %rsi
jle 0x57370
movl 0x17c(%rsp,%rsi,4), %edx
andl %ecx, %edx
movb 0x700(%rsp,%rdx,4), %dl
movb %dl, (%rax,%rsi)
decq %rsi
jmp 0x5765c