Last active
May 25, 2024 08:39
-
-
Save dacr/eb11a5bffb6f7f4ff0386cd7c8a61fb8 to your computer and use it in GitHub Desktop.
encode/decode 4 component vector into an integer / published by https://github.com/dacr/code-examples-manager #e3887820-4329-4e6d-94fe-bb37ef830d1f/9beeed5bf259b95454da9537e4e61b4c42c23ca8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// summary : encode/decode 4 component vector into an integer | |
// keywords : scala, vector, conversions, @testable | |
// publish : gist | |
// authors : David Crosson | |
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2) | |
// id : e3887820-4329-4e6d-94fe-bb37ef830d1f | |
// created-on : 2020-11-21T10:54:55Z | |
// managed-by : https://github.com/dacr/code-examples-manager | |
// run-with : scala-cli $file | |
// --------------------- | |
//> using scala "3.4.2" | |
//> using dep "org.scalatest::scalatest:3.2.16" | |
//> using objectWrapper | |
// --------------------- | |
import org.scalatest._, flatspec._, matchers._ | |
// Vector of 4 bytes encoded into a single 32 bits Integer | |
type Vect4 = Int | |
def v4enc(a: Int, b: Int, c: Int, d: Int): Vect4 = | |
((a + 128) << 24) + ((b + 128) << 16) + ((c + 128) << 8) + (d + 128) | |
def v4dec(vect: Vect4): Array[Int] = Array( | |
(vect >>> 24 & 0xFF) - 128, | |
(vect >>> 16 & 0xFF) - 128, | |
(vect >>> 8 & 0xFF) - 128, | |
(vect & 0xFF) - 128 | |
) | |
def v4get(vect: Vect4, i: Int): Int = { | |
if (i == 0) (vect >>> 24 & 0xFF) - 128 | |
else if (i == 1) (vect >>> 16 & 0xFF) - 128 | |
else if (i == 2) (vect >>> 8 & 0xFF) - 128 | |
else (vect & 0xFF) - 128 | |
} | |
//// GENERATES MANY BOX / UNBOX | |
def v4monoOp(a: Vect4, op: Int => Int): Vect4 = { | |
v4enc( | |
op((a >>> 24 & 0xFF) - 128), | |
op((a >>> 16 & 0xFF) - 128), | |
op((a >>> 8 & 0xFF) - 128), | |
op((a & 0xFF) - 128), | |
) | |
} | |
def v4biOp(a: Vect4, b: Vect4, op: (Int, Int) => Int): Vect4 = { | |
v4enc( | |
op((a >>> 24 & 0xFF) - 128, (b >>> 24 & 0xFF) - 128), | |
op((a >>> 16 & 0xFF) - 128, (b >>> 16 & 0xFF) - 128), | |
op((a >>> 8 & 0xFF) - 128, (b >>> 8 & 0xFF) - 128), | |
op((a & 0xFF) - 128, (b & 0xFF) - 128), | |
) | |
} | |
def v4addVect(a: Vect4, b: Vect4): Vect4 = { | |
//v4biOp(a, b, _ + _) | |
v4enc( | |
(a >>> 24 & 0xFF) - 128 + (b >>> 24 & 0xFF) - 128, | |
(a >>> 16 & 0xFF) - 128 + (b >>> 16 & 0xFF) - 128, | |
(a >>> 8 & 0xFF) - 128 + (b >>> 8 & 0xFF) - 128, | |
(a & 0xFF) - 128 + (b & 0xFF) - 128, | |
) | |
} | |
def v4minusVect(a: Vect4, b: Vect4): Vect4 = { | |
//v4biOp(a, b, _ - _) | |
v4enc( | |
((a >>> 24 & 0xFF) - 128) - ((b >>> 24 & 0xFF) - 128), | |
((a >>> 16 & 0xFF) - 128) - ((b >>> 16 & 0xFF) - 128), | |
((a >>> 8 & 0xFF) - 128) - ((b >>> 8 & 0xFF) - 128), | |
((a & 0xFF) - 128) - ((b & 0xFF) - 128), | |
) | |
} | |
def v4add(a: Vect4, b: Int): Vect4 = { | |
//v4monoOp(a, _ + b) | |
v4enc( | |
((a >>> 24 & 0xFF) - 128) + b, | |
((a >>> 16 & 0xFF) - 128) + b, | |
((a >>> 8 & 0xFF) - 128) + b, | |
((a & 0xFF) - 128) + b, | |
) | |
} | |
def v4add0(a: Vect4, b: Int): Vect4 = { | |
(a & 0x00FFFFFF) + ((((a >>> 24) - 128) + b + 128) << 24) | |
} | |
def v4minus(a: Vect4, b: Int): Vect4 = { | |
//v4monoOp(a, _ - b) | |
v4enc( | |
((a >>> 24 & 0xFF) - 128) - b, | |
((a >>> 16 & 0xFF) - 128) - b, | |
((a >>> 8 & 0xFF) - 128) - b, | |
((a & 0xFF) - 128) - b, | |
) | |
} | |
def v4mult(a: Vect4, b: Int): Vect4 = { | |
//v4monoOp(a, _ * b) | |
v4enc( | |
((a >>> 24 & 0xFF) - 128) * b, | |
((a >>> 16 & 0xFF) - 128) * b, | |
((a >>> 8 & 0xFF) - 128) * b, | |
((a & 0xFF) - 128) * b, | |
) | |
} | |
def v4eq(a: Vect4, b: Vect4): Boolean = a == b | |
val v4zero = v4enc(0, 0, 0, 0) | |
def v4map[T](vect: Vect4, process: (Int, Int, Int, Int) => T): T = { | |
process((vect >>> 24 & 0xFF) - 128, | |
(vect >>> 16 & 0xFF) - 128, | |
(vect >>> 8 & 0xFF) - 128, | |
(vect & 0xFF) - 128) | |
} | |
// is positive ? (zero is not considerated as positive) | |
def v4positive(vect: Vect4): Boolean = { | |
//!v4map(vect, (a, b, c, d) => a < 0 || b < 0 || c < 0 || d < 0) && vect != v4zero | |
vect != v4zero && !( | |
(((vect >>> 24 & 0xFF) - 128) < 0) || | |
(((vect >>> 16 & 0xFF) - 128) < 0) || | |
(((vect >>> 8 & 0xFF) - 128) < 0) || | |
(((vect & 0xFF) - 128) < 0) | |
) | |
} | |
def v4negative(vect: Vect4): Boolean = { | |
//v4map(vect, (a, b, c, d) => a < 0 || b < 0 || c < 0 || d < 0) | |
(((vect >>> 24 & 0xFF) - 128) < 0) || | |
(((vect >>> 16 & 0xFF) - 128) < 0) || | |
(((vect >>> 8 & 0xFF) - 128) < 0) || | |
(((vect & 0xFF) - 128) < 0) | |
} | |
// sum each vector component | |
def v4sum(vect: Vect4): Int = { | |
//v4map(vect, (a, b, c, d) => a + b + c + d) | |
(((vect >>> 24 & 0xFF) - 128)) + | |
(((vect >>> 16 & 0xFF) - 128)) + | |
(((vect >>> 8 & 0xFF) - 128)) + | |
(((vect & 0xFF) - 128)) | |
} | |
// vect4 to string | |
def v4toString(vect: Vect4): String = { | |
v4map(vect, (a, b, c, d) => s"[$a,$b,$c,$d]") | |
} | |
class Vect4Test extends AnyFlatSpec with should.Matchers { | |
"vect4" should "decode" in { | |
def selfTest(data:Byte*):Unit = | |
v4dec(v4enc(data(0), data(1), data(2), data(3))) should contain theSameElementsAs data | |
selfTest(0,0,0,0) | |
selfTest(1,1,1,1) | |
selfTest(0,0,0,42) | |
selfTest(42,0,0,0) | |
selfTest(0,0,0,-42) | |
selfTest(-42,0,0,0) | |
selfTest(-1,-1,-1,-1) | |
selfTest(127,127,127,127) | |
selfTest(-128,-128,-128,-128) | |
} | |
it should "support mathematical operations between vectors" in { | |
v4addVect(v4enc(0,0,0,0), v4enc(0,0,0,1)) shouldBe v4enc(0,0,0,1) | |
v4addVect(v4enc(1,1,1,1), v4enc(1,1,1,1)) shouldBe v4enc(2,2,2,2) | |
v4addVect(v4enc(-1,-1,-1,-1), v4enc(1,1,1,1)) shouldBe v4enc(0,0,0,0) | |
v4addVect(v4enc(-128,-128,-128,-128), v4enc(1,1,1,1)) shouldBe v4enc(-127,-127,-127,-127) | |
v4minusVect(v4enc(127,127,127,127), v4enc(1,1,1,1)) shouldBe v4enc(126,126,126,126) | |
v4minusVect(v4enc(127,127,127,127), v4enc(127,127,127,127)) shouldBe v4enc(0,0,0,0) | |
} | |
it should "support mathematical operations with values" in { | |
v4add(v4enc(0,0,0,1), 1) shouldBe v4enc(1,1,1,2) | |
v4minus(v4enc(0,0,0,1), 10) shouldBe v4enc(-10,-10,-10,-9) | |
v4mult(v4enc(1,2,0,3), 2) shouldBe v4enc(2,4,0,6) | |
} | |
it should "be comparable" in { | |
v4positive(v4enc(0,0,0,0)) shouldBe false | |
v4positive(v4enc(0,0,0,1)) shouldBe true | |
} | |
it should "be summable" in { | |
v4sum(v4enc(0,0,0,0)) shouldBe 0 | |
v4sum(v4enc(1,2,3,4)) shouldBe 10 | |
v4sum(v4enc(127,127,127,127)) shouldBe 127*4 | |
v4sum(v4enc(-1,-1,-1,-1)) shouldBe -4 | |
v4sum(v4enc(-128,-128,-128,-128)) shouldBe -128*4 | |
} | |
it should "be stringable" in { | |
v4toString(v4enc(0,0,0,0)) shouldBe "[0,0,0,0]" | |
v4toString(v4enc(1,2,3,4)) shouldBe "[1,2,3,4]" | |
v4toString(v4enc(-1,-2,-3,-4)) shouldBe "[-1,-2,-3,-4]" | |
} | |
it should "be possible to get parts" in { | |
val va = v4enc(100,101,102,103) | |
v4get(va,0) shouldBe 100 | |
v4get(va,1) shouldBe 101 | |
v4get(va,2) shouldBe 102 | |
v4get(va,3) shouldBe 103 | |
val vb = v4enc(-100,-101,-102,-103) | |
v4get(vb,0) shouldBe -100 | |
v4get(vb,1) shouldBe -101 | |
v4get(vb,2) shouldBe -102 | |
v4get(vb,3) shouldBe -103 | |
} | |
it should "provide some math helpers" in { | |
v4add0(v4enc(1,0,0,0), 1) shouldBe v4enc(2,0,0,0) | |
v4add0(v4enc(10,0,0,0), -11) shouldBe v4enc(-1,0,0,0) | |
v4add0(v4enc(-42,0,0,0), -11) shouldBe v4enc(-53,0,0,0) | |
} | |
} | |
org.scalatest.tools.Runner.main(Array("-oDF", "-s", classOf[Vect4Test].getName)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment