rationals.nim 9.8 KB

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