Last updated on
Proof of InOrderTraversalMirror
- Points
- 40 points
- Topics
- Computer-checked proofs, Pattern matching
- Files to submit
src/main/scala/proof/InOrderMirrorProof.scala
In this exercise, your task is to write the inductive case of a computer-checked proof.
The first section defines the datatypes and axioms available to you, the second
is a refresher on proof syntax and instructions to evaluate theorems, and the
last section presents the graded question, the
inductive case of InOrderTraversalMirror
.
Datatypes and Axioms
Consider the following definition of binary trees:
sealed trait BinaryTree[+T]
case object Empty extends BinaryTree[Nothing]
case class Node[T](left: BinaryTree[T], x: T, right: BinaryTree[T]) extends BinaryTree[T]
and the usual definition of Scala lists:
sealed trait List[+T]
case object Nil extends List[Nothing]
case class Cons[T](head: T, tail: List[T]) extends List[T]
We use the infix notation x :: xs
for Cons(x, xs)
.
All lemmas in this exercise hold for all types T
and all x: T
,
xs, ys, zs: List[T]
, tree: BinaryTree[T]
,
left: BinaryTree[T]
, right: BinaryTree[T]
.
On BinaryTree[T]
we define functions inorder[T]: BinaryTree[T] => List[T]
and mirror[T]: BinaryTree[T] => BinaryTree[T]
specified by the
following axioms:
object InOrder:
val EmptyCase = Axiom:
Empty.inOrder === Nil
val NodeCase = Axiom:
Node(left, x, right).inOrder ===
left.inOrder ++ ((x :: Nil) ++ right.inOrder)
object Mirror:
val EmptyCase = Axiom:
Empty.mirror === Empty
val NodeCase = Axiom:
Node(left, x, right).mirror ===
Node(right.mirror, x, left.mirror)
src/main/scala/proof/InOrderMirrorAxioms.scala
In addition, the binary function append
on lists, noted as the infix operator
++
, and the function reverse
, given by the following Axioms:
object Reverse:
val NilCase = Axiom:
Nil.reverse === Nil
val ConsCase = Axiom:
(x :: xs).reverse === xs.reverse ++ (x :: Nil)
val AppendCase = Axiom:
(xs ++ ys).reverse === ys.reverse ++ xs.reverse
val SingleCase = Axiom:
(x :: Nil).reverse === x :: Nil
object Append:
val NilCase = Axiom:
(Nil ++ xs) === xs
val ConsCase = Axiom:
(x :: xs) ++ ys === x :: (xs ++ ys)
val Associativity = Axiom:
(xs ++ ys) ++ zs === xs ++ (ys ++ zs)
src/main/scala/proof/InOrderMirrorAxioms.scala
Refresher on Theorem Syntax
The syntax of a theorem involves the Theorem
constructor, accepting a
statement (possibly with assumptions), and a proof of the given statement.
The proof is a series of equalities and justifications, of the form x === y ==< Rule
. As examples:
Without assumptions:
// example of a theorem applying two rules successively
val multipleRuleApplication = Theorem((Nil ++ Nil).map(f) === Nil):
(Nil ++ Nil).map(f)
=== Nil.map(f) ==< Append.NilCase
=== Nil ==< Map.NilCase
src/main/scala/proof/MapSingleFlatten.scala
With assumptions:
// a formula to use as an assumption
val assumption = xs ++ Nil === Nil ++ xs
// example of a theorem using an assumption
// the assumption is automatically taken from the theorem statement
val usingAssumption = Theorem(assumption ==> (xs ++ Nil === xs)):
xs ++ Nil
=== Nil ++ xs ==< assumption // if assumption is not in the statement, this will fail
=== xs ==< Append.NilCase
src/main/scala/proof/MapSingleFlatten.scala
These examples were discussed in the list-proofs
exercise. They can be found
through Moodle.
The basic examples are also present in the file
src/main/scala/proof/MapSingleFlatten.scala
. These are available strictly for
a refresher on proof syntax, and will not be considered for grading.
If you face issues with operators, e.g. operator ++ is not defined on Formula[T]
, put your whole terms inside parentheses. When in doubt, more
parentheses are better.
Also, some of the provided axioms allow you to manipulate parentheses.
Running and Testing Instructions
The proofs are checked automatically on running runMain proof.runTheorems
in
sbt
or using run
and selecting proof.runTheorems
. You can use ~runMain proof.runTheorems
to re-run the theorems on file modification for quick feedback.
Running the theorems will identify incorrect steps and report the line number. A correctly checked theorem will report Proved Theorem
:
[info] running (fork) proof.runTheorems
[info] Proved Theorem EmptyCase: (Empty.mirror.inOrder === Empty.inOrder.reverse)
Since the proofs are checked by the system when running the theorems, the tests simply verify that the theorem and axiom statements have not been altered. The proofs may be additionally checked manually by teaching staff. The tests and printed information are for your feedback.
InOrderTraversalMirror
You need to prove the property InorderTraversalMirror:
tree.mirror.inOrder === tree.inOrder.reverse
The proof is done by induction, and is in two parts, EmptyCase
, for tree
being Empty
, and NodeCase
, for tree
being Node(left, x, right)
for some
trees left
and right
, and x : T
as above.
Base Case (example)
The base case is provided as an example of manipulating trees:
val EmptyCase = Theorem(EmptyCaseStatement):
Empty.mirror.inOrder
=== Empty.inOrder ==< Mirror.EmptyCase
=== Nil ==< InOrder.EmptyCase
=== Nil.reverse ==< Reverse.NilCase
=== Empty.inOrder.reverse ==< InOrder.EmptyCase
src/main/scala/proof/InOrderMirrorProof.scala
Inductive case
Complete the proof of the inductive case, the theorem NodeCase
, using the
provided axioms and the inductive hypotheses IHLeft
and IHRight
:
val NodeCase = Theorem(NodeCaseStatement):
Node(left, x, right).inOrder.reverse
???
src/main/scala/proof/InOrderMirrorProof.scala
You are provided with the theorem skeleton in the file
src/main/scala/proof/InOrderMirrorProof.scala
as above.