// File: /workspace/github_c_makefile_star2_O3/felipelouza[P]egap/multiround.c
// Function: multiround
void multiround(bool hm, int group_size,char *path, g_data *input, int num_threads)
{
  customInt offset, tot_symb=0;
  // threads related
  pc_system merge;
  pthread_t t[num_threads];
  g_data buffer[Threads_buf_size];
  int e;
  
  // init threads
  if(num_threads>0) {
    pc_system_init(&merge,Threads_buf_size);
    merge.buffer = (void *) buffer;
    merge.hm = hm;
    for(int i=0;i<num_threads;i++) {
      e = pthread_create(&t[i], NULL, merger, &merge);
      if(e) die("multiround create");
    }
    if(input->verbose>0) printf("%d merger threads created\n",num_threads);
  }
  
  // multiround merging  
  for(int r=0; ;r++) {     // do as many rounds as necessary
    
    if(input->numBwt<= 2*group_size-1 ) 
       break;  // we have reached the last round (or almost)
       
    // main loop to be executed by parallel threads
    // offset is within the number of bwt
    if(num_threads>0) {
      e = pthread_mutex_lock(&merge.remutex);
      if(e) die("remaining lock 1");
      merge.remaining = (input->numBwt+group_size-1)/group_size; // parallel merges in this round
      e= pthread_mutex_unlock(&merge.remutex);
      if(e) die("remaining unlock 1");
    }
    tot_symb = 0; 
    for(offset=0; offset < input->numBwt; offset += group_size) {
      g_data g=*input; // copy current status
      if(g.verbose>2) printf("Round %d, NumBwt offset "CUSTOM_FORMAT"\n",r,offset);
      g.bws = input->bws+offset;
      g.bwtLen = input->bwtLen+offset;
      if(input->smallAlpha) g.bwtOcc = input->bwtOcc+offset;
      g.numBwt = min(group_size, input->numBwt - offset);
      g.symb_offset = tot_symb;
      g.mergeLen=0;
      for(int i=0;i<g.numBwt;i++) g.mergeLen += g.bwtLen[i];
      if(input->lcpCompute) {
        assert(!input->bwtOnly && !input->lcpMerge);
        g.bwtOnly = true;      // since this is not the last round we can only
        g.lcpCompute = false;  // compute the BWT and ignore LCP  
      }
      // prepare access to lcp values
      if(g.lcpMerge) g.lcps = input->lcps+offset;
      // execute merge, possibly using threads 
      if(num_threads>0) {
        e = sem_wait(merge.free_slots); if(e) die("multiround producer wait");
        check_g_data(&g);
        buffer[merge.pindex++ % merge.buf_size]=g; // copy g_data to buffer
        e = sem_post(merge.ready);  if(e) die("multiround producer post");
      }
      else { // no helper threads
        if (hm) holtMcMillan(&g, false);
        else gap(&g, false);
        // update input via g
        for(int i=1;i<g.numBwt;i++) {
          g.bwtLen[0] += g.bwtLen[i];
          if(g.smallAlpha)
            for(int j=0;j<g.sizeOfAlpha;j++)
              g.bwtOcc[0][j] += g.bwtOcc[i][j];
        }
        assert(g.bwtLen[0]==g.mergeLen);
      }
      // update tot_symb seen so far
      tot_symb += g.mergeLen;
    } // end of inner parallelizable loop
    assert(input->mergeLen==tot_symb);
    // --- wait till all merges for this round have been completed 
    if(num_threads>0) {
      e = pthread_mutex_lock(&merge.remutex); if(e) die("remaining lock 2");
      while(merge.remaining>0) {
        e = pthread_cond_wait(&merge.recond,&merge.remutex);
        if(e) die("remaining wait");
      }
      e = pthread_mutex_unlock(&merge.remutex); if(e) die("remaining unlock 2");
      assert(merge.pindex==merge.cindex);
      if(input->verbose>1) puts("Threads syncronized, round completed");
    }
    // merged done for all segments. merged bwt/lcp are in g.bws[0][] and g.lcps[0] (if g.lcpMerge==true)

    // update input so that it consists of fewer, larger segments. move Len and Occ real entries to the left
    for(offset=0; offset < input->numBwt; offset += group_size) {
      input->bwtLen[offset/group_size] = input->bwtLen[offset];
      input->bws[offset/group_size] = input->bws[offset];
      if(input->lcpMerge) input->lcps[offset/group_size] = input->lcps[offset];;
      // replace with a swap ??? 
      if(input->smallAlpha)
        for(int j=0;j<input->sizeOfAlpha;j++)
          input->bwtOcc[offset/group_size][j] = input->bwtOcc[offset][j];
    }
    input->numBwt = (input->numBwt+group_size-1)/group_size;
    check_g_data(input);
    if(input->verbose>0)  printf("Round %d complete: %d merged bwt/lcp saved\n",r,input->numBwt);
  } // end of all rounds except last
  if(num_threads>0) {  // last round does not use threads, kill consumers
    g_data g; // dummy task
    g.numBwt = 0;
    g.verbose = input->verbose;
    for(int i=0;i<num_threads;i++) {
        e = sem_wait(merge.free_slots); if(e) die("cleaning producer wait");
        buffer[merge.pindex++ % merge.buf_size]=g; // copy g_data to buffer
        e = sem_post(merge.ready);  if(e) die("cleaning producer post");
    }
    for(int i=0;i<num_threads;i++) {
      e = pthread_join(t[i], NULL);
      if(e) die("multiround join");
    }
    pc_system_destroy(&merge);
    if(input->verbose>1) printf("%d merge threads destroyed\n",num_threads);
  }
  // here maybe there is an additional round to go from n<2g, to g;
  // there is some code duplication, but it is an important special case 
  if(input->numBwt > group_size) { 
    offset = group_size-1; // first BWT to consider 
    tot_symb = 0;          // total number of symbols to skip
    for(int i=0;i<offset;i++)
      tot_symb += input->bwtLen[i];
    // prepare merge task  
    g_data g=*input; // copy current status
    if(g.verbose>2) printf("Special round, NumBwt offset "CUSTOM_FORMAT"\n",offset);
    g.bws = input->bws+offset;
    g.bwtLen = input->bwtLen+offset;
    if(input->smallAlpha) g.bwtOcc = input->bwtOcc+offset;
    g.numBwt = input->numBwt - offset;
    assert(g.numBwt <= group_size);
    g.symb_offset = tot_symb;
    g.mergeLen=0;
    for(int i=0;i<g.numBwt;i++) g.mergeLen += g.bwtLen[i];
    if(input->lcpCompute) {
      assert(!input->bwtOnly && !input->lcpMerge);
      g.bwtOnly = true;      // since this is not the last round we can only
      g.lcpCompute = false;  // compute the BWT and ignore LCP  
    }
    if(g.lcpMerge) g.lcps = input->lcps+offset;
    // execute merge 
    if (hm) holtMcMillan(&g, false);
    else gap(&g, false);
    // update input via g
    for(int i=1;i<g.numBwt;i++) {
      g.bwtLen[0] += g.bwtLen[i];
      if(g.smallAlpha)
        for(int j=0;j<g.sizeOfAlpha;j++)
          g.bwtOcc[0][j] += g.bwtOcc[i][j];
    }
    assert(g.bwtLen[0]==g.mergeLen);
    // update input so that it consists of fewer, larger segments. move Len and Occ real entries to the left
    input->numBwt = (input->numBwt-g.numBwt+1);
    assert(input->numBwt==group_size);
    check_g_data(input);
  }
  assert(input->numBwt <= group_size);
  // execute last round (only point where gap/hm with lastRound==true is called) 
  if (hm) holtMcMillan(input, true);
  else gap(input, true);
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x6a898, %rsp          # imm = 0x6A898
movq %rcx, %rbx
movl %esi, %r12d
movl %edi, -0x30(%rbp)
movq %rsp, -0x68(%rbp)
movl %r8d, %eax
movq %rsp, %rcx
movq %rax, -0x60(%rbp)
leaq 0xf(,%rax,8), %rax
andq $-0x10, %rax
subq %rax, %rcx
movq %rcx, -0x58(%rbp)
movq %rcx, %rsp
testl %r8d, %r8d
movq %r12, -0x40(%rbp)
movl %r8d, -0x2c(%rbp)
jle 0xcec7
leaq -0x120(%rbp), %r14
movq %r14, %rdi
movl $0x14, %esi
callq 0xcaa8
leaq -0x6a8c0(%rbp), %rax
movq %rax, 0x40(%r14)
movl -0x30(%rbp), %eax
movb %al, 0xa8(%r14)
leaq -0x26a(%rip), %r15      # 0xcc04
movq -0x60(%rbp), %r12
movq -0x58(%rbp), %r13
movq %r13, %rdi
xorl %esi, %esi
movq %r15, %rdx
movq %r14, %rcx
callq 0x22d0
testl %eax, %eax
je 0xce96
leaq 0x3770(%rip), %rdi      # 0x10601
callq 0x39fc
addq $0x8, %r13
decq %r12
jne 0xce76
cmpl $0x0, 0x94(%rbx)
movq -0x40(%rbp), %r12
movl -0x2c(%rbp), %r8d
jle 0xcec7
leaq 0x375c(%rip), %rdi      # 0x10613
movl %r8d, %esi
xorl %eax, %eax
movl %r8d, %r14d
callq 0x2100
movl %r14d, %r8d
leal (%r12,%r12), %ecx
movl 0x20(%rbx), %eax
movl %ecx, -0x4c(%rbp)
cmpl %ecx, %eax
jl 0xd2af
leaq -0xd0(%rbp), %r14
leal -0x1(%r12), %eax
movl %eax, -0x34(%rbp)
movslq %r12d, %r15
leaq (,%r15,8), %rax
movq %rax, -0x70(%rbp)
xorl %r13d, %r13d
movq %r15, -0x48(%rbp)
movl %r13d, -0x38(%rbp)
testl %r8d, %r8d
jle 0xcf47
movq %r14, %rdi
callq 0x23f0
testl %eax, %eax
je 0xcf1f
leaq 0x3714(%rip), %rdi      # 0x1062e
callq 0x39fc
movl 0x20(%rbx), %eax
addl -0x34(%rbp), %eax
cltd
idivl %r12d
movl %eax, -0xd4(%rbp)
movq %r14, %rdi
callq 0x2240
testl %eax, %eax
je 0xcf47
leaq 0x36fd(%rip), %rdi      # 0x1063f
callq 0x39fc
cmpl $0x0, 0x20(%rbx)
je 0xd16f
xorl %r13d, %r13d
xorl %r15d, %r15d
xorl %r14d, %r14d
movl $0x5120, %edx           # imm = 0x5120
leaq -0x5240(%rbp), %rdi
movq %rbx, %rsi
callq 0x21f0
cmpl $0x3, -0x51ac(%rbp)
jl 0xcf8b
leaq 0x36d4(%rip), %rdi      # 0x10652
movl -0x38(%rbp), %esi
movq %r15, %rdx
xorl %eax, %eax
callq 0x2100
leaq (,%r15,8), %rax
movq (%rbx), %rcx
addq %rax, %rcx
movq %rcx, -0x5240(%rbp)
movq 0x8(%rbx), %rcx
leaq (%rcx,%r15,8), %rdx
movq %rdx, -0x5238(%rbp)
cmpb $0x1, 0x68(%rbx)
jne 0xcfc3
movq 0x18(%rbx), %rdx
addq %rax, %rdx
movq %rdx, -0x5228(%rbp)
movslq 0x20(%rbx), %rdx
subq %r15, %rdx
cmpq -0x48(%rbp), %rdx
cmoval %r12d, %edx
movl %edx, -0x5220(%rbp)
movq %r14, -0x5210(%rbp)
movq $0x0, -0x5218(%rbp)
testl %edx, %edx
jle 0xd00a
movl %edx, %edx
addq %r13, %rcx
xorl %esi, %esi
xorl %edi, %edi
addq (%rcx,%rsi,8), %rdi
movq %rdi, -0x5218(%rbp)
incq %rsi
cmpq %rsi, %rdx
jne 0xcff7
cmpb $0x0, 0x3e(%rbx)
je 0xd01e
movb $0x1, -0x5204(%rbp)
movb $0x0, -0x5202(%rbp)
cmpb $0x1, -0x5203(%rbp)
jne 0xd035
addq 0x88(%rbx), %rax
movq %rax, -0x51b8(%rbp)
cmpl $0x0, -0x2c(%rbp)
jle 0xd0c7
movq -0xf0(%rbp), %rdi
callq 0x2080
testl %eax, %eax
je 0xd05b
leaq 0x3619(%rip), %rdi      # 0x1066f
callq 0x39fc
leaq -0x5240(%rbp), %r12
movq %r12, %rdi
callq 0x3a58
movl -0x120(%rbp), %eax
leal 0x1(%rax), %ecx
movl %ecx, -0x120(%rbp)
cltd
idivl -0xd8(%rbp)
movslq %edx, %rax
imulq $0x5120, %rax, %rax     # imm = 0x5120
leaq (%rax,%rbp), %rdi
addq $-0x6a8c0, %rdi         # imm = 0xFFF95740
movl $0x5120, %edx           # imm = 0x5120
movq %r12, %rsi
callq 0x21f0
movq -0xe8(%rbp), %rdi
callq 0x2170
testl %eax, %eax
je 0xd14f
leaq 0x35cb(%rip), %rdi      # 0x10688
callq 0x39fc
jmp 0xd14f
cmpb $0x0, -0x30(%rbp)
je 0xd0dd
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0xbf20
jmp 0xd0eb
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0xa789
movslq -0x5220(%rbp), %rax
cmpq $0x2, %rax
jl 0xd14f
movq -0x5238(%rbp), %rcx
movq -0x5228(%rbp), %rdx
movl -0x5208(%rbp), %edi
testl %edi, %edi
setg %sil
andb -0x51d8(%rbp), %sil
movl %edi, %edi
movl $0x1, %r8d
movq (%rcx,%r8,8), %r9
addq %r9, (%rcx)
testb %sil, %sil
je 0xd147
movq (%rdx,%r8,8), %r9
movq (%rdx), %r10
xorl %r11d, %r11d
movq (%r9,%r11,8), %r12
addq %r12, (%r10,%r11,8)
incq %r11
cmpq %r11, %rdi
jne 0xd137
incq %r8
cmpq %rax, %r8
jne 0xd121
addq -0x5218(%rbp), %r14
addq -0x48(%rbp), %r15
movslq 0x20(%rbx), %rax
addq -0x70(%rbp), %r13
cmpq %rax, %r15
movq -0x40(%rbp), %r12
jb 0xcf5a
cmpl $0x0, -0x2c(%rbp)
leaq -0xd0(%rbp), %r14
movq -0x48(%rbp), %r15
jle 0xd1ec
movq %r14, %rdi
callq 0x23f0
testl %eax, %eax
je 0xd195
leaq 0x350e(%rip), %rdi      # 0x106a1
jmp 0xd1b8
cmpl $0x0, -0xd4(%rbp)
jle 0xd1bf
leaq -0xa8(%rbp), %rdi
movq %r14, %rsi
callq 0x20c0
testl %eax, %eax
je 0xd195
leaq 0x34fa(%rip), %rdi      # 0x106b2
callq 0x39fc
jmp 0xd195
movq %r14, %rdi
callq 0x2240
testl %eax, %eax
je 0xd1d7
leaq 0x34ef(%rip), %rdi      # 0x106c1
callq 0x39fc
cmpl $0x2, 0x94(%rbx)
jl 0xd1ec
leaq 0x34ed(%rip), %rdi      # 0x106d4
callq 0x2090
movslq 0x20(%rbx), %rcx
testq %rcx, %rcx
je 0xd267
movb 0x3d(%rbx), %dil
movb 0x68(%rbx), %r8b
xorl %esi, %esi
movq 0x8(%rbx), %r9
movq (%r9,%rsi,8), %r10
movq %rsi, %rax
xorl %edx, %edx
divq %r15
movq %r10, (%r9,%rax,8)
movq (%rbx), %rdx
movq (%rdx,%rsi,8), %r9
movq %r9, (%rdx,%rax,8)
testb %dil, %dil
je 0xd232
movq 0x88(%rbx), %rdx
movq (%rdx,%rsi,8), %r9
movq %r9, (%rdx,%rax,8)
testb %r8b, %r8b
je 0xd25f
movslq 0x38(%rbx), %rdx
testq %rdx, %rdx
jle 0xd25f
movq 0x18(%rbx), %r10
movq (%r10,%rsi,8), %r9
movq (%r10,%rax,8), %rax
xorl %r10d, %r10d
movq (%r9,%r10,8), %r11
movq %r11, (%rax,%r10,8)
incq %r10
cmpq %r10, %rdx
jne 0xd24f
addq %r15, %rsi
cmpq %rcx, %rsi
jb 0xd1ff
addl -0x34(%rbp), %ecx
movl %ecx, %eax
cltd
idivl %r12d
movl %eax, 0x20(%rbx)
movq %rbx, %rdi
callq 0x3a58
cmpl $0x0, 0x94(%rbx)
movl -0x38(%rbp), %r13d
jle 0xd29c
movl 0x20(%rbx), %edx
leaq 0x3467(%rip), %rdi      # 0x106f9
movl %r13d, %esi
xorl %eax, %eax
callq 0x2100
incl %r13d
movl 0x20(%rbx), %eax
cmpl -0x4c(%rbp), %eax
movl -0x2c(%rbp), %r8d
jge 0xcefe
testl %r8d, %r8d
jle 0xd397
movl 0x94(%rbx), %r12d
leaq 0x3476(%rip), %r14      # 0x1073c
leaq 0x3458(%rip), %r15      # 0x10725
movl %r8d, %r13d
movq -0xf0(%rbp), %rdi
callq 0x2080
testl %eax, %eax
je 0xd2e8
movq %r15, %rdi
callq 0x39fc
movl -0x120(%rbp), %eax
leal 0x1(%rax), %ecx
movl %ecx, -0x120(%rbp)
cltd
idivl -0xd8(%rbp)
movslq %edx, %rax
imulq $0x5120, %rax, %rax     # imm = 0x5120
movl $0x0, -0x6a8a0(%rbp,%rax)
movl %r12d, -0x6a82c(%rbp,%rax)
movq -0xe8(%rbp), %rdi
callq 0x2170
testl %eax, %eax
je 0xd333
movq %r14, %rdi
callq 0x39fc
decl %r13d
jne 0xd2d0
xorl %r15d, %r15d
leaq 0x3411(%rip), %r14      # 0x10753
movq -0x40(%rbp), %r12
movl -0x2c(%rbp), %r13d
movq -0x58(%rbp), %rax
movq (%rax,%r15,8), %rdi
xorl %esi, %esi
callq 0x23a0
testl %eax, %eax
je 0xd365
movq %r14, %rdi
callq 0x39fc
incq %r15
cmpq %r15, -0x60(%rbp)
jne 0xd34a
leaq -0x120(%rbp), %rdi
callq 0xcb94
cmpl $0x1, 0x94(%rbx)
jle 0xd394
leaq 0x33d9(%rip), %rdi      # 0x10763
movl %r13d, %esi
xorl %eax, %eax
callq 0x2100
movl 0x20(%rbx), %eax
cmpl %r12d, %eax
jle 0xd530
decl %r12d
je 0xd3c2
movq 0x8(%rbx), %rax
movq %r12, %r13
movl %r12d, %ecx
xorl %edx, %edx
xorl %r12d, %r12d
addq (%rax,%rdx,8), %r12
incq %rdx
cmpq %rdx, %rcx
jne 0xd3b4
jmp 0xd3c8
movq %r12, %r13
xorl %r12d, %r12d
movslq %r13d, %r14
leaq -0x5240(%rbp), %r15
movl $0x5120, %edx           # imm = 0x5120
movq %r15, %rdi
movq %rbx, %rsi
callq 0x21f0
cmpl $0x2, 0x94(%r15)
jle 0xd3fd
leaq 0x338c(%rip), %rdi      # 0x1077f
movq %r14, %rsi
xorl %eax, %eax
callq 0x2100
shlq $0x3, %r14
movq (%rbx), %rax
addq %r14, %rax
movq %rax, -0x5240(%rbp)
movq 0x8(%rbx), %rax
addq %r14, %rax
movq %rax, -0x5238(%rbp)
cmpb $0x1, 0x68(%rbx)
jne 0xd430
movq 0x18(%rbx), %rcx
addq %r14, %rcx
movq %rcx, -0x5228(%rbp)
movl 0x20(%rbx), %ecx
subl %r13d, %ecx
movl %ecx, -0x5220(%rbp)
movq %r12, -0x5210(%rbp)
movq $0x0, -0x5218(%rbp)
testl %ecx, %ecx
jle 0xd46b
movl %ecx, %ecx
xorl %edx, %edx
xorl %esi, %esi
addq (%rax,%rdx,8), %rsi
movq %rsi, -0x5218(%rbp)
incq %rdx
cmpq %rdx, %rcx
jne 0xd458
cmpb $0x0, 0x3e(%rbx)
je 0xd47f
movb $0x1, -0x5204(%rbp)
movb $0x0, -0x5202(%rbp)
cmpb $0x1, -0x5203(%rbp)
jne 0xd496
addq 0x88(%rbx), %r14
movq %r14, -0x51b8(%rbp)
cmpb $0x0, -0x30(%rbp)
je 0xd4ac
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0xbf20
jmp 0xd4ba
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0xa789
movslq -0x5220(%rbp), %rax
cmpq $0x2, %rax
jl 0xd51e
movq -0x5238(%rbp), %rcx
movq -0x5228(%rbp), %rdx
movl -0x5208(%rbp), %edi
testl %edi, %edi
setg %sil
andb -0x51d8(%rbp), %sil
movl %edi, %edi
movl $0x1, %r8d
movq (%rcx,%r8,8), %r9
addq %r9, (%rcx)
testb %sil, %sil
je 0xd516
movq (%rdx,%r8,8), %r9
movq (%rdx), %r10
xorl %r11d, %r11d
movq (%r9,%r11,8), %r14
addq %r14, (%r10,%r11,8)
incq %r11
cmpq %r11, %rdi
jne 0xd506
incq %r8
cmpq %rax, %r8
jne 0xd4f0
movl 0x20(%rbx), %ecx
subl %eax, %ecx
incl %ecx
movl %ecx, 0x20(%rbx)
movq %rbx, %rdi
callq 0x3a58
cmpb $0x0, -0x30(%rbp)
je 0xd545
movq %rbx, %rdi
movl $0x1, %esi
callq 0xbf20
jmp 0xd552
movq %rbx, %rdi
movl $0x1, %esi
callq 0xa789
movq -0x68(%rbp), %rsp
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq