newly written circulararray

This commit is contained in:
minjaesong
2019-07-14 03:55:27 +09:00
parent 81625a4382
commit 801e737683
2 changed files with 167 additions and 48 deletions

View File

@@ -8,38 +8,58 @@ import net.torvald.util.CircularArray
class CircularArrayTest {
operator fun invoke() {
val testSet = CircularArray<Int?>(5)
val testSet2 = CircularArray<Int?>(5)
val testSet = CircularArray<Int?>(5, true)
val testSet2 = CircularArray<Int?>(5, true)
testSet2.overwritingPolicy = { println("Overwritten: $it") }
val testSet3 = CircularArray<Int?>(5, true)
for (i in 1..5) {
testSet.add(i)
testSet.appendHead(i)
}
println("Metadata:")
println(testSet)
println("forEach():")
testSet.forEach { print("$it ") }
testSet.forEach { print("$it ") } // 1 2 3 4 5
println("\nfold(0, sum):")
println(testSet.fold(0) { acc, v -> acc + (v ?: 0) })
println(testSet.fold(0) { acc, v -> acc + (v ?: 0) }) // 15
println("Raw:")
testSet.buffer.forEach { print("$it ") }
testSet.buffer.forEach { print("$it ") } // 1 2 3 4 5
println()
println()
for (i in 1..6) {
testSet2.add(i)
testSet2.appendHead(i) // must print "Overwritten: 1"
}
println("Metadata:")
println(testSet2)
println("forEach():")
println("forEach():") // 2 3 4 5 6
testSet2.forEach { print("$it ") }
println("\nfold(0, sum):")
println(testSet2.fold(0) { acc, v -> acc + (v ?: 0) })
println(testSet2.fold(0) { acc, v -> acc + (v ?: 0) }) // 20
println("Raw:")
testSet2.buffer.forEach { print("$it ") }
testSet2.buffer.forEach { print("$it ") } // 6 2 3 4 5
println()
println()
for (i in 1..5) {
testSet3.appendHead(i)
}
val a1 = testSet3.removeTail()
val a2 = testSet3.removeTail()
println("Removed elems: $a1 $a2")
println("Metadata:")
println(testSet3)
println("forEach():") // 3 4 5
testSet3.forEach { print("$it ") }
println("\nfold(0, sum):")
println(testSet3.fold(0) { acc, v -> acc + (v ?: 0) }) // 12
println("Raw:")
testSet3.buffer.forEach { print("$it ") } // 1 2 3 4 5
println()
}
}

View File

@@ -1,5 +1,7 @@
package net.torvald.util
import java.util.*
/**
* buffer[head] contains the most recent item, whereas buffer[tail] contains the oldest one.
@@ -11,13 +13,25 @@ package net.torvald.util
*
* Created by minjaesong on 2017-01-22.
*/
class CircularArray<T>(val size: Int) {
class CircularArray<T>(val size: Int, val overwriteOnOverflow: Boolean): Iterable<T> {
/**
* What to do when old element is being overridden by the new element (only makes sense when ```overwriteOnOverflow = true```)
*
* This function will not be called when ```removeHead()``` or ```removeTail()``` is called.
*/
var overwritingPolicy: (T) -> Unit = {
// do nothing
}
val buffer: Array<T> = arrayOfNulls<Any>(size) as Array<T>
var tail: Int = 0; private set
var head: Int = -1; private set
private var unreliableAddCount = 0
/** Tail stands for the oldest element. The tail index points AT the tail element */
var tail: Int = 0; private set
/** Head stands for the youngest element. The head index points AFTER the head element */
var head: Int = 0; private set
private var overflow = false
val lastIndex = size - 1
@@ -25,58 +39,143 @@ class CircularArray<T>(val size: Int) {
* Number of elements that forEach() or fold() would iterate.
*/
val elemCount: Int
get() = minOf(unreliableAddCount, size)
get() = if (overflow) size else head - tail
val isEmpty: Boolean
get() = !overflow && head == tail
fun add(item: T) {
if (unreliableAddCount <= size) unreliableAddCount += 1
head = (head + 1) % size
if (unreliableAddCount > size) {
tail = (tail + 1) % size
}
buffer[head] = item // overwrites oldest item when eligible
//println("$this $unreliableAddCount")
}
fun getHeadElem(): T = buffer[head]
fun getTailElem(): T = buffer[tail]
private inline fun incHead() { head = (head + 1).wrap() }
private inline fun decHead() { head = (head - 1).wrap() }
private inline fun incTail() { tail = (tail + 1).wrap() }
private inline fun decTail() { tail = (tail - 1).wrap() }
/**
* Iterates the array with oldest element first.
* When the overflowing is enabled, tail element (ultimate element) will be changed into the penultimate element.
*/
fun forEach(action: (T) -> Unit) {
// has slightly better iteration performance than lambda
if (unreliableAddCount <= size) {
for (i in 0..head)
action(buffer[i])
fun appendHead(item: T) {
if (overflow && !overwriteOnOverflow) {
throw StackOverflowError()
}
else {
for (i in 0..size - 1)
action(buffer[(i + tail) % size])
if (overflow) {
overwritingPolicy.invoke(buffer[head])
}
buffer[head] = item
incHead()
}
if (overflow) {
incTail()
}
// must be checked AFTER the actual head increment; otherwise this condition doesn't make sense
if (tail == head) {
overflow = true
}
}
fun appendTail(item: T) {
// even if overflowing is enabled, appending at tail causes head element to be altered, therefore such action
// must be blocked by throwing overflow error
if (overflow) {
throw StackOverflowError()
}
else {
decTail()
buffer[tail] = item
}
// must be checked AFTER the actual head increment; otherwise this condition doesn't make sense
if (tail == head) {
overflow = true
}
}
fun removeHead(): T {
if (isEmpty) throw EmptyStackException()
decHead()
overflow = false
return buffer[head]
}
fun removeTail(): T {
if (isEmpty) throw EmptyStackException()
val ret = buffer[tail]
incTail()
overflow = false
return ret
}
/** Returns the youngest (last of the array) element */
fun getHeadElem(): T = if (isEmpty) throw EmptyStackException() else buffer[(head - 1).wrap()]
/** Returns the oldest (first of the array) element */
fun getTailElem(): T = buffer[tail]
private fun getAbsoluteRange() = 0 until when {
head == tail -> buffer.size
tail > head -> buffer.size - (((head - 1).wrap()) - tail)
else -> head - tail
}
override fun iterator(): Iterator<T> {
if (isEmpty) {
return object : Iterator<T> {
override fun next(): T = throw EmptyStackException()
override fun hasNext() = false
}
}
val rangeMax = getAbsoluteRange().last
var counter = 0
return object : Iterator<T> {
override fun next(): T {
val ret = buffer[(counter + tail).wrap()]
counter += 1
return ret
}
override fun hasNext() = (counter <= rangeMax)
}
}
/**
* Iterates the array with oldest element (tail) first.
*/
fun forEach(action: (T) -> Unit) {
// for (element in buffer) action(element)
// return nothing
iterator().forEach(action)
}
fun <R> fold(initial: R, operation: (R, T) -> R): R {
// accumulator = initial
// for (element in buffer) accumulator = operation(accumulator, element)
// return accumulator
var accumulator = initial
//for (element in buffer) accumulator = operation(accumulator, element)
if (unreliableAddCount <= size) {
for (i in 0..head)
accumulator = operation(accumulator, buffer[i])
}
if (isEmpty)
return initial
else {
for (i in 0..size - 1)
accumulator = operation(accumulator, buffer[(i + tail) % size])
iterator().forEach {
accumulator = operation(accumulator, it)
}
}
return accumulator
}
private fun Int.wrap() = this fmod size
override fun toString(): String {
return "CircularArray(size=" + buffer.size + ", head=" + head + ", tail=" + tail + ")"
return "CircularArray(size=${buffer.size}, head=$head, tail=$tail, overflow=$overflow)"
}
private infix fun Int.fmod(other: Int) = Math.floorMod(this, other)
}