rationals.nim 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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 `num` and
  10. ## a denominator `den`, both of type int. The denominator can not be 0.
  11. import math
  12. import hashes
  13. type Rational*[T] = object
  14. ## a rational number, consisting of a numerator and denominator
  15. num*, den*: T
  16. proc initRational*[T: SomeInteger](num, den: T): Rational[T] =
  17. ## Create a new rational number.
  18. assert(den != 0, "a denominator of zero value is invalid")
  19. result.num = num
  20. result.den = den
  21. proc `//`*[T](num, den: T): Rational[T] = initRational[T](num, den)
  22. ## A friendlier version of `initRational`. Example usage:
  23. ##
  24. ## .. code-block:: nim
  25. ## var x = 1//3 + 1//5
  26. proc `$`*[T](x: Rational[T]): string =
  27. ## Turn a rational number into a string.
  28. result = $x.num & "/" & $x.den
  29. proc toRational*[T: SomeInteger](x: T): Rational[T] =
  30. ## Convert some integer `x` to a rational number.
  31. result.num = x
  32. result.den = 1
  33. proc toRational*(x: float,
  34. n: int = high(int) shr (sizeof(int) div 2 * 8)): Rational[int] =
  35. ## Calculates the best rational numerator and denominator
  36. ## that approximates to `x`, where the denominator is
  37. ## smaller than `n` (default is the largest possible
  38. ## int to give maximum resolution).
  39. ##
  40. ## The algorithm is based on the theory of continued fractions.
  41. ##
  42. ## .. code-block:: Nim
  43. ## import math, rationals
  44. ## for i in 1..10:
  45. ## let t = (10 ^ (i+3)).int
  46. ## let x = toRational(PI, t)
  47. ## let newPI = x.num / x.den
  48. ## echo x, " ", newPI, " error: ", PI - newPI, " ", t
  49. # David Eppstein / UC Irvine / 8 Aug 1993
  50. # With corrections from Arno Formella, May 2008
  51. var
  52. m11, m22 = 1
  53. m12, m21 = 0
  54. ai = int(x)
  55. x = x
  56. while m21 * ai + m22 <= n:
  57. swap m12, m11
  58. swap m22, m21
  59. m11 = m12 * ai + m11
  60. m21 = m22 * ai + m21
  61. if x == float(ai): break # division by zero
  62. x = 1/(x - float(ai))
  63. if x > float(high(int32)): break # representation failure
  64. ai = int(x)
  65. result = m11 // m21
  66. proc toFloat*[T](x: Rational[T]): float =
  67. ## Convert a rational number `x` to a float.
  68. x.num / x.den
  69. proc toInt*[T](x: Rational[T]): int =
  70. ## Convert a rational number `x` to an int. Conversion rounds towards 0 if
  71. ## `x` does not contain an integer value.
  72. x.num div x.den
  73. proc reduce*[T: SomeInteger](x: var Rational[T]) =
  74. ## Reduce rational `x`.
  75. let common = gcd(x.num, x.den)
  76. if x.den > 0:
  77. x.num = x.num div common
  78. x.den = x.den div common
  79. elif x.den < 0:
  80. x.num = -x.num div common
  81. x.den = -x.den div common
  82. else:
  83. raise newException(DivByZeroError, "division by zero")
  84. proc `+` *[T](x, y: Rational[T]): Rational[T] =
  85. ## Add two rational numbers.
  86. let common = lcm(x.den, y.den)
  87. result.num = common div x.den * x.num + common div y.den * y.num
  88. result.den = common
  89. reduce(result)
  90. proc `+` *[T](x: Rational[T], y: T): Rational[T] =
  91. ## Add rational `x` to int `y`.
  92. result.num = x.num + y * x.den
  93. result.den = x.den
  94. proc `+` *[T](x: T, y: Rational[T]): Rational[T] =
  95. ## Add int `x` to rational `y`.
  96. result.num = x * y.den + y.num
  97. result.den = y.den
  98. proc `+=` *[T](x: var Rational[T], y: Rational[T]) =
  99. ## Add rational `y` to rational `x`.
  100. let common = lcm(x.den, y.den)
  101. x.num = common div x.den * x.num + common div y.den * y.num
  102. x.den = common
  103. reduce(x)
  104. proc `+=` *[T](x: var Rational[T], y: T) =
  105. ## Add int `y` to rational `x`.
  106. x.num += y * x.den
  107. proc `-` *[T](x: Rational[T]): Rational[T] =
  108. ## Unary minus for rational numbers.
  109. result.num = -x.num
  110. result.den = x.den
  111. proc `-` *[T](x, y: Rational[T]): Rational[T] =
  112. ## Subtract two rational numbers.
  113. let common = lcm(x.den, y.den)
  114. result.num = common div x.den * x.num - common div y.den * y.num
  115. result.den = common
  116. reduce(result)
  117. proc `-` *[T](x: Rational[T], y: T): Rational[T] =
  118. ## Subtract int `y` from rational `x`.
  119. result.num = x.num - y * x.den
  120. result.den = x.den
  121. proc `-` *[T](x: T, y: Rational[T]): Rational[T] =
  122. ## Subtract rational `y` from int `x`.
  123. result.num = x * y.den - y.num
  124. result.den = y.den
  125. proc `-=` *[T](x: var Rational[T], y: Rational[T]) =
  126. ## Subtract rational `y` from rational `x`.
  127. let common = lcm(x.den, y.den)
  128. x.num = common div x.den * x.num - common div y.den * y.num
  129. x.den = common
  130. reduce(x)
  131. proc `-=` *[T](x: var Rational[T], y: T) =
  132. ## Subtract int `y` from rational `x`.
  133. x.num -= y * x.den
  134. proc `*` *[T](x, y: Rational[T]): Rational[T] =
  135. ## Multiply two rational numbers.
  136. result.num = x.num * y.num
  137. result.den = x.den * y.den
  138. reduce(result)
  139. proc `*` *[T](x: Rational[T], y: T): Rational[T] =
  140. ## Multiply rational `x` with int `y`.
  141. result.num = x.num * y
  142. result.den = x.den
  143. reduce(result)
  144. proc `*` *[T](x: T, y: Rational[T]): Rational[T] =
  145. ## Multiply int `x` with rational `y`.
  146. result.num = x * y.num
  147. result.den = y.den
  148. reduce(result)
  149. proc `*=` *[T](x: var Rational[T], y: Rational[T]) =
  150. ## Multiply rationals `y` to `x`.
  151. x.num *= y.num
  152. x.den *= y.den
  153. reduce(x)
  154. proc `*=` *[T](x: var Rational[T], y: T) =
  155. ## Multiply int `y` to rational `x`.
  156. x.num *= y
  157. reduce(x)
  158. proc reciprocal*[T](x: Rational[T]): Rational[T] =
  159. ## Calculate the reciprocal of `x`. (1/x)
  160. if x.num > 0:
  161. result.num = x.den
  162. result.den = x.num
  163. elif x.num < 0:
  164. result.num = -x.den
  165. result.den = -x.num
  166. else:
  167. raise newException(DivByZeroError, "division by zero")
  168. proc `/`*[T](x, y: Rational[T]): Rational[T] =
  169. ## Divide rationals `x` by `y`.
  170. result.num = x.num * y.den
  171. result.den = x.den * y.num
  172. reduce(result)
  173. proc `/`*[T](x: Rational[T], y: T): Rational[T] =
  174. ## Divide rational `x` by int `y`.
  175. result.num = x.num
  176. result.den = x.den * y
  177. reduce(result)
  178. proc `/`*[T](x: T, y: Rational[T]): Rational[T] =
  179. ## Divide int `x` by Rational `y`.
  180. result.num = x * y.den
  181. result.den = y.num
  182. reduce(result)
  183. proc `/=`*[T](x: var Rational[T], y: Rational[T]) =
  184. ## Divide rationals `x` by `y` in place.
  185. x.num *= y.den
  186. x.den *= y.num
  187. reduce(x)
  188. proc `/=`*[T](x: var Rational[T], y: T) =
  189. ## Divide rational `x` by int `y` in place.
  190. x.den *= y
  191. reduce(x)
  192. proc cmp*(x, y: Rational): int {.procvar.} =
  193. ## Compares two rationals.
  194. (x - y).num
  195. proc `<` *(x, y: Rational): bool =
  196. (x - y).num < 0
  197. proc `<=` *(x, y: Rational): bool =
  198. (x - y).num <= 0
  199. proc `==` *(x, y: Rational): bool =
  200. (x - y).num == 0
  201. proc abs*[T](x: Rational[T]): Rational[T] =
  202. result.num = abs x.num
  203. result.den = abs x.den
  204. proc `div`*[T: SomeInteger](x, y: Rational[T]): T =
  205. ## Computes the rational truncated division.
  206. (x.num * y.den) div (y.num * x.den)
  207. proc `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
  208. ## Computes the rational modulo by truncated division (remainder).
  209. ## This is same as ``x - (x div y) * y``.
  210. result = ((x.num * y.den) mod (y.num * x.den)) // (x.den * y.den)
  211. reduce(result)
  212. proc floorDiv*[T: SomeInteger](x, y: Rational[T]): T =
  213. ## Computes the rational floor division.
  214. ##
  215. ## Floor division is conceptually defined as ``floor(x / y)``.
  216. ## This is different from the ``div`` operator, which is defined
  217. ## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv``
  218. ## rounds down.
  219. floorDiv(x.num * y.den, y.num * x.den)
  220. proc floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
  221. ## Computes the rational modulo by floor division (modulo).
  222. ##
  223. ## This is same as ``x - floorDiv(x, y) * y``.
  224. ## This proc behaves the same as the ``%`` operator in python.
  225. result = floorMod(x.num * y.den, y.num * x.den) // (x.den * y.den)
  226. reduce(result)
  227. proc hash*[T](x: Rational[T]): Hash =
  228. ## Computes hash for rational `x`
  229. # reduce first so that hash(x) == hash(y) for x == y
  230. var copy = x
  231. reduce(copy)
  232. var h: Hash = 0
  233. h = h !& hash(copy.num)
  234. h = h !& hash(copy.den)
  235. result = !$h
  236. when isMainModule:
  237. var
  238. z = Rational[int](num: 0, den: 1)
  239. o = initRational(num = 1, den = 1)
  240. a = initRational(1, 2)
  241. b = -1 // -2
  242. m1 = -1 // 1
  243. tt = 10 // 2
  244. assert(a == a)
  245. assert( (a-a) == z)
  246. assert( (a+b) == o)
  247. assert( (a/b) == o)
  248. assert( (a*b) == 1 // 4)
  249. assert( (3/a) == 6 // 1)
  250. assert( (a/3) == 1 // 6)
  251. assert(a*b == 1 // 4)
  252. assert(tt*z == z)
  253. assert(10*a == tt)
  254. assert(a*10 == tt)
  255. assert(tt/10 == a)
  256. assert(a-m1 == 3 // 2)
  257. assert(a+m1 == -1 // 2)
  258. assert(m1+tt == 16 // 4)
  259. assert(m1-tt == 6 // -1)
  260. assert(z < o)
  261. assert(z <= o)
  262. assert(z == z)
  263. assert(cmp(z, o) < 0)
  264. assert(cmp(o, z) > 0)
  265. assert(o == o)
  266. assert(o >= o)
  267. assert(not(o > o))
  268. assert(cmp(o, o) == 0)
  269. assert(cmp(z, z) == 0)
  270. assert(hash(o) == hash(o))
  271. assert(a == b)
  272. assert(a >= b)
  273. assert(not(b > a))
  274. assert(cmp(a, b) == 0)
  275. assert(hash(a) == hash(b))
  276. var x = 1//3
  277. x *= 5//1
  278. assert(x == 5//3)
  279. x += 2 // 9
  280. assert(x == 17//9)
  281. x -= 9//18
  282. assert(x == 25//18)
  283. x /= 1//2
  284. assert(x == 50//18)
  285. var y = 1//3
  286. y *= 4
  287. assert(y == 4//3)
  288. y += 5
  289. assert(y == 19//3)
  290. y -= 2
  291. assert(y == 13//3)
  292. y /= 9
  293. assert(y == 13//27)
  294. assert toRational(5) == 5//1
  295. assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7
  296. assert toInt(z) == 0
  297. when sizeof(int) == 8:
  298. assert toRational(0.98765432) == 2111111029 // 2137499919
  299. assert toRational(PI) == 817696623 // 260280919
  300. when sizeof(int) == 4:
  301. assert toRational(0.98765432) == 80 // 81
  302. assert toRational(PI) == 355 // 113
  303. assert toRational(0.1) == 1 // 10
  304. assert toRational(0.9) == 9 // 10
  305. assert toRational(0.0) == 0 // 1
  306. assert toRational(-0.25) == 1 // -4
  307. assert toRational(3.2) == 16 // 5
  308. assert toRational(0.33) == 33 // 100
  309. assert toRational(0.22) == 11 // 50
  310. assert toRational(10.0) == 10 // 1
  311. assert (1//1) div (3//10) == 3
  312. assert (-1//1) div (3//10) == -3
  313. assert (3//10) mod (1//1) == 3//10
  314. assert (-3//10) mod (1//1) == -3//10
  315. assert floorDiv(1//1, 3//10) == 3
  316. assert floorDiv(-1//1, 3//10) == -4
  317. assert floorMod(3//10, 1//1) == 3//10
  318. assert floorMod(-3//10, 1//1) == 7//10