integer-types-0.1.2.0: library/Integer/Positive/Unsafe.hs
-- | This module is unsafe not merely in the sense that it contains partial
-- functions, but moreover than it is capable of constructing the invalid
-- 'Positive' value @'FromNatural' 0@ representing zero, which is not positive.
-- When a function has "checked" in its name, this indicates that it is partial but
-- will never construct an invalid 'Positive'.
module Integer.Positive.Unsafe
( -- * Type
Positive (FromNatural),
-- * Conversion
-- ** Natural
toNatural,
fromNatural,
fromNaturalChecked,
-- ** Integer
toInteger,
fromInteger,
fromIntegerChecked,
-- ** Int
toInt,
fromInt,
fromIntChecked,
-- * Arithmetic
subtract,
subtractChecked,
-- * One (1)
one,
addOne,
subtractOne,
subtractOneChecked,
)
where
import Control.DeepSeq qualified as DeepSeq
import Control.Exception qualified as Exception
import Control.Monad.Fail (fail)
import Data.Bits qualified as Bits
import Data.Hashable (Hashable)
import Data.List qualified as List
import Data.Maybe qualified as Maybe
import Data.Ord qualified as Ord
import Essentials
import Integer.BoundedBelow (BoundedBelow)
import Integer.BoundedBelow qualified as BoundedBelow
import Numeric.Natural (Natural)
import Text.Read qualified as Read
import Text.Show qualified as Show
import Prelude (Int, Integer, Integral, Num, Read, Real)
import Prelude qualified as Enum (Enum (..))
import Prelude qualified as Num
( Integral (..),
Num (..),
Real (..),
fromIntegral,
)
newtype Positive = FromNatural {toNatural :: Natural}
deriving newtype (Eq, Ord, Hashable)
instance DeepSeq.NFData Positive where rnf (FromNatural x) = DeepSeq.rnf x
fromNatural :: Natural -> Positive
fromNatural = FromNatural
fromNaturalChecked :: Natural -> Positive
fromNaturalChecked x = case x of 0 -> Exception.throw Exception.Underflow; _ -> fromNatural x
toInteger :: Positive -> Integer
toInteger = Num.toInteger . toNatural
fromInteger :: Integer -> Positive
fromInteger = fromNatural . Num.fromInteger
fromIntegerChecked :: Integer -> Positive
fromIntegerChecked x = if x Ord.>= 1 then fromInteger x else Exception.throw Exception.Underflow
add :: Positive -> Positive -> Positive
add a b = fromNatural (toNatural a Num.+ toNatural b)
subtract :: Positive -> Positive -> Positive
subtract a b = fromNatural (toNatural a Num.- toNatural b)
subtractChecked :: Positive -> Positive -> Positive
subtractChecked a b = if a Ord.> b then subtract a b else Exception.throw Exception.Underflow
multiply :: Positive -> Positive -> Positive
multiply a b = fromNatural (toNatural a Num.* toNatural b)
one :: Positive
one = fromNatural 1
addOne :: Positive -> Positive
addOne = fromNatural . (Num.+ 1) . toNatural
subtractOne :: Positive -> Positive
subtractOne = fromNatural . (Num.- 1) . toNatural
subtractOneChecked :: Positive -> Positive
subtractOneChecked x = case x of 1 -> Exception.throw Exception.Underflow; _ -> subtractOne x
toInt :: Positive -> Int
toInt = Num.fromIntegral . toNatural
toIntChecked :: Positive -> Int
toIntChecked = Maybe.fromMaybe (Exception.throw Exception.Overflow) . Bits.toIntegralSized . toNatural
fromInt :: Int -> Positive
fromInt = fromNatural . Num.fromIntegral
fromIntChecked :: Int -> Positive
fromIntChecked x = case Num.signum x of 1 -> fromInt x; _ -> Exception.throw Exception.Underflow
enumFrom :: Positive -> [Positive]
enumFrom = List.map fromNatural . Enum.enumFrom . toNatural
enumFromTo :: Positive -> Positive -> [Positive]
enumFromTo a b = List.map fromNatural $ Enum.enumFromTo (toNatural a) (toNatural b)
enumFromThen :: Positive -> Positive -> [Positive]
enumFromThen a b = if a Ord.< b then ascending else descending
where
ascending = List.map fromNatural $ Enum.enumFromThen (toNatural a) (toNatural b)
descending =
List.map fromInteger $
List.takeWhile (Ord.>= 1) $
Enum.enumFromThen (toInteger a) (toInteger b)
enumFromThenTo :: Positive -> Positive -> Positive -> [Positive]
enumFromThenTo a b c = if a Ord.< b then ascending else descending
where
ascending = List.map fromNatural $ Enum.enumFromThenTo (toNatural a) (toNatural b) (toNatural c)
descending =
List.map fromInteger $
List.takeWhile (Ord.>= 1) $
Enum.enumFromThenTo (toInteger a) (toInteger b) (toInteger c)
type Div a = a -> a -> (a, a)
divisionOp :: Div Natural -> Div Positive
divisionOp o a b =
let (q, r) = o (toNatural a) (toNatural b)
in (fromNaturalChecked q, fromNaturalChecked r)
instance BoundedBelow Positive where
minBound = 1
instance Num Positive where
abs = id
negate = \_ -> Exception.throw Exception.Underflow
signum = \_ -> fromNatural 1
fromInteger = fromIntegerChecked
(+) = add
(*) = multiply
(-) = subtractChecked
instance Enum Positive where
succ = addOne
pred = subtractOneChecked
fromEnum = toIntChecked
toEnum = fromIntChecked
enumFrom = enumFrom
enumFromTo = enumFromTo
enumFromThen = enumFromThen
enumFromThenTo = enumFromThenTo
instance Real Positive where
toRational = Num.toRational . toInteger
instance Integral Positive where
toInteger = toInteger
quotRem = divisionOp Num.quotRem
divMod = divisionOp Num.divMod
instance Show Positive where
show = Show.show . toNatural
showsPrec i = Show.showsPrec i . toNatural
instance Read Positive where
readsPrec i = do
xs <- Read.readsPrec @Natural i
pure $ xs & Maybe.mapMaybe \case
(0, _) -> Nothing
(n, s) -> Just (fromNatural n, s)
readPrec = do
n <- Read.readPrec @Natural
if n == 0 then fail "0" else pure $ fromNatural n