------------------------------------------------------------------------
-- The extraction function.
------------------------------------------------------------------------

open import Graded.Modality

module Graded.Erasure.Extraction
  {a} {M : Set a}
  (š•„ : Modality M)
  where

open Modality š•„

open import Tools.Bool
open import Tools.Function
open import Tools.Nat using (Nat; 2+)
open import Tools.Relation

open import Definition.Untyped M as U
open import Graded.Erasure.Target as T
open import Graded.Erasure.Target.Non-terminating

private
  variable
    m n l : Nat
    Ī“ : Con U.Term n
    A t t′ u : U.Term n
    v v′ w : T.Term n
    p : M

-- If the first argument is strict, then the result is ↯ (which is a
-- value), but if the first argument is non-strict, then the result is
-- loopĀ non-strict (which does not reduce to a value).

loop? : Strictness → T.Term n
loop? non-strict = loop non-strict
loop? strict     = ↯

-- Extraction for prodrec when the match is not erased.

erase-prodrecω :
  Strictness → M → T.Term n → T.Term (2+ n) → T.Term n
erase-prodrecω s p t u = case is-šŸ˜? p of Ī» where
    (yes pā‰”šŸ˜) → T.lam (u T.[ T.liftSubst (T.sgSubst (loop s)) ])
                  T.∘⟨ s ⟩ t
    (no pā‰¢šŸ˜) → T.prodrec t u

-- A function application that is used when the grade isĀ šŸ˜.
--
-- The argument is removed entirely if the boolean is true.

app-šŸ˜ā€² : Bool → Strictness → T.Term n → T.Term n
app-šŸ˜ā€² false s t = t T.∘⟨ s ⟩ loop? s
app-šŸ˜ā€² true  _ t = t

-- A variant of app-šŸ˜.

app-šŸ˜ : Strictness → T.Term n → T.Term n
app-šŸ˜ s = app-šŸ˜ā€² (s == non-strict) s

mutual

  -- The extraction function.
  --
  -- Function and constructor applications are made strict if the
  -- first argument is "strict".
  --
  -- Erasable arguments are removed entirely if the first argument is
  -- "non-strict".
  --
  -- A non-terminating term, loopĀ s, is used in some places. The idea
  -- is that it should be safe to replace this term with, say, a term
  -- that throws an exception. The term loop?Ā s is equal to loopĀ s
  -- when s is non-strict, but if s is strict, then loop?Ā s is ↯,
  -- which is a value.

  erase : Strictness → U.Term n → T.Term n
  erase s = erase′ (s == non-strict) s

  -- A variant of the extraction function.
  --
  -- If the boolean is true, then erasable arguments are removed
  -- entirely.

  erase′ : Bool → Strictness → U.Term n → T.Term n
  erase′ remove s = erase″
    where
    erase″ : U.Term n → T.Term n
    erase″ (var x)                 = T.var x
    erase″ (U _)                   = loop? s
    erase″ (ΠΣ⟨ _ ⟩ _ , _ ā–· _ ā–¹ _) = loop? s
    erase″ (U.lam p t)             = case remove of Ī» where
      false → T.lam (erase″ t)
      true  → case is-šŸ˜? p of Ī» where
        (no _)  → T.lam (erase″ t)
        (yes _) → erase″ t T.[ loop s ]ā‚€
    erase″ (t U.∘⟨ p ⟩ u) = case is-šŸ˜? p of Ī» where
      (no _)  → erase″ t T.∘⟨ s ⟩ erase″ u
      (yes _) → app-šŸ˜ā€² remove s (erase″ t)
    erase″ (U.prod _ p t u) = case is-šŸ˜? p of Ī» where
      (no _)  → prod⟨ s ⟩ (erase″ t) (erase″ u)
      (yes _) → erase″ u
    erase″ (U.fst p t) = case is-šŸ˜? p of Ī» where
      (no _)  → T.fst (erase″ t)
      (yes _) → loop s
    erase″ (U.snd p t) = case is-šŸ˜? p of Ī» where
      (no _)  → T.snd (erase″ t)
      (yes _) → erase″ t
    erase″ (U.prodrec r p _ _ t u) = case is-šŸ˜? r of Ī» where
      (no _)  → erase-prodrecω s p (erase″ t) (erase″ u)
      (yes _) → erase″ u T.[ loop s , loop s ]₁₀
    erase″ ā„•                        = loop? s
    erase″ U.zero                   = T.zero
    erase″ (U.suc t)                = suc⟨ s ⟩ (erase″ t)
    erase″ (U.natrec p q r A t u v) =
      T.natrec (erase″ t) (erase″ u) (erase″ v)
    erase″ Unit!                 = loop? s
    erase″ U.star!               = T.star
    erase″ (U.unitrec _ p _ _ t u) = case is-šŸ˜? p of Ī» where
      (no _)  → T.unitrec (erase″ t) (erase″ u)
      (yes _) → erase″ u
    erase″ Empty               = loop? s
    erase″ (emptyrec p A t)    = loop s
    erase″ (Id _ _ _)          = loop? s
    erase″ U.rfl               = loop? s
    erase″ (J _ _ _ _ _ u _ _) = erase″ u
    erase″ (K _ _ _ _ u _)     = erase″ u
    erase″ ([]-cong _ _ _ _ _) = loop? s

mutual

  -- Extraction of substitutions.

  eraseSubst : Strictness → U.Subst m n → T.Subst m n
  eraseSubst = eraseSubst′ false

  -- A variant of eraseSubst.

  eraseSubst′ : Bool → Strictness → U.Subst m n → T.Subst m n
  eraseSubst′ b s σ x = erase′ b s (σ x)