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

axle.algebra.Plottable.scala Maven / Gradle / Ivy

The newest version!
package axle.algebra

import spire.math._
import spire.implicits._
import spire.algebra.Eq
import spire.algebra.Field
import spire.algebra.Order
import spire.algebra.MetricSpace
import spire.algebra.Monoid
import spire.algebra.AdditiveMonoid
import java.lang.Double.{ isInfinite, isNaN }
import math.{ pow, abs, log10, floor, ceil }

trait Plottable[T] extends Order[T] {

  def zero: T

  def portion(left: T, v: T, right: T): Double

  def tics(from: T, to: T): Seq[(T, String)]

  def isPlottable(t: T): Boolean
}

object Plottable {

  import org.joda.time.DateTime

  implicit object DoublePlottable extends spire.std.DoubleAlgebra with Plottable[Double] {

    def isPlottable(t: Double): Boolean = (!t.isInfinite) && (!t.isNaN)

    def portion(left: Double, v: Double, right: Double): Double = (v - left) / (right - left)

    def step(from: Double, to: Double): Double = pow(10, ceil(log10(abs(to - from))).toInt - 1)

    def tics(from: Double, to: Double): Seq[(Double, String)] = {
      if ((from === to) || from.isNaN || from.isInfinity || to.isNaN || to.isInfinity) {
        List((0d, "0.0"), (1d, "1.0"))
      } else {
        val s = step(from, to)
        val n = ceil((to - from) / s).toInt
        val start = BigDecimal.valueOf(s * floor(from / s))
        (0 to n).map(i => {
          val v = start + BigDecimal(s) * i
          (v.toDouble, v.toString)
        }).filter({ case (d, _) => (d >= from && d <= to) })
      }
    }
  }

  val asdf = implicitly[Order[Long]]

  implicit object LongPlottable extends spire.std.LongAlgebra with Plottable[Long] {

    def isPlottable(t: Long): Boolean = true

    def portion(left: Long, v: Long, right: Long): Double = (v - left).toDouble / (right - left)

    def step(from: Long, to: Long): Long = {
      val n = (scala.math.ceil(scala.math.log10(abs(to - from))) - 1).toInt
      max(1, pow(10, n).toLong)
    }

    def tics(from: Long, to: Long): Seq[(Long, String)] = {
      val s = step(from, to)
      val n = (to - from) / s
      val start = (s * (from / s))
      (0L to n).map(i => {
        val v = start + s * i
        (v, v.toString)
      }).filter(vs => (vs._1 >= from && vs._1 <= to))
    }

  }

  implicit object IntPlottable extends spire.std.IntAlgebra with Plottable[Int] {

    def isPlottable(t: Int): Boolean = true

    def portion(left: Int, v: Int, right: Int): Double = (v - left).toDouble / (right - left)

    def step(from: Int, to: Int): Int = {
      val n = (scala.math.ceil(scala.math.log10(abs(to - from))) - 1).toInt
      max(1, pow(10, n).toInt)
    }

    def tics(from: Int, to: Int): Seq[(Int, String)] = {
      val s = step(from, to)
      val n = (to - from) / s
      val start = (s * (from / s))
      (0 to n).map(i => {
        val v = start + s * i
        (v, v.toString)
      }).filter(vs => (vs._1 >= from && vs._1 <= to))
    }
  }

