```{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators    #-}

{-|
Module      : Markov.Extra
Maintainer  : atloomis@math.arizona.edu
Description : Utilities for "Markov".
Stability   : Experimental
-}

module Markov.Extra
( fromLists
, randomPath
, (:*)
, (>*<)
) where

import qualified Data.List            as DL
import           Markov

---------------------------------------------------------------------------------------
-- Misc
---------------------------------------------------------------------------------------

-- |Randomly choose from a list by probability.
randomProduct :: (Real a, MR.MonadRandom m) => [(a, b)] -> m (a, b)
randomProduct = MR.fromList . fmap (\x -> (x, toRational \$ fst x))

-- |Returns a single realization of a Markov chain.
randomPath :: (Markov ((,) a) b, Real a, MR.RandomGen g) => g -> b -> [(a,b)]
randomPath g x = fmap (`MR.evalRand` g) . iterate (>>= (randomProduct . step))
\$ pure (1,x)

-- |Create a transition function from a transition matrix.
-- Inputs should obey:
--
-- > all (== length matrix) (map length matrix)
-- > length matrix == length states
fromLists :: Eq  b => [[a]] -> [b] -> b -> [(a, c -> b)]
fromLists matrix states b = case DL.elemIndex b states of
Nothing -> []
Just n  -> zip (matrix!!n) \$ fmap const states

---------------------------------------------------------------------------------------
-- Easier way to write nested 2-tuples
---------------------------------------------------------------------------------------

-- |Easier way to write nested 2-tuples.
type a :* b = (a,b)
-- |Easier way to write nested 2-tuples.
-- Left associative, binds weaker than @+@
-- but stronger than @==@.
(>*<) :: a -> b -> a :* b
a >*< b = (a,b)
infixl 5 >*<

```