module NatOptics.NonNegative
  (
    {- * Type constructor -} NonNegative,

    {- * Optics -}           refine, natPrism, intPrism,
                             natIso, textPrism, stringPrism,

    {- * Re-exports -}       Natural, Integer, Prism', Iso',
                             view, review, preview
  ) where

import Control.Applicative ( (*>) )
import Control.Monad       ( guard )
import Data.Bits           ( Bits, toIntegralSized )
import Data.Eq             ( Eq )
import Data.Function       ( (.) )
import Data.Functor        ( fmap, ($>), (<$>) )
import Data.Maybe          ( Maybe )
import Data.Ord            ( Ord, (>=) )
import Data.String         ( String )
import Data.Text           ( Text )
import NatOptics.Internal  ( strNat, textStr )
import Numeric.Natural     ( Natural )
import Optics.AffineFold   ( preview )
import Optics.Getter       ( view )
import Optics.Iso          ( Iso', iso )
import Optics.Optic        ( (%) )
import Optics.Prism        ( Prism', prism' )
import Optics.Review       ( review )
import Prelude             ( Integer, Integral, Num,
                             fromIntegral, toInteger )
import Text.Show           ( Show )

newtype NonNegative number = NonNegative{ NonNegative number -> number
number :: number }
    deriving newtype (NonNegative number -> NonNegative number -> Bool
(NonNegative number -> NonNegative number -> Bool)
-> (NonNegative number -> NonNegative number -> Bool)
-> Eq (NonNegative number)
forall number.
Eq number =>
NonNegative number -> NonNegative number -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NonNegative number -> NonNegative number -> Bool
$c/= :: forall number.
Eq number =>
NonNegative number -> NonNegative number -> Bool
== :: NonNegative number -> NonNegative number -> Bool
$c== :: forall number.
Eq number =>
NonNegative number -> NonNegative number -> Bool
Eq, Eq (NonNegative number)
Eq (NonNegative number)
-> (NonNegative number -> NonNegative number -> Ordering)
-> (NonNegative number -> NonNegative number -> Bool)
-> (NonNegative number -> NonNegative number -> Bool)
-> (NonNegative number -> NonNegative number -> Bool)
-> (NonNegative number -> NonNegative number -> Bool)
-> (NonNegative number -> NonNegative number -> NonNegative number)
-> (NonNegative number -> NonNegative number -> NonNegative number)
-> Ord (NonNegative number)
NonNegative number -> NonNegative number -> Bool
NonNegative number -> NonNegative number -> Ordering
NonNegative number -> NonNegative number -> NonNegative number
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall number. Ord number => Eq (NonNegative number)
forall number.
Ord number =>
NonNegative number -> NonNegative number -> Bool
forall number.
Ord number =>
NonNegative number -> NonNegative number -> Ordering
forall number.
Ord number =>
NonNegative number -> NonNegative number -> NonNegative number
min :: NonNegative number -> NonNegative number -> NonNegative number
$cmin :: forall number.
Ord number =>
NonNegative number -> NonNegative number -> NonNegative number
max :: NonNegative number -> NonNegative number -> NonNegative number
$cmax :: forall number.
Ord number =>
NonNegative number -> NonNegative number -> NonNegative number
>= :: NonNegative number -> NonNegative number -> Bool
$c>= :: forall number.
Ord number =>
NonNegative number -> NonNegative number -> Bool
> :: NonNegative number -> NonNegative number -> Bool
$c> :: forall number.
Ord number =>
NonNegative number -> NonNegative number -> Bool
<= :: NonNegative number -> NonNegative number -> Bool
$c<= :: forall number.
Ord number =>
NonNegative number -> NonNegative number -> Bool
< :: NonNegative number -> NonNegative number -> Bool
$c< :: forall number.
Ord number =>
NonNegative number -> NonNegative number -> Bool
compare :: NonNegative number -> NonNegative number -> Ordering
$ccompare :: forall number.
Ord number =>
NonNegative number -> NonNegative number -> Ordering
$cp1Ord :: forall number. Ord number => Eq (NonNegative number)
Ord, Int -> NonNegative number -> ShowS
[NonNegative number] -> ShowS
NonNegative number -> String
(Int -> NonNegative number -> ShowS)
-> (NonNegative number -> String)
-> ([NonNegative number] -> ShowS)
-> Show (NonNegative number)
forall number. Show number => Int -> NonNegative number -> ShowS
forall number. Show number => [NonNegative number] -> ShowS
forall number. Show number => NonNegative number -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [NonNegative number] -> ShowS
$cshowList :: forall number. Show number => [NonNegative number] -> ShowS
show :: NonNegative number -> String
$cshow :: forall number. Show number => NonNegative number -> String
showsPrec :: Int -> NonNegative number -> ShowS
$cshowsPrec :: forall number. Show number => Int -> NonNegative number -> ShowS
Show)

{- | For any numeric type @n@,
     @'NonNegative' n@ is a subset of @n@. -}
refine :: (Num n, Ord n) => Prism' n (NonNegative n)
refine :: Prism' n (NonNegative n)
refine = (NonNegative n -> n)
-> (n -> Maybe (NonNegative n)) -> Prism' n (NonNegative n)
forall b s a. (b -> s) -> (s -> Maybe a) -> Prism s s a b
prism' NonNegative n -> n
forall number. NonNegative number -> number
number n -> Maybe (NonNegative n)
forall n. (Ord n, Num n) => n -> Maybe (NonNegative n)
verify

{- | For any integral type @n@,
     @'NonNegative' n@ is a subset of 'Natural'. -}
natPrism :: (Integral n, Bits n) => Prism' Natural (NonNegative n)
natPrism :: Prism' Natural (NonNegative n)
natPrism =
    (NonNegative n -> Natural)
-> (Natural -> Maybe (NonNegative n))
-> Prism' Natural (NonNegative n)
forall b s a. (b -> s) -> (s -> Maybe a) -> Prism s s a b
prism'
        (n -> Natural
forall a b. (Integral a, Num b) => a -> b
fromIntegral (n -> Natural) -> (NonNegative n -> n) -> NonNegative n -> Natural
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonNegative n -> n
forall number. NonNegative number -> number
number)
        ((n -> NonNegative n) -> Maybe n -> Maybe (NonNegative n)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap n -> NonNegative n
forall number. number -> NonNegative number
NonNegative (Maybe n -> Maybe (NonNegative n))
-> (Natural -> Maybe n) -> Natural -> Maybe (NonNegative n)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> Maybe n
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
toIntegralSized) {- No need to verify
            here, because Natural is always non-negative. The only
            check here is when converting from 'Natural' to ensure
            that it does not overflow the max bound of 'n'. -}

{- | For any integral type @n@,
     @'NonNegative' n@ is a subset of 'Integer'. -}
intPrism :: (Integral n, Bits n) => Prism' Integer (NonNegative n)
intPrism :: Prism' Integer (NonNegative n)
intPrism = (NonNegative n -> Integer)
-> (Integer -> Maybe (NonNegative n))
-> Prism' Integer (NonNegative n)
forall b s a. (b -> s) -> (s -> Maybe a) -> Prism s s a b
prism' (n -> Integer
forall a. Integral a => a -> Integer
toInteger (n -> Integer) -> (NonNegative n -> n) -> NonNegative n -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonNegative n -> n
forall number. NonNegative number -> number
number) Integer -> Maybe (NonNegative n)
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe (NonNegative b)
verifyAndResize

