mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-07 12:24:19 +00:00
Add js BigInts (#16409)
* Add BigInts * Renames tos plurals * Improve Stringifications * Update changelog.md Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * RunnableExamplerize * discard the discardable pragma * Several improvements from peer reviews, more docs * More doc, more test * More doc, more test * Better error message 'Error: usage of low is an {.error.} defined at jsbigints.nim' instead of just 'type mismatch JsBigInt' * is an overload, rename * proc to scare kids away * Update lib/js/jsbigints.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * https://github.com/nim-lang/Nim/pull/16409#discussion_r554365041 Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
This commit is contained in:
@@ -72,6 +72,8 @@
|
||||
- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior
|
||||
silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior.
|
||||
|
||||
- Added `jsbigints` module, arbitrary precision integers for JavaScript target.
|
||||
|
||||
- Added `math.copySign`.
|
||||
- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList`
|
||||
and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements
|
||||
|
||||
193
lib/std/jsbigints.nim
Normal file
193
lib/std/jsbigints.nim
Normal file
@@ -0,0 +1,193 @@
|
||||
## Arbitrary precision integers.
|
||||
## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
|
||||
when not defined(js):
|
||||
{.fatal: "Module jsbigints is designed to be used with the JavaScript backend.".}
|
||||
|
||||
type JsBigIntImpl {.importc: "bigint".} = int # https://github.com/nim-lang/Nim/pull/16606
|
||||
type JsBigInt* = distinct JsBigIntImpl ## Arbitrary precision integer for JavaScript target.
|
||||
|
||||
func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} =
|
||||
## Constructor for `JsBigInt`.
|
||||
runnableExamples:
|
||||
doAssert big(1234567890) == big"1234567890"
|
||||
|
||||
func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} =
|
||||
## Constructor for `JsBigInt`.
|
||||
runnableExamples:
|
||||
doAssert big"-1" == big"1" - big"2"
|
||||
|
||||
func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} =
|
||||
## Converts from `JsBigInt` to `cstring` representation.
|
||||
## * `radix` Base to use for representing numeric values.
|
||||
## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
|
||||
runnableExamples:
|
||||
doAssert big"2147483647".toCstring(2) == "1111111111111111111111111111111".cstring
|
||||
|
||||
func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".}
|
||||
## Converts from `JsBigInt` to `cstring` representation.
|
||||
## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
|
||||
|
||||
func `$`*(this: JsBigInt): string =
|
||||
## Returns a `string` representation of `JsBigInt`.
|
||||
runnableExamples: doAssert $big"1024" == "1024n"
|
||||
$toCstring(this) & 'n'
|
||||
|
||||
func wrapToInt*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
|
||||
"(() => { const i = #, b = #; return BigInt.asIntN(b, i) })()".} =
|
||||
## Wraps `this` to a signed `JsBigInt` of `bits` bits in `-2 ^ (bits - 1)` .. `2 ^ (bits - 1) - 1`.
|
||||
## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN
|
||||
runnableExamples:
|
||||
doAssert (big("3") + big("2") ** big("66")).wrapToInt(13) == big("3")
|
||||
|
||||
func wrapToUint*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
|
||||
"(() => { const i = #, b = #; return BigInt.asUintN(b, i) })()".} =
|
||||
## Wraps `this` to an unsigned `JsBigInt` of `bits` bits in 0 .. `2 ^ bits - 1`.
|
||||
## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN
|
||||
runnableExamples:
|
||||
doAssert (big("3") + big("2") ** big("66")).wrapToUint(66) == big("3")
|
||||
|
||||
func toNumber*(this: JsBigInt): BiggestInt {.importjs: "Number(#)".} =
|
||||
## Does not do any bounds check and may or may not return an inexact representation.
|
||||
runnableExamples:
|
||||
doAssert toNumber(big"2147483647") == 2147483647.BiggestInt
|
||||
|
||||
func `+`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
|
||||
runnableExamples:
|
||||
doAssert (big"9" + big"1") == big"10"
|
||||
|
||||
func `-`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
|
||||
runnableExamples:
|
||||
doAssert (big"9" - big"1") == big"8"
|
||||
|
||||
func `*`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
|
||||
runnableExamples:
|
||||
doAssert (big"42" * big"9") == big"378"
|
||||
|
||||
func `div`*(x, y: JsBigInt): JsBigInt {.importjs: "(# / #)".} =
|
||||
## Same as `div` but for `JsBigInt`(uses JavaScript `BigInt() / BigInt()`).
|
||||
runnableExamples:
|
||||
doAssert big"13" div big"3" == big"4"
|
||||
doAssert big"-13" div big"3" == big"-4"
|
||||
doAssert big"13" div big"-3" == big"-4"
|
||||
doAssert big"-13" div big"-3" == big"4"
|
||||
|
||||
func `mod`*(x, y: JsBigInt): JsBigInt {.importjs: "(# % #)".} =
|
||||
## Same as `mod` but for `JsBigInt` (uses JavaScript `BigInt() % BigInt()`).
|
||||
runnableExamples:
|
||||
doAssert big"13" mod big"3" == big"1"
|
||||
doAssert big"-13" mod big"3" == big"-1"
|
||||
doAssert big"13" mod big"-3" == big"1"
|
||||
doAssert big"-13" mod big"-3" == big"-1"
|
||||
|
||||
func `<`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
|
||||
runnableExamples:
|
||||
doAssert big"2" < big"9"
|
||||
|
||||
func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
|
||||
runnableExamples:
|
||||
doAssert big"1" <= big"5"
|
||||
|
||||
func `==`*(x, y: JsBigInt): bool {.importjs: "(# == #)".} =
|
||||
runnableExamples:
|
||||
doAssert big"42" == big"42"
|
||||
|
||||
func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} =
|
||||
runnableExamples:
|
||||
doAssert (big"9" ** big"5") == big"59049"
|
||||
|
||||
func `xor`*(x, y: JsBigInt): JsBigInt {.importjs: "(# ^ #)".} =
|
||||
runnableExamples:
|
||||
doAssert (big"555" xor big"2") == big"553"
|
||||
|
||||
func `shl`*(a, b: JsBigInt): JsBigInt {.importjs: "(# << #)".} =
|
||||
runnableExamples:
|
||||
doAssert (big"999" shl big"2") == big"3996"
|
||||
|
||||
func `shr`*(a, b: JsBigInt): JsBigInt {.importjs: "(# >> #)".} =
|
||||
runnableExamples:
|
||||
doAssert (big"999" shr big"2") == big"249"
|
||||
|
||||
func `-`*(this: JsBigInt): JsBigInt {.importjs: "($1#)".} =
|
||||
runnableExamples:
|
||||
doAssert -(big"10101010101") == big"-10101010101"
|
||||
|
||||
func inc*(this: var JsBigInt) {.importjs: "(++[#][0][0])".} =
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"1"
|
||||
inc big1
|
||||
doAssert big1 == big"2"
|
||||
|
||||
func dec*(this: var JsBigInt) {.importjs: "(--[#][0][0])".} =
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"2"
|
||||
dec big1
|
||||
doAssert big1 == big"1"
|
||||
|
||||
func inc*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] += #)".} =
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"1"
|
||||
inc big1, big"2"
|
||||
doAssert big1 == big"3"
|
||||
|
||||
func dec*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] -= #)".} =
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"1"
|
||||
dec big1, big"2"
|
||||
doAssert big1 == big"-1"
|
||||
|
||||
func `+=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"1"
|
||||
big1 += big"2"
|
||||
doAssert big1 == big"3"
|
||||
|
||||
func `-=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"1"
|
||||
big1 -= big"2"
|
||||
doAssert big1 == big"-1"
|
||||
|
||||
func `*=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"2"
|
||||
big1 *= big"4"
|
||||
doAssert big1 == big"8"
|
||||
|
||||
func `/=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
|
||||
## Same as `x = x div y`.
|
||||
runnableExamples:
|
||||
var big1: JsBigInt = big"11"
|
||||
big1 /= big"2"
|
||||
doAssert big1 == big"5"
|
||||
|
||||
proc `+`*(_: JsBigInt): JsBigInt {.error:
|
||||
"See https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs".} # Can not be used by design
|
||||
## **Do NOT use.** https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs
|
||||
|
||||
proc low*(_: typedesc[JsBigInt]): JsBigInt {.error:
|
||||
"Arbitrary precision integers do not have a known low.".} ## **Do NOT use.**
|
||||
|
||||
proc high*(_: typedesc[JsBigInt]): JsBigInt {.error:
|
||||
"Arbitrary precision integers do not have a known high.".} ## **Do NOT use.**
|
||||
|
||||
|
||||
runnableExamples:
|
||||
let big1: JsBigInt = big"2147483647"
|
||||
let big2: JsBigInt = big"666"
|
||||
doAssert JsBigInt isnot int
|
||||
doAssert big1 != big2
|
||||
doAssert big1 > big2
|
||||
doAssert big1 >= big2
|
||||
doAssert big2 < big1
|
||||
doAssert big2 <= big1
|
||||
doAssert not(big1 == big2)
|
||||
let z = JsBigInt.default
|
||||
doAssert $z == "0n"
|
||||
block:
|
||||
var a: seq[JsBigInt]
|
||||
a.setLen 2
|
||||
doAssert a == @[big"0", big"0"]
|
||||
doAssert a[^1] == big"0"
|
||||
var b: JsBigInt
|
||||
doAssert b == big"0"
|
||||
doAssert b == JsBigInt.default
|
||||
38
tests/stdlib/tjsbigints.nim
Normal file
38
tests/stdlib/tjsbigints.nim
Normal file
@@ -0,0 +1,38 @@
|
||||
discard """
|
||||
targets: "js"
|
||||
"""
|
||||
|
||||
import std/jsbigints
|
||||
|
||||
|
||||
let big1: JsBigInt = big"2147483647"
|
||||
let big2: JsBigInt = big"666"
|
||||
var big3: JsBigInt = big"2"
|
||||
|
||||
doAssert big3 == big"2"
|
||||
doAssert (big3 xor big2) == big"664"
|
||||
doAssert (big1 mod big2) == big"613"
|
||||
doAssert -big1 == big"-2147483647"
|
||||
doAssert big1 div big2 == big"3224449"
|
||||
doAssert big1 + big2 == big"2147484313"
|
||||
doAssert big1 - big2 == big"2147482981"
|
||||
doAssert big1 shl big3 == big"8589934588"
|
||||
doAssert big1 shr big3 == big"536870911"
|
||||
doAssert big1 * big2 == big"1430224108902"
|
||||
doAssert $big1 == "2147483647n"
|
||||
doAssert big1.toCstring(10) == "2147483647".cstring
|
||||
doAssert big2 ** big3 == big(443556)
|
||||
var huge = big"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
huge.inc
|
||||
huge = huge + big"-999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
doAssert huge == big"1"
|
||||
var list: seq[JsBigInt]
|
||||
for i in big"0" .. big"5":
|
||||
doAssert i is JsBigInt
|
||||
list.add i
|
||||
doAssert list == @[big"0", big"1", big"2", big"3", big"4", big"5"]
|
||||
list = @[]
|
||||
for i in big"0" ..< big"5":
|
||||
doAssert i is JsBigInt
|
||||
list.add i
|
||||
doAssert list == @[big"0", big"1", big"2", big"3", big"4"]
|
||||
@@ -14,8 +14,11 @@ const
|
||||
webUploadOutput = "web/upload"
|
||||
|
||||
var nimExe*: string
|
||||
const allowList = ["jsbigints.nim"]
|
||||
|
||||
template isJsOnly(file: string): bool = file.isRelativeTo("lib/js")
|
||||
template isJsOnly(file: string): bool =
|
||||
file.isRelativeTo("lib/js") or
|
||||
file.extractFilename in allowList
|
||||
|
||||
proc exe*(f: string): string =
|
||||
result = addFileExt(f, ExeExt)
|
||||
|
||||
Reference in New Issue
Block a user