{-|
Module      : Htcc.Utils
Description : Utilities
Copyright   : (c) roki, 2019
License     : MIT
Maintainer  : falgon53@yahoo.co.jp
Stability   : experimental
Portability : POSIX

General-purpose utilities
-}
{-# LANGUAGE BangPatterns, Rank2Types, ScopedTypeVariables, TupleSections,
             TypeOperators #-}
module Htcc.Utils (
    -- * Extra functions for lists
    module Htcc.Utils.List,
    -- * For Monad
    bothM, (*^*),
    -- * For Data.Maybe
    maybe',
    -- * For Char
    isStrictSpace,
    -- * For Data.Text
    module Htcc.Utils.Text,
    -- * For Numeric.Natural
    toNatural, toInteger,
    -- * For print shortcuts
    module Htcc.Utils.Print,
    -- * For triples and quadruple
    module Htcc.Utils.Tuple,
    -- * Boolean methods
    module Htcc.Utils.Bool,
    -- * Natural transformations
    module Htcc.Utils.NaturalTransformations,
    -- * For data type
    toInts
) where

import           Data.Char                         (isSpace)
import           Data.Tuple.Extra                  (both)
import           Numeric.Natural
import           Prelude                           hiding (toInteger)

import           Htcc.Utils.Bool
import           Htcc.Utils.List
import           Htcc.Utils.NaturalTransformations
import           Htcc.Utils.Print
import           Htcc.Utils.Text
import           Htcc.Utils.Tuple

{-# INLINE maybe' #-}
-- | `maybe'` is `maybe` with changed argument order.
maybe' :: b -> Maybe a -> (a -> b) -> b
maybe' :: b -> Maybe a -> (a -> b) -> b
maybe' n :: b
n m :: Maybe a
m f :: a -> b
f = b -> (a -> b) -> Maybe a -> b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe b
n a -> b
f Maybe a
m

-- | `toNatural` is a shortcut for @fromIntegral :: Integral i => i -> Natural@
{-# INLINE toNatural #-}
toNatural :: Integral i => i -> Natural
toNatural :: i -> Natural
toNatural = i -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | `toInteger` is a shortcut for @fromIntegral :: Natural -> Integer@
{-# INLINE toInteger #-}
toInteger :: Natural -> Integer
toInteger :: Natural -> Integer
toInteger = Natural -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Convert the instance of `Integral` to Int. When it cause overflow, express it as a list of `Int`s divided into multiple values.
-- `toInts` is useful for functions that have an `Int` type as an argument. e.g.:
--
-- >>> toInts (fromIntegral (maxBound :: Int) + 1 :: Integer)
-- [9223372036854775807,1]
-- >>> toInts (fromIntegral (maxBound :: Int) * 3 + 4 :: Integer)
-- [9223372036854775807,9223372036854775807,9223372036854775807,4]
toInts :: Integral i => i -> [Int]
toInts :: i -> [Int]
toInts !i
x = if Int
xd Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= 1 Bool -> Bool -> Bool
&& Int
xm Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 0 then [i -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral i
x] else Int -> Int -> [Int]
forall a. Int -> a -> [a]
replicate Int
xd (Int
forall a. Bounded a => a
maxBound :: Int) [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [Int
xm]
    where
        (!Int
xd, !Int
xm) = (i -> Int) -> (i, i) -> (Int, Int)
forall a b. (a -> b) -> (a, a) -> (b, b)
both i -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral ((i, i) -> (Int, Int)) -> (i, i) -> (Int, Int)
forall a b. (a -> b) -> a -> b
$ i
x i -> i -> (i, i)
forall a. Integral a => a -> a -> (a, a)
`divMod` Int -> i
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
forall a. Bounded a => a
maxBound :: Int)

-- | `isStrictSpace` returns True only if the given string is not a linefeed code and `Data.Char.isSpace` returns `True`, otherwise returns `False`.
isStrictSpace :: Char -> Bool
isStrictSpace :: Char -> Bool
isStrictSpace = [Char -> Bool] -> Char -> Bool
forall a. [a -> Bool] -> a -> Bool
land [(Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/='\n'), (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/='\r'), Char -> Bool
isSpace]

-- | The monadic `Data.Tuple.Extra.both`.
-- e.g.:
--
-- >>> a <- newIORef (42 :: Int)
-- >>> b <- newIORef (53 :: Int)
-- >>> bothM readIORef (a, b) >>= print
-- (42,53)
bothM :: Monad m => (a -> m b) -> (a, a) -> m (b, b)
bothM :: (a -> m b) -> (a, a) -> m (b, b)
bothM f :: a -> m b
f (x :: a
x, y :: a
y) = do
    b
x' <- a -> m b
f a
x
    (b
x',) (b -> (b, b)) -> m b -> m (b, b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> m b
f a
y

infixr 3 *^*

-- | The monadic `Data.Tuple.Extra.***`.
-- e.g.:
--
-- >>> a <- newIORef 1
-- >>> b <- newIORef 2
-- >>> (writeIORef a *^* writeIORef b) (42, 53) >> bothM readIORef (a, b) >>= print
-- (42,53)
(*^*) :: Monad m => (a -> m c) -> (b -> m d) -> (a, b) -> m (c, d)
*^* :: (a -> m c) -> (b -> m d) -> (a, b) -> m (c, d)
(*^*) f :: a -> m c
f g :: b -> m d
g (x :: a
x, y :: b
y) = do
    c
x' <- a -> m c
f a
x
    (c
x',) (d -> (c, d)) -> m d -> m (c, d)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> b -> m d
g b
y