src/Tools/jEdit/src/utils/LinearSet.scala
author immler@in.tum.de
Thu, 05 Mar 2009 10:53:47 +0100
changeset 34530 de629c23b389
parent 34529 f0e55d9ffe45
child 34531 db1c28e326fc
permissions -rw-r--r--
implemented delete_after

/*  Title:      LinearSet.scala
    Author:     Makarius

Sets with canonical linear order, or immutable linked-lists.
*/

object LinearSet
{
  def empty[A]: LinearSet[A] = new LinearSet[A]
  def apply[A](elems: A*): LinearSet[A] =
    (empty[A] /: elems) ((s, elem) => s + elem)

  class Duplicate(s: String) extends Exception(s)
  class Undefined(s: String) extends Exception(s)

  private def make[A](first: Option[A], last: Option[A], b: Map[A, A]): LinearSet[A] =
    new LinearSet[A] {
      override val first_elem = first
      override val last_elem = last
      override val body = b
    }
}

class LinearSet[A] extends scala.collection.immutable.Set[A]
{
  /* representation */

  val first_elem: Option[A] = None
  val last_elem: Option[A] = None
  val body: Map[A, A] = Map.empty


  /* basic methods */

  override def isEmpty: Boolean = !last_elem.isDefined
  def size: Int = if (isEmpty) 0 else body.size + 1

  def empty[B] = LinearSet.empty[B]

  def contains(elem: A): Boolean =
    !isEmpty && (last_elem.get == elem || body.isDefinedAt(elem))

  def insert_after(hook: Option[A], elem: A): LinearSet[A] =
    if (contains(elem)) throw new LinearSet.Duplicate(elem.toString)
    else hook match {
      case Some(hook) =>
        if (!contains(hook)) throw new LinearSet.Undefined(hook.toString)
        else if (body.isDefinedAt(hook))
          LinearSet.make(first_elem, last_elem, body - hook + (hook -> elem) + (elem -> body(hook)))
        else LinearSet.make(first_elem, Some(elem), body + (hook -> elem))
      case None =>
        if (isEmpty) LinearSet.make(Some(elem), Some(elem), Map.empty)
        else LinearSet.make(Some(elem), last_elem, body + (elem -> first_elem.get))
    }
    
  def +(elem: A): LinearSet[A] = insert_after(last_elem, elem)

  def delete_after(elem: Option[A]): LinearSet[A] =
    elem match {
      case Some(elem) =>
        if (!contains(elem)) throw new LinearSet.Undefined(elem.toString)
        else if (!body.isDefinedAt(elem)) throw new LinearSet.Undefined(null)
        else if (body(elem) == last_elem.get) LinearSet.make(first_elem, Some(elem), body - elem)
        else LinearSet.make(first_elem, last_elem, body - elem - body(elem) + (elem -> body(body(elem))))
      case None =>
        if (isEmpty) throw new LinearSet.Undefined(null)
        else if (size == 1) empty
        else LinearSet.make(Some(body(first_elem.get)), last_elem, body - first_elem.get)
    }
    
  def -(elem: A): LinearSet[A] =
    if (!contains(elem)) throw new LinearSet.Undefined(elem.toString)
    else delete_after(body find (p => p._2 == elem) map (p => p._1))

  def elements = new Iterator[A] {
    private var next_elem = first_elem
    def hasNext = next_elem.isDefined
    def next = {
      val elem = next_elem.get
      next_elem = if (body.isDefinedAt(elem)) Some(body(elem)) else None
      elem
    }
  }
}