// File: /workspace/github_c_makefile_star2_O2/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
movl %r8d, %r12d
movq %rcx, %rbx
movq %rsi, -0x40(%rbp)
movl %edi, -0x30(%rbp)
movq %rsp, -0x60(%rbp)
movl %r8d, %eax
movq %rsp, %rcx
movq %rax, -0x58(%rbp)
leaq 0xf(,%rax,8), %rax
andq $-0x10, %rax
subq %rax, %rcx
movq %rcx, -0x50(%rbp)
movq %rcx, %rsp
testl %r8d, %r8d
jle 0xc482
leaq -0x120(%rbp), %r14
pushq $0x14
popq %rsi
movq %r14, %rdi
callq 0xc040
leaq -0x6a8c0(%rbp), %rax
movq %rax, 0x40(%r14)
movl -0x30(%rbp), %eax
movb %al, 0xa8(%r14)
leaq -0x257(%rip), %r13      # 0xc1d9
movq -0x50(%rbp), %r14
movq -0x58(%rbp), %r15
subq $0x1, %r15
jb 0xc468
movq %r14, %rdi
xorl %esi, %esi
movq %r13, %rdx
leaq -0x120(%rbp), %rcx
callq 0x22d0
testl %eax, %eax
je 0xc462
leaq 0x315c(%rip), %rdi      # 0xf5b9
callq 0x35b7
addq $0x8, %r14
jmp 0xc438
cmpl $0x0, 0x94(%rbx)
jle 0xc482
leaq 0x3153(%rip), %rdi      # 0xf5cb
movl %r12d, %esi
xorl %eax, %eax
callq 0x2100
movq -0x40(%rbp), %rax
leal (%rax,%rax), %ecx
movl %ecx, -0x48(%rbp)
leaq -0xd0(%rbp), %r14
leal -0x1(%rax), %ecx
movl %ecx, -0x2c(%rbp)
movslq %eax, %r15
leaq (,%r15,8), %rax
movq %rax, -0x68(%rbp)
xorl %r13d, %r13d
movl %r12d, -0x34(%rbp)
movl 0x20(%rbx), %eax
cmpl -0x48(%rbp), %eax
jl 0xc85c
testl %r12d, %r12d
jle 0xc500
movq %r14, %rdi
callq 0x23f0
testl %eax, %eax
je 0xc4d8
leaq 0x3113(%rip), %rdi      # 0xf5e6
callq 0x35b7
movl 0x20(%rbx), %eax
addl -0x2c(%rbp), %eax
cltd
idivl -0x40(%rbp)
movl %eax, -0xd4(%rbp)
movq %r14, %rdi
callq 0x2240
testl %eax, %eax
je 0xc500
leaq 0x30fc(%rip), %rdi      # 0xf5f7
callq 0x35b7
movl %r13d, -0x44(%rbp)
xorl %r12d, %r12d
xorl %r14d, %r14d
xorl %r13d, %r13d
movslq 0x20(%rbx), %rax
cmpq %rax, %r13
jae 0xc72b
movl $0x5120, %edx           # imm = 0x5120
leaq -0x5240(%rbp), %rdi
movq %rbx, %rsi
callq 0x21f0
cmpl $0x3, -0x51ac(%rbp)
jl 0xc54b
leaq 0x30cc(%rip), %rdi      # 0xf60a
movl -0x44(%rbp), %esi
movq %r13, %rdx
xorl %eax, %eax
callq 0x2100
leaq (,%r13,8), %rax
movq (%rbx), %rcx
addq %rax, %rcx
movq %rcx, -0x5240(%rbp)
movq 0x8(%rbx), %rcx
leaq (%rcx,%r13,8), %rdx
movq %rdx, -0x5238(%rbp)
cmpb $0x1, 0x68(%rbx)
jne 0xc583
movq 0x18(%rbx), %rdx
addq %rax, %rdx
movq %rdx, -0x5228(%rbp)
movslq 0x20(%rbx), %rdx
subq %r13, %rdx
cmpq %r15, %rdx
cmoval -0x40(%rbp), %edx
movl %edx, -0x5220(%rbp)
movq %r14, -0x5210(%rbp)
andq $0x0, -0x5218(%rbp)
testl %edx, %edx
movl $0x0, %esi
cmovlel %esi, %edx
addq %r12, %rcx
xorl %esi, %esi
xorl %edi, %edi
cmpq %rsi, %rdx
je 0xc5cc
addq (%rcx,%rsi,8), %rdi
movq %rdi, -0x5218(%rbp)
incq %rsi
jmp 0xc5b7
cmpb $0x0, 0x3e(%rbx)
je 0xc5e0
movb $0x1, -0x5204(%rbp)
movb $0x0, -0x5202(%rbp)
cmpb $0x1, -0x5203(%rbp)
jne 0xc5f7
addq 0x88(%rbx), %rax
movq %rax, -0x51b8(%rbp)
cmpl $0x0, -0x34(%rbp)
jle 0xc6a7
movq %r14, -0x70(%rbp)
movq %r12, %r14
movq %r15, %r12
movq -0xf0(%rbp), %rdi
callq 0x2080
testl %eax, %eax
je 0xc627
leaq 0x3005(%rip), %rdi      # 0xf627
callq 0x35b7
leaq -0x5240(%rbp), %r15
movq %r15, %rdi
callq 0x3610
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 %r15, %rsi
callq 0x21f0
movq -0xe8(%rbp), %rdi
callq 0x2170
testl %eax, %eax
movq %r12, %r15
movq %r14, %r12
movq -0x70(%rbp), %r14
je 0xc694
leaq 0x2fb1(%rip), %rdi      # 0xf640
callq 0x35b7
addq -0x5218(%rbp), %r14
addq %r15, %r13
addq -0x68(%rbp), %r12
jmp 0xc50d
cmpb $0x0, -0x30(%rbp)
je 0xc6bd
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0xb558
jmp 0xc6cb
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0x9e9e
movslq -0x5220(%rbp), %rax
movq -0x5238(%rbp), %rcx
movb -0x51d8(%rbp), %dl
movl -0x5208(%rbp), %esi
testl %esi, %esi
movl $0x0, %edi
cmovlel %edi, %esi
pushq $0x1
popq %rdi
cmpq %rax, %rdi
jge 0xc694
movq (%rcx,%rdi,8), %r8
addq %r8, (%rcx)
testb $0x1, %dl
je 0xc726
movq -0x5228(%rbp), %r8
xorl %r9d, %r9d
cmpq %r9, %rsi
je 0xc726
movq (%r8,%rdi,8), %r10
movq (%r10,%r9,8), %r10
movq (%r8), %r11
addq %r10, (%r11,%r9,8)
incq %r9
jmp 0xc70d
incq %rdi
jmp 0xc6f2
movl -0x34(%rbp), %r12d
testl %r12d, %r12d
leaq -0xd0(%rbp), %r14
leaq -0xa8(%rbp), %r13
jle 0xc7a8
movq %r14, %rdi
callq 0x23f0
leaq 0x2f08(%rip), %rdi      # 0xf659
testl %eax, %eax
jne 0xc774
cmpl $0x0, -0xd4(%rbp)
jle 0xc77b
movq %r13, %rdi
movq %r14, %rsi
callq 0x20c0
testl %eax, %eax
je 0xc755
leaq 0x2ef6(%rip), %rdi      # 0xf66a
callq 0x35b7
jmp 0xc755
movq %r14, %rdi
callq 0x2240
testl %eax, %eax
je 0xc793
leaq 0x2eeb(%rip), %rdi      # 0xf679
callq 0x35b7
cmpl $0x2, 0x94(%rbx)
jl 0xc7a8
leaq 0x2ee9(%rip), %rdi      # 0xf68c
callq 0x2090
movslq 0x20(%rbx), %rcx
xorl %esi, %esi
movl -0x44(%rbp), %r13d
cmpq %rcx, %rsi
jae 0xc823
movq 0x8(%rbx), %rdi
movq (%rdi,%rsi,8), %r8
movq %rsi, %rax
xorl %edx, %edx
divq %r15
movq %r8, (%rdi,%rax,8)
movq (%rbx), %rdx
movq (%rdx,%rsi,8), %rdi
movq %rdi, (%rdx,%rax,8)
cmpb $0x1, 0x3d(%rbx)
jne 0xc7eb
movq 0x88(%rbx), %rdx
movq (%rdx,%rsi,8), %rdi
movq %rdi, (%rdx,%rax,8)
cmpb $0x1, 0x68(%rbx)
jne 0xc81e
movl 0x38(%rbx), %edx
testl %edx, %edx
movl $0x0, %edi
cmovlel %edi, %edx
xorl %edi, %edi
cmpq %rdi, %rdx
je 0xc81e
movq 0x18(%rbx), %r8
movq (%r8,%rsi,8), %r9
movq (%r9,%rdi,8), %r9
movq (%r8,%rax,8), %r8
movq %r9, (%r8,%rdi,8)
incq %rdi
jmp 0xc800
addq %r15, %rsi
jmp 0xc7b2
addl -0x2c(%rbp), %ecx
movl %ecx, %eax
cltd
idivl -0x40(%rbp)
movl %eax, 0x20(%rbx)
movq %rbx, %rdi
callq 0x3610
cmpl $0x0, 0x94(%rbx)
jle 0xc854
movl 0x20(%rbx), %edx
leaq 0x2e67(%rip), %rdi      # 0xf6b1
movl %r13d, %esi
xorl %eax, %eax
callq 0x2100
incl %r13d
jmp 0xc4af
testl %r12d, %r12d
jle 0xc93f
movl 0x94(%rbx), %r15d
leaq 0x2e81(%rip), %r14      # 0xf6f4
leaq 0x2e63(%rip), %r13      # 0xf6dd
subl $0x1, %r12d
jb 0xc8e2
movq -0xf0(%rbp), %rdi
callq 0x2080
testl %eax, %eax
je 0xc898
movq %r13, %rdi
callq 0x35b7
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
andl $0x0, -0x6a8a0(%rbp,%rax)
movl %r15d, -0x6a82c(%rbp,%rax)
movq -0xe8(%rbp), %rdi
callq 0x2170
testl %eax, %eax
je 0xc87a
movq %r14, %rdi
callq 0x35b7
jmp 0xc87a
xorl %r15d, %r15d
leaq 0x2e1f(%rip), %r14      # 0xf70b
movl -0x34(%rbp), %r12d
cmpq %r15, -0x58(%rbp)
je 0xc916
movq -0x50(%rbp), %rax
movq (%rax,%r15,8), %rdi
xorl %esi, %esi
callq 0x23a0
testl %eax, %eax
je 0xc911
movq %r14, %rdi
callq 0x35b7
incq %r15
jmp 0xc8f0
leaq -0x120(%rbp), %rdi
callq 0xc169
cmpl $0x1, 0x94(%rbx)
jle 0xc93c
leaq 0x2de9(%rip), %rdi      # 0xf71b
movl %r12d, %esi
xorl %eax, %eax
callq 0x2100
movl 0x20(%rbx), %eax
cmpl -0x40(%rbp), %eax
jle 0xcac2
movl -0x2c(%rbp), %eax
xorl %ecx, %ecx
xorl %r12d, %r12d
cmpq %rcx, %rax
je 0xc962
movq 0x8(%rbx), %rdx
addq (%rdx,%rcx,8), %r12
incq %rcx
jmp 0xc950
movslq -0x2c(%rbp), %r14
leaq -0x5240(%rbp), %r15
movl $0x5120, %edx           # imm = 0x5120
movq %r15, %rdi
movq %rbx, %rsi
callq 0x21f0
cmpl $0x2, 0x94(%r15)
jle 0xc998
leaq 0x2da9(%rip), %rdi      # 0xf737
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 0xc9cb
movq 0x18(%rbx), %rcx
addq %r14, %rcx
movq %rcx, -0x5228(%rbp)
movl 0x20(%rbx), %ecx
subl -0x2c(%rbp), %ecx
movl %ecx, -0x5220(%rbp)
movq %r12, -0x5210(%rbp)
andq $0x0, -0x5218(%rbp)
xorl %edx, %edx
testl %ecx, %ecx
cmovlel %edx, %ecx
xorl %esi, %esi
cmpq %rdx, %rcx
je 0xca04
addq (%rax,%rdx,8), %rsi
movq %rsi, -0x5218(%rbp)
incq %rdx
jmp 0xc9ef
cmpb $0x0, 0x3e(%rbx)
je 0xca18
movb $0x1, -0x5204(%rbp)
movb $0x0, -0x5202(%rbp)
cmpb $0x1, -0x5203(%rbp)
jne 0xca2f
addq 0x88(%rbx), %r14
movq %r14, -0x51b8(%rbp)
cmpb $0x0, -0x30(%rbp)
je 0xca45
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0xb558
jmp 0xca53
leaq -0x5240(%rbp), %rdi
xorl %esi, %esi
callq 0x9e9e
movslq -0x5220(%rbp), %rax
movq -0x5238(%rbp), %rcx
movb -0x51d8(%rbp), %dl
movl -0x5208(%rbp), %esi
xorl %edi, %edi
testl %esi, %esi
cmovlel %edi, %esi
pushq $0x1
popq %rdi
cmpq %rax, %rdi
jge 0xcab0
movq (%rcx,%rdi,8), %r8
addq %r8, (%rcx)
testb $0x1, %dl
je 0xcaab
movq -0x5228(%rbp), %r8
xorl %r9d, %r9d
cmpq %r9, %rsi
je 0xcaab
movq (%r8,%rdi,8), %r10
movq (%r10,%r9,8), %r10
movq (%r8), %r11
addq %r10, (%r11,%r9,8)
incq %r9
jmp 0xca92
incq %rdi
jmp 0xca77
movl 0x20(%rbx), %ecx
subl %eax, %ecx
incl %ecx
movl %ecx, 0x20(%rbx)
movq %rbx, %rdi
callq 0x3610
cmpb $0x0, -0x30(%rbp)
je 0xcad5
pushq $0x1
popq %rsi
movq %rbx, %rdi
callq 0xb558
jmp 0xcae0
pushq $0x1
popq %rsi
movq %rbx, %rdi
callq 0x9e9e
movq -0x60(%rbp), %rsp
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq