Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove AbstractConstMultiSet #69

Merged
merged 11 commits into from
Apr 10, 2024
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package xyz.lbres.kotlinutils.set.multiset

import xyz.lbres.kotlinutils.set.multiset.utils.CountsMap
import xyz.lbres.kotlinutils.set.multiset.utils.performIntersect
import xyz.lbres.kotlinutils.set.multiset.utils.performMinus
import xyz.lbres.kotlinutils.set.multiset.utils.performPlus

/**
* Interface for set that allows multiple occurrences of a value.
* The interface supports only read access to the values.
Expand Down Expand Up @@ -55,7 +60,9 @@ interface MultiSet<E> : Collection<E> {
* @param multiSet2 [MultiSet]<E>: second MultiSet in addition
* @return [MultiSet]<E>: MultiSet containing all values from both sets
*/
fun <E> genericPlus(multiSet1: MultiSet<E>, multiSet2: MultiSet<E>): MultiSet<E> = genericMultiSetPlus(multiSet1, multiSet2)
fun <E> genericPlus(multiSet1: MultiSet<E>, multiSet2: MultiSet<E>): MultiSet<E> {
return performPlus(CountsMap.from(multiSet1), multiSet2)
}

/**
* [minus] implementation that can be used with any [MultiSet] implementations.
Expand All @@ -65,7 +72,9 @@ interface MultiSet<E> : Collection<E> {
* @param multiSet2 [MultiSet]<E>: second MultiSet in subtraction
* @return [MultiSet]<E>: MultiSet containing the items in the first MultiSet but not the second
*/
fun <E> genericMinus(multiSet1: MultiSet<E>, multiSet2: MultiSet<E>): MultiSet<E> = genericMultiSetMinus(multiSet1, multiSet2)
fun <E> genericMinus(multiSet1: MultiSet<E>, multiSet2: MultiSet<E>): MultiSet<E> {
return performMinus(CountsMap.from(multiSet1), multiSet2)
}

