Coverage Summary for Class: Length (it.unibo.tuprolog.argumentation.core.engine.stdlib.primitive)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| Length |
100%
(1/1)
|
100%
(6/6)
|
95.8%
(23/24)
|
100%
(23/23)
|
97.4%
(263/270)
|
package it.unibo.tuprolog.argumentation.core.engine.stdlib.primitive
import it.unibo.tuprolog.argumentation.core.runtime.Arg2pUnificator
import it.unibo.tuprolog.core.Cons
import it.unibo.tuprolog.core.Integer
import it.unibo.tuprolog.core.List
import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.core.Var
import it.unibo.tuprolog.solve.ExecutionContext
import it.unibo.tuprolog.solve.exception.error.DomainError
import it.unibo.tuprolog.solve.exception.error.TypeError
import it.unibo.tuprolog.solve.primitive.BinaryRelation
import it.unibo.tuprolog.solve.primitive.Solve
import it.unibo.tuprolog.solve.primitive.Solve.Response
/**
* `length(?List, ?Length)` (arg2p: tuProlog 3 has none): Length is the number of items of List. A partial list (its
* tail a variable) is completed with fresh variables to the given length, or, with no length given, to each length
* in turn from the items it has.
*/
object Length : BinaryRelation<ExecutionContext>("length") {
override fun Solve.Request<ExecutionContext>.computeAll(
first: Term,
second: Term,
): Sequence<Response> {
if (second !is Var && second !is Integer) {
throw TypeError.forArgument(context, signature, TypeError.Expected.INTEGER, second, 1)
}
if (second is Integer && second.intValue.signum < 0) {
throw DomainError.forArgument(context, signature, DomainError.Expected.NOT_LESS_THAN_ZERO, second, 1)
}
var items = 0
var tail = first
while (tail is Cons) {
items++
tail = tail.tail
}
return when {
tail.isEmptyList -> sequenceOf(unifying(second, Integer.of(items)))
tail !is Var -> throw TypeError.forArgument(context, signature, TypeError.Expected.LIST, first, 0)
second is Integer -> {
val length = second.intValue.toInt()
if (length < items) emptySequence() else sequenceOf(unifying(tail, freshList(length - items)))
}
else ->
generateSequence(items) { it + 1 }.map { length ->
unifyingBoth(tail, freshList(length - items), second, Integer.of(length))
}
}
}
private fun freshList(size: Int): Term = List.of(List(size) { Var.anonymous() })
private fun Solve.Request<ExecutionContext>.unifying(
term: Term,
value: Term,
): Response = Arg2pUnificator.mgu(term, value).let { if (it.isSuccess) replyWith(it) else replyFail() }
private fun Solve.Request<ExecutionContext>.unifyingBoth(
tail: Term,
items: Term,
length: Term,
value: Term,
): Response =
Arg2pUnificator
.mgu(List.of(tail, length), List.of(items, value))
.let { if (it.isSuccess) replyWith(it) else replyFail() }
}