From 1838fdb8bcb17781ac66789e2ba8b035189232b5 Mon Sep 17 00:00:00 2001 From: lbressler13 Date: Tue, 19 Mar 2024 23:10:50 -0400 Subject: [PATCH] Move combineCounts to util file (#64) --- .../set/multiset/const/ConstMultiSet.kt | 2 +- .../set/multiset/const/ConstMultiSetImpl.kt | 2 +- .../multiset/const/ConstMultiSetManager.kt | 41 ++---- .../multiset/const/ConstMutableMultiSet.kt | 2 +- .../const/ConstMutableMultiSetImpl.kt | 2 +- .../set/multiset/impl/AbstractMultiSetImpl.kt | 47 +------ .../set/multiset/utils/CountsMap.kt | 38 ++++++ .../kotlinutils/set/multiset/utils/utils.kt | 42 +++++++ .../set/multiset/utils/UtilsTest.kt | 2 + .../set/multiset/utils/combineCountsTests.kt | 117 ++++++++++++++++++ 10 files changed, 217 insertions(+), 78 deletions(-) create mode 100644 kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/CountsMap.kt create mode 100644 kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/combineCountsTests.kt 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 6f73d241..e1a3c948 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 @@ -7,7 +7,7 @@ import xyz.lbres.kotlinutils.set.multiset.MultiSet * [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 constructor(private val initialElements: Collection, private var initialCounts: Map? = null) : MultiSet { +sealed class ConstMultiSet : MultiSet { @Suppress(Suppressions.FUNCTION_NAME) infix fun `+c`(other: ConstMultiSet): ConstMultiSet = plusC(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 dbd12c66..0591f71c 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 @@ -6,7 +6,7 @@ import xyz.lbres.kotlinutils.set.multiset.utils.countsToString import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap // final implementation of ConstMultiSet -internal class ConstMultiSetImpl(private val elements: Collection, initialCounts: Map? = null) : ConstMultiSet(elements, initialCounts) { +internal class ConstMultiSetImpl(private val elements: Collection, initialCounts: Map? = null) : ConstMultiSet() { private val manager: ConstMultiSetManager override val size: Int = elements.size override val distinctValues: Set diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt index 47b07e5c..f8da7d7c 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt @@ -1,8 +1,8 @@ package xyz.lbres.kotlinutils.set.multiset.const -import xyz.lbres.kotlinutils.general.simpleIf import xyz.lbres.kotlinutils.set.multiset.MultiSet -import xyz.lbres.kotlinutils.set.multiset.impl.MultiSetImpl +import xyz.lbres.kotlinutils.set.multiset.utils.CountsMap +import xyz.lbres.kotlinutils.set.multiset.utils.combineCounts import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap import kotlin.math.min @@ -25,47 +25,22 @@ internal class ConstMultiSetManager(private val counts: Map) { } fun plus(other: MultiSet): MultiSet { - return combineCounts(other, Int::plus, true, const = false) + return combineCounts(CountsMap(counts), other, Int::plus, true, const = false) } fun minus(other: MultiSet): MultiSet { - return combineCounts(other, Int::minus, false, const = false) + return combineCounts(CountsMap(counts), other, Int::minus, false, const = false) } fun intersect(other: MultiSet): MultiSet { - return combineCounts(other, ::min, false, const = false) + return combineCounts(CountsMap(counts), other, ::min, false, const = false) } fun plusC(other: ConstMultiSet): ConstMultiSet { - return combineCounts(other, Int::plus, true, const = true) as ConstMultiSet + return combineCounts(CountsMap(counts), other, Int::plus, true, const = true) as ConstMultiSet } fun minusC(other: ConstMultiSet): ConstMultiSet { - return combineCounts(other, Int::minus, false, const = true) as ConstMultiSet + return combineCounts(CountsMap(counts), other, Int::minus, false, const = true) as ConstMultiSet } fun intersectC(other: ConstMultiSet): ConstMultiSet { - return combineCounts(other, ::min, false, const = true) as ConstMultiSet - } - - /** - * Combine counts with another MultiSet, using the given operation - * - * @param other [MultiSet]: MultiSet to combine with - * @param operation (Int, Int) -> Int: combination function - * @param useAllValues [Boolean]: if all values from both sets should be used to generate the new set. If `false`, only the values from this set 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 const multi set - */ - private fun combineCounts(other: MultiSet, operation: (count: Int, otherCount: Int) -> Int, useAllValues: Boolean, const: Boolean): MultiSet { - val values: Set = simpleIf(useAllValues, { counts.keys + other.distinctValues }, { counts.keys }) - val newCounts: MutableMap = mutableMapOf() - val newElements: MutableList = mutableListOf() - - values.forEach { value -> - val count = operation(getCountOf(value), other.getCountOf(value)) - if (count > 0) { - newCounts[value] = count - repeat(count) { newElements.add(value) } - } - } - - return simpleIf(const, { ConstMultiSetImpl(newElements, newCounts) }, { MultiSetImpl(newElements) }) + return combineCounts(CountsMap(counts), other, ::min, false, const = true) as ConstMultiSet } /** diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt index e58813d7..c40528b2 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt @@ -6,7 +6,7 @@ import xyz.lbres.kotlinutils.set.multiset.MutableMultiSet * [MutableMultiSet] 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 ConstMutableMultiSet constructor(initialElements: Collection) : MutableMultiSet, ConstMultiSet(initialElements) { +sealed class ConstMutableMultiSet : MutableMultiSet, ConstMultiSet() { /** * Get an iterator for the elements in this set. * 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 bd01623c..fb8cf134 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 @@ -7,7 +7,7 @@ import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap import kotlin.math.min // final implementation of ConstMutableMultiSet -internal class ConstMutableMultiSetImpl(initialElements: Collection) : ConstMutableMultiSet(initialElements) { +internal class ConstMutableMultiSetImpl(initialElements: Collection) : ConstMutableMultiSet() { /** * If all properties are up-to-date with the most recent changes to the counts map */ 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 cd59f416..d3f46ca0 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 @@ -1,17 +1,18 @@ package xyz.lbres.kotlinutils.set.multiset.impl -import xyz.lbres.kotlinutils.general.simpleIf 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.utils.CountsMap +import xyz.lbres.kotlinutils.set.multiset.utils.combineCounts import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap import kotlin.math.min /** * Partial [MultiSet] implementation which supports modifications to values of elements (i.e. adding elements to a mutable list). */ -internal abstract class AbstractMultiSetImpl(private val initialElements: Collection) : MultiSet { +internal abstract class AbstractMultiSetImpl(initialElements: Collection) : MultiSet { /** * Number of elements in set. */ @@ -102,7 +103,7 @@ internal abstract class AbstractMultiSetImpl(private val initialElements: Col * @return [MultiSet]: MultiSet containing only values that are in both MultiSets */ override infix fun intersect(other: MultiSet): MultiSet { - return genericBinaryOperation(other, { count, otherCount -> min(count, otherCount) }, useAllValues = false) + return combineCounts(CountsMap.from(elements), other, ::min, useAllValues = false, const = false) } /** @@ -113,7 +114,7 @@ internal abstract class AbstractMultiSetImpl(private val initialElements: Col * @return [MultiSet]: MultiSet containing the items in this MultiSet but not the other */ override operator fun minus(other: MultiSet): MultiSet { - return genericBinaryOperation(other, Int::minus, useAllValues = false) + return combineCounts(CountsMap.from(elements), other, Int::minus, useAllValues = false, const = false) } /** @@ -124,43 +125,7 @@ internal abstract class AbstractMultiSetImpl(private val initialElements: Col * @return [MultiSet]: MultiSet containing all values from both MultiSets */ override operator fun plus(other: MultiSet): MultiSet { - return genericBinaryOperation(other, Int::plus) - } - - /** - * Execute a binary operation with another MultiSet, with special handling for the case of another AbstractMultiSetImpl. - * - * @param other [MultiSet]: other set to use in operation - * @param operation (Int, Int) -> Int: function which uses the count of an element in this set and the count in another set, and returns the new count for the element - * @param useAllValues [Boolean]: if all values from both sets should be used to generate the new set. If `false`, only the values from this set will be used. - * Defaults to `true` - * @return [MultiSet]: new set where each element has the number of occurrences specified by the operation - */ - private fun genericBinaryOperation(other: MultiSet, operation: (count: Int, otherCount: Int) -> Int, useAllValues: Boolean = true): MultiSet { - val counts = getCounts() - val distinct = counts.keys - - var getOtherCount: (E) -> Int = { other.getCountOf(it) } - var getOtherDistinct: () -> Set = { other.distinctValues } - // increase efficiency of operation with other AbstractMultiSetImpl - if (other is AbstractMultiSetImpl) { - val otherCounts = other.getCounts() - getOtherCount = { otherCounts.getOrDefault(it, 0) } - getOtherDistinct = { otherCounts.keys } - } - - val newElements: MutableList = mutableListOf() - - val values = simpleIf(useAllValues, { distinct + getOtherDistinct() }, { distinct }) - values.forEach { - val count = counts.getOrDefault(it, 0) - val otherCount = getOtherCount(it) - val newCount = operation(count, otherCount) - - repeat(newCount) { _ -> newElements.add(it) } - } - - return MultiSetImpl(newElements) + return combineCounts(CountsMap.from(elements), other, Int::plus, useAllValues = true, const = false) } /** 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 new file mode 100644 index 00000000..4bdf3043 --- /dev/null +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/CountsMap.kt @@ -0,0 +1,38 @@ +package xyz.lbres.kotlinutils.set.multiset.utils + +/** + * Mapping of occurrences to the number of times that they occur + */ +@JvmInline +internal value class CountsMap(private val counts: Map) { + /** + * Distinct values in map + */ + val distinct: Set + get() = counts.keys + + /** + * Get number of occurrences of a single element + * + * @param element E: element to get count of + * @return [Int]: number of occurrences + */ + fun getCountOf(element: E): Int = counts.getOrDefault(element, 0) + + companion object { + /** + * Create a CountsMap from a collection of elements + * + * @param elements [Collection]: elements to include in the map + * @return [CountsMap]: map containing exactly the elements in the given collection + */ + fun from(elements: Collection): CountsMap { + val counts: MutableMap = mutableMapOf() + elements.forEach { + counts[it] = counts.getOrDefault(it, 0) + 1 + } + + return CountsMap(counts) + } + } +} 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 index a3d655ee..2c523062 100644 --- 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 @@ -1,5 +1,11 @@ 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 + /** * Create a mapping of each element in a collection to the number of occurrences of the element. * @@ -51,3 +57,39 @@ internal fun countsToList(counts: Map): List { return list } + +/** + * 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 const multi set. + */ +internal fun combineCounts(counts: CountsMap, multiset: MultiSet, operation: (Int, Int) -> Int, useAllValues: Boolean, const: Boolean): MultiSet { + var getOtherCount: (E) -> Int = { multiset.getCountOf(it) } + var otherDistinct: () -> Set = { multiset.distinctValues } + + // increase efficiency for AbstractMultiSetImpl + if (multiset is AbstractMultiSetImpl) { + val otherCounts = CountsMap.from(multiset) + getOtherCount = { otherCounts.getCountOf(it) } + 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, newCounts) }, { MultiSetImpl(newElements) }) +} 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 index 038e127d..398cd5a4 100644 --- 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 @@ -91,4 +91,6 @@ class UtilsTest { val stringOptions = setOf(listOf("hello", "world", "world"), listOf("world", "world", "hello")) assertContains(stringOptions, countsToList(stringCounts)) } + + @Test fun testCombineCounts() = runCombineCountsTests() } diff --git a/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/combineCountsTests.kt b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/combineCountsTests.kt new file mode 100644 index 00000000..67c4f402 --- /dev/null +++ b/kotlin-utils/src/test/kotlin/xyz/lbres/kotlinutils/set/multiset/utils/combineCountsTests.kt @@ -0,0 +1,117 @@ +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.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertIsNot + +fun runCombineCountsTests() { + // 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, 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, 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, false) + 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) +}