diff --git a/CHANGELOG.md b/CHANGELOG.md index 58df6f1bb..9498cc4ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Float32 geometries. - New Attributes-kt module that could be used as stand-alone. It declares. type-safe attributes containers. - Explicit `mutableStructureND` builders for mutable structures. +- `Buffer.asList()` zero-copy transformation. ### Changed - Default naming for algebra and buffers now uses IntXX/FloatXX notation instead of Java types. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt index 26e238145..df9d3c0eb 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt @@ -101,32 +101,13 @@ public interface Buffer : WithSize, WithType { } } -/** - * Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([Int32Buffer], - * [Float64Buffer], etc.), [ListBuffer] is returned otherwise. - * - * The [size] is specified, and each element is calculated by calling the specified [initializer] function. - */ -@Suppress("UNCHECKED_CAST") -public inline fun Buffer(size: Int, initializer: (Int) -> T): Buffer { - val type = safeTypeOf() - return when (type.kType) { - typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer - typeOf() -> MutableBuffer.short(size) { initializer(it) as Short } as Buffer - typeOf() -> MutableBuffer.int(size) { initializer(it) as Int } as Buffer - typeOf() -> MutableBuffer.long(size) { initializer(it) as Long } as Buffer - typeOf() -> MutableBuffer.float(size) { initializer(it) as Float } as Buffer - else -> List(size, initializer).asBuffer(type) - } -} - /** * Creates a [Buffer] of given [type]. If the type is primitive, specialized buffers are used ([Int32Buffer], * [Float64Buffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ -@Suppress("UNCHECKED_CAST") +@Suppress("UNCHECKED_CAST", "DuplicatedCode") public fun Buffer( type: SafeType, size: Int, @@ -140,6 +121,26 @@ public fun Buffer( else -> List(size, initializer).asBuffer(type) } +/** + * Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([Int32Buffer], + * [Float64Buffer], etc.), [ListBuffer] is returned otherwise. + * + * The [size] is specified, and each element is calculated by calling the specified [initializer] function. + */ +@Suppress("UNCHECKED_CAST", "DuplicatedCode") +public inline fun Buffer(size: Int, initializer: (Int) -> T): Buffer { + //code duplication here because we want to inline initializers + val type = safeTypeOf() + return when (type.kType) { + typeOf() -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer + typeOf() -> MutableBuffer.short(size) { initializer(it) as Short } as Buffer + typeOf() -> MutableBuffer.int(size) { initializer(it) as Int } as Buffer + typeOf() -> MutableBuffer.long(size) { initializer(it) as Long } as Buffer + typeOf() -> MutableBuffer.float(size) { initializer(it) as Float } as Buffer + else -> List(size, initializer).asBuffer(type) + } +} + /** * Returns an [IntRange] of the valid indices for this [Buffer]. */ diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferList.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferList.kt new file mode 100644 index 000000000..ff668589e --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferList.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.structures + +import kotlin.jvm.JvmInline + +@JvmInline +private value class BufferList(val buffer: Buffer) : List { + override val size: Int get() = buffer.size + + override fun get(index: Int): T = buffer[index] + + override fun isEmpty(): Boolean = buffer.size == 0 + + override fun iterator(): Iterator = buffer.iterator() + + override fun listIterator(index: Int): ListIterator = object : ListIterator { + var currentIndex = index + + override fun hasNext(): Boolean = currentIndex < buffer.size - 1 + + override fun hasPrevious(): Boolean = currentIndex > 0 + + override fun next(): T { + if (!hasNext()) throw NoSuchElementException() + return get(currentIndex++) + } + + override fun nextIndex(): Int = currentIndex + + override fun previous(): T { + if (!hasPrevious()) throw NoSuchElementException() + return get(--currentIndex) + } + + override fun previousIndex(): Int = currentIndex - 1 + + } + + override fun listIterator(): ListIterator = listIterator(0) + + override fun subList(fromIndex: Int, toIndex: Int): List = + buffer.slice(fromIndex..toIndex).asList() + + override fun lastIndexOf(element: T): Int { + for (i in buffer.indices.reversed()) { + if (buffer[i] == element) return i + } + return -1 + } + + override fun indexOf(element: T): Int { + for (i in buffer.indices) { + if (buffer[i] == element) return i + } + return -1 + } + + override fun containsAll(elements: Collection): Boolean { + val remainingElements = HashSet(elements) + for (e in buffer) { + if (e in remainingElements) { + remainingElements.remove(e) + } + if (remainingElements.isEmpty()) { + return true + } + } + return false + } + + override fun contains(element: T): Boolean = indexOf(element) >= 0 +} + +/** + * Returns a zero-copy list that reflects the content of the buffer. + */ +public fun Buffer.asList(): List = BufferList(this) \ No newline at end of file diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt index 882df9536..3a44a9e64 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt @@ -24,8 +24,6 @@ import space.kscience.attributes.safeTypeOf import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix -import space.kscience.kmath.nd.Structure2D -import space.kscience.kmath.nd.StructureAttribute import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.operations.Float32Field import space.kscience.kmath.operations.Float64Field @@ -80,6 +78,7 @@ public class EjmlFloatMatrix(override val origin: M) : EjmlMatr } + /** * [EjmlLinearSpace] implementation based on [CommonOps_DDRM], [DecompositionFactory_DDRM] operations and * [DMatrixRMaj] matrices. @@ -218,18 +217,6 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace): EjmlDoubleVector = v * this - override fun > computeAttribute(structure: Structure2D, attribute: A): V? { - val origin = structure.toEjml().origin - return when(attribute){ - Inverted -> { - val res = origin.copy() - CommonOps_DDRM.invert(res) - res.wrapMatrix() - } - else-> - } - } - @UnstableKMathAPI override fun computeFeature(structure: Matrix, type: KClass): F? { structure.getFeature(type)?.let { return it } @@ -330,7 +317,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace