-- Note: Turing machines and lambda calculus are NOT equivalent
-- models of computation as soon as we go beyond first-order functions ℕ → ℕ
-- to higher-order functions (such as functions of type (ℕ → ℕ) → ℕ).
--
-- The reason is that a higher-order Turing machine (ℕ → ℕ) → ℕ is given as input
-- the source code for a Turing machine of type ℕ → ℕ.
--
-- In contrast, a higher-order lambda term of type (ℕ → ℕ) → ℕ is given
-- the input function only as a blackbox, without the source code.

----------

-- Peano arithmetic (PA) is a formal system for expressing
-- and proving statements about natural numbers.

-- Ingredients:
-- Z   zero
-- S   succ
-- +
-- ·

open import Padova2025.ProgrammingBasics.Naturals.Base
open import Padova2025.ProgrammingBasics.Naturals.Arithmetic
open import Padova2025.ProvingBasics.Negation
open import Padova2025.ProvingBasics.Equality.Base
open import Padova2025.ProvingBasics.Connectives.Conjunction
open import Padova2025.ProvingBasics.Connectives.Disjunction
open import Padova2025.ProvingBasics.Termination.Gas using (𝟙; tt)

-- "Fin n" will be an Agda datatype consisting of exactly n many inhabitants.
data Fin :   Set where
  zero : {n : }  Fin (succ n)          -- "zero is smaller than succ n, for any n"
  succ : {n : }  Fin n  Fin (succ n)  -- "if x is smaller than n, then succ x is smaller than succ n"

module _ where private  -- temporary scratch pad
  example-1 : Fin three
  example-1 = zero

  example-2 : Fin three
  example-2 = succ zero

  example-3 : Fin three
  example-3 = succ (succ zero)

  -- example-4 : Fin three
  -- example-4 = succ (succ (succ zero))
  -- should not and does not work

-- Agda datatype of PA terms, more precisely "Term n" will be
-- the Agda datatype of PA terms in which the variables 0, 1, ..., n-1
-- may occur.
data Term (n : ) : Set where
  Z   : Term n
  S   : Term n  Term n
  _⊕_ : Term n  Term n  Term n
  _⊛_ : Term n  Term n  Term n
  var : Fin n  Term n

-- Agda datatype of PA formulas, more precisely "Form n" will be
-- the Agda datatype of PA formulas in which the variables 0, 1, ..., n-1
-- may occur.
data Form (n : ) : Set where
  _∧_ : Form n  Form n  Form n
  _∨_ : Form n  Form n  Form n
  _⇒_ : Form n  Form n  Form n
  top : Form n
  bot : Form n
  fa  : Form (succ n)  Form n    -- ∀
  ex  : Form (succ n)  Form n    -- ∃
  _≈_ : Term n  Term n  Form n
-- Strictly speaking we don't need the _⇒_ constructor, as we could
-- also define (α ⇒ β) as (¬ α ∨ β).

example-commutativity : Form zero
example-commutativity =
  fa (fa ((var (succ zero)  var zero)  (var zero  var (succ zero))))
-- ∀x. ∀y. x + y = y + x

-- parse : String → Form n
-- parse "∀x. ∀y. x + y = y + x"

-- We don't need negation as a constructor, as we can define it:
neg : {n : }  Form n  Form n
neg φ = (φ  bot)

-- The formula "∀x. ∀y. x + y = y + x" would be regarded as well-formed,
-- but the formula "∀x. ∀y. x + a = y + x" would not.

-- Axioms:
-- ∀x. Z + x = x
-- induction scheme: (P(Z) ∧ (∀n. P(n) ⇒ P(S(n)))) ⇒ ∀n. P(n)
-- (one axiom for each formula P)
-- ...

-- Inference rules:
-- P ⊢ P        ("from P, we may conclude P")
-- P ∧ Q ⊢ P    ("from P ∧ Q, we may conclude P")
-- ⊤ ⊢ P ∨ ¬ P  ("from ⊤, we may conclude the law of excluded middle for P")
-- ...

data _⊢_ : {n : }  Form n  Form n  Set where
  identity   : {n : } {φ : Form n}      φ  φ
  conj-elimₗ : {n : } {α β : Form n}    (α  β)  α
  conj-elimᵣ : {n : } {α β : Form n}    (α  β)  β
  cut        : {n : } {α β γ : Form n}  (α  β)  (β  γ)  (α  γ)
  -- ...

-- Let us verify that HA is consistent, i.e. that HA does NOT prove ⊤ ⊢ Z ≈ S Z.
-- We do that by exhibiting a model.

Env :   Set
Env n = Fin n  
-- "Env n" is the type of environments, specifying an actual value
-- for each of the variables 0, ..., n-1.

-- "eval φ" should be the Agda truth value of the HA-formula φ.
-- For instance, the type "eval example-commutativity" should be inhabited,
-- by a witness that addition of natural numbers is commutative.
-- In constrast, the type "eval bot" should be empty.

eval₀ : {n : }  Env n  Term n  
eval₀ env Z       = zero
eval₀ env (S x)   = succ (eval₀ env x)
eval₀ env (x  y) = eval₀ env x + eval₀ env y
eval₀ env (x  y) = {!!}
eval₀ env (var v) = env v

eval : {n : }  Env n  Form n  Set
eval env (φ  ψ) = eval env φ × eval env ψ
eval env (φ  ψ) = eval env φ  eval env ψ
eval env (φ  ψ) = eval env φ  eval env ψ
eval env top     = 𝟙      -- data 𝟙 : Set where tt : 𝟙
eval env bot     = 
eval env (fa φ)  = (x : )  eval  { zero  x ; (succ v)  env v }) φ
eval env (ex φ)  = {!!}
eval env (x  y) = eval₀ env x  eval₀ env y

-- Let us prove soundness: If PA proves α ⊢ β, then α actually implies β.
sound : {n : } (α β : Form n) (env : Env n)  (α  β)  (eval env α  eval env β)
sound α β env identity   = λ z  z
sound α β env conj-elimₗ = fst
sound α β env conj-elimᵣ = {!!}
sound α β env (cut p q)  = λ z  sound _ β env q (sound α _ env p z)

consistency : ¬ (top  (Z  S Z))
consistency p with sound _ _ _ p tt
... | ()

{-
  ZFC
   |
   ZF
   |  \
   |    IZF
   |     |
   |     |
   PA    |
   |  \  |
   |    HA                         PRA + QF-TI(ε₀)
   |                                    |
   PRA primitive recursive arithmetic---+

  The consistency of PA can be proven in:
  - ZFC, ZF, IZF, ...
  - PRA + QF-TI(ε₀)  "quantifier-free transfinite induction up to ε₀"

  The consistency of ZFC can be proven in:
  - PRA + QF-TI(???),  where we are currently lacking the technology
                       to describe ???
-}

{-
example-higher-order-function : (ℕ → ℕ) → ℕ
example-higher-order-function f = f three + f four
-}

-- Possible follow-up projects:
-- 1. Define Heyting arithmetic (HA), the constructive variant of Peano arithmetic
-- 2. Prove that, if PA proves that some Turing machine terminates, then
--    HA also proves that this Turing machine terminates.
-- 3. Prove that, if PA proves a formula of the form "∀x ∃y P(x,y)", then
--    HA does as well (provided that no further quantifiers appear in P).