{-# OPTIONS --without-K #-}
module Composition.Vertical where

open import Level
open import Type hiding ()
open import Function
open import Data.One using (𝟙)
open import Composition.Horizontal using (ConstArr)

VComposition :  {i j} {I :  i} (_×_ : I  I  I) (_↝_ : I  I   j)   (i  j)
VComposition _×_ _↝_ =  {i₀ i₁ o₀ o₁}  i₀  o₀  i₁  o₁  (i₀ × i₁)  (o₀ × o₁)

IVComposition :  {i t a} {I :  i} {T :  t}
                   (_×ᵢ_ : I  I  I)
                   (_×_ : T  T  T)
                   (⟨_⟩_↝_ : I  T  T   a)   (a  t  i)
IVComposition _×ᵢ_ _×_ ⟨_⟩_↝_ =
   {j₀ j₁ i₀ i₁ o₀ o₁}   j₀  i₀  o₀   j₁  i₁  o₁   j₀ ×ᵢ j₁  (i₀ × i₁)  (o₀ × o₁)

record IVComposable {i j t a} {I :  i} {_×ᵢ_ : I  I  I} {_↝ᵢ_ : I  I   j} {T : I   t}
                    (_·_ : VComposition _×ᵢ_ _↝ᵢ_)
                    (_×_ :  {i₀ i₁}  T i₀  T i₁  T (i₀ ×ᵢ i₁))
                    (⟨_⟩_↝_ :  {i₀ i₁}  i₀ ↝ᵢ i₁  T i₀  T i₁   a) :  (a  t  i  j) where
  constructor mk
  infixr 3 _***_
  field
    _***_ :  {i₀ i₁ o₀ o₁} {j₀ : i₀ ↝ᵢ o₀} {j₁ : i₁ ↝ᵢ o₁} {A B C D}
              j₀  A  C   j₁  B  D
              j₀ · j₁  (A × B)  (C × D)

vComposition :  {t a} {T :  t} {_×_ : T  T  T} {_↝_ : T  T   a}
                IVComposable {I = 𝟙} {_↝ᵢ_ = λ _ _  𝟙} {T = const T} _ _×_ (const _↝_)
                VComposition _×_ _↝_
vComposition (mk _***_) = _***_

vCIComposition :  {i t a} {T :  t} {_×_ : T  T  T}
                 {I :  i} {_·_ : I  I  I}
                 {⟨_⟩_↝_ : I  T  T   a}
                IVComposable {I = 𝟙} _·_ _×_ ⟨_⟩_↝_
                IVComposition _·_ _×_ ⟨_⟩_↝_
vCIComposition (mk _***_) = _***_

VComposable :  {t a} {T :  t} (_×_ : T  T  T) (_↝_ : T  T   a)   (t  a)
VComposable _×_ _↝_ = IVComposable {i = zero} {_↝ᵢ_ = ConstArr 𝟙} _ _×_ (const _↝_)

{- VComposable unfolds to:
record VComposable {t a} {T : ★ t} (_`×_ : T → T → T) (_↝_ : T → T → ★ a) : ★ (t ⊔ a) where
  constructor mk
  infixr 3 _***_
  field
    _***_ : ∀ {i₀ i₁ o₀ o₁} → i₀ ↝ o₀ → i₁ ↝ o₁ → (i₀ `× i₁) ↝ (o₀ `× o₁)
-}

module VComposable = IVComposable

open import Data.Nat
open import Data.Fin.NP as Fin using (Fin; inject+; raise; bound; free)

opVComp :  {t a} {T :  t} {_×_ : T  T  T} {_↝_ : T  T   a}
           VComposable _×_ _↝_  VComposable (flip _×_) (flip _↝_)
opVComp (mk _***_) = mk (flip _***_)

open import Data.Product

funVComp :  {t}  VComposable _×_  (A B :  t)  A  B)
funVComp {t} = mk _***_ where
  _***_ :  {A B C D :  t}  (A  B)  (C  D)  A × C  B × D
  (f *** g) (x , y) = (f x , g y)

finFunVComp : VComposable _+_  i o  Fin i  Fin o)
finFunVComp = mk _***_ where
  C = λ i o  Fin i  Fin o
  _***_ :  {i₀ i₁ o₀ o₁}  C i₀ o₀  C i₁ o₁  C (i₀ + i₁) (o₀ + o₁)
  _***_ {i}      f g x with Fin.cmp i _ x
  _***_          f _ ._ | Fin.bound x = inject+ _ (f x)
  _***_ {o₀ = o} _ g ._ | Fin.free  x = raise o (g x)

-- finFunOpVComp = opVComp finFunVComp [modulo commutativity of _+_]
finFunOpVComp : VComposable _+_  i o  Fin o  Fin i)
finFunOpVComp = mk _***_ where
  C = λ i o  Fin o  Fin i
  _***_ :  {i₀ i₁ o₀ o₁}  C i₀ o₀  C i₁ o₁  C (i₀ + i₁) (o₀ + o₁)
  _***_ {o₀ = o} f g x with Fin.cmp o _ x
  _***_          f _ ._ | Fin.bound x = inject+ _ (f x)
  _***_ {i}      _ g ._ | Fin.free  x = raise i (g x)

module _ {a} (A :  a) where
    open import Data.Vec.NP
    open FunVec {A = A}

    vecFunVComp : VComposable _+_ _→ᵛ_
    vecFunVComp = mk _***_ where
      C = _→ᵛ_
      _***_ :  {i₀ i₁ o₀ o₁}  C i₀ o₀  C i₁ o₁
                               C (i₀ + i₁) (o₀ + o₁)
      (f *** g) xs with splitAt _ xs
      ... | ys , zs , _ = f ys ++ g zs

open import Data.Bit
open import Data.Bits

bitsFunVComp : VComposable _+_ _→ᵇ_
bitsFunVComp = vecFunVComp Bit

constVComp :  {a} {A :  a} (_***_ : A  A  A)  VComposable _ (ConstArr A)
constVComp _***_ = mk _***_