diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt index 3cd2212f6..45e392693 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/SeriesAlgebra.kt @@ -6,6 +6,7 @@ import space.kscience.kmath.operations.RingOps import space.kscience.kmath.stat.StatisticalAlgebra import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferView +import space.kscience.kmath.structures.getOrNull import kotlin.math.max import kotlin.math.min @@ -33,8 +34,6 @@ public interface Series : Buffer { public val position: Int } -public val Series.absoluteIndices: IntRange get() = position until position + size - /** * A [BufferView] with index offset (both positive and negative) and possible size change */ @@ -60,54 +59,68 @@ public class SeriesAlgebra, out BA : BufferAlgebra, L>( private val labelResolver: (Int) -> L, ) : RingOps>, StatisticalAlgebra { - public val Buffer.indices: IntRange + /** + * A range of valid offset indices. In general, does not start with zero. + */ + public val Buffer.offsetIndices: IntRange get() = if (this is Series) { - absoluteIndices + position until position + size } else { 0 until size } /** - * Get the value by absolute index in the series algebra or return null if index is out of range + * Get the value by absolute offset in the series algebra or return null if index is out of range */ - public fun Buffer.getAbsoluteOrNull(index: Int): T? = when { - index !in indices -> null - this is Series -> origin[index - position] - else -> get(index) + public fun Buffer.getByOffsetOrNull(index: Int): T? = when { + index !in offsetIndices -> null + this is Series -> origin.getOrNull(index - position) + else -> getOrNull(index) } /** * Get the value by absolute index in the series algebra or throw [IndexOutOfBoundsException] if index is out of range */ - public fun Buffer.getAbsolute(index: Int): T = - getAbsoluteOrNull(index) ?: throw IndexOutOfBoundsException("Index $index is not in $indices") + public fun Buffer.getByOffset(index: Int): T = + getByOffsetOrNull(index) ?: throw IndexOutOfBoundsException("Index $index is not in $offsetIndices") /** - * Create an offset series with index starting point at [index] + * Zero-copy move [Buffer] or [Series] to given [position] ignoring series offset if it is present. */ - public fun Buffer.moveTo(index: Int): Series = if (this is Series) { - SeriesImpl(origin, index, size) + public fun Buffer.moveTo(position: Int): Series = if (this is Series) { + SeriesImpl(origin, position, size) } else { - SeriesImpl(this, index, size) + SeriesImpl(this, position, size) } - public val Buffer.offset: Int get() = if (this is Series) position else 0 + /** + * Zero-copy move [Buffer] or [Series] by given [offset]. If it is [Series], sum intrinsic series position and the [offset]. + */ + public fun Buffer.moveBy(offset: Int): Series = if (this is Series) { + SeriesImpl(origin, position + offset, size) + } else { + SeriesImpl(this, offset, size) + } /** - * Build a new series + * An offset of the buffer start relative to [SeriesAlgebra] zero offset */ - public fun series(size: Int, fromIndex: Int = 0, block: A.(label: L) -> T): Series { + public val Buffer.startOffset: Int get() = if (this is Series) position else 0 + + /** + * Build a new series positioned at [startOffset]. + */ + public fun series(size: Int, startOffset: Int = 0, block: A.(label: L) -> T): Series { return elementAlgebra.bufferFactory(size) { - val index = it + fromIndex + val index = it + startOffset elementAlgebra.block(labelResolver(index)) - }.moveTo(fromIndex) + }.moveTo(startOffset) } /** * Get a label buffer for given buffer. */ - public val Buffer.labels: List get() = indices.map(labelResolver) - + public val Buffer.labels: List get() = offsetIndices.map(labelResolver) /** * Try to resolve element by label and return null if element with a given label is not found @@ -115,7 +128,7 @@ public class SeriesAlgebra, out BA : BufferAlgebra, L>( public operator fun Buffer.get(label: L): T? { val index = labels.indexOf(label) if (index == -1) return null - return getAbsolute(index + offset) + return getByOffset(index + startOffset) } /** @@ -123,9 +136,9 @@ public class SeriesAlgebra, out BA : BufferAlgebra, L>( */ public inline fun Buffer.map(crossinline transform: A.(T) -> T): Series { val buf = elementAlgebra.bufferFactory(size) { - elementAlgebra.transform(getAbsolute(it)) + elementAlgebra.transform(getByOffset(it)) } - return buf.moveTo(indices.first) + return buf.moveTo(offsetIndices.first) } /** @@ -134,22 +147,22 @@ public class SeriesAlgebra, out BA : BufferAlgebra, L>( public inline fun Buffer.mapWithLabel(crossinline transform: A.(arg: T, label: L) -> T): Series { val labels = labels val buf = elementAlgebra.bufferFactory(size) { - elementAlgebra.transform(getAbsolute(it), labels[it]) + elementAlgebra.transform(getByOffset(it), labels[it]) } - return buf.moveTo(indices.first) + return buf.moveTo(offsetIndices.first) } public inline fun Buffer.fold(initial: R, operation: A.(acc: R, T) -> R): R { var accumulator = initial - for (index in this.indices) accumulator = elementAlgebra.operation(accumulator, getAbsolute(index)) + for (index in this.offsetIndices) accumulator = elementAlgebra.operation(accumulator, getByOffset(index)) return accumulator } public inline fun Buffer.foldWithLabel(initial: R, operation: A.(acc: R, arg: T, label: L) -> R): R { val labels = labels var accumulator = initial - for (index in this.indices) accumulator = - elementAlgebra.operation(accumulator, getAbsolute(index), labels[index]) + for (index in this.offsetIndices) accumulator = + elementAlgebra.operation(accumulator, getByOffset(index), labels[index]) return accumulator } @@ -160,11 +173,11 @@ public class SeriesAlgebra, out BA : BufferAlgebra, L>( other: Buffer, crossinline operation: A.(left: T, right: T) -> T, ): Series { - val newRange = indices.intersect(other.indices) + val newRange = offsetIndices.intersect(other.offsetIndices) return elementAlgebra.bufferFactory(newRange.size) { elementAlgebra.operation( - getAbsolute(it), - other.getAbsolute(it) + getByOffset(it), + other.getByOffset(it) ) }.moveTo(newRange.first) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/seriesExtensions.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/seriesExtensions.kt index 882fa2c46..fa5e0addd 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/seriesExtensions.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/seriesExtensions.kt @@ -12,32 +12,32 @@ import space.kscience.kmath.structures.Buffer public fun SeriesAlgebra.sin( arg: Buffer, ): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = - bufferAlgebra.sin(arg).moveTo(arg.offset) + bufferAlgebra.sin(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.cos( arg: Buffer, ): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = - bufferAlgebra.cos(arg).moveTo(arg.offset) + bufferAlgebra.cos(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.tan( arg: Buffer, ): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = - bufferAlgebra.tan(arg).moveTo(arg.offset) + bufferAlgebra.tan(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.asin( arg: Buffer, ): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = - bufferAlgebra.asin(arg).moveTo(arg.offset) + bufferAlgebra.asin(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.acos( arg: Buffer, ): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = - bufferAlgebra.acos(arg).moveTo(arg.offset) + bufferAlgebra.acos(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.atan( arg: Buffer, ): Series where BA : BufferAlgebra, BA : TrigonometricOperations> = - bufferAlgebra.atan(arg).moveTo(arg.offset) + bufferAlgebra.atan(arg).moveTo(arg.startOffset) //exponential @@ -45,42 +45,42 @@ public fun SeriesAlgebra.atan( public fun SeriesAlgebra.exp( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.exp(arg).moveTo(arg.offset) + bufferAlgebra.exp(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.ln( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.ln(arg).moveTo(arg.offset) + bufferAlgebra.ln(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.sinh( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.sinh(arg).moveTo(arg.offset) + bufferAlgebra.sinh(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.cosh( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.cosh(arg).moveTo(arg.offset) + bufferAlgebra.cosh(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.tanh( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.tanh(arg).moveTo(arg.offset) + bufferAlgebra.tanh(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.asinh( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.asinh(arg).moveTo(arg.offset) + bufferAlgebra.asinh(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.acosh( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.acosh(arg).moveTo(arg.offset) + bufferAlgebra.acosh(arg).moveTo(arg.startOffset) public fun SeriesAlgebra.atanh( arg: Buffer, ): Series where BA : BufferAlgebra, BA : ExponentialOperations> = - bufferAlgebra.atanh(arg).moveTo(arg.offset) + bufferAlgebra.atanh(arg).moveTo(arg.startOffset) //power @@ -89,9 +89,9 @@ public fun SeriesAlgebra.power( arg: Buffer, pow: Number, ): Series where BA : BufferAlgebra, BA : PowerOperations> = - bufferAlgebra.power(arg, pow).moveTo(arg.offset) + bufferAlgebra.power(arg, pow).moveTo(arg.startOffset) public fun SeriesAlgebra.sqrt( arg: Buffer, ): Series where BA : BufferAlgebra, BA : PowerOperations> = - bufferAlgebra.sqrt(arg).moveTo(arg.offset) \ No newline at end of file + bufferAlgebra.sqrt(arg).moveTo(arg.startOffset) \ No newline at end of file