Skip to content

Commit 2af2615

Browse files
committed
Use Option in GaussianElimination
1 parent 2af0082 commit 2af2615

File tree

2 files changed

+48
-38
lines changed

2 files changed

+48
-38
lines changed

src/main/scala/eu/sim642/adventofcode2025/Day10.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ object Day10 {
123123
.map(_.foldLeft(zeroCol)(_.updated(_, 1)))
124124
.transpose
125125

126-
val sol = GaussianElimination.solve(rows, machine.joltages)
126+
val sol = GaussianElimination.solve(rows, machine.joltages).get
127127
//val mSum = m.transpose.map(_.sum) // TODO: use?
128128

129129
def helper(freeMaxs: List[Int]): Iterator[List[Int]] = freeMaxs match { // TODO: this seems like it should exist from earlier somewhere
@@ -140,7 +140,7 @@ object Day10 {
140140
val dependentMaxs = sol.dependentVars.map(maxs)
141141
(for {
142142
freeVals <- helper(freeMaxs.toList)
143-
dependentVals = sol.evaluate(freeVals)
143+
dependentVals <- sol.evaluate(freeVals)
144144
if dependentVals.forall(_ >= 0)
145145
if (dependentVals lazyZip dependentMaxs).forall(_ <= _)
146146
} yield dependentVals.sum + freeVals.sum).min

src/main/scala/eu/sim642/adventofcodelib/GaussianElimination.scala

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,43 @@
11
package eu.sim642.adventofcodelib
22

3+
import eu.sim642.adventofcodelib.IntegralImplicits.ExtraDivModIntegralOps
4+
35
import scala.collection.mutable
46
import scala.math.Integral.Implicits.infixIntegralOps
57
import scala.math.Ordering.Implicits.infixOrderingOps
68
import scala.reflect.ClassTag
9+
import scala.util.boundary
10+
import scala.util.boundary.break
711

12+
/**
13+
* Integer-only Gaussian elimination.
14+
*/
815
object GaussianElimination {
916

1017
case class Solution[A: Integral](dependentVars: Seq[Int], dependentGenerator: Seq[A],
1118
freeVars: Seq[Int], freeGenerators: Seq[Seq[A]],
12-
const: Seq[A]) {
19+
constGenerator: Seq[A]) {
1320
private lazy val freeGeneratorsTransposed = { // transpose of empty generators needs right length for lazyZip to work below
1421
if (freeGenerators.isEmpty)
15-
const.map(_ => Nil)
22+
constGenerator.map(_ => Nil)
1623
else
1724
freeGenerators.transpose
1825
}
1926

20-
require(dependentGenerator.size == const.size)
27+
require(dependentGenerator.size == constGenerator.size)
2128
require(dependentGenerator.size == freeGeneratorsTransposed.size)
2229

23-
def evaluate(freeVals: Seq[A]): Seq[A] = {
24-
(const lazyZip freeGeneratorsTransposed lazyZip dependentGenerator).map((v, fgt, mainVar) => {
25-
val r = v - (fgt lazyZip freeVals).map(_ * _).sum
26-
if (r % mainVar == 0)
27-
r / mainVar
28-
else
29-
-summon[Integral[A]].one // TODO: Option
30-
})
30+
def evaluate(freeVals: Seq[A]): Option[Seq[A]] = {
31+
boundary { // poor man's sequence
32+
Some((constGenerator lazyZip freeGeneratorsTransposed lazyZip dependentGenerator).map((const, freeCoeffs, dependentCoeff) => {
33+
val r = const - (freeCoeffs lazyZip freeVals).map(_ * _).sum
34+
(r /! dependentCoeff).getOrElse(break(None))
35+
}))
36+
}
3137
}
3238
}
3339

34-
def solve[A: ClassTag](initialA: Seq[Seq[A]], initialb: Seq[A])(using aIntegral: Integral[A]): Solution[A] = {
40+
def solve[A: ClassTag](initialA: Seq[Seq[A]], initialb: Seq[A])(using aIntegral: Integral[A]): Option[Solution[A]] = {
3541
val m = initialA.size
3642
val n = initialA.head.size
3743
require(initialb.sizeIs == m)
@@ -83,32 +89,36 @@ object GaussianElimination {
8389
}
8490
}
8591

86-
// check consistency
87-
for (y2 <- y until b.size)
88-
assert(b(y2) == 0) // TODO: return Option
92+
boundary {
93+
// check consistency
94+
for (y2 <- y until b.size) {
95+
if (b(y2) != 0)
96+
break(None)
97+
}
8998

90-
// backward elimination
91-
val dependentVars = mutable.ArrayBuffer.empty[Int]
92-
val freeVars = mutable.ArrayBuffer.empty[Int]
93-
y = 0
94-
for (x <- 0 until n) {
95-
if (y >= m || A(y)(x) == 0)
96-
freeVars += x
97-
else {
98-
dependentVars += x
99-
for (y2 <- 0 until y)
100-
reduceRow(x, y, y2)
101-
y += 1
99+
// backward elimination
100+
val dependentVars = mutable.ArrayBuffer.empty[Int]
101+
val freeVars = mutable.ArrayBuffer.empty[Int]
102+
y = 0
103+
for (x <- 0 until n) {
104+
if (y >= m || A(y)(x) == 0)
105+
freeVars += x
106+
else {
107+
dependentVars += x
108+
for (y2 <- 0 until y)
109+
reduceRow(x, y, y2)
110+
y += 1
111+
}
102112
}
103-
}
104113

105-
val Aview = A.view.take(dependentVars.size)
106-
Solution(
107-
dependentVars = dependentVars.toSeq,
108-
dependentGenerator = (A lazyZip dependentVars).map(_(_)).toSeq,
109-
freeVars = freeVars.toSeq,
110-
freeGenerators = freeVars.view.map(x => Aview.map(_(x)).toSeq).toSeq,
111-
const = b.view.take(dependentVars.size).toSeq
112-
)
114+
val Aview = A.view.take(dependentVars.size)
115+
Some(Solution(
116+
dependentVars = dependentVars.toSeq,
117+
dependentGenerator = (A lazyZip dependentVars).map(_(_)).toSeq,
118+
freeVars = freeVars.toSeq,
119+
freeGenerators = freeVars.view.map(x => Aview.map(_(x)).toSeq).toSeq,
120+
constGenerator = b.view.take(dependentVars.size).toSeq
121+
))
122+
}
113123
}
114124
}

0 commit comments

Comments
 (0)