jsbigints.nim 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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: doAssert false, "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: doAssert false, "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: doAssert false, "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, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812
  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. # pending https://github.com/nim-lang/Nim/pull/15940, simplify to:
  104. # doAssertRaises: discard big"2" ** big"-1" # raises foreign `RangeError`
  105. func `and`*(x, y: JsBigInt): JsBigInt {.importjs: "(# & #)".} =
  106. runnableExamples:
  107. doAssert (big"555" and big"2") == big"2"
  108. func `or`*(x, y: JsBigInt): JsBigInt {.importjs: "(# | #)".} =
  109. runnableExamples:
  110. doAssert (big"555" or big"2") == big"555"
  111. func `xor`*(x, y: JsBigInt): JsBigInt {.importjs: "(# ^ #)".} =
  112. runnableExamples:
  113. doAssert (big"555" xor big"2") == big"553"
  114. func `shl`*(a, b: JsBigInt): JsBigInt {.importjs: "(# << #)".} =
  115. runnableExamples:
  116. doAssert (big"999" shl big"2") == big"3996"
  117. func `shr`*(a, b: JsBigInt): JsBigInt {.importjs: "(# >> #)".} =
  118. runnableExamples:
  119. doAssert (big"999" shr big"2") == big"249"
  120. func `-`*(this: JsBigInt): JsBigInt {.importjs: "($1#)".} =
  121. runnableExamples:
  122. doAssert -(big"10101010101") == big"-10101010101"
  123. func inc*(this: var JsBigInt) {.importjs: "(++[#][0][0])".} =
  124. runnableExamples:
  125. var big1: JsBigInt = big"1"
  126. inc big1
  127. doAssert big1 == big"2"
  128. func dec*(this: var JsBigInt) {.importjs: "(--[#][0][0])".} =
  129. runnableExamples:
  130. var big1: JsBigInt = big"2"
  131. dec big1
  132. doAssert big1 == big"1"
  133. func inc*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] += #)".} =
  134. runnableExamples:
  135. var big1: JsBigInt = big"1"
  136. inc big1, big"2"
  137. doAssert big1 == big"3"
  138. func dec*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] -= #)".} =
  139. runnableExamples:
  140. var big1: JsBigInt = big"1"
  141. dec big1, big"2"
  142. doAssert big1 == big"-1"
  143. func `+=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  144. runnableExamples:
  145. var big1: JsBigInt = big"1"
  146. big1 += big"2"
  147. doAssert big1 == big"3"
  148. func `-=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  149. runnableExamples:
  150. var big1: JsBigInt = big"1"
  151. big1 -= big"2"
  152. doAssert big1 == big"-1"
  153. func `*=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  154. runnableExamples:
  155. var big1: JsBigInt = big"2"
  156. big1 *= big"4"
  157. doAssert big1 == big"8"
  158. func `/=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
  159. ## Same as `x = x div y`.
  160. runnableExamples:
  161. var big1: JsBigInt = big"11"
  162. big1 /= big"2"
  163. doAssert big1 == big"5"
  164. proc `+`*(_: JsBigInt): JsBigInt {.error:
  165. "See https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs".} # Can not be used by design
  166. ## **Do NOT use.** https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs
  167. proc low*(_: typedesc[JsBigInt]): JsBigInt {.error:
  168. "Arbitrary precision integers do not have a known low.".} ## **Do NOT use.**
  169. proc high*(_: typedesc[JsBigInt]): JsBigInt {.error:
  170. "Arbitrary precision integers do not have a known high.".} ## **Do NOT use.**
  171. runnableExamples:
  172. block:
  173. let big1: JsBigInt = big"2147483647"
  174. let big2: JsBigInt = big"666"
  175. doAssert JsBigInt isnot int
  176. doAssert big1 != big2
  177. doAssert big1 > big2
  178. doAssert big1 >= big2
  179. doAssert big2 < big1
  180. doAssert big2 <= big1
  181. doAssert not(big1 == big2)
  182. let z = JsBigInt.default
  183. doAssert $z == "0n"
  184. block:
  185. var a: seq[JsBigInt]
  186. a.setLen 2
  187. doAssert a == @[big"0", big"0"]
  188. doAssert a[^1] == big"0"
  189. var b: JsBigInt
  190. doAssert b == big"0"
  191. doAssert b == JsBigInt.default