/**
* [minus] implementation that can be used with any [MultiSet] implementations.
Expand All @@ -75,6 +84,8 @@ interface MultiSet<E> : Collection<E> {
* @param multiSet2 [MultiSet]<E>: second MultiSet in intersect
* @return [MultiSet]<E>: MultiSet containing only values that are in both sets
*/
fun <E> genericIntersect(multiSet1: MultiSet<E>, multiSet2: MultiSet<E>): MultiSet<E> = genericMultiSetIntersect(multiSet1, multiSet2)
fun <E> genericIntersect(multiSet1: MultiSet<E>, multiSet2: MultiSet<E>): MultiSet<E> {
return performIntersect(CountsMap.from(multiSet1), multiSet2)
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
package xyz.lbres.kotlinutils.set.multiset.const

import xyz.lbres.kotlinutils.general.tryOrDefault
import xyz.lbres.kotlinutils.internal.constants.Suppressions
import xyz.lbres.kotlinutils.set.multiset.MultiSet
import xyz.lbres.kotlinutils.set.multiset.utils.CountsMap
import xyz.lbres.kotlinutils.set.multiset.utils.performIntersect
import xyz.lbres.kotlinutils.set.multiset.utils.performMinus
import xyz.lbres.kotlinutils.set.multiset.utils.performPlus

/**
* [MultiSet] implementation where values of elements are assumed to be constant.
* Behavior is not defined if values of elements are changed (i.e. elements are added to a mutable list).
*/
sealed class ConstMultiSet<E> : MultiSet<E> {
// required to be CountsMap<E>
protected abstract val counts: Any
private val _counts: CountsMap<E>
@Suppress(Suppressions.UNCHECKED_CAST)
get() = counts as CountsMap<E>

override fun plus(other: MultiSet<E>): MultiSet<E> = performPlus(_counts, other)
override fun minus(other: MultiSet<E>): MultiSet<E> = performMinus(_counts, other)
override fun intersect(other: MultiSet<E>): MultiSet<E> = performIntersect(_counts, other)

@Suppress(Suppressions.FUNCTION_NAME)
infix fun `+c`(other: ConstMultiSet<E>): ConstMultiSet<E> = plusC(other)
Expand All @@ -20,21 +34,43 @@ sealed class ConstMultiSet<E> : MultiSet<E> {
* @param other [ConstMultiSet]<E>: values to add to this set
* @return [ConstMultiSet]<E>: ConstMultiSet containing all values from both sets
*/
abstract fun plusC(other: ConstMultiSet<E>): ConstMultiSet<E>
fun plusC(other: ConstMultiSet<E>): ConstMultiSet<E> = performPlus(_counts, other, true) as ConstMultiSet<E>

/**
* Alternate version of [minus], which returns a ConstMultiSet
*
* @param other [ConstMultiSet]<E>: values to subtract from this set
* @return [ConstMultiSet]<E>: ConstMultiSet containing the items in this set but not the other
*/
abstract fun minusC(other: ConstMultiSet<E>): ConstMultiSet<E>
fun minusC(other: ConstMultiSet<E>): ConstMultiSet<E> = performMinus(_counts, other, true) as ConstMultiSet<E>

/**
* Alternate version of [intersect], which returns a ConstMultiSet
*
* @param other [ConstMultiSet]<E>: ConstMultiSet to intersect with current
* @return [ConstMultiSet]<E>: ConstMultiSet containing only values that are in both sets
*/
abstract infix fun intersectC(other: ConstMultiSet<E>): ConstMultiSet<E>
infix fun intersectC(other: ConstMultiSet<E>): ConstMultiSet<E> = performIntersect(_counts, other, true) as ConstMultiSet<E>

override fun getCountOf(element: E): Int = _counts.getCountOf(element)
override fun contains(element: E): Boolean = _counts.contains(element)
override fun containsAll(elements: Collection<E>): Boolean = _counts.containsAll(elements)
override fun isEmpty(): Boolean = _counts.isEmpty()

override fun hashCode(): Int {
val hashCode = _counts.hashCode()
return 31 * hashCode + MultiSet::class.java.name.hashCode()
}

override fun equals(other: Any?): Boolean {
return if (other is ConstMultiSet<*>) {
_counts == other._counts
} else {
tryOrDefault(false, listOf(ClassCastException::class)) {
@Suppress(Suppressions.UNCHECKED_CAST)
other as MultiSet<E>
_counts == CountsMap.from(other)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package xyz.lbres.kotlinutils.set.multiset.const

import xyz.lbres.kotlinutils.generic.ext.ifNull
import xyz.lbres.kotlinutils.set.multiset.MultiSet
import xyz.lbres.kotlinutils.set.multiset.utils.CountsMap

// final implementation of ConstMultiSet
internal class ConstMultiSetImpl<E>(private val elements: Collection<E>, initialCounts: CountsMap<E>? = null) : ConstMultiSet<E>(), AbstractConstMultiSetImpl<E> {
internal class ConstMultiSetImpl<E>(private val elements: Collection<E>, initialCounts: CountsMap<E>? = null) : ConstMultiSet<E>() {
override val size: Int = elements.size
override val distinctValues: Set<E>
private val string: String
Expand All @@ -14,24 +13,10 @@ internal class ConstMultiSetImpl<E>(private val elements: Collection<E>, initial
init {
counts = initialCounts.ifNull { CountsMap.from(elements) }
distinctValues = counts.distinct
string = counts.toString()
string = "[${elements.joinToString()}]"
}

override fun getCountOf(element: E): Int = counts.getCountOf(element)
override fun contains(element: E): Boolean = counts.contains(element)
override fun containsAll(elements: Collection<E>): Boolean = counts.containsAll(elements)

override fun plus(other: MultiSet<E>): MultiSet<E> = super<AbstractConstMultiSetImpl>.plus(other)
override fun minus(other: MultiSet<E>): MultiSet<E> = super<AbstractConstMultiSetImpl>.minus(other)
override fun intersect(other: MultiSet<E>): MultiSet<E> = super<AbstractConstMultiSetImpl>.intersect(other)

override fun plusC(other: ConstMultiSet<E>): ConstMultiSet<E> = super.plusC(other)
override fun minusC(other: ConstMultiSet<E>): ConstMultiSet<E> = super.minusC(other)
override fun intersectC(other: ConstMultiSet<E>): ConstMultiSet<E> = super.intersectC(other)

override fun isEmpty(): Boolean = counts.isEmpty()
fun toCountsMap(): CountsMap<E> = counts
override fun iterator(): Iterator<E> = elements.iterator()
override fun hashCode(): Int = getHashCode()
override fun equals(other: Any?): Boolean = other is MultiSet<*> && equalsSet(other)
override fun toString(): String = string
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package xyz.lbres.kotlinutils.set.multiset.const

import xyz.lbres.kotlinutils.set.multiset.MultiSet
import xyz.lbres.kotlinutils.set.multiset.utils.CountsMap
import kotlin.math.min

// final implementation of ConstMutableMultiSet
internal class ConstMutableMultiSetImpl<E>(initialElements: Collection<E>) : ConstMutableMultiSet<E>(), AbstractConstMultiSetImpl<E> {
internal class ConstMutableMultiSetImpl<E>(initialElements: Collection<E>) : ConstMutableMultiSet<E>() {
/**
* If all properties are up-to-date with the most recent changes to the counts map
*/
Expand Down Expand Up @@ -97,21 +96,7 @@ internal class ConstMutableMultiSetImpl<E>(initialElements: Collection<E>) : Con
allPropertiesUpdated = false
}

override fun getCountOf(element: E): Int = counts.getCountOf(element)
override fun contains(element: E): Boolean = counts.contains(element)
override fun containsAll(elements: Collection<E>): Boolean = counts.containsAll(elements)

override fun plus(other: MultiSet<E>): MultiSet<E> = super<AbstractConstMultiSetImpl>.plus(other)
override fun minus(other: MultiSet<E>): MultiSet<E> = super<AbstractConstMultiSetImpl>.minus(other)
override fun intersect(other: MultiSet<E>): MultiSet<E> = super<AbstractConstMultiSetImpl>.intersect(other)

override fun plusC(other: ConstMultiSet<E>): ConstMultiSet<E> = super.plusC(other)
override fun minusC(other: ConstMultiSet<E>): ConstMultiSet<E> = super.minusC(other)
override fun intersectC(other: ConstMultiSet<E>): ConstMultiSet<E> = super.intersectC(other)

override fun isEmpty(): Boolean = counts.isEmpty()
override fun equals(other: Any?): Boolean = other is MultiSet<*> && equalsSet(other)
override fun hashCode(): Int = getHashCode()
fun toCountsMap(): CountsMap<E> = counts

override fun iterator(): MutableIterator<E> {
updateMutableValues()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import xyz.lbres.kotlinutils.general.tryOrDefault
import xyz.lbres.kotlinutils.internal.constants.Suppressions
import xyz.lbres.kotlinutils.iterable.ext.countElement
import xyz.lbres.kotlinutils.set.multiset.MultiSet
import xyz.lbres.kotlinutils.set.multiset.const.AbstractConstMultiSetImpl
import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSetImpl
import xyz.lbres.kotlinutils.set.multiset.const.ConstMutableMultiSetImpl
import xyz.lbres.kotlinutils.set.multiset.utils.CountsMap
import xyz.lbres.kotlinutils.set.multiset.utils.combineCounts
import kotlin.math.min
import xyz.lbres.kotlinutils.set.multiset.utils.performIntersect
import xyz.lbres.kotlinutils.set.multiset.utils.performMinus
import xyz.lbres.kotlinutils.set.multiset.utils.performPlus

/**
* Partial [MultiSet] implementation which supports modifications to values of elements (i.e. adding elements to a mutable list).
Expand Down Expand Up @@ -36,7 +38,8 @@ internal abstract class AbstractMultiSetImpl<E>(initialElements: Collection<E>)
return tryOrDefault(false, listOf(ClassCastException::class)) {
when (other) {
is AbstractMultiSetImpl<*> -> counts == other.counts
is AbstractConstMultiSetImpl<*> -> counts == other.counts
is ConstMultiSetImpl<*> -> counts == other.toCountsMap()
is ConstMutableMultiSetImpl<*> -> counts == other.toCountsMap()
is MultiSet<*> -> {
@Suppress(Suppressions.UNCHECKED_CAST)
other as MultiSet<E>
Expand All @@ -47,15 +50,9 @@ internal abstract class AbstractMultiSetImpl<E>(initialElements: Collection<E>)
}
}

override fun plus(other: MultiSet<E>): MultiSet<E> {
return combineCounts(counts, other, Int::plus, useAllValues = true)
}
override fun minus(other: MultiSet<E>): MultiSet<E> {
return combineCounts(counts, other, Int::minus, useAllValues = false)
}
override fun intersect(other: MultiSet<E>): MultiSet<E> {
return combineCounts(counts, other, ::min, useAllValues = false)
}
override fun plus(other: MultiSet<E>): MultiSet<E> = performPlus(counts, other)
override fun minus(other: MultiSet<E>): MultiSet<E> = performMinus(counts, other)
override fun intersect(other: MultiSet<E>): MultiSet<E> = performIntersect(counts, other)

override fun isEmpty(): Boolean = elements.isEmpty()
override fun iterator(): Iterator<E> = elements.toList().iterator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package xyz.lbres.kotlinutils.set.multiset.utils

import xyz.lbres.kotlinutils.general.simpleIf
import xyz.lbres.kotlinutils.internal.constants.Suppressions
import xyz.lbres.kotlinutils.set.multiset.const.AbstractConstMultiSetImpl
import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSetImpl
import xyz.lbres.kotlinutils.set.multiset.const.ConstMutableMultiSetImpl

/**
* Mapping of occurrences to the number of times that they occur
Expand Down Expand Up @@ -102,12 +103,13 @@ internal value class CountsMap<E>(private val counts: Map<E, Int>) {
* @return [CountsMap]<E>: map containing exactly the elements in the given collection
*/
fun <E> from(elements: Collection<E>): CountsMap<E> {
@Suppress(Suppressions.UNCHECKED_CAST)
try {
if (elements is AbstractConstMultiSetImpl<*>) {
@Suppress(Suppressions.UNCHECKED_CAST)
return elements.counts as CountsMap<E>
when (elements) {
is ConstMultiSetImpl<*> -> return elements.toCountsMap() as CountsMap<E>
is ConstMutableMultiSetImpl<*> -> return elements.toCountsMap() as CountsMap<E>
}
} catch (_: Exception) {}
} catch (_: ClassCastException) {}

val counts: MutableMap<E, Int> = mutableMapOf()
elements.forEach {
Expand Down
Loading