Source code on Github
open import Prelude.Init; open SetAsType
open L.Mem
open import Prelude.DecEq
open import Prelude.Setoid
open import Prelude.InfEnumerable
open import Prelude.InferenceRules

module Nominal.Support (Atom : Type)  _ : DecEq Atom   _ : Enumerable∞ Atom  where

open import Nominal.New  Atom
open import Nominal.Swap Atom

freshAtom : Atoms  Atom
freshAtom = proj₁  minFresh

freshAtom∉ :  {xs : Atoms}  freshAtom xs  xs
freshAtom∉ {xs} = minFresh xs .proj₂

private variable A : Type ; B : Type ℓ′

module _  _ : Swap A   _ : ISetoid A  where

  ∃FinSupp FinSupp ∃Equivariant′ Equivariant′ : Pred A _

  -- NB: this is an over-approximation!
  -- e.g. ∃supp (ƛ x ⇒ x) = {x}
  ∃FinSupp x = И² λ 𝕒 𝕓  swap 𝕓 𝕒 x  x

  -- ** a proper notion of support
  -- e.g. in λ-calculus this would correspond to the free variables of a term
  FinSupp a =  λ (xs : Atoms) 
    (∀ x y  x  xs  y  xs  swap y x a  a)
    ×
    (∀ x y  x  xs  y  xs  swap y x a  a)

  -- alternative definition of equivariance based on (finite) support
  --  * equivariant(x) := supp(x) = ∅
  ∃Equivariant′ x =  λ (fin-x : ∃FinSupp x)  fin-x .proj₁  []
  Equivariant′  x =  λ (fin-x : FinSupp x)   fin-x .proj₁  []

-- counter-example: a function with infinite support
-- e.g. λ x → (x == 𝕒) ∨ (x == 𝕓)

record ∃FinitelySupported (A : Type )
   _ : ISetoid A   _ : SetoidLaws A 
   _ : Swap A   _ : SwapLaws A  : Typeω
  where

  field ∀∃fin : Unary.Universal ∃FinSupp

  ∃supp : A  Atoms
  ∃supp = proj₁  ∀∃fin

  _∙∃supp = ∃supp

  -- T0D0: extract minimal support
  --   i.e. filter out elements of `supp` that already satisfy P
  -- module _ ⦃ _ : IDecSetoid A ⦄ where
  --   minSupp : A → Atoms
  --   minSupp a =
  --     let xs , P = ∀fin a
  --     in  filter ? xs
  --     ?
  -- NB: doesn't hold in general ⇒ leads to a solution to the halting problem
  -- T0D0: find a characterization of this decidable sub-space

  ∃fresh∉ : (a : A)   (_∉ ∃supp a)
  ∃fresh∉ = minFresh  ∃supp
  -- NB: optimized fresh that generates the *least* element

  ∃fresh-var : A  Atom
  ∃fresh-var = proj₁  ∃fresh∉

  swap-∃fresh :  {𝕒 𝕓} (x : A) 
     𝕒  ∃supp x
     𝕓  ∃supp x
      ────────────────
       𝕒  𝕓  x  x
  swap-∃fresh x = flip (∀∃fin x .proj₂ _ _)

{-
  supp-swap : ∀ {𝕒 𝕓} (t : A) → supp (swap 𝕒 𝕓 t) ⊆ 𝕒 ∷ 𝕓 ∷ t ∷ []
  -- ≡ swap 𝕒 𝕓 (supp t) -- [swap 𝕒 𝕓 x₁, swap 𝕒 𝕓 x₂, ...]
  supp-swap {x}{a}{b} x∉ = ?

  swap-∉ : ∀ {x 𝕒 𝕓} (t : A) → x ∉ supp t → swap 𝕒 𝕓 x ∉ supp (swap 𝕒 𝕓 t)
  -- T0D0: add hypothesis `x ∉ [a, b]`
  swap-∉ {x}{a}{b} x∉
    with x ≟ a
  ... | yes refl
    -- b ∉ supp (swap a b t)
    = ?
  ... | no x≢a
    with x ≟ b
  ... | yes refl
    -- a ∉ supp (swap a b t)
    = ?
  ... | no x≢b
    -- x ∉ supp (swap a b t)
    = ?
-}
open ∃FinitelySupported ⦃...⦄ public

instance
  ∃FinSupp-Atom : ∃FinitelySupported Atom
  ∃FinSupp-Atom .∀∃fin 𝕒 = [ 𝕒 ] , λ _ _ y∉ z∉ 
    swap-noop _ _ _ λ where 𝟘  z∉ 𝟘; 𝟙  y∉ 𝟘

-- T0D0: generalize this to more complex types than Atom (c.f. supp-swap above)
∃supp-swap-atom :  {𝕒 𝕓} (t : Atom)  ∃supp (swap 𝕒 𝕓 t)  𝕒  𝕓  t  []
-- supp (swap 𝕒 𝕓 t) ≡ swap 𝕒 𝕓 (supp t)
∃supp-swap-atom {a}{b} t
  with t  a
... | yes refl = λ where 𝟘  𝟙
... | no _
  with t  b
... | yes refl = λ where 𝟘  𝟘
... | no _     = λ where 𝟘  𝟚

record FinitelySupported (A : Type )
   _ : ISetoid A   _ : SetoidLaws A 
   _ : Swap A   _ : SwapLaws A  : Typeω
  where

  field ∀fin : Unary.Universal FinSupp

  supp : A  Atoms
  supp = proj₁  ∀fin

  _∙supp = supp

  infix 4 _♯_
  _♯_ : Atom  A  Type _
  𝕒  x = 𝕒  supp x

  fresh∉-min : (a : A)   (_∉ supp a)
  fresh∉-min = fresh  supp
  -- NB: optimized fresh that generates the *least* element

  fresh-var-min : A  Atom
  fresh-var-min = proj₁  fresh∉-min

  swap-fresh-min :  {𝕒 𝕓} (x : A) 
     𝕒  supp x
     𝕓  supp x
      ────────────────
       𝕒  𝕓  x  x
  swap-fresh-min x = flip (∀fin x .proj₂ .proj₁ _ _)

  ∃fresh :  (x : A)   λ 𝕒   λ 𝕓 
      (𝕒  x)
    × (𝕓  x)
    × (swap 𝕓 𝕒 x  x)
  ∃fresh x =
    let xs , swap≈ , swap≉ = ∀fin x
        -- (a ∷ b ∷ [] , a∉ ∷ b∉ ∷ []) = (fresh^ 2) xs
        a , a∉ = minFresh xs
        b , b∉ = minFresh xs
    in a , b , a∉ , b∉ , swap≈ a b a∉ b∉

  -- T0D0: meta-programming tactic `fresh-in-context` (big sister to `deriveSwap`)
  -- NB: these tactics correspond to two fundamental axioms/notions in nominal sets
  -- (c.f. EZFA)

open FinitelySupported ⦃...⦄ public

instance
  FinSupp-Atom : FinitelySupported Atom
  FinSupp-Atom .∀fin 𝕒 = [ 𝕒 ] , eq , ¬eq
    where
      eq :  x y  x  [ 𝕒 ]  y  [ 𝕒 ]  swap y x 𝕒  𝕒
      eq _ _ x∉ y∉ = swap-noop _ _ _ λ where 𝟘  y∉ 𝟘; 𝟙  x∉ 𝟘

      ¬eq :  x y  x  [ 𝕒 ]  y  [ 𝕒 ]  swap y x 𝕒  𝕒
      ¬eq _ y 𝟘 y∉
        rewrite ≟-refl 𝕒 | ≟-refl y
        with 𝕒  y
      ... | yes refl = ⊥-elim $ y∉ 𝟘
      ... | no y≢    = ≢-sym y≢


-- T0D0: generalize this to more complex types than Atom (c.f. supp-swap above)
supp-swap-atom :  {𝕒 𝕓} (t : Atom)  supp (swap 𝕒 𝕓 t)  𝕒  𝕓  t  []
supp-swap-atom {a}{b} t
  with t  a
... | yes refl = λ where 𝟘  𝟙
... | no _
  with t  b
... | yes refl = λ where 𝟘  𝟘
... | no _     = λ where 𝟘  𝟚