Skip to content

Commit 11a4af2

Browse files
authored
Merge pull request #29 from anttih/ratio
Remove Rational newtype, move reducing to Ratio
2 parents 1919ece + d2b7d39 commit 11a4af2

File tree

4 files changed

+87
-75
lines changed

4 files changed

+87
-75
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,30 @@ You can turn a `Rational` to a `Number`:
2323
0.3
2424
```
2525

26+
## Other Ratios
27+
28+
`Rational` is just a type alias for `Ratio Int` and you might want to use
29+
`Ratio` with other than `Int`. The type you choose must however be an `EuclideanRing`.
30+
31+
For example, one limitation with `Rational` is that it can easily overflow
32+
the 32-bit PureScript `Int`. You can get around this problem by using
33+
[`BigInt`](https://pursuit.purescript.org/packages/purescript-bigints/3.1.0/docs/Data.BigInt#t:BigInt).
34+
35+
```
36+
> import Data.Ratio ((%), reduce)
37+
> import Data.BigInt (fromInt, fromString)
38+
> :type fromInt 1 % fromInt 3
39+
Ratio BigInt
40+
> reduce <$> fromString "10" <*> fromString "857981209301293808359384092830482"
41+
(Just fromString "5" % fromString "428990604650646904179692046415241")
42+
```
43+
2644
## Installation
2745

2846
```
2947
bower install purescript-rationals
3048
```
3149

32-
## Documentation
50+
## API documentation
3351

3452
Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-rationals/).

src/Data/Ratio.purs

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,63 @@
11
module Data.Ratio
2-
( Ratio(Ratio)
2+
( Ratio
3+
, reduce
4+
, (%)
35
, numerator
46
, denominator
5-
, gcd
67
) where
78

8-
import Prelude ( class CommutativeRing, class Eq, class EuclideanRing
9-
, class Field, class Ring, class Semiring
10-
, one, zero, (*), (+), (-)
11-
, class Show, show, (<>)
12-
)
13-
import Prelude ( gcd ) as Prelude
9+
import Prelude
10+
import Data.Ord (abs, signum)
1411

1512
data Ratio a = Ratio a a
1613

1714
instance showRatio :: Show a => Show (Ratio a) where
18-
show (Ratio a b) = "(Ratio " <> show a <> " " <> show b <> ")"
19-
20-
instance semiringRatio :: Semiring a => Semiring (Ratio a) where
15+
show (Ratio a b) = show a <> " % " <> show b
16+
17+
instance eqRatio :: Eq a => Eq (Ratio a) where
18+
eq (Ratio a b) (Ratio c d) = a == c && b == d
19+
20+
instance ordRatio :: (Ord a, EuclideanRing a) => Ord (Ratio a) where
21+
compare x y =
22+
case x - y of
23+
Ratio n d ->
24+
if n == zero
25+
then EQ
26+
else case n > zero, d > zero of
27+
true, true -> GT
28+
false, false -> GT
29+
_, _ -> LT
30+
31+
instance semiringRatio :: (Ord a, EuclideanRing a) => Semiring (Ratio a) where
2132
one = Ratio one one
22-
mul (Ratio a b) (Ratio c d) = Ratio (a * c) (b * d)
33+
mul (Ratio a b) (Ratio c d) = reduce (a * c) (b * d)
2334
zero = Ratio zero one
24-
add (Ratio a b) (Ratio c d) = Ratio ((a * d) + (b * c)) (b * d)
35+
add (Ratio a b) (Ratio c d) = reduce ((a * d) + (b * c)) (b * d)
2536

26-
instance ringRatio :: Ring a => Ring (Ratio a) where
27-
sub (Ratio a b) (Ratio c d) = Ratio ((a * d) - (b * c)) (b * d)
37+
instance ringRatio :: (Ord a, EuclideanRing a) => Ring (Ratio a) where
38+
sub (Ratio a b) (Ratio c d) = reduce ((a * d) - (b * c)) (b * d)
2839

29-
instance commutativeRingRatio :: CommutativeRing a => CommutativeRing (Ratio a)
40+
instance commutativeRingRatio :: (Ord a, EuclideanRing a) => CommutativeRing (Ratio a)
3041

31-
instance euclideanRingRatio :: (CommutativeRing a, Semiring a) => EuclideanRing (Ratio a) where
42+
instance euclideanRingRatio :: (Ord a, EuclideanRing a) => EuclideanRing (Ratio a) where
3243
degree _ = 1
33-
div (Ratio a b) (Ratio c d) = Ratio (a * d) (b * c)
44+
div (Ratio a b) (Ratio c d) = reduce (a * d) (b * c)
3445
mod _ _ = zero
3546

36-
instance fieldRatio :: Field a => Field (Ratio a)
47+
instance fieldRatio :: (Ord a, EuclideanRing a) => Field (Ratio a)
48+
49+
reduce :: forall a. Ord a => EuclideanRing a => a -> a -> Ratio a
50+
reduce n d =
51+
let
52+
g = gcd n d
53+
d' = d / g
54+
in
55+
Ratio ((n / g) * signum d') (abs d')
56+
57+
infixl 7 reduce as %
3758

3859
numerator :: forall a. Ratio a -> a
3960
numerator (Ratio a _) = a
4061

4162
denominator :: forall a. Ratio a -> a
4263
denominator (Ratio _ b) = b
43-
44-
gcd :: forall a. Eq a => EuclideanRing a => Ratio a -> a
45-
gcd (Ratio m n) = Prelude.gcd m n

src/Data/Rational.purs

Lines changed: 5 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,18 @@
11
module Data.Rational
22
( Rational
3-
, runRational
4-
, rational
5-
, (%)
63
, toNumber
74
, fromInt
5+
, module Data.Ratio
86
) where
97

108
import Prelude
119
import Data.Int as Int
12-
import Data.Ord (signum)
13-
import Data.Ratio (Ratio(Ratio))
10+
import Data.Ratio (Ratio, (%), numerator, denominator)
1411

15-
newtype Rational = Rational (Ratio Int)
16-
17-
runRational :: Rational -> Ratio Int
18-
runRational (Rational ratio) = ratio
19-
20-
instance showRational :: Show Rational where
21-
show (Rational (Ratio a b)) = show a <> " % " <> show b
22-
23-
instance eqRational :: Eq Rational where
24-
eq x y = eq' (reduce x) (reduce y)
25-
where
26-
eq' (Rational (Ratio a' b')) (Rational (Ratio c' d')) = a' == c' && b' == d'
27-
28-
instance ordRational :: Ord Rational where
29-
compare (Rational x) (Rational y) = case x / y of Ratio a b -> compare a b
30-
31-
instance semiringRational :: Semiring Rational where
32-
one = Rational one
33-
mul (Rational a) (Rational b) = reduce $ Rational $ a `mul` b
34-
zero = Rational zero
35-
add (Rational a) (Rational b) = reduce $ Rational $ a `add` b
36-
37-
instance ringRational :: Ring Rational where
38-
sub (Rational a) (Rational b) = reduce $ Rational $ a `sub` b
39-
40-
instance commutativeRingRational :: CommutativeRing Rational
41-
42-
instance euclideanRingRational :: EuclideanRing Rational where
43-
degree (Rational a) = degree a
44-
div (Rational a) (Rational b) = Rational $ a `div` b
45-
mod _ _ = Rational zero
46-
47-
instance fieldRational :: Field Rational
48-
49-
infixl 7 rational as %
50-
51-
rational :: Int -> Int -> Rational
52-
rational x y = reduce $ Rational $ Ratio x y
12+
type Rational = Ratio Int
5313

5414
toNumber :: Rational -> Number
55-
toNumber (Rational (Ratio a b)) = Int.toNumber a / Int.toNumber b
15+
toNumber x = Int.toNumber (numerator x) / Int.toNumber (denominator x)
5616

5717
fromInt :: Int -> Rational
58-
fromInt i = Rational $ Ratio i 1
59-
60-
reduce :: Rational -> Rational
61-
reduce (Rational (Ratio a b)) =
62-
let g = gcd a b
63-
b' = b / g
64-
in Rational $ Ratio ((a / g) * signum b') (degree b')
18+
fromInt i = i % 1

test/Main.purs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import Control.Monad.Eff.Console (CONSOLE, log)
77
import Control.Monad.Eff.Random (RANDOM)
88
import Control.Monad.Eff.Exception (EXCEPTION)
99

10-
import Data.Rational (Rational, (%))
10+
import Data.Ratio ((%))
11+
import Data.Rational (Rational)
1112

1213
import Test.StrongCheck (Result, quickCheck', (===))
1314
import Test.StrongCheck.Arbitrary (class Arbitrary)
@@ -21,6 +22,7 @@ newtype TestRational = TestRational Rational
2122

2223
derive newtype instance commutativeRingTestRational :: CommutativeRing TestRational
2324
derive newtype instance eqTestRational :: Eq TestRational
25+
derive newtype instance euclideanRingTestRational :: EuclideanRing TestRational
2426
derive newtype instance fieldTestRational :: Field TestRational
2527
derive newtype instance ordTestRational :: Ord TestRational
2628
derive newtype instance ringTestRational :: Ring TestRational
@@ -32,6 +34,16 @@ int = chooseInt (-999) 999
3234
nonZeroInt :: Gen Int
3335
nonZeroInt = int `suchThat` notEq 0
3436

37+
newtype SmallInt = SmallInt Int
38+
39+
instance arbitrarySmallInt :: Arbitrary SmallInt where
40+
arbitrary = SmallInt <$> int
41+
42+
newtype NonZeroInt = NonZeroInt Int
43+
44+
instance arbitraryNonZeroInt :: Arbitrary NonZeroInt where
45+
arbitrary = NonZeroInt <$> nonZeroInt
46+
3547
instance arbitraryTestRational :: Arbitrary TestRational where
3648
arbitrary = compose TestRational <<< (%) <$> int <*> nonZeroInt
3749

@@ -41,6 +53,9 @@ testRational = Proxy
4153
newtype TestRatNonZero = TestRatNonZero Rational
4254

4355
derive newtype instance eqTestRatNonZero :: Eq TestRatNonZero
56+
derive newtype instance semiringTestRatNonZero :: Semiring TestRatNonZero
57+
derive newtype instance ringTestRatNonZero :: Ring TestRatNonZero
58+
derive newtype instance commutativeRingTestRatNonZero :: CommutativeRing TestRatNonZero
4459
derive newtype instance euclideanRingTestRatNonZero :: EuclideanRing TestRatNonZero
4560

4661
instance arbitraryTestRatNonZero :: Arbitrary TestRatNonZero where
@@ -62,7 +77,14 @@ main = checkLaws "Rational" do
6277
log "Checking 'Remainder' law for MuduloSemiring"
6378
quickCheck' 1000 remainder
6479

80+
log "Checking `reduce`"
81+
quickCheck' 1000 reducing
82+
6583
where
6684

6785
remainder :: TestRatNonZero -> TestRatNonZero -> Result
6886
remainder (TestRatNonZero a) (TestRatNonZero b) = a / b * b + (a `mod` b) === a
87+
88+
reducing :: NonZeroInt -> NonZeroInt -> SmallInt -> NonZeroInt -> Result
89+
reducing (NonZeroInt a) (NonZeroInt b) (SmallInt n) (NonZeroInt d)
90+
= (a * n) % (a * d) === (b * n) % (b * d)

0 commit comments

Comments
 (0)