packages feed

moonlight-core-0.1.0.0: src-basis/Moonlight/Core/Cardinality.hs

{-# LANGUAGE DerivingStrategies #-}

module Moonlight.Core.Cardinality
  ( CardinalityFailure (..),
    checkedNaturalToInt,
    checkedNonNegativeProduct,
    checkedNonNegativeSum,
  )
where

import Numeric.Natural (Natural)
import Prelude

data CardinalityFailure
  = NegativeCardinalityFactor Int
  | NaturalCardinalityExceedsIntRange Natural
  | CardinalityProductExceedsIntRange Int Int
  | CardinalitySumExceedsIntRange Int Int
  deriving stock (Eq, Show)

checkedNaturalToInt :: Natural -> Either CardinalityFailure Int
checkedNaturalToInt cardinality
  | cardinality > fromIntegral (maxBound :: Int) =
      Left (NaturalCardinalityExceedsIntRange cardinality)
  | otherwise =
      Right (fromIntegral cardinality)

checkedNonNegativeProduct :: Int -> Int -> Either CardinalityFailure Int
checkedNonNegativeProduct =
  checkedNonNegativeBinary CardinalityProductExceedsIntRange (*)

checkedNonNegativeSum :: Int -> Int -> Either CardinalityFailure Int
checkedNonNegativeSum =
  checkedNonNegativeBinary CardinalitySumExceedsIntRange (+)

checkedNonNegativeBinary ::
  (Int -> Int -> CardinalityFailure) ->
  (Integer -> Integer -> Integer) ->
  Int ->
  Int ->
  Either CardinalityFailure Int
checkedNonNegativeBinary rangeFailure combine left right
  | left < 0 = Left (NegativeCardinalityFactor left)
  | right < 0 = Left (NegativeCardinalityFactor right)
  | combinedValue > toInteger (maxBound :: Int) =
      Left (rangeFailure left right)
  | otherwise = Right (fromInteger combinedValue)
  where
    combinedValue = combine (toInteger left) (toInteger right)
{-# INLINE checkedNonNegativeBinary #-}