shithub: MicroHs

Download patch

ref: 32d735b3c57f473efede372cba30549bfb7119ac
parent: 467a298ff2034683199996d080b82dca6ee4cfb7
author: Lennart Augustsson <lennart.augustsson@epicgames.com>
date: Wed Mar 27 20:28:24 EDT 2024

Change Ratio a little to better conform to being a wheel.

--- a/lib/Data/Ratio.hs
+++ b/lib/Data/Ratio.hs
@@ -4,16 +4,13 @@
   numerator, denominator,
   rationalInfinity,
   rationalNaN,
-  Rational,
+  rationalMinusZero,
   ) where
 import Prelude()              -- do not import Prelude
-import Primitives
-import Control.Error
 import Data.Bool
 import Data.Eq
 import Data.Fractional
 import Data.Function
---import Data.Int
 import Data.Integer
 import Data.Integral
 import Data.Num
@@ -22,10 +19,26 @@
 import Text.Show
 
 {- in Data.Ratio_Type
-data Ratio a = (:%) a a   -- XXX should be strict
-
-type Rational = Ratio Integer
+data Ratio a = !a :% !a
 -}
+-- The value x :% y represents the rational number x/y.
+-- The y is always >= 0, and the number is in reduced for,
+-- i.e., there are no common factor > 1 for x and y.
+-- In addition to the ordinary rationals, we use the "wheel"
+-- extension of the rationals.
+-- This means that the / operation is total.  In particular
+--  x/0 == 1/0 == rationalInfinity, when x/=0
+--  0/0 == rationalNaN (often called perp for wheels).
+-- Wheels obey most of the usual algebraic laws for rationals,
+-- but the distributive law has to be augmented to work for all
+-- numbers.
+--  (x + y) * z + 0*z == x*z + y*z
+-- When z is a normal rational number the is clearly the same
+-- as the usual distributive law.
+-- 
+-- NOTE. Experimentally, we extend this with:
+--  x/0 ==  1/0 when x > 0
+--  x/0 == -1/0 when x < 0
 
 instance forall a . Eq a => Eq (Ratio a) where
   (x :% y) == (x' :% y')  =  x == x' && y == y'
@@ -36,7 +49,7 @@
   (x :% y) >= (x' :% y')  =  x * y' >= x' * y
   (x :% y) >  (x' :% y')  =  x * y' >  x' * y
 
-instance forall a . (Integral a) => Num (Ratio a) where
+instance forall a . (Integral a, Ord a) => Num (Ratio a) where
   (x:%y) + (x':%y')   =  reduce (x*y' + x'*y) (y*y')
   (x:%y) - (x':%y')   =  reduce (x*y' - x'*y) (y*y')
   (x:%y) * (x':%y')   =  reduce (x * x') (y * y')
@@ -46,11 +59,10 @@
   fromInteger x       =  fromInteger x :% 1
 
 instance forall a . (Integral a, Ord a) => Fractional (Ratio a) where
-  (x:%y) / (x':%y')   = (x*y') % (y*x')
+  (x:%y) / (x':%y')   = reduce (x*y') (y*x')
   recip (x:%y)
-    | y == 0        = error "Data.Ratio.recip: division by 0"
-    | x < 0         = negate y :% negate x
-    | otherwise     = y :% x
+    | x < 0           = (-y) :% (-x)
+    | otherwise       =  y :%  x
   fromRational (x:%y) =  fromInteger x % fromInteger y
 
 instance forall a . (Show a) => Show (Ratio a)  where
@@ -65,17 +77,17 @@
 rationalNaN :: Rational
 rationalNaN = 0 :% 0
 
+rationalMinusZero :: Rational
+rationalMinusZero = 0 :% (-1)
+
 infixl 7 %
-(%) :: forall a . (Integral a) => a -> a -> Ratio a
-x % y = reduce (x * signum y) (abs y)
+(%) :: forall a . (Integral a, Ord a) => a -> a -> Ratio a
+x % y = reduce x y
 
-reduce :: forall a . (Integral a) => a -> a -> Ratio a
-reduce x y =
-  if y == 0 then
-    error "Data.Ratio.%: 0 denominator"
-  else
-    let d = gcd x y
-    in  (x `quot` d) :% (y `quot` d)
+reduce :: forall a . (Ord a, Integral a) => a -> a -> Ratio a
+reduce x y | y > 0 = let d = gcd x y in (x `quot` d) :% (y `quot` d)
+           | y < 0 = reduce (- x) (- y)
+           | otherwise = signum x :% 0
 
 numerator :: forall a . Ratio a -> a
 numerator (x :% _) = x
--