{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
module Natural (
  Natural
, HasNatural(..)
, AsNatural(..)
, ProductNatural(..)
, MaxNatural(..)
, MinNatural(..)
, zero
, zero'
, successor
, successor'
, plus
, multiply
, square
, zeroOr
, length
, replicate
, take
, drop
, splitAt
, (!!)
, findIndices
, findIndex
, elemIndices
, elemIndex
, minus
, list
, Positive
, HasPositive(..)
, AsPositive(..)
, SumPositive(..)
, MaxPositive(..)
, MinPositive(..)
, naturalPositive
, one
, one'
, successor1
, successor1'
, successorW
, plus1
, multiply1
, square1
, oneOr
, notZero
, length1
, replicate1
, take1
, drop1
, splitAt1
, (!!!)
, findIndices1
, findIndex1
, elemIndices1
, elemIndex1
, minus1
, list1
, plusone
, minusone
) where
import Control.Applicative(Const)
import Control.Category((.), id)
import Control.Lens(Wrapped(_Wrapped', Unwrapped), Rewrapped, Prism', Lens', Iso', (^?), ( # ), (^.), _Wrapped, prism', iso)
import Control.Monad((>>=))
import Data.Bool(Bool)
import Data.Eq(Eq((==)))
import Data.Foldable(Foldable(foldl))
import Data.Function(const)
import Data.Functor.Identity(Identity)
import Data.Int(Int)
import Data.List(iterate, zip, filter, map, repeat)
import Data.List.NonEmpty(NonEmpty((:|)))
import qualified Data.List.NonEmpty as NonEmpty(iterate, zip, filter)
import Data.Maybe(listToMaybe, Maybe(Just, Nothing), fromMaybe)
import Data.Monoid(Monoid(mappend, mempty))
import Data.Ord(Ord((<)), min, max)
import Data.Semigroup(Semigroup((<>)))
import Data.Semigroup.Foldable(Foldable1(foldMap1))
import Data.Tuple(fst, snd)
import Data.Word(Word)
import Prelude(Show, Integral, Integer, (-), (+), (*), (^), fromIntegral)
newtype Natural =
  Natural
    Integer
  deriving (Eq, Ord, Show)
instance Semigroup Natural where
  Natural x <> Natural y =
    Natural (x + y)
instance Monoid Natural where
  mappend =
    (<>)
  mempty =
    Natural 0
class HasNatural a where
  natural ::
    Lens'
      a
      Natural
instance HasNatural Natural where
  natural =
    id
class AsNatural a where
  _Natural ::
    Prism'
      a
      Natural
instance AsNatural Natural where
  _Natural =
    id
integralPrism ::
  Integral a =>
  Prism'
    a
    Natural
integralPrism =
  prism'
    (\(Natural n) -> fromIntegral n)
    (\n -> if n < 0 then Nothing else Just (Natural (fromIntegral n)))
instance AsNatural Int where
  _Natural =
    integralPrism
instance AsNatural Integer where
  _Natural =
    integralPrism
instance AsNatural Word where
  _Natural =
    integralPrism
instance Integral a => AsNatural (Const a b) where
  _Natural =
    integralPrism
instance Integral a => AsNatural (Identity a) where
  _Natural =
    integralPrism
newtype ProductNatural =
  ProductNatural
    Natural
  deriving (Eq, Ord, Show)
instance HasNatural ProductNatural where
  natural =
    _Wrapped . natural
instance AsNatural ProductNatural where
  _Natural =
    _Wrapped . _Natural
instance ProductNatural ~ a =>
  Rewrapped ProductNatural a
instance Wrapped ProductNatural where
  type Unwrapped ProductNatural = Natural
  _Wrapped' =
    iso
      (\(ProductNatural x) -> x)
      ProductNatural
instance Semigroup ProductNatural where
  ProductNatural (Natural x) <> ProductNatural (Natural y) =
    ProductNatural (Natural (x * y))
instance Monoid ProductNatural where
  mappend =
    (<>)
  mempty =
    ProductNatural (Natural 1)
newtype MaxNatural =
  MaxNatural
    Natural
  deriving (Eq, Ord, Show)
instance HasNatural MaxNatural where
  natural =
    _Wrapped . natural
instance AsNatural MaxNatural where
  _Natural =
    _Wrapped . _Natural
instance MaxNatural ~ a =>
  Rewrapped MaxNatural a
instance Wrapped MaxNatural where
  type Unwrapped MaxNatural = Natural
  _Wrapped' =
    iso
      (\(MaxNatural x) -> x)
      MaxNatural
instance Semigroup MaxNatural where
  MaxNatural (Natural x) <> MaxNatural (Natural y) =
    MaxNatural (Natural (x `max` y))
newtype MinNatural =
  MinNatural
    Natural
  deriving (Eq, Ord, Show)
instance HasNatural MinNatural where
  natural =
    _Wrapped . natural
instance AsNatural MinNatural where
  _Natural =
    _Wrapped . _Natural
instance MinNatural ~ a =>
  Rewrapped MinNatural a
instance Wrapped MinNatural where
  type Unwrapped MinNatural = Natural
  _Wrapped' =
    iso
      (\(MinNatural x) -> x)
      MinNatural
instance Semigroup MinNatural where
  MinNatural (Natural x) <> MinNatural (Natural y) =
    MinNatural (Natural (x `min` y))
zero ::
  Prism'
    Natural
    ()
zero =
  prism'
    (\() -> Natural 0)
    (\(Natural n) -> if n == 0 then Just () else Nothing)
zero' ::
  Natural
zero' =
  zero # ()
successor ::
  Prism'
    Natural
    Natural
successor =
  prism'
    (\(Natural n) -> Natural (n + 1))
    (\(Natural n) -> if n == 0 then Nothing else Just (Natural (n - 1)))
successor' ::
  Natural
  -> Natural
successor' =
  (successor #)
plus ::
  Natural
  -> Natural
  -> Natural
plus =
  (<>)
multiply ::
  Natural
  -> Natural
  -> Natural
multiply x y =
  (_Wrapped # x <> (_Wrapped # y :: ProductNatural)) ^. _Wrapped
square ::
  Natural
  -> Natural
  -> Natural
square (Natural x) (Natural y) =
  Natural (x ^ y)
zeroOr ::
  AsNatural a =>
  a
  -> Natural
zeroOr n =
  fromMaybe zero' (n ^? _Natural)
length ::
  Foldable f =>
  f a
  -> Natural
length =
  foldl (const . successor') zero'
replicate ::
  Natural
  -> a
  -> [a]
replicate n =
  take n . repeat
take ::
  Natural
  -> [a]
  -> [a]
take _ [] =
  []
take n (h:t) =
  case n ^? successor of
    Nothing ->
      []
    Just p ->
      h : take p t
drop ::
  Natural
  -> [a]
  -> [a]
drop _ [] =
  []
drop n (h:t) =
  case n ^? successor of
    Nothing ->
      h:t
    Just p ->
      drop p t
splitAt ::
  Natural
  -> [a]
  -> ([a], [a])
splitAt n x =
  (take n x, drop n x)
(!!) ::
  [a]
  -> Natural
  -> Maybe a
[] !! _ =
  Nothing
(_:t) !! n =
  (n ^? successor) >>= (t !!)
findIndices ::
  (a -> Bool)
  -> [a]
  -> [Natural]
findIndices p x =
  map snd (filter (p . fst) (zip x (iterate successor' zero')))
findIndex ::
  (a -> Bool)
  -> [a]
  -> Maybe Natural
findIndex p =
  listToMaybe . findIndices p
elemIndices ::
  Eq a =>
  a
  -> [a]
  -> [Natural]
elemIndices =
  findIndices . (==)
elemIndex ::
  Eq a =>
  a
  -> [a]
  -> Maybe Natural
elemIndex =
  findIndex . (==)
minus ::
  Natural
  -> Natural
  -> Natural
minus (Natural x) (Natural y) =
  Natural (if x < y then 0 else x - y)
list ::
  Iso'
    Natural
    [()]
list =
  iso
    (\n -> replicate n ())
    length
newtype Positive =
  Positive
    Integer
  deriving (Eq, Ord, Show)
instance Semigroup Positive where
  Positive x <> Positive y =
    Positive (x + y)
instance Monoid Positive where
  mappend =
    (<>)
  mempty =
    Positive 0
class HasPositive a where
  positive ::
    Lens'
      a
      Positive
instance HasPositive Positive where
  positive =
    id
class AsPositive a where
  _Positive ::
    Prism'
      a
      Positive
instance AsPositive Positive where
  _Positive =
    id
integralPrism1 ::
  Integral a =>
  Prism'
    a
    Positive
integralPrism1 =
  prism'
    (\(Positive n) -> fromIntegral n)
    (\n -> if n < 1 then Nothing else Just (Positive (fromIntegral n)))
instance AsPositive Int where
  _Positive =
    integralPrism1
instance AsPositive Integer where
  _Positive =
    integralPrism1
instance AsPositive Word where
  _Positive =
    integralPrism1
instance Integral a => AsPositive (Const a b) where
  _Positive =
    integralPrism1
instance Integral a => AsPositive (Identity a) where
  _Positive =
    integralPrism1
newtype SumPositive =
  SumPositive
    Positive
  deriving (Eq, Ord, Show)
instance HasPositive SumPositive where
  positive =
    _Wrapped . positive
instance AsPositive SumPositive where
  _Positive =
    _Wrapped . _Positive
instance SumPositive ~ a =>
  Rewrapped SumPositive a
instance Wrapped SumPositive where
  type Unwrapped SumPositive = Positive
  _Wrapped' =
    iso
      (\(SumPositive x) -> x)
      SumPositive
instance Semigroup SumPositive where
  SumPositive (Positive x) <> SumPositive (Positive y) =
    SumPositive (Positive (x + y))
newtype MaxPositive =
  MaxPositive
    Positive
  deriving (Eq, Ord, Show)
instance HasPositive MaxPositive where
  positive =
    _Wrapped . positive
instance AsPositive MaxPositive where
  _Positive =
    _Wrapped . _Positive
instance MaxPositive ~ a =>
  Rewrapped MaxPositive a
instance Wrapped MaxPositive where
  type Unwrapped MaxPositive = Positive
  _Wrapped' =
    iso
      (\(MaxPositive x) -> x)
      MaxPositive
instance Semigroup MaxPositive where
  MaxPositive (Positive x) <> MaxPositive (Positive y) =
    MaxPositive (Positive (x `max` y))
newtype MinPositive =
  MinPositive
    Positive
  deriving (Eq, Ord, Show)
instance HasPositive MinPositive where
  positive =
    _Wrapped . positive
instance AsPositive MinPositive where
  _Positive =
    _Wrapped . _Positive
instance MinPositive ~ a =>
  Rewrapped MinPositive a
instance Wrapped MinPositive where
  type Unwrapped MinPositive = Positive
  _Wrapped' =
    iso
      (\(MinPositive x) -> x)
      MinPositive
instance Semigroup MinPositive where
  MinPositive (Positive x) <> MinPositive (Positive y) =
    MinPositive (Positive (x `min` y))
naturalPositive ::
  Iso' Natural (Maybe Positive)
naturalPositive =
  iso
    (\(Natural n) ->
        if n == 0 then Nothing else Just (Positive n))
    (\x ->  Natural (
              case x of
                Nothing ->
                  0
                Just (Positive n) ->
                  n)
            )
instance AsPositive Natural where
  _Positive =
    prism'
      (\(Positive n) -> Natural n)
      (\(Natural n) -> if n == 0 then Nothing else Just (Positive n))
one ::
  Prism'
    Positive
    ()
one =
  prism'
    (\() -> Positive 1)
    (\(Positive n) -> if n == 1 then Just () else Nothing)
one' ::
  Positive
one' =
  one # ()
successor1 ::
  Prism'
    Positive
    Positive
successor1 =
  prism'
    (\(Positive n) -> Positive (n + 1))
    (\(Positive n) -> if n == 1 then Nothing else Just (Positive (n - 1)))
successor1' ::
  Positive
  -> Positive
successor1' =
  (successor1 #)
successorW ::
  Iso'
    Natural
    Positive
successorW =
  iso
    (\(Natural n) -> Positive (n + 1))
    (\(Positive n) -> Natural (n - 1))
plus1 ::
  Positive
  -> Positive
  -> Positive
plus1 x y =
  (_Wrapped # x <> (_Wrapped # y :: SumPositive)) ^. _Wrapped
multiply1 ::
  Positive
  -> Positive
  -> Positive
multiply1 =
  (<>)
square1 ::
  Positive
  -> Positive
  -> Positive
square1 (Positive x) (Positive y) =
  Positive (x ^ y)
oneOr ::
  AsPositive a =>
  a
  -> Positive
oneOr n =
  fromMaybe one' (n ^? _Positive)
notZero ::
  Prism'
    Natural
    Positive
notZero =
  prism'
    (\(Positive n) -> Natural n)
    (\(Natural n) -> if n == 0 then Nothing else Just (Positive n))
length1 ::
  Foldable1 f =>
  f a
  -> Positive
length1 x =
  foldMap1 (const (SumPositive one')) x ^. _Wrapped
replicate1 ::
  Positive
  -> a
  -> NonEmpty a
replicate1 n a =
  take1 n (a :| repeat a)
take1 ::
  Positive
  -> NonEmpty a
  -> NonEmpty a
take1 n (h:|t) =
  h :| take (successorW # n) t
drop1 ::
  Positive
  -> NonEmpty a
  -> [a]
drop1 n (_:|t) =
  drop (successorW # n) t
splitAt1 ::
  Positive
  -> NonEmpty a
  -> (NonEmpty a, [a])
splitAt1 n x =
  (take1 n x, drop1 n x)
(!!!) ::
  NonEmpty a
  -> Positive
  -> Maybe a
(_:|t) !!! n =
  t !! (successorW # n)
findIndices1 ::
  (a -> Bool)
  -> NonEmpty a
  -> [Positive]
findIndices1 p x =
  map snd (NonEmpty.filter (p . fst) (NonEmpty.zip x (NonEmpty.iterate successor1' one')))
findIndex1 ::
  (a -> Bool)
  -> NonEmpty a
  -> Maybe Positive
findIndex1 p =
  listToMaybe . findIndices1 p
elemIndices1 ::
  Eq a =>
  a
  -> NonEmpty a
  -> [Positive]
elemIndices1 =
  findIndices1 . (==)
elemIndex1 ::
  Eq a =>
  a
  -> NonEmpty a
  -> Maybe Positive
elemIndex1 =
  findIndex1 . (==)
minus1 ::
  Positive
  -> Positive
  -> Positive
minus1 (Positive x) (Positive y) =
  Positive (if x < y then 1 else x - y)
list1 ::
  Iso'
    Positive
    (NonEmpty ())
list1 =
  iso
    (\n -> replicate1 n ())
    length1
plusone ::
  Natural
  -> Positive
plusone =
  (^. successorW)
minusone ::
  Positive
  -> Natural
minusone =
  (successorW #)