// File: /workspace/github_cpp_makefile_star2_O3/Altius[P]hotspot2/src/hotspot2_part1.cpp
// Function: BackgroundRegionManager::findCutoff()
void BackgroundRegionManager::findCutoff()
{
  // In all three noise models employed (negative binomial (NB), binomial, and Poisson,
  // though the NB is essentially always used because the data essentially always has variance > mean),
  // the distribution monotonically decreases beyond its mode.
  // Our idea is to identify a cutoff, when one exists,
  // which essentially marks the upper bound of observed monotonic decrease.
  // (Observations beyond the cutoff are then "predominantly signal," rather than noise.)
  // We do this by starting just beyond the mode of the
  // moving averages (MAs) of occurrence counts.
  // Whenever we encounter a global minimum (among values seen so far) in the MAs,
  // we measure whether a subsequent MA is substantially greater,
  // i.e. above some threshold (e.g., an increase of 33.3% or more).
  // If such an extreme increase is observed,
  // then we declare that "global minimum so far" to be the cutoff value.
  // If we exhaust the set of observations and no such increase is detected,
  // the highest observation is returned, i.e., no points are actually "cut off."
  // If a new "global minimum so far" is observed, below the previous "global minimum so far,"
  // the previous global minimum is ignored and the search process begins anew at the new local minimum.
  //
  // If no "trend reversal" is detected, but we detect a contiguous set of empty histogram bins,
  // we set the cutoff at the value immediately preceding those empty histogram bins.

  int kcutoff_uponEntry = m_kcutoff;

  if (m_sampledDataDistnSize < m_MAlength)
    {
      // Too few distinct count values were observed to compute a moving average of length MAlength;
      // set the "cutoff" to the largest observed count, i.e., use all data, don't "cut off" any points.
      m_kcutoff = m_sampledDataDistnSize - 1;
      m_minMAxN = m_kTrendReversal = -1;
      m_modeXval = m_modeYval = -1;
      m_kvalsWithMinMAxN.clear();
      m_needToUpdate_kcutoff = false;

      if (m_kcutoff != kcutoff_uponEntry)
        {
        UpdateNullRegionStatsAndExit:
          // The boundary of the null region has changed.
          // Recompute the running sums and the number of observations in the null region.
          if (kcutoff_uponEntry <= 0)
            {
              // Compute from scratch.
              m_runningSum_count = m_runningSum_countSquared = 0;
              m_numPtsInNullRegion = 0;
              for (long kk = 0; kk <= static_cast<long>(m_kcutoff); kk++)
                {
                  m_runningSum_count += static_cast<long>(m_distn[kk].numOccs) * kk;
                  m_runningSum_countSquared += static_cast<long>(m_distn[kk].numOccs) * kk * kk;
                  m_numPtsInNullRegion += m_distn[kk].numOccs;
                }
            }
          else
            {
              if (m_kcutoff > kcutoff_uponEntry)
                {
                  // The null region has expanded; increase the values accordingly.
                  for (long kk = static_cast<long>(kcutoff_uponEntry + 1);
		       kk <= static_cast<long>(m_kcutoff); kk++)
                    {
                      m_runningSum_count += static_cast<long>(m_distn[kk].numOccs) * kk;
                      m_runningSum_countSquared += static_cast<long>(m_distn[kk].numOccs) * kk * kk;
                      m_numPtsInNullRegion += m_distn[kk].numOccs;
                    }
                }
              else
                {
                  // The null region has contracted; decrease the values accordingly.
                  for (long kk = static_cast<long>(kcutoff_uponEntry); kk > static_cast<long>(m_kcutoff); kk--)
                    {
                      m_runningSum_count -= static_cast<long>(m_distn[kk].numOccs) * kk;
                      m_runningSum_countSquared -= static_cast<long>(m_distn[kk].numOccs) * kk * kk;
                      m_numPtsInNullRegion -= m_distn[kk].numOccs;
                    }
                }
            }
        }

      return;
    }

  int sum(0), idxL(m_modeXval + 1), idxR(m_modeXval + 1 + m_MAlength - 1); // these idxL and idxR values will be ignored/replaced if !m_sliding
  int idxC = m_modeXval + 1 + m_MAlength / 2; // integer division; see commendt directly above re: idxL and idxR
  pair<int, int> xyCurMAxN;
  bool useGlobMin(false);

  if (!m_sliding)
    {
      // Need to compute the moving averages (MAs).
      // Technically, these aren't moving averages, they're sums,
      // because we're not dividing sums by the number of terms.
      // But these "MA x N" values function as MAs.
      // Benefits:  No unnecessary division and casting from int to double,
      // and we gain the ability to perform exact tests for equality
      // (int == int instead of double == double).
      idxL = 0;
      idxC = idxL + m_MAlength / 2;
      idxR = idxL + m_MAlength - 1;
      // Compute the initial moving average (times the number of terms).
      for (int i = idxL; i <= idxR; i++)
        sum += m_distn[i].numOccs;
      m_distn[idxC].MAxN = sum;
      if (-1 == m_modeYval || m_distn[idxC].MAxN > m_modeYval)
        {
          m_modeXval = idxC;
          m_modeYval = m_distn[idxC].MAxN;
        }
      while (idxR < m_sampledDataDistnSize - 1)
        {
          idxC++;
          idxR++;
          sum -= m_distn[idxL].numOccs;
          sum += m_distn[idxR].numOccs;
          m_distn[idxC].MAxN = sum;
          if (m_distn[idxC].MAxN > m_modeYval)
            {
              m_modeXval = idxC;
              m_modeYval = m_distn[idxC].MAxN;
            }
          idxL++;
        }
      if (idxL > m_modeXval + 1)
        {
          idxL = m_modeXval + 1;
          idxC = idxL + m_MAlength / 2;
          idxR = idxL + m_MAlength - 1;
        }
      // else the "while" loop below won't get executed
    }
  m_kvalsWithMinMAxN.clear();
  if (idxR != m_sampledDataDistnSize - 1)
    {
      m_kvalsWithMinMAxN.insert(idxC); // idxC == m_modeXval+1 + m_MAlength/2 here, whether !m_sliding or m_sliding==true
      m_minMAxN = m_distn[idxC].MAxN;
    }
  long double minMAxN = static_cast<long double>(m_minMAxN);

  idxR++;
  while (idxR < m_sampledDataDistnSize)
    {
      idxC++;
      xyCurMAxN.first = idxC;
      xyCurMAxN.second = m_distn[idxC].MAxN;
      if (static_cast<long double>(xyCurMAxN.second) > m_thresholdRatio * minMAxN)
        {
          useGlobMin = true;
          break;
        }
      else
        {
          if (0 == xyCurMAxN.second)
            {
              // We've detected a contiguous stretch of at least m_MAlength empty histogram bins.
              // Set the global minimum here, and break out of the loop.
              // m_kcutoff will be set following the exit from the loop.
              m_kvalsWithMinMAxN.clear();
              m_kvalsWithMinMAxN.insert(xyCurMAxN.first); // k == idxC
              m_minMAxN = 0;
              useGlobMin = true;
              break;
            }
        }
      if (xyCurMAxN.second <= m_minMAxN)
        {
          if (xyCurMAxN.second < m_minMAxN)
            {
              // This is a unique, new minimum.
              m_kvalsWithMinMAxN.clear();
              m_minMAxN = xyCurMAxN.second;
              minMAxN = static_cast<long double>(m_minMAxN);
            } // else it's a duplicate occurrence of the existing minimum
          m_kvalsWithMinMAxN.insert(xyCurMAxN.first); // k == idxC
        }
      idxL++;
      idxR++;
    }

  // If useGlobMin is false, then we exhausted all observed values
  // without finding an "extreme" global minimum in the moving averages.
  // Return the maximum observed count as the "cutoff."
  if (!useGlobMin)
    {
      m_kcutoff = m_sampledDataDistnSize - 1;
      m_kTrendReversal = -1;
      m_kvalsWithMinMAxN.clear();
      m_minMAxN = -1;
      m_needToUpdate_kcutoff = false;
      if (m_kcutoff != kcutoff_uponEntry)
        goto UpdateNullRegionStatsAndExit;
      return;
    }
  // Otherwise, return the "global minimum so far" as the cutoff.
  // If there are ties (multiple k values with the same MAxN values), return the highest of these k values.
  set<int>::const_iterator it = m_kvalsWithMinMAxN.end();
  int k = *(--it);
  m_kcutoff = k;

  if (m_minMAxN > 0)
    m_kTrendReversal = xyCurMAxN.first; // where we determined the monotonically decreasing trend to have reversed
  else
    m_kTrendReversal = -1;

  m_needToUpdate_kcutoff = false;
  if (m_kcutoff != kcutoff_uponEntry)
    goto UpdateNullRegionStatsAndExit;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x28, %rsp
movq %rdi, %rbx
movl 0x3c(%rdi), %r14d
movl 0x110(%rdi), %r12d
movl 0x160(%rdi), %ecx
cmpl %r14d, %ecx
jge 0x3268
decl %ecx
movl %ecx, 0x110(%rbx)
movl $0xffffffff, %eax       # imm = 0xFFFFFFFF
movl %eax, 0x114(%rbx)
movl %eax, 0x150(%rbx)
movq $-0x1, 0x108(%rbx)
leaq 0x120(%rbx), %rdi
callq 0x821a
jmp 0x34aa
movl 0x108(%rbx), %edx
movl %r14d, %esi
shrl $0x1f, %esi
addl %r14d, %esi
sarl %esi
leal (%rsi,%rdx), %eax
incl %eax
movl %eax, (%rsp)
movq $0x0, 0x4(%rsp)
cmpb $0x0, 0x118(%rbx)
jne 0x3394
movl %esi, (%rsp)
movq 0x50(%rbx), %rdi
testl %r14d, %r14d
jle 0x32bf
movq %r14, %r9
shlq $0x6, %r9
xorl %r10d, %r10d
xorl %r8d, %r8d
addl (%rdi,%r10), %r8d
addq $0x40, %r10
cmpq %r10, %r9
jne 0x32b0
jmp 0x32c2
xorl %r8d, %r8d
movslq %esi, %rax
shlq $0x6, %rax
movl %r8d, 0x30(%rdi,%rax)
movl 0x10c(%rbx), %r11d
movslq (%rsp), %r10
movq %r10, %rax
shlq $0x6, %rax
movl 0x30(%rdi,%rax), %r9d
cmpl $-0x1, %r11d
je 0x32f0
cmpl %r11d, %r9d
jle 0x3304
movl %r10d, 0x108(%rbx)
movl %r9d, 0x10c(%rbx)
movl %r10d, %edx
movl %r9d, %r11d
leal -0x1(%r14), %ebp
cmpl %ecx, %r14d
jge 0x3386
movslq %ebp, %rax
movq %r14, 0x18(%rsp)
subl %r14d, %ecx
shlq $0x6, %rax
movq %rcx, %r15
negq %r15
xorl %r14d, %r14d
movq %rdi, %r13
movslq %r10d, %r9
movq %r9, %r10
shlq $0x6, %r10
incl %r9d
movl %r9d, (%rsp)
subl (%r13), %r8d
addl 0x40(%r13,%rax), %r8d
movl %r8d, 0x70(%r10,%rdi)
movslq (%rsp), %r10
movq %r10, %r9
shlq $0x6, %r9
movl 0x30(%rdi,%r9), %r9d
cmpl %r11d, %r9d
jle 0x3370
movl %r10d, 0x108(%rbx)
movl %r9d, 0x10c(%rbx)
movl %r10d, %edx
movl %r9d, %r11d
decq %r14
addq $0x40, %r13
cmpq %r14, %r15
jne 0x3328
subl %r14d, %ebp
movq 0x18(%rsp), %r14
jmp 0x3388
xorl %ecx, %ecx
leal 0x1(%rdx), %eax
cmpl %eax, %ecx
jle 0x3399
addl %esi, %eax
movl %eax, (%rsp)
addl %r14d, %edx
movl %edx, %ebp
leaq 0x120(%rbx), %r14
movq %r14, %rdi
callq 0x821a
movl 0x160(%rbx), %eax
leal -0x1(%rax), %ecx
cmpl %ecx, %ebp
je 0x33de
movq %rsp, %r15
movq %r14, %rdi
movq %r15, %rsi
callq 0x8248
movslq (%r15), %rax
movq 0x50(%rbx), %rcx
shlq $0x6, %rax
movl 0x30(%rcx,%rax), %eax
movl %eax, 0x150(%rbx)
movl 0x160(%rbx), %eax
incl %ebp
cmpl %eax, %ebp
jge 0x3485
movl 0x150(%rbx), %ecx
movl %ecx, 0x14(%rsp)
fildl 0x14(%rsp)
leaq 0x4(%rsp), %r15
movslq (%rsp), %rcx
incq %rcx
movl %ecx, (%rsp)
movl %ecx, 0x4(%rsp)
movq 0x50(%rbx), %rdx
shlq $0x6, %rcx
movl 0x30(%rdx,%rcx), %ecx
movl %ecx, 0x8(%rsp)
movl %ecx, 0x10(%rsp)
fildl 0x10(%rsp)
fldt 0x40(%rbx)
fmul %st(2), %st
fxch %st(1)
fucompi %st(1), %st
fstp %st(0)
ja 0x35cc
testl %ecx, %ecx
je 0x35f8
cmpl 0x150(%rbx), %ecx
jg 0x3479
jge 0x3460
fstp %st(0)
movq %r14, %rdi
callq 0x821a
movl 0x8(%rsp), %eax
movl %eax, 0x150(%rbx)
movl %eax, 0xc(%rsp)
fildl 0xc(%rsp)
fstpt 0x18(%rsp)
movq %r14, %rdi
movq %r15, %rsi
callq 0x8248
fldt 0x18(%rsp)
movl 0x160(%rbx), %eax
incl %ebp
cmpl %eax, %ebp
jl 0x33fb
jmp 0x3487
fldz
fstp %st(0)
decl %eax
movl %eax, 0x110(%rbx)
movl $0xffffffff, %ebp       # imm = 0xFFFFFFFF
movl %ebp, 0x114(%rbx)
movq %r14, %rdi
callq 0x821a
movl %ebp, 0x150(%rbx)
movb $0x0, 0x119(%rbx)
movl 0x110(%rbx), %eax
cmpl %r12d, %eax
je 0x35bd
testl %r12d, %r12d
jle 0x351e
cmpl %r12d, %eax
jle 0x3574
movl %eax, %eax
incq %r12
movq 0x10(%rbx), %rsi
movq 0x18(%rbx), %rdx
movl 0x20(%rbx), %ecx
movq %r12, %rdi
shlq $0x6, %rdi
addq 0x50(%rbx), %rdi
movslq (%rdi), %r8
addl %r8d, %ecx
imulq %r12, %r8
addq %r8, %rsi
imulq %r12, %r8
addq %r8, %rdx
leaq 0x1(%r12), %r8
addq $0x40, %rdi
cmpq %rax, %r12
movq %r8, %r12
jb 0x34e9
movq %rsi, 0x10(%rbx)
movq %rdx, 0x18(%rbx)
movl %ecx, 0x20(%rbx)
jmp 0x35bd
xorps %xmm0, %xmm0
movaps %xmm0, 0x10(%rbx)
movl $0x0, 0x20(%rbx)
testl %eax, %eax
js 0x35bd
movl %eax, %eax
movq 0x50(%rbx), %rcx
incq %rax
xorl %edx, %edx
xorl %esi, %esi
xorl %edi, %edi
xorl %r8d, %r8d
movslq (%rcx), %r9
addl %r9d, %r8d
imulq %rdx, %r9
addq %r9, %rsi
imulq %rdx, %r9
addq %r9, %rdi
incq %rdx
addq $0x40, %rcx
cmpq %rdx, %rax
jne 0x3546
movq %rsi, 0x10(%rbx)
movq %rdi, 0x18(%rbx)
movl %r8d, 0x20(%rbx)
jmp 0x35bd
cmpl %eax, %r12d
jle 0x35bd
movslq %eax, %rsi
movq 0x10(%rbx), %rdx
movq 0x18(%rbx), %rcx
movl 0x20(%rbx), %eax
movq %r12, %rdi
shlq $0x6, %rdi
addq 0x50(%rbx), %rdi
movslq (%rdi), %r8
subl %r8d, %eax
imulq %r12, %r8
subq %r8, %rdx
imulq %r12, %r8
subq %r8, %rcx
decq %r12
addq $-0x40, %rdi
cmpq %rsi, %r12
jg 0x3592
movq %rdx, 0x10(%rbx)
movq %rcx, 0x18(%rbx)
movl %eax, 0x20(%rbx)
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
fstp %st(0)
movl 0x150(%rbx), %ebp
leaq 0x128(%rbx), %rdi
callq 0x2110
movl 0x20(%rax), %eax
movl %eax, 0x110(%rbx)
movl $0xffffffff, %ecx       # imm = 0xFFFFFFFF
testl %ebp, %ebp
jle 0x3633
movl 0x4(%rsp), %ecx
jmp 0x3633
fstp %st(0)
movq %r14, %rdi
callq 0x821a
leaq 0x4(%rsp), %rsi
movq %r14, %rdi
callq 0x8248
movl $0x0, 0x150(%rbx)
leaq 0x128(%rbx), %rdi
callq 0x2110
movl 0x20(%rax), %eax
movl %eax, 0x110(%rbx)
movl $0xffffffff, %ecx       # imm = 0xFFFFFFFF
movl %ecx, 0x114(%rbx)
movb $0x0, 0x119(%rbx)
jmp 0x34b7
nop