{-# OPTIONS --without-K #-}
open import FunUniverse.Core
import      FunUniverse.Category.Op as CatOp
open import FunUniverse.Rewiring.Linear

open import Data.Nat

module FunUniverse.Inverse {t} {T : Set t}
                           (funU : FunUniverse T)
                           (bijU : Bijective funU) where

opU : FunUniverse T
opU = OpFunU.opFunU funU

open FunUniverse opU

invLinRewiring : LinRewiring funU → LinRewiring opU
invLinRewiring linRewiring = mk (CatOp.op L.cat)
                                first swap assoc <tt,id> snd<tt,>
                                <_×_> second tt→[] []→tt <∷> uncons
  where
    module L = LinRewiring linRewiring
    -- Functions
    id : ∀ {A} → A `→ A
    id = L.id

    _∘_   : ∀ {A B C} → (B `→ C) → (A `→ B) → (A `→ C)
    f ∘ g = g L.∘ f

    -- Products (group 2 primitive functions or derived from group 1)
    first   : ∀ {A B C} → (A `→ C) → (A `× B) `→ (C `× B)
    first f = L.first f

    swap : ∀ {A B} → (A `× B) `→ (B `× A)
    swap = L.swap

    assoc : ∀ {A B C} → ((A `× B) `× C) `→ (A `× (B `× C))
    assoc = L.assoc′

    <tt,id> : ∀ {A} → A `→ `𝟙 `× A
    <tt,id> = L.snd<tt,>

    snd<tt,> : ∀ {A} → `𝟙 `× A `→ A
    snd<tt,> = L.<tt,id>

    -- Products (derived from group 1 or 2)
    <_×_>     : ∀ {A B C D} → (A `→ C) → (B `→ D) → (A `× B) `→ (C `× D)
    < f × g > = L.< f × g >

    second   : ∀ {A B C} → (B `→ C) → (A `× B) `→ (A `× C)
    second f = L.second f

    -- Vectors
    tt→[] : ∀ {A} → `𝟙 `→ `Vec A 0
    tt→[] = L.[]→tt

    []→tt : ∀ {A} → `Vec A 0 `→ `𝟙
    []→tt = L.tt→[]

    <∷> : ∀ {n A} → (A `× `Vec A n) `→ `Vec A (1 + n)
    <∷> = L.uncons

    uncons : ∀ {n A} → `Vec A (1 + n) `→ (A `× `Vec A n)
    uncons = L.<∷>

invHasBijFork : HasBijFork funU → HasBijFork opU
invHasBijFork hasBijFork = mk F.bijFork
  where module F = HasBijFork hasBijFork

invHasXor : HasXor funU → HasXor opU
invHasXor hasXor = mk H.⟨⊕_⟩
  where module H = HasXor hasXor

invU : Bijective funU → Bijective opU
invU bijective = mk (invLinRewiring linRewiring)
                    (invHasBijFork hasBijFork)
                    (invHasXor hasXor)
  where open Bijective bijective