Skip to content

Commit

Permalink
Remove AbstractConstMultiSet (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
lbressler13 authored Apr 10, 2024
1 parent 575e095 commit acd582e
Show file tree
Hide file tree
Showing 14 changed files with 483 additions and 577 deletions.
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

0 comments on commit acd582e

Please sign in to comment.