// File: /workspace/github_cpp_makefile_star2_O1/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), %eax
movl 0x110(%rdi), %r12d
movl 0x160(%rdi), %edx
cmpl %eax, %edx
jge 0x3296
decl %edx
movl %edx, 0x110(%rbx)
movl $0xffffffff, %eax       # imm = 0xFFFFFFFF
movl %eax, 0x114(%rbx)
movl %eax, 0x150(%rbx)
movq $-0x1, 0x108(%rbx)
leaq 0x120(%rbx), %rdi
callq 0x7ed6
movb $0x0, 0x119(%rbx)
cmpl %r12d, 0x110(%rbx)
jne 0x355b
jmp 0x3656
movl 0x108(%rbx), %r13d
movl %eax, %ecx
shrl $0x1f, %ecx
addl %eax, %ecx
sarl %ecx
leal (%rcx,%r13), %esi
incl %esi
movl %esi, 0x4(%rsp)
movq $0x0, 0x8(%rsp)
cmpb $0x0, 0x118(%rbx)
je 0x32ca
addl %eax, %r13d
jmp 0x33cf
movl %ecx, 0x4(%rsp)
testl %eax, %eax
jle 0x32f1
movq 0x50(%rbx), %rdi
movq %rax, %r8
shlq $0x6, %r8
xorl %r9d, %r9d
xorl %esi, %esi
addl (%rdi,%r9), %esi
addq $0x40, %r9
cmpq %r9, %r8
jne 0x32e2
jmp 0x32f3
xorl %esi, %esi
movslq %ecx, %r8
movq 0x50(%rbx), %rdi
shlq $0x6, %r8
movl %esi, 0x30(%rdi,%r8)
movl 0x10c(%rbx), %r8d
cmpl $-0x1, %r8d
je 0x3320
movslq 0x4(%rsp), %r9
shlq $0x6, %r9
cmpl %r8d, 0x30(%rdi,%r9)
jle 0x333c
movslq 0x4(%rsp), %r8
movl %r8d, 0x108(%rbx)
shlq $0x6, %r8
movl 0x30(%rdi,%r8), %r8d
movl %r8d, 0x10c(%rbx)
leal -0x1(%rax), %r13d
xorl %r8d, %r8d
cmpl %edx, %eax
jge 0x33b6
movl 0x10c(%rbx), %r10d
movslq %r13d, %r9
subl %eax, %edx
shlq $0x6, %r9
negq %rdx
xorl %r8d, %r8d
movq %rdi, %r11
movslq 0x4(%rsp), %r14
incq %r14
movl %r14d, 0x4(%rsp)
subl (%r11), %esi
addl 0x40(%r11,%r9), %esi
shlq $0x6, %r14
movl %esi, 0x30(%rdi,%r14)
movslq 0x4(%rsp), %r14
movq %r14, %r15
shlq $0x6, %r15
movl 0x30(%rdi,%r15), %ebp
cmpl %r10d, %ebp
jle 0x33a4
movl %r14d, 0x108(%rbx)
movl %ebp, 0x10c(%rbx)
movl %ebp, %r10d
decq %r8
addq $0x40, %r11
cmpq %r8, %rdx
jne 0x3360
subl %r8d, %r13d
negl %r8d
movl 0x108(%rbx), %edx
leal 0x1(%rdx), %esi
cmpl %esi, %r8d
jle 0x33cf
addl %ecx, %esi
movl %esi, 0x4(%rsp)
addl %eax, %edx
movl %edx, %r13d
leaq 0x120(%rbx), %r14
movq %r14, %rdi
callq 0x7ed6
movl 0x160(%rbx), %eax
decl %eax
cmpl %eax, %r13d
je 0x3410
leaq 0x4(%rsp), %r15
movq %r14, %rdi
movq %r15, %rsi
callq 0x7f04
movslq (%r15), %rax
movq 0x50(%rbx), %rcx
shlq $0x6, %rax
movl 0x30(%rcx,%rax), %eax
movl %eax, 0x150(%rbx)
incl %r13d
cmpl 0x160(%rbx), %r13d
fldz
jge 0x34c1
fstp %st(0)
movl 0x150(%rbx), %eax
movl %eax, 0x18(%rsp)
fildl 0x18(%rsp)
leaq 0x8(%rsp), %r15
movslq 0x4(%rsp), %rax
incq %rax
movl %eax, 0x4(%rsp)
movl %eax, 0x8(%rsp)
movq 0x50(%rbx), %rcx
shlq $0x6, %rax
movl 0x30(%rcx,%rax), %eax
movl %eax, 0xc(%rsp)
movl %eax, 0x14(%rsp)
fildl 0x14(%rsp)
fldt 0x40(%rbx)
fmul %st(2), %st
fxch %st(1)
fucompi %st(1), %st
fstp %st(0)
ja 0x351c
testl %eax, %eax
je 0x34f9
cmpl 0x150(%rbx), %eax
jg 0x34b1
jge 0x349e
fstp %st(0)
movq %r14, %rdi
callq 0x7ed6
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 0x7f04
fldt 0x1c(%rsp)
incl %r13d
cmpl 0x160(%rbx), %r13d
jl 0x3437
fstp %st(0)
movl 0x160(%rbx), %eax
decl %eax
movl %eax, 0x110(%rbx)
movl $0xffffffff, %ebp       # imm = 0xFFFFFFFF
movl %ebp, 0x114(%rbx)
movq %r14, %rdi
callq 0x7ed6
movl %ebp, 0x150(%rbx)
movb $0x0, 0x119(%rbx)
movl 0x110(%rbx), %eax
jmp 0x3552
fstp %st(0)
movq %r14, %rdi
callq 0x7ed6
leaq 0x8(%rsp), %rsi
movq %r14, %rdi
callq 0x7f04
movl $0x0, 0x150(%rbx)
fldz
fstp %st(0)
leaq 0x128(%rbx), %rdi
callq 0x2110
movl 0x20(%rax), %eax
movl %eax, 0x110(%rbx)
cmpl $0x0, 0x150(%rbx)
movl $0xffffffff, %ecx       # imm = 0xFFFFFFFF
jle 0x3545
movl 0x8(%rsp), %ecx
movl %ecx, 0x114(%rbx)
movb $0x0, 0x119(%rbx)
cmpl %r12d, %eax
je 0x3656
testl %r12d, %r12d
jle 0x35b3
movslq 0x110(%rbx), %rax
cmpl %r12d, %eax
jle 0x3610
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
jl 0x3589
jmp 0x364b
xorps %xmm0, %xmm0
movaps %xmm0, 0x10(%rbx)
movl $0x0, 0x20(%rbx)
movslq 0x110(%rbx), %rax
testq %rax, %rax
js 0x3656
movl 0x20(%rbx), %ecx
movq 0x10(%rbx), %rdx
movq 0x18(%rbx), %rsi
movq 0x50(%rbx), %rdi
incq %rax
xorl %r8d, %r8d
movslq (%rdi), %r9
addl %r9d, %ecx
imulq %r8, %r9
addq %r9, %rdx
imulq %r8, %r9
addq %r9, %rsi
incq %r8
addq $0x40, %rdi
cmpq %r8, %rax
jne 0x35e6
movq %rdx, 0x10(%rbx)
movq %rsi, 0x18(%rbx)
jmp 0x3653
cmpl %eax, %r12d
jle 0x3656
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
subl %r8d, %ecx
imulq %r12, %r8
subq %r8, %rsi
imulq %r12, %r8
subq %r8, %rdx
decq %r12
addq $-0x40, %rdi
cmpq %rax, %r12
jg 0x362b
movq %rsi, 0x10(%rbx)
movq %rdx, 0x18(%rbx)
movl %ecx, 0x20(%rbx)
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
nop