-----------------------------------------------------------------------------------------
-- |
-- Module      :  FRP.Yampa.Miscellany
-- Copyright   :  (c) Antony Courtney and Henrik Nilsson, Yale University, 2003
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  nilsson@cs.yale.edu
-- Stability   :  provisional
-- Portability :  portable
--
-- Collection of entities that really should be part of Haskell base, other packages,
-- or simply have no better home.
--
-----------------------------------------------------------------------------------------

module FRP.Yampa.Miscellany (
    -- * Reverse function composition
    ( # ),      -- :: (a -> b) -> (b -> c) -> (a -> c), infixl 9

    -- * Arrow plumbing aids
    dup,        -- :: a -> (a,a)

    -- * Maps over lists of pairs
    mapFst,     -- :: (a -> b) -> [(a,c)] -> [(b,c)]
    mapSnd,     -- :: (a -> b) -> [(c,a)] -> [(c,b)]

    -- * Generalized tuple selectors
    sel3_1, sel3_2, sel3_3,
    sel4_1, sel4_2, sel4_3, sel4_4,
    sel5_1, sel5_2, sel5_3, sel5_4, sel5_5,

    -- * Floating point utilities
    fDiv,       -- :: (RealFrac a, Integral b) => a -> a -> b
    fMod,       -- :: RealFrac a => a -> a -> a
    fDivMod,    -- :: (RealFrac a, Integral b) => a -> a -> (b, a)

    -- * Liftings
    arr2,       -- :: Arrow a => (b->c->d) -> a (b,c) d
    arr3,       -- :: Arrow a => (b->c->d->e) -> a (b,c,d) e
    arr4,       -- :: Arrow a => (b->c->d->e->f) -> a (b,c,d,e) f
    arr5,       -- :: Arrow a => (b->c->d->e->f->g) -> a (b,c,d,e,f) g
    lift0,      -- :: Arrow a => c -> a b c
    lift1,      -- :: Arrow a => (c->d) -> (a b c->a b d)
    lift2,      -- :: Arrow a => (c->d->e) -> (a b c->a b d->a b e)
    lift3,      -- :: Arrow a => (c->d->e->f) -> (a b c-> ... ->a b f)
    lift4,      -- :: Arrow a => (c->d->e->f->g) -> (a b c->...->a b g)
    lift5,      -- :: Arrow a => (c->d->e->f->g->h)->(a b c->...a b h)
) where

import Control.Arrow

infixl 9 #
infixl 7 `fDiv`, `fMod`

-- | Reverse function composition

-- !!! Reverse function composition should go.
-- !!! Better to use <<< and >>> for, respectively,
-- !!! function composition and reverse function composition.

{-# DEPRECATED (#) "Use Control.Arrow.(>>>) and Control.Arrow.(<<<)." #-}
( # ) :: (a -> b) -> (b -> c) -> (a -> c)
f # g = g . f


------------------------------------------------------------------------------
-- Arrow plumbing aids

-- | Duplicate an input.
dup :: a -> (a,a)
dup x = (x,x)

------------------------------------------------------------------------------
-- Maps over lists of pairs
------------------------------------------------------------------------------

-- | Map a function over the first component of pairs in a list.
{-# DEPRECATED mapFst "mapFst is not used by Yampa and will be removed from the next release" #-}
mapFst :: (a -> b) -> [(a,c)] -> [(b,c)]
mapFst f = map (\(x,y) -> (f x, y))

-- | Map a function over the second component of pairs in a list.
{-# DEPRECATED mapSnd "mapSnd is not used by Yampa and will be removed from the next release" #-}
mapSnd :: (a -> b) -> [(c,a)] -> [(c,b)]
mapSnd f = map (\(x,y) -> (x, f y))


------------------------------------------------------------------------------
-- Generalized tuple selectors
------------------------------------------------------------------------------

{-# DEPRECATED sel3_1, sel3_2, sel3_3 "Use the tuple package instead." #-}
-- Triples

-- | Select the first component of a triple.
sel3_1 :: (a, b, c) -> a
sel3_1 (x,_,_) = x

-- | Select the second component of a triple.
sel3_2 :: (a, b, c) -> b
sel3_2 (_,x,_) = x

-- | Select the third component of a triple.
sel3_3 :: (a, b, c) -> c
sel3_3 (_,_,x) = x


{-# DEPRECATED sel4_1, sel4_2, sel4_3, sel4_4 "Use the tuple package instead." #-}
-- 4-tuples
-- | Select the first component of a 4-element tuple.
sel4_1 :: (a, b, c, d) -> a
sel4_1 (x,_,_,_) = x
-- | Select the second component of a 4-element tuple.
sel4_2 :: (a, b, c, d) -> b
sel4_2 (_,x,_,_) = x
-- | Select the third component of a 4-element tuple.
sel4_3 :: (a, b, c, d) -> c
sel4_3 (_,_,x,_) = x
-- | Select the fourth component of a 4-element tuple.
sel4_4 :: (a, b, c, d) -> d
sel4_4 (_,_,_,x) = x


-- 5-tuples

{-# DEPRECATED sel5_1, sel5_2, sel5_3, sel5_4, sel5_5 "Use the tuple package instead." #-}
-- | Select the first component of a 5-element tuple.
sel5_1 :: (a, b, c, d, e) -> a
sel5_1 (x,_,_,_,_) = x
-- | Select the second component of a 5-element tuple.
sel5_2 :: (a, b, c, d, e) -> b
sel5_2 (_,x,_,_,_) = x
-- | Select the third component of a 5-element tuple.
sel5_3 :: (a, b, c, d, e) -> c
sel5_3 (_,_,x,_,_) = x
-- | Select the fourth component of a 5-element tuple.
sel5_4 :: (a, b, c, d, e) -> d
sel5_4 (_,_,_,x,_) = x
-- | Select the fifth component of a 5-element tuple.
sel5_5 :: (a, b, c, d, e) -> e
sel5_5 (_,_,_,_,x) = x


------------------------------------------------------------------------------
-- Floating point utilities
------------------------------------------------------------------------------

-- Floating-point div and modulo operators.

{-# DEPRECATED fDiv, fMod, fDivMod "These are not used by Yampa and will be removed." #-}
-- | Floating-point integer division.
fDiv :: (RealFrac a) => a -> a -> Integer
fDiv x y = fst (fDivMod x y)

-- | Floating-point modulo.
fMod :: (RealFrac a) => a -> a -> a
fMod x y = snd (fDivMod x y)

-- | Floating-point integer division and modulo.
fDivMod :: (RealFrac a) => a -> a -> (Integer, a)
fDivMod x y = (q, r)
    where
        q = (floor (x/y))
        r = x - fromIntegral q * y

-- * Arrows

-- ** Liftings

-- | Lift a binary function onto an arrow
arr2 :: Arrow a => (b -> c -> d) -> a (b, c) d
arr2 = arr . uncurry

-- | Lift a 3-ary function onto an arrow
arr3 :: Arrow a => (b -> c -> d -> e) -> a (b, c, d) e
arr3 = arr . \h (b, c, d) -> h b c d

-- | Lift a 4-ary function onto an arrow
arr4 :: Arrow a => (b -> c -> d -> e -> f) -> a (b, c, d, e) f
arr4 = arr . \h (b, c, d, e) -> h b c d e

-- | Lift a 5-ary function onto an arrow
arr5 :: Arrow a => (b -> c -> d -> e -> f -> g) -> a (b, c, d, e, f) g
arr5 = arr . \h (b, c, d, e, f) -> h b c d e f

-- | Lift an 0-ary function onto an arrow
--
-- If there was an @arr0@ function, this would be a synonym.
lift0 :: Arrow a => c -> a b c
lift0 c = arr (const  c)

-- | Lift a function into a function between arrows.
lift1 :: Arrow a => (c -> d) -> (a b c -> a b d)
lift1 f = \a -> a >>> arr f

-- | Lift a binary function into a function between arrows.
lift2 :: Arrow a => (c -> d -> e) -> (a b c -> a b d -> a b e)
lift2 f = \a1 a2 -> a1 &&& a2 >>> arr2 f

-- | Lift a 3-ary function into a function between arrows.
lift3 :: Arrow a => (c -> d -> e -> f) -> (a b c -> a b d -> a b e -> a b f)
lift3 f = \a1 a2 a3 -> (lift2 f) a1 a2 &&& a3 >>> arr2 ($)

-- | Lift a 4-ary function into a function between arrows.
lift4 :: Arrow a => (c->d->e->f->g) -> (a b c->a b d->a b e->a b f->a b g)
lift4 f = \a1 a2 a3 a4 -> (lift3 f) a1 a2 a3 &&& a4 >>> arr2 ($)

-- | Lift a 5-ary function into a function between arrows.
lift5 :: Arrow a =>
    (c->d->e->f->g->h) -> (a b c->a b d->a b e->a b f->a b g->a b h)
lift5 f = \a1 a2 a3 a4 a5 ->(lift4 f) a1 a2 a3 a4 &&& a5 >>> arr2 ($)