rationals.nim 10 KB

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