rationals.nim 9.1 KB

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