diff --git a/lisa-utils/src/main/scala/lisa/utils/tactics/BasicStepTactic.scala b/lisa-utils/src/main/scala/lisa/utils/tactics/BasicStepTactic.scala index 9c02f590aaff348ac874ecf9bc3fc1a75e1211d6..23e9e616388828c9eb7c768bf202222b8be86b1b 100644 --- a/lisa-utils/src/main/scala/lisa/utils/tactics/BasicStepTactic.scala +++ b/lisa-utils/src/main/scala/lisa/utils/tactics/BasicStepTactic.scala @@ -12,13 +12,37 @@ import lisa.utils.tactics.ProofStepLib.{_, given} object BasicStepTactic { case object Hypothesis extends ProofStepWithoutBotNorPrem(0) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.Hypothesis(bot, bot.left.intersect(bot.right).head) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val intersectedPivot = bot.left.intersect(bot.right) + + if (premises.length != 0) + invalid(s"No premises expected, ${premises.length} received.") + else if (intersectedPivot.isEmpty) + invalid("A formula for input to hypothesis could not be inferred from left and right side of sequent.") + else + SC.Hypothesis(bot, intersectedPivot.head) + } } case object Rewrite extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.Rewrite(bot, premises(0)) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!SC.isSameSequent(bot, currentProof.getSequent(premises(0)))) + invalid("The premise and the conclusion are not trivially equivalent.") + else + SC.Rewrite(bot, premises(0)) + } } /** @@ -29,30 +53,54 @@ object BasicStepTactic { * </pre> */ case class Cut(phi: Formula) extends ProofStepWithoutBotNorPrem(2) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.Cut(bot, premises(0), premises(1), phi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val leftSequent = currentProof.getSequent(premises(0)) + lazy val rightSequent = currentProof.getSequent(premises(1)) + + if (premises.length != 2) + invalid(s"Two premises expected, ${premises.length} received.") + else if (!contains(leftSequent.right, phi)) + invalid("Right-hand side of first premise does not contain φ as claimed.") + else if (!contains(rightSequent.left, phi)) + invalid("Left-hand side of second premise does not contain φ as claimed.") + else if (!isSameSet(bot.left, leftSequent.left ++ rightSequent.left.filterNot(isSame(_, phi)))) + invalid("Left-hand side of conclusion + φ is not the union of the left-hand sides of the premises.") + else if (!isSameSet(bot.right, leftSequent.right.filterNot(isSame(_, phi)) ++ rightSequent.right)) + invalid("Right-hand side of conclusion + φ is not the union of the right-hand sides of the premises.") + else + SC.Cut(bot, premises(0), premises(1), phi) + } } case object CutWithoutFormula extends ProofStepWithoutBotNorPrem(2) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val leftSequent = currentProof.getSequent(premises(0)) - val rightSequent = currentProof.getSequent(premises(1)) - val cutSet = rightSequent.left.diff(bot.left) ++ leftSequent.right.diff(bot.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val leftSequent = currentProof.getSequent(premises(0)) + lazy val rightSequent = currentProof.getSequent(premises(1)) + lazy val cutSet = rightSequent.left.diff(bot.left) ++ leftSequent.right.diff(bot.right) lazy val intersectedCutSet = rightSequent.left & leftSequent.right - if (!cutSet.isEmpty) - if (cutSet.tail.isEmpty) { - SC.Cut(bot, premises(0), premises(1), cutSet.head) - } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Inferred cut pivot is not a singleton set.") + if (premises.length != 2) + invalid(s"Two premises expected, ${premises.length} received.") + else if (!cutSet.isEmpty) + if (cutSet.tail.isEmpty) + Cut(cutSet.head).asSCProof(bot, premises, currentProof) + else + invalid("Inferred cut pivot is not a singleton set.") else if (!intersectedCutSet.isEmpty && intersectedCutSet.tail.isEmpty) // can still find a pivot - SC.Cut(bot, premises(0), premises(1), intersectedCutSet.head) + Cut(intersectedCutSet.head).asSCProof(bot, premises, currentProof) else - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "A consistent cut pivot cannot be inferred from the premises. Possibly a missing or extraneous clause." - ) + invalid("A consistent cut pivot cannot be inferred from the premises. Possibly a missing or extraneous clause.") } } @@ -76,33 +124,57 @@ object BasicStepTactic { * </pre> */ case class LeftAnd(phi: Formula, psi: Formula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftAnd(bot, premises(0), phi, psi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val phiAndPsi = ConnectorFormula(And, Seq(phi, psi)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of the conclusion is not the same as the right-hand side of the premise.") + else if ( + !isSameSet(bot.left + phi, premiseSequent.left + phiAndPsi) && + !isSameSet(bot.left + psi, premiseSequent.left + phiAndPsi) && + !isSameSet(bot.left + phi + psi, premiseSequent.left + phiAndPsi) + ) + invalid("Left-hand side of premise + φ∧ψ is not the same as left-hand side of conclusion + either φ, ψ or both.") + else + SC.LeftAnd(bot, premises(0), phi, psi) + } } case object LeftAndWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.left.diff(premiseSequent.left) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.left.diff(premiseSequent.left) - if (!pivot.isEmpty && pivot.tail.isEmpty) + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!pivot.isEmpty && pivot.tail.isEmpty) pivot.head match { case ConnectorFormula(And, Seq(phi, psi)) => if (premiseSequent.left.contains(phi)) - SC.LeftAnd(bot, premises(0), phi, psi) + LeftAnd(phi, psi).asSCProof(bot, premises, currentProof) else - SC.LeftAnd(bot, premises(0), psi, phi) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer a conjunction as pivot from premise and conclusion.") + LeftAnd(psi, phi).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer a conjunction as pivot from premise and conclusion.") } else // try a rewrite, if it works, go ahead with it, otherwise malformed if (SC.isSameSequent(premiseSequent, bot)) - SC.Rewrite(bot, premises(0)) + Rewrite.asSCProof(bot, premises, currentProof) else - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Left-hand side of conclusion + φ∧ψ must be same as left-hand side of premise + either φ, ψ or both." - ) + invalid("Left-hand side of premise + φ∧ψ is not the same as left-hand side of conclusion + either φ, ψ or both.") } } @@ -125,31 +197,50 @@ object BasicStepTactic { */ case class LeftOr(disjuncts: Seq[Formula]) extends ProofStepWithoutBotNorPrem(-1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - SC.LeftOr(bot, premises, disjuncts) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequents = premises.map(currentProof.getSequent(_)) + lazy val disjunction = ConnectorFormula(Or, disjuncts) + + if (premises.length == 0) + invalid(s"Premises expected, ${premises.length} received.") + else if (!isSameSet(bot.right, premiseSequents.map(_.right).reduce(_ union _))) + invalid("Right-hand side of conclusion is not the union of the right-hand sides of the premises.") + else if (!isSameSet(disjuncts.foldLeft(bot.left)(_ + _), premiseSequents.map(_.left).reduce(_ union _) + disjunction)) + invalid("Left-hand side of conclusion + disjuncts is not the same as the union of the left-hand sides of the premises + φ∨ψ.") + else + SC.LeftOr(bot, premises, disjuncts) } } - case class LeftOrWithoutFormula() extends ProofStepWithoutBotNorPrem(-1) { + case object LeftOrWithoutFormula extends ProofStepWithoutBotNorPrem(-1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequents = premises.map(currentProof.getSequent(_)) - val pivots = premiseSequents.map(_.left.diff(bot.left)) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if (pivots.exists(_.isEmpty)) - SC.Weakening(bot, premises(pivots.indexWhere(_.isEmpty))) + lazy val premiseSequents = premises.map(currentProof.getSequent(_)) + lazy val pivots = premiseSequents.map(_.left.diff(bot.left)) + + if (premises.length == 0) + invalid(s"Premises expected, ${premises.length} received.") + else if (pivots.exists(_.isEmpty)) + Weakening.asSCProof(bot, Seq(premises(pivots.indexWhere(_.isEmpty))), currentProof) else if (pivots.forall(_.tail.isEmpty)) - SC.LeftOr(bot, premises, pivots.map(_.head)) + LeftOr(pivots.map(_.head)).asSCProof(bot, premises, currentProof) else // some extraneous formulae - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Left-hand side of conclusion + disjuncts is not the same as the union of the left-hand sides of the premises + φ∨ψ." - ) + invalid("Left-hand side of conclusion + disjuncts is not the same as the union of the left-hand sides of the premises + φ∨ψ.") } } case object LeftOr extends ProofStepWithoutBotNorPrem(-1) { // default construction: // def apply(disjuncts: Seq[Formula]) = new LeftOr(disjuncts) - def apply() = new LeftOrWithoutFormula() + def apply() = LeftOrWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -164,28 +255,49 @@ object BasicStepTactic { * </pre> */ case class LeftImplies(phi: Formula, psi: Formula) extends ProofStepWithoutBotNorPrem(2) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftImplies(bot, premises(0), premises(1), phi, psi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val leftSequent = currentProof.getSequent(premises(0)) + lazy val rightSequent = currentProof.getSequent(premises(1)) + lazy val implication = ConnectorFormula(Implies, Seq(phi, psi)) + + if (premises.length != 2) + invalid(s"Two premises expected, ${premises.length} received.") + else if (!isSameSet(bot.right + phi, leftSequent.right union rightSequent.right)) + invalid("Right-hand side of conclusion + φ is not the union of right-hand sides of premises.") + else if (!isSameSet(bot.left + psi, leftSequent.left union rightSequent.left + implication)) + invalid("Left-hand side of conclusion + ψ is not the union of left-hand sides of premises + φ→ψ.") + else + SC.LeftImplies(bot, premises(0), premises(1), phi, psi) + } } case object LeftImpliesWithoutFormula extends ProofStepWithoutBotNorPrem(2) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val leftSequent = currentProof.getSequent(premises(0)) - val rightSequent = currentProof.getSequent(premises(1)) - val pivotLeft = leftSequent.right.diff(bot.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val leftSequent = currentProof.getSequent(premises(0)) + lazy val rightSequent = currentProof.getSequent(premises(1)) + lazy val pivotLeft = leftSequent.right.diff(bot.right) lazy val pivotRight = rightSequent.left.diff(bot.left) - if (pivotLeft.isEmpty) - SC.Weakening(bot, premises(0)) + if (premises.length != 2) + invalid(s"Two premises expected, ${premises.length} received.") + else if (pivotLeft.isEmpty) + Weakening.asSCProof(bot, premises, currentProof) else if (pivotRight.isEmpty) - SC.Weakening(bot, premises(1)) + Weakening.asSCProof(bot, premises, currentProof) else if (pivotLeft.tail.isEmpty && pivotRight.tail.isEmpty) - SC.LeftImplies(bot, premises(0), premises(1), pivotLeft.head, pivotRight.head) + LeftImplies(pivotLeft.head, pivotRight.head).asSCProof(bot, premises, currentProof) else - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Could not infer an implication as a pivot from the premises and conclusion, possible extraneous formulae in premises." - ) + invalid("Could not infer an implication as a pivot from the premises and conclusion, possible extraneous formulae in premises.") } } @@ -207,21 +319,50 @@ object BasicStepTactic { * </pre> */ case class LeftIff(phi: Formula, psi: Formula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftIff(bot, premises(0), phi, psi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val implication = ConnectorFormula(Iff, Seq(phi, psi)) + lazy val impLeft = ConnectorFormula(Implies, Seq(phi, psi)) + lazy val impRight = ConnectorFormula(Implies, Seq(psi, phi)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of premise is not the same as right-hand side of conclusion.") + else if ( + !isSameSet(bot.left + impLeft, premiseSequent.left + implication) && + !isSameSet(bot.left + impRight, premiseSequent.left + implication) && + !isSameSet(bot.left + impLeft + impRight, premiseSequent.left + implication) + ) + invalid("Left-hand side of premise + φ↔ψ is not the same as left-hand side of conclusion + either φ→ψ, ψ→φ or both.") + else + SC.LeftIff(bot, premises(0), phi, psi) + } } - case class LeftIffWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object LeftIffWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = premiseSequent.left.diff(bot.left) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if (pivot.isEmpty) - SC.Weakening(bot, premises(0)) + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = premiseSequent.left.diff(bot.left) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (pivot.isEmpty) + Weakening.asSCProof(bot, premises, currentProof) else pivot.head match { - case ConnectorFormula(Implies, Seq(phi, psi)) => SC.LeftIff(bot, premises(0), phi, psi) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer a pivot implication from premise.") + case ConnectorFormula(Implies, Seq(phi, psi)) => LeftIff(phi, psi).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer a pivot implication from premise.") } } } @@ -229,7 +370,7 @@ object BasicStepTactic { case object LeftIff extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula, psi: Formula) = new LeftIff(phi, psi) - def apply() = new LeftIffWithoutFormula() + def apply() = LeftIffWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -244,21 +385,44 @@ object BasicStepTactic { * </pre> */ case class LeftNot(phi: Formula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftNot(bot, premises(0), phi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val negation = ConnectorFormula(Neg, Seq(phi)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.right + phi, premiseSequent.right)) + invalid("Right-hand side of conclusion + φ is not the same as right-hand side of premise.") + else if (!isSameSet(bot.left, premiseSequent.left + negation)) + invalid("Left-hand side of conclusion is not the same as left-hand side of premise + ¬φ.") + else + SC.LeftNot(bot, premises(0), phi) + } } - case class LeftNotWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object LeftNotWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = premiseSequent.right.diff(bot.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if (pivot.isEmpty) - SC.Weakening(bot, premises(0)) + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = premiseSequent.right.diff(bot.right) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (pivot.isEmpty) + Weakening.asSCProof(bot, premises, currentProof) else if (pivot.tail.isEmpty) - SC.LeftNot(bot, premises(0), pivot.head) + LeftNot(pivot.head).asSCProof(bot, premises, currentProof) else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Right-hand side of conclusion + φ must be the same as right-hand side of premise.") + invalid("Right-hand side of conclusion + φ is not the same as right-hand side of premise.") } } @@ -266,7 +430,7 @@ object BasicStepTactic { case object LeftNot extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula) = new LeftNot(phi) - def apply() = new LeftNotWithoutFormula() + def apply() = LeftNotWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -282,25 +446,48 @@ object BasicStepTactic { * </pre> */ case class LeftForall(phi: Formula, x: VariableLabel, t: Term) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftForall(bot, premises(0), phi, x, t) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val quantified = BinderFormula(Forall, x, phi) + lazy val instantiated = substituteVariables(phi, Map(x -> t)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of conclusion is not the same as right-hand side of premise") + else if (!isSameSet(bot.left + instantiated, premiseSequent.left + quantified)) + invalid("Left-hand side of conclusion + φ[t/x] is not the same as left-hand side of premise + ∀x. φ") + else + SC.LeftForall(bot, premises(0), phi, x, t) + } } case class LeftForallWithoutFormula(t: Term) extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.left.diff(premiseSequent.left) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.left.diff(premiseSequent.left) lazy val instantiatedPivot = premiseSequent.left.diff(bot.left) - if (!pivot.isEmpty) + if (premises.length != 1) invalid(s"One premise expected, ${premises.length} received.") + else if (!pivot.isEmpty) if (pivot.tail.isEmpty) pivot.head match { - case BinderFormula(Forall, x, phi) => SC.LeftForall(bot, premises(0), phi, x, t) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer a universally quantified pivot from premise and conclusion.") + case BinderFormula(Forall, x, phi) => LeftForall(phi, x, t).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer a universally quantified pivot from premise and conclusion.") } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Left-hand side of conclusion + φ[t/x] must be the same as left-hand side of premise + ∀x. φ.") - else if (instantiatedPivot.isEmpty) SC.Weakening(bot, premises(0)) + invalid("Left-hand side of conclusion + φ[t/x] is not the same as left-hand side of premise + ∀x. φ.") + else if (instantiatedPivot.isEmpty) Weakening.asSCProof(bot, premises, currentProof) else if (instantiatedPivot.tail.isEmpty) { // go through conclusion to find a matching quantified formula @@ -313,10 +500,10 @@ object BasicStepTactic { ) quantifiedPhi match { - case Some(BinderFormula(Forall, x, phi)) => SC.LeftForall(bot, premises(0), phi, x, t) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer a universally quantified pivot from premise and conclusion.") + case Some(BinderFormula(Forall, x, phi)) => LeftForall(phi, x, t).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer a universally quantified pivot from premise and conclusion.") } - } else ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Left-hand side of conclusion + φ[t/x] must be the same as left-hand side of premise + ∀x. φ.") + } else invalid("Left-hand side of conclusion + φ[t/x] is not the same as left-hand side of premise + ∀x. φ.") } } @@ -343,18 +530,43 @@ object BasicStepTactic { * </pre> */ case class LeftExists(phi: Formula, x: VariableLabel) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftExists(bot, premises(0), phi, x) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val quantified = BinderFormula(Exists, x, phi) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if ((bot.left union bot.right).exists(_.freeVariables.contains(x))) + invalid("The variable x must not be free in the resulting sequent.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of conclusion is not the same as right-hand side of premise") + else if (!isSameSet(bot.left + phi, premiseSequent.left + quantified)) + invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise + ∃x. φ") + else + SC.LeftExists(bot, premises(0), phi, x) + } } - case class LeftExistsWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object LeftExistsWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.left.diff(premiseSequent.left) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.left.diff(premiseSequent.left) lazy val instantiatedPivot = premiseSequent.left.diff(bot.left) - if (pivot.isEmpty) - if (instantiatedPivot.isEmpty) SC.Rewrite(bot, premises(0)) + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (pivot.isEmpty) + if (instantiatedPivot.isEmpty) Rewrite.asSCProof(bot, premises, currentProof) else if (instantiatedPivot.tail.isEmpty) { val in: Formula = instantiatedPivot.head val quantifiedPhi: Option[Formula] = bot.left.find(f => @@ -365,24 +577,24 @@ object BasicStepTactic { ) quantifiedPhi match { - case Some(BinderFormula(Exists, x, phi)) => SC.LeftExists(bot, premises(0), phi, x) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existensially quantified pivot from premise and conclusion.") + case Some(BinderFormula(Exists, x, phi)) => LeftExists(phi, x).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an existensially quantified pivot from premise and conclusion.") } - } else ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Left-hand side of conclusion + φ must be the same as left-hand side of premise + ∃x. φ.") + } else invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise + ∃x. φ.") else if (pivot.tail.isEmpty) pivot.head match { - case BinderFormula(Exists, x, phi) => SC.LeftExists(bot, premises(0), phi, x) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existentially quantified pivot from premise and conclusion.") + case BinderFormula(Exists, x, phi) => LeftExists(phi, x).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an existentially quantified pivot from premise and conclusion.") } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Left-hand side of conclusion + φ must be the same as left-hand side of premise + ∃x. φ.") + invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise + ∃x. φ.") } } case object LeftExists extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula, x: VariableLabel) = new LeftExists(phi, x) - def apply() = new LeftExistsWithoutFormula() + def apply() = LeftExistsWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -397,41 +609,66 @@ object BasicStepTactic { * </pre> */ case class LeftExistsOne(phi: Formula, x: VariableLabel) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftExistsOne(bot, premises(0), phi, x) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val y = VariableLabel(freshId(phi.freeVariables.map(_.id) + x.id, "y")) + lazy val instantiated = BinderFormula(Exists, y, BinderFormula(Forall, x, ConnectorFormula(Iff, List(PredicateFormula(equality, List(VariableTerm(x), VariableTerm(y))), phi)))) + lazy val quantified = BinderFormula(ExistsOne, x, phi) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of conclusion is not the same as right-hand side of premise.") + else if (!isSameSet(bot.left + instantiated, premiseSequent.left + quantified)) + invalid("Left-hand side of conclusion + ∃y.∀x. (x=y) ↔ φ is not the same as left-hand side of premise + ∃!x. φ.") + else + SC.LeftExistsOne(bot, premises(0), phi, x) + } } - case class LeftExistsOneWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object LeftExistsOneWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.left.diff(premiseSequent.left) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.left.diff(premiseSequent.left) lazy val instantiatedPivot = premiseSequent.left.diff(bot.left) - if (pivot.isEmpty) + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (pivot.isEmpty) if (instantiatedPivot.isEmpty) - SC.Rewrite(bot, premises(0)) + Rewrite.asSCProof(bot, premises, currentProof) else if (instantiatedPivot.tail.isEmpty) { instantiatedPivot.head match { // ∃_. ∀x. _ ↔ φ == extract ==> x, phi - case BinderFormula(Exists, _, BinderFormula(Forall, x, ConnectorFormula(Iff, Seq(_, phi)))) => SC.LeftExistsOne(bot, premises(0), phi, x) + case BinderFormula(Exists, _, BinderFormula(Forall, x, ConnectorFormula(Iff, Seq(_, phi)))) => LeftExistsOne(phi, x).asSCProof(bot, premises, currentProof) case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existentially quantified pivot from premise and conclusion.") } } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Left-hand side of conclusion + φ must be the same as left-hand side of premise + ∃x. φ.") + invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise + ∃x. φ.") else if (pivot.tail.isEmpty) pivot.head match { case BinderFormula(ExistsOne, x, phi) => SC.LeftExistsOne(bot, premises(0), phi, x) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existentially quantified pivot from premise and conclusion.") + case _ => invalid("Could not infer an existentially quantified pivot from premise and conclusion.") } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Left-hand side of conclusion + φ must be the same as left-hand side of premise + ∃x. φ.") + invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise + ∃x. φ.") } } case object LeftExistsOne extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula, x: VariableLabel) = new LeftExistsOne(phi, x) - def apply() = new LeftExistsOneWithoutFormula() + def apply() = LeftExistsOneWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -446,25 +683,45 @@ object BasicStepTactic { * Γ, Σ |- φ∧ψ∧..., Π, Δ * </pre> */ - case class RightAnd(cunjuncts: Seq[Formula]) extends ProofStepWithoutBotNorPrem(-1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightAnd(bot, premises, cunjuncts) + case class RightAnd(conjuncts: Seq[Formula]) extends ProofStepWithoutBotNorPrem(-1) { + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequents = premises.map(currentProof.getSequent(_)) + lazy val conjunction = ConnectorFormula(And, conjuncts) + + if (premises.length == 0) + invalid(s"Premises expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequents.map(_.left).reduce(_ union _))) + invalid("Left-hand side of conclusion is not the union of the left-hand sides of the premises.") + else if (!isSameSet(conjuncts.foldLeft(bot.right)(_ + _), premiseSequents.map(_.right).reduce(_ union _) + conjunction)) + invalid("Right-hand side of conclusion + conjuncts is not the same as the union of the right-hand sides of the premises + φ∧ψ....") + else + SC.RightAnd(bot, premises, conjuncts) + } } case object RightAndWithoutFormula extends ProofStepWithoutBotNorPrem(-1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequents = premises.map(currentProof.getSequent(_)) - val pivots = premiseSequents.map(_.right.diff(bot.right)) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if (pivots.exists(_.isEmpty)) - SC.Weakening(bot, premises(pivots.indexWhere(_.isEmpty))) + lazy val premiseSequents = premises.map(currentProof.getSequent(_)) + lazy val pivots = premiseSequents.map(_.right.diff(bot.right)) + + if (premises.length == 0) + invalid(s"Premises expected, ${premises.length} received.") + else if (pivots.exists(_.isEmpty)) + Weakening.asSCProof(bot, Seq(premises(pivots.indexWhere(_.isEmpty))), currentProof) else if (pivots.forall(_.tail.isEmpty)) - SC.RightAnd(bot, premises, pivots.map(_.head)) + RightAnd(pivots.map(_.head)).asSCProof(bot, premises, currentProof) else // some extraneous formulae - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Right-hand side of conclusion + φ + ψ is not the same as the union of the right-hand sides of the premises +φ∧ψ." - ) + invalid("Right-hand side of conclusion + φ + ψ is not the same as the union of the right-hand sides of the premises +φ∧ψ.") } } @@ -486,40 +743,64 @@ object BasicStepTactic { * </pre> */ case class RightOr(phi: Formula, psi: Formula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightOr(bot, premises(0), phi, psi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val phiAndPsi = ConnectorFormula(Or, Seq(phi, psi)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequent.left)) + invalid("Left-hand side of the premise is not the same as the left-hand side of the conclusion.") + else if ( + !isSameSet(bot.right + phi, premiseSequent.right + phiAndPsi) && + !isSameSet(bot.right + psi, premiseSequent.right + phiAndPsi) && + !isSameSet(bot.right + phi + psi, premiseSequent.right + phiAndPsi) + ) + invalid("Right-hand side of premise + φ∧ψ is not the same as right-hand side of conclusion + either φ, ψ or both.") + else + SC.RightOr(bot, premises(0), phi, psi) + } } - case class RightOrWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object RightOrWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.right.diff(premiseSequent.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if (!pivot.isEmpty && pivot.tail.isEmpty) + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.right.diff(premiseSequent.right) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!pivot.isEmpty && pivot.tail.isEmpty) pivot.head match { case ConnectorFormula(Or, Seq(phi, psi)) => if (premiseSequent.left.contains(phi)) - SC.RightOr(bot, premises(0), phi, psi) + RightOr(phi, psi).asSCProof(bot, premises, currentProof) else - SC.RightOr(bot, premises(0), psi, phi) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer a disjunction as pivot from premise and conclusion.") + RightOr(psi, phi).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer a disjunction as pivot from premise and conclusion.") } else // try a rewrite, if it works, go ahead with it, otherwise malformed if (SC.isSameSequent(premiseSequent, bot)) - SC.Rewrite(bot, premises(0)) + Rewrite.asSCProof(bot, premises, currentProof) else - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Right-hand side of conclusion + φ∧ψ must be same as right-hand side of premise + either φ, ψ or both." - ) + invalid("Right-hand side of conclusion + φ∧ψ is not the same as right-hand side of premise + either φ, ψ or both.") } } case object RightOr extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula, psi: Formula) = new RightOr(phi, psi) - def apply() = new RightOrWithoutFormula() + def apply() = RightOrWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -534,30 +815,53 @@ object BasicStepTactic { * </pre> */ case class RightImplies(phi: Formula, psi: Formula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightImplies(bot, premises(0), phi, psi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val implication = ConnectorFormula(Implies, Seq(phi, psi)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left + phi, premiseSequent.left)) + invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise.") + else if (!isSameSet(bot.right + psi, premiseSequent.right + implication)) + invalid("Right-hand side of conclusion + ψ is not the same as right-hand side of premise + φ→ψ.") + else + SC.RightImplies(bot, premises(0), phi, psi) + } } - case class RightImpliesWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object RightImpliesWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val leftPivot = premiseSequent.left.diff(bot.left) - val rightPivot = premiseSequent.right.diff(bot.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if ( + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val leftPivot = premiseSequent.left.diff(bot.left) + lazy val rightPivot = premiseSequent.right.diff(bot.right) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if ( !leftPivot.isEmpty && leftPivot.tail.isEmpty && !rightPivot.isEmpty && rightPivot.tail.isEmpty ) - SC.RightImplies(bot, premises(0), leftPivot.head, rightPivot.head) + RightImplies(leftPivot.head, rightPivot.head).asSCProof(bot, premises, currentProof) else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Right-hand side of conclusion + ψ must be same as right-hand side of premise + φ→ψ.") + invalid("Right-hand side of conclusion + ψ is not the same as right-hand side of premise + φ→ψ.") } } case object RightImplies extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula, psi: Formula) = new RightImplies(phi, psi) - def apply() = new RightImpliesWithoutFormula() + def apply() = RightImpliesWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -572,34 +876,57 @@ object BasicStepTactic { * </pre> */ case class RightIff(phi: Formula, psi: Formula) extends ProofStepWithoutBotNorPrem(2) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightIff(bot, premises(0), premises(1), phi, psi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val leftSequent = currentProof.getSequent(premises(0)) + lazy val rightSequent = currentProof.getSequent(premises(1)) + lazy val implication = ConnectorFormula(Iff, Seq(phi, psi)) + lazy val impLeft = ConnectorFormula(Implies, Seq(phi, psi)) + lazy val impRight = ConnectorFormula(Implies, Seq(psi, phi)) + + if (premises.length != 2) + invalid(s"Two premises expected, ${premises.length} received.") + else if (!isSameSet(bot.left, leftSequent.left union rightSequent.left)) + invalid("Left-hand side of conclusion is not the union of the left-hand sides of the premises.") + else if (!isSameSet(bot.right + impLeft + impRight, leftSequent.right union rightSequent.right + implication)) + invalid("Right-hand side of conclusion + φ→ψ + ψ→φ is not the same as the union of the right-hand sides of the premises + φ↔ψ.") + else + SC.RightIff(bot, premises(0), premises(1), phi, psi) + } } - case class RightIffWithoutFormula() extends ProofStepWithoutBotNorPrem(2) { + case object RightIffWithoutFormula extends ProofStepWithoutBotNorPrem(2) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = premiseSequent.right.diff(bot.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if (pivot.isEmpty) - SC.Weakening(bot, premises(0)) + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = premiseSequent.right.diff(bot.right) + + if (premises.length != 2) + invalid(s"Two premises expected, ${premises.length} received.") + else if (pivot.isEmpty) + Weakening.asSCProof(bot, Seq(premises(0)), currentProof) else if (pivot.tail.isEmpty) pivot.head match { - case ConnectorFormula(Implies, Seq(phi, psi)) => SC.RightIff(bot, premises(0), premises(1), phi, psi) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an implication as pivot from premise and conclusion.") + case ConnectorFormula(Implies, Seq(phi, psi)) => RightIff(phi, psi).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an implication as pivot from premise and conclusion.") } else - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Right-hand side of conclusion + φ→ψ + ψ→φ is not the same as the union of the right-hand sides of the premises φ↔ψ." - ) + invalid("Right-hand side of conclusion + φ→ψ + ψ→φ is not the same as the union of the right-hand sides of the premises φ↔ψ.") } } case object RightIff extends ProofStepWithoutBotNorPrem(2) { // default construction: // def apply(phi: Formula, psi: Formula) = new RightIff(phi, psi) - def apply() = new RightIffWithoutFormula() + def apply() = RightIffWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -608,27 +935,50 @@ object BasicStepTactic { /** * <pre> - * Γ, φ |- Δ + * Γ, φ |- Δ * -------------- * Γ |- ¬φ, Δ * </pre> */ case class RightNot(phi: Formula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightNot(bot, premises(0), phi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val negation = ConnectorFormula(Neg, Seq(phi)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left + phi, premiseSequent.left)) + invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise.") + else if (!isSameSet(bot.right, premiseSequent.right + negation)) + invalid("Right-hand side of conclusion is not the same as right-hand side of premise + ¬φ.") + else + SC.RightNot(bot, premises(0), phi) + } } - case class RightNotWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object RightNotWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = premiseSequent.left.diff(bot.left) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) - if (pivot.isEmpty) - SC.Weakening(bot, premises(0)) + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = premiseSequent.left.diff(bot.left) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (pivot.isEmpty) + Weakening.asSCProof(bot, Seq(premises(0)), currentProof) else if (pivot.tail.isEmpty) - SC.RightNot(bot, premises(0), pivot.head) + RightNot(pivot.head).asSCProof(bot, premises, currentProof) else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Left-hand side of conclusion + φ must be the same as left-hand side of premise.") + invalid("Left-hand side of conclusion + φ is not the same as left-hand side of premise.") } } @@ -636,7 +986,7 @@ object BasicStepTactic { case object RightNot extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula) = new RightNot(phi) - def apply() = new RightNotWithoutFormula() + def apply() = RightNotWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -651,18 +1001,43 @@ object BasicStepTactic { * </pre> */ case class RightForall(phi: Formula, x: VariableLabel) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightForall(bot, premises(0), phi, x) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val quantified = BinderFormula(Forall, x, phi) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if ((bot.left union bot.right).exists(_.freeVariables.contains(x))) + invalid("The variable x is free in the resulting sequent.") + else if (!isSameSet(bot.left, premiseSequent.left)) + invalid("Left-hand side of conclusion is not the same as left-hand side of premise.") + else if (!isSameSet(bot.right + phi, premiseSequent.right + quantified)) + invalid("Right-hand side of conclusion + φ is not the same as right-hand side of premise + ∀x. φ.") + else + SC.RightForall(bot, premises(0), phi, x) + } } - case class RightForallWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object RightForallWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.right.diff(premiseSequent.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.right.diff(premiseSequent.right) lazy val instantiatedPivot = premiseSequent.right.diff(bot.right) - if (pivot.isEmpty) - if (instantiatedPivot.isEmpty) SC.Rewrite(bot, premises(0)) + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (pivot.isEmpty) + if (instantiatedPivot.isEmpty) Rewrite.asSCProof(bot, Seq(premises(0)), currentProof) else if (instantiatedPivot.tail.isEmpty) { val in: Formula = instantiatedPivot.head val quantifiedPhi: Option[Formula] = bot.right.find(f => @@ -673,24 +1048,24 @@ object BasicStepTactic { ) quantifiedPhi match { - case Some(BinderFormula(Forall, x, phi)) => SC.RightForall(bot, premises(0), phi, x) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer a universally quantified pivot from premise and conclusion.") + case Some(BinderFormula(Forall, x, phi)) => RightForall(phi, x).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer a universally quantified pivot from premise and conclusion.") } - } else ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Right-hand side of conclusion + φ must be the same as right-hand side of premise + ∃x. φ.") + } else invalid("Right-hand side of conclusion + φ is not the same as right-hand side of premise + ∃x. φ.") else if (pivot.tail.isEmpty) pivot.head match { - case BinderFormula(Forall, x, phi) => SC.RightForall(bot, premises(0), phi, x) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer a universally quantified pivot from premise and conclusion.") + case BinderFormula(Forall, x, phi) => RightForall(phi, x).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer a universally quantified pivot from premise and conclusion.") } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Right-hand side of conclusion + φ must be the same as right-hand side of premise + ∃x. φ.") + invalid("Right-hand side of conclusion + φ is not the same as right-hand side of premise + ∃x. φ.") } } case object RightForall extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula, x: VariableLabel) = new RightForall(phi, x) - def apply() = new RightForallWithoutFormula() + def apply() = RightForallWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -703,32 +1078,51 @@ object BasicStepTactic { * ------------------- * Γ |- ∃x. φ, Δ * - * (ln-x stands for locally nameless x) * </pre> */ case class RightExists(phi: Formula, x: VariableLabel, t: Term) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightExists(bot, premises(0), phi, x, t) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val quantified = BinderFormula(Exists, x, phi) + lazy val instantiated = substituteVariables(phi, Map(x -> t)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequent.left)) + invalid("Left-hand side of conclusion is not the same as left-hand side of premise") + else if (!isSameSet(bot.right + instantiated, premiseSequent.right + quantified)) + invalid("Right-hand side of conclusion + φ[t/x] is not the same as right-hand side of premise + ∃x. φ") + else + SC.RightExists(bot, premises(0), phi, x, t) + } } case class RightExistsWithoutFormula(t: Term) extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.right.diff(premiseSequent.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.right.diff(premiseSequent.right) lazy val instantiatedPivot = premiseSequent.right.diff(bot.right) - if (!pivot.isEmpty) + if (premises.length != 1) invalid(s"One premise expected, ${premises.length} received.") + else if (!pivot.isEmpty) if (pivot.tail.isEmpty) pivot.head match { - case BinderFormula(Exists, x, phi) => SC.RightExists(bot, premises(0), phi, x, t) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existentially quantified pivot from premise and conclusion.") + case BinderFormula(Exists, x, phi) => RightExists(phi, x, t).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an existentially quantified pivot from premise and conclusion.") } else - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Right-hand side of conclusion + φ[t/x] must be the same as right-hand side of premise + ∀x. φ." - ) - else if (instantiatedPivot.isEmpty) SC.Weakening(bot, premises(0)) + invalid("Right-hand side of conclusion + φ[t/x] is not the same as right-hand side of premise + ∀x. φ.") + else if (instantiatedPivot.isEmpty) Weakening.asSCProof(bot, premises, currentProof) else if (instantiatedPivot.tail.isEmpty) { // go through conclusion to find a matching quantified formula @@ -741,14 +1135,10 @@ object BasicStepTactic { ) quantifiedPhi match { - case Some(BinderFormula(Exists, x, phi)) => SC.RightExists(bot, premises(0), phi, x, t) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existentially quantified pivot from premise and conclusion.") + case Some(BinderFormula(Exists, x, phi)) => RightExists(phi, x, t).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an existentially quantified pivot from premise and conclusion.") } - } else - ProofStepJudgement.InvalidProofStep( - this.asProofStepWithoutBot(premises).asProofStep(bot), - "Right-hand side of conclusion + φ[t/x] must be the same as right-hand side of premise + ∀x. φ." - ) + } else invalid("Right-hand side of conclusion + φ[t/x] is not the same as right-hand side of premise + ∀x. φ.") } } @@ -774,41 +1164,66 @@ object BasicStepTactic { * </pre> */ case class RightExistsOne(phi: Formula, x: VariableLabel) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightExistsOne(bot, premises(0), phi, x) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val y = VariableLabel(freshId(phi.freeVariables.map(_.id) + x.id, "y")) + lazy val instantiated = BinderFormula(Exists, y, BinderFormula(Forall, x, ConnectorFormula(Iff, List(PredicateFormula(equality, List(VariableTerm(x), VariableTerm(y))), phi)))) + lazy val quantified = BinderFormula(ExistsOne, x, phi) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequent.left)) + invalid("Left-hand side of conclusion is not the same as left-hand side of premise.") + else if (!isSameSet(bot.right + instantiated, premiseSequent.right + quantified)) + invalid("Right-hand side of conclusion + ∃y.∀x. (x=y) ↔ φ is not the same as right-hand side of premise + ∃!x. φ.") + else + SC.RightExistsOne(bot, premises(0), phi, x) + } } - case class RightExistsOneWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { + case object RightExistsOneWithoutFormula extends ProofStepWithoutBotNorPrem(1) { def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { - val premiseSequent = currentProof.getSequent(premises(0)) - val pivot = bot.right.diff(premiseSequent.right) + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = bot.right.diff(premiseSequent.right) lazy val instantiatedPivot = premiseSequent.right.diff(bot.right) - if (pivot.isEmpty) + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (pivot.isEmpty) if (instantiatedPivot.isEmpty) - SC.Rewrite(bot, premises(0)) + Rewrite.asSCProof(bot, premises, currentProof) else if (instantiatedPivot.tail.isEmpty) { instantiatedPivot.head match { // ∃_. ∀x. _ ↔ φ == extract ==> x, phi - case BinderFormula(Exists, _, BinderFormula(Forall, x, ConnectorFormula(Iff, Seq(_, phi)))) => SC.RightExistsOne(bot, premises(0), phi, x) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existentially quantified pivot from premise and conclusion.") + case BinderFormula(Exists, _, BinderFormula(Forall, x, ConnectorFormula(Iff, Seq(_, phi)))) => RightExistsOne(phi, x).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an existentially quantified pivot from premise and conclusion.") } } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Right-hand side of conclusion + φ must be the same as right-hand side of premise + ∃x. φ.") + invalid("Right-hand side of conclusion + φ is not the same as right-hand side of premise + ∃x. φ.") else if (pivot.tail.isEmpty) pivot.head match { - case BinderFormula(ExistsOne, x, phi) => SC.RightExistsOne(bot, premises(0), phi, x) - case _ => ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Could not infer an existentially quantified pivot from premise and conclusion.") + case BinderFormula(ExistsOne, x, phi) => RightExistsOne(phi, x).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an existentially quantified pivot from premise and conclusion.") } else - ProofStepJudgement.InvalidProofStep(this.asProofStepWithoutBot(premises).asProofStep(bot), "Right-hand side of conclusion + φ must be the same as right-hand side of premise + ∃x. φ.") + invalid("Right-hand side of conclusion + φ is not the same as right-hand side of premise + ∃x. φ.") } } case object RightExistsOne extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(phi: Formula, x: VariableLabel) = new RightExistsOne(phi, x) - def apply() = new RightExistsOneWithoutFormula() + def apply() = RightExistsOneWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -824,8 +1239,23 @@ object BasicStepTactic { * </pre> */ case object Weakening extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.Weakening(bot, premises(0)) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSubset(premiseSequent.left, bot.left)) + invalid("Left-hand side of conclusion is not the same as left-hand side of premise.") + else if (!isSubset(premiseSequent.right, bot.right)) + invalid("Left-hand side of premise is not a subset of left-hand side of conclusion.") + else + SC.Weakening(bot, premises(0)) + } } // Equality Rules @@ -836,20 +1266,56 @@ object BasicStepTactic { * Γ |- Δ * </pre> */ - case class LeftRefl(fa: Formula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftRefl(bot, premises(0), fa) + case class LeftRefl(phi: Formula) extends ProofStepWithoutBotNorPrem(1) { + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left + phi, premiseSequent.left)) + invalid("Left-hand sides of the conclusion + φ is not the same as left-hand side of the premise.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of the premise is not the same as the right-hand side of the conclusion.") + else + phi match { + case PredicateFormula(`equality`, Seq(left, right)) => + if (isSame(left, right)) + SC.LeftRefl(bot, premises(0), phi) + else + invalid("φ is not an instance of reflexivity.") + case _ => invalid("φ is not an equality.") + } + } } - case class LeftReflWithoutFormula() extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.Rewrite(bot, premises(0)) + case object LeftReflWithoutFormula extends ProofStepWithoutBotNorPrem(1) { + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val pivot = premiseSequent.left.diff(bot.left) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!pivot.isEmpty && pivot.tail.isEmpty) + LeftRefl(pivot.head).asSCProof(bot, premises, currentProof) + else + invalid("Could not infer an equality as pivot from premise and conclusion.") + } } case object LeftRefl extends ProofStepWithoutBotNorPrem(1) { // default construction: // def apply(fa: Formula) = new LeftRefl(fa) - def apply() = new LeftReflWithoutFormula() + def apply() = LeftReflWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -863,20 +1329,61 @@ object BasicStepTactic { * |- s=s * </pre> */ - case class RightRefl(fa: Formula) extends ProofStepWithoutBotNorPrem(0) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightRefl(bot, fa) + case class RightRefl(phi: Formula) extends ProofStepWithoutBotNorPrem(0) { + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + if (premises.length != 0) + invalid(s"No premises expected, ${premises.length} received.") + else if (!contains(bot.right, phi)) + invalid("Right-hand side of conclusion does not contain φ.") + else + phi match { + case PredicateFormula(`equality`, Seq(left, right)) => + if (isSame(left, right)) + SC.RightRefl(bot, phi) + else + invalid("φ is not an instance of reflexivity.") + case _ => invalid("φ is not an equality.") + } + } } - case class RightReflWithoutFormula() extends ProofStepWithoutBotNorPrem(0) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RewriteTrue(bot) + case object RightReflWithoutFormula extends ProofStepWithoutBotNorPrem(0) { + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + if (premises.length != 0) invalid(s"No premises expected, ${premises.length} received.") + else if (bot.right.isEmpty) invalid("Right-hand side of conclusion does not contain an instance of reflexivity.") + else { + // go through conclusion to see if you can find an reflexive formula + val pivot: Option[Formula] = bot.right.find(f => + f match { + case PredicateFormula(`equality`, Seq(l, r)) => isSame(l, r) + case _ => false + } + ) + + pivot match { + case Some(phi) => RightRefl(phi).asSCProof(bot, premises, currentProof) + case _ => invalid("Could not infer an equality as pivot from conclusion.") + } + + } + + } } case object RightRefl extends ProofStepWithoutBotNorPrem(0) { // default construction: // def apply(fa: Formula) = new RightRefl(fa) - def apply() = new RightReflWithoutFormula() + def apply() = RightReflWithoutFormula // usage without an argument list def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = @@ -891,8 +1398,30 @@ object BasicStepTactic { * </pre> */ case class LeftSubstEq(equals: List[(Term, Term)], lambdaPhi: LambdaTermFormula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftSubstEq(bot, premises(0), equals, lambdaPhi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val (s_es, t_es) = equals.unzip + lazy val phi_s = lambdaPhi(s_es) + lazy val phi_t = lambdaPhi(t_es) + lazy val equalities = equals map { case (s, t) => PredicateFormula(equality, Seq(s, t)) } + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of the premise is not the same as the right-hand side of the conclusion.") + else if ( + !isSameSet(bot.left + phi_s, premiseSequent.left ++ equalities + phi_t) && + !isSameSet(bot.left + phi_t, premiseSequent.left ++ equalities + phi_s) + ) + invalid("Left-hand side of the conclusion + φ(s_) is not the same as left-hand side of the premise + (s=t)_ + φ(t_) (or with s_ and t_ swapped).") + else + SC.LeftSubstEq(bot, premises(0), equals, lambdaPhi) + } } /** @@ -903,8 +1432,30 @@ object BasicStepTactic { * </pre> */ case class RightSubstEq(equals: List[(Term, Term)], lambdaPhi: LambdaTermFormula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightSubstEq(bot, premises(0), equals, lambdaPhi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val (s_es, t_es) = equals.unzip + lazy val phi_s = lambdaPhi(s_es) + lazy val phi_t = lambdaPhi(t_es) + lazy val equalities = equals map { case (s, t) => PredicateFormula(equality, Seq(s, t)) } + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequent.left ++ equalities)) + invalid("Left-hand side of the conclusion is not the same as the left-hand side of the premise + (s=t)_.") + else if ( + !isSameSet(bot.left + phi_s, premiseSequent.left + phi_t) && + !isSameSet(bot.left + phi_t, premiseSequent.left + phi_s) + ) + invalid("Right-hand side of the conclusion + φ(s_) is not the same as right-hand side of the premise + φ(t_) (or with s_ and t_ swapped).") + else + SC.RightSubstEq(bot, premises(0), equals, lambdaPhi) + } } /** @@ -915,8 +1466,30 @@ object BasicStepTactic { * </pre> */ case class LeftSubstIff(equals: List[(Formula, Formula)], lambdaPhi: LambdaFormulaFormula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.LeftSubstIff(bot, premises(0), equals, lambdaPhi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val (psi_es, tau_es) = equals.unzip + lazy val phi_psi = lambdaPhi(psi_es) + lazy val phi_tau = lambdaPhi(tau_es) + lazy val implications = equals map { case (s, t) => ConnectorFormula(Iff, Seq(s, t)) } + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.right, premiseSequent.right)) + invalid("Right-hand side of the premise is not the same as the right-hand side of the conclusion.") + else if ( + !isSameSet(bot.left + phi_psi, premiseSequent.left ++ implications + phi_tau) && + !isSameSet(bot.left + phi_tau, premiseSequent.left ++ implications + phi_psi) + ) + invalid("Left-hand side of the conclusion + φ(ψ_) is not the same as left-hand side of the premise + (ψ ↔ τ)_ + φ(τ_) (or with ψ_ and τ_ swapped).") + else + SC.LeftSubstIff(bot, premises(0), equals, lambdaPhi) + } } /** @@ -927,8 +1500,30 @@ object BasicStepTactic { * </pre> */ case class RightSubstIff(equals: List[(Formula, Formula)], lambdaPhi: LambdaFormulaFormula) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.RightSubstIff(bot, premises(0), equals, lambdaPhi) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + lazy val (psi_es, tau_es) = equals.unzip + lazy val phi_psi = lambdaPhi(psi_es) + lazy val phi_tau = lambdaPhi(tau_es) + lazy val implications = equals map { case (s, t) => ConnectorFormula(Iff, Seq(s, t)) } + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequent.left ++ implications)) + invalid("Left-hand side of the conclusion is not the same as the left-hand side of the premise + (ψ ↔ τ)_.") + else if ( + !isSameSet(bot.left + phi_psi, premiseSequent.left + phi_tau) && + !isSameSet(bot.left + phi_tau, premiseSequent.left + phi_psi) + ) + invalid("Right-hand side of the conclusion + φ(ψ_) is not the same as right-hand side of the premise + φ(τ_) (or with ψ_ and τ_ swapped).") + else + SC.RightSubstIff(bot, premises(0), equals, lambdaPhi) + } } /** @@ -939,8 +1534,23 @@ object BasicStepTactic { * </pre> */ case class InstFunSchema(insts: Map[SchematicTermLabel, LambdaTermTerm]) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.InstFunSchema(bot, premises(0), insts) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequent.left.map(instantiateTermSchemas(_, insts)))) + invalid("Left-hand side of premise instantiated with the map 'insts' is not the same as left-hand side of conclusion.") + else if (!isSameSet(bot.right, premiseSequent.right.map(instantiateTermSchemas(_, insts)))) + invalid("Right-hand side of premise instantiated with the map 'insts' is not the same as right-hand side of conclusion.") + else + SC.InstFunSchema(bot, premises(0), insts) + } } /** @@ -951,16 +1561,43 @@ object BasicStepTactic { * </pre> */ case class InstPredSchema(insts: Map[SchematicVarOrPredLabel, LambdaTermFormula]) extends ProofStepWithoutBotNorPrem(1) { - def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = - SC.InstPredSchema(bot, premises(0), insts) + def asSCProof(bot: Sequent, premises: Seq[Int], currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this.asProofStepWithoutBot(premises).asProofStep(bot), + msg + ) + + lazy val premiseSequent = currentProof.getSequent(premises(0)) + + if (premises.length != 1) + invalid(s"One premise expected, ${premises.length} received.") + else if (!isSameSet(bot.left, premiseSequent.left.map(instantiatePredicateSchemas(_, insts)))) + invalid("Left-hand side of premise instantiated with the map 'insts' is not the same as left-hand side of conclusion.") + else if (!isSameSet(bot.right, premiseSequent.right.map(instantiatePredicateSchemas(_, insts)))) + invalid("Right-hand side of premise instantiated with the map 'insts' is not the same as right-hand side of conclusion.") + else + SC.InstPredSchema(bot, premises(0), insts) + } } // Proof Organisation rules case class SCSubproof(sp: SCProof, premises: Seq[Int] = Seq.empty, display: Boolean = true) extends ProofStep { - def asSCProof(currentProof: Library#Proof): ProofStepJudgement = - sp match { - case sp: SCProof => SC.SCSubproof(sp, premises) - } + def asSCProof(currentProof: Library#Proof): ProofStepJudgement = { + def invalid(msg: String) = ProofStepJudgement.InvalidProofStep( + this, + msg + ) + + lazy val premiseSequents = premises.map(currentProof.getSequent(_)) + lazy val invalidPremise = premises.zipWithIndex.find((no, p) => !SC.isSameSequent(premiseSequents(no), sp.imports(p))) + + if (premises.length != sp.imports.length) + invalid(s"Subproof expected ${sp.imports.length} premises, ${premises.length} received.") + else if (!invalidPremise.isEmpty) + invalid(s"Premise number ${invalidPremise.get._1} (refering to step ${invalidPremise.get}) is not the same as import number ${invalidPremise.get._1} of the subproof.") + else + SC.SCSubproof(sp, premises) + } } }