From e0bb89b08c951b417f1023cb9a2ebb4ade6e8b96 Mon Sep 17 00:00:00 2001 From: lbressler13 Date: Sun, 21 Apr 2024 12:12:27 -0400 Subject: [PATCH] v1.4.4 (#72) --- kotlin-utils/build.gradle.kts | 2 +- .../kotlinutils/set/multiset/MultiSet.kt | 17 +- .../set/multiset/MultiSetGenerics.kt | 61 ------ .../set/multiset/MultiSetInlineMethods.kt | 22 +- .../const/AbstractConstMultiSetImpl.kt | 44 ---- .../set/multiset/const/ConstMultiSet.kt | 69 +++++- .../set/multiset/const/ConstMultiSetImpl.kt | 17 +- .../const/ConstMultiSetInlineMethods.kt | 128 +++++++++++ .../const/ConstMutableMultiSetImpl.kt | 15 +- .../set/multiset/impl/AbstractMultiSetImpl.kt | 23 +- .../set/multiset/utils/CountsMap.kt | 11 + .../set/multiset/utils/operators.kt | 86 ++++++++ .../kotlinutils/set/multiset/utils/utils.kt | 43 ---- .../set/mutableset/ext/MutableSetExt.kt | 2 +- .../set/multiset/MultiSetCompanionTest.kt | 205 +++++++++++++++--- .../set/multiset/MultiSetGenericsTest.kt | 205 ------------------ .../set/multiset/MultiSetInlineMethodsTest.kt | 2 + .../const/ConstMultiSetInlineMethodsTest.kt | 61 ++++++ .../set/multiset/const/ConstMultiSetTest.kt | 10 +- .../const/ConstMutableMultiSetTest.kt | 10 +- .../set/multiset/impl/MultiSetImplTest.kt | 6 +- .../multiset/impl/MutableMultiSetImplTest.kt | 6 +- ...Tests.kt => minMaxCountConsistentTests.kt} | 47 ++++ .../testutils/commonIntersectTests.kt | 56 +++-- .../multiset/testutils/commonMinusTests.kt | 68 +++--- .../set/multiset/testutils/commonPlusTests.kt | 46 ++-- .../set/multiset/testutils/testutils.kt | 2 + .../set/multiset/utils/OperatorsTest.kt | 152 +++++++++++++ .../set/multiset/utils/UtilsTest.kt | 121 ----------- 29 files changed, 901 insertions(+), 636 deletions(-) delete mode 100644 kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenerics.kt delete mode 100644 kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/AbstractConstMultiSetImpl.kt create mode 100644 kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethods.kt create mode 100644 kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/operators.kt delete mode 100644 kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/utils.kt delete mode 100644 kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenericsTest.kt create mode 100644 kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethodsTest.kt rename kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/inline/{minMaxConsistentTests.kt => minMaxCountConsistentTests.kt} (73%) create mode 100644 kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/OperatorsTest.kt delete mode 100644 kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/UtilsTest.kt diff --git a/kotlin-utils/build.gradle.kts b/kotlin-utils/build.gradle.kts index d97930cd..d73d09cb 100644 --- a/kotlin-utils/build.gradle.kts +++ b/kotlin-utils/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "xyz.lbres" -version = "1.3.4" +version = "1.4.4" repositories { mavenCentral() diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSet.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSet.kt index 949141fc..0dd67343 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSet.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSet.kt @@ -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. @@ -55,7 +60,9 @@ interface MultiSet : Collection { * @param multiSet2 [MultiSet]: second MultiSet in addition * @return [MultiSet]: MultiSet containing all values from both sets */ - fun genericPlus(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet = genericMultiSetPlus(multiSet1, multiSet2) + fun genericPlus(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet { + return performPlus(CountsMap.from(multiSet1), multiSet2) + } /** * [minus] implementation that can be used with any [MultiSet] implementations. @@ -65,7 +72,9 @@ interface MultiSet : Collection { * @param multiSet2 [MultiSet]: second MultiSet in subtraction * @return [MultiSet]: MultiSet containing the items in the first MultiSet but not the second */ - fun genericMinus(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet = genericMultiSetMinus(multiSet1, multiSet2) + fun genericMinus(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet { + return performMinus(CountsMap.from(multiSet1), multiSet2) + } /** * [minus] implementation that can be used with any [MultiSet] implementations. @@ -75,6 +84,8 @@ interface MultiSet : Collection { * @param multiSet2 [MultiSet]: second MultiSet in intersect * @return [MultiSet]: MultiSet containing only values that are in both sets */ - fun genericIntersect(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet = genericMultiSetIntersect(multiSet1, multiSet2) + fun genericIntersect(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet { + return performIntersect(CountsMap.from(multiSet1), multiSet2) + } } } diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenerics.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenerics.kt deleted file mode 100644 index 8d2b7241..00000000 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenerics.kt +++ /dev/null @@ -1,61 +0,0 @@ -package xyz.lbres.kotlinutils.set.multiset - -import kotlin.math.max -import kotlin.math.min - -/** - * Generic [MultiSet.plus] implementation that can be applied to any two MultiSets. - * - * @param multiSet1 [MultiSet]: first MultiSet in addition - * @param multiSet2 [MultiSet]: second MultiSet in addition - * @return [MultiSet]: MultiSet containing all values from both MultiSets - */ -internal fun genericMultiSetPlus(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet { - val newSet = mutableMultiSetOf() - val distinctValues = multiSet1.distinctValues + multiSet2.distinctValues - - distinctValues.forEach { element -> - val totalCount = multiSet1.getCountOf(element) + multiSet2.getCountOf(element) - repeat(totalCount) { newSet.add(element) } - } - - return newSet -} - -/** - * Generic [MultiSet.minus] implementation that can be applied to any two MultiSets. - * - * @param multiSet1 [MultiSet]: first MultiSet in subtraction - * @param multiSet2 [MultiSet]: second MultiSet in subtraction - * @return [MultiSet]: MultiSet containing the items in the first MultiSet but not the second - */ -internal fun genericMultiSetMinus(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet { - val newSet = mutableMultiSetOf() - val distinctValues = multiSet1.distinctValues + multiSet2.distinctValues - - distinctValues.forEach { element -> - val totalCount = max(0, multiSet1.getCountOf(element) - multiSet2.getCountOf(element)) - repeat(totalCount) { newSet.add(element) } - } - - return newSet -} - -/** - * Generic [MultiSet.intersect] implementation that can be applied to any two MultiSets. - * - * @param multiSet1 [MultiSet]: first MultiSet in intersection - * @param multiSet2 [MultiSet]: second MultiSet in intersection - * @return [MultiSet]: MultiSet containing only values that are in both MultiSets - */ -internal fun genericMultiSetIntersect(multiSet1: MultiSet, multiSet2: MultiSet): MultiSet { - val newSet = mutableMultiSetOf() - val distinctValues = multiSet1.distinctValues intersect multiSet2.distinctValues - - distinctValues.forEach { element -> - val totalCount = min(multiSet1.getCountOf(element), multiSet2.getCountOf(element)) - repeat(totalCount) { newSet.add(element) } - } - - return newSet -} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethods.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethods.kt index 8396f38b..7a86237c 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethods.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethods.kt @@ -1,6 +1,6 @@ package xyz.lbres.kotlinutils.set.multiset -// TODO count consistent +import xyz.lbres.kotlinutils.general.simpleIf /** * Create a new MultiSet with the results of applying the transform function to each value in the current MultiSet. @@ -63,7 +63,7 @@ inline fun MultiSet.filterNotToSet(predicate: (E) -> Boolean): MultiSet T: transformation function, which returns the same value for every occurrence of an element * @return [List]: list with transformed values @@ -80,7 +80,7 @@ inline fun MultiSet.mapConsistent(transform: (E) -> T): List { /** * Create a list containing only elements that match the given predicate. * Requires a [predicate] function that returns the same value for every occurrence of an element. - * To use a function that does not return the same value for every occurrence, see [filter]. + * To use a function that does not return the same value for every occurrence, see [Collection.filter]. * * @param predicate (E) -> [Boolean]: predicate to use for filtering, which returns the same value for every occurrence of an element * @return [List]: list containing only values for which [predicate] returns `true` @@ -101,7 +101,7 @@ inline fun MultiSet.filterConsistent(predicate: (E) -> Boolean): List /** * Create a list containing only elements that do not match the given predicate. * Requires a [predicate] function that returns the same value for every occurrence of an element. - * To use a function that does not return the same value for every occurrence, see [filterNot]. + * To use a function that does not return the same value for every occurrence, see [Collection.filterNot]. * * @param predicate (E) -> [Boolean]: predicate to use for filtering, which returns the same value for every occurrence of an element * @return [List]: list containing only values for which [predicate] returns `false` @@ -255,3 +255,17 @@ inline fun > MultiSet.maxByOrNullConsistent(selector: (E return distinctValues.maxByOrNull(selector) } + +/** + * Count number of values matching predicate + * Requires a [predicate] function that returns the same value for every occurrence of an element. + * To use a function that does not return the same value for every occurrence, see [Collection.count]. + * + * @param predicate (E) -> [Boolean]: predicate to use for counting, which returns the same value for every occurrence of an element + * @return [Int]: number of values for which [predicate] return `true` + */ +inline fun MultiSet.countConsistent(predicate: (E) -> Boolean): Int { + return distinctValues.fold(0) { acc: Int, element: E -> + simpleIf(predicate(element), { acc + getCountOf(element) }, { acc }) + } +} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/AbstractConstMultiSetImpl.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/AbstractConstMultiSetImpl.kt deleted file mode 100644 index f36d8b23..00000000 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/AbstractConstMultiSetImpl.kt +++ /dev/null @@ -1,44 +0,0 @@ -package xyz.lbres.kotlinutils.set.multiset.const - -import xyz.lbres.kotlinutils.internal.constants.Suppressions -import xyz.lbres.kotlinutils.set.multiset.MultiSet -import xyz.lbres.kotlinutils.set.multiset.impl.AbstractMultiSetImpl -import xyz.lbres.kotlinutils.set.multiset.utils.CountsMap -import xyz.lbres.kotlinutils.set.multiset.utils.combineCounts -import kotlin.math.min - -/** - * Common functionality for ConstMultiSet implementations - */ -internal interface AbstractConstMultiSetImpl { - val counts: CountsMap - val size: Int - - fun plus(other: MultiSet): MultiSet { - return combineCounts(counts, other, Int::plus, true, const = false) - } - fun minus(other: MultiSet): MultiSet { - return combineCounts(counts, other, Int::minus, false, const = false) - } - fun intersect(other: MultiSet): MultiSet { - return combineCounts(counts, other, ::min, false, const = false) - } - - // check equality to another multiset - fun equalsSet(other: MultiSet<*>): Boolean { - return when (other) { - is AbstractMultiSetImpl<*> -> counts == CountsMap.from(other) - is AbstractConstMultiSetImpl<*> -> counts == other.counts - else -> { - @Suppress(Suppressions.UNCHECKED_CAST) - other as MultiSet - size == other.size && counts.distinct.all { counts.getCountOf(it) == other.getCountOf(it) } - } - } - } - - fun getHashCode(): Int { - val hashCode = counts.hashCode() - return 31 * hashCode + MultiSet::class.java.name.hashCode() - } -} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt index b0acb01d..c5014a79 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt @@ -1,9 +1,76 @@ 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 : MultiSet +sealed class ConstMultiSet : MultiSet { + // required because ConstMultiSet is a public class. Must be CountsMap + protected abstract val counts: Any + private val _counts: CountsMap + @Suppress(Suppressions.UNCHECKED_CAST) + get() = counts as CountsMap + + override fun plus(other: MultiSet): MultiSet = performPlus(_counts, other) + override fun minus(other: MultiSet): MultiSet = performMinus(_counts, other) + override fun intersect(other: MultiSet): MultiSet = performIntersect(_counts, other) + + @Suppress(Suppressions.FUNCTION_NAME) + infix fun `+c`(other: ConstMultiSet): ConstMultiSet = plusC(other) + @Suppress(Suppressions.FUNCTION_NAME) + infix fun `-c`(other: ConstMultiSet): ConstMultiSet = minusC(other) + + /** + * Alternate version of [plus], which returns a ConstMultiSet + * + * @param other [ConstMultiSet]: values to add to this MultiSet + * @return [ConstMultiSet]: ConstMultiSet containing all values from both MultiSets + */ + fun plusC(other: ConstMultiSet): ConstMultiSet = performPlus(_counts, other, true) as ConstMultiSet + + /** + * Alternate version of [minus], which returns a ConstMultiSet + * + * @param other [ConstMultiSet]: values to subtract from this MultiSet + * @return [ConstMultiSet]: ConstMultiSet containing the items in this MultiSet but not the other + */ + fun minusC(other: ConstMultiSet): ConstMultiSet = performMinus(_counts, other, true) as ConstMultiSet + + /** + * Alternate version of [intersect], which returns a ConstMultiSet + * + * @param other [ConstMultiSet]: values to intersect with this MultiSet + * @return [ConstMultiSet]: ConstMultiSet containing only values that are in both MultiSets + */ + infix fun intersectC(other: ConstMultiSet): ConstMultiSet = performIntersect(_counts, other, true) as ConstMultiSet + + override fun getCountOf(element: E): Int = _counts.getCountOf(element) + override fun contains(element: E): Boolean = _counts.contains(element) + override fun containsAll(elements: Collection): 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 + _counts == CountsMap.from(other) + } + } + } +} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt index 8bff7af4..4a13386a 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt @@ -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(private val elements: Collection, initialCounts: CountsMap? = null) : ConstMultiSet(), AbstractConstMultiSetImpl { +internal class ConstMultiSetImpl(private val elements: Collection, initialCounts: CountsMap? = null) : ConstMultiSet() { override val size: Int = elements.size override val distinctValues: Set private val string: String @@ -14,20 +13,10 @@ internal class ConstMultiSetImpl(private val elements: Collection, 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): Boolean = counts.containsAll(elements) - - override fun plus(other: MultiSet): MultiSet = super.plus(other) - override fun minus(other: MultiSet): MultiSet = super.minus(other) - override fun intersect(other: MultiSet): MultiSet = super.intersect(other) - - override fun isEmpty(): Boolean = counts.isEmpty() + fun toCountsMap(): CountsMap = counts override fun iterator(): Iterator = elements.iterator() - override fun hashCode(): Int = getHashCode() - override fun equals(other: Any?): Boolean = other is MultiSet<*> && equalsSet(other) override fun toString(): String = string } diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethods.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethods.kt new file mode 100644 index 00000000..d75c1452 --- /dev/null +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethods.kt @@ -0,0 +1,128 @@ +package xyz.lbres.kotlinutils.set.multiset.const + +/** + * Create a new ConstMultiSet with the results of applying the transform function to each value in the current ConstMultiSet. + * The [transform] function can return different values for different occurrences of an element. + * If the function returns the same value for every occurrence of an element, see the more efficient [mapToConstSetConsistent] method. + * + * @param transform (E) -> T: transformation function + * @return [ConstMultiSet]: new ConstMultiSet with transformed values + */ +inline fun ConstMultiSet.mapToConstSet(transform: (E) -> T): ConstMultiSet { + // avoid overhead of creating list + val newSet: ConstMutableMultiSet = constMutableMultiSetOf() + forEach { newSet.add(transform(it)) } + + return newSet +} + +/** + * Create a new ConstMultiSet containing only elements that match the given predicate. + * The [predicate] function can return different values for different occurrences of an element. + * If the function returns the same value for every occurrence of an element, see the more efficient [filterToConstSetConsistent] method. + * + * @param predicate (E) -> [Boolean]: predicate to use for filtering + * @return [ConstMultiSet]: ConstMultiSet containing only values for which [predicate] returns `true` + */ +inline fun ConstMultiSet.filterToConstSet(predicate: (E) -> Boolean): ConstMultiSet { + // avoid overhead of creating list + val newSet: ConstMutableMultiSet = constMutableMultiSetOf() + + forEach { + if (predicate(it)) { + newSet.add(it) + } + } + + return newSet +} + +/** + * Create a new ConstMultiSet containing only elements that do not match the given predicate. + * The [predicate] function can return different values for different occurrences of an element. + * If the function returns the same value for every occurrence of an element, see the more efficient [filterNotToConstSetConsistent] method. + * + * @param predicate (E) -> [Boolean]: predicate to use for filtering + * @return [ConstMultiSet]: ConstMultiSet containing only values for which [predicate] returns `false` + */ +inline fun ConstMultiSet.filterNotToConstSet(predicate: (E) -> Boolean): ConstMultiSet { + // avoid overhead of creating list + val newSet: ConstMutableMultiSet = constMutableMultiSetOf() + + forEach { + if (!predicate(it)) { + newSet.add(it) + } + } + + return newSet +} + +/** + * Create a new ConstMultiSet with the results of applying the transform function to each value in the current ConstMultiSet. + * Requires a [transform] function that returns the same value for every occurrence of an element. + * To use a function that does not return the same value for every occurrence, see [mapToConstSet]. + * + * @param transform (E) -> T: transformation function, which returns the same value for every occurrence of an element + * @return [ConstMultiSet]: new ConstMultiSet with transformed values + */ +inline fun ConstMultiSet.mapToConstSetConsistent(transform: (E) -> T): ConstMultiSet { + val newSet: ConstMutableMultiSet = constMutableMultiSetOf() + + distinctValues.forEach { + val mappedValue = transform(it) + val count = getCountOf(it) + // using repeat instead of List(count) to avoid overhead of creating list + repeat(count) { newSet.add(mappedValue) } + } + + return newSet +} + +/** + * Create a new ConstMultiSet containing only elements that match the given predicate. + * Requires a [predicate] function that returns the same value for every occurrence of an element. + * To use a function that does not return the same value for every occurrence, see [filterToConstSet]. + * + * @param predicate (E) -> [Boolean]: predicate to use for filtering, which returns the same value for every occurrence of an element + * @return [ConstMultiSet]: ConstMultiSet containing only values for which [predicate] returns `true` + */ +inline fun ConstMultiSet.filterToConstSetConsistent(predicate: (E) -> Boolean): ConstMultiSet { + val newSet = constMutableMultiSetOf() + + distinctValues.forEach { element -> + val matchesPredicate = predicate(element) + val count = getCountOf(element) + + if (matchesPredicate) { + // using repeat instead of List(count) to avoid overhead of creating list + repeat(count) { newSet.add(element) } + } + } + + return newSet +} + +/** + * Create a new ConstMultiSet containing only elements that do not match the given predicate. + * Requires a [predicate] function that returns the same value for every occurrence of an element. + * To use a function that does not return the same value for every occurrence, see [filterNotToConstSet]. + * + * @param predicate (E) -> [Boolean]: predicate to use for filtering, which returns the same value for every occurrence of an element + * @return [ConstMultiSet]: ConstMultiSet containing only values for which [predicate] returns `false` + */ +inline fun ConstMultiSet.filterNotToConstSetConsistent(predicate: (E) -> Boolean): ConstMultiSet { + val newSet = constMutableMultiSetOf() + + distinctValues.forEach { element -> + val matchesPredicate = predicate(element) + val count = getCountOf(element) + + if (!matchesPredicate) { + // using repeat instead of List(count) to avoid overhead of creating list + repeat(count) { newSet.add(element) } + } + } + + return newSet +} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt index 946faa98..4df70b60 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt @@ -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(initialElements: Collection) : ConstMutableMultiSet(), AbstractConstMultiSetImpl { +internal class ConstMutableMultiSetImpl(initialElements: Collection) : ConstMutableMultiSet() { /** * If all properties are up-to-date with the most recent changes to the counts map */ @@ -97,17 +96,7 @@ internal class ConstMutableMultiSetImpl(initialElements: Collection) : 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): Boolean = counts.containsAll(elements) - - override fun plus(other: MultiSet): MultiSet = super.plus(other) - override fun minus(other: MultiSet): MultiSet = super.minus(other) - override fun intersect(other: MultiSet): MultiSet = super.intersect(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 = counts override fun iterator(): MutableIterator { updateMutableValues() diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt index 14c76a8a..b2b59cda 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt @@ -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). @@ -36,7 +38,8 @@ internal abstract class AbstractMultiSetImpl(initialElements: Collection) 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 @@ -47,15 +50,9 @@ internal abstract class AbstractMultiSetImpl(initialElements: Collection) } } - override fun plus(other: MultiSet): MultiSet { - return combineCounts(counts, other, Int::plus, useAllValues = true) - } - override fun minus(other: MultiSet): MultiSet { - return combineCounts(counts, other, Int::minus, useAllValues = false) - } - override fun intersect(other: MultiSet): MultiSet { - return combineCounts(counts, other, ::min, useAllValues = false) - } + override fun plus(other: MultiSet): MultiSet = performPlus(counts, other) + override fun minus(other: MultiSet): MultiSet = performMinus(counts, other) + override fun intersect(other: MultiSet): MultiSet = performIntersect(counts, other) override fun isEmpty(): Boolean = elements.isEmpty() override fun iterator(): Iterator = elements.toList().iterator() diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/CountsMap.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/CountsMap.kt index 43755c9b..a405bd71 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/CountsMap.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/CountsMap.kt @@ -1,6 +1,9 @@ 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.ConstMultiSetImpl +import xyz.lbres.kotlinutils.set.multiset.const.ConstMutableMultiSetImpl /** * Mapping of occurrences to the number of times that they occur @@ -100,6 +103,14 @@ internal value class CountsMap(private val counts: Map) { * @return [CountsMap]: map containing exactly the elements in the given collection */ fun from(elements: Collection): CountsMap { + @Suppress(Suppressions.UNCHECKED_CAST) + try { + when (elements) { + is ConstMultiSetImpl<*> -> return elements.toCountsMap() as CountsMap + is ConstMutableMultiSetImpl<*> -> return elements.toCountsMap() as CountsMap + } + } catch (_: ClassCastException) {} + val counts: MutableMap = mutableMapOf() elements.forEach { counts[it] = counts.getOrDefault(it, 0) + 1 diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/operators.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/operators.kt new file mode 100644 index 00000000..a4f9c8e5 --- /dev/null +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/operators.kt @@ -0,0 +1,86 @@ +package xyz.lbres.kotlinutils.set.multiset.utils + +import xyz.lbres.kotlinutils.general.simpleIf +import xyz.lbres.kotlinutils.set.multiset.MultiSet +import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSetImpl +import xyz.lbres.kotlinutils.set.multiset.impl.AbstractMultiSetImpl +import xyz.lbres.kotlinutils.set.multiset.impl.MultiSetImpl +import kotlin.math.min + +/** + * Generate new MultiSet by adding values in the given CountsMap and MultiSet + * + * @param counts [CountsMap]: counts map + * @param multiset [MultiSet]: MultiSet to combine with + * @param const [Boolean]: if the returned MultiSet should be a ConstMultiSet. Defaults to `false` + * @return [MultiSet]: new set containing result of addition. If [const] is `true`, the set will be a ConstMultiSet. + */ +internal fun performPlus(counts: CountsMap, multiset: MultiSet, const: Boolean = false): MultiSet { + return combineCounts(counts, multiset, Int::plus, useAllValues = true, const) +} + +/** + * Generate new MultiSet by subtracting the values in the given MultiSet from the values in the CountsMap + * + * @param counts [CountsMap]: counts map + * @param multiset [MultiSet]: MultiSet to combine with + * @param const [Boolean]: if the returned MultiSet should be a ConstMultiSet. Defaults to `false` + * @return [MultiSet]: new set containing result of subtraction. If [const] is `true`, the set will be a ConstMultiSet. + */ +internal fun performMinus(counts: CountsMap, multiset: MultiSet, const: Boolean = false): MultiSet { + return combineCounts(counts, multiset, Int::minus, useAllValues = false, const) +} + +/** + * Generate new MultiSet by intersecting values in the given CountsMap and MultiSet + * + * @param counts [CountsMap]: counts map + * @param multiset [MultiSet]: MultiSet to combine with + * @param const [Boolean]: if the returned MultiSet should be a ConstMultiSet. Defaults to `false` + * @return [MultiSet]: new set containing result of intersection. If [const] is `true`, the set will be a ConstMultiSet. + */ +internal fun performIntersect(counts: CountsMap, multiset: MultiSet, const: Boolean = false): MultiSet { + return combineCounts(counts, multiset, ::min, useAllValues = false, const) +} + +/** + * Combine counts map and MultiSet, using the given operation + * + * @param counts [CountsMap]: counts map + * @param multiset [MultiSet]: MultiSet to combine with + * @param operation (Int, Int) -> Int: combination function + * @param useAllValues [Boolean]: if all values from the map and the set should be used to generate the new set. If `false`, only the values from the counts map will be used. + * @param const [Boolean]: if the returned MultiSet should be a ConstMultiSet + * @return [MultiSet]: new set where each element has the number of occurrences specified by the operation. If [const] is `true`, the set will be a ConstMultiSet. + */ +private fun combineCounts( + counts: CountsMap, + multiset: MultiSet, + operation: (Int, Int) -> Int, + useAllValues: Boolean, + const: Boolean +): MultiSet { + var getOtherCount = multiset::getCountOf + var otherDistinct = multiset::distinctValues + + // increase efficiency for AbstractMultiSetImpl + if (multiset is AbstractMultiSetImpl) { + val otherCounts = CountsMap.from(multiset) + getOtherCount = otherCounts::getCountOf + otherDistinct = otherCounts::distinct + } + + val values: Set = simpleIf(useAllValues, { counts.distinct + otherDistinct() }, { counts.distinct }) + val newCounts: MutableMap = mutableMapOf() + val newElements: MutableList = mutableListOf() + + values.forEach { value -> + val count = operation(counts.getCountOf(value), getOtherCount(value)) + if (count > 0) { + newCounts[value] = count + repeat(count) { newElements.add(value) } + } + } + + return simpleIf(const, { ConstMultiSetImpl(newElements, CountsMap(newCounts)) }, { MultiSetImpl(newElements) }) +} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/utils.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/utils.kt deleted file mode 100644 index f839e9ad..00000000 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/utils.kt +++ /dev/null @@ -1,43 +0,0 @@ -package xyz.lbres.kotlinutils.set.multiset.utils - -import xyz.lbres.kotlinutils.general.simpleIf -import xyz.lbres.kotlinutils.set.multiset.MultiSet -import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSetImpl -import xyz.lbres.kotlinutils.set.multiset.impl.AbstractMultiSetImpl -import xyz.lbres.kotlinutils.set.multiset.impl.MultiSetImpl - -/** - * Combine counts map and MultiSet, using the given operation - * - * @param counts [CountsMap]: counts map - * @param multiset [MultiSet]: MultiSet to combine with - * @param operation (Int, Int) -> Int: combination function - * @param useAllValues [Boolean]: if all values from the map and the set should be used to generate the new set. If `false`, only the values from the counts map will be used. - * @param const [Boolean]: if the returned MultiSet should be a ConstMultiSet. Defaults to `false` - * @return [MultiSet]: new set where each element has the number of occurrences specified by the operation. If [const] is `true`, the set will be a const multi set. - */ -internal fun combineCounts(counts: CountsMap, multiset: MultiSet, operation: (Int, Int) -> Int, useAllValues: Boolean, const: Boolean = false): MultiSet { - var getOtherCount = multiset::getCountOf - var otherDistinct = multiset::distinctValues - - // increase efficiency for AbstractMultiSetImpl - if (multiset is AbstractMultiSetImpl) { - val otherCounts = CountsMap.from(multiset) - getOtherCount = otherCounts::getCountOf - otherDistinct = otherCounts::distinct - } - - val values: Set = simpleIf(useAllValues, { counts.distinct + otherDistinct() }, { counts.distinct }) - val newCounts: MutableMap = mutableMapOf() - val newElements: MutableList = mutableListOf() - - values.forEach { value -> - val count = operation(counts.getCountOf(value), getOtherCount(value)) - if (count > 0) { - newCounts[value] = count - repeat(count) { newElements.add(value) } - } - } - - return simpleIf(const, { ConstMultiSetImpl(newElements, CountsMap(newCounts)) }, { MultiSetImpl(newElements) }) -} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/mutableset/ext/MutableSetExt.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/mutableset/ext/MutableSetExt.kt index 48e1d2ed..d76ca3a7 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/mutableset/ext/MutableSetExt.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/mutableset/ext/MutableSetExt.kt @@ -6,6 +6,6 @@ import xyz.lbres.kotlinutils.collection.mutable.ext.popRandom * Remove a random element from set and return it. * Has been moved to a MutableCollection method. * - * @return [T]?: an element from the set, or null if the set is empty + * @return T?: an element from the set, or null if the set is empty */ fun MutableSet.popRandom(): T? = popRandom() diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetCompanionTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetCompanionTest.kt index a1fb4dd3..c828c978 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetCompanionTest.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetCompanionTest.kt @@ -1,6 +1,8 @@ package xyz.lbres.kotlinutils.set.multiset +import xyz.lbres.kotlinutils.list.IntList import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSetImpl +import xyz.lbres.kotlinutils.set.multiset.const.ConstMutableMultiSetImpl import kotlin.test.Test import kotlin.test.assertEquals @@ -8,62 +10,197 @@ import kotlin.test.assertEquals class MultiSetCompanionTest { @Test fun testGenericPlus() { - val intSet1 = multiSetOf(1, 2, 2, 3, 3, 3) - val intSet2 = multiSetOf(1, 2, 0) - val intSet3 = ConstMultiSetImpl(listOf(1, 2, 2, 3, 3, 3)) - val intSet4 = ConstMultiSetImpl(listOf(1, 2, 0)) - val expectedInt = multiSetOf(1, 2, 2, 3, 3, 3, 1, 2, 0) + // empty + var intSet1 = emptyMultiSet() + var intSet2 = emptyMultiSet() + var expectedInt = emptyMultiSet() + assertEquals(expectedInt, MultiSet.genericPlus(intSet1, intSet2)) + assertEquals(expectedInt, MultiSet.genericPlus(intSet2, intSet1)) + + // non-empty + intSet1 = multiSetOf(1) + intSet2 = multiSetOf(1) + expectedInt = multiSetOf(1, 1) + assertEquals(expectedInt, MultiSet.genericPlus(intSet1, intSet2)) + assertEquals(expectedInt, MultiSet.genericPlus(intSet2, intSet1)) + intSet1 = multiSetOf(1, 2, 2, 3, 3, 3) + intSet2 = multiSetOf(1, 2, 0) + expectedInt = multiSetOf(1, 2, 2, 3, 3, 3, 1, 2, 0) assertEquals(expectedInt, MultiSet.genericPlus(intSet1, intSet2)) - assertEquals(expectedInt, MultiSet.genericPlus(intSet1, intSet4)) - assertEquals(expectedInt, MultiSet.genericPlus(intSet3, intSet2)) - assertEquals(expectedInt, MultiSet.genericPlus(intSet3, intSet4)) + assertEquals(expectedInt, MultiSet.genericPlus(intSet2, intSet1)) + + val stringSet1 = multiSetOf("", "hello", "world", "goodbye") + val stringSet2 = ConstMultiSetImpl(listOf("hi", "no", "bye")) + val expectedString = multiSetOf("", "hello", "world", "goodbye", "hi", "no", "bye") + assertEquals(expectedString, MultiSet.genericPlus(stringSet1, stringSet2)) + assertEquals(expectedString, MultiSet.genericPlus(stringSet2, stringSet1)) - val nullSet1 = mutableMultiSetOf(1, 2, 4, null) - val nullSet2 = mutableMultiSetOf(1, 3, null, null) + val listSet1 = ConstMultiSetImpl(listOf(listOf(-3), listOf(2, 3, 4), listOf(1, 2, 3))) + val listSet2 = ConstMultiSetImpl(multiSetOf(emptyList(), listOf(1, 2, 3))) + val expectedList = multiSetOf(listOf(-3), listOf(2, 3, 4), listOf(1, 2, 3), emptyList(), listOf(1, 2, 3)) + assertEquals(expectedList, MultiSet.genericPlus(listSet1, listSet2)) + assertEquals(expectedList, MultiSet.genericPlus(listSet2, listSet1)) + + val compListSet1: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) + val compListSet2: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) + val expectedCompList: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def"), listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) + assertEquals(expectedCompList, MultiSet.genericPlus(compListSet1, compListSet2)) + assertEquals(expectedCompList, MultiSet.genericPlus(compListSet2, compListSet1)) + + val nullSet1 = multiSetOf(1, 2, 4, null) + val nullSet2 = multiSetOf(1, 3, null, null) val expectedNull = multiSetOf(1, 2, 4, null, 1, 3, null, null) assertEquals(expectedNull, MultiSet.genericPlus(nullSet1, nullSet2)) assertEquals(expectedNull, MultiSet.genericPlus(nullSet2, nullSet1)) + + // mutable + val mutableSet1 = mutableMultiSetOf(1, 2) + val mutableSet2 = mutableMultiSetOf(2, 3, 4) + val expectedMutable = multiSetOf(1, 2, 2, 3, 4) + assertEquals(expectedMutable, MultiSet.genericPlus(mutableSet1, mutableSet2)) + assertEquals(expectedMutable, MultiSet.genericPlus(mutableSet2, mutableSet1)) + + val otherMutable = ConstMutableMultiSetImpl(listOf(2, 3, 4)) + assertEquals(expectedMutable, MultiSet.genericPlus(mutableSet1, otherMutable)) + assertEquals(expectedMutable, MultiSet.genericPlus(otherMutable, mutableSet1)) } @Test fun testGenericMinus() { - val intSet1 = multiSetOf(1, 1, 2, 3, 4, 5, 5) - val intSet2 = multiSetOf(1, 1, 5, 6, 6, 7) - val intSet3 = ConstMultiSetImpl(listOf(1, 1, 2, 3, 4, 5, 5)) - val intSet4 = ConstMultiSetImpl(listOf(1, 1, 5, 6, 6, 7)) - val expectedInt = multiSetOf(2, 3, 4, 5) + // empty + var intSet1 = emptyMultiSet() + var intSet2 = emptyMultiSet() + var expectedInt = emptyMultiSet() + assertEquals(expectedInt, MultiSet.genericMinus(intSet1, intSet2)) + assertEquals(expectedInt, MultiSet.genericMinus(intSet2, intSet1)) + + intSet1 = multiSetOf(1, 1, 2, 3, 4, 4, 4) + intSet2 = multiSetOf(1, 2, 2, 3, 4, 4) + expectedInt = multiSetOf(1, 4) + assertEquals(expectedInt, MultiSet.genericMinus(intSet1, intSet2)) + expectedInt = multiSetOf(2) + assertEquals(expectedInt, MultiSet.genericMinus(intSet2, intSet1)) + + intSet1 = multiSetOf(1, 2, 2, 2, 3, 3, 5, 6, 6, 7) + intSet2 = multiSetOf(1, 1, 2, 3, 3, 5, 5, 5, 6, 7, 7) + expectedInt = multiSetOf(2, 2, 6) + assertEquals(expectedInt, MultiSet.genericMinus(intSet1, intSet2)) + expectedInt = multiSetOf(1, 5, 5, 7) + assertEquals(expectedInt, MultiSet.genericMinus(intSet2, intSet1)) + + intSet1 = multiSetOf(1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8) + intSet2 = multiSetOf(-1, -1, -1, -1, -2, -3, -4, -5, -6, -7, -7, -8) + expectedInt = multiSetOf(1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8) + assertEquals(expectedInt, MultiSet.genericMinus(intSet1, intSet2)) + expectedInt = multiSetOf(-1, -1, -1, -1, -2, -3, -4, -5, -6, -7, -7, -8) + assertEquals(expectedInt, MultiSet.genericMinus(intSet2, intSet1)) + + val stringSet1 = multiSetOf("hello", "world", "goodbye", "world", "hello", "goodbye") + val stringSet2 = ConstMultiSetImpl(listOf("greetings", "planet", "farewell", "planet", "greetings", "farewell")) + var stringExpected = ConstMultiSetImpl(listOf("hello", "world", "goodbye", "world", "hello", "goodbye")) + assertEquals(stringExpected, MultiSet.genericMinus(stringSet1, stringSet2)) + stringExpected = ConstMultiSetImpl(listOf("greetings", "planet", "farewell", "planet", "greetings", "farewell")) + assertEquals(stringExpected, MultiSet.genericMinus(stringSet2, stringSet1)) + intSet1 = multiSetOf(1, 1, 2, 3, 4, 5, 5) + intSet2 = multiSetOf(1, 1, 5, 6, 6, 7) + expectedInt = multiSetOf(2, 3, 4, 5) assertEquals(expectedInt, MultiSet.genericMinus(intSet1, intSet2)) - assertEquals(expectedInt, MultiSet.genericMinus(intSet1, intSet4)) - assertEquals(expectedInt, MultiSet.genericMinus(intSet3, intSet2)) - assertEquals(expectedInt, MultiSet.genericMinus(intSet3, intSet4)) + expectedInt = multiSetOf(6, 6, 7) + assertEquals(expectedInt, MultiSet.genericMinus(intSet2, intSet1)) - val nullSet1 = mutableMultiSetOf(1, 1, 2, null) - val nullSet2 = mutableMultiSetOf(1, 1, 5, 6, null, null) + val listSet1 = ConstMultiSetImpl(listOf(listOf(1, 2, 3), listOf(2, 3, 4), listOf(1, 2, 3))) + val listSet2: MultiSet> = ConstMultiSetImpl(listOf(emptyList(), listOf(1, 2, 3))) + var listExpected = multiSetOf(listOf(1, 2, 3), listOf(2, 3, 4)) + assertEquals(listExpected, MultiSet.genericMinus(listSet1, listSet2)) + listExpected = multiSetOf(emptyList()) + assertEquals(listExpected, MultiSet.genericMinus(listSet2, listSet1)) + + val compListSet1: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) + val compListSet2: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) + var compListExpected: MultiSet>> = multiSetOf(listOf("abc", "def"), listOf("abc", "def")) + assertEquals(compListExpected, MultiSet.genericMinus(compListSet1, compListSet2)) + compListExpected = multiSetOf(listOf(1, 2, 3), emptyList()) + assertEquals(compListExpected, MultiSet.genericMinus(compListSet2, compListSet1)) + + val nullSet1 = multiSetOf(1, 1, 2, null) var expectedNull: MultiSet = multiSetOf(2) + val nullSet2 = multiSetOf(1, 1, 5, 6, null, null) assertEquals(expectedNull, MultiSet.genericMinus(nullSet1, nullSet2)) expectedNull = multiSetOf(5, 6, null) assertEquals(expectedNull, MultiSet.genericMinus(nullSet2, nullSet1)) + + // mutable + val mutableSet1 = mutableMultiSetOf(1, 2, 3, 3) + val mutableSet2 = mutableMultiSetOf(2, 3, 4) + val expectedMutable = multiSetOf(1, 3) + assertEquals(expectedMutable, MultiSet.genericMinus(mutableSet1, mutableSet2)) + + val otherMutable = ConstMutableMultiSetImpl(listOf(2, 3, 4)) + assertEquals(expectedMutable, MultiSet.genericMinus(mutableSet1, otherMutable)) } @Test fun testGenericIntersect() { - val intSet1 = multiSetOf(1, 2, 3, 5, 5, 5) - val intSet2 = multiSetOf(4, 5, 5, 6, 7, 8) - val intSet3 = ConstMultiSetImpl(listOf(1, 2, 3, 5, 5, 5)) - val intSet4 = ConstMultiSetImpl(listOf(4, 5, 5, 6, 7, 8)) - val expectedInt = multiSetOf(5, 5) + // empty + var intSet1 = emptyMultiSet() + var intSet2 = emptyMultiSet() + var expectedInt = emptyMultiSet() + assertEquals(expectedInt, MultiSet.genericIntersect(intSet1, intSet2)) + assertEquals(expectedInt, MultiSet.genericIntersect(intSet2, intSet1)) + + intSet1 = emptyMultiSet() + intSet2 = multiSetOf(1, 2, 3) + expectedInt = emptyMultiSet() + assertEquals(expectedInt, MultiSet.genericIntersect(intSet1, intSet2)) + assertEquals(expectedInt, MultiSet.genericIntersect(intSet2, intSet1)) + + intSet1 = multiSetOf(1, 2, 3) + intSet2 = multiSetOf(4, 5, 6, 7, 8) + expectedInt = emptyMultiSet() + assertEquals(expectedInt, MultiSet.genericIntersect(intSet1, intSet2)) + assertEquals(expectedInt, MultiSet.genericIntersect(intSet2, intSet1)) + + var listSet1 = multiSetOf(listOf(1, 2, 3), listOf(4, 5)) + var listSet2 = multiSetOf(listOf(1, 2), listOf(3, 4, 5)) + var expectedList = emptyMultiSet() + assertEquals(expectedList, MultiSet.genericIntersect(listSet1, listSet2)) + assertEquals(expectedList, MultiSet.genericIntersect(listSet2, listSet1)) + + intSet1 = multiSetOf(1, 2, 3) + intSet2 = multiSetOf(1, 2, 3) + expectedInt = multiSetOf(1, 2, 3) + assertEquals(expectedInt, MultiSet.genericIntersect(intSet1, intSet2)) + assertEquals(expectedInt, MultiSet.genericIntersect(intSet2, intSet1)) + intSet1 = multiSetOf(1, 2, 2, 4, 5, 6, 7, -1, 10) + intSet2 = multiSetOf(-1, 14, 3, 9, 9, 6) + expectedInt = multiSetOf(-1, 6) assertEquals(expectedInt, MultiSet.genericIntersect(intSet1, intSet2)) - assertEquals(expectedInt, MultiSet.genericIntersect(intSet3, intSet2)) - assertEquals(expectedInt, MultiSet.genericIntersect(intSet1, intSet4)) - assertEquals(expectedInt, MultiSet.genericIntersect(intSet4, intSet3)) - - val nullSet1 = mutableMultiSetOf(1, 1, 2, null) - val nullSet2 = mutableMultiSetOf(1, 1, 5, 6, null, null) - val nullExpected = multiSetOf(1, 1, null) - assertEquals(nullExpected, MultiSet.genericIntersect(nullSet1, nullSet2)) - assertEquals(nullExpected, MultiSet.genericIntersect(nullSet2, nullSet1)) + assertEquals(expectedInt, MultiSet.genericIntersect(intSet2, intSet1)) + + listSet1 = multiSetOf(listOf(1, 2, 3), listOf(2, 3, 4), listOf(1, 2, 3)) + listSet2 = multiSetOf(emptyList(), listOf(1, 2, 3)) + expectedList = multiSetOf(listOf(1, 2, 3)) + assertEquals(expectedList, MultiSet.genericIntersect(listSet1, listSet2)) + assertEquals(expectedList, MultiSet.genericIntersect(listSet2, listSet1)) + + val nullSet1 = multiSetOf(1, 1, 2, null) + val nullSet2 = multiSetOf(1, 1, 5, 6, null, null) + val expectedNull = multiSetOf(1, 1, null) + assertEquals(expectedNull, MultiSet.genericIntersect(nullSet1, nullSet2)) + assertEquals(expectedNull, MultiSet.genericIntersect(nullSet2, nullSet1)) + + // mutable + val mutableSet1 = mutableMultiSetOf(1, 2, 3) + val mutableSet2 = mutableMultiSetOf(2, 3, 4, 2) + val expectedMutable = multiSetOf(2, 3) + assertEquals(expectedMutable, MultiSet.genericIntersect(mutableSet1, mutableSet2)) + assertEquals(expectedMutable, MultiSet.genericIntersect(mutableSet2, mutableSet1)) + + val otherMutable = ConstMutableMultiSetImpl(listOf(2, 3, 4, 2)) + assertEquals(expectedMutable, MultiSet.genericIntersect(mutableSet1, otherMutable)) + assertEquals(expectedMutable, MultiSet.genericIntersect(otherMutable, mutableSet1)) } } diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenericsTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenericsTest.kt deleted file mode 100644 index 0bc2f483..00000000 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetGenericsTest.kt +++ /dev/null @@ -1,205 +0,0 @@ -package xyz.lbres.kotlinutils.set.multiset - -import xyz.lbres.kotlinutils.list.IntList -import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSetImpl -import xyz.lbres.kotlinutils.set.multiset.const.ConstMutableMultiSetImpl -import kotlin.test.Test -import kotlin.test.assertEquals - -class MultiSetGenericsTest { - @Test - fun testGenericMultiSetPlus() { - // empty - var intSet1 = emptyMultiSet() - var intSet2 = emptyMultiSet() - var expectedInt = emptyMultiSet() - assertEquals(expectedInt, genericMultiSetPlus(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetPlus(intSet2, intSet1)) - - // non-empty - intSet1 = multiSetOf(1) - intSet2 = multiSetOf(1) - expectedInt = multiSetOf(1, 1) - assertEquals(expectedInt, genericMultiSetPlus(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetPlus(intSet2, intSet1)) - - intSet1 = multiSetOf(1, 2, 2, 3, 3, 3) - intSet2 = multiSetOf(1, 2, 0) - expectedInt = multiSetOf(1, 2, 2, 3, 3, 3, 1, 2, 0) - assertEquals(expectedInt, genericMultiSetPlus(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetPlus(intSet2, intSet1)) - - val stringSet1 = multiSetOf("", "hello", "world", "goodbye") - val stringSet2 = ConstMultiSetImpl(listOf("hi", "no", "bye")) - val expectedString = multiSetOf("", "hello", "world", "goodbye", "hi", "no", "bye") - assertEquals(expectedString, genericMultiSetPlus(stringSet1, stringSet2)) - assertEquals(expectedString, genericMultiSetPlus(stringSet2, stringSet1)) - - val listSet1 = ConstMultiSetImpl(listOf(listOf(-3), listOf(2, 3, 4), listOf(1, 2, 3))) - val listSet2 = ConstMultiSetImpl(multiSetOf(emptyList(), listOf(1, 2, 3))) - val expectedList = multiSetOf(listOf(-3), listOf(2, 3, 4), listOf(1, 2, 3), emptyList(), listOf(1, 2, 3)) - assertEquals(expectedList, genericMultiSetPlus(listSet1, listSet2)) - assertEquals(expectedList, genericMultiSetPlus(listSet2, listSet1)) - - val compListSet1: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) - val compListSet2: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) - val expectedCompList: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def"), listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) - assertEquals(expectedCompList, genericMultiSetPlus(compListSet1, compListSet2)) - assertEquals(expectedCompList, genericMultiSetPlus(compListSet2, compListSet1)) - - val nullSet1 = multiSetOf(1, 2, 4, null) - val nullSet2 = multiSetOf(1, 3, null, null) - val expectedNull = multiSetOf(1, 2, 4, null, 1, 3, null, null) - assertEquals(expectedNull, genericMultiSetPlus(nullSet1, nullSet2)) - assertEquals(expectedNull, genericMultiSetPlus(nullSet2, nullSet1)) - - // mutable - val mutableSet1 = mutableMultiSetOf(1, 2) - val mutableSet2 = mutableMultiSetOf(2, 3, 4) - val expectedMutable = multiSetOf(1, 2, 2, 3, 4) - assertEquals(expectedMutable, genericMultiSetPlus(mutableSet1, mutableSet2)) - assertEquals(expectedMutable, genericMultiSetPlus(mutableSet2, mutableSet1)) - - val otherMutable = ConstMutableMultiSetImpl(listOf(2, 3, 4)) - assertEquals(expectedMutable, genericMultiSetPlus(mutableSet1, otherMutable)) - assertEquals(expectedMutable, genericMultiSetPlus(otherMutable, mutableSet1)) - } - - @Test - fun testGenericMultiSetMinus() { - // empty - var intSet1 = emptyMultiSet() - var intSet2 = emptyMultiSet() - var expectedInt = emptyMultiSet() - assertEquals(expectedInt, genericMultiSetMinus(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetMinus(intSet2, intSet1)) - - intSet1 = multiSetOf(1, 1, 2, 3, 4, 4, 4) - intSet2 = multiSetOf(1, 2, 2, 3, 4, 4) - expectedInt = multiSetOf(1, 4) - assertEquals(expectedInt, genericMultiSetMinus(intSet1, intSet2)) - expectedInt = multiSetOf(2) - assertEquals(expectedInt, genericMultiSetMinus(intSet2, intSet1)) - - intSet1 = multiSetOf(1, 2, 2, 2, 3, 3, 5, 6, 6, 7) - intSet2 = multiSetOf(1, 1, 2, 3, 3, 5, 5, 5, 6, 7, 7) - expectedInt = multiSetOf(2, 2, 6) - assertEquals(expectedInt, genericMultiSetMinus(intSet1, intSet2)) - expectedInt = multiSetOf(1, 5, 5, 7) - assertEquals(expectedInt, genericMultiSetMinus(intSet2, intSet1)) - - intSet1 = multiSetOf(1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8) - intSet2 = multiSetOf(-1, -1, -1, -1, -2, -3, -4, -5, -6, -7, -7, -8) - expectedInt = multiSetOf(1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8) - assertEquals(expectedInt, genericMultiSetMinus(intSet1, intSet2)) - expectedInt = multiSetOf(-1, -1, -1, -1, -2, -3, -4, -5, -6, -7, -7, -8) - assertEquals(expectedInt, genericMultiSetMinus(intSet2, intSet1)) - - val stringSet1 = multiSetOf("hello", "world", "goodbye", "world", "hello", "goodbye") - val stringSet2 = ConstMultiSetImpl(listOf("greetings", "planet", "farewell", "planet", "greetings", "farewell")) - var stringExpected = ConstMultiSetImpl(listOf("hello", "world", "goodbye", "world", "hello", "goodbye")) - assertEquals(stringExpected, genericMultiSetMinus(stringSet1, stringSet2)) - stringExpected = ConstMultiSetImpl(listOf("greetings", "planet", "farewell", "planet", "greetings", "farewell")) - assertEquals(stringExpected, genericMultiSetMinus(stringSet2, stringSet1)) - - intSet1 = multiSetOf(1, 1, 2, 3, 4, 5, 5) - intSet2 = multiSetOf(1, 1, 5, 6, 6, 7) - expectedInt = multiSetOf(2, 3, 4, 5) - assertEquals(expectedInt, genericMultiSetMinus(intSet1, intSet2)) - expectedInt = multiSetOf(6, 6, 7) - assertEquals(expectedInt, genericMultiSetMinus(intSet2, intSet1)) - - val listSet1 = ConstMultiSetImpl(listOf(listOf(1, 2, 3), listOf(2, 3, 4), listOf(1, 2, 3))) - val listSet2: MultiSet> = ConstMultiSetImpl(listOf(emptyList(), listOf(1, 2, 3))) - var listExpected = multiSetOf(listOf(1, 2, 3), listOf(2, 3, 4)) - assertEquals(listExpected, genericMultiSetMinus(listSet1, listSet2)) - listExpected = multiSetOf(emptyList()) - assertEquals(listExpected, genericMultiSetMinus(listSet2, listSet1)) - - val compListSet1: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) - val compListSet2: MultiSet>> = multiSetOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) - var compListExpected: MultiSet>> = multiSetOf(listOf("abc", "def"), listOf("abc", "def")) - assertEquals(compListExpected, genericMultiSetMinus(compListSet1, compListSet2)) - compListExpected = multiSetOf(listOf(1, 2, 3), emptyList()) - assertEquals(compListExpected, genericMultiSetMinus(compListSet2, compListSet1)) - - val nullSet1 = multiSetOf(1, 1, 2, null) - var expectedNull: MultiSet = multiSetOf(2) - val nullSet2 = multiSetOf(1, 1, 5, 6, null, null) - assertEquals(expectedNull, genericMultiSetMinus(nullSet1, nullSet2)) - expectedNull = multiSetOf(5, 6, null) - assertEquals(expectedNull, genericMultiSetMinus(nullSet2, nullSet1)) - - // mutable - val mutableSet1 = mutableMultiSetOf(1, 2, 3, 3) - val mutableSet2 = mutableMultiSetOf(2, 3, 4) - val expectedMutable = multiSetOf(1, 3) - assertEquals(expectedMutable, genericMultiSetMinus(mutableSet1, mutableSet2)) - - val otherMutable = ConstMutableMultiSetImpl(listOf(2, 3, 4)) - assertEquals(expectedMutable, genericMultiSetMinus(mutableSet1, otherMutable)) - } - - @Test - fun testGenericMultiSetIntersect() { - // empty - var intSet1 = emptyMultiSet() - var intSet2 = emptyMultiSet() - var expectedInt = emptyMultiSet() - assertEquals(expectedInt, genericMultiSetIntersect(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetIntersect(intSet2, intSet1)) - - intSet1 = emptyMultiSet() - intSet2 = multiSetOf(1, 2, 3) - expectedInt = emptyMultiSet() - assertEquals(expectedInt, genericMultiSetIntersect(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetIntersect(intSet2, intSet1)) - - intSet1 = multiSetOf(1, 2, 3) - intSet2 = multiSetOf(4, 5, 6, 7, 8) - expectedInt = emptyMultiSet() - assertEquals(expectedInt, genericMultiSetIntersect(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetIntersect(intSet2, intSet1)) - - var listSet1 = multiSetOf(listOf(1, 2, 3), listOf(4, 5)) - var listSet2 = multiSetOf(listOf(1, 2), listOf(3, 4, 5)) - var expectedList = emptyMultiSet() - assertEquals(expectedList, genericMultiSetIntersect(listSet1, listSet2)) - assertEquals(expectedList, genericMultiSetIntersect(listSet2, listSet1)) - - intSet1 = multiSetOf(1, 2, 3) - intSet2 = multiSetOf(1, 2, 3) - expectedInt = multiSetOf(1, 2, 3) - assertEquals(expectedInt, genericMultiSetIntersect(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetIntersect(intSet2, intSet1)) - - intSet1 = multiSetOf(1, 2, 2, 4, 5, 6, 7, -1, 10) - intSet2 = multiSetOf(-1, 14, 3, 9, 9, 6) - expectedInt = multiSetOf(-1, 6) - assertEquals(expectedInt, genericMultiSetIntersect(intSet1, intSet2)) - assertEquals(expectedInt, genericMultiSetIntersect(intSet2, intSet1)) - - listSet1 = multiSetOf(listOf(1, 2, 3), listOf(2, 3, 4), listOf(1, 2, 3)) - listSet2 = multiSetOf(emptyList(), listOf(1, 2, 3)) - expectedList = multiSetOf(listOf(1, 2, 3)) - assertEquals(expectedList, genericMultiSetIntersect(listSet1, listSet2)) - assertEquals(expectedList, genericMultiSetIntersect(listSet2, listSet1)) - - val nullSet1 = multiSetOf(1, 1, 2, null) - val nullSet2 = multiSetOf(1, 1, 5, 6, null, null) - val expectedNull = multiSetOf(1, 1, null) - assertEquals(expectedNull, genericMultiSetIntersect(nullSet1, nullSet2)) - assertEquals(expectedNull, genericMultiSetIntersect(nullSet2, nullSet1)) - - // mutable - val mutableSet1 = mutableMultiSetOf(1, 2, 3) - val mutableSet2 = mutableMultiSetOf(2, 3, 4, 2) - val expectedMutable = multiSetOf(2, 3) - assertEquals(expectedMutable, genericMultiSetIntersect(mutableSet1, mutableSet2)) - assertEquals(expectedMutable, genericMultiSetIntersect(mutableSet2, mutableSet1)) - - val otherMutable = ConstMutableMultiSetImpl(listOf(2, 3, 4, 2)) - assertEquals(expectedMutable, genericMultiSetIntersect(mutableSet1, otherMutable)) - assertEquals(expectedMutable, genericMultiSetIntersect(otherMutable, mutableSet1)) - } -} diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethodsTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethodsTest.kt index ce8dcaa6..60b3dc2b 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethodsTest.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/MultiSetInlineMethodsTest.kt @@ -22,4 +22,6 @@ class MultiSetInlineMethodsTest { @Test fun testMinByConsistent() = runMinByConsistentTests() @Test fun testMaxByConsistent() = runMaxByConsistentTests() + + @Test fun testCountConsistent() = runCountConsistentTests() } diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethodsTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethodsTest.kt new file mode 100644 index 00000000..e67702ef --- /dev/null +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetInlineMethodsTest.kt @@ -0,0 +1,61 @@ +package xyz.lbres.kotlinutils.set.multiset.const + +import org.junit.Test +import xyz.lbres.kotlinutils.internal.constants.Suppressions +import xyz.lbres.kotlinutils.set.multiset.testutils.* // ktlint-disable no-wildcard-imports no-unused-imports + +class ConstMultiSetInlineMethodsTest { + @Test + fun testMapToConstSet() { + runCommonMapToSetTests(::ConstMultiSetImpl, true) { set, fn -> + set as ConstMultiSet + @Suppress(Suppressions.UNCHECKED_CAST) + set.mapToConstSet(fn as (Any?) -> Any) + } + } + + @Test + fun testFilterToConstSet() { + runCommonFilterToSetTests(::ConstMultiSetImpl, true) { set, fn -> + set as ConstMultiSet + @Suppress(Suppressions.UNCHECKED_CAST) + set.filterToConstSet(fn as (Any?) -> Boolean) + } + } + + @Test + fun testFilterNotToConstSet() { + runCommonFilterNotToSetTests(::ConstMultiSetImpl, true) { set, fn -> + set as ConstMultiSet + @Suppress(Suppressions.UNCHECKED_CAST) + set.filterNotToConstSet(fn as (Any?) -> Boolean) + } + } + + @Test + fun testMapToConstSetConsistent() { + runCommonMapToSetConsistentTests(::ConstMultiSetImpl, true) { set, fn -> + set as ConstMultiSet + @Suppress(Suppressions.UNCHECKED_CAST) + set.mapToConstSetConsistent(fn as (Any?) -> Any) + } + } + + @Test + fun testFilterToConstSetConsistent() { + runCommonFilterToSetConsistentTests(::ConstMultiSetImpl, true) { set, fn -> + set as ConstMultiSet + @Suppress(Suppressions.UNCHECKED_CAST) + set.filterToConstSetConsistent(fn as (Any?) -> Boolean) + } + } + + @Test + fun testFilterNotToConstSetConsistent() { + runCommonFilterNotToSetConsistentTests(::ConstMultiSetImpl, true) { set, fn -> + set as ConstMultiSet + @Suppress(Suppressions.UNCHECKED_CAST) + set.filterNotToConstSetConsistent(fn as (Any?) -> Boolean) + } + } +} diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetTest.kt index 197c5b37..4e1ec265 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetTest.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetTest.kt @@ -27,9 +27,13 @@ class ConstMultiSetTest { @Test fun testContains() = runContainsTests(::ConstMultiSetImpl) @Test fun testContainsAll() = runContainsAllTests(::ConstMultiSetImpl) - @Test fun testMinus() = runMinusTests(::ConstMultiSetImpl, ::MutableMultiSetImpl) - @Test fun testPlus() = runPlusTests(::ConstMultiSetImpl, ::MutableMultiSetImpl) - @Test fun testIntersect() = runIntersectTests(::ConstMultiSetImpl, ::MutableMultiSetImpl) + @Test fun testMinus() = runMinusTests(::ConstMultiSetImpl, ::MutableMultiSetImpl, const = false) + @Test fun testPlus() = runPlusTests(::ConstMultiSetImpl, ::MutableMultiSetImpl, const = false) + @Test fun testIntersect() = runIntersectTests(::ConstMultiSetImpl, ::MutableMultiSetImpl, const = false) + + @Test fun testMinusC() = runMinusTests(::ConstMultiSetImpl, ::ConstMutableMultiSetImpl, const = true) + @Test fun testPlusC() = runPlusTests(::ConstMultiSetImpl, ::ConstMutableMultiSetImpl, const = true) + @Test fun testIntersectC() = runIntersectTests(::ConstMultiSetImpl, ::ConstMutableMultiSetImpl, const = true) @Test fun testIsEmpty() = runIsEmptyTests(::ConstMultiSetImpl) @Test fun testGetCountOf() = runGetCountOfTests(::ConstMultiSetImpl) diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetTest.kt index 56bdd205..778fbbf0 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetTest.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetTest.kt @@ -34,9 +34,13 @@ class ConstMutableMultiSetTest { @Test fun testRemoveAll() = runRemoveAllTests(::ConstMutableMultiSetImpl) @Test fun testRetainAll() = runClearTests(::ConstMutableMultiSetImpl) - @Test fun testMinus() = runMinusTests(::ConstMutableMultiSetImpl, ::MultiSetImpl) - @Test fun testPlus() = runPlusTests(::ConstMutableMultiSetImpl, ::MultiSetImpl) - @Test fun testIntersect() = runIntersectTests(::ConstMutableMultiSetImpl, ::MultiSetImpl) + @Test fun testMinus() = runMinusTests(::ConstMutableMultiSetImpl, ::MultiSetImpl, const = false) + @Test fun testPlus() = runPlusTests(::ConstMutableMultiSetImpl, ::MultiSetImpl, const = false) + @Test fun testIntersect() = runIntersectTests(::ConstMutableMultiSetImpl, ::MultiSetImpl, const = false) + + @Test fun testMinusC() = runMinusTests(::ConstMutableMultiSetImpl, ::ConstMultiSetImpl, const = true) + @Test fun testPlusC() = runPlusTests(::ConstMutableMultiSetImpl, ::ConstMultiSetImpl, const = true) + @Test fun testIntersectC() = runIntersectTests(::ConstMutableMultiSetImpl, ::ConstMultiSetImpl, const = true) @Test fun testIsEmpty() = runMutableIsEmptyTests(::ConstMutableMultiSetImpl) @Test fun testGetCountOf() = runMutableGetCountOfTests(::ConstMutableMultiSetImpl) diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MultiSetImplTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MultiSetImplTest.kt index 29b96964..56c33b66 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MultiSetImplTest.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MultiSetImplTest.kt @@ -43,19 +43,19 @@ class MultiSetImplTest { @Test fun testMinus() { - runMinusTests(::MultiSetImpl, ::ConstMutableMultiSetImpl) + runMinusTests(::MultiSetImpl, ::ConstMutableMultiSetImpl, const = false) runMutableElementMinusTests(::MultiSetImpl) } @Test fun testPlus() { - runPlusTests(::MultiSetImpl, ::ConstMutableMultiSetImpl) + runPlusTests(::MultiSetImpl, ::ConstMutableMultiSetImpl, const = false) runMutableElementPlusTests(::MultiSetImpl) } @Test fun testIntersect() { - runIntersectTests(::MultiSetImpl, ::ConstMutableMultiSetImpl) + runIntersectTests(::MultiSetImpl, ::ConstMutableMultiSetImpl, const = false) runMutableElementIntersectTests(::MultiSetImpl) } diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MutableMultiSetImplTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MutableMultiSetImplTest.kt index 973913f1..27d4692b 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MutableMultiSetImplTest.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/MutableMultiSetImplTest.kt @@ -50,19 +50,19 @@ class MutableMultiSetImplTest { @Test fun testMinus() { - runMinusTests(::MutableMultiSetImpl, ::ConstMultiSetImpl) + runMinusTests(::MutableMultiSetImpl, ::ConstMultiSetImpl, const = false) runMutableElementMinusTests(::MutableMultiSetImpl) } @Test fun testPlus() { - runPlusTests(::MutableMultiSetImpl, ::ConstMultiSetImpl) + runPlusTests(::MutableMultiSetImpl, ::ConstMultiSetImpl, const = false) runMutableElementPlusTests(::MutableMultiSetImpl) } @Test fun testIntersect() { - runIntersectTests(::MutableMultiSetImpl, ::ConstMultiSetImpl) + runIntersectTests(::MutableMultiSetImpl, ::ConstMultiSetImpl, const = false) runMutableElementIntersectTests(::MutableMultiSetImpl) } diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/inline/minMaxConsistentTests.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/inline/minMaxCountConsistentTests.kt similarity index 73% rename from kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/inline/minMaxConsistentTests.kt rename to kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/inline/minMaxCountConsistentTests.kt index 161b7279..86a9966c 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/inline/minMaxConsistentTests.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/inline/minMaxCountConsistentTests.kt @@ -1,5 +1,6 @@ package xyz.lbres.kotlinutils.set.multiset.inline +import xyz.lbres.kotlinutils.int.ext.isZero import xyz.lbres.kotlinutils.list.IntList import xyz.lbres.kotlinutils.set.multiset.* // ktlint-disable no-wildcard-imports no-unused-imports import kotlin.test.assertContains @@ -146,3 +147,49 @@ fun runMaxByConsistentTests() { past12 = false assertContains(listOf(listOf(1, 2, 3), listOf(1, 2, 3, 0)), listSet.maxByOrNullConsistent(listSelector)) } + +fun runCountConsistentTests() { + // empty set + var intSet: MultiSet = multiSetOf() + assertEquals(0, intSet.countConsistent { it == 0 }) + assertEquals(0, intSet.countConsistent { it != 0 }) + + // none match + intSet = multiSetOf(1, 2, -1) + assertEquals(0, intSet.countConsistent(Int::isZero)) + + var listSet = multiSetOf(listOf(1, 2), listOf("hello", 123), listOf(-.5)) + assertEquals(0, listSet.countConsistent(List<*>::isEmpty)) + + // all match + intSet = multiSetOf(0, 0, 0) + assertEquals(3, intSet.countConsistent(Int::isZero)) + + listSet = multiSetOf(listOf(1, 2), listOf("hello", 123), listOf(-.5)) + assertEquals(3, listSet.countConsistent(List<*>::isNotEmpty)) + + // some match + intSet = multiSetOf(-1, 0, 0) + assertEquals(2, intSet.countConsistent(Int::isZero)) + + intSet = multiSetOf(1, -1, 9, 8, 5, 5, 2, 3, 8, 5, -2) + assertEquals(5, intSet.countConsistent { intSet.getCountOf(it) > 1 }) + + listSet = multiSetOf(listOf(1, 2), emptyList(), listOf(-.5)) + assertEquals(1, listSet.countConsistent(List<*>::isEmpty)) + + // modified + var includeOdd = true + intSet = multiSetOf(1, 1, 4, 5, 1, 4) + val predicate: (Int) -> Boolean = { + if (it % 2 == 0) { + true + } else { + val result = includeOdd + includeOdd = !includeOdd + result + } + } + val resultOptions = listOf(3, 5) + assertContains(resultOptions, intSet.countConsistent(predicate)) +} diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonIntersectTests.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonIntersectTests.kt index 33af1ea0..ad1147bf 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonIntersectTests.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonIntersectTests.kt @@ -1,19 +1,31 @@ package xyz.lbres.kotlinutils.set.multiset.testutils import xyz.lbres.kotlinutils.assertEmpty +import xyz.lbres.kotlinutils.general.simpleIf +import xyz.lbres.kotlinutils.internal.constants.Suppressions import xyz.lbres.kotlinutils.list.IntList import xyz.lbres.kotlinutils.set.multiset.MultiSet import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSet import xyz.lbres.kotlinutils.set.multiset.emptyMultiSet import xyz.lbres.kotlinutils.set.multiset.multiSetOf import kotlin.test.assertEquals -import kotlin.test.assertIsNot private val e1 = ArithmeticException() private val e2 = NullPointerException() private val e3 = IllegalArgumentException() -fun runIntersectTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Collection<*>) -> MultiSet<*>) { +@Suppress(Suppressions.UNCHECKED_CAST) +private val genericIntersect: MultiSetOp<*> = { set1: MultiSet, set2: MultiSet -> set1 intersect set2 } as MultiSetOp<*> + +@Suppress(Suppressions.UNCHECKED_CAST) +private val genericConstIntersect: MultiSetOp<*> = { set1: MultiSet, set2: MultiSet -> + set1 as ConstMultiSet + set2 as ConstMultiSet + set1 intersectC set2 +} as MultiSetOp<*> + +fun runIntersectTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Collection<*>) -> MultiSet<*>, const: Boolean) { + val intersectFn = simpleIf(const, genericConstIntersect, genericIntersect) val createIntSet = getCreateSet(createSet) val createIntListSet = getCreateSet(createSet) val createExceptionSet = getCreateSet(createSet) @@ -22,63 +34,63 @@ fun runIntersectTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: // empty var intSet1 = createIntSet(emptyList()) var intSet2 = createIntSet(emptyList()) - assertEmpty(intSet1 intersect intSet2) + assertEmpty(intersectFn(intSet1, intSet2)) - assertIsNot>(intSet1 intersect intSet2) + assertEquals(const, (intersectFn(intSet1, intSet2)) is ConstMultiSet<*>) intSet2 = createIntSet(listOf(1, 2, 3)) - assertEmpty(intSet1 intersect intSet2) - assertEmpty(intSet2 intersect intSet1) + assertEmpty(intersectFn(intSet1, intSet2)) + assertEmpty(intersectFn(intSet2, intSet1)) // none shared intSet1 = createIntSet(listOf(1, 2, 3)) intSet2 = createIntSet(listOf(4, 5, 6, 7, 8)) - assertEmpty(intSet1 intersect intSet2) - assertEmpty(intSet2 intersect intSet1) + assertEmpty(intersectFn(intSet1, intSet2)) + assertEmpty(intersectFn(intSet2, intSet1)) var otherSet = createOtherIntSet(listOf(4, 5, 6, 7, 8)) - assertEmpty(intSet1 intersect otherSet) + assertEmpty(intersectFn(intSet1, otherSet)) var listSet1 = createIntListSet(listOf(listOf(1, 2, 3), listOf(4, 5))) var listSet2 = createIntListSet(listOf(listOf(1, 2), listOf(3, 4, 5))) - assertEmpty(listSet1 intersect listSet2) - assertEmpty(listSet2 intersect listSet1) + assertEmpty(intersectFn(listSet1, intSet2)) + assertEmpty(intersectFn(listSet2, intSet1)) // all overlapping elements intSet1 = createIntSet(listOf(1, 2, 3)) intSet2 = createIntSet(listOf(1, 2, 3)) var expectedInt = multiSetOf(1, 2, 3) - assertEquals(expectedInt, intSet1 intersect intSet2) - assertEquals(expectedInt, intSet2 intersect intSet1) + assertEquals(expectedInt, intersectFn(intSet1, intSet2)) + assertEquals(expectedInt, intersectFn(intSet2, intSet1)) intSet1 = createIntSet(listOf(1, 1, 2, 2, 3, 3)) intSet2 = createIntSet(listOf(1, 2, 2, 2, 3)) expectedInt = multiSetOf(1, 2, 2, 3) - assertEquals(expectedInt, intSet1 intersect intSet2) - assertEquals(expectedInt, intSet2 intersect intSet1) + assertEquals(expectedInt, intersectFn(intSet1, intSet2)) + assertEquals(expectedInt, intersectFn(intSet2, intSet1)) // some overlapping elements intSet1 = createIntSet(listOf(1, 2, 2, 4, 5, 6, 7, -1, 10)) intSet2 = createIntSet(listOf(-1, 14, 3, 9, 9, 6)) expectedInt = multiSetOf(-1, 6) - assertEquals(expectedInt, intSet1 intersect intSet2) - assertEquals(expectedInt, intSet2 intersect intSet1) + assertEquals(expectedInt, intersectFn(intSet1, intSet2)) + assertEquals(expectedInt, intersectFn(intSet2, intSet1)) otherSet = createOtherIntSet(listOf(-1, 14, 3, 9, 9, 6)) expectedInt = multiSetOf(-1, 6) - assertEquals(expectedInt, intSet1 intersect otherSet) + assertEquals(expectedInt, intersectFn(intSet1, otherSet)) listSet1 = createIntListSet(listOf(listOf(1, 2, 3), listOf(2, 3, 4), listOf(1, 2, 3))) listSet2 = createIntListSet(listOf(emptyList(), listOf(1, 2, 3))) val expectedList = multiSetOf(listOf(1, 2, 3)) - assertEquals(expectedList, listSet1 intersect listSet2) - assertEquals(expectedList, listSet2 intersect listSet1) + assertEquals(expectedList, intersectFn(listSet1, listSet2)) + assertEquals(expectedList, intersectFn(listSet2, listSet1)) val errorSet1: MultiSet = createExceptionSet(listOf(e1, e2, e1, e2)) val errorSet2: MultiSet = createExceptionSet(listOf(e1, e3, e3, e2, e1, e1)) val expectedError: MultiSet = multiSetOf(e1, e1, e2) - assertEquals(expectedError, errorSet1 intersect errorSet2) - assertEquals(expectedError, errorSet2 intersect errorSet1) + assertEquals(expectedError, intersectFn(errorSet1, errorSet2)) + assertEquals(expectedError, intersectFn(errorSet2, errorSet1)) } fun runMutableElementIntersectTests(createIntListSet: (List) -> MultiSet>) { diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonMinusTests.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonMinusTests.kt index 32e3feae..8bbbd5b3 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonMinusTests.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonMinusTests.kt @@ -1,18 +1,30 @@ package xyz.lbres.kotlinutils.set.multiset.testutils import xyz.lbres.kotlinutils.assertEmpty +import xyz.lbres.kotlinutils.general.simpleIf +import xyz.lbres.kotlinutils.internal.constants.Suppressions import xyz.lbres.kotlinutils.list.IntList import xyz.lbres.kotlinutils.set.multiset.MultiSet import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSet import xyz.lbres.kotlinutils.set.multiset.multiSetOf import kotlin.test.assertEquals -import kotlin.test.assertIsNot private val e1 = ArithmeticException() private val e2 = NullPointerException() private val e3 = IllegalArgumentException() -fun runMinusTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Collection<*>) -> MultiSet<*>) { +@Suppress(Suppressions.UNCHECKED_CAST) +private val genericMinus: MultiSetOp<*> = { set1: MultiSet, set2: MultiSet -> set1 - set2 } as MultiSetOp<*> + +@Suppress(Suppressions.UNCHECKED_CAST) +private val genericConstMinus: MultiSetOp<*> = { set1: MultiSet, set2: MultiSet -> + set1 as ConstMultiSet + set2 as ConstMultiSet + set1 `-c` set2 +} as MultiSetOp<*> + +fun runMinusTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Collection<*>) -> MultiSet<*>, const: Boolean) { + val minusFn: MultiSetOp<*> = simpleIf(const, genericConstMinus, genericMinus) val createIntSet = getCreateSet(createSet) val createStringSet = getCreateSet(createSet) val createExceptionSet = getCreateSet(createSet) @@ -22,82 +34,84 @@ fun runMinusTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Co // empty var intSet1 = createIntSet(emptyList()) var intSet2 = createIntSet(emptyList()) - assertEmpty(intSet1 - intSet2) - assertEmpty(intSet2 - intSet1) + intSet1 - intSet2 + assertEmpty(minusFn(intSet1, intSet2)) + assertEmpty(minusFn(intSet2, intSet1)) - assertIsNot>(intSet1 - intSet2) + assertEquals(const, minusFn(intSet1, intSet2) is ConstMultiSet<*>) intSet1 = createIntSet(listOf(1, 2, 3, 3)) - assertEquals(intSet1, intSet1 - intSet2) - assertEmpty(intSet2 - intSet1) + assertEquals(intSet1, minusFn(intSet1, intSet2)) + assertEmpty(minusFn(intSet2, intSet1)) + assertEquals(const, minusFn(intSet2, intSet1) is ConstMultiSet<*>) // equal intSet1 = createIntSet(listOf(1, 2, 3, 4, 5)) - assertEmpty(intSet1 - intSet1) + assertEmpty(minusFn(intSet1, intSet1)) var listSet1 = createCompListSet(listOf(listOf(1, 2, 3), listOf(456, 789))) - assertEmpty(listSet1 - listSet1) + assertEmpty(minusFn(listSet1, listSet1)) var otherSet = createOtherIntSet(listOf(1, 2, 3, 4, 5)) - assertEmpty(intSet1 - otherSet) + assertEmpty(minusFn(intSet1, otherSet)) // all overlapping elements intSet1 = createIntSet(listOf(1, 1, 2, 3, 4, 4, 4)) intSet2 = createIntSet(listOf(1, 2, 2, 3, 4, 4)) var expectedInt = multiSetOf(1, 4) - assertEquals(expectedInt, intSet1 - intSet2) + assertEquals(expectedInt, minusFn(intSet1, intSet2)) expectedInt = multiSetOf(2) - assertEquals(expectedInt, intSet2 - intSet1) + assertEquals(expectedInt, minusFn(intSet2, intSet1)) intSet1 = createIntSet(listOf(1, 2, 2, 2, 3, 3, 5, 6, 6, 7)) intSet2 = createIntSet(listOf(1, 1, 2, 3, 3, 5, 5, 5, 6, 7, 7)) expectedInt = multiSetOf(2, 2, 6) - assertEquals(expectedInt, intSet1 - intSet2) + assertEquals(expectedInt, minusFn(intSet1, intSet2)) expectedInt = multiSetOf(1, 5, 5, 7) - assertEquals(expectedInt, intSet2 - intSet1) + assertEquals(expectedInt, minusFn(intSet2, intSet1)) - // none overlapping elements + // no overlapping elements intSet1 = createIntSet(listOf(1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8)) intSet2 = createIntSet(listOf(-1, -1, -1, -1, -2, -3, -4, -5, -6, -7, -7, -8)) - assertEquals(intSet1, intSet1 - intSet2) - assertEquals(intSet2, intSet2 - intSet1) + assertEquals(intSet1, minusFn(intSet1, intSet2)) + assertEquals(intSet2, minusFn(intSet2, intSet1)) intSet1 = createIntSet(listOf(1, 2, 2, 2, 3, 3, 5, 6, 6, 7)) otherSet = createOtherIntSet(listOf(1, 1, 2, 3, 3, 5, 5, 5, 6, 7, 7)) expectedInt = multiSetOf(2, 2, 6) - assertEquals(expectedInt, intSet1 - otherSet) + assertEquals(expectedInt, minusFn(intSet1, otherSet)) val stringSet1 = createStringSet(listOf("hello", "world", "goodbye", "world", "hello", "goodbye")) val stringSet2 = createStringSet(listOf("greetings", "planet", "farewell", "planet", "greetings", "farewell")) - assertEquals(stringSet1, stringSet1 - stringSet2) - assertEquals(stringSet2, stringSet2 - stringSet1) + assertEquals(stringSet1, minusFn(stringSet1, stringSet2)) + assertEquals(stringSet2, minusFn(stringSet2, stringSet1)) // some overlapping elements intSet1 = createIntSet(listOf(1, 1, 2, 3, 4, 5, 5)) intSet2 = createIntSet(listOf(1, 1, 5, 6, 6, 7)) expectedInt = multiSetOf(2, 3, 4, 5) - assertEquals(expectedInt, intSet1 - intSet2) + assertEquals(expectedInt, minusFn(intSet1, intSet2)) expectedInt = multiSetOf(6, 6, 7) - assertEquals(expectedInt, intSet2 - intSet1) + assertEquals(expectedInt, minusFn(intSet2, intSet1)) intSet1 = createIntSet(listOf(1, 2, 2, 2, 3, 3, 5, 6, 6, 7)) otherSet = createIntSet(listOf(1, 1, 2, 3, 3, 5, 5, 5, 6, 7, 7)) expectedInt = multiSetOf(2, 2, 6) - assertEquals(expectedInt, intSet1 - otherSet) + assertEquals(expectedInt, minusFn(intSet1, otherSet)) listSet1 = createCompListSet(listOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def"))) val listSet2 = createCompListSet(listOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList())) var expectedList: MultiSet>> = multiSetOf(listOf("abc", "def"), listOf("abc", "def")) - assertEquals(expectedList, listSet1 - listSet2) + assertEquals(expectedList, minusFn(listSet1, listSet2)) expectedList = multiSetOf(listOf(1, 2, 3), emptyList()) - assertEquals(expectedList, listSet2 - listSet1) + assertEquals(expectedList, minusFn(listSet2, listSet1)) val errorSet1 = createExceptionSet(listOf(e1, e2, e1, e1, e2)) val errorSet2 = createExceptionSet(listOf(e1, e3, e3, e1, e1)) var expectedError: MultiSet = multiSetOf(e2, e2) - assertEquals(expectedError, errorSet1 - errorSet2) + assertEquals(expectedError, minusFn(errorSet1, errorSet2)) expectedError = multiSetOf(e3, e3) - assertEquals(expectedError, errorSet2 - errorSet1) + assertEquals(expectedError, minusFn(errorSet2, errorSet1)) } fun runMutableElementMinusTests(createIntListSet: (List) -> MultiSet>) { diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonPlusTests.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonPlusTests.kt index 7288fd86..dae566aa 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonPlusTests.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/commonPlusTests.kt @@ -1,18 +1,30 @@ package xyz.lbres.kotlinutils.set.multiset.testutils import xyz.lbres.kotlinutils.assertEmpty +import xyz.lbres.kotlinutils.general.simpleIf +import xyz.lbres.kotlinutils.internal.constants.Suppressions import xyz.lbres.kotlinutils.list.IntList import xyz.lbres.kotlinutils.set.multiset.MultiSet import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSet import xyz.lbres.kotlinutils.set.multiset.multiSetOf import kotlin.test.assertEquals -import kotlin.test.assertIsNot private val e1 = ArithmeticException() private val e2 = NullPointerException() private val e3 = IllegalArgumentException() -fun runPlusTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Collection<*>) -> MultiSet<*>) { +@Suppress(Suppressions.UNCHECKED_CAST) +private val genericPlus: MultiSetOp<*> = { set1: MultiSet, set2: MultiSet -> set1 + set2 } as MultiSetOp<*> + +@Suppress(Suppressions.UNCHECKED_CAST) +private val genericConstPlus: MultiSetOp<*> = { set1: MultiSet, set2: MultiSet -> + set1 as ConstMultiSet + set2 as ConstMultiSet + set1 `+c` set2 +} as MultiSetOp<*> + +fun runPlusTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Collection<*>) -> MultiSet<*>, const: Boolean) { + val plusFn: MultiSetOp<*> = simpleIf(const, genericConstPlus, genericPlus) val createIntSet = getCreateSet(createSet) val createStringSet = getCreateSet(createSet) val createExceptionSet = getCreateSet(createSet) @@ -22,45 +34,45 @@ fun runPlusTests(createSet: (Collection<*>) -> MultiSet<*>, createOtherSet: (Col // empty var intSet1: MultiSet = createIntSet(emptyList()) var intSet2: MultiSet = createIntSet(emptyList()) - assertEmpty(intSet1 + intSet2) - assertEmpty(intSet2 + intSet1) + assertEmpty(plusFn(intSet1, intSet2)) + assertEmpty(plusFn(intSet2, intSet1)) - assertIsNot>(intSet1 + intSet2) + assertEquals(const, plusFn(intSet1, intSet2) is ConstMultiSet<*>) intSet1 = createIntSet(listOf(1, 2, 3, 3)) - assertEquals(intSet1, intSet1 + intSet2) + assertEquals(const, plusFn(intSet1, intSet2) is ConstMultiSet<*>) // non-empty intSet1 = createIntSet(listOf(1)) intSet2 = createIntSet(listOf(1)) var intExpected = multiSetOf(1, 1) - assertEquals(intExpected, intSet1 + intSet2) + assertEquals(intExpected, plusFn(intSet1, intSet2)) intSet1 = createIntSet(listOf(1, 2, 2, 3, 3, 3)) intSet2 = createIntSet(listOf(1, 2, 0)) intExpected = multiSetOf(0, 1, 1, 2, 2, 2, 3, 3, 3) - assertEquals(intExpected, intSet1 + intSet2) + assertEquals(intExpected, plusFn(intSet1, intSet2)) val otherSet = createOtherIntSet(listOf(1, 2, 2, 3, 3, 3)) - assertEquals(intExpected, intSet2 + otherSet) + assertEquals(intExpected, plusFn(intSet2, otherSet)) val stringSet1 = createStringSet(listOf("", "hello", "world", "goodbye")) val stringSet2 = createStringSet(listOf("hi", "no", "bye")) val stringExpected = multiSetOf("", "bye", "goodbye", "hello", "hi", "no", "world") - assertEquals(stringExpected, stringSet1 + stringSet2) - assertEquals(stringExpected, stringSet2 + stringSet1) + assertEquals(stringExpected, plusFn(stringSet1, stringSet2)) + assertEquals(stringExpected, plusFn(stringSet2, stringSet1)) val listSet1 = createCompListSet(listOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def"))) val listSet2 = createCompListSet(listOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList())) - val expectedList: MultiSet>> = multiSetOf(emptyList(), listOf(1, 2, 3), listOf(1, 2, 3), listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) - assertEquals(expectedList, listSet1 + listSet2) - assertEquals(expectedList, listSet2 + listSet1) + val listExpected: MultiSet>> = multiSetOf(emptyList(), listOf(1, 2, 3), listOf(1, 2, 3), listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) + assertEquals(listExpected, plusFn(listSet1, listSet2)) + assertEquals(listExpected, plusFn(listSet2, listSet1)) val errorSet1 = createExceptionSet(listOf(e1, e2, e1, e2)) val errorSet2 = createExceptionSet(listOf(e1, e3, e3, e2, e1, e1)) - val expectedError: MultiSet = multiSetOf(e1, e1, e1, e1, e1, e2, e2, e2, e3, e3) - assertEquals(expectedError, errorSet1 + errorSet2) - assertEquals(expectedError, errorSet2 + errorSet1) + val errorExpected: MultiSet = multiSetOf(e1, e1, e1, e1, e1, e2, e2, e2, e3, e3) + assertEquals(errorExpected, plusFn(errorSet1, errorSet2)) + assertEquals(errorExpected, plusFn(errorSet2, errorSet1)) } fun runMutableElementPlusTests(createIntListSet: (List) -> MultiSet>) { diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/testutils.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/testutils.kt index f3d8cea7..48ffafdd 100644 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/testutils.kt +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/testutils/testutils.kt @@ -5,6 +5,8 @@ import xyz.lbres.kotlinutils.set.multiset.MultiSet import xyz.lbres.kotlinutils.set.multiset.MutableMultiSet import kotlin.test.assertEquals +typealias MultiSetOp = (MultiSet, MultiSet) -> MultiSet + /** * Run single test to mutate set * diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/OperatorsTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/OperatorsTest.kt new file mode 100644 index 00000000..5e0b292f --- /dev/null +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/OperatorsTest.kt @@ -0,0 +1,152 @@ +package xyz.lbres.kotlinutils.set.multiset.utils + +import xyz.lbres.kotlinutils.set.multiset.MultiSet +import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSet +import xyz.lbres.kotlinutils.set.multiset.emptyMultiSet +import xyz.lbres.kotlinutils.set.multiset.multiSetOf +import xyz.lbres.kotlinutils.set.multiset.mutableMultiSetOf +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertIsNot + +private typealias CompList = List> + +// light testing, heavier tests are in the MultiSet classes +class OperatorsTest { + @Test + fun testPerformPlus() { + // empty + runSingleTest(CountsMap(emptyMap()), emptyMultiSet(), emptyMultiSet(), ::performPlus) + runSingleTest(CountsMap(emptyMap()), multiSetOf(1, 2, 3), multiSetOf(1, 2, 3), ::performPlus) + runSingleTest(CountsMap(mapOf(3 to 2)), emptyMultiSet(), multiSetOf(3, 3), ::performPlus) + + // non-empty + runSingleTest(CountsMap.from(listOf(1)), multiSetOf(1), multiSetOf(1, 1), ::performPlus) + + var intCounts = CountsMap.from(listOf(1, 2, 2, 3, 3, 3)) + var intSet = multiSetOf(1, 2, 0) + var intExpected = multiSetOf(1, 2, 2, 3, 3, 3, 1, 2, 0) + runSingleTest(intCounts, intSet, intExpected, ::performPlus) + + intCounts = CountsMap.from(listOf(0, 4, 5, -1)) + intSet = multiSetOf(1, 2, 3) + intExpected = multiSetOf(-1, 0, 1, 2, 3, 4, 5) + runSingleTest(intCounts, intSet, intExpected, ::performPlus) + + val listCounts: CountsMap = CountsMap.from(listOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def"))) + val listSet: MultiSet = multiSetOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) + val listExpected: MultiSet = + multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def"), listOf(1, 2, 3), listOf(1, 2, 3), emptyList()) + runSingleTest(listCounts, listSet, listExpected, ::performPlus) + + // mutable + intCounts = CountsMap.from(listOf(1, 2)) + val mutableSet = mutableMultiSetOf(2, 3, 4) + intExpected = multiSetOf(1, 2, 2, 3, 4) + runSingleTest(intCounts, mutableSet, intExpected, ::performPlus) + + mutableSet.remove(3) + intExpected = multiSetOf(1, 2, 2, 4) + runSingleTest(intCounts, mutableSet, intExpected, ::performPlus) + } + + @Test + fun testPerformMinus() { + // empty + runSingleTest(CountsMap(emptyMap()), emptyMultiSet(), emptyMultiSet(), ::performMinus) + runSingleTest(CountsMap(emptyMap()), multiSetOf(1, 2, 3), emptyMultiSet(), ::performMinus) + runSingleTest(CountsMap(mapOf(3 to 2)), emptyMultiSet(), multiSetOf(3, 3), ::performMinus) + + // non-empty + var intCounts = CountsMap.from(listOf(1, 2, 2, 2, 3, 3, 5, 6, 6, 7)) + var intSet = multiSetOf(1, 1, 2, 3, 3, 5, 5, 5, 6, 7, 7) + var intExpected = multiSetOf(2, 2, 6) + runSingleTest(intCounts, intSet, intExpected, ::performMinus) + + intCounts = CountsMap.from(listOf(1, 1, 2, 3, 3, 5, 5, 5, 6, 7, 7)) + intSet = multiSetOf(1, 2, 2, 2, 3, 3, 5, 6, 6, 7) + intExpected = multiSetOf(1, 5, 5, 7) + runSingleTest(intCounts, intSet, intExpected, ::performMinus) + + intCounts = CountsMap.from(listOf(1, 2, 3, 4)) + intSet = multiSetOf(5, 6, 7, 8) + intExpected = multiSetOf(1, 2, 3, 4) + runSingleTest(intCounts, intSet, intExpected, ::performMinus) + + val listCounts: CountsMap = CountsMap.from(multiSetOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList())) + val listSet: MultiSet = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) + val listExpected: MultiSet = multiSetOf(listOf(1, 2, 3), emptyList()) + runSingleTest(listCounts, listSet, listExpected, ::performMinus) + + // mutable + intCounts = CountsMap.from(listOf(1, 2, 3, 4)) + val mutableSet = mutableMultiSetOf(2, 3, 4) + intExpected = multiSetOf(1) + runSingleTest(intCounts, mutableSet, intExpected, ::performMinus) + + mutableSet.remove(3) + intExpected = multiSetOf(1, 3) + runSingleTest(intCounts, mutableSet, intExpected, ::performMinus) + } + + @Test + fun testPerformIntersect() { + // empty + runSingleTest(CountsMap(emptyMap()), emptyMultiSet(), emptyMultiSet(), ::performIntersect) + runSingleTest(CountsMap(emptyMap()), multiSetOf(1, 2, 3), emptyMultiSet(), ::performIntersect) + runSingleTest(CountsMap(mapOf(3 to 2)), emptyMultiSet(), emptyMultiSet(), ::performIntersect) + + // non-empty + var intCounts = CountsMap.from(multiSetOf(1, 2, 3)) + var intSet = multiSetOf(4, 5, 6, 7, 8) + var intExpected: MultiSet = emptyMultiSet() + runSingleTest(intCounts, intSet, intExpected, ::performIntersect) + + intCounts = CountsMap.from(multiSetOf(4, 5, 6, 7, 8)) + intSet = multiSetOf(1, 2, 3) + runSingleTest(intCounts, intSet, intExpected, ::performIntersect) + + intCounts = CountsMap.from(listOf(1, 2, 3)) + intSet = multiSetOf(1, 2, 3) + intExpected = multiSetOf(1, 2, 3) + runSingleTest(intCounts, intSet, intExpected, ::performIntersect) + + intCounts = CountsMap.from(multiSetOf(1, 2, 2, 4, 5, 6, 7, -1, 10)) + intSet = multiSetOf(-1, 14, 3, 9, 9, 6) + intExpected = multiSetOf(-1, 6) + runSingleTest(intCounts, intSet, intExpected, ::performIntersect) + + val listCounts: CountsMap = CountsMap.from(multiSetOf(listOf(1, 2, 3), listOf(1, 2, 3), emptyList())) + val listSet: MultiSet = multiSetOf(listOf(1, 2, 3), listOf("abc", "def"), listOf("abc", "def")) + val listExpected: MultiSet = multiSetOf(listOf(1, 2, 3)) + runSingleTest(listCounts, listSet, listExpected, ::performIntersect) + + // mutable + intCounts = CountsMap.from(listOf(1, 2, 3)) + val mutableSet = mutableMultiSetOf(2, 3, 4, 2) + intExpected = multiSetOf(2, 3) + runSingleTest(intCounts, mutableSet, intExpected, ::performIntersect) + + mutableSet.remove(3) + intExpected = multiSetOf(2) + runSingleTest(intCounts, mutableSet, intExpected, ::performIntersect) + } + + // run single test for a given map, set, and operator + private fun runSingleTest( + counts: CountsMap, + set: MultiSet, + expected: MultiSet, + operator: (CountsMap, MultiSet, Boolean) -> MultiSet + ) { + // non-const + var result = operator(counts, set, false) + assertEquals(expected, result) + assertIsNot>(result) + // const + result = operator(counts, set, true) + assertEquals(expected, result) + assertIs>(result) + } +} diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/UtilsTest.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/UtilsTest.kt deleted file mode 100644 index 93d6b570..00000000 --- a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/UtilsTest.kt +++ /dev/null @@ -1,121 +0,0 @@ -package xyz.lbres.kotlinutils.set.multiset.utils - -import xyz.lbres.kotlinutils.general.simpleIf -import xyz.lbres.kotlinutils.int.ext.isZero -import xyz.lbres.kotlinutils.set.multiset.MultiSet -import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSet -import xyz.lbres.kotlinutils.set.multiset.emptyMultiSet -import xyz.lbres.kotlinutils.set.multiset.multiSetOf -import kotlin.math.min -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertIs -import kotlin.test.assertIsNot - -class UtilsTest { - @Test - fun testCombineCounts() { - // empty - var intCounts: CountsMap = CountsMap(emptyMap()) - var intSet: MultiSet = emptyMultiSet() - var intResult = combineCounts(intCounts, intSet, Int::minus, false, false) - assertEquals(emptyMultiSet(), intResult) - assertIsNot>(intResult) - - intCounts = CountsMap.from(listOf(1, 4, 1, 1)) - intSet = emptyMultiSet() - var intExpected = multiSetOf(1, 1, 1, 4) - intResult = combineCounts(intCounts, intSet, Int::minus, false, false) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - // constant map function - intCounts = CountsMap.from(listOf(1, 4, 5, 1, 2)) - intSet = multiSetOf(1, 2, -4, 5, 5) - var map: (Int, Int) -> Int = { _, _ -> 0 } - intExpected = emptyMultiSet() - intResult = combineCounts(intCounts, intSet, map, false) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - intCounts = CountsMap.from(listOf(1, 4, 5, 1, 2)) - intSet = multiSetOf(1, 2, -4, 5, 5) - map = { _, _ -> -1 } - intExpected = emptyMultiSet() - intResult = combineCounts(intCounts, intSet, map, true, false) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - intCounts = CountsMap.from(listOf(1, 4, 5, 1, 2)) - intSet = multiSetOf(1, 2, -4, 5, 5) - map = { _, _ -> 2 } - intExpected = multiSetOf(-4, -4, 1, 1, 2, 2, 4, 4, 5, 5) - intResult = combineCounts(intCounts, intSet, map, true, false) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - // dynamic map function - intCounts = CountsMap.from(listOf(1, 4, 5, 1, 2)) - intSet = multiSetOf(1, 2, -4, 5, 5) - intExpected = multiSetOf(1, 1, 1, 2, 2, 4, 5, 5, 5) - intResult = combineCounts(intCounts, intSet, Int::plus, false, false) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - intCounts = CountsMap.from(listOf(1, 4, 4, 4, -4, 5)) - intSet = multiSetOf(1, 1, 5, 4, 4) - intExpected = multiSetOf(1, 1, 4, 4, 4, 4, 4, 4, 5) - intResult = combineCounts(intCounts, intSet, Int::times, false) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - // use all values - intCounts = CountsMap.from(listOf(1, 4, 5, 1, 2)) - intSet = multiSetOf(1, 2, -4, 5, 5) - intExpected = multiSetOf(-4, 1, 1, 1, 2, 2, 4, 5, 5, 5) - intResult = combineCounts(intCounts, intSet, Int::plus, true) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - intCounts = CountsMap(mapOf(7 to 6, 4 to 1, 1 to 1, 8 to 3)) - intSet = multiSetOf(6, 6, 7, 1, 8, 8, 7, 12, 1) - map = { i1, i2 -> simpleIf(i1.isZero(), i2, min(i1, i2)) } - intExpected = multiSetOf(7, 7, 6, 6, 8, 8, 1, 12) - intResult = combineCounts(intCounts, intSet, map, true, false) - assertEquals(intExpected, intResult) - assertIsNot>(intResult) - - // const - intCounts = CountsMap.from(listOf(1, 4, 4, 4, -4, 5)) - intSet = multiSetOf(1, 1, 5, 4, 4) - intExpected = multiSetOf(1, 1, 4, 4, 4, 4, 4, 4, 5) - intResult = combineCounts(intCounts, intSet, Int::times, false, true) - assertEquals(intExpected, intResult) - assertIs>(intResult) - - intCounts = CountsMap.from(listOf(1, 4, 5, 1, 2)) - intSet = multiSetOf(1, 2, -4, 5, 5) - intExpected = multiSetOf(1, 1, 1, 2, 2, 4, 5, 5, 5) - intResult = combineCounts(intCounts, intSet, Int::plus, false, true) - assertEquals(intExpected, intResult) - assertIs>(intResult) - - // other types - val stringCounts = CountsMap.from(listOf("hello", "world", "hello world", "world", "world", "hello")) - val stringSet = multiSetOf("world", "world", "planet", "hello world") - val stringExpected = multiSetOf("world", "world", "hello world") - val stringResult = combineCounts(stringCounts, stringSet, ::min, true, true) - assertEquals(stringExpected, stringResult) - assertIs>(stringResult) - - val map1 = CountsMap(mapOf(7 to 2, 8 to 3)) - val map2 = CountsMap(mapOf(3 to 2, 1 to 7)) - val map3 = CountsMap(mapOf(4 to 15, 1 to 3, 4 to 5, -6 to 1)) - val countsMapCounts = CountsMap.from(listOf(map2, map3, map2, map1, map2, map1, map1, map2, map1, map3)) - val countsMapSet = multiSetOf(map3, map2, map3, map3, map1, map1, map1, map2) - val countsMapExpected = multiSetOf(map1, map2, map2) - val countsMapResult = combineCounts(countsMapCounts, countsMapSet, Int::minus, false, false) - assertEquals(countsMapExpected, countsMapResult) - assertIsNot>(countsMapResult) - } -}