jsbigints.nim 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. type JsBigIntImpl {.importjs: "bigint".} = int # https://github.com/nim-lang/Nim/pull/16606
  6. type JsBigInt* = distinct JsBigIntImpl ## Arbitrary precision integer for JavaScript target.
  7. func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} =
  8. ## Constructor for `JsBigInt`.
  9. runnableExamples:
  10. doAssert big(1234567890) == big"1234567890"
  11. doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big
  12. when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard
  13. func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} =
  14. ## Constructor for `JsBigInt`.
  15. runnableExamples:
  16. doAssert -1'big == 1'big - 2'big
  17. # supports decimal, binary, octal, hex:
  18. doAssert -12'big == big"-12"
  19. doAssert 12'big == 12.big
  20. doAssert 0b101'big == 0b101.big
  21. doAssert 0o701'big == 0o701.big
  22. doAssert 0xdeadbeaf'big == 0xdeadbeaf.big
  23. doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big
  24. doAssert not compiles(static(12'big))
  25. when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard
  26. func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} =
  27. ## Alias for `'big`
  28. when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard
  29. func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} =
  30. ## Converts from `JsBigInt` to `cstring` representation.
  31. ## * `radix` Base to use for representing numeric values.
  32. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
  33. runnableExamples:
  34. doAssert big"2147483647".toCstring(2) == "1111111111111111111111111111111".cstring
  35. func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".}
  36. ## Converts from `JsBigInt` to `cstring` representation.
  37. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
  38. func `$`*(this: JsBigInt): string =
  39. ## Returns a `string` representation of `JsBigInt`.
  40. runnableExamples: doAssert $big"1024" == "1024n"
  41. $toCstring(this) & 'n'
  42. func wrapToInt*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
  43. "(() => { const i = #, b = #; return BigInt.asIntN(b, i) })()".} =
  44. ## Wraps `this` to a signed `JsBigInt` of `bits` bits in `-2 ^ (bits - 1)` .. `2 ^ (bits - 1) - 1`.
  45. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN
  46. runnableExamples:
  47. doAssert (big("3") + big("2") ** big("66")).wrapToInt(13) == big("3")
  48. func wrapToUint*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
  49. "(() => { const i = #, b = #; return BigInt.asUintN(b, i) })()".} =
  50. ## Wraps `this` to an unsigned `JsBigInt` of `bits` bits in 0 .. `2 ^ bits - 1`.
  51. ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN
  52. runnableExamples:
  53. doAssert (big("3") + big("2") ** big("66")).wrapToUint(66) == big("3")
  54. func toNumber*(this: JsBigInt): BiggestInt {.importjs: "Number(#)".} =
  55. ## Does not do any bounds check and may or may not return an inexact representation.
  56. runnableExamples:
  57. doAssert toNumber(big"2147483647") == 2147483647.BiggestInt
  58. func `+`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
  59. runnableExamples:
  60. doAssert (big"9" + big"1") == big"10"
  61. func `-`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
  62. runnableExamples:
  63. doAssert (big"9" - big"1") == big"8"
  64. func `*`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
  65. runnableExamples:
  66. doAssert (big"42" * big"9") == big"378"
  67. func `div`*(x, y: JsBigInt): JsBigInt {.importjs: "(# / #)".} =
  68. ## Same as `div` but for `JsBigInt`(uses JavaScript `BigInt() / BigInt()`).
  69. runnableExamples:
  70. doAssert big"13" div big"3" == big"4"
  71. doAssert big"-13" div big"3" == big"-4"
  72. doAssert big"13" div big"-3" == big"-4"
  73. doAssert big"-13" div big"-3" == big"4"
  74. func `mod`*(x, y: JsBigInt): JsBigInt {.importjs: "(# % #)".} =
  75. ## Same as `mod` but for `JsBigInt` (uses JavaScript `BigInt() % BigInt()`).
  76. runnableExamples:
  77. doAssert big"13" mod big"3" == big"1"
  78. doAssert big"-13" mod big"3" == big"-1"
  79. doAssert big"13" mod big"-3" == big"1"
  80. doAssert big"-13" mod big"-3" == big"-1"
  81. func `<`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
  82. runnableExamples:
  83. doAssert big"2" < big"9"
  84. func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
  85. runnableExamples:
  86. doAssert big"1" <= big"5"
  87. func `==`*(x, y: JsBigInt): bool {.importjs: "(# == #)".} =
  88. runnableExamples:
  89. doAssert big"42" == big"42"
  90. func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} =
  91. # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812
  92. runnableExamples:
  93. doAssert big"2" ** big"64" == big"18446744073709551616"
  94. doAssert big"-2" ** big"3" == big"-8"
  95. doAssert -big"2" ** big"2" == big"4" # parsed as: (-2n) ** 2n
  96. doAssert big"0" ** big"0" == big"1" # edge case
  97. var ok = false
  98. try: discard big"2" ** big"-1" # raises foreign `RangeError`
  99. except: ok = true
  100. doAssert ok
  101. # pending https://github.com/nim-lang/Nim/pull/15940, simplify to:
  102. # doAssertRaises: discard big"2" ** big"-1" # raises foreign `RangeError`
  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