This commit is contained in:
flywind 2021-02-06 10:33:11 -06:00 • committed by GitHub
commit 065264eae1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 30 deletions

View file

@ -28,7 +28,7 @@ type Rational*[T] = object
num*, den*: T num*, den*: T
func reduce*[T: SomeInteger](x: var Rational[T]) = func reduce*[T: SomeInteger](x: var Rational[T]) =
## Reduce the rational number `x`, so that the numerator and denominator ## Reduces the rational number `x`, so that the numerator and denominator
## have no common divisors other than 1 (and -1). ## have no common divisors other than 1 (and -1).
## If `x` is 0, raises `DivByZeroDefect`. ## If `x` is 0, raises `DivByZeroDefect`.
## ##
@ -50,7 +50,7 @@ func reduce*[T: SomeInteger](x: var Rational[T]) =
raise newException(DivByZeroDefect, "division by zero") raise newException(DivByZeroDefect, "division by zero")
func initRational*[T: SomeInteger](num, den: T): Rational[T] = func initRational*[T: SomeInteger](num, den: T): Rational[T] =
## Create a new rational number with numerator `num` and denominator `den`. ## Creates a new rational number with numerator `num` and denominator `den`.
## `den` must not be 0. ## `den` must not be 0.
## ##
## **Note:** `den != 0` is not checked when assertions are turned off. ## **Note:** `den != 0` is not checked when assertions are turned off.
@ -68,14 +68,14 @@ func `//`*[T](num, den: T): Rational[T] =
initRational[T](num, den) initRational[T](num, den)
func `$`*[T](x: Rational[T]): string = func `$`*[T](x: Rational[T]): string =
## Turn a rational number into a string. ## Turns a rational number into a string.
runnableExamples: runnableExamples:
doAssert $(1 // 2) == "1/2" doAssert $(1 // 2) == "1/2"
result = $x.num & "/" & $x.den result = $x.num & "/" & $x.den
func toRational*[T: SomeInteger](x: T): Rational[T] = func toRational*[T: SomeInteger](x: T): Rational[T] =
## Convert some integer `x` to a rational number. ## Converts some integer `x` to a rational number.
runnableExamples: runnableExamples:
doAssert toRational(42) == 42 // 1 doAssert toRational(42) == 42 // 1
@ -92,9 +92,8 @@ func toRational*(x: float,
# David Eppstein / UC Irvine / 8 Aug 1993 # David Eppstein / UC Irvine / 8 Aug 1993
# With corrections from Arno Formella, May 2008 # With corrections from Arno Formella, May 2008
runnableExamples: runnableExamples:
import std/math let x = 1.2
doAssert x.toRational.toFloat == x
doAssert almostEqual(PI.toRational.toFloat, PI)
var var
m11, m22 = 1 m11, m22 = 1
@ -113,40 +112,40 @@ func toRational*(x: float,
result = m11 // m21 result = m11 // m21
func toFloat*[T](x: Rational[T]): float = func toFloat*[T](x: Rational[T]): float =
## Convert a rational number `x` to a `float`. ## Converts a rational number `x` to a `float`.
x.num / x.den x.num / x.den
func toInt*[T](x: Rational[T]): int = func toInt*[T](x: Rational[T]): int =
## Convert a rational number `x` to an `int`. Conversion rounds towards 0 if ## Converts a rational number `x` to an `int`. Conversion rounds towards 0 if
## `x` does not contain an integer value. ## `x` does not contain an integer value.
x.num div x.den x.num div x.den
func `+`*[T](x, y: Rational[T]): Rational[T] = func `+`*[T](x, y: Rational[T]): Rational[T] =
## Add two rational numbers. ## Adds two rational numbers.
let common = lcm(x.den, y.den) let common = lcm(x.den, y.den)
result.num = common div x.den * x.num + common div y.den * y.num result.num = common div x.den * x.num + common div y.den * y.num
result.den = common result.den = common
reduce(result) reduce(result)
func `+`*[T](x: Rational[T], y: T): Rational[T] = func `+`*[T](x: Rational[T], y: T): Rational[T] =
## Add the rational `x` to the int `y`. ## Adds the rational `x` to the int `y`.
result.num = x.num + y * x.den result.num = x.num + y * x.den
result.den = x.den result.den = x.den
func `+`*[T](x: T, y: Rational[T]): Rational[T] = func `+`*[T](x: T, y: Rational[T]): Rational[T] =
## Add the int `x` to the rational `y`. ## Adds the int `x` to the rational `y`.
result.num = x * y.den + y.num result.num = x * y.den + y.num
result.den = y.den result.den = y.den
func `+=`*[T](x: var Rational[T], y: Rational[T]) = func `+=`*[T](x: var Rational[T], y: Rational[T]) =
## Add the rational `y` to the rational `x` in-place. ## Adds the rational `y` to the rational `x` in-place.
let common = lcm(x.den, y.den) let common = lcm(x.den, y.den)
x.num = common div x.den * x.num + common div y.den * y.num x.num = common div x.den * x.num + common div y.den * y.num
x.den = common x.den = common
reduce(x) reduce(x)
func `+=`*[T](x: var Rational[T], y: T) = func `+=`*[T](x: var Rational[T], y: T) =
## Add the int `y` to the rational `x` in-place. ## Adds the int `y` to the rational `x` in-place.
x.num += y * x.den x.num += y * x.den
func `-`*[T](x: Rational[T]): Rational[T] = func `-`*[T](x: Rational[T]): Rational[T] =
@ -155,64 +154,64 @@ func `-`*[T](x: Rational[T]): Rational[T] =
result.den = x.den result.den = x.den
func `-`*[T](x, y: Rational[T]): Rational[T] = func `-`*[T](x, y: Rational[T]): Rational[T] =
## Subtract two rational numbers. ## Subtracts two rational numbers.
let common = lcm(x.den, y.den) let common = lcm(x.den, y.den)
result.num = common div x.den * x.num - common div y.den * y.num result.num = common div x.den * x.num - common div y.den * y.num
result.den = common result.den = common
reduce(result) reduce(result)
func `-`*[T](x: Rational[T], y: T): Rational[T] = func `-`*[T](x: Rational[T], y: T): Rational[T] =
## Subtract the int `y` from the rational `x`. ## Subtracts the int `y` from the rational `x`.
result.num = x.num - y * x.den result.num = x.num - y * x.den
result.den = x.den result.den = x.den
func `-`*[T](x: T, y: Rational[T]): Rational[T] = func `-`*[T](x: T, y: Rational[T]): Rational[T] =
## Subtract the rational `y` from the int `x`. ## Subtracts the rational `y` from the int `x`.
result.num = x * y.den - y.num result.num = x * y.den - y.num
result.den = y.den result.den = y.den
func `-=`*[T](x: var Rational[T], y: Rational[T]) = func `-=`*[T](x: var Rational[T], y: Rational[T]) =
## Subtract the rational `y` from the rational `x` in-place. ## Subtracts the rational `y` from the rational `x` in-place.
let common = lcm(x.den, y.den) let common = lcm(x.den, y.den)
x.num = common div x.den * x.num - common div y.den * y.num x.num = common div x.den * x.num - common div y.den * y.num
x.den = common x.den = common
reduce(x) reduce(x)
func `-=`*[T](x: var Rational[T], y: T) = func `-=`*[T](x: var Rational[T], y: T) =
## Subtract the int `y` from the rational `x` in-place. ## Subtracts the int `y` from the rational `x` in-place.
x.num -= y * x.den x.num -= y * x.den
func `*`*[T](x, y: Rational[T]): Rational[T] = func `*`*[T](x, y: Rational[T]): Rational[T] =
## Multiply two rational numbers. ## Multiplies two rational numbers.
result.num = x.num * y.num result.num = x.num * y.num
result.den = x.den * y.den result.den = x.den * y.den
reduce(result) reduce(result)
func `*`*[T](x: Rational[T], y: T): Rational[T] = func `*`*[T](x: Rational[T], y: T): Rational[T] =
## Multiply the rational `x` with the int `y`. ## Multiplies the rational `x` with the int `y`.
result.num = x.num * y result.num = x.num * y
result.den = x.den result.den = x.den
reduce(result) reduce(result)
func `*`*[T](x: T, y: Rational[T]): Rational[T] = func `*`*[T](x: T, y: Rational[T]): Rational[T] =
## Multiply the int `x` with the rational `y`. ## Multiplies the int `x` with the rational `y`.
result.num = x * y.num result.num = x * y.num
result.den = y.den result.den = y.den
reduce(result) reduce(result)
func `*=`*[T](x: var Rational[T], y: Rational[T]) = func `*=`*[T](x: var Rational[T], y: Rational[T]) =
## Multiply the rational `x` by `y` in-place. ## Multiplies the rational `x` by `y` in-place.
x.num *= y.num x.num *= y.num
x.den *= y.den x.den *= y.den
reduce(x) reduce(x)
func `*=`*[T](x: var Rational[T], y: T) = func `*=`*[T](x: var Rational[T], y: T) =
## Multiply the rational `x` by the int `y` in-place. ## Multiplies the rational `x` by the int `y` in-place.
x.num *= y x.num *= y
reduce(x) reduce(x)
func reciprocal*[T](x: Rational[T]): Rational[T] = func reciprocal*[T](x: Rational[T]): Rational[T] =
## Calculate the reciprocal of `x` (`1/x`). ## Calculates the reciprocal of `x` (`1/x`).
## If `x` is 0, raises `DivByZeroDefect`. ## If `x` is 0, raises `DivByZeroDefect`.
if x.num > 0: if x.num > 0:
result.num = x.den result.num = x.den
@ -224,31 +223,31 @@ func reciprocal*[T](x: Rational[T]): Rational[T] =
raise newException(DivByZeroDefect, "division by zero") raise newException(DivByZeroDefect, "division by zero")
func `/`*[T](x, y: Rational[T]): Rational[T] = func `/`*[T](x, y: Rational[T]): Rational[T] =
## Divide the rational `x` by the rational `y`. ## Divides the rational `x` by the rational `y`.
result.num = x.num * y.den result.num = x.num * y.den
result.den = x.den * y.num result.den = x.den * y.num
reduce(result) reduce(result)
func `/`*[T](x: Rational[T], y: T): Rational[T] = func `/`*[T](x: Rational[T], y: T): Rational[T] =
## Divide the rational `x` by the int `y`. ## Divides the rational `x` by the int `y`.
result.num = x.num result.num = x.num
result.den = x.den * y result.den = x.den * y
reduce(result) reduce(result)
func `/`*[T](x: T, y: Rational[T]): Rational[T] = func `/`*[T](x: T, y: Rational[T]): Rational[T] =
## Divide the int `x` by the rational `y`. ## Divides the int `x` by the rational `y`.
result.num = x * y.den result.num = x * y.den
result.den = y.num result.den = y.num
reduce(result) reduce(result)
func `/=`*[T](x: var Rational[T], y: Rational[T]) = func `/=`*[T](x: var Rational[T], y: Rational[T]) =
## Divide the rational `x` by the rational `y` in-place. ## Divides the rational `x` by the rational `y` in-place.
x.num *= y.den x.num *= y.den
x.den *= y.num x.den *= y.num
reduce(x) reduce(x)
func `/=`*[T](x: var Rational[T], y: T) = func `/=`*[T](x: var Rational[T], y: T) =
## Divide the rational `x` by the int `y` in-place. ## Divides the rational `x` by the int `y` in-place.
x.den *= y x.den *= y
reduce(x) reduce(x)

View file

@ -96,5 +96,8 @@ template main() =
doAssert floorMod(3 // 10, 1 // 1) == 3 // 10 doAssert floorMod(3 // 10, 1 // 1) == 3 // 10
doAssert floorMod(-3 // 10, 1 // 1) == 7 // 10 doAssert floorMod(-3 // 10, 1 // 1) == 7 // 10
when sizeof(int) == 8:
doAssert almostEqual(PI.toRational.toFloat, PI)
static: main() static: main()
main() main()