Skip to content

Commit

Permalink
Add type PositiveNatural.
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanknowles committed Jan 13, 2021
1 parent 501db44 commit 9e101f6
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 0 deletions.
62 changes: 62 additions & 0 deletions lib/numeric/cardano-numeric.cabal
@@ -0,0 +1,62 @@
name: cardano-numeric
version: 2020.12.8
synopsis: Types and functions for performing numerical calculations.
homepage: https://github.com/input-output-hk/cardano-wallet
author: IOHK Engineering Team
maintainer: operations@iohk.io
copyright: 2018-2020 IOHK
license: Apache-2.0
category: Math
build-type: Simple
cabal-version: >=1.10

flag release
description: Enable optimization and `-Werror`
default: False
manual: True

library
default-language:
Haskell2010
default-extensions:
NoImplicitPrelude
OverloadedStrings
ghc-options:
-Wall
-Wcompat
-fwarn-redundant-constraints
if (flag(release))
ghc-options: -O2 -Werror
build-depends:
base
hs-source-dirs:
src
exposed-modules:
Cardano.Numeric.PositiveNatural

test-suite unit
default-language:
Haskell2010
default-extensions:
NoImplicitPrelude
OverloadedStrings
ghc-options:
-threaded -rtsopts
-Wall
if (flag(release))
ghc-options: -O2 -Werror
build-depends:
base
, cardano-numeric
, hspec
, QuickCheck
build-tools:
hspec-discover
type:
exitcode-stdio-1.0
hs-source-dirs:
test/unit
main-is:
Main.hs
other-modules:
Cardano.Numeric.PositiveNatural.Gen
193 changes: 193 additions & 0 deletions lib/numeric/src/Cardano/Numeric/PositiveNatural.hs
@@ -0,0 +1,193 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Cardano.Numeric.PositiveNatural
(
-- * Type
PositiveNatural

-- * Conversions
, fromIntegral
, toInteger
, toNatural

-- * Constants
, one

-- * Arithmetic Operations

-- ** Unary Arithmetic Operations
, pred
, succ

-- ** Binary Arithmetic Operations
, add
, sub
, mul
, div
, mod
, gcd
, lcm
, distance

-- ** Transformations
, Product (..)
, Sum (..)

) where

import GHC.Generics
( Generic )
import Numeric.Natural
( Natural )

import Prelude hiding
( and, div, fromIntegral, gcd, lcm, mod, or, pred, succ, toInteger )

import qualified Prelude

-- | Represents a strictly positive natural number, a member of the set
-- \( \mathbb {N}_{1} \).
--
-- Values of this type are greater than or equal to 'one'.
--
newtype PositiveNatural = PositiveNatural
{ unPositiveNatural :: Natural }
deriving stock (Eq, Generic, Ord)
deriving newtype Show

--------------------------------------------------------------------------------
-- Internal Functions
--------------------------------------------------------------------------------

wrap :: Natural -> PositiveNatural
wrap = PositiveNatural

unwrap :: PositiveNatural -> Natural
unwrap = unPositiveNatural

--------------------------------------------------------------------------------
-- Conversions
--------------------------------------------------------------------------------

-- | Constructs a 'PositiveNatural' from any 'Integral' value.
--
-- Returns 'Nothing' if the specified value is zero or negative.
--
fromIntegral :: Integral i => i -> Maybe PositiveNatural
fromIntegral i
| i > 0 =
Just $ wrap $ Prelude.fromIntegral i
| otherwise =
Nothing

-- | Converts a 'PositiveNatural' to a value of type 'Integer'.
--
toInteger :: PositiveNatural -> Integer
toInteger = Prelude.fromIntegral . unwrap

-- | Converts a 'PositiveNatural' to a value of type 'Natural'.
--
toNatural :: PositiveNatural -> Natural
toNatural = unwrap

--------------------------------------------------------------------------------
-- Constants
--------------------------------------------------------------------------------

-- | The smallest possible positive natural value.
--
one :: PositiveNatural
one = wrap 1

--------------------------------------------------------------------------------
-- Unary Arithmetic Operations
--------------------------------------------------------------------------------

-- | Calculates the predecessor of the given value by subtracting 'one'.
--
-- Since the result may be zero, the result is of type 'Natural'.
--
pred :: PositiveNatural -> Natural
pred = Prelude.pred . unwrap

