You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.0 KiB
Kotlin

package net.cijber.pubgrub
import net.cijber.pubgrub.packages.PackageSelection
import net.cijber.pubgrub.stubs.PackageId
import net.cijber.pubgrub.stubs.Version
class PartialSolution<P : PackageId, V : Version<V>> {
private var decisionLevel = 0
private val assignments = mutableListOf<Assignment<P, V>>()
private var backtracking = false
private val decisions = mutableListOf<Assignment<P, V>>()
private val positive = mutableMapOf<P, Term<P, V>>()
private val negative = mutableMapOf<P, Term<P, V>>()
infix fun relation(term: Term<P, V>): SetRelation {
positive.get(term.pkg)?.let {
return it relation term
}
negative.get(term.pkg)?.let {
return it relation term
}
return SetRelation.Overlaps
}
fun derive(pkg: PackageSelection<P, V>, exclusive: Boolean, incompatibility: Incompatibility<P, V>) {
assign(Assignment(pkg, !exclusive, incompatibility, decisionLevel, assignments.count()))
}
private fun assign(assignment: Assignment<P, V>) {
assignments.add(assignment)
register(assignment)
}
private fun register(assignment: Assignment<P, V>) {
val pkg = assignment.pkg
val oldPositive = positive[pkg]
if (oldPositive != null) {
positive.remove(pkg)
oldPositive.intersect(assignment)?.let {
positive[pkg] = it
}
}
val oldNegative = negative[pkg]
val term = if (oldNegative == null) {
assignment
} else {
assignment.intersect(oldNegative)!!
}
if (!term.exclusive) {
negative.remove(pkg)
positive[pkg] = term
} else {
negative[pkg] = term
}
}
fun satisfier(term: Term<P, V>): Assignment<P, V> {
var assignedTerm: Term<P, V>? = null;
for (assignment in assignments) {
if (assignment.pkg != term.pkg) continue
assignedTerm = if (assignedTerm == null) {
assignment
} else {
assignedTerm.intersect(assignment)
}
if (assignedTerm?.satisfies(term) == true) {
return assignment
}
}
error("[BUG] $term is not satisfied")
}
fun backtrack(decisionLevel: Int) {
backtracking = true
val packages = mutableSetOf<P>()
while (assignments.lastOrNull()?.decisionLevel?.let { it > decisionLevel } == true) {
val removed = assignments.removeAt(assignments.lastIndex)
packages.add(removed.pkg)
if (removed.isDecision) {
decisions.remove(removed)
}
}
for (pkg in packages) {
positive.remove(pkg)
negative.remove(pkg)
}
for (assignment in assignments) {
if (packages.contains(assignment.pkg)) {
register(assignment)
}
}
}
}