commonMain.org.antlr.v4.kotlinruntime.misc.IntervalSet.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of antlr-kotlin-runtime-jvm Show documentation
Show all versions of antlr-kotlin-runtime-jvm Show documentation
Kotlin multiplatform port of ANTLR
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.kotlinruntime.misc
import org.antlr.v4.kotlinruntime.Lexer
import org.antlr.v4.kotlinruntime.Token
import org.antlr.v4.kotlinruntime.Token.Companion.EOF
import org.antlr.v4.kotlinruntime.Vocabulary
import org.antlr.v4.kotlinruntime.VocabularyImpl
/**
* This class implements the [IntSet] backed by a sorted array of
* non-overlapping intervals. It is particularly efficient for representing
* large collections of numbers, where the majority of elements appear as part
* of a sequential range of numbers that are all part of the set. For example,
* the set { 1, 2, 3, 4, 7, 8 } may be represented as { [1, 4], [7, 8] }.
*
*
*
* This class is able to represent sets containing any combination of values in
* the range [Integer.MIN_VALUE] to [Integer.MAX_VALUE]
* (inclusive).
*/
class IntervalSet : IntSet {
/** The list of sorted, disjoint intervals. */
protected var intervals: MutableList? = null
//protected var readonly: Boolean = false
// fun setReadonly(value: Boolean) {
// this.readonly = value
// }
/** {@inheritDoc} */
override val isNil: Boolean
get() = intervals == null || intervals!!.isEmpty()
/**
* Returns the maximum value contained in the set if not isNil().
*
* @return the maximum value contained in the set.
* @throws RuntimeException if set is empty
*/
val maxElement: Int
get() {
if (isNil) {
throw RuntimeException("set is empty")
}
val last = intervals!![intervals!!.size - 1]
return last.b
}
/**
* Returns the minimum value contained in the set if not isNil().
*
* @return the minimum value contained in the set.
* @throws RuntimeException if set is empty
*/
val minElement: Int
get() {
if (isNil) {
throw RuntimeException("set is empty")
}
return intervals!![0].a
}
var isReadonly: Boolean = false
get
set(readonly) {
if (field && !readonly) throw IllegalStateException("can't alter readonly IntervalSet")
field = readonly
}
constructor(intervals: MutableList) {
this.intervals = intervals
}
constructor(set: IntervalSet) : this() {
addAll(set)
}
constructor(vararg els: Int) {
if (els == null) {
intervals = ArrayList(2) // most sets are 1 or 2 elements
} else {
intervals = ArrayList(els.size)
for (e in els) add(e)
}
}
fun clear() {
if (isReadonly) throw IllegalStateException("can't alter readonly IntervalSet")
intervals!!.clear()
}
/** Add a single element to the set. An isolated element is stored
* as a range el..el.
*/
override fun add(el: Int) {
if (isReadonly) throw IllegalStateException("can't alter readonly IntervalSet")
add(el, el)
}
/** Add interval; i.e., add all integers from a to b to set.
* If b<a, do nothing.
* Keep list in sorted order (by left range value).
* If overlap, combine ranges. For example,
* If this is {1..5, 10..20}, adding 6..7 yields
* {1..5, 6..7, 10..20}. Adding 4..8 yields {1..8, 10..20}.
*/
fun add(a: Int, b: Int) {
add(Interval.of(a, b))
}
// copy on write so we can cache a..a intervals and sets of that
protected fun add(addition: Interval) {
if (isReadonly) throw IllegalStateException("can't alter readonly IntervalSet")
//System.out.println("add "+addition+" to "+intervals.toString());
if (addition.b < addition.a) {
return
}
// find position in list
// Use iterators as we modify list in place
val iter = intervals!!.listIterator()
while (iter.hasNext()) {
val r = iter.next()
if (addition == r) {
return
}
if (addition.adjacent(r) || !addition.disjoint(r)) {
// next to each other, make a single larger interval
val bigger = addition.union(r)
iter.set(bigger)
// make sure we didn't just create an interval that
// should be merged with next interval in list
while (iter.hasNext()) {
val next = iter.next()
if (!bigger.adjacent(next) && bigger.disjoint(next)) {
break
}
// if we bump up against or overlap next, merge
iter.remove() // remove this one
iter.previous() // move backwards to what we just set
iter.set(bigger.union(next)) // set to 3 merged ones
iter.next() // first call to next after previous duplicates the result
}
return
}
if (addition.startsBeforeDisjoint(r)) {
// insert before r
iter.previous()
iter.add(addition)
return
}
// if disjoint and after r, a future iteration will handle it
}
// ok, must be after last interval (and disjoint from last interval)
// just add it
intervals!!.add(addition)
}
override fun addAll(set: IntSet?): IntervalSet {
if (set == null) {
return this
}
if (set is IntervalSet) {
val other = set as IntervalSet?
// walk set and add each interval
val n = other!!.intervals!!.size
for (i in 0 until n) {
val I = other.intervals!![i]
this.add(I.a, I.b)
}
} else {
for (value in set!!.toList()) {
add(value)
}
}
return this
}
fun complement(minElement: Int, maxElement: Int): IntervalSet? {
return this.complement(IntervalSet.of(minElement, maxElement))
}
/** {@inheritDoc} */
override fun complement(vocabulary: IntSet?): IntervalSet? {
if (vocabulary == null || vocabulary!!.isNil) {
return null // nothing in common with null set
}
val vocabularyIS: IntervalSet
if (vocabulary is IntervalSet) {
vocabularyIS = vocabulary
} else {
vocabularyIS = IntervalSet()
vocabularyIS.addAll(vocabulary)
}
return vocabularyIS.subtract(this)
}
override fun subtract(a: IntSet?): IntervalSet {
if (a == null || a!!.isNil) {
return IntervalSet(this)
}
if (a is IntervalSet) {
return subtract(this, a as IntervalSet?)
}
val other = IntervalSet()
other.addAll(a)
return subtract(this, other)
}
override fun or(a: IntSet?): IntervalSet {
val o = IntervalSet()
o.addAll(this)
o.addAll(a)
return o
}
/** {@inheritDoc} */
override fun and(other: IntSet?): IntervalSet? {
if (other == null) { //|| !(other instanceof IntervalSet) ) {
return null // nothing in common with null set
}
val myIntervals = this.intervals
val theirIntervals = (other as IntervalSet).intervals
var intersection: IntervalSet? = null
val mySize = myIntervals!!.size
val theirSize = theirIntervals!!.size
var i = 0
var j = 0
// iterate down both interval lists looking for nondisjoint intervals
while (i < mySize && j < theirSize) {
val mine = myIntervals[i]
val theirs = theirIntervals[j]
//System.out.println("mine="+mine+" and theirs="+theirs);
if (mine.startsBeforeDisjoint(theirs)) {
// move this iterator looking for interval that might overlap
i++
} else if (theirs.startsBeforeDisjoint(mine)) {
// move other iterator looking for interval that might overlap
j++
} else if (mine.properlyContains(theirs)) {
// overlap, add intersection, get next theirs
if (intersection == null) {
intersection = IntervalSet()
}
intersection.add(mine.intersection(theirs))
j++
} else if (theirs.properlyContains(mine)) {
// overlap, add intersection, get next mine
if (intersection == null) {
intersection = IntervalSet()
}
intersection.add(mine.intersection(theirs))
i++
} else if (!mine.disjoint(theirs)) {
// overlap, add intersection
if (intersection == null) {
intersection = IntervalSet()
}
intersection.add(mine.intersection(theirs))
// Move the iterator of lower range [a..b], but not
// the upper range as it may contain elements that will collide
// with the next iterator. So, if mine=[0..115] and
// theirs=[115..200], then intersection is 115 and move mine
// but not theirs as theirs may collide with the next range
// in thisIter.
// move both iterators to next ranges
if (mine.startsAfterNonDisjoint(theirs)) {
j++
} else if (theirs.startsAfterNonDisjoint(mine)) {
i++
}
}
}
return if (intersection == null) {
IntervalSet()
} else intersection
}
/** {@inheritDoc} */
override operator fun contains(el: Int): Boolean {
val n = intervals!!.size
var l = 0
var r = n - 1
// Binary search for the element in the (sorted,
// disjoint) array of intervals.
while (l <= r) {
val m = (l + r) / 2
val I = intervals!![m]
val a = I.a
val b = I.b
if (b < el) {
l = m + 1
} else if (a > el) {
r = m - 1
} else { // el >= a && el <= b
return true
}
}
return false
}
// /** Return a list of Interval objects. */
// fun getIntervals(): List? {
// return intervals
// }
override fun hashCode(): Int {
var hash = MurmurHash.initialize()
for (I in intervals!!) {
hash = MurmurHash.update(hash, I.a)
hash = MurmurHash.update(hash, I.b)
}
hash = MurmurHash.finish(hash, intervals!!.size * 2)
return hash
}
/** Are two IntervalSets equal? Because all intervals are sorted
* and disjoint, equals is a simple linear walk over both lists
* to make sure they are the same. Interval.equals() is used
* by the List.equals() method to check the ranges.
*/
override fun equals(obj: Any?): Boolean {
if (obj == null || obj !is IntervalSet) {
return false
}
val other = obj as IntervalSet?
return this.intervals == other!!.intervals
}
override fun toString(): String {
return toString(false)
}
fun toString(elemAreChar: Boolean): String {
val buf = StringBuilder()
if (this.intervals == null || this.intervals!!.isEmpty()) {
return "{}"
}
if (this.size() > 1) {
buf.append("{")
}
val iter = this.intervals!!.iterator()
while (iter.hasNext()) {
val I = iter.next()
val a = I.a
val b = I.b
if (a == b) {
if (a == EOF)
buf.append("")
else if (elemAreChar)
TODO()
//buf.append("'").appendCodePoint(a).append("'")
else
buf.append(a)
} else {
if (elemAreChar)
TODO()
//buf.append("'").appendCodePoint(a).append("'..'").appendCodePoint(b).append("'")
else
buf.append(a).append("..").append(b)
}
if (iter.hasNext()) {
buf.append(", ")
}
}
if (this.size() > 1) {
buf.append("}")
}
return buf.toString()
}
@Deprecated("Use {@link #toString(Vocabulary)} instead.")
fun toString(tokenNames: Array): String {
return toString(VocabularyImpl.fromTokenNames(tokenNames as Array))
}
fun toString(vocabulary: Vocabulary): String {
val buf = StringBuilder()
if (this.intervals == null || this.intervals!!.isEmpty()) {
return "{}"
}
if (this.size() > 1) {
buf.append("{")
}
val iter = this.intervals!!.iterator()
while (iter.hasNext()) {
val I = iter.next()
val a = I.a
val b = I.b
if (a == b) {
buf.append(elementName(vocabulary, a))
} else {
for (i in a..b) {
if (i > a) buf.append(", ")
buf.append(elementName(vocabulary, i))
}
}
if (iter.hasNext()) {
buf.append(", ")
}
}
if (this.size() > 1) {
buf.append("}")
}
return buf.toString()
}
@Deprecated("Use {@link #elementName(Vocabulary, int)} instead.")
protected fun elementName(tokenNames: Array, a: Int): String {
return elementName(VocabularyImpl.fromTokenNames(tokenNames as Array), a)
}
protected fun elementName(vocabulary: Vocabulary, a: Int): String {
return if (a == Token.EOF) {
""
} else if (a == Token.EPSILON) {
""
} else {
vocabulary.getDisplayName(a)
}
}
override fun size(): Int {
var n = 0
val numIntervals = intervals!!.size
if (numIntervals == 1) {
val firstInterval = this.intervals!![0]
return firstInterval.b - firstInterval.a + 1
}
for (i in 0 until numIntervals) {
val I = intervals!![i]
n += I.b - I.a + 1
}
return n
}
fun toIntegerList(): IntegerList {
val values = IntegerList(size())
val n = intervals!!.size
for (i in 0 until n) {
val I = intervals!![i]
val a = I.a
val b = I.b
for (v in a..b) {
values.add(v)
}
}
return values
}
override fun toList(): List {
val values = ArrayList()
val n = intervals!!.size
for (i in 0 until n) {
val I = intervals!![i]
val a = I.a
val b = I.b
for (v in a..b) {
values.add(v)
}
}
return values
}
fun toSet(): Set {
val s = HashSet()
for (I in intervals!!) {
val a = I.a
val b = I.b
for (v in a..b) {
s.add(v)
}
}
return s
}
/** Get the ith element of ordered set. Used only by RandomPhrase so
* don't bother to implement if you're not doing that for a new
* ANTLR code gen target.
*/
operator fun get(i: Int): Int {
val n = intervals!!.size
var index = 0
for (j in 0 until n) {
val I = intervals!![j]
val a = I.a
val b = I.b
for (v in a..b) {
if (index == i) {
return v
}
index++
}
}
return -1
}
fun toArray(): IntArray {
return toIntegerList().toArray()
}
override fun remove(el: Int) {
if (isReadonly) throw IllegalStateException("can't alter readonly IntervalSet")
val n = intervals!!.size
for (i in 0 until n) {
val I = intervals!![i]
val a = I.a
val b = I.b
if (el < a) {
break // list is sorted and el is before this interval; not here
}
// if whole interval x..x, rm
if (el == a && el == b) {
intervals!!.removeAt(i)
break
}
// if on left edge x..b, adjust left
if (el == a) {
I.a++
break
}
// if on right edge a..x, adjust right
if (el == b) {
I.b--
break
}
// if in middle a..x..b, split interval
if (el > a && el < b) { // found in this interval
val oldb = I.b
I.b = el - 1 // [a..x-1]
add(el + 1, oldb) // add [x+1..b]
}
}
}
companion object {
val COMPLETE_CHAR_SET = IntervalSet.of(Lexer.MIN_CHAR_VALUE, Lexer.MAX_CHAR_VALUE)
init {
COMPLETE_CHAR_SET.isReadonly = true
}
val EMPTY_SET = IntervalSet()
init {
EMPTY_SET.isReadonly = true
}
/** Create a set with a single element, el. */
fun of(a: Int): IntervalSet {
val s = IntervalSet()
s.add(a)
return s
}
/** Create a set with all ints within range a..b (inclusive) */
fun of(a: Int, b: Int): IntervalSet {
val s = IntervalSet()
s.add(a, b)
return s
}
/** combine all sets in the array returned the or'd value */
fun or(sets: Array): IntervalSet {
val r = IntervalSet()
for (s in sets) r.addAll(s)
return r
}
/**
* Compute the set difference between two interval sets. The specific
* operation is `left - right`. If either of the input sets is
* `null`, it is treated as though it was an empty set.
*/
fun subtract(left: IntervalSet?, right: IntervalSet?): IntervalSet {
if (left == null || left.isNil) {
return IntervalSet()
}
val result = IntervalSet(left)
if (right == null || right.isNil) {
// right set has no elements; just return the copy of the current set
return result
}
var resultI = 0
var rightI = 0
while (resultI < result.intervals!!.size && rightI < right.intervals!!.size) {
val resultInterval = result.intervals!![resultI]
val rightInterval = right.intervals!![rightI]
// operation: (resultInterval - rightInterval) and update indexes
if (rightInterval.b < resultInterval.a) {
rightI++
continue
}
if (rightInterval.a > resultInterval.b) {
resultI++
continue
}
var beforeCurrent: Interval? = null
var afterCurrent: Interval? = null
if (rightInterval.a > resultInterval.a) {
beforeCurrent = Interval(resultInterval.a, rightInterval.a - 1)
}
if (rightInterval.b < resultInterval.b) {
afterCurrent = Interval(rightInterval.b + 1, resultInterval.b)
}
if (beforeCurrent != null) {
if (afterCurrent != null) {
// split the current interval into two
result.intervals!![resultI] = beforeCurrent
result.intervals!!.add(resultI + 1, afterCurrent)
resultI++
rightI++
continue
} else {
// replace the current interval
result.intervals!![resultI] = beforeCurrent
resultI++
continue
}
} else {
if (afterCurrent != null) {
// replace the current interval
result.intervals!![resultI] = afterCurrent
rightI++
continue
} else {
// remove the current interval (thus no need to increment resultI)
result.intervals!!.removeAt(resultI)
continue
}
}
}
// If rightI reached right.intervals.size(), no more intervals to subtract from result.
// If resultI reached result.intervals.size(), we would be subtracting from an empty set.
// Either way, we are done.
return result
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy