All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.barrybecker4.math.cutpoints.AbstractCutPointFinder.scala Maven / Gradle / Ivy

The newest version!
/* Copyright by Barry G. Becker, 2000-2018. Licensed under MIT License: http://www.opensource.org/licenses/MIT */
package com.barrybecker4.math.cutpoints

import com.barrybecker4.math.Range

import scala.collection.mutable.ArrayBuffer


/**
  * Base class for cut point finders.
  * @author Barry Becker
  */
object AbstractCutPointFinder {
  /** The range should not get smaller than this. */
  private[cutpoints] val MIN_RANGE: Double = 1.0E-10
}

abstract class AbstractCutPointFinder {
  /**
    * Retrieve the cut point values.
    * If its a really small range include both min and max to avoid having just one label.
    * @param range range to be divided into intervals.
    * @param maxNumTicks upper limit on number of cut points to return.
    * @return the cut points
    */
  def getCutPoints(range: Range, maxNumTicks: Int): Array[Double] = {
    validateArguments(range)
    var finalRange: Range = new Range(range)
    if (range.getExtent <= AbstractCutPointFinder.MIN_RANGE)
      finalRange = finalRange.add(range.min + AbstractCutPointFinder.MIN_RANGE)
    var positions: ArrayBuffer[Double] = new ArrayBuffer[Double]()
    if (finalRange.getExtent < AbstractCutPointFinder.MIN_RANGE)
      positions :+= finalRange.min
    else determineCutPoints(maxNumTicks, finalRange, positions)
    //val result: Array[Double] = new Array[Double](positions.size)
    positions.toArray
  }

  private def determineCutPoints(maxTicks: Int, finalRange: Range, positions: ArrayBuffer[Double]): Unit = {
    val extent: Double = Rounder.roundUp(finalRange.getExtent)
    val d: Double = Rounder.roundDown(extent / (maxTicks - 1))
    val roundedRange: Range = Range(Math.floor(finalRange.min / d) * d, Math.ceil(finalRange.max / d) * d)
    addPoints(positions, roundedRange, finalRange, d)
  }

  protected def addPoints(positions: ArrayBuffer[Double], roundedRange: Range, finalRange: Range, d: Double): Unit

  /** Verify that the min and max are valid.
    * @param range range to check for NaN values.
    */
  private def validateArguments(range: Range): Unit = {
    if (range.getExtent.isNaN) throw new IllegalArgumentException("Min cannot be greater than max for " + range)
    if (range.min.isNaN || range.min.isInfinite || range.max.isNaN || range.max.isInfinite)
      throw new IllegalArgumentException("Min or max of the range [" + range + "] is not a number.")
  }

  /** If real small just assume it is zero.
    * @param value the value to check.
    * @return zero if value is below the smallest allowed range, else return value.
    */
  private[cutpoints] def checkSmallNumber(value: Double): Double =
    if (Math.abs(value) < AbstractCutPointFinder.MIN_RANGE) 0 else value
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy