From 93354a94a7977487a2c89aa8f8f2e0c72db2d35d Mon Sep 17 00:00:00 2001 From: Stephan Linkel Date: Fri, 13 Dec 2024 11:17:45 +0100 Subject: [PATCH] first linear algebra equation solving helpers --- .../utils/geometry/plain/discrete/Vector.kt | 10 + .../kotlin/de/linkel/aoc/utils/grid/Grid.kt | 23 +- .../aoc/utils/math/algebra/Equations.kt | 22 + .../linkel/aoc/utils/math/algebra/Matrix.kt | 155 ++++++ .../linkel/aoc/utils/readers/ReaderMixIn.kt | 14 + .../geometry/plain/discrete/VectorTests.kt | 14 + .../aoc/utils/math/algebra/EquationTests.kt | 26 + .../aoc/utils/math/algebra/MatrixTests.kt | 467 ++++++++++++++++++ 8 files changed, 729 insertions(+), 2 deletions(-) create mode 100644 lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Equations.kt create mode 100644 lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Matrix.kt create mode 100644 lib/src/main/kotlin/de/linkel/aoc/utils/readers/ReaderMixIn.kt create mode 100644 lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/EquationTests.kt create mode 100644 lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/MatrixTests.kt diff --git a/lib/src/main/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/Vector.kt b/lib/src/main/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/Vector.kt index 5012b17..588a168 100644 --- a/lib/src/main/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/Vector.kt +++ b/lib/src/main/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/Vector.kt @@ -1,6 +1,7 @@ package de.linkel.aoc.utils.geometry.plain.discrete import de.linkel.aoc.utils.geometry.plain.continuous.VectorD +import de.linkel.aoc.utils.math.CommonMath import kotlin.math.* class Vector( @@ -51,6 +52,15 @@ class Vector( && abs(this.deltaY) >= abs(other.deltaY) } + fun min(): Vector { + return if (deltaX == 0) Vector(deltaX = 0, deltaY = deltaY.sign) + else if (deltaY == 0) Vector(deltaX = deltaX.sign, deltaY = 0) + else { + val gcd = CommonMath.gcd(deltaX, deltaY) + Vector(deltaX / gcd, deltaY / gcd) + } + } + fun turnClockwise(): Vector = Vector(-deltaY, deltaX) fun turnCounterClockwise(): Vector = Vector(deltaY, -deltaX) fun turnAround(): Vector = Vector(-deltaX, -deltaY) diff --git a/lib/src/main/kotlin/de/linkel/aoc/utils/grid/Grid.kt b/lib/src/main/kotlin/de/linkel/aoc/utils/grid/Grid.kt index d81edf3..1db41c0 100644 --- a/lib/src/main/kotlin/de/linkel/aoc/utils/grid/Grid.kt +++ b/lib/src/main/kotlin/de/linkel/aoc/utils/grid/Grid.kt @@ -10,7 +10,7 @@ class Grid( dimension: Dimension = Dimension(1,1) ) { companion object { - fun parse(lines: Sequence, lambda: (pos: Point, c: Char) -> T?): Grid { + fun parse(lines: Sequence, crop: Boolean = true, lambda: (pos: Point, c: Char) -> T?): Grid { val grid = Grid() lines .filter { it.isNotEmpty() } @@ -27,7 +27,8 @@ class Grid( } } } - grid.crop() + if (crop) + grid.crop() return grid } } @@ -39,6 +40,24 @@ class Grid( height = dimension.height ) + fun straightLine(start: Point, direction: Vector): List { + val result = mutableListOf() + var pos = start + while (pos in boundingBox) { + result.add(pos) + pos += direction + } + return result + } + + fun walkUntil(start: Point, vector: Vector, stop: (point: Point) -> Boolean): Point { + var pos = start + while (true) { + pos += vector + if (stop(pos)) return pos + } + } + // evtl nen performance-optimierteren zugriff? / ne liste aller belegten punkte pro row/col? private val store = mutableMapOf() val width get(): Int = boundingBox.width diff --git a/lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Equations.kt b/lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Equations.kt new file mode 100644 index 0000000..7160790 --- /dev/null +++ b/lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Equations.kt @@ -0,0 +1,22 @@ +package de.linkel.aoc.utils.math.algebra + +import java.math.BigDecimal + +object Equations { + fun cramer(coefficients: Matrix, results: List): List? { + require(coefficients.rows == coefficients.cols) { "coefficients matrix has to be square" } + require(results.size == coefficients.rows) { "results vector has to be same size as coefficients rows" } + val denominator = coefficients.det() + return (0 until coefficients.cols) + .map { i -> + coefficients.replaceCol(i, results).det() + } + .takeIf { numerators -> + numerators + .all { it % denominator == BigDecimal.ZERO} + } + ?.map { + it / denominator + } + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Matrix.kt b/lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Matrix.kt new file mode 100644 index 0000000..714390d --- /dev/null +++ b/lib/src/main/kotlin/de/linkel/aoc/utils/math/algebra/Matrix.kt @@ -0,0 +1,155 @@ +package de.linkel.aoc.utils.math.algebra + +import java.math.BigDecimal + +data class Matrix( + val cols: Int, + val rows: Int, + val values: List, +) { + init { + require(rows > 0) { "rows must be greater than zero" } + require(cols > 0) { "cols must be greater than zero" } + require(values.size == rows * cols) { "wrong number of values" } + } + + fun det(): BigDecimal { + require(rows == cols) { "only square matrices can be determined" } + return when (rows) { + 1 -> values[0] + 2 -> values[0] * values[3] - values[1] * values[2] + else -> { + val withoutFirstCol = this.removeCol(0) + val minusOne = BigDecimal.ONE.negate() + (0 until rows) + .sumOf { idx -> + (minusOne.pow(idx)) * values[idx * cols] * withoutFirstCol.removeRow(idx).det() + } + } + } + } + + fun getRow(idx: Int): List { + require(idx in 0 until rows) { "row index out of bounds" } + val select = (cols * idx) until (cols * (idx + 1)) + return values.filterIndexed { i, _ -> i in select } + } + + fun getCol(idx: Int): List { + require(idx in 0 until cols) { "col index out of bounds" } + return values.filterIndexed { i, _ -> i % cols == idx } + } + + fun getAt(col: Int, row: Int): BigDecimal { + require(col in 0 until cols) { "col index out of bounds" } + require(row in 0 until rows) { "col index out of bounds" } + return values[row * cols + col] + } + + fun removeRow(idx: Int): Matrix { + require(idx in 0 until rows) { "row index out of bounds" } + require(rows > 1) { "matrix must have more than one row" } + val remove = (cols * idx) until (cols * (idx + 1)) + return Matrix( + cols = cols, + rows = rows - 1, + values = values.filterIndexed { i, _ -> i !in remove }) + } + + fun removeCol(idx: Int): Matrix { + require(idx in 0 until cols) { "col index out of bounds" } + require(cols > 1) { "matrix must have more than one col" } + return Matrix( + cols = cols - 1, + rows = rows, + values = values.filterIndexed { i, _ -> i % cols != idx } + ) + } + + fun insertRow(idx: Int, row: List): Matrix { + require(idx in 0 until rows+1) { "row index out of bounds" } + require(row.size == cols) { "wrong number of values" } + return Matrix( + cols = cols, + rows = rows + 1, + values = values.subList(0, idx * cols) + row + values.subList(idx * cols, values.size) + ) + } + + fun replaceRow(idx: Int, row: List): Matrix { + require(idx in 0 until rows) { "row index out of bounds" } + require(row.size == cols) { "wrong number of values" } + return Matrix( + cols = cols, + rows = rows, + values = values.subList(0, idx * cols) + row + values.subList((idx + 1) * cols, values.size) + ) + } + + fun insertCol(idx: Int, col: List): Matrix { + require(idx in 0 until cols+1) { "col index out of bounds" } + require(col.size == rows) { "wrong number of values" } + return Matrix( + cols = cols + 1, + rows = rows, + values = (0 until (cols + 1) * rows).map { i -> + val c = i % (cols + 1) + val r = i / (cols + 1) + if (c < idx) + values[r * cols + c] + else if (c == idx) + col[r] + else values[r * cols + c - 1] + } + ) + } + + fun replaceCol(idx: Int, col: List): Matrix { + require(idx in 0 until cols) { "col index out of bounds" } + require(col.size == rows) { "wrong number of values" } + return Matrix( + cols = cols, + rows = rows, + values = values.mapIndexed { i, v -> + val c = i % cols + val r = i / cols + if (c == idx) + col[r] + else + v + } + ) + } + + fun appendRow(row: List): Matrix { + require(row.size == cols) { "wrong number of values" } + return Matrix( + cols = cols, + rows = rows + 1, + values = values + row + ) + } + + fun appendCol(col: List): Matrix { + require(col.size == rows) { "wrong number of values" } + return Matrix( + cols = cols + 1, + rows = rows, + values = (0 until rows).flatMap { r -> + values.subList(r * cols, (r + 1) * cols) + col[r] + } + ) + } + + fun transpose(): Matrix { + return Matrix( + cols = rows, + rows = cols, + (0 until cols) + .flatMap { c -> + (0 until rows) + .map { r -> getAt(c, r) } + } + ) + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/de/linkel/aoc/utils/readers/ReaderMixIn.kt b/lib/src/main/kotlin/de/linkel/aoc/utils/readers/ReaderMixIn.kt new file mode 100644 index 0000000..319ab27 --- /dev/null +++ b/lib/src/main/kotlin/de/linkel/aoc/utils/readers/ReaderMixIn.kt @@ -0,0 +1,14 @@ +package de.linkel.aoc.utils.readers + +import java.io.Reader + +fun Reader.charSequence(): Sequence { + val reader = this + return sequence { + while (true) { + val i = reader.read() + if (i >= 0) yield(i.toChar()) + else break + } + } +} diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/VectorTests.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/VectorTests.kt index 0d7662e..de735a5 100644 --- a/lib/src/test/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/VectorTests.kt +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/geometry/plain/discrete/VectorTests.kt @@ -21,6 +21,20 @@ class VectorTests { Assertions.assertThat(vector1).isEqualTo(vector2) Assertions.assertThat(vector1).isNotEqualTo(vector3) } + + @Test + fun `test Vector min method`() { + val vector1 = Vector(2, 3) + val vector2 = Vector(18, 6) + val vector3 = Vector(5, 0) + val vector4 = Vector(0, 2) + val vector5 = Vector(0, 0) + Assertions.assertThat(vector1.min()).isEqualTo(vector1) + Assertions.assertThat(vector2.min()).isEqualTo(Vector(3, 1)) + Assertions.assertThat(vector3.min()).isEqualTo(Vector(1, 0)) + Assertions.assertThat(vector4.min()).isEqualTo(Vector(0, 1)) + Assertions.assertThat(vector5.min()).isEqualTo(Vector(0, 0)) + } @Test fun `test Vector distance calculation`() { diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/EquationTests.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/EquationTests.kt new file mode 100644 index 0000000..d5efb8f --- /dev/null +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/EquationTests.kt @@ -0,0 +1,26 @@ +package de.linkel.aoc.utils.math.algebra + +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + + +class EquationTests { + private fun bd(vararg values: Int) = values.map { it.toBigDecimal() }.toList() + + @Test + fun `simple 2x2 cramer`() { + Assertions.assertThat( + Equations.cramer( + Matrix(2, 2, bd(94, 22, 34, 67)), + bd(8400, 5400) + ) + ).isEqualTo(bd(80, 40)) + + Assertions.assertThat( + Equations.cramer( + Matrix(2, 2, bd(26, 67, 66, 21)), + bd(12748, 12176) + ) + ).isNull() + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/MatrixTests.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/MatrixTests.kt new file mode 100644 index 0000000..88c4a76 --- /dev/null +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/math/algebra/MatrixTests.kt @@ -0,0 +1,467 @@ +package de.linkel.aoc.utils.math.algebra + +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + + +class MatrixTests { + private fun bd(vararg values: Int) = values.map { it.toBigDecimal() }.toList() + + @Test + fun `matrix getAt works as expected`() { + val m1 = Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + Assertions.assertThat(m1.getAt(0, 0)).isEqualTo(1.toBigDecimal()) + Assertions.assertThat(m1.getAt(2, 0)).isEqualTo(3.toBigDecimal()) + Assertions.assertThat(m1.getAt(1, 1)).isEqualTo(5.toBigDecimal()) + Assertions.assertThat(m1.getAt(0, 2)).isEqualTo(7.toBigDecimal()) + Assertions.assertThat(m1.getAt(1, 2)).isEqualTo(8.toBigDecimal()) + val m2 = Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)) + Assertions.assertThat(m2.getAt(0, 0)).isEqualTo(1.toBigDecimal()) + Assertions.assertThat(m2.getAt(2, 0)).isEqualTo(3.toBigDecimal()) + Assertions.assertThat(m2.getAt(1, 1)).isEqualTo(5.toBigDecimal()) + } + @Test + fun `matrix getAt fails when it should`() { + val m = Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + Assertions.assertThatThrownBy { + m.getAt(3, 1) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + m.getAt(1, 3) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + m.getAt(-1, 1) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + m.getAt(1, -1) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix getRow works as expected`() { + val m = Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + Assertions.assertThat(m.getRow(0)) + .isEqualTo(bd(1, 2, 3)) + Assertions.assertThat(m.getRow(1)) + .isEqualTo(bd(4, 5, 6)) + Assertions.assertThat(m.getRow(2)) + .isEqualTo(bd(7, 8, 9)) + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)).getRow(1) + ).isEqualTo(bd(3, 4)) + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)).getRow(1) + ).isEqualTo(bd(4, 5, 6)) + } + @Test + fun `matrix getRow fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)).getRow(3) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix getCol works as expected`() { + val m = Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + Assertions.assertThat(m.getCol(0)) + .isEqualTo(bd(1, 4, 7)) + Assertions.assertThat(m.getCol(1)) + .isEqualTo(bd(2, 5, 8)) + Assertions.assertThat(m.getCol(2)) + .isEqualTo(bd(3, 6, 9)) + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)).getCol(1) + ).isEqualTo(bd(2, 4, 6)) + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)).getCol(1) + ).isEqualTo(bd(2, 5)) + } + @Test + fun `matrix getCol fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)).getCol(3) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix removeRow works as expected`() { + val m = Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + Assertions.assertThat(m.removeRow(0)) + .isEqualTo(Matrix(3, 2, bd(4, 5, 6, 7, 8, 9))) + Assertions.assertThat(m.removeRow(1)) + .isEqualTo(Matrix(3, 2, bd(1, 2, 3, 7, 8, 9))) + Assertions.assertThat(m.removeRow(2)) + .isEqualTo(Matrix(3, 2, bd(1, 2, 3, 4, 5, 6))) + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)).removeRow(1) + ).isEqualTo(Matrix(2, 2, bd(1, 2, 5, 6))) + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)).removeRow(1) + ).isEqualTo(Matrix(3, 1, bd(1, 2, 3))) + } + @Test + fun `matrix removeRow fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)).removeRow(3) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(3, 1, bd(1, 2, 3)).removeRow(0) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix removeCol works as expected`() { + val m = Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + Assertions.assertThat(m.removeCol(0)) + .isEqualTo(Matrix(2, 3, bd(2, 3, 5, 6, 8, 9))) + Assertions.assertThat(m.removeCol(1)) + .isEqualTo(Matrix(2, 3, bd(1, 3, 4, 6, 7, 9))) + Assertions.assertThat(m.removeCol(2)) + .isEqualTo(Matrix(2, 3, bd(1, 2, 4, 5, 7, 8))) + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)).removeCol(1) + ).isEqualTo(Matrix(1, 3, bd(1, 3, 5))) + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)).removeCol(1) + ).isEqualTo(Matrix(2, 2, bd(1, 3, 4, 6))) + } + @Test + fun `matrix removeCol fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)).removeCol(3) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 3, bd(1, 2, 3)).removeCol(0) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix appendCol works as expected`() { + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .appendCol(bd(10, 11, 12)) + ).isEqualTo(Matrix(4, 3, bd(1, 2, 3, 10, 4, 5, 6, 11, 7, 8, 9, 12))) + Assertions.assertThat( + Matrix(2, 2, bd(1, 2, 3, 4)) + .appendCol(bd(10, 11)) + ).isEqualTo(Matrix(3, 2, bd(1, 2, 10, 3, 4, 11))) + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)) + .appendCol(bd(10, 11)) + ).isEqualTo(Matrix(4, 2, bd(1, 2, 3, 10, 4, 5, 6, 11))) + Assertions.assertThat( + Matrix(1, 1, bd(1)) + .appendCol(bd(2)) + .appendCol(bd(3)) + ).isEqualTo(Matrix(3, 1, bd(1, 2, 3))) + } + @Test + fun `matrix appendCol fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).appendCol(bd(1)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).appendCol(bd(1, 2, 3)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).appendCol(bd()) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).appendCol(bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix appendRow works as expected`() { + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .appendRow(bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 4, bd(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))) + Assertions.assertThat( + Matrix(2, 2, bd(1, 2, 3, 4)) + .appendRow(bd(10, 11)) + ).isEqualTo(Matrix(2, 3, bd(1, 2, 3, 4, 10, 11))) + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)) + .appendRow(bd(10, 11)) + ).isEqualTo(Matrix(2, 4, bd(1, 2, 3, 4, 5, 6, 10, 11))) + Assertions.assertThat( + Matrix(1, 1, bd(1)) + .appendRow(bd(2)) + .appendRow(bd(3)) + ).isEqualTo(Matrix(1, 3, bd(1, 2, 3))) + } + @Test + fun `matrix appendRow fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).appendRow(bd(1)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).appendRow(bd(1, 2, 3)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).appendRow(bd()) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).appendRow(bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix insertRow works as expected`() { + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertRow(0, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 4, bd(10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertRow(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 4, bd(1, 2, 3, 10, 11, 12, 4, 5, 6, 7, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertRow(2, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 4, bd(1, 2, 3, 4, 5, 6, 10, 11, 12, 7, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertRow(3, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 4, bd(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))) + + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)) + .insertRow(1, bd(10, 11)) + ).isEqualTo(Matrix(2, 4, bd(1, 2, 10, 11, 3, 4, 5, 6))) + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)) + .insertRow(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(1, 2, 3, 10, 11, 12, 4, 5, 6))) + } + @Test + fun `matrix insertRow fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertRow(0, bd(1)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertRow(-1, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertRow(3, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertRow(0, bd(1, 2, 3)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).insertRow(0, bd()) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).insertRow(0, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix replaceRow works as expected`() { + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .replaceRow(0, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(10, 11, 12, 4, 5, 6, 7, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .replaceRow(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(1, 2, 3, 10, 11, 12, 7, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .replaceRow(2, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 10, 11, 12))) + + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)) + .replaceRow(1, bd(10, 11)) + ).isEqualTo(Matrix(2, 3, bd(1, 2, 10, 11, 5, 6))) + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)) + .replaceRow(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 2, bd(1, 2, 3, 10, 11, 12))) + } + @Test + fun `matrix replaceRow fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceRow(0, bd(1)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceRow(-1, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceRow(2, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceRow(0, bd(1, 2, 3)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).replaceRow(0, bd()) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).replaceRow(0, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix insertCol works as expected`() { + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertCol(0, bd(10, 11, 12)) + ).isEqualTo(Matrix(4, 3, bd(10, 1, 2, 3, 11, 4, 5, 6, 12, 7, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertCol(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(4, 3, bd(1, 10, 2, 3, 4, 11, 5, 6, 7, 12, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertCol(2, bd(10, 11, 12)) + ).isEqualTo(Matrix(4, 3, bd(1, 2, 10, 3, 4, 5, 11, 6, 7, 8, 12, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .insertCol(3, bd(10, 11, 12)) + ).isEqualTo(Matrix(4, 3, bd(1, 2, 3, 10, 4, 5, 6, 11, 7, 8, 9, 12))) + + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)) + .insertCol(1, bd(10, 11)) + ).isEqualTo(Matrix(4, 2, bd(1, 10, 2, 3, 4, 11, 5, 6))) + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)) + .insertCol(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(1, 10, 2, 3, 11, 4, 5, 12, 6))) + } + @Test + fun `matrix insertCol fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertCol(0, bd(1)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertCol(-1, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertCol(3, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).insertCol(0, bd(1, 2, 3)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).insertCol(0, bd()) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).insertCol(0, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix replaceCol works as expected`() { + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .replaceCol(0, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(10, 2, 3, 11, 5, 6, 12, 8, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .replaceCol(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(1, 10, 3, 4, 11, 6, 7, 12, 9))) + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)) + .replaceCol(2, bd(10, 11, 12)) + ).isEqualTo(Matrix(3, 3, bd(1, 2, 10, 4, 5, 11, 7, 8, 12))) + + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)) + .replaceCol(1, bd(10, 11)) + ).isEqualTo(Matrix(3, 2, bd(1, 10, 3, 4, 11, 6))) + Assertions.assertThat( + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)) + .replaceCol(1, bd(10, 11, 12)) + ).isEqualTo(Matrix(2, 3, bd(1, 10, 3, 11, 5, 12))) + } + @Test + fun `matrix replaceCol fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceCol(0, bd(1)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceCol(-1, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceCol(2, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(2, 2, bd(1, 2, 3, 4)).replaceCol(0, bd(1, 2, 3)) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).replaceCol(0, bd()) + }.isInstanceOf(IllegalArgumentException::class.java) + Assertions.assertThatThrownBy { + Matrix(1, 1, bd(1)).replaceCol(0, bd(1, 2)) + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix det works as expected`() { + Assertions.assertThat( + Matrix(1, 1, bd(5)).det() + ).isEqualTo(5.toBigDecimal()) + + Assertions.assertThat( + Matrix(2, 2, bd(1, 2, 3, 4)).det() + ).isEqualTo((-2).toBigDecimal()) + + Assertions.assertThat( + Matrix(2, 2, bd(3, 7, 1, -4)).det() + ).isEqualTo((-19).toBigDecimal()) + + Assertions.assertThat( + Matrix(2, 2, bd(1, 0, 0, 1)).det() + ).isEqualTo(1.toBigDecimal()) + + Assertions.assertThat( + Matrix(3, 3, bd(-2, -1, 2, 2, 1, 4, -3, 3, -1)).det() + ).isEqualTo(54.toBigDecimal()) + + Assertions.assertThat( + Matrix(3, 3, bd(1, 0, 0, 0, 1, 0, 0, 0, 1)).det() + ).isEqualTo(1.toBigDecimal()) + } + @Test + fun `matrix det fails when it should`() { + Assertions.assertThatThrownBy { + Matrix(2, 3, bd(1, 2, 3, 4, 5, 6)).det() + }.isInstanceOf(IllegalArgumentException::class.java) + } + + @Test + fun `matrix transpose works as expected`() { + Assertions.assertThat( + Matrix(1, 1, bd(5)).transpose() + ).isEqualTo( + Matrix(1, 1, bd(5)) + ) + + Assertions.assertThat( + Matrix(2, 2, bd(1, 2, 3, 4)).transpose() + ).isEqualTo( + Matrix(2, 2, bd(1, 3, 2, 4)) + ) + + Assertions.assertThat( + Matrix(3, 3, bd(1, 2, 3, 4, 5, 6, 7, 8, 9)).transpose() + ).isEqualTo( + Matrix(3, 3, bd(1, 4, 7, 2, 5, 8, 3, 6, 9)) + ) + + Assertions.assertThat( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)).transpose() + ).isEqualTo( + Matrix(2, 3, bd(1, 4, 2, 5, 3, 6)) + ) + + Assertions.assertThat( + Matrix(2, 3, bd(1, 4, 2, 5, 3, 6)).transpose() + ).isEqualTo( + Matrix(3, 2, bd(1, 2, 3, 4, 5, 6)) + ) + } +} \ No newline at end of file