packages feed

ordinal-0.6.0.0: src/Text/Numerals/Internal.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE Safe #-}

module Text.Numerals.Internal
  ( _div10,
    _rem10,
    _divisableBy,
    _divisable100,
    _pluralize,
    _pluralize',
    _showText,
    _mergeWith,
    _mergeWithSpace,
    _mergeWithHyphen,
    _mergeWith',
    _replaceSuffix,
    _hundred,
    _thousand,
    _million,
    _billion,
    _trillion,
    _iLog,
    _iLogFloor,
    _stripLastIf,
    _showIntegral,
    _showPositive,
    _genText,
    _shrinkText,
  )
where

import Control.Applicative (liftA2)
import Data.Char (intToDigit)
#if __GLASGOW_HASKELL__ < 803
import Data.Semigroup((<>))
#endif
import Data.Text (Text, cons, dropEnd, inits, isSuffixOf, pack, singleton, tails)
import qualified Data.Text as T
import Test.QuickCheck (listOf)
import Test.QuickCheck.Arbitrary (Arbitrary (arbitrary))
import Test.QuickCheck.Gen (Gen)

_pluralize :: a -> a -> Int -> a
_pluralize sing plur = go
  where
    go 1 = sing
    go (-1) = sing
    go _ = plur

_pluralize' :: a -> a -> Int -> a
_pluralize' sing plur = go
  where
    go 1 = sing
    go _ = plur

_stripLastIf :: Char -> Text -> Text
_stripLastIf c t
  | singleton c `isSuffixOf` t = T.init t
  | otherwise = t

_mergeWith' :: Char -> Text -> Text -> Text
_mergeWith' m = (. cons m) . (<>)

_mergeWithSpace :: Text -> Text -> Text
_mergeWithSpace = _mergeWith' ' '

_mergeWithHyphen :: Text -> Text -> Text
_mergeWithHyphen = _mergeWith' '-'

_mergeWith :: Text -> Text -> Text -> Text
_mergeWith m = (<>) . (<> m)

_showText :: Show a => a -> Text
_showText = pack . show

_divisableBy :: Integral i => i -> i -> Bool
_divisableBy n = (0 ==) . (`mod` n)

_divisable100 :: Integral i => i -> Bool
_divisable100 = _divisableBy _hundred

_div10 :: Integral i => i -> i
_div10 = (`div` _ten)

_rem10 :: Integral i => i -> i
_rem10 = (`rem` _ten)

_ten :: Integral i => i
_ten = 10

_hundred :: Integral i => i
_hundred = 100

_thousand :: Integral i => i
_thousand = 1000

_million :: Integral i => i
_million = 1000000

_billion :: Integral i => i
_billion = 1000000000

_trillion :: Integral i => i
_trillion = 1000000000000

_iLogFloor :: (Integral i, Integral j) => i -> i -> (i, j, i)
_iLogFloor b m = go b
  where
    go i
      | m < i = (m, 0, 1)
      | q < i = (q, 2 * e, j)
      | otherwise = (div q i, 2 * e + 1, j * i)
      where
        ~(q, e, j) = go (i * i)

_iLog :: (Integral i, Integral j) => i -> i -> Maybe j
_iLog b m = snd <$> go b
  where
    go i
      | m < i = Just (m, 0)
      | Just (q, e) <- go (i * i) = go' i q e
      | otherwise = Nothing
    go' i q e
      | q < i = Just (q, 2 * e)
      | md == 0 = Just (d, 2 * e + 1)
      | otherwise = Nothing
      where
        (d, md) = divMod q i

_replaceSuffix :: Int -> Text -> Text -> Text
_replaceSuffix n s = (<> s) . dropEnd n

_showIntegral :: Integral i => i -> String -> String
_showIntegral n s
  | n < 0 = '-' : _showPositive (-(fromIntegral n :: Integer)) s
  | otherwise = _showPositive n s

_showPositive :: Integral i => i -> String -> String
_showPositive n s
  | q == 0 = tl
  | otherwise = _showPositive q tl
  where
    (q, r) = quotRem n 10
    tl = intToDigit (fromIntegral r) : s

_genText :: Gen Text
_genText = pack <$> listOf arbitrary

_shrinkText :: Text -> [Text]
_shrinkText = liftA2 (zipWith (<>)) inits (tails . T.drop 1)