  implicit object DateTimePlottable extends axle.JodaDateTimeOrder with Plottable[DateTime] {

    import org.joda.time._

    def isPlottable(t: DateTime): Boolean = true

    lazy val now = new DateTime()

    def zero: DateTime = now

    def portion(left: DateTime, v: DateTime, right: DateTime): Double = (v.getMillis - left.getMillis).toDouble / (right.getMillis - left.getMillis)

    // TODO: bigger and smaller time-scales
    def step(duration: Duration): (DateTime => DateTime, String) =
      if (duration.isLongerThan(Weeks.ONE.multipliedBy(104).toStandardDuration)) {
        (_.plusMonths(6), "MM/dd YY")
      } else if (duration.isLongerThan(Weeks.ONE.multipliedBy(20).toStandardDuration)) {
        (_.plusMonths(1), "MM/dd YY")
      } else if (duration.isLongerThan(Weeks.THREE.toStandardDuration)) {
        (_.plusWeeks(1), "MM/dd")
      } else if (duration.isLongerThan(Days.ONE.toStandardDuration)) {
        (_.plusDays(1), "MM/dd hh")
      } else if (duration.isLongerThan(Hours.SEVEN.toStandardDuration)) {
        (_.plusHours(2), "dd hh:mm")
      } else if (duration.isLongerThan(Hours.ONE.toStandardDuration)) {
        (_.plusHours(1), "dd hh:mm")
      } else if (duration.isLongerThan(Minutes.TWO.toStandardDuration)) {
        (_.plusMinutes(1), "hh:mm")
      } else if (duration.isLongerThan(Seconds.ONE.multipliedBy(10).toStandardDuration())) {
        (_.plusSeconds(10), "mm:ss")
      } else {
        (_.plusSeconds(1), "mm:ss")
      }

    def ticStream(from: DateTime, to: DateTime, stepFn: DateTime => DateTime, fmt: String): Stream[(DateTime, String)] = {
      val nextTic = stepFn(from)
      if (nextTic.isAfter(to)) {
        Stream.empty
      } else {
        Stream.cons((nextTic, nextTic.toString(fmt)), ticStream(nextTic, to, stepFn, fmt))
      }
    }

    def tics(from: DateTime, to: DateTime): Seq[(DateTime, String)] = {
      val dur = new Interval(from, to).toDuration
      val (stepFn, fmt) = step(dur)
      ticStream(from, to, stepFn, fmt).toList
    }

  }

  implicit object RationalPlottable extends Plottable[Rational] {

    val ratOrd = implicitly[Order[Rational]]

    def zero: Rational = Rational(0)
    
    def compare(x: Rational, y: Rational): Int = ratOrd.compare(x, y) 
    
    def isPlottable(t: Rational): Boolean = {
      val d = t.toDouble
      !isInfinite(d) && !isNaN(d)
    }

    def portion(left: Rational, v: Rational, right: Rational): Double =
      ((v - left) / (right - left)).toDouble

    import spire.implicits._

    def step(from: Rational, to: Rational): Rational = {
      val power = (ceil(log10((to - from).abs.toDouble)) - 1).toInt
      if (power >= 0) {
        Rational(10 ** power, 1)
      } else {
        // spire doesn't like negative arguments to **
        Rational(1, 10 ** power.abs)
      }
    }

    def tics(from: Rational, to: Rational): Seq[(Rational, String)] = {
      val fromDouble = from.toDouble
      val toDouble = to.toDouble
      if (isNaN(fromDouble) || isInfinite(fromDouble) || isNaN(toDouble) || isInfinite(toDouble)) {
        List((Rational.zero, "0"), (Rational(1), "1"))
      } else {
        val s = step(from, to)
        val start = (from / s).floor * s
        val n = ((to - from) / s).ceil.toInt
        (0 to n).map(i => {
          val v = start + s * i
          (v, v.toString)
        }).filter({ case (d, _) => (d >= from && d <= to) })
      }
    }
  }

  implicit def abstractAlgebraPlottable[N: Field: Order](implicit space: MetricSpace[N, Double]) = new Plottable[N] {

    import spire.algebra._
    import spire.implicits._

    val ord = implicitly[Order[N]]
    val field = implicitly[Field[N]]

    def zero: N = field.zero
    
    def compare(x: N, y: N): Int = ord.compare(x, y)

    def isPlottable(t: N): Boolean = true

    def portion(left: N, v: N, right: N): Double =
      space.distance(v, right) / space.distance(right, left)

    // TODO
    def tics(from: N, to: N): Seq[(N, String)] =
      Vector((from, from.toString), (to, to.toString))

  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy