------------------------------------------------------------------------
-- The Agda standard library
--
-- Examples showing how the reflective ring solver may be used.
------------------------------------------------------------------------

module README.Tactic.RingSolver where

-- You can ignore this bit! We're just overloading the literals Agda uses for
-- numbers. This bit isn't necessary if you're just using Nats, or if you
-- construct your type directly. We only really do it here so that we can use
-- different numeric types in the same file.

open import Agda.Builtin.FromNat
open import Data.Nat using ()
open import Data.Integer using ()
import Data.Nat.Literals as 
import Data.Integer.Literals as 

instance
  numberNat : Number 
  numberNat = ℕ.number

instance
  numberInt : Number 
  numberInt = ℤ.number

------------------------------------------------------------------------------
-- Imports!

open import Data.List.Base as List using (List; _∷_; [])
open import Relation.Binary.PropositionalEquality
  using (subst; cong; _≡_; module ≡-Reasoning)
open import Data.Bool as Bool using (Bool; true; false; if_then_else_)
open import Data.Unit using (; tt)

open import Tactic.RingSolver.Core.AlmostCommutativeRing
  using (AlmostCommutativeRing)

------------------------------------------------------------------------
-- Integer examples
------------------------------------------------------------------------

module IntegerExamples where
  open import Data.Integer hiding (_^_)
  open import Data.Integer.Tactic.RingSolver

  open AlmostCommutativeRing ring using (_^_)

  -- Everything is automatic: you just ask Agda to solve it and it does!

  -- Addition
  lemma₁ :  x y  x + y + 3  2 + y + x + 1
  lemma₁ = solve-∀

  -- Multiplication
  lemma₂ :  x  x * 2 + 1  x + 1 + x
  lemma₂ = solve-∀

  -- Negation
  lemma₃ :  x y  (- x) + (- y)  - (x + y)
  lemma₃ = solve-∀

  -- Subtraction
  lemma₄ :  x y  (x - y) * 2 - 2  (- 2) * y - 1 + 2 * x - 1
  lemma₄ = solve-∀

  -- Exponentiation by constant literals
  lemma₅ :  x y  (x + y) ^ 2  x ^ 2 + 2 * x * y + y ^ 2
  lemma₅ = solve-∀

  -- It can be interleaved with manual proofs as well.
  lemma₆ :  x y z  y  z  x + y * 1 + 3  2 + z + x + 1
  lemma₆ x y z y≡z = begin
    x + y * 1 + 3 ≡⟨ solve (x  y  []) 
    2 + y + x + 1 ≡⟨ cong  v  2 + v + x + 1) y≡z 
    2 + z + x + 1 
    where open ≡-Reasoning

------------------------------------------------------------------------
-- Natural examples
------------------------------------------------------------------------

module NaturalExamples where
  open import Data.Nat
  open import Data.Nat.Tactic.RingSolver

  -- The solver is flexible enough to work with ℕ (even though it asks
  -- for rings!)
  lemma₁ :  x y  x + y * 1 + 3  2 + 1 + y + x
  lemma₁ = solve-∀

------------------------------------------------------------------------
-- Checking invariants
------------------------------------------------------------------------
-- The solver makes it easy to prove invariants, without having to
-- rewrite proof code every time something changes in the data
-- structure.

module _ {a} {A : Set a} (_≤_ : A  A  Bool) where
  open import Data.Nat hiding (_≤_)
  open import Data.Nat.Tactic.RingSolver

  -- A Skew Heap, indexed by its size.
  data Tree :   Set a where
    leaf : Tree 0
    node :  {n m}  A  Tree n  Tree m  Tree (1 + n + m)

  -- A substitution operator, to clean things up.
  infixr 1 _⇒_
  _⇒_ :  {n}  Tree n   {m}  n  m  Tree m
  x  n≡m  = subst Tree n≡m x

  open ≡-Reasoning

  _∪_ :  {n m}  Tree n  Tree m  Tree (n + m)
  leaf                  ys                   = ys
  node {a} {b} x xl xr  leaf                 =
    node x xl xr  solve (a  b  [])
  node {a} {b} x xl xr  node {c} {d} y yl yr =
      if x  y
        then node x (node y yl yr  xr) xl  begin
          1 + (1 + c + d + b) + a ≡⟨ solve (a  b  c  d  []) 
          1 + a + b + (1 + c + d) 
        else node y (node x xl xr  yr) yl  begin
          1 + (1 + a + b + d) + c ≡⟨ solve (a  b  c  d  []) 
          1 + a + b + (1 + c + d)