scalaz.Diev.scala Maven / Gradle / Ivy
The newest version!
package scalaz
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
import syntax.`enum`._
/**
* Implementation of a Discrete Interval Encoding Tree [[https://web.engr.oregonstate.edu/~erwig/diet/]] that
* is actually implemented using a Vector and is balanced at all times as a result.
*/
sealed abstract class Diev[A] {
def +(interval: (A, A)): Diev[A]
def +(value: A): Diev[A]
def -(interval: (A, A)): Diev[A]
def -(value: A): Diev[A]
def ++(other: Diev[A]): Diev[A]
def --(other: Diev[A]): Diev[A]
def intervals: Vector[(A, A)]
def contains(value: A): Boolean
def contains(interval: (A, A)): Boolean
def map[B](f: A => B)(implicit EB: Enum[B]): Diev[B]
def flatMap[B](f: A => Diev[B])(implicit EB: Enum[B]): Diev[B]
def filter(f: A => Boolean): Diev[A]
def foreach(f: A => Unit): Unit
def foldLeft[B](z: B)(f: (B, A) => B): B
def toSet: Set[A]
def toList: List[A]
def toIList: IList[A]
}
object DievInterval {
def subtractInterval[A](minuend: (A, A), subtraend: (A, A))(implicit E: Enum[A]): Vector[(A, A)] = {
val startOverlap = if(subtraend._1 > minuend._1) Vector((minuend._1, subtraend._1.pred)) else Vector()
//println("startOverlap = " + startOverlap)
val endOverlap = if(subtraend._2 < minuend._2) Vector((subtraend._2.succ, minuend._2)) else Vector()
//println("endOverlap = " + endOverlap)
startOverlap ++ endOverlap
}
def fixIntervalOrder[A](interval: (A, A))(implicit E: Enum[A]): (A, A) = if (interval._2 < interval._1) interval.swap else interval
}
trait DievImplementation {
import syntax.std.option._
import std.anyVal._
import DievInterval._
protected[this] case class DieVector[A](intervals: Vector[(A, A)] = Vector())(implicit EA: Enum[A]) extends Diev[A] {
val liftedIntervals = intervals.lift
private[this] sealed abstract class SearchResult
private[this] sealed case class Coincidence(position: Int) extends SearchResult
private[this] sealed case class Between(before: Option[Int], after: Option[Int]) extends SearchResult {
def adjacentBefore(interval: (A, A)): Option[Int] = before.filter{pos => intervals(pos)._2.succ === interval._1}
def adjacentAfter(interval: (A, A)): Option[Int] = after.filter{pos => intervals(pos)._1.pred === interval._2}
}
private def construct(prefixCount: Int, middle: Vector[(A, A)], suffixStart: Int): Diev[A] = {
DieVector(intervals.take(prefixCount) ++ middle ++ intervals.drop(suffixStart))
}
private[this] def binarySearch(value: A): SearchResult = {
@tailrec
def innerSearch(min: Int = 0, max: Int = intervals.size): SearchResult = {
if (max <= min) {
val adjustedPosition = 0.max(min.min(max).min(intervals.size - 1))
liftedIntervals(adjustedPosition) match {
case Some((start, end)) => {
if (start <= value && value <= end) Coincidence(adjustedPosition)
else {
if (value < start) Between(liftedIntervals(adjustedPosition - 1).map(_ => adjustedPosition - 1), adjustedPosition.some)
else Between(adjustedPosition.some, liftedIntervals(adjustedPosition + 1).map(_ => adjustedPosition + 1))
}
}
case _ => Between(None, None)
}
} else {
val mid = min + ((max - min) / 2)
intervals(mid) match {
case (start, end) => {
if (start <= value && value <= end) Coincidence(mid)
else {
if (value < start) innerSearch(min, mid - 1)
else innerSearch(mid + 1, max)
}
}
}
}
}
val resultOfSearch = innerSearch()
//println("resultOfSearch = " + resultOfSearch)
resultOfSearch
}
def +(interval: (A, A)): Diev[A] = {
val correctedInterval = fixIntervalOrder(interval)
(binarySearch(correctedInterval._1), binarySearch(correctedInterval._2)) match {
case (Coincidence(startPosition), Coincidence(endPosition)) => {
construct(startPosition, Vector((intervals(startPosition)._1.min(correctedInterval._1), intervals(endPosition)._2.max(correctedInterval._2))), endPosition + 1)
}
case (Coincidence(startPosition), between@Between(_, after)) => {
val adjacentAfterResult = between.adjacentAfter(correctedInterval)
construct(
startPosition,
Vector((intervals(startPosition)._1.min(correctedInterval._1), adjacentAfterResult.map(intervals(_)._2).getOrElse(correctedInterval._2))),
adjacentAfterResult.map(_ + 1).orElse(after).getOrElse(intervals.size)
)
}
case (earlyBound@ Between(before, after), Coincidence(endPosition)) => {
val adjacentBeforeResult = earlyBound.adjacentBefore(correctedInterval)
construct(
adjacentBeforeResult.orElse(before.map(_ + 1)).getOrElse(0),
Vector((adjacentBeforeResult.map(intervals(_)._1).getOrElse(correctedInterval._1), intervals(endPosition)._2.max(correctedInterval._2))),
endPosition + 1
)
}
//(Between(None,Some(0)),Between(Some(0),Some(1)))
case (earlyBound@ Between(before, after), lateBound@Between(_, otherAfter)) => {
val adjacentBeforeResult = earlyBound.adjacentBefore(correctedInterval)
val adjacentAfterResult = lateBound.adjacentAfter(correctedInterval)
construct(
adjacentBeforeResult.orElse(before.map(_ + 1)).getOrElse(0),
Vector((adjacentBeforeResult.map(intervals(_)._1).getOrElse(correctedInterval._1), adjacentAfterResult.map(intervals(_)._2).getOrElse(correctedInterval._2))),
adjacentAfterResult.map(_ + 1).orElse(otherAfter).getOrElse(intervals.size)
)
}
}
}
def +(value: A): Diev[A] = this + ((value, value))
def -(interval: (A, A)): Diev[A] = {
val orderedInterval = fixIntervalOrder(interval)
(binarySearch(orderedInterval._1), binarySearch(orderedInterval._2)) match {
case (Coincidence(startPosition), Coincidence(endPosition)) => {
val middle = if (startPosition == endPosition) subtractInterval(intervals(startPosition), interval)
else subtractInterval(intervals(startPosition), interval) ++ subtractInterval(intervals(endPosition), interval)
construct(startPosition, middle, endPosition + 1)
}
case (Coincidence(startPosition), Between(_, endAfter)) => {
val middle = subtractInterval(intervals(startPosition), orderedInterval)
construct(startPosition, middle, endAfter.getOrElse(intervals.size))
}
case (Between(startBefore, _), Coincidence(endPosition)) => {
val middle = subtractInterval(intervals(endPosition), orderedInterval)
construct(startBefore.map(startBeforePos => startBeforePos + 1).orZero, middle, endPosition + 1)
}
case (Between(startBefore, _), Between(_, endAfter)) => {
construct(startBefore.map(startBeforePos => startBeforePos + 1).orZero, Vector.empty, endAfter.getOrElse(intervals.size))
}
}
}
def -(value: A): Diev[A] = this - ((value, value))
def ++(other: Diev[A]): Diev[A] = other.intervals.foldLeft(this: Diev[A])(_ + _)
def --(other: Diev[A]): Diev[A] = other.intervals.foldLeft(this: Diev[A])(_ - _)
def contains(value: A): Boolean = binarySearch(value) match {
case Coincidence(_) => true
case _ => false
}
def contains(interval: (A, A)): Boolean = binarySearch(interval._1) match {
case Coincidence(position) if (intervals(position)._2 >= interval._2) => true
case _ => false
}
def map[B](f: A => B)(implicit EB: Enum[B]): Diev[B] = foldLeft[Diev[B]](DieVector[B]())(_ + f(_))
def flatMap[B](f: A => Diev[B])(implicit EB: Enum[B]): Diev[B] = foldLeft[Diev[B]](DieVector[B]())(_ ++ f(_))
def filter(f: A => Boolean): Diev[A] = foldLeft[Diev[A]](DieVector[A]())((working, value) => if (f(value)) working + value else working)
def foreach(f: A => Unit): Unit = foldLeft[Unit](())((_, value) => f(value))
def foldLeft[B](z: B)(f: (B, A) => B): B = {
intervals.foldLeft(z){(z1, interval) =>
val range = interval._1 |-> interval._2
range.foldLeft(z1)(f)
}
}
def foldRight[B](z: B)(f: (A, B) => B): B = {
intervals.foldRight(z){(interval, z1) =>
val range = interval._1 |-> interval._2
range.foldRight(z1)(f)
}
}
def toSet: Set[A] = foldLeft[Set[A]](Set[A]())(_ + _)
def toList: List[A] = foldLeft[ListBuffer[A]](new ListBuffer())(_ += _).toList
def toIList: IList[A] = foldRight[IList[A]](INil())(_ :: _)
override def toString(): String = intervals.foldLeft(new StringBuilder().append("("))(_.append(_)).append(")").toString
}
}
object Diev extends DievInstances {
def empty[A](implicit E: Enum[A]): Diev[A] = DieVector()
def fromValuesSeq[A](values: Seq[A])(implicit E: Enum[A]): Diev[A] = values.foldLeft(empty[A])(_ + _)
def fromIntervalsSeq[A](intervals: Seq[(A, A)])(implicit E: Enum[A]): Diev[A] = intervals.foldLeft(empty[A])(_ + _)
}
sealed abstract class DievInstances extends DievImplementation {
import std.tuple._, std.vector._
implicit def dievEqual[A: Equal]: Equal[Diev[A]] =
Equal.equalBy[Diev[A], Vector[(A, A)]](_.intervals)(using std.vector.vectorEqual[(A, A)])
implicit def dievMonoid[A: Enum]: Monoid[Diev[A]] = new Monoid[Diev[A]] {
def append(f1: Diev[A], f2: => Diev[A]) = f1 ++ f2
def zero: Diev[A] = new DieVector[A]()
}
implicit def dievShow[A: Show]: Show[Diev[A]] =
(diev: Diev[A]) => Show[Vector[(A, A)]].show(diev.intervals)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy