All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.github.quillraven.fleks.collection.entityBag.kt Maven / Gradle / Ivy

The newest version!
@file:Suppress("OVERRIDE_BY_INLINE")

package com.github.quillraven.fleks.collection

import com.github.quillraven.fleks.Entity
import kotlin.math.max
import kotlin.math.min
import kotlin.random.Random

interface EntityBag {
    /**
     * Returns the size of the [EntityBag].
     */
    val size: Int

    /**
     * Returns true if and only if the given [entity] is part of the bag.
     */
    operator fun contains(entity: Entity): Boolean

    /**
     * Returns true if and only if all given [entities] are part of the bag.
     */
    fun containsAll(entities: Collection): Boolean

    /**
     * Returns true if and only if all given [entities] are part of the bag.
     */
    fun containsAll(entities: EntityBag): Boolean

    /**
     * Returns true if and only if the bag is empty and contains no [entities][Entity].
     */
    fun isEmpty(): Boolean

    /**
     * Returns true if and only if the bag is not empty and contains at least one [entity][Entity].
     */
    fun isNotEmpty(): Boolean

    /**
     * Returns true if all [entities][Entity] of the bag match the given [predicate].
     */
    fun all(predicate: (Entity) -> Boolean): Boolean

    /**
     * Returns true if at least one [entity][Entity] of the bag matches the given [predicate].
     */
    fun any(predicate: (Entity) -> Boolean): Boolean

    /**
     * Returns true if no [entity][Entity] of the bag matches the given [predicate].
     */
    fun none(predicate: (Entity) -> Boolean): Boolean

    /**
     * Returns a [Map] containing key-value pairs provided by the [transform] function applied to
     * each [entity][Entity] of the bag.
     */
    fun  associate(transform: (Entity) -> Pair): Map

    /**
     * Returns a [Map] containing the [entities][Entity] of the bag indexed by the key
     * returned from [keySelector] function applied to each [entity][Entity] of the bag.
     */
    fun  associateBy(keySelector: (Entity) -> K): Map

    /**
     * Returns a [Map] containing the values provided by [valueTransform] and indexed by the
     * [keySelector] function applied to each [entity][Entity] of the bag.
     */
    fun  associateBy(
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): Map

    /**
     * Populates and returns the [destination] mutable map containing key-value pairs
     * provided by the [transform] function applied to each [entity][Entity] of the bag.
     */
    fun > associateTo(
        destination: M,
        transform: (Entity) -> Pair
    ): M

    /**
     * Populates and returns the [destination] mutable map containing the [entities][Entity]
     * of the bag indexed by the key returned from [keySelector] function applied to
     * each [entity][Entity] of the bag.
     */
    fun > associateByTo(
        destination: M,
        keySelector: (Entity) -> K
    ): M

    /**
     * Populates and returns the [destination] mutable map containing the values
     * provided by [valueTransform] and indexed by the [keySelector] function applied
     * to each [entity][Entity] of the bag.
     */
    fun > associateByTo(
        destination: M,
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): M

    /**
     * Returns the number of [entities][Entity] in this bag.
     */
    fun count(): Int

    /**
     * Returns the number of [entities][Entity] matching the given [predicate].
     */
    fun count(predicate: (Entity) -> Boolean): Int

    /**
     * Returns a [List] containing only [entities][Entity] matching the given [predicate].
     */
    fun filter(predicate: (Entity) -> Boolean): EntityBag

    /**
     * Returns a [List] containing all [entities][Entity] not matching the given [predicate].
     */
    fun filterNot(predicate: (Entity) -> Boolean): EntityBag

    /**
     * Returns a [List] containing only [entities][Entity] matching the given [predicate].
     */
    fun filterIndexed(predicate: (index: Int, Entity) -> Boolean): EntityBag

    /**
     * Appends all [entities][Entity] matching the given [predicate] to the given [destination].
     */
    fun filterTo(
        destination: MutableEntityBag,
        predicate: (Entity) -> Boolean
    ): MutableEntityBag

    /**
     * Appends all [entities][Entity] not matching the given [predicate] to the given [destination].
     */
    fun filterNotTo(
        destination: MutableEntityBag,
        predicate: (Entity) -> Boolean
    ): MutableEntityBag

    /**
     * Appends all [entities][Entity] matching the given [predicate] to the given [destination].
     */
    fun filterIndexedTo(
        destination: MutableEntityBag,
        predicate: (index: Int, Entity) -> Boolean
    ): MutableEntityBag

    /**
     * Returns the first [entity][Entity] matching the given [predicate], or null if no such
     * [entity][Entity] was found.
     */
    fun find(predicate: (Entity) -> Boolean): Entity?

    /**
     * Returns the first [entity][Entity].
     *
     * @throws [NoSuchElementException] if the bag is empty.
     */
    fun first(): Entity

    /**
     * Returns the first [entity][Entity] matching the given [predicate].
     *
     * @throws [NoSuchElementException] if the bag is empty or there is no such [entity][Entity].
     */
    fun first(predicate: (Entity) -> Boolean): Entity

    /**
     * Returns the first [entity][Entity], or null if the bag is empty.
     */
    fun firstOrNull(): Entity?

    /**
     * Returns the first [entity][Entity] matching the given [predicate], or null
     * if the bag is empty or no such [entity][Entity] was found.
     */
    fun firstOrNull(predicate: (Entity) -> Boolean): Entity?

    /**
     * Returns a single [List] of all elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    fun  flatMap(transform: (Entity) -> Iterable): List

    /**
     * Returns a single [List] of all elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    fun  flatMapSequence(transform: (Entity) -> Sequence): List

    /**
     * Returns a new bag of all elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    fun flatMapBag(transform: (Entity) -> EntityBag): EntityBag

    /**
     * Returns a single [List] of all non-null elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    fun  flatMapNotNull(transform: (Entity) -> Iterable?): List

    /**
     * Returns a single [List] of all non-null elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    fun  flatMapSequenceNotNull(transform: (Entity) -> Sequence?): List

    /**
     * Returns a new bag of all non-null elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    fun flatMapBagNotNull(transform: (Entity) -> EntityBag?): EntityBag

    /**
     * Accumulates value starting with [initial] value and applying [operation] from left to right to
     * current accumulator value and each [entity][Entity].
     */
    fun  fold(
        initial: R,
        operation: (acc: R, Entity) -> R
    ): R

    /**
     * Accumulates value starting with [initial] value and applying [operation] from left to right to
     * current accumulator value and each [entity][Entity] with its index in the original bag.
     */
    fun  foldIndexed(
        initial: R,
        operation: (index: Int, acc: R, Entity) -> R
    ): R

    /**
     * Performs the given [action] on each [entity][Entity].
     */
    fun forEach(action: (Entity) -> Unit)

    /**
     * Performs the given [action] on each [entity][Entity], providing sequential
     * index with the [entity][Entity].
     */
    fun forEachIndexed(action: (index: Int, Entity) -> Unit)

    /**
     * Returns the [entity][Entity] of the given [index].
     *
     * @throws [IndexOutOfBoundsException] if [index] is less than zero or greater equal [size].
     */
    operator fun get(index: Int): Entity

    /**
     * Groups [entities][Entity] by the key returned by the given [keySelector] function
     * applied to each [entity][Entity] and returns a map where each group key is associated with an [EntityBag]
     * of corresponding [entities][Entity].
     */
    fun  groupBy(keySelector: (Entity) -> K): Map

    /**
     * Groups values returned by the [valueTransform] function applied to each [entity][Entity] of the bag
     * by the key returned by the given [keySelector] function applied to the [entity][Entity] and returns
     * a map where each group key is associated with a list of corresponding values.
     */
    fun  groupBy(
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): Map>

    /**
     * Groups [entities][Entity] by the key returned by the given [keySelector] function
     * applied to each [entity][Entity] and puts to the [destination] map each group key associated with
     * an [EntityBag] of corresponding elements.
     */
    fun > groupByTo(
        destination: M,
        keySelector: (Entity) -> K
    ): M

