-- | Operations for working with a pair of elements of the same type.
module MCSP.Data.Pair (
    -- * Data Types
    Pair,
    pattern Pair,
    left,
    right,
    pattern First,
    pattern Second,
    pattern (::|),

    -- * Operations
    module Data.Tuple,
    module Data.Tuple.Extra,
    bothM,
    zipM,
    zip,
    unzip,
    cartesian,
    liftP,
    transpose,
    ($:),
) where

import Control.Applicative (Applicative, liftA2)
import Data.Function (id)
import Data.List (zip)
import Data.List.NonEmpty (unzip)
import Data.Tuple (fst, snd, swap, uncurry)
import Data.Tuple.Extra (both, dupe, first, firstM, second, secondM, (&&&), (***))

-- | A pair of elements of the same type @a@.
type Pair a = (a, a)

{-# COMPLETE Pair #-}
{-# COMPLETE First #-}
{-# COMPLETE Second #-}

-- | A pair of elements of the same type @a@.
pattern Pair :: a -> a -> Pair a
pattern $mPair :: forall {r} {a}. Pair a -> (a -> a -> r) -> ((# #) -> r) -> r
$bPair :: forall a. a -> a -> Pair a
Pair {forall a. Pair a -> a
left, forall a. Pair a -> a
right} = (left, right)
{-# INLINE CONLIKE Pair #-}

-- | Matches the first element of a pair.
--
-- >>> case ('x', 10) of First v -> v
-- 'x'
pattern First :: a -> (a, b)
pattern $mFirst :: forall {r} {a} {b}. (a, b) -> (a -> r) -> ((# #) -> r) -> r
First x <- (x, _)
{-# INLINE CONLIKE First #-}

-- | Matches the second element of a pair.
--
-- >>> case ('x', 10) of Second v -> v
-- 10
pattern Second :: b -> (a, b)
pattern $mSecond :: forall {r} {b} {a}. (a, b) -> (b -> r) -> ((# #) -> r) -> r
Second x <- (_, x)
{-# INLINE CONLIKE Second #-}

-- | Extracts the first two elements in a list as a pair.
--
-- >>> case [1, 2, 3] of (p ::| _) -> p
-- (1,2)
pattern (::|) :: Pair a -> [a] -> [a]
pattern p $m::| :: forall {r} {a}. [a] -> (Pair a -> [a] -> r) -> ((# #) -> r) -> r
$b::| :: forall a. Pair a -> [a] -> [a]
::| xs <- (\(a
x : a
y : [a]
rest) -> ((a
x, a
y), [a]
rest) -> (p, xs))
    where
        (a
x, a
y) ::| [a]
xs = a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
: a
y a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
xs
{-# INLINE CONLIKE (::|) #-}

-- | Apply an action to both components of a pair.
--
-- >>> import Data.List.NonEmpty (nonEmpty)
-- >>> bothM nonEmpty ([], [1])
-- Nothing
--
-- >>> bothM nonEmpty ([1], [2])
-- Just (1 :| [],2 :| [])
bothM :: Applicative m => (a -> m b) -> Pair a -> m (Pair b)
bothM :: forall (m :: * -> *) a b.
Applicative m =>
(a -> m b) -> Pair a -> m (Pair b)
bothM a -> m b
f (a
x, a
y) = (b -> b -> Pair b) -> m b -> m b -> m (Pair b)
forall a b c. (a -> b -> c) -> m a -> m b -> m c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,) (a -> m b
f a
x) (a -> m b
f a
y)
{-# INLINE bothM #-}

-- | Extract a pair elements from a pair of actions.
--
-- >>> import Data.Maybe (Maybe (..))
-- >>> zipM (Just 1, Nothing)
-- Nothing
--
-- >>> zipM (Just 1, Just 2)
-- Just (1,2)
--
-- >>> import Data.Int (Int)
-- >>> zipM @[] ([1, 2, 3], [4, 5])
-- [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]
zipM :: Applicative m => Pair (m a) -> m (Pair a)
zipM :: forall (m :: * -> *) a. Applicative m => Pair (m a) -> m (Pair a)
zipM = (m a -> m a) -> Pair (m a) -> m (Pair a)
forall (m :: * -> *) a b.
Applicative m =>
(a -> m b) -> Pair a -> m (Pair b)
bothM m a -> m a
forall a. a -> a
id
{-# INLINE zipM #-}

-- | Cartesian product of the elements of two lists.
--
-- >>> cartesian [1, 2, 3] "ab"
-- [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')]
cartesian :: [a] -> [b] -> [(a, b)]
cartesian :: forall a b. [a] -> [b] -> [(a, b)]
cartesian = (a -> b -> (a, b)) -> [a] -> [b] -> [(a, b)]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,)
{-# INLINE cartesian #-}

-- | Apply a binary operator in both elements of a pair.
--
-- >>> import GHC.Num ((+))
-- >>> liftP (+) (10, 20) (3, 4)
-- (13,24)
liftP :: (a -> b -> c) -> Pair a -> Pair b -> Pair c
liftP :: forall a b c. (a -> b -> c) -> Pair a -> Pair b -> Pair c
liftP a -> b -> c
op (a
x1, a
x2) (b
y1, b
y2) = (a
x1 a -> b -> c
`op` b
y1, a
x2 a -> b -> c
`op` b
y2)
{-# INLINE liftP #-}

-- | Transpose elements in a pair of pairs like in a square matrix.
--
-- >>> transpose (('a', 1), ('b', 2))
-- (('a','b'),(1,2))
transpose :: ((a, b), (c, d)) -> ((a, c), (b, d))
transpose :: forall a b c d. ((a, b), (c, d)) -> ((a, c), (b, d))
transpose ((a
x, b
y), (c
z, d
w)) = ((a
x, c
z), (b
y, d
w))
{-# INLINE transpose #-}

infixr 4 $:

-- | Spread a pair of values as arguments to a function.
--
-- Infix version of `uncurry`.
--
-- >>> import GHC.Num ((+))
-- >>> f x y = x + y
-- >>> f $: (1, 2)
-- 3
($:) :: (a -> b -> c) -> (a, b) -> c
$: :: forall a b c. (a -> b -> c) -> (a, b) -> c
($:) = (a -> b -> c) -> (a, b) -> c
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry
{-# INLINE ($:) #-}