// File: /workspace/github_cpp_makefile_star2_O2/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), %ecx
movl 0x110(%rdi), %r12d
movslq 0x160(%rdi), %rsi
cmpl %ecx, %esi
jge 0x2ea7
decl %esi
movl %esi, 0x110(%rbx)
orl $-0x1, 0x114(%rbx)
orl $-0x1, 0x150(%rbx)
orq $-0x1, 0x108(%rbx)
leaq 0x120(%rbx), %rdi
callq 0x7220
jmp 0x30da
movl 0x108(%rbx), %r13d
pushq $0x2
popq %rdi
movl %ecx, %eax
cltd
idivl %edi
andq $0x0, 0x8(%rsp)
leal (%rax,%r13), %edx
incl %edx
movl %edx, 0x4(%rsp)
cmpb $0x0, 0x118(%rbx)
jne 0x2fc6
movl %eax, 0x4(%rsp)
movq 0x50(%rbx), %rdx
xorl %r8d, %r8d
testl %ecx, %ecx
movl $0x0, %r9d
cmovgl %ecx, %r9d
shlq $0x6, %r9
xorl %edi, %edi
cmpq %r8, %r9
je 0x2eff
addl (%rdx,%r8), %edi
addq $0x40, %r8
jmp 0x2ef0
leal -0x1(%rcx), %ebp
movslq %eax, %r8
shlq $0x6, %r8
movl %edi, 0x30(%rdx,%r8)
movl 0x10c(%rbx), %r8d
movslq 0x4(%rsp), %r10
movq %r10, %r9
shlq $0x6, %r9
movl 0x30(%rdx,%r9), %r11d
cmpl $-0x1, %r8d
je 0x2f31
cmpl %r8d, %r11d
jle 0x2f45
movl %r10d, 0x108(%rbx)
movl %r11d, 0x10c(%rbx)
movl %r10d, %r13d
movl %r11d, %r8d
decq %rsi
movl %ebp, 0x1c(%rsp)
movslq %ebp, %r14
movq %r14, %r15
shlq $0x6, %r15
xorl %r11d, %r11d
movq %rdx, %rbp
leaq (%r14,%r11), %r9
cmpq %rsi, %r9
jge 0x2fb7
movslq %r10d, %r9
movq %r9, %r10
shlq $0x6, %r10
incl %r9d
movl %r9d, 0x4(%rsp)
subl (%rbp), %edi
addl 0x40(%rbp,%r15), %edi
movl %edi, 0x70(%r10,%rdx)
movslq 0x4(%rsp), %r10
movq %r10, %r9
shlq $0x6, %r9
movl 0x30(%rdx,%r9), %r9d
cmpl %r8d, %r9d
jle 0x2fae
movl %r10d, 0x108(%rbx)
movl %r9d, 0x10c(%rbx)
movl %r10d, %r13d
movl %r9d, %r8d
incq %r11
addq $0x40, %rbp
jmp 0x2f5c
leal 0x1(%r13), %edx
cmpl %r11d, %edx
jge 0x2fcb
addl %eax, %edx
movl %edx, 0x4(%rsp)
addl %ecx, %r13d
jmp 0x2fd3
movl 0x1c(%rsp), %r13d
addq %r11, %r13
leaq 0x120(%rbx), %r14
movq %r14, %rdi
callq 0x7220
movl 0x160(%rbx), %eax
decl %eax
cmpl %eax, %r13d
jne 0x2ff7
movl 0x150(%rbx), %eax
jmp 0x301c
leaq 0x4(%rsp), %r15
movq %r14, %rdi
movq %r15, %rsi
callq 0x724e
movslq (%r15), %rax
movq 0x50(%rbx), %rcx
shlq $0x6, %rax
movl 0x30(%rcx,%rax), %eax
movl %eax, 0x150(%rbx)
pushq $-0x1
popq %rbp
movl %eax, 0x18(%rsp)
fildl 0x18(%rsp)
leaq 0x8(%rsp), %r15
movl 0x160(%rbx), %eax
movq 0x50(%rbx), %rcx
fldt 0x40(%rbx)
fmul %st(1), %st
movl 0x150(%rbx), %edx
movl 0x4(%rsp), %esi
incl %esi
incl %r13d
cmpl %eax, %r13d
jge 0x30b8
movl %esi, 0x4(%rsp)
movl %esi, 0x8(%rsp)
movslq %esi, %rdi
shlq $0x6, %rdi
movl 0x30(%rcx,%rdi), %edi
movl %edi, 0xc(%rsp)
movl %edi, 0x14(%rsp)
fildl 0x14(%rsp)
fucompi %st(1), %st
ja 0x30e9
testl %edi, %edi
je 0x30fa
incl %esi
cmpl %edx, %edi
jg 0x3047
fstp %st(0)
jge 0x30a0
fstp %st(0)
movq %r14, %rdi
callq 0x7220
movl 0xc(%rsp), %eax
movl %eax, 0x150(%rbx)
movl %eax, 0x10(%rsp)
fildl 0x10(%rsp)
fstpt 0x1c(%rsp)
movq %r14, %rdi
movq %r15, %rsi
callq 0x724e
fldt 0x1c(%rsp)
jmp 0x302c
fstp %st(0)
fstp %st(0)
decl %eax
movl %eax, 0x110(%rbx)
orl $-0x1, 0x114(%rbx)
movq %r14, %rdi
callq 0x7220
orl $-0x1, 0x150(%rbx)
movb $0x0, 0x119(%rbx)
movl 0x110(%rbx), %ecx
jmp 0x3148
fstp %st(0)
fstp %st(0)
cmpl $0x0, 0x150(%rbx)
setg %r14b
jmp 0x311d
fstp %st(0)
fstp %st(0)
movq %r14, %rdi
callq 0x7220
leaq 0x8(%rsp), %rsi
movq %r14, %rdi
callq 0x724e
andl $0x0, 0x150(%rbx)
xorl %r14d, %r14d
leaq 0x128(%rbx), %rdi
callq 0x2110
movl 0x20(%rax), %ecx
movl %ecx, 0x110(%rbx)
testb %r14b, %r14b
je 0x313b
movl 0x8(%rsp), %ebp
movl %ebp, 0x114(%rbx)
movb $0x0, 0x119(%rbx)
cmpl %r12d, %ecx
jne 0x315c
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
testl %r12d, %r12d
jle 0x31b2
cmpl %r12d, %ecx
jle 0x3209
incq %r12
movl %ecx, %eax
movq 0x10(%rbx), %rcx
movq 0x18(%rbx), %rdx
movl 0x20(%rbx), %esi
movq %r12, %rdi
shlq $0x6, %rdi
addq 0x50(%rbx), %rdi
cmpq %rax, %r12
ja 0x314d
movslq (%rdi), %r8
addl %r8d, %esi
imulq %r12, %r8
addq %r8, %rcx
movq %rcx, 0x10(%rbx)
imulq %r12, %r8
addq %r8, %rdx
movq %rdx, 0x18(%rbx)
movl %esi, 0x20(%rbx)
incq %r12
addq $0x40, %rdi
jmp 0x3185
xorps %xmm0, %xmm0
andl $0x0, 0x20(%rbx)
movaps %xmm0, 0x10(%rbx)
movq 0x50(%rbx), %rax
testl %ecx, %ecx
pushq $-0x1
popq %rdx
cmovnsl %ecx, %edx
movslq %edx, %rcx
incq %rcx
xorl %edx, %edx
xorl %esi, %esi
xorl %edi, %edi
xorl %r8d, %r8d
cmpq %r8, %rcx
je 0x314d
movslq (%rax), %r9
addl %r9d, %edx
imulq %r8, %r9
addq %r9, %rdi
movq %rdi, 0x10(%rbx)
imulq %r8, %r9
addq %r9, %rsi
movq %rsi, 0x18(%rbx)
movl %edx, 0x20(%rbx)
incq %r8
addq $0x40, %rax
jmp 0x31d8
movslq %ecx, %rax
movq 0x10(%rbx), %rcx
movq 0x18(%rbx), %rdx
movl 0x20(%rbx), %esi
movq %r12, %rdi
shlq $0x6, %rdi
addq 0x50(%rbx), %rdi
cmpq %rax, %r12
jle 0x314d
movslq (%rdi), %r8
subl %r8d, %esi
imulq %r12, %r8
subq %r8, %rcx
movq %rcx, 0x10(%rbx)
imulq %r12, %r8
subq %r8, %rdx
movq %rdx, 0x18(%rbx)
movl %esi, 0x20(%rbx)
decq %r12
addq $-0x40, %rdi
jmp 0x3222
nop