feat: Floating point arithmetic
This commit is contained in:
parent
f9950a3fd3
commit
4a6850527f
13 changed files with 527 additions and 55 deletions
34
src/prolog/ast/arithmetic/Float.kt
Normal file
34
src/prolog/ast/arithmetic/Float.kt
Normal file
|
@ -0,0 +1,34 @@
|
|||
package prolog.ast.arithmetic
|
||||
|
||||
import prolog.Substitutions
|
||||
|
||||
class Float(override val value: kotlin.Float): Number {
|
||||
// Floats are already evaluated
|
||||
override fun simplify(subs: Substitutions): Simplification = Simplification(this, this)
|
||||
|
||||
override fun toString(): String = value.toString()
|
||||
|
||||
override operator fun plus(other: Number): Number = when (other) {
|
||||
is Float -> Float(value + other.value)
|
||||
is Integer -> Float(value + other.value.toFloat())
|
||||
else -> throw IllegalArgumentException("Cannot add $this and $other")
|
||||
}
|
||||
|
||||
override operator fun minus(other: Number): Number = when (other) {
|
||||
is Float -> Float(value - other.value)
|
||||
is Integer -> Float(value - other.value.toFloat())
|
||||
else -> throw IllegalArgumentException("Cannot subtract $this and $other")
|
||||
}
|
||||
|
||||
override operator fun div(other: Number): Number = when (other) {
|
||||
is Float -> Float(value / other.value)
|
||||
is Integer -> Float(value / other.value.toFloat())
|
||||
else -> throw IllegalArgumentException("Cannot divide $this and $other")
|
||||
}
|
||||
|
||||
override operator fun times(other: Number): Number = when (other) {
|
||||
is Float -> Float(value * other.value)
|
||||
is Integer -> Float(value * other.value.toFloat())
|
||||
else -> throw IllegalArgumentException("Cannot multiply $this and $other")
|
||||
}
|
||||
}
|
40
src/prolog/ast/arithmetic/Integer.kt
Normal file
40
src/prolog/ast/arithmetic/Integer.kt
Normal file
|
@ -0,0 +1,40 @@
|
|||
package prolog.ast.arithmetic
|
||||
|
||||
import prolog.Substitutions
|
||||
|
||||
data class Integer(override val value: Int) : Number {
|
||||
// Integers are already evaluated
|
||||
override fun simplify(subs: Substitutions): Simplification = Simplification(this, this)
|
||||
|
||||
override fun toString(): String = value.toString()
|
||||
|
||||
override operator fun plus(other: Number): Number = when (other) {
|
||||
is Float -> Float(value + other.value)
|
||||
is Integer -> Integer(value + other.value)
|
||||
else -> throw IllegalArgumentException("Cannot add $this and $other")
|
||||
}
|
||||
|
||||
override operator fun minus(other: Number): Number = when (other) {
|
||||
is Float -> Float(value - other.value)
|
||||
is Integer -> Integer(value - other.value)
|
||||
else -> throw IllegalArgumentException("Cannot subtract $this and $other")
|
||||
}
|
||||
|
||||
override operator fun div(other: Number): Number = when (other) {
|
||||
is Float -> Float(value / other.value)
|
||||
is Integer -> {
|
||||
if (value / other.value * other.value == value) {
|
||||
Integer(value / other.value)
|
||||
} else {
|
||||
Float(value / other.value.toFloat())
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("Cannot divide $this and $other")
|
||||
}
|
||||
|
||||
override operator fun times(other: Number): Number = when (other) {
|
||||
is Float -> Float(value * other.value)
|
||||
is Integer -> Integer(value * other.value)
|
||||
else -> throw IllegalArgumentException("Cannot multiply $this and $other")
|
||||
}
|
||||
}
|
10
src/prolog/ast/arithmetic/Number.kt
Normal file
10
src/prolog/ast/arithmetic/Number.kt
Normal file
|
@ -0,0 +1,10 @@
|
|||
package prolog.ast.arithmetic
|
||||
|
||||
interface Number: Expression {
|
||||
val value: kotlin.Number
|
||||
|
||||
fun plus(other: Number): Number
|
||||
fun minus(other: Number): Number
|
||||
fun times(other: Number): Number
|
||||
fun div(other: Number): Number
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package prolog.ast.terms
|
||||
|
||||
import prolog.Substitutions
|
||||
import prolog.ast.arithmetic.Expression
|
||||
import prolog.ast.arithmetic.Simplification
|
||||
|
||||
data class Integer(val value: Int): Expression {
|
||||
// Integers are already evaluated
|
||||
override fun simplify(subs: Substitutions): Simplification = Simplification(this, this)
|
||||
|
||||
override fun toString(): String {
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
operator fun plus(other: Integer): Integer {
|
||||
return Integer(value + other.value)
|
||||
}
|
||||
|
||||
operator fun minus(other: Integer): Integer {
|
||||
return Integer(value - other.value)
|
||||
}
|
||||
|
||||
operator fun times(other: Integer): Integer {
|
||||
return Integer(value * other.value)
|
||||
}
|
||||
|
||||
operator fun div(other: Integer): Integer {
|
||||
return Integer(value / other.value)
|
||||
}
|
||||
}
|
|
@ -5,8 +5,9 @@ import prolog.logic.compare
|
|||
/**
|
||||
* Value in Prolog.
|
||||
*
|
||||
* A [Term] is either a [Variable], [Atom], [Integer], float or [CompoundTerm].
|
||||
* In addition, SWI-Prolog also defines the type string.
|
||||
* A [Term] is either a [Variable], [Atom], [Integer][prolog.ast.arithmetic.Integer],
|
||||
* [Float][prolog.ast.arithmetic.Float] or [CompoundTerm].
|
||||
* In addition, SWI-Prolog also defines the type TODO string.
|
||||
*/
|
||||
interface Term : Comparable<Term> {
|
||||
override fun compareTo(other: Term): Int = compare(this, other, emptyMap())
|
||||
|
|
|
@ -4,6 +4,7 @@ import prolog.Answers
|
|||
import prolog.Substitutions
|
||||
import prolog.ast.arithmetic.ArithmeticOperator
|
||||
import prolog.ast.arithmetic.Expression
|
||||
import prolog.ast.arithmetic.Integer
|
||||
import prolog.ast.arithmetic.Simplification
|
||||
import prolog.ast.logic.Satisfiable
|
||||
import prolog.ast.terms.*
|
||||
|
@ -115,7 +116,6 @@ open class Subtract(private val expr1: Expression, private val expr2: Expression
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Expr * Expr
|
||||
/**
|
||||
* Result = Expr1 * Expr2
|
||||
*/
|
||||
|
@ -129,7 +129,15 @@ class Multiply(private val expr1: Expression, private val expr2: Expression) :
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Expr / Expr
|
||||
class Divide(private val expr1: Expression, private val expr2: Expression) :
|
||||
ArithmeticOperator(Atom("/"), expr1, expr2) {
|
||||
override fun simplify(subs: Substitutions): Simplification {
|
||||
val result = Variable("Result")
|
||||
val map = div(expr1, expr2, result, subs)
|
||||
val simplification = result.simplify(map.first().getOrThrow())
|
||||
return Simplification(this, simplification.to)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Expr mod Expr
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package prolog.logic
|
||||
|
||||
import prolog.Answers
|
||||
import prolog.Substitution
|
||||
import prolog.Substitutions
|
||||
import prolog.ast.arithmetic.Expression
|
||||
import prolog.ast.terms.Integer
|
||||
import prolog.ast.arithmetic.Integer
|
||||
import prolog.ast.terms.Term
|
||||
import prolog.ast.terms.Variable
|
||||
import prolog.ast.arithmetic.Number
|
||||
|
||||
/**
|
||||
* Low and High are integers, High ≥Low.
|
||||
|
@ -78,18 +78,24 @@ fun succ(term1: Expression, term2: Expression, subs: Substitutions): Answers {
|
|||
* At least two of the three arguments must be instantiated to integers.
|
||||
*/
|
||||
fun plus(term1: Expression, term2: Expression, term3: Expression, subs: Substitutions): Answers =
|
||||
operate(term1, term2, term3, subs, Integer::plus, Integer::minus)
|
||||
operate(term1, term2, term3, subs, Number::plus, Number::minus)
|
||||
|
||||
fun minus(term1: Expression, term2: Expression, term3: Expression, subs: Substitutions): Answers =
|
||||
operate(term1, term2, term3, subs, Number::minus, Number::plus)
|
||||
|
||||
fun mul(term1: Expression, term2: Expression, term3: Expression, subs: Substitutions): Answers =
|
||||
operate(term1, term2, term3, subs, Integer::times, Integer::div)
|
||||
operate(term1, term2, term3, subs, Number::times, Number::div)
|
||||
|
||||
fun div(term1: Expression, term2: Expression, term3: Expression, subs: Substitutions): Answers =
|
||||
operate(term1, term2, term3, subs, Number::div, Number::times)
|
||||
|
||||
fun operate(
|
||||
term1: Expression,
|
||||
term2: Expression,
|
||||
term3: Expression,
|
||||
subs: Substitutions,
|
||||
op: (Integer, Integer) -> Integer,
|
||||
inverseOp: (Integer, Integer) -> Integer
|
||||
op: (Number, Number) -> Number,
|
||||
inverseOp: (Number, Number) -> Number
|
||||
): Answers = sequence {
|
||||
val t1 = applySubstitution(term1, subs)
|
||||
val t2 = applySubstitution(term2, subs)
|
||||
|
@ -101,8 +107,8 @@ fun operate(
|
|||
val e2 = t2.simplify(subs)
|
||||
val e3 = t3.simplify(subs)
|
||||
|
||||
val int3Value = op(e1.to as Integer, e2.to as Integer)
|
||||
if (int3Value == e3.to as Integer) {
|
||||
val int3Value = op(e1.to as Number, e2.to as Number)
|
||||
if (equivalent(int3Value, e3.to, emptyMap())) {
|
||||
val opSubs: Substitutions = listOfNotNull(e1.mapped, e2.mapped, e3.mapped)
|
||||
.filter{ pair: Pair<Term, Term>? -> pair != null && !subs.contains(pair.first) }
|
||||
.toMap()
|
||||
|
@ -114,7 +120,7 @@ fun operate(
|
|||
val e1 = t1.simplify(subs)
|
||||
val e2 = t2.simplify(subs)
|
||||
|
||||
val int3Value = op(e1.to as Integer, e2.to as Integer)
|
||||
val int3Value = op(e1.to as Number, e2.to as Number)
|
||||
val int3 = t3 as Variable
|
||||
yield(Result.success(mapOf(int3 to int3Value) + listOfNotNull(e1.mapped, e2.mapped)))
|
||||
}
|
||||
|
@ -124,7 +130,7 @@ fun operate(
|
|||
val e = if (nonvariable(t1, subs)) t1.simplify(subs) else t2.simplify(subs)
|
||||
val e3 = t3.simplify(subs)
|
||||
|
||||
val value = inverseOp(e3.to as Integer, e.to as Integer)
|
||||
val value = inverseOp(e3.to as Number, e.to as Number)
|
||||
val int = t as Variable
|
||||
yield(Result.success(mapOf(int to value) + listOfNotNull(e.mapped, e3.mapped)))
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ import prolog.ast.arithmetic.Expression
|
|||
import prolog.ast.logic.LogicOperator
|
||||
import prolog.ast.terms.*
|
||||
import kotlin.NoSuchElementException
|
||||
import prolog.ast.arithmetic.Number
|
||||
import prolog.ast.arithmetic.Integer
|
||||
import prolog.ast.arithmetic.Float
|
||||
|
||||
// Apply substitutions to a term
|
||||
fun applySubstitution(term: Term, subs: Substitutions): Term = when {
|
||||
|
@ -101,6 +104,7 @@ fun equivalent(term1: Term, term2: Term, subs: Substitutions): Boolean {
|
|||
term1 is Atom && term2 is Atom -> compare(term1, term2, subs) == 0
|
||||
term1 is Structure && term2 is Structure -> compare(term1, term2, subs) == 0
|
||||
term1 is Integer && term2 is Integer -> compare(term1, term2, subs) == 0
|
||||
term1 is Number && term2 is Number -> compare(term1, term2, subs) == 0
|
||||
term1 is Variable && term2 is Variable -> term1 == term2
|
||||
term1 is Variable -> term1 in subs && equivalent(subs[term1]!!, term2, subs)
|
||||
term2 is Variable -> term2 in subs && equivalent(subs[term2]!!, term1, subs)
|
||||
|
@ -119,16 +123,17 @@ fun compare(term1: Term, term2: Term, subs: Substitutions): Int {
|
|||
is Variable -> {
|
||||
when (t2) {
|
||||
is Variable -> t1.name.compareTo(t2.name)
|
||||
is Integer -> -1
|
||||
is Number -> -1
|
||||
is Atom -> -1
|
||||
is Structure -> -1
|
||||
else -> throw IllegalArgumentException("Cannot compare $t1 with $t2")
|
||||
}
|
||||
}
|
||||
is Integer -> {
|
||||
is Number -> {
|
||||
when (t2) {
|
||||
is Variable -> 1
|
||||
is Integer -> t1.value.compareTo(t2.value)
|
||||
is Integer -> (t1.value as Int).compareTo(t2.value)
|
||||
is Float -> (t1.value as kotlin.Float).compareTo(t2.value)
|
||||
is Atom -> -1
|
||||
is Structure -> -1
|
||||
else -> throw IllegalArgumentException("Cannot compare $t1 with $t2")
|
||||
|
@ -137,7 +142,7 @@ fun compare(term1: Term, term2: Term, subs: Substitutions): Int {
|
|||
is Atom -> {
|
||||
when (t2) {
|
||||
is Variable -> 1
|
||||
is Integer -> 1
|
||||
is Number -> 1
|
||||
is Atom -> t1.name.compareTo(t2.name)
|
||||
is Structure -> -1
|
||||
else -> throw IllegalArgumentException("Cannot compare $t1 with $t2")
|
||||
|
@ -146,7 +151,7 @@ fun compare(term1: Term, term2: Term, subs: Substitutions): Int {
|
|||
is Structure -> {
|
||||
when (t2) {
|
||||
is Variable -> 1
|
||||
is Integer -> 1
|
||||
is Number -> 1
|
||||
is Atom -> 1
|
||||
is Structure -> {
|
||||
val arityComparison = t1.arguments.size.compareTo(t2.arguments.size)
|
||||
|
|
Reference in a new issue