{- | 'Natural' and @'NonNegative' 'Natural'@ are the same thing. -}
natIso :: Iso' Natural (NonNegative Natural)
natIso :: Iso' Natural (NonNegative Natural)
natIso = (Natural -> NonNegative Natural)
-> (NonNegative Natural -> Natural)
-> Iso' Natural (NonNegative Natural)
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso Natural -> NonNegative Natural
forall number. number -> NonNegative number
NonNegative NonNegative Natural -> Natural
forall number. NonNegative number -> number
number

stringPrism :: (Integral n, Bits n) => Prism' String (NonNegative n)
stringPrism :: Prism' String (NonNegative n)
stringPrism = Prism' String Natural
strNat Prism' String Natural
-> Optic
     A_Prism NoIx Natural Natural (NonNegative n) (NonNegative n)
-> Prism' String (NonNegative n)
forall k l m (is :: IxList) (js :: IxList) (ks :: IxList) s t u v a
       b.
(JoinKinds k l m, AppendIndices is js ks) =>
Optic k is s t u v -> Optic l js u v a b -> Optic m ks s t a b
% Optic A_Prism NoIx Natural Natural (NonNegative n) (NonNegative n)
forall n. (Integral n, Bits n) => Prism' Natural (NonNegative n)
natPrism

textPrism :: (Integral n, Bits n) => Prism' Text (NonNegative n)
textPrism :: Prism' Text (NonNegative n)
textPrism = Iso' Text String
textStr Iso' Text String
-> Optic A_Prism NoIx String String (NonNegative n) (NonNegative n)
-> Prism' Text (NonNegative n)
forall k l m (is :: IxList) (js :: IxList) (ks :: IxList) s t u v a
       b.
(JoinKinds k l m, AppendIndices is js ks) =>
Optic k is s t u v -> Optic l js u v a b -> Optic m ks s t a b
% Optic A_Prism NoIx String String (NonNegative n) (NonNegative n)
forall n. (Integral n, Bits n) => Prism' String (NonNegative n)
stringPrism

verify :: (Ord n, Num n) => n -> Maybe (NonNegative n)
verify :: n -> Maybe (NonNegative n)
verify n
n = Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (n
n n -> n -> Bool
forall a. Ord a => a -> a -> Bool
>= n
0) Maybe () -> NonNegative n -> Maybe (NonNegative n)
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> n -> NonNegative n
forall number. number -> NonNegative number
NonNegative n
n

verifyAndResize :: (Integral a, Integral b, Bits a, Bits b)
                => a -> Maybe (NonNegative b)
verifyAndResize :: a -> Maybe (NonNegative b)
verifyAndResize a
x = a -> Maybe (NonNegative a)
forall n. (Ord n, Num n) => n -> Maybe (NonNegative n)
verify a
x Maybe (NonNegative a)
-> Maybe (NonNegative b) -> Maybe (NonNegative b)
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (b -> NonNegative b
forall number. number -> NonNegative number
NonNegative (b -> NonNegative b) -> Maybe b -> Maybe (NonNegative b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> Maybe b
forall a b.
(Integral a, Integral b, Bits a, Bits b) =>
a -> Maybe b
toIntegralSized a
x)