rationals.nim 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Dennis Felsing
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements rational numbers, consisting of a numerator and
  10. ## a denominator. The denominator can not be 0.
  11. runnableExamples:
  12. let
  13. r1 = 1 // 2
  14. r2 = -3 // 4
  15. doAssert r1 + r2 == -1 // 4
  16. doAssert r1 - r2 == 5 // 4
  17. doAssert r1 * r2 == -3 // 8
  18. doAssert r1 / r2 == -2 // 3
  19. import math, hashes
  20. when defined(nimPreviewSlimSystem):
  21. import std/assertions
  22. type Rational*[T] = object
  23. ## A rational number, consisting of a numerator `num` and a denominator `den`.
  24. num*, den*: T
  25. func reduce*[T: SomeInteger](x: var Rational[T]) =
  26. ## Reduces the rational number `x`, so that the numerator and denominator
  27. ## have no common divisors other than 1 (and -1).
  28. ## If `x` is 0, raises `DivByZeroDefect`.
  29. ##
  30. ## **Note:** This is called automatically by the various operations on rationals.
  31. runnableExamples:
  32. var r = Rational[int](num: 2, den: 4) # 1/2
  33. reduce(r)
  34. doAssert r.num == 1
  35. doAssert r.den == 2
  36. let common = gcd(x.num, x.den)
  37. if x.den > 0:
  38. x.num = x.num div common
  39. x.den = x.den div common
  40. elif x.den < 0:
  41. x.num = -x.num div common
  42. x.den = -x.den div common
  43. else:
  44. raise newException(DivByZeroDefect, "division by zero")
  45. func initRational*[T: SomeInteger](num, den: T): Rational[T] =
  46. ## Creates a new rational number with numerator `num` and denominator `den`.
  47. ## `den` must not be 0.
  48. ##
  49. ## **Note:** `den != 0` is not checked when assertions are turned off.
  50. assert(den != 0, "a denominator of zero is invalid")
  51. result.num = num
  52. result.den = den
  53. reduce(result)
  54. func `//`*[T](num, den: T): Rational[T] =
  55. ## A friendlier version of `initRational <#initRational,T,T>`_.
  56. runnableExamples:
  57. let x = 1 // 3 + 1 // 5
  58. doAssert x == 8 // 15
  59. initRational[T](num, den)
  60. func `$`*[T](x: Rational[T]): string =
  61. ## Turns a rational number into a string.
  62. runnableExamples:
  63. doAssert $(1 // 2) == "1/2"
  64. result = $x.num & "/" & $x.den
  65. func toRational*[T: SomeInteger](x: T): Rational[T] =
  66. ## Converts some integer `x` to a rational number.
  67. runnableExamples:
  68. doAssert toRational(42) == 42 // 1
  69. result.num = x
  70. result.den = 1
  71. func toRational*(x: float,
  72. n: int = high(int) shr (sizeof(int) div 2 * 8)): Rational[int] =
  73. ## Calculates the best rational approximation of `x`,
  74. ## where the denominator is smaller than `n`
  75. ## (default is the largest possible `int` for maximal resolution).
  76. ##
  77. ## The algorithm is based on the theory of continued fractions.
  78. # David Eppstein / UC Irvine / 8 Aug 1993
  79. # With corrections from Arno Formella, May 2008
  80. runnableExamples:
  81. let x = 1.2
  82. doAssert x.toRational.toFloat == x
  83. var
  84. m11, m22 = 1
  85. m12, m21 = 0
  86. ai = int(x)
  87. x = x
  88. while m21 * ai + m22 <= n:
  89. swap m12, m11
  90. swap m22, m21
  91. m11 = m12 * ai + m11
  92. m21 = m22 * ai + m21
  93. if x == float(ai): break # division by zero
  94. x = 1 / (x - float(ai))
  95. if x > float(high(int32)): break # representation failure
  96. ai = int(x)
  97. result = m11 // m21
  98. func toFloat*[T](x: Rational[T]): float =
  99. ## Converts a rational number `x` to a `float`.
  100. x.num / x.den
  101. func toInt*[T](x: Rational[T]): int =
  102. ## Converts a rational number `x` to an `int`. Conversion rounds towards 0 if
  103. ## `x` does not contain an integer value.
  104. x.num div x.den
  105. func `+`*[T](x, y: Rational[T]): Rational[T] =
  106. ## Adds two rational numbers.
  107. let common = lcm(x.den, y.den)
  108. result.num = common div x.den * x.num + common div y.den * y.num
  109. result.den = common
  110. reduce(result)
  111. func `+`*[T](x: Rational[T], y: T): Rational[T] =
  112. ## Adds the rational `x` to the int `y`.
  113. result.num = x.num + y * x.den
  114. result.den = x.den
  115. func `+`*[T](x: T, y: Rational[T]): Rational[T] =
  116. ## Adds the int `x` to the rational `y`.
  117. result.num = x * y.den + y.num
  118. result.den = y.den
  119. func `+=`*[T](x: var Rational[T], y: Rational[T]) =
  120. ## Adds the rational `y` to the rational `x` in-place.
  121. let common = lcm(x.den, y.den)
  122. x.num = common div x.den * x.num + common div y.den * y.num
  123. x.den = common
  124. reduce(x)
  125. func `+=`*[T](x: var Rational[T], y: T) =
  126. ## Adds the int `y` to the rational `x` in-place.
  127. x.num += y * x.den
  128. func `-`*[T](x: Rational[T]): Rational[T] =
  129. ## Unary minus for rational numbers.
  130. result.num = -x.num
  131. result.den = x.den
  132. func `-`*[T](x, y: Rational[T]): Rational[T] =
  133. ## Subtracts two rational numbers.
  134. let common = lcm(x.den, y.den)
  135. result.num = common div x.den * x.num - common div y.den * y.num
  136. result.den = common
  137. reduce(result)
  138. func `-`*[T](x: Rational[T], y: T): Rational[T] =
  139. ## Subtracts the int `y` from the rational `x`.
  140. result.num = x.num - y * x.den
  141. result.den = x.den
  142. func `-`*[T](x: T, y: Rational[T]): Rational[T] =
  143. ## Subtracts the rational `y` from the int `x`.
  144. result.num = x * y.den - y.num
  145. result.den = y.den
  146. func `-=`*[T](x: var Rational[T], y: Rational[T]) =
  147. ## Subtracts the rational `y` from the rational `x` in-place.
  148. let common = lcm(x.den, y.den)
  149. x.num = common div x.den * x.num - common div y.den * y.num
  150. x.den = common
  151. reduce(x)
  152. func `-=`*[T](x: var Rational[T], y: T) =
  153. ## Subtracts the int `y` from the rational `x` in-place.
  154. x.num -= y * x.den
  155. func `*`*[T](x, y: Rational[T]): Rational[T] =
  156. ## Multiplies two rational numbers.
  157. result.num = x.num * y.num
  158. result.den = x.den * y.den
  159. reduce(result)
  160. func `*`*[T](x: Rational[T], y: T): Rational[T] =
  161. ## Multiplies the rational `x` with the int `y`.
  162. result.num = x.num * y
  163. result.den = x.den
  164. reduce(result)
  165. func `*`*[T](x: T, y: Rational[T]): Rational[T] =
  166. ## Multiplies the int `x` with the rational `y`.
  167. result.num = x * y.num
  168. result.den = y.den
  169. reduce(result)
  170. func `*=`*[T](x: var Rational[T], y: Rational[T]) =
  171. ## Multiplies the rational `x` by `y` in-place.
  172. x.num *= y.num
  173. x.den *= y.den
  174. reduce(x)
  175. func `*=`*[T](x: var Rational[T], y: T) =
  176. ## Multiplies the rational `x` by the int `y` in-place.
  177. x.num *= y
  178. reduce(x)
  179. func reciprocal*[T](x: Rational[T]): Rational[T] =
  180. ## Calculates the reciprocal of `x` (`1/x`).
  181. ## If `x` is 0, raises `DivByZeroDefect`.
  182. if x.num > 0:
  183. result.num = x.den
  184. result.den = x.num
  185. elif x.num < 0:
  186. result.num = -x.den
  187. result.den = -x.num
  188. else:
  189. raise newException(DivByZeroDefect, "division by zero")
  190. func `/`*[T](x, y: Rational[T]): Rational[T] =
  191. ## Divides the rational `x` by the rational `y`.
  192. result.num = x.num * y.den
  193. result.den = x.den * y.num
  194. reduce(result)
  195. func `/`*[T](x: Rational[T], y: T): Rational[T] =
  196. ## Divides the rational `x` by the int `y`.
  197. result.num = x.num
  198. result.den = x.den * y
  199. reduce(result)
  200. func `/`*[T](x: T, y: Rational[T]): Rational[T] =
  201. ## Divides the int `x` by the rational `y`.
  202. result.num = x * y.den
  203. result.den = y.num
  204. reduce(result)
  205. func `/=`*[T](x: var Rational[T], y: Rational[T]) =
  206. ## Divides the rational `x` by the rational `y` in-place.
  207. x.num *= y.den
  208. x.den *= y.num
  209. reduce(x)
  210. func `/=`*[T](x: var Rational[T], y: T) =
  211. ## Divides the rational `x` by the int `y` in-place.
  212. x.den *= y
  213. reduce(x)
  214. func cmp*(x, y: Rational): int =
  215. ## Compares two rationals. Returns
  216. ## * a value less than zero, if `x < y`
  217. ## * a value greater than zero, if `x > y`
  218. ## * zero, if `x == y`
  219. (x - y).num
  220. func `<`*(x, y: Rational): bool =
  221. ## Returns true if `x` is less than `y`.
  222. (x - y).num < 0
  223. func `<=`*(x, y: Rational): bool =
  224. ## Returns tue if `x` is less than or equal to `y`.
  225. (x - y).num <= 0
  226. func `==`*(x, y: Rational): bool =
  227. ## Compares two rationals for equality.
  228. (x - y).num == 0
  229. func abs*[T](x: Rational[T]): Rational[T] =
  230. ## Returns the absolute value of `x`.
  231. runnableExamples:
  232. doAssert abs(1 // 2) == 1 // 2
  233. doAssert abs(-1 // 2) == 1 // 2
  234. result.num = abs x.num
  235. result.den = abs x.den
  236. func `div`*[T: SomeInteger](x, y: Rational[T]): T =
  237. ## Computes the rational truncated division.
  238. (x.num * y.den) div (y.num * x.den)
  239. func `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
  240. ## Computes the rational modulo by truncated division (remainder).
  241. ## This is same as `x - (x div y) * y`.
  242. result = ((x.num * y.den) mod (y.num * x.den)) // (x.den * y.den)
  243. reduce(result)
  244. func floorDiv*[T: SomeInteger](x, y: Rational[T]): T =
  245. ## Computes the rational floor division.
  246. ##
  247. ## Floor division is conceptually defined as `floor(x / y)`.
  248. ## This is different from the `div` operator, which is defined
  249. ## as `trunc(x / y)`. That is, `div` rounds towards 0 and `floorDiv`
  250. ## rounds down.
  251. floorDiv(x.num * y.den, y.num * x.den)
  252. func floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
  253. ## Computes the rational modulo by floor division (modulo).
  254. ##
  255. ## This is same as `x - floorDiv(x, y) * y`.
  256. ## This func behaves the same as the `%` operator in Python.
  257. result = floorMod(x.num * y.den, y.num * x.den) // (x.den * y.den)
  258. reduce(result)
  259. func hash*[T](x: Rational[T]): Hash =
  260. ## Computes the hash for the rational `x`.
  261. # reduce first so that hash(x) == hash(y) for x == y
  262. var copy = x
  263. reduce(copy)
  264. var h: Hash = 0
  265. h = h !& hash(copy.num)
  266. h = h !& hash(copy.den)
  267. result = !$h