```{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
module Numeric.Algebra.Free.Class
( FreeAlgebra(..)
, FreeCoalgebra(..)
) where

import Numeric.Semiring.Internal
import Prelude ()

-- A coassociative coalgebra over a semiring using
class Semiring r => FreeCoalgebra r c where
cojoin :: (c -> r) -> c -> c -> r

-- convolve :: (FreeAlgebra r a, FreeCoalgebra r c) => ((c -> r) -> a -> r) -> ((c -> r) -> a -> r) -> ((c -> r) -> a -> r

-- | Every coalgebra gives rise to an algebra by vector space duality classically.
-- Sadly, it requires vector space duality, which we cannot use constructively.
-- This is the dual, which relies in the fact that any constructive coalgebra can only inspect a finite number of coefficients.
instance FreeAlgebra r m => FreeCoalgebra r (m -> r) where
cojoin k f g = k (f * g)

instance FreeCoalgebra () c where
cojoin _ _ _ = ()

instance (FreeAlgebra r b, FreeCoalgebra r c) => FreeCoalgebra (b -> r) c where
cojoin f c1 c2 b = cojoin (`f` b) c1 c2

instance (FreeCoalgebra r a, FreeCoalgebra r b) => FreeCoalgebra r (a, b) where
cojoin f (a1,b1) (a2,b2) = cojoin (\a -> cojoin (\b -> f (a,b)) b1 b2) a1 a2

instance (FreeCoalgebra r a, FreeCoalgebra r b, FreeCoalgebra r c) => FreeCoalgebra r (a, b, c) where
cojoin f (a1,b1,c1) (a2,b2,c2) = cojoin (\a -> cojoin (\b -> cojoin (\c -> f (a,b,c)) c1 c2) b1 b2) a1 a2

instance (FreeCoalgebra r a, FreeCoalgebra r b, FreeCoalgebra r c, FreeCoalgebra r d) => FreeCoalgebra r (a, b, c, d) where
cojoin f (a1,b1,c1,d1) (a2,b2,c2,d2) = cojoin (\a -> cojoin (\b -> cojoin (\c -> cojoin (\d -> f (a,b,c,d)) d1 d2) c1 c2) b1 b2) a1 a2

instance (FreeCoalgebra r a, FreeCoalgebra r b, FreeCoalgebra r c, FreeCoalgebra r d, FreeCoalgebra r e) => FreeCoalgebra r (a, b, c, d, e) where
cojoin f (a1,b1,c1,d1,e1) (a2,b2,c2,d2,e2) = cojoin (\a -> cojoin (\b -> cojoin (\c -> cojoin (\d -> cojoin (\e -> f (a,b,c,d,e)) e1 e2) d1 d2) c1 c2) b1 b2) a1 a2
```