// File: /workspace/github_cpp_makefile_star2_O2/ralgond[P]SortedListIntersection/algo_highlyscalable_avx512.cpp
// Function: highlyscalable_avx512_intersection(unsigned int const*, unsigned long, unsigned int const*, unsigned long, unsigned int*)
size_t highlyscalable_avx512_intersection(const uint32_t *A, const size_t s_a,
                                     const uint32_t *B, const size_t s_b,
                                     uint32_t *out) {
  assert(out != A);
  assert(out != B);
  const uint32_t *const initout(out);
  size_t i_a = 0, i_b = 0;

  // trim lengths to be a multiple of 16
  size_t st_a = (s_a / 16) * 16;
  size_t st_b = (s_b / 16) * 16;

  while (i_a < st_a && i_b < st_b) {
    __m512i v_a = _mm512_loadu_si512(&A[i_a]);
    __m512i v_b = _mm512_loadu_si512(&B[i_b]);
    //]

    //[ move pointers
    const uint32_t a_max = A[i_a + 15];
    const uint32_t b_max = B[i_b + 15];
    i_a += (a_max <= b_max) * 16;
    i_b += (a_max >= b_max) * 16;
    
    //[ compute mask of common elements
    const __m512i rotate_arg = _mm512_set_epi32(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
    __mmask16 cmp_mask = _mm512_cmpeq_epu32_mask(v_a, v_b); // pairwise comparison

    for (int i = 0; i < 15; i++) {
	    v_b = _mm512_permutexvar_epi32(rotate_arg, v_b);    // shuffling
	    __mmask16 cmp_mask1 = _mm512_cmpeq_epu32_mask(v_a, v_b);
	    cmp_mask = _mm512_kor(cmp_mask, cmp_mask1);
    }

    const int mask = _mm512_mask2int(cmp_mask);
    //cout<<"mask: "<<mask<<endl;

    //[ copy out common elements
    //const __m256i p = _mm256_permutevar8x32_epi32(v_a, shuffle_mask64[mask]);
    //__m256i p; //_mm256_permutevar8x32_epi32(v_a, shuffle_mask64[mask]);
    __m512i p = _mm512_permutexvar_epi32(shuffle_mask64[mask], v_a);
    _mm512_storeu_si512(out, p);
    out += _mm_popcnt_u32(mask); // a number of elements is a weight of the mask
                                 //]
  }

  // intersect the tail using scalar intersection
  while (i_a < s_a && i_b < s_b) {
    if (A[i_a] < B[i_b]) {
      i_a++;
    } else if (B[i_b] < A[i_a]) {
      i_b++;
    } else {
      *out++ = B[i_b];
      ;
      i_a++;
      i_b++;
    }
  }

  return out - initout;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %rbx
pushq %rax
cmpq %rdi, %r8
je 0x401b
cmpq %rdx, %r8
je 0x4034
vpmovsxbd 0x24ce(%rip), %xmm1 # 0x63d8
movq %rsi, %r9
movq %rcx, %r10
andq $-0x10, %r9
andq $-0x10, %r10
vpxor %xmm0, %xmm0, %xmm0
pushq $0xf
popq %rbx
leaq 0x72da(%rip), %r14      # 0xb200
movq %r8, %rax
vpextrq $0x1, %xmm0, %r11
vmovq %xmm0, %r15
cmpq %r9, %r11
jae 0x4001
cmpq %r10, %r15
jae 0x4001
vpbroadcastq 0x38(%rdi,%r11,4), %xmm5
vpbroadcastq 0x38(%rdx,%r15,4), %xmm4
vmovdqu64 (%rdi,%r11,4), %zmm2
vmovdqu64 (%rdx,%r15,4), %zmm3
movl %ebx, %ebp
vmovdqa %xmm5, %xmm6
vpermt2d %xmm4, %xmm1, %xmm6
vpermt2d %xmm5, %xmm1, %xmm4
vpcmpeqd %zmm3, %zmm2, %k0
kmovd %k0, %r11d
vpcmpnltud %xmm4, %xmm6, %k1
vpbroadcastq 0x2751(%rip), %xmm4 {%k1} {z} # 0x66e0
subl $0x1, %ebp
jb 0x3faa
valignd $0x1, %zmm3, %zmm3, %zmm3 # zmm3 = zmm3[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0]
vpcmpeqd %zmm3, %zmm2, %k0
kmovd %k0, %r15d
orl %r15d, %r11d
jmp 0x3f8f
movzwl %r11w, %r11d
vpaddq %xmm0, %xmm4, %xmm0
popcntl %r11d, %r15d
shll $0x6, %r11d
vmovdqa64 (%r11,%r14), %zmm3
vpermd %zmm2, %zmm3, %zmm2
vmovdqu64 %zmm2, (%rax)
leaq (%rax,%r15,4), %rax
jmp 0x3f29
cmpq %rcx, %r15
jae 0x4006
movl (%rdi,%r11,4), %r10d
movl (%rdx,%r15,4), %r9d
cmpl %r9d, %r10d
jae 0x3fee
incq %r11
jmp 0x4001
jbe 0x3ff5
incq %r15
jmp 0x4001
movl %r9d, (%rax)
addq $0x4, %rax
incq %r11
jmp 0x3ff0
cmpq %rsi, %r11
jb 0x3fd7
subq %r8, %rax
sarq $0x2, %rax
addq $0x8, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
vzeroupper
retq
leaq 0x24be(%rip), %rdi      # 0x64e0
leaq 0x26bf(%rip), %rsi      # 0x66e8
leaq 0x26d7(%rip), %rcx      # 0x6707
pushq $0x57
jmp 0x404b
leaq 0x253f(%rip), %rdi      # 0x657a
leaq 0x26a6(%rip), %rsi      # 0x66e8
leaq 0x26be(%rip), %rcx      # 0x6707
pushq $0x58
popq %rdx
callq 0x20f0
nopl (%rax)