-- | Calculates the successor of the given value by adding 'one'.
--
succ :: PositiveNatural -> PositiveNatural
succ = wrap . Prelude.succ . unwrap

--------------------------------------------------------------------------------
-- Binary Arithmetic Operations
--------------------------------------------------------------------------------

-- | Calculates the sum of two 'PositiveNatural' values.
--
add :: PositiveNatural -> PositiveNatural -> PositiveNatural
add a b = wrap $ unwrap a + unwrap b

-- | Calculates the product of two 'PositiveNatural' values.
--
mul :: PositiveNatural -> PositiveNatural -> PositiveNatural
mul a b = wrap $ unwrap a * unwrap b

-- | Subtracts the second 'PositiveNatural' value from the first.
--
-- Since the result may be negative, the result is of type 'Integer'.
--
sub :: PositiveNatural -> PositiveNatural -> Integer
sub a b = toInteger a - toInteger b

-- | Divides the first 'PositiveNatural' by the second.
--
-- The result is truncated toward zero.
--
-- Since the result may be zero, the result is of type 'Natural'.
--
div :: PositiveNatural -> PositiveNatural -> Natural
div a b = unwrap a `Prelude.div` unwrap b

-- | Finds the remainder of dividing the first 'PositiveNatural' by the second.
--
-- Since the result may be zero, the result is of type 'Natural'.
--
mod :: PositiveNatural -> PositiveNatural -> Natural
mod a b = unwrap a `Prelude.mod` unwrap b

-- | Calculates the greatest common divisor of two 'PositiveNatural' values.
--
gcd :: PositiveNatural -> PositiveNatural -> PositiveNatural
gcd a b = wrap $ unwrap a `Prelude.gcd` unwrap b

-- | Calculates the lowest common multiple of two 'PositiveNatural' values.
--
lcm :: PositiveNatural -> PositiveNatural -> PositiveNatural
lcm a b = wrap $ unwrap a `Prelude.lcm` unwrap b

-- | Finds the absolute difference between two 'PositiveNatural' values.
--
-- Since the distance may be zero, the result is of type 'Natural'.
--
distance :: PositiveNatural -> PositiveNatural -> Natural
distance a b
| a > b = unwrap a - unwrap b
| b > a = unwrap b - unwrap a
| otherwise = 0

--------------------------------------------------------------------------------
-- Transformations
--------------------------------------------------------------------------------

newtype Product = Product { unProduct :: PositiveNatural }

instance Semigroup Product where
Product a <> Product b = Product $ mul a b

instance Monoid Product where
mempty = Product one

newtype Sum = Sum { unSum :: PositiveNatural }

instance Semigroup Sum where
Sum a <> Sum b = Sum $ add a b
34 changes: 34 additions & 0 deletions lib/numeric/test/unit/Cardano/Numeric/PositiveNatural/Gen.hs
@@ -0,0 +1,34 @@
{-# LANGUAGE TypeApplications #-}

module Cardano.Numeric.PositiveNatural.Gen
( genPositiveNaturalAny
, shrinkPositiveNaturalAny
) where

import Prelude

import Cardano.Numeric.PositiveNatural
( PositiveNatural )
import Data.Maybe
( fromMaybe, mapMaybe )
import Test.QuickCheck
( Arbitrary (..), Gen, Positive (..), shrink )

import qualified Cardano.Numeric.PositiveNatural as PN

--------------------------------------------------------------------------------
-- Positive natural numbers chosen from the full range available, but biased
-- toward smaller numbers.
--------------------------------------------------------------------------------

genPositiveNaturalAny :: Gen PositiveNatural
genPositiveNaturalAny
= fromMaybe PN.one
. PN.fromIntegral
. getPositive @Integer <$> arbitrary

shrinkPositiveNaturalAny :: PositiveNatural -> [PositiveNatural]
shrinkPositiveNaturalAny
= mapMaybe (PN.fromIntegral @Integer)
. shrink
. PN.toInteger
1 change: 1 addition & 0 deletions lib/numeric/test/unit/Main.hs
@@ -0,0 +1 @@
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
45 changes: 45 additions & 0 deletions nix/.stack.nix/cardano-numeric.nix

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions stack.yaml
Expand Up @@ -10,6 +10,7 @@ packages:
- lib/core-integration
- lib/cli
- lib/launcher
- lib/numeric
- lib/text-class
- lib/test-utils
- lib/shelley
Expand Down

0 comments on commit 9e101f6

Please sign in to comment.