jsbigints.nim 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. ## Arbitrary precision integers.
  2. ## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
  3. when not defined(js):
  4. {.fatal: "Module jsbigints is designed to be used with the JavaScript backend.".}
  5. when defined(nimPreviewSlimSystem):
  6. import std/assertions
  7. type JsBigIntImpl {.importjs: "bigint".} = int # https://github.com/nim-lang/Nim/pull/16606
  8. type JsBigInt* = distinct JsBigIntImpl ## Arbitrary precision integer for JavaScript target.
  9. func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} =
  10. ## Constructor for `JsBigInt`.
  11. runnableExamples:
  12. doAssert big(1234567890) == big"1234567890"
  13. doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big
  14. when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
  15. func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} =
  16. ## Constructor for `JsBigInt`.
  17. runnableExamples:
  18. doAssert -1'big == 1'big - 2'big
  19. # supports decimal, binary, octal, hex:
  20. doAssert -12'big == big"-12"
  21. doAssert 12'big == 12.big
  22. doAssert 0b101'big == 0b101.big
  23. doAssert 0o701'big == 0o701.big
  24. doAssert 0xdeadbeaf'big == 0xdeadbeaf.big
  25. doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big
  26. doAssert not compiles(static(12'big))
  27. when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
  28. func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} =
  29. ## Alias for `'big`
  30. when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
  31. func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} =
  32. ## Converts from `JsBigInt` to `cstring` representation.
  33. ## * `radix` Base to use for representing numeric values.
  34. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
  35. runnableExamples:
  36. doAssert big"2147483647".toCstring(2) == "1111111111111111111111111111111".cstring
  37. func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".}
  38. ## Converts from `JsBigInt` to `cstring` representation.
  39. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
  40. func `$`*(this: JsBigInt): string =
  41. ## Returns a `string` representation of `JsBigInt`.
  42. runnableExamples: doAssert $big"1024" == "1024n"
  43. $toCstring(this) & 'n'
  44. func wrapToInt*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
  45. "(() => { const i = #, b = #; return BigInt.asIntN(b, i) })()".} =
  46. ## Wraps `this` to a signed `JsBigInt` of `bits` bits in `-2 ^ (bits - 1)` .. `2 ^ (bits - 1) - 1`.
  47. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN
  48. runnableExamples:
  49. doAssert (big("3") + big("2") ** big("66")).wrapToInt(13) == big("3")
  50. func wrapToUint*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
  51. "(() => { const i = #, b = #; return BigInt.asUintN(b, i) })()".} =
  52. ## Wraps `this` to an unsigned `JsBigInt` of `bits` bits in 0 .. `2 ^ bits - 1`.
  53. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN
  54. runnableExamples:
  55. doAssert (big("3") + big("2") ** big("66")).wrapToUint(66) == big("3")
  56. func toNumber*(this: JsBigInt): int {.importjs: "Number(#)".} =
  57. ## Does not do any bounds check and may or may not return an inexact representation.
  58. runnableExamples:
  59. doAssert toNumber(big"2147483647") == 2147483647.int
  60. func `+`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
  61. runnableExamples:
  62. doAssert (big"9" + big"1") == big"10"
  63. func `-`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
  64. runnableExamples:
  65. doAssert (big"9" - big"1") == big"8"
  66. func `*`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
  67. runnableExamples:
  68. doAssert (big"42" * big"9") == big"378"
  69. func `div`*(x, y: JsBigInt): JsBigInt {.importjs: "(# / #)".} =
  70. ## Same as `div` but for `JsBigInt`(uses JavaScript `BigInt() / BigInt()`).
  71. runnableExamples:
  72. doAssert big"13" div big"3" == big"4"
  73. doAssert big"-13" div big"3" == big"-4"
  74. doAssert big"13" div big"-3" == big"-4"
  75. doAssert big"-13" div big"-3" == big"4"
  76. func `mod`*(x, y: JsBigInt): JsBigInt {.importjs: "(# % #)".} =
  77. ## Same as `mod` but for `JsBigInt` (uses JavaScript `BigInt() % BigInt()`).
  78. runnableExamples:
  79. doAssert big"13" mod big"3" == big"1"
  80. doAssert big"-13" mod big"3" == big"-1"
  81. doAssert big"13" mod big"-3" == big"1"
  82. doAssert big"-13" mod big"-3" == big"-1"
  83. func `<`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
  84. runnableExamples:
  85. doAssert big"2" < big"9"
  86. func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
  87. runnableExamples:
  88. doAssert big"1" <= big"5"
  89. func `==`*(x, y: JsBigInt): bool {.importjs: "(# == #)".} =
  90. runnableExamples:
  91. doAssert big"42" == big"42"
  92. func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} =
  93. # (#) needed due to unary minus
  94. runnableExamples:
  95. doAssert big"2" ** big"64" == big"18446744073709551616"
  96. doAssert big"-2" ** big"3" == big"-8"
  97. doAssert -big"2" ** big"2" == big"4" # parsed as: (-2n) ** 2n
  98. doAssert big"0" ** big"0" == big"1" # edge case
  99. var ok = false
  100. try: discard big"2" ** big"-1" # raises foreign `RangeError`
  101. except: ok = true
  102. doAssert ok
  103. func `and`*(x, y: JsBigInt): JsBigInt {.importjs: "(# & #)".} =
  104. runnableExamples:
  105. doAssert (big"555" and big"2") == big"2"
  106. func `or`*(x, y: JsBigInt): JsBigInt {.importjs: "(# | #)".} =
  107. runnableExamples:
  108. doAssert (big"555" or big"2") == big"555"
  109. func `xor`*(x, y: JsBigInt): JsBigInt {.importjs: "(# ^ #)".} =
  110. runnableExamples:
  111. doAssert (big"555" xor big"2") == big"553"
  112. func `shl`*(a, b: JsBigInt): JsBigInt {.importjs: "(# << #)".} =
  113. runnableExamples:
  114. doAssert (big"999" shl big"2") == big"3996"
  115. func `shr`*(a, b: JsBigInt): JsBigInt {.importjs: "(# >> #)".} =
  116. runnableExamples:
  117. doAssert (big"999" shr big"2") == big"249"
  118. func `-`*(this: JsBigInt): JsBigInt {.importjs: "($1#)".} =
  119. runnableExamples:
  120. doAssert -(big"10101010101") == big"-10101010101"
  121. func inc*(this: var JsBigInt) {.importjs: "(++[#][0][0])".} =
  122. runnableExamples:
  123. var big1: JsBigInt = big"1"
  124. inc big1
  125. doAssert big1 == big"2"
  126. func dec*(this: var JsBigInt) {.importjs: "(--[#][0][0])".} =
  127. runnableExamples:
  128. var big1: JsBigInt = big"2"
  129. dec big1
  130. doAssert big1 == big"1"
  131. func inc*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] += #)".} =
  132. runnableExamples:
  133. var big1: JsBigInt = big"1"
  134. inc big1, big"2"
  135. doAssert big1 == big"3"
  136. func dec*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] -= #)".} =
  137. runnableExamples:
  138. var big1: JsBigInt = big"1"
  139. dec big1, big"2"
  140. doAssert big1 == big"-1"
  141. func `+=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  142. runnableExamples:
  143. var big1: JsBigInt = big"1"
  144. big1 += big"2"
  145. doAssert big1 == big"3"
  146. func `-=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  147. runnableExamples:
  148. var big1: JsBigInt = big"1"
  149. big1 -= big"2"
  150. doAssert big1 == big"-1"
  151. func `*=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  152. runnableExamples:
  153. var big1: JsBigInt = big"2"
  154. big1 *= big"4"
  155. doAssert big1 == big"8"
  156. func `/=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  157. ## Same as `x = x div y`.
  158. runnableExamples:
  159. var big1: JsBigInt = big"11"
  160. big1 /= big"2"
  161. doAssert big1 == big"5"
  162. proc `+`*(_: JsBigInt): JsBigInt {.error:
  163. "See https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs".} # Can not be used by design
  164. ## **Do NOT use.** https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs
  165. proc low*(_: typedesc[JsBigInt]): JsBigInt {.error:
  166. "Arbitrary precision integers do not have a known low.".} ## **Do NOT use.**
  167. proc high*(_: typedesc[JsBigInt]): JsBigInt {.error:
  168. "Arbitrary precision integers do not have a known high.".} ## **Do NOT use.**
  169. runnableExamples:
  170. block:
  171. let big1: JsBigInt = big"2147483647"
  172. let big2: JsBigInt = big"666"
  173. doAssert JsBigInt isnot int
  174. doAssert big1 != big2
  175. doAssert big1 > big2
  176. doAssert big1 >= big2
  177. doAssert big2 < big1
  178. doAssert big2 <= big1
  179. doAssert not(big1 == big2)
  180. let z = JsBigInt.default
  181. doAssert $z == "0n"
  182. block:
  183. var a: seq[JsBigInt]
  184. a.setLen 2
  185. doAssert a == @[big"0", big"0"]
  186. doAssert a[^1] == big"0"
  187. var b: JsBigInt
  188. doAssert b == big"0"
  189. doAssert b == JsBigInt.default