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

xerial.core.collection.CyclicArray.scala Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/*
 * Copyright 2012 Taro L. Saito
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package xerial.core.collection

import collection.mutable.{IndexedSeqOptimized, ArrayOps, ArrayLike}
import xerial.core.log.Logger
import reflect.ClassTag


//--------------------------------------
//
// CyclicArray.scalaa
// Since: 2012/08/12 14:13
//
//--------------------------------------


object CyclicArray {

  def apply[A : ClassTag](elem:A *) : CyclicArray[A] = {
    val c = new CyclicArray[A](elem.length)
    elem foreach { c append _ }
    c
  }
}

/**
 * A mutable array that supports append and prepend operations in O(1) amortized complexity.
 * 
 * @author Taro L. Saito
 */
class CyclicArray[@specialized A](capacity:Int = 8)(implicit m:ClassTag[A]) extends IndexedSeq[A] with Logger {
  require((capacity & (capacity - 1)) == 0, "queue size must be 2^i but %s".format(capacity))
  type self = this.type
  private var queue:Array[A] = m.newArray(capacity)
  private var h:Int = 0
  private var t:Int = 0

  def clear { h = 0; t = 0 }

  def apply(i:Int) : A = queue(index(h + i))

  @inline private def index(i:Int) = i & (queue.length - 1) // equivalent to mod queue.length

  def peekFirst : A = queue(index(h))
  def peekLast : A = queue(index(t-1))
  def peekFirst(k:Int) : A = queue(index(h+k))

  def addFirst(e:A) = prepend(e)
  def addLast(e:A) = append(e)

  def prepend(e:A) : self = {
    h = index(h - 1)
    queue(h) = e
    if(h == t)
      doubleCapacity
    this
  }

  def append(e:A) : self = {
    queue(t) = e
    t = index(t + 1)
    if(h == t)
      doubleCapacity
    this
  }

  def pollFirst = removeFirst
  def pollLast = removeLast
  def removeFirst : A = {
    val e = peekFirst
    h = index(h + 1)
    e
  }
  def removeLast : A = {
    val e = peekLast
    t = index(t-1)
    e
  }

  override def size = index(t - h)
  override def isEmpty = h == t

  private def doubleCapacity {
    assert(h == t)
    val p = h
    val n = queue.length
    val r = n - p // the number of elements to the right of p
    val newCapacity = n << 1
    if(newCapacity < 0)
      sys.error("Too big queue size: %,d".format(newCapacity))
    val newQueue = m.newArray(newCapacity)
    Array.copy(queue, p, newQueue, 0, r)
    Array.copy(queue, 0, newQueue, r, p)
    queue = newQueue
    h = 0
    t = n
  }


  def length = size

  def update(idx: Int, elem: A) { queue(index(h + idx)) = elem }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy