serializable and hashable BitArray. Comparable BitList. Small improvements

This commit is contained in:
Sergey Chernov 2025-09-26 19:45:28 +04:00
parent 2696f1546d
commit 3481a718b1
11 changed files with 80 additions and 14 deletions

View File

@ -21,10 +21,10 @@ import lyng.time
// //
// After all optimizations it takes ~120ms. // After all optimizations it takes ~120ms.
// //
//for( r in 1..100 ) { for( r in 1..100 ) {
// val start = Instant.now() val start = Instant.now()
val found = naiveCountHappyNumbers() val found = naiveCountHappyNumbers()
// println("Found happy numbers: %d time %s"(found, Instant.now() - start)) println("Found happy numbers: %d time %s"(found, Instant.now() - start))
assert( found == 55252 ) assert( found == 55252 )
// delay(0.01) delay(0.1)
//} }

View File

@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych" group = "net.sergeych"
version = "0.9.1-SNAPSHOT" version = "0.9.2-SNAPSHOT"
buildscript { buildscript {
repositories { repositories {
@ -66,6 +66,7 @@ kotlin {
// iosArm64() // iosArm64()
// iosSimulatorArm64() // iosSimulatorArm64()
linuxX64() linuxX64()
linuxArm64()
js { js {
browser() browser()
nodejs() nodejs()
@ -79,9 +80,9 @@ kotlin {
sourceSets { sourceSets {
all { all {
languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
languageSettings.optIn("kotlin.ExperimentalUnsignedTypes") languageSettings.optIn("kotlin.ExperimentalUnsignedTypes")
languageSettings.optIn("kotlin.coroutines.DelicateCoroutinesApi") languageSettings.optIn("kotlin.coroutines.DelicateCoroutinesApi")
languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
languageSettings.optIn("kotlinx.coroutines.flow.DelicateCoroutinesApi") languageSettings.optIn("kotlinx.coroutines.flow.DelicateCoroutinesApi")
} }

View File

@ -164,6 +164,7 @@ class CompilerContext(val tokens: List<Token>) {
} }
} }
@Suppress("NOTHING_TO_INLINE")
inline fun addBreak() { inline fun addBreak() {
breakFound = true breakFound = true
} }

View File

@ -26,6 +26,7 @@ import net.sergeych.lynon.ObjLynonClass
import net.sergeych.mp_tools.globalDefer import net.sergeych.mp_tools.globalDefer
import kotlin.math.* import kotlin.math.*
@Suppress("TYPE_INTERSECTION_AS_REIFIED_WARNING")
class Script( class Script(
override val pos: Pos, override val pos: Pos,
private val statements: List<Statement> = emptyList(), private val statements: List<Statement> = emptyList(),

View File

@ -367,6 +367,7 @@ open class Obj {
} }
@Suppress("NOTHING_TO_INLINE")
inline fun from(obj: Any?): Obj { inline fun from(obj: Any?): Obj {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return when (obj) { return when (obj) {

View File

@ -35,12 +35,12 @@ class ObjBitBuffer(val bitArray: BitArray) : Obj() {
}.apply { }.apply {
addFn("toBuffer") { addFn("toBuffer") {
requireNoArgs() requireNoArgs()
ObjBuffer(thisAs<ObjBitBuffer>().bitArray.asUbyteArray()) ObjBuffer(thisAs<ObjBitBuffer>().bitArray.asUByteArray())
} }
addFn("toDump") { addFn("toDump") {
requireNoArgs() requireNoArgs()
ObjString( ObjString(
thisAs<ObjBitBuffer>().bitArray.asUbyteArray().toDump() thisAs<ObjBitBuffer>().bitArray.asUByteArray().toDump()
) )
} }
addFn("size") { addFn("size") {

View File

@ -17,8 +17,10 @@
package net.sergeych.lynon package net.sergeych.lynon
import kotlin.math.min
@Suppress("unused") @Suppress("unused")
interface BitList { interface BitList: Comparable<BitList> {
operator fun get(bitIndex: Long): Int operator fun get(bitIndex: Long): Int
operator fun set(bitIndex: Long,value: Int) operator fun set(bitIndex: Long,value: Int)
val size: Long val size: Long
@ -31,6 +33,22 @@ interface BitList {
if( index < size) this@BitList[index++] if( index < size) this@BitList[index++]
else null else null
} }
override fun compareTo(other: BitList): Int {
val m = min(size, other.size)
for( i in 0 ..< m) {
val a = this[i]
val b = other[i]
when {
a < b -> return -1
a > b -> return 1
}
}
if( size > other.size) return 1
if( size < other.size) return -1
return 0
}
} }
fun bitListOf(vararg bits: Int): BitList { fun bitListOf(vararg bits: Int): BitList {

View File

@ -17,13 +17,18 @@
package net.sergeych.lynon package net.sergeych.lynon
import kotlinx.serialization.Serializable
import kotlin.math.min import kotlin.math.min
/** /**
* BitList implementation as fixed suze array of bits; indexing works exactly same as if * BitList implementation as fixed suze array of bits; indexing works exactly same as if
* [MemoryBitInput] is used with [MemoryBitInput.getBit]. See [MemoryBitOutput] for * [MemoryBitInput] is used with [MemoryBitInput.getBit]. See [MemoryBitOutput] for
* bits order and more information. * bits order and more information.
*
* It is [BitList] - comparable, and provides valid [hashCode] and [equals], so it could
* also be used as a key in maps.
*/ */
@Serializable
class BitArray(val bytes: UByteArray, val lastByteBits: Int) : BitList { class BitArray(val bytes: UByteArray, val lastByteBits: Int) : BitList {
val bytesSize: Int get() = bytes.size val bytesSize: Int get() = bytes.size
@ -73,7 +78,15 @@ class BitArray(val bytes: UByteArray, val lastByteBits: Int) : BitList {
fun asByteArray(): ByteArray = bytes.asByteArray() fun asByteArray(): ByteArray = bytes.asByteArray()
@Suppress("unused") @Suppress("unused")
fun asUbyteArray(): UByteArray = bytes fun asUByteArray(): UByteArray = bytes
override fun equals(other: Any?): Boolean {
return other is BitList && this.compareTo(other) == 0
}
override fun hashCode(): Int {
return bytes.contentHashCode()
}
companion object { companion object {

View File

@ -18,7 +18,6 @@
package net.sergeych.lynon package net.sergeych.lynon
import net.sergeych.collections.SortedList import net.sergeych.collections.SortedList
import net.sergeych.lynon.Huffman.Alphabet
/** /**
@ -285,7 +284,7 @@ object Huffman {
fun <T>decompress(bin: BitInput,alphabet: Alphabet<T>): UByteArray { fun <T>decompress(bin: BitInput,alphabet: Alphabet<T>): UByteArray {
val codes = deserializeCanonicCodes(bin, alphabet) val codes = deserializeCanonicCodes(bin, alphabet)
return decompressUsingCodes(bin, codes, alphabet).asUbyteArray() return decompressUsingCodes(bin, codes, alphabet).asUByteArray()
} }
} }

View File

@ -56,7 +56,7 @@ object ObjLynonClass : ObjClass("Lynon") {
@Suppress("unused") @Suppress("unused")
suspend fun lynonEncodeAny(scope: Scope, value: Obj): UByteArray = suspend fun lynonEncodeAny(scope: Scope, value: Obj): UByteArray =
(ObjLynonClass.encodeAny(scope, value)) (ObjLynonClass.encodeAny(scope, value))
.bitArray.asUbyteArray() .bitArray.asUByteArray()
@Suppress("unused") @Suppress("unused")
suspend fun lynonDecodeAny(scope: Scope, encoded: UByteArray): Obj = suspend fun lynonDecodeAny(scope: Scope, encoded: UByteArray): Obj =

View File

@ -22,7 +22,10 @@ import net.sergeych.lyng.Source
import net.sergeych.lyng.eval import net.sergeych.lyng.eval
import net.sergeych.lyng.pacman.InlineSourcesImportProvider import net.sergeych.lyng.pacman.InlineSourcesImportProvider
import net.sergeych.lyng.toSource import net.sergeych.lyng.toSource
import net.sergeych.lynon.BitArray
import net.sergeych.lynon.BitList
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertNotEquals
class OtherTests { class OtherTests {
@Test @Test
@ -67,4 +70,33 @@ class OtherTests {
Unit Unit
} }
@Test
fun testBitArrayEqAndHash() {
val b1 = BitArray.ofBits(1, 0, 1, 1)
val b11 = BitArray.ofBits(1, 0, 1, 1)
val b2 = BitArray.ofBits(1, 1, 1, 1)
val b3 = BitArray.ofBits(1, 0, 1, 1, 0)
assert( b3 > b1 )
assert( b2 > b1)
assert( b11.compareTo(b1) == 0)
assertEquals(b1, b11)
assertNotEquals(b1, b2)
assertNotEquals(b1, b3)
assert( b1.hashCode() == b11.hashCode() )
val x = mutableMapOf<BitList,String>()
x[b1] = "wrong"
x[b11] = "OK"
x[b2] = "other"
assertEquals("OK", x[b11])
assertEquals("OK", x[b1])
assertEquals("other", x[b2])
}
} }