{-|
Copyright  :  (C) 2013-2016, University of Twente,
                  2017-2019, Myrtle Software Ltd, Google Inc.
License    :  BSD2 (see the file LICENSE)
Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com>

The Product/Signal isomorphism
-}

{-# LANGUAGE CPP                    #-}
{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE DefaultSignatures      #-}
{-# LANGUAGE KindSignatures         #-}
{-# LANGUAGE MagicHash              #-}
{-# LANGUAGE TemplateHaskell        #-}
{-# LANGUAGE TypeFamilies           #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
#if __GLASGOW_HASKELL__ < 806
{-# LANGUAGE TypeInType #-}
#endif

{-# LANGUAGE Trustworthy #-}

{-# OPTIONS_HADDOCK show-extensions #-}

module Clash.Signal.Bundle
  ( Bundle (..)
  )
where

import GHC.TypeLits                 (KnownNat)
import Prelude                      hiding (head, map, tail)

import Clash.Signal.Bundle.Internal (deriveBundleTuples)
import Clash.Signal.Internal        (Signal (..), Domain)
import Clash.Sized.BitVector        (Bit, BitVector)
import Clash.Sized.Fixed            (Fixed)
import Clash.Sized.Index            (Index)
import Clash.Sized.Signed           (Signed)
import Clash.Sized.Unsigned         (Unsigned)
import Clash.Sized.Vector           (Vec, traverse#, lazyV)
import Clash.Sized.RTree            (RTree, lazyT)

-- | Isomorphism between a 'Clash.Signal.Signal' of a product type (e.g. a tuple) and a
-- product type of 'Clash.Signal.Signal''s.
--
-- Instances of 'Bundle' must satisfy the following laws:
--
-- @
-- 'bundle' . 'unbundle' = 'id'
-- 'unbundle' . 'bundle' = 'id'
-- @
--
-- By default, 'bundle' and 'unbundle', are defined as the identity, that is,
-- writing:
--
-- @
-- data D = A | B
--
-- instance Bundle D
-- @
--
-- is the same as:
--
-- @
-- data D = A | B
--
-- instance Bundle D where
--   type 'Unbundled' clk D = 'Signal' clk D
--   'bundle'   s = s
--   'unbundle' s = s
-- @
--
-- For custom product types you'll have to write the instance manually:
-- @
-- data Pair a b = MkPair { getA :: a, getB :: b }
--
-- instance Bundle (Pair a b) where
--   type Unbundled dom (Pair a b) = Pair (Signal dom a) (Signal dom b)
--
--   -- bundle :: Pair (Signal dom a) (Signal dom b) -> Signal dom (Pair a b)
--   bundle   (MkPair as bs) = MkPair <$> as <*> bs
--
--   -- unbundle :: Signal dom (Pair a b) -> Pair (Signal dom a) (Signal dom b)
--   unbundle pairs = MkPair (getA <$> pairs) (getB <$> pairs)
-- @

class Bundle a where
  type Unbundled (dom :: Domain) a = res | res -> dom a
  type Unbundled dom a = Signal dom a
  -- | Example:
  --
  -- @
  -- __bundle__ :: ('Signal' dom a, 'Signal' dom b) -> 'Signal' dom (a,b)
  -- @
  --
  -- However:
  --
  -- @
  -- __bundle__ :: 'Signal' dom 'Clash.Sized.BitVector.Bit' -> 'Signal' dom 'Clash.Sized.BitVector.Bit'
  -- @
  bundle :: Unbundled dom a -> Signal dom a

  {-# INLINE bundle #-}
  default bundle :: (Signal dom a ~ Unbundled dom a)
                 => Unbundled dom a -> Signal dom a
  bundle s :: Unbundled dom a
s = Signal dom a
Unbundled dom a
s
  -- | Example:
  --
  -- @
  -- __unbundle__ :: 'Signal' dom (a,b) -> ('Signal' dom a, 'Signal' dom b)
  -- @
  --
  -- However:
  --
  -- @
  -- __unbundle__ :: 'Signal' dom 'Clash.Sized.BitVector.Bit' -> 'Signal' dom 'Clash.Sized.BitVector.Bit'
  -- @
  unbundle :: Signal dom a -> Unbundled dom a

  {-# INLINE unbundle #-}
  default unbundle :: (Unbundled dom a ~ Signal dom a)
                   => Signal dom a -> Unbundled dom a
  unbundle s :: Signal dom a
s = Signal dom a
Unbundled dom a
s

instance Bundle ()
instance Bundle Bool
instance Bundle Integer
instance Bundle Int
instance Bundle Float
instance Bundle Double
instance Bundle (Maybe a)
instance Bundle (Either a b)

instance Bundle Bit
instance Bundle (BitVector n)
instance Bundle (Index n)
instance Bundle (Fixed rep int frac)
instance Bundle (Signed n)
instance Bundle (Unsigned n)

deriveBundleTuples ''Bundle ''Unbundled 'bundle 'unbundle

instance KnownNat n => Bundle (Vec n a) where
  type Unbundled t (Vec n a) = Vec n (Signal t a)
  -- The 'Traversable' instance of 'Vec' is not synthesizable, so we must
  -- define 'bundle' as a primitive.
  bundle :: Unbundled dom (Vec n a) -> Signal dom (Vec n a)
bundle   = Unbundled dom (Vec n a) -> Signal dom (Vec n a)
forall (n :: Nat) (t :: Domain) a.
Vec n (Signal t a) -> Signal t (Vec n a)
vecBundle#
  unbundle :: Signal dom (Vec n a) -> Unbundled dom (Vec n a)
unbundle = Signal dom (Vec n a) -> Vec n (Signal dom a)
forall (t :: * -> *) (f :: * -> *) a.
(Traversable t, Applicative f) =>
t (f a) -> f (t a)
sequenceA (Signal dom (Vec n a) -> Vec n (Signal dom a))
-> (Signal dom (Vec n a) -> Signal dom (Vec n a))
-> Signal dom (Vec n a)
-> Vec n (Signal dom a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Vec n a -> Vec n a)
-> Signal dom (Vec n a) -> Signal dom (Vec n a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Vec n a -> Vec n a
forall (n :: Nat) a. KnownNat n => Vec n a -> Vec n a
lazyV

{-# NOINLINE vecBundle# #-}
vecBundle# :: Vec n (Signal t a) -> Signal t (Vec n a)
vecBundle# :: Vec n (Signal t a) -> Signal t (Vec n a)
vecBundle# = (Signal t a -> Signal t a)
-> Vec n (Signal t a) -> Signal t (Vec n a)
forall a (f :: * -> *) b (n :: Nat).
Applicative f =>
(a -> f b) -> Vec n a -> f (Vec n b)
traverse# Signal t a -> Signal t a
forall a. a -> a
id

instance KnownNat d => Bundle (RTree d a) where
  type Unbundled t (RTree d a) = RTree d (Signal t a)
  bundle :: Unbundled dom (RTree d a) -> Signal dom (RTree d a)
bundle   = Unbundled dom (RTree d a) -> Signal dom (RTree d a)
forall (t :: * -> *) (f :: * -> *) a.
(Traversable t, Applicative f) =>
t (f a) -> f (t a)
sequenceA
  unbundle :: Signal dom (RTree d a) -> Unbundled dom (RTree d a)
unbundle = Signal dom (RTree d a) -> RTree d (Signal dom a)
forall (t :: * -> *) (f :: * -> *) a.
(Traversable t, Applicative f) =>
t (f a) -> f (t a)
sequenceA (Signal dom (RTree d a) -> RTree d (Signal dom a))
-> (Signal dom (RTree d a) -> Signal dom (RTree d a))
-> Signal dom (RTree d a)
-> RTree d (Signal dom a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (RTree d a -> RTree d a)
-> Signal dom (RTree d a) -> Signal dom (RTree d a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap RTree d a -> RTree d a
forall (d :: Nat) a. KnownNat d => RTree d a -> RTree d a
lazyT