    /**
     * Groups values returned by the [valueTransform] function applied to each [entity][Entity] of the bag
     * by the key returned by the given [keySelector] function applied to the [entity][Entity] and puts
     * to the [destination] map each group key associated with a list of corresponding values.
     */
    fun >> groupByTo(
        destination: M,
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): M

    /**
     * Returns a [List] containing the results of applying the given [transform] function
     * to each [entity][Entity] of the bag.
     */
    fun  map(transform: (Entity) -> R): List

    /**
     * Returns a [List] containing the results of applying the given [transform] function
     * to each [entity][Entity] and its index of the bag.
     */
    fun  mapIndexed(transform: (index: Int, Entity) -> R): List

    /**
     * Applies the given [transform] function to each [entity][Entity] of the bag and appends
     * the results to the given [destination].
     */
    fun > mapTo(
        destination: C,
        transform: (Entity) -> R
    ): C

    /**
     * Applies the given [transform] function to each [entity][Entity] and its index of the bag and appends
     * the results to the given [destination].
     */
    fun > mapIndexedTo(
        destination: C,
        transform: (index: Int, Entity) -> R
    ): C

    /**
     * Returns a list containing only the non-null results of applying the given [transform] function
     * to each [entity][Entity] of the bag.
     */
    fun  mapNotNull(transform: (Entity) -> R?): List

    /**
     * Applies the given [transform] function to each [entity][Entity] of the bag and appends only
     * the non-null results to the given [destination].
     */
    fun > mapNotNullTo(
        destination: C,
        transform: (Entity) -> R?
    ): C

    /**
     * Splits the original bag into a pair of bags,
     * where the first bag contains elements for which predicate yielded true,
     * while the second bag contains elements for which predicate yielded false.
     */
    fun partition(predicate: (Entity) -> Boolean): Pair

    /**
     * Splits the original bag into two bags,
     * where [first] contains elements for which predicate yielded true,
     * while [second] contains elements for which predicate yielded false.
     */
    fun partitionTo(
        first: MutableEntityBag,
        second: MutableEntityBag,
        predicate: (Entity) -> Boolean
    )

    /**
     * Returns a random [entity][Entity] of the bag.
     *
     * @throws [NoSuchElementException] if the bag is empty.
     */
    fun random(): Entity

    /**
     * Returns a random [entity][Entity] of the bag, or null if the bag is empty.
     */
    fun randomOrNull(): Entity?

    /**
     * Returns the single [entity][Entity] of the bag, or throws an exception
     * if the bag is empty or has more than one [entity][Entity].
     */
    fun single(): Entity

    /**
     * Returns the single [entity][Entity] of the bag matching the given [predicate],
     * or throws an exception if the bag is empty or has more than one [entity][Entity].
     */
    fun single(predicate: (Entity) -> Boolean): Entity

    /**
     * Returns single [entity][Entity] of the bag, or null
     * if the bag is empty or has more than one [entity][Entity].
     */
    fun singleOrNull(): Entity?

    /**
     * Returns the single [entity][Entity] of the bag matching the given [predicate],
     * or null if the bag is empty or has more than one [entity][Entity].
     */
    fun singleOrNull(predicate: (Entity) -> Boolean): Entity?

    /**
     * Returns a [List] containing the first [n] [entities][Entity].
     */
    fun take(n: Int): EntityBag
}

/**
 * A bag implementation for [entities][Entity] (=integer) values in Kotlin to avoid autoboxing.
 * It contains necessary functions for Fleks and some additional Kotlin standard library utilities.
 */
class MutableEntityBag(
    size: Int = 64
) : EntityBag {
    @PublishedApi
    internal var values: Array = Array(size) { Entity.NONE }

    /**
     * Returns the size of the [MutableEntityBag].
     */
    override var size: Int = 0
        private set

    /**
     * Returns the size of the underlying [Array] that reflects the maximum amount
     * of [entities][Entity] that can be stored before resizing the [Array].
     */
    val capacity: Int
        get() = values.size

    /**
     * Adds the [entity] to the bag. If the [capacity] is not sufficient then a resize is happening.
     */
    operator fun plusAssign(entity: Entity) {
        if (size == values.size) {
            values = values.copyInto(Array(max(1, size * 2)) { Entity.NONE })
        }
        values[size++] = entity
    }

    /**
     * Removes the [entity] of the bag.
     */
    operator fun minusAssign(entity: Entity) {
        for (i in 0 until size) {
            if (values[i] == entity) {
                values[i] = values[--size]
                values[size] = Entity.NONE
                return
            }
        }
    }

    /**
     * Resets [size] to zero and clears any [entity][Entity] of the bag.
     */
    fun clear() {
        values.fill(Entity.NONE)
        size = 0
    }

    /**
     * Resizes the bag to fit in the given [capacity] of [entities][Entity] if necessary.
     */
    fun ensureCapacity(capacity: Int) {
        if (capacity > values.size) {
            values = values.copyInto(Array(capacity + 1) { Entity.NONE })
        }
    }

    /**
     * Resets [size] to zero, clears any [entity][Entity] of the bag, and if necessary,
     * resizes the bag to be able to fit the given [capacity] of [entities][Entity].
     */
    fun clearEnsuringCapacity(capacity: Int) {
        if (capacity > values.size) {
            values = Array(capacity + 1) { Entity.NONE }
        } else {
            values.fill(Entity.NONE)
        }
        size = 0
    }

    /**
     * Sorts the bag according to the given [comparator].
     */
    fun sort(comparator: EntityComparator) {
        values.quickSort(0, size, comparator)
    }

    /**
     * Returns true if and only if the given [entity] is part of the bag.
     */
    override fun contains(entity: Entity): Boolean {
        for (i in 0 until size) {
            if (values[i] == entity) {
                return true
            }
        }
        return false
    }

    /**
     * Returns true if and only if all given [entities] are part of the bag.
     */
    override fun containsAll(entities: Collection): Boolean = entities.all { it in this }

    /**
     * Returns true if and only if all given [entities] are part of the bag.
     */
    override fun containsAll(entities: EntityBag): Boolean = entities.all { it in this }

    /**
     * Returns true if and only if the bag is empty and contains no [entities][Entity].
     */
    override fun isEmpty(): Boolean = size == 0

    /**
     * Returns true if and only if the bag is not empty and contains at least one [entity][Entity].
     */
    override fun isNotEmpty(): Boolean = size > 0

    /**
     * Returns true if all [entities][Entity] of the bag match the given [predicate].
     */
    override inline fun all(predicate: (Entity) -> Boolean): Boolean {
        for (i in 0 until size) {
            if (!predicate(values[i])) {
                return false
            }
        }
        return true
    }

    /**
     * Returns true if at least one [entity][Entity] of the bag matches the given [predicate].
     */
    override inline fun any(predicate: (Entity) -> Boolean): Boolean {
        for (i in 0 until size) {
            if (predicate(values[i])) {
                return true
            }
        }
        return false
    }

    /**
     * Returns true if no [entity][Entity] of the bag matches the given [predicate].
     */
    override inline fun none(predicate: (Entity) -> Boolean): Boolean {
        for (i in 0 until size) {
            if (predicate(values[i])) {
                return false
            }
        }
        return true
    }

    /**
     * Returns a [Map] containing key-value pairs provided by the [transform] function applied to
     * each [entity][Entity] of the bag.
     */
    override inline fun  associate(transform: (Entity) -> Pair): Map {
        val result = mutableMapOf()
        for (i in 0 until size) {
            result += transform(values[i])
        }
        return result
    }

    /**
     * Returns a [Map] containing the [entities][Entity] of the bag indexed by the key
     * returned from [keySelector] function applied to each [entity][Entity] of the bag.
     */
    override inline fun  associateBy(keySelector: (Entity) -> K): Map {
        val result = mutableMapOf()
        for (i in 0 until size) {
            val entity = values[i]
            result[keySelector(entity)] = entity
        }
        return result
    }

    /**
     * Returns a [Map] containing the values provided by [valueTransform] and indexed by the
     * [keySelector] function applied to each [entity][Entity] of the bag.
     */
    override inline fun  associateBy(
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): Map {
        val result = mutableMapOf()
        for (i in 0 until size) {
            val entity = values[i]
            result[keySelector(entity)] = valueTransform(entity)
        }
        return result
    }

    /**
     * Populates and returns the [destination] mutable map containing key-value pairs
     * provided by the [transform] function applied to each [entity][Entity] of the bag.
     */
    override inline fun > associateTo(
        destination: M,
        transform: (Entity) -> Pair
    ): M {
        for (i in 0 until size) {
            destination += transform(values[i])
        }
        return destination
    }

    /**
     * Populates and returns the [destination] mutable map containing the [entities][Entity]
     * of the bag indexed by the key returned from [keySelector] function applied to
     * each [entity][Entity] of the bag.
     */
    override inline fun > associateByTo(
        destination: M,
        keySelector: (Entity) -> K
    ): M {
        for (i in 0 until size) {
            val entity = values[i]
            destination[keySelector(entity)] = entity
        }
        return destination
    }

    /**
     * Populates and returns the [destination] mutable map containing the values
     * provided by [valueTransform] and indexed by the [keySelector] function applied
     * to each [entity][Entity] of the bag.
     */
    override inline fun > associateByTo(
        destination: M,
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): M {
        for (i in 0 until size) {
            val entity = values[i]
            destination[keySelector(entity)] = valueTransform(entity)
        }
        return destination
    }

    /**
     * Returns the number of [entities][Entity] in this bag.
     */
    override fun count(): Int = size

    /**
     * Returns the number of [entities][Entity] matching the given [predicate].
     */
    override inline fun count(predicate: (Entity) -> Boolean): Int {
        var result = 0
        for (i in 0 until size) {
            if (predicate(values[i])) {
                ++result
            }
        }
        return result
    }

    /**
     * Returns a [List] containing only [entities][Entity] matching the given [predicate].
     */
    override inline fun filter(predicate: (Entity) -> Boolean): EntityBag {
        val result = MutableEntityBag((size * 0.25f).toInt())
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(entity)) {
                result += entity
            }
        }
        return result
    }

    /**
     * Returns a [List] containing all [entities][Entity] not matching the given [predicate].
     */
    override inline fun filterNot(predicate: (Entity) -> Boolean): EntityBag {
        val result = MutableEntityBag((size * 0.25f).toInt())
        for (i in 0 until size) {
            val entity = values[i]
            if (!predicate(entity)) {
                result += entity
            }
        }
        return result
    }

    /**
     * Returns a [List] containing only [entities][Entity] matching the given [predicate].
     */
    override inline fun filterIndexed(predicate: (index: Int, entity: Entity) -> Boolean): EntityBag {
        val result = MutableEntityBag((size * 0.25f).toInt())
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(i, entity)) {
                result += entity
            }
        }
        return result
    }

    /**
     * Appends all [entities][Entity] matching the given [predicate] to the given [destination].
     */
    override inline fun filterTo(
        destination: MutableEntityBag,
        predicate: (Entity) -> Boolean
    ): MutableEntityBag {
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(entity)) {
                destination += entity
            }
        }
        return destination
    }

    /**
     * Appends all [entities][Entity] not matching the given [predicate] to the given [destination].
     */
    override inline fun filterNotTo(
        destination: MutableEntityBag,
        predicate: (Entity) -> Boolean
    ): MutableEntityBag {
        for (i in 0 until size) {
            val entity = values[i]
            if (!predicate(entity)) {
                destination += entity
            }
        }
        return destination
    }

    /**
     * Appends all [entities][Entity] matching the given [predicate] to the given [destination].
     */
    override inline fun filterIndexedTo(
        destination: MutableEntityBag,
        predicate: (index: Int, Entity) -> Boolean
    ): MutableEntityBag {
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(i, entity)) {
                destination += entity
            }
        }
        return destination
    }

    /**
     * Returns the first [entity][Entity] matching the given [predicate], or null if no such
     * [entity][Entity] was found.
     */
    override inline fun find(predicate: (Entity) -> Boolean): Entity? {
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(entity)) {
                return entity
            }
        }
        return null
    }

    /**
     * Returns the first [entity][Entity].
     *
     * @throws [NoSuchElementException] if the bag is empty.
     */
    override fun first(): Entity {
        if (isEmpty()) {
            throw NoSuchElementException("EntityBag is empty!")
        }
        return values[0]
    }

    /**
     * Returns the first [entity][Entity] matching the given [predicate].
     *
     * @throws [NoSuchElementException] if the bag is empty or there is no such [entity][Entity].
     */
    override inline fun first(predicate: (Entity) -> Boolean): Entity {
        return find(predicate) ?: throw NoSuchElementException("There is no entity matching the given predicate!")
    }

    /**
     * Returns the first [entity][Entity], or null if the bag is empty.
     */
    override fun firstOrNull(): Entity? {
        if (isEmpty()) {
            return null
        }
        return values[0]
    }

    /**
     * Returns the first [entity][Entity] matching the given [predicate], or null
     * if the bag is empty or no such [entity][Entity] was found.
     */
    override inline fun firstOrNull(predicate: (Entity) -> Boolean): Entity? = find(predicate)

    /**
     * Returns a single [List] of all elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    override inline fun  flatMap(transform: (Entity) -> Iterable): List {
        val result = mutableListOf()
        for (i in 0 until size) {
            result.addAll(transform(values[i]))
        }
        return result
    }

    /**
     * Returns a single [List] of all elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    override inline fun  flatMapSequence(transform: (Entity) -> Sequence): List {
        val result = mutableListOf()
        for (i in 0 until size) {
            result.addAll(transform(values[i]))
        }
        return result
    }

    /**
     * Returns a new bag of all elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    override inline fun flatMapBag(transform: (Entity) -> EntityBag): EntityBag {
        val result = MutableEntityBag(size)
        for (i in 0 until size) {
            transform(values[i]).forEach { result += it }
        }
        return result
    }

    /**
     * Returns a single [List] of all non-null elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    override inline fun  flatMapNotNull(transform: (Entity) -> Iterable?): List {
        val result = mutableListOf()
        for (i in 0 until size) {
            transform(values[i])?.forEach {
                if (it == null) return@forEach
                result += it
            }
        }
        return result
    }

    /**
     * Returns a single [List] of all non-null elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    override inline fun  flatMapSequenceNotNull(transform: (Entity) -> Sequence?): List {
        val result = mutableListOf()
        for (i in 0 until size) {
            transform(values[i])?.forEach {
                if (it == null) return@forEach
                result += it
            }
        }
        return result
    }

    /**
     * Returns a new bag of all non-null elements yielded from the results of [transform] function
     * being invoked on each [entity][Entity] of the bag.
     */
    override inline fun flatMapBagNotNull(transform: (Entity) -> EntityBag?): EntityBag {
        val result = MutableEntityBag(size)
        for (i in 0 until size) {
            val transformVal = transform(values[i]) ?: continue
            transformVal.forEach { result += it }
        }
        return result
    }

    /**
     * Accumulates value starting with [initial] value and applying [operation] from left to right to
     * current accumulator value and each [entity][Entity].
     */
    override inline fun  fold(
        initial: R,
        operation: (acc: R, entity: Entity) -> R
    ): R {
        var accumulator = initial
        for (i in 0 until size) {
            accumulator = operation(accumulator, values[i])
        }
        return accumulator
    }

    /**
     * Accumulates value starting with [initial] value and applying [operation] from left to right to
     * current accumulator value and each [entity][Entity] with its index in the original bag.
     */
    override inline fun  foldIndexed(
        initial: R,
        operation: (index: Int, acc: R, entity: Entity) -> R
    ): R {
        var accumulator = initial
        for (i in 0 until size) {
            accumulator = operation(i, accumulator, values[i])
        }
        return accumulator
    }

    /**
     * Performs the given [action] on each [entity][Entity].
     */
    override inline fun forEach(action: (Entity) -> Unit) {
        for (i in 0 until size) {
            action(values[i])
        }
    }

    /**
     * Performs the given [action] on each [entity][Entity], providing sequential
     * index with the [entity][Entity].
     */
    override inline fun forEachIndexed(action: (index: Int, entity: Entity) -> Unit) {
        for (i in 0 until size) {
            action(i, values[i])
        }
    }

    /**
     * Groups [entities][Entity] by the key returned by the given [keySelector] function
     * applied to each [entity][Entity] and returns a map where each group key is associated with an [EntityBag]
     * of corresponding [entities][Entity].
     */
    override inline fun  groupBy(keySelector: (Entity) -> K): Map {
        val result = mutableMapOf()
        for (i in 0 until size) {
            val entity = values[i]
            val key = keySelector(entity)
            result.getOrPut(key) { MutableEntityBag() } += entity
        }
        return result
    }

    /**
     * Returns the [entity][Entity] of the given [index].
     *
     * @throws [IndexOutOfBoundsException] if [index] is less than zero or greater equal [size].
     */
    override operator fun get(index: Int): Entity {
        if (index < 0 || index >= size) throw IndexOutOfBoundsException("$index is not valid for bag of size $size")
        return values[index]
    }

    /**
     * Groups values returned by the [valueTransform] function applied to each [entity][Entity] of the bag
     * by the key returned by the given [keySelector] function applied to the [entity][Entity] and returns
     * a map where each group key is associated with a list of corresponding values.
     */
    override inline fun  groupBy(
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): Map> {
        val result = mutableMapOf>()
        for (i in 0 until size) {
            val entity = values[i]
            val key = keySelector(entity)
            result.getOrPut(key) { mutableListOf() } += valueTransform(entity)
        }
        return result
    }

    /**
     * Groups [entities][Entity] by the key returned by the given [keySelector] function
     * applied to each [entity][Entity] and puts to the [destination] map each group key associated with
     * an [EntityBag] of corresponding elements.
     */
    override inline fun > groupByTo(
        destination: M,
        keySelector: (Entity) -> K
    ): M {
        for (i in 0 until size) {
            val entity = values[i]
            val key = keySelector(entity)
            destination.getOrPut(key) { MutableEntityBag() } += entity
        }
        return destination
    }

    /**
     * Groups values returned by the [valueTransform] function applied to each [entity][Entity] of the bag
     * by the key returned by the given [keySelector] function applied to the [entity][Entity] and puts
     * to the [destination] map each group key associated with a list of corresponding values.
     */
    override inline fun >> groupByTo(
        destination: M,
        keySelector: (Entity) -> K,
        valueTransform: (Entity) -> V
    ): M {
        for (i in 0 until size) {
            val entity = values[i]
            val key = keySelector(entity)
            destination.getOrPut(key) { mutableListOf() } += valueTransform(entity)
        }
        return destination
    }

    /**
     * Returns a [List] containing the results of applying the given [transform] function
     * to each [entity][Entity] of the bag.
     */
    override inline fun  map(transform: (Entity) -> R): List {
        val result = mutableListOf()
        for (i in 0 until size) {
            result += transform(values[i])
        }
        return result
    }

    /**
     * Returns a [List] containing the results of applying the given [transform] function
     * to each [entity][Entity] and its index of the bag.
     */
    override inline fun  mapIndexed(transform: (index: Int, entity: Entity) -> R): List {
        val result = mutableListOf()
        for (i in 0 until size) {
            result += transform(i, values[i])
        }
        return result
    }

    /**
     * Applies the given [transform] function to each [entity][Entity] of the bag and appends
     * the results to the given [destination].
     */
    override inline fun > mapTo(
        destination: C,
        transform: (Entity) -> R
    ): C {
        for (i in 0 until size) {
            destination += transform(values[i])
        }
        return destination
    }

    /**
     * Applies the given [transform] function to each [entity][Entity] and its index of the bag and appends
     * the results to the given [destination].
     */
    override inline fun > mapIndexedTo(
        destination: C,
        transform: (index: Int, Entity) -> R
    ): C {
        for (i in 0 until size) {
            destination += transform(i, values[i])
        }
        return destination
    }

    /**
     * Returns a list containing only the non-null results of applying the given [transform] function
     * to each [entity][Entity] of the bag.
     */
    override inline fun  mapNotNull(transform: (Entity) -> R?): List {
        val result = mutableListOf()
        for (i in 0 until size) {
            val transformVal = transform(values[i]) ?: continue
            result += transformVal
        }
        return result
    }

    /**
     * Applies the given [transform] function to each [entity][Entity] of the bag and appends only
     * the non-null results to the given [destination].
     */
    override inline fun > mapNotNullTo(
        destination: C,
        transform: (Entity) -> R?
    ): C {
        for (i in 0 until size) {
            val transformVal = transform(values[i]) ?: continue
            destination += transformVal
        }
        return destination
    }

    /**
     * Splits the original bag into a pair of bags,
     * where the first bag contains elements for which predicate yielded true,
     * while the second bag contains elements for which predicate yielded false.
     */
    override inline fun partition(predicate: (Entity) -> Boolean): Pair {
        val first = MutableEntityBag()
        val second = MutableEntityBag()
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(entity)) {
                first += entity
            } else {
                second += entity
            }
        }
        return Pair(first, second)
    }

    /**
     * Splits the original bag into two bags,
     * where [first] contains elements for which predicate yielded true,
     * while [second] contains elements for which predicate yielded false.
     */
    override inline fun partitionTo(
        first: MutableEntityBag,
        second: MutableEntityBag,
        predicate: (Entity) -> Boolean
    ) {
        first.clear()
        second.clear()
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(entity)) {
                first += entity
            } else {
                second += entity
            }
        }
    }

    /**
     * Returns a random [entity][Entity] of the bag.
     *
     * @throws [NoSuchElementException] if the bag is empty.
     */
    override fun random(): Entity {
        if (isEmpty()) {
            throw NoSuchElementException("EntityBag is empty!")
        }
        return values[Random.Default.nextInt(size)]
    }

    /**
     * Returns a random [entity][Entity] of the bag, or null if the bag is empty.
     */
    override fun randomOrNull(): Entity? {
        if (isEmpty()) {
            return null
        }
        return values[Random.Default.nextInt(size)]
    }

    /**
     * Returns the single [entity][Entity] of the bag, or throws an exception
     * if the bag is empty or has more than one [entity][Entity].
     */
    override fun single(): Entity {
        return when (size) {
            0 -> throw NoSuchElementException("Bag is empty.")
            1 -> values[0]
            else -> throw IllegalArgumentException("Bag has more than one element.")
        }
    }

    /**
     * Returns the single [entity][Entity] of the bag matching the given [predicate],
     * or throws an exception if the bag is empty or has more than one [entity][Entity].
     */
    override fun single(predicate: (Entity) -> Boolean): Entity {
        var single: Entity? = null
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(entity)) {
                if (single != null) {
                    throw IllegalArgumentException("Bag contains more than one matching element.")
                }
                single = entity
            }
        }
        if (single == null) {
            throw NoSuchElementException("Bag contains no element matching the predicate.")
        }
        return single
    }

    /**
     * Returns single [entity][Entity] of the bag, or null
     * if the bag is empty or has more than one [entity][Entity].
     */
    override fun singleOrNull(): Entity? {
        return when (size) {
            1 -> values[0]
            else -> null
        }
    }

    /**
     * Returns the single [entity][Entity] of the bag matching the given [predicate],
     * or null if the bag is empty or has more than one [entity][Entity].
     */
    override fun singleOrNull(predicate: (Entity) -> Boolean): Entity? {
        var single: Entity? = null
        for (i in 0 until size) {
            val entity = values[i]
            if (predicate(entity)) {
                if (single != null) {
                    return null
                }
                single = entity
            }
        }
        return single
    }

    /**
     * Returns a [List] containing the first [n] [entities][Entity].
     */
    override fun take(n: Int): EntityBag {
        val result = MutableEntityBag(max(min(n, size), 0))
        for (i in 0 until min(n, size)) {
            result += values[i]
        }
        return result
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other == null || this::class != other::class) return false

        other as MutableEntityBag

        if (size != other.size) return false
        return containsAll(other)
    }

    override fun hashCode(): Int {
        var result = values.contentHashCode()
        result = 31 * result + size
        return result
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy