docs: double getGrowScore(int poolSize, double forecast, double throughput, boolean cpuHigh, boolean lowTput) {
          double growScore = 0.0;
          boolean flippedCoin = false;
          int upwardCompareSpan = 0;
          // Don't grow beyond max or in lowTput situation
          if (poolSize + poolIncrement <= maxThreads && !lowTput) {
              // compareSpan is the poolSize range used for throughput comparison
              upwardCompareSpan = Math.min(compareRange * poolIncrement, maxThreads - poolSize);
              // average the probabilityGreaterThan results for all valid (not too old or out-of-range)
              // throughput data for compareRange larger poolSizes ... discard invalid data found enroute
              Integer growKey = threadStats.higherKey(poolSize);
              Integer priorKey = Integer.valueOf(poolSize);
              int largerPools = 0;
              while (true) {
                  int distance = 0;
                  // stop if we run out of data
                  if (growKey == null)
                      break;
                  priorKey = growKey;
                  growKey = threadStats.higherKey(growKey);
                  // discard invalid data (old or out-of-range)
                  ThroughputDistribution priorStats = threadStats.get(priorKey);
                  distance = priorKey - poolSize;
                  if (pruneData(priorStats, forecast)) {
                      threadStats.remove(priorKey);
                  } else {
                      // found valid data, add it to the score
                      largerPools++;
                      growScore += priorStats.getProbabilityGreaterThan(forecast);
                      // stop if reached/passed compareSpan
                      if (distance >= upwardCompareSpan)
                          break;
                  }
              }
  
              // if we didn't find compareRange datapoints, flip a coin
              if (largerPools < compareRange) {
                  growScore += (flipCoin()) ? 0.7 : 0.0;
                  largerPools++;
                  flippedCoin = true;
              }
              // average the results for the larger poolSize data found
              growScore /= largerPools;
  
              ThroughputDistribution currentStats = getThroughputDistribution(poolSize, false);
              ThroughputDistribution growStats = getThroughputDistribution(poolSize + poolIncrement, false);
              // 8/8/2012: Don't grow if there's a significant probability we'll shrink immediately
              if (currentStats != null && growStats != null) {
                  if (growScore < 0.5 && currentStats.getProbabilityGreaterThan(growStats.getMovingAverage()) >= 0.5) {
                      growScore = 0.0;
                  }
              }
  
              // grow less eagerly based on no data when cpuUtil is high
              if (cpuHigh && growScore > 0.0 && flippedCoin) {
                  if (poolSize > hangBufferPoolSize) {
                      growScore = (flipCoin()) ? growScore : 0.0;
                  }
              } else if (growScore == 0 && flippedCoin && (queueDepth > poolSize * 4)) {
                  // if we are not growing due to no larger pool data, and there is a lot of
                  // work waiting, lean weakly (0.5) toward growing ...
                  growScore = (flipCoin()) ? 0.5 : 0.0;
              } else
              // next are sanity checks to reduce likelihood of random growth, if a broader-scope
              // look at the available data suggests growth is not very likely to help
              if (growScore > 0.0) {
                  if (flippedCoin) {
                      // we decided to grow based on no larger poolSize data (flippedCoin), so let's
                      // grow less eagerly if much smaller poolSize data shows not much smaller tput
                      try {
                          // potentially different compare span when looking at smaller poolSizes
                          int downwardCompareSpan = Math.min(compareRange * poolDecrement, poolSize - coreThreads);
                          Integer smallestPoolSize = getSmallestValidPoolSize(poolSize, forecast);
                          // only make this check if data is available well beyond compareSpan
                          if ((poolSize - smallestPoolSize) > (downwardCompareSpan * compareSpanRatioMultiplier)) {
                              if (leanTowardShrinking(smallestPoolSize, poolSize, threadStats.get(smallestPoolSize).getMovingAverage(), forecast))
                                  growScore = 0.0;
                          }
                      } catch (Exception e) {
                          FFDCFilter.processException(e, getClass().getName(), "getGrowScore - smallestPoolSize", this);
                      }
                  } else {
                      if (growScore < 0.5) {
                          // We are weakly inclined to grow (< 0.5) based on compareRange data .. if we have
                          // data for much larger poolSizes showing tput is not much better, we can be even
                          // less eager to grow
                          try {
                              Integer largestPoolSize = getLargestValidPoolSize(poolSize, forecast);
                              // only make this check if data is available well beyond compareSpan
                              if ((largestPoolSize - poolSize) > (upwardCompareSpan * compareSpanRatioMultiplier)) {
                                  if (leanTowardShrinking(poolSize, largestPoolSize, forecast, threadStats.get(largestPoolSize).getMovingAverage()))
                                      growScore = 0.0;
                              }
                          } catch (Exception e) {
                              FFDCFilter.processException(e, getClass().getName(), "getGrowScore - largestPoolSize", this);
                          }
                      }
                  }
              }
              /**
               * Nov 2018 - moved this to the growScore/shrinkScore comparison in evaluateInterval
               * // Filter out small growScores
               * if (growScore < growScoreFilterLevel) {
               * if (tc.isEventEnabled()) {
               * Tr.event(tc, "growScore pruning", (" growScore " + growScore + " pruned"));
               * }
               * growScore = 0.0;
               * }
               */
          }
          return growScore;
      }