diff --git a/CHANGELOG.md b/CHANGELOG.md index f1bca3c..fa258ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.0.4] +This version is compatible with [`ab4560900cf475ff515054bec0ca9a4491aca366`](https://github.com/Chia-Network/clvm/tree/ab4560900cf475ff515054bec0ca9a4491aca366) of [clvm@0.9.7](https://github.com/Chia-Network/clvm) + +### Fixed +- Fixed an issue where `int_from_bytes` and `bigint_from_bytes` returned always signed int/bigint. +- Fixed an issue where `int_to_bytes` and `bigint_to_bytes` blindly recognized the argument as a signed int/bigint. + ## [1.0.3] This version is compatible with [`ab4560900cf475ff515054bec0ca9a4491aca366`](https://github.com/Chia-Network/clvm/tree/ab4560900cf475ff515054bec0ca9a4491aca366) of [clvm@0.9.7](https://github.com/Chia-Network/clvm) @@ -187,6 +194,7 @@ At this version, I've managed to improve test complete time to `79s` -> `2s` by Initial (beta) release. +[1.0.4]: https://github.com/Chia-Mine/clvm-js/compare/v1.0.3...v1.0.4 [1.0.3]: https://github.com/Chia-Mine/clvm-js/compare/v1.0.2...v1.0.3 [1.0.2]: https://github.com/Chia-Mine/clvm-js/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/Chia-Mine/clvm-js/compare/v1.0.0...v1.0.1 diff --git a/package.json b/package.json index eae9fde..4cd80f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clvm", - "version": "1.0.3", + "version": "1.0.4", "author": "Admin ChiaMineJP ", "description": "Javascript implementation of chia lisp", "license": "MIT", diff --git a/src/SExp.ts b/src/SExp.ts index cc85fb3..332164c 100644 --- a/src/SExp.ts +++ b/src/SExp.ts @@ -36,13 +36,13 @@ export function convert_atom_to_bytes(v: any): Bytes { return Bytes.from(v, "utf8"); } else if(typeof v === "number"){ - return int_to_bytes(v); + return int_to_bytes(v, {signed: true}); } else if(typeof v === "boolean"){ // Tips. In Python, isinstance(True, int) == True. - return int_to_bytes(v ? 1 : 0); + return int_to_bytes(v ? 1 : 0, {signed: true}); } else if(typeof v === "bigint"){ - return bigint_to_bytes(v); + return bigint_to_bytes(v, {signed: true}); } else if(v === None || !v){ return Bytes.NULL; @@ -218,11 +218,11 @@ export class SExp implements CLVMType { } public as_int(){ - return int_from_bytes(this.atom); + return int_from_bytes(this.atom, {signed: true}); } public as_bigint(){ - return bigint_from_bytes(this.atom); + return bigint_from_bytes(this.atom, {signed: true}); } public as_bin(){ diff --git a/src/casts.ts b/src/casts.ts index 079e2e0..e44a724 100644 --- a/src/casts.ts +++ b/src/casts.ts @@ -1,42 +1,48 @@ import {None} from "./__python_types__"; import {Bytes} from "./__type_compatibility__"; -export function int_from_bytes(b: Bytes|None): number { +export type TConvertOption = { + signed: boolean; +}; + +export function int_from_bytes(b: Bytes|None, option?: Partial): number { if(!b || b.length === 0){ return 0; } else if(b.length*8 > 52){ throw new Error("Cannot convert Bytes to Integer larger than 52bit. Use bigint_from_bytes instead."); } + const signed = (option && typeof option.signed === "boolean") ? option.signed : false; let unsigned32 = 0; for(let i=b.length-1;i>=0;i--){ const byte = b.at(i); unsigned32 += byte * (256**((b.length-1)-i)); } // If the first bit is 1, it is recognized as a negative number. - if(b.at(0) & 0x80){ + if(signed && (b.at(0) & 0x80)){ return unsigned32 - (256**b.length); } return unsigned32; } -export function bigint_from_bytes(b: Bytes|None): bigint { +export function bigint_from_bytes(b: Bytes|None, option?: Partial): bigint { if(!b || b.length === 0){ return BigInt(0); } + const signed = (option && typeof option.signed === "boolean") ? option.signed : false; let unsigned32 = BigInt(0); for(let i=b.length-1;i>=0;i--){ const byte = b.at(i); unsigned32 += BigInt(byte) * (BigInt(256)**(BigInt((b.length-1)-i))); } // If the first bit is 1, it is recognized as a negative number. - if(b.at(0) & 0x80){ + if(signed && (b.at(0) & 0x80)){ return unsigned32 - (BigInt(1) << BigInt(b.length*8)); } return unsigned32; } -export function int_to_bytes(v: number): Bytes { +export function int_to_bytes(v: number, option?: Partial): Bytes { if(v > Number.MAX_SAFE_INTEGER || v < Number.MIN_SAFE_INTEGER){ throw new Error(`The int value is beyond ${v > 0 ? "MAX_SAFE_INTEGER" : "MIN_SAFE_INTEGER"}: ${v}`); } @@ -44,9 +50,13 @@ export function int_to_bytes(v: number): Bytes { return Bytes.NULL; } + const signed = (option && typeof option.signed === "boolean") ? option.signed : false; + if(!signed && v < 0){ + throw new Error("OverflowError: can't convert negative int to unsigned"); + } let byte_count = 1; if(v > 0){ - while(2**(8*byte_count - 1) - 1 < v){ + while(2**(8*byte_count - (signed ? 1 : 0)) - 1 < v){ byte_count++; } } @@ -56,7 +66,7 @@ export function int_to_bytes(v: number): Bytes { } } - const needExtraByte = v > 0 && ((v >> ((byte_count-1)*8)) & 0x80) > 0; + const needExtraByte = signed && v > 0 && ((v >> ((byte_count-1)*8)) & 0x80) > 0; const u8 = new Uint8Array(byte_count+(needExtraByte ? 1 : 0)); for(let i=0;i): Bytes { if(v === BigInt(0)){ return Bytes.NULL; } + + const signed = (option && typeof option.signed === "boolean") ? option.signed : false; + if(!signed && v < BigInt(0)){ + throw new Error("OverflowError: can't convert negative int to unsigned"); + } let byte_count = 1; if(v > 0){ - while(BigInt(2)**(BigInt(8)*BigInt(byte_count) - BigInt(1)) - BigInt(1) < v){ + while(BigInt(2)**(BigInt(8)*BigInt(byte_count) - (signed ? BigInt(1) : BigInt(0))) - BigInt(1) < v){ byte_count++; } } @@ -82,7 +97,7 @@ export function bigint_to_bytes(v: bigint): Bytes { } } - const needExtraByte = v > 0 && ((v >> (BigInt(byte_count-1)*BigInt(8))) & BigInt(0x80)) > BigInt(0); + const needExtraByte = signed && v > 0 && ((v >> (BigInt(byte_count-1)*BigInt(8))) & BigInt(0x80)) > BigInt(0); const u8 = new Uint8Array(byte_count+(needExtraByte ? 1 : 0)); for(let i=0;i 0){ bytes.set(u0, 32 - u0.length); } @@ -376,7 +376,7 @@ export function op_lsh(args: SExp){ } // we actually want i0 to be an *unsigned* int const a0 = args.first().atom; - const i0 = bigint_from_bytes(a0); + const i0 = bigint_from_bytes(a0, {signed: false}); let r; if(i1 >= 0){ r = i0 << i1; diff --git a/src/operators.ts b/src/operators.ts index 75fe509..16239a4 100644 --- a/src/operators.ts +++ b/src/operators.ts @@ -223,7 +223,7 @@ export function default_unknown_op(op: Bytes, args: SExp): Tuple { - expect(int_from_bytes(None)).toBe(0); - expect(int_from_bytes(h("01"))).toBe(1); - expect(int_from_bytes(h("7f"))).toBe(127); - expect(int_from_bytes(h("80"))).toBe(-128); - expect(int_from_bytes(h("ff"))).toBe(-1); - expect(int_from_bytes(h("ffffffff"))).toBe(-1); - expect(() => int_from_bytes(h("ffffffffffffffff"))).toThrow(); -}); +describe("int_from_bytes", () => { + test("signed: true", () => { + expect(int_from_bytes(None, {signed: true})).toBe(0); + expect(int_from_bytes(h("01"), {signed: true})).toBe(1); + expect(int_from_bytes(h("7f"), {signed: true})).toBe(127); + expect(int_from_bytes(h("80"), {signed: true})).toBe(-128); + expect(int_from_bytes(h("ff"), {signed: true})).toBe(-1); + expect(int_from_bytes(h("ffffffff"), {signed: true})).toBe(-1); + expect(() => int_from_bytes(h("ffffffffffffffff"), {signed: true})).toThrow(); + }); + test("signed: false", () => { + expect(int_from_bytes(None, {signed: false})).toBe(0); + expect(int_from_bytes(h("01"), {signed: false})).toBe(1); + expect(int_from_bytes(h("7f"), {signed: false})).toBe(127); + expect(int_from_bytes(h("80"), {signed: false})).toBe(128); + expect(int_from_bytes(h("ff"), {signed: false})).toBe(255); + expect(int_from_bytes(h("ffffffff"), {signed: false})).toBe(4294967295); + expect(() => int_from_bytes(h("ffffffffffffffff"), {signed: false})).toThrow(); + }); + test("default option", () => { + expect(int_from_bytes(None)).toBe(int_from_bytes(None, {signed: false})); + expect(int_from_bytes(h("01"))).toBe(int_from_bytes(h("01"), {signed: false})); + expect(int_from_bytes(h("7f"))).toBe(int_from_bytes(h("7f"), {signed: false})); + expect(int_from_bytes(h("80"))).toBe(int_from_bytes(h("80"), {signed: false})); + expect(int_from_bytes(h("ff"))).toBe(int_from_bytes(h("ff"), {signed: false})); + expect(int_from_bytes(h("ffffffff"))).toBe(int_from_bytes(h("ffffffff"), {signed: false})); + expect(() => int_from_bytes(h("ffffffffffffffff"))).toThrow(); + }); +}) -test("bigint_from_bytes", () => { - expect(bigint_from_bytes(None)).toBe(BigInt(0)); - expect(bigint_from_bytes(h("01"))).toBe(BigInt(1)); - expect(bigint_from_bytes(h("7f"))).toBe(BigInt(127)); - expect(bigint_from_bytes(h("80"))).toBe(BigInt(-128)); - expect(bigint_from_bytes(h("ff"))).toBe(BigInt(-1)); - expect(bigint_from_bytes(h("ffffffff"))).toBe(BigInt(-1)); - expect(bigint_from_bytes(h("ffffffffffffffff"))).toBe(BigInt(-1)); - expect(bigint_from_bytes(h("7fffffffffffffff"))).toBe(BigInt(2)**BigInt(63) - BigInt(1)); +describe("bigint_from_bytes", () => { + test("signed: true", () => { + expect(bigint_from_bytes(None, {signed: true}) === BigInt(0)).toBeTruthy(); + expect(bigint_from_bytes(h("01"), {signed: true}) === BigInt(1)).toBeTruthy(); + expect(bigint_from_bytes(h("7f"), {signed: true}) === BigInt(127)).toBeTruthy(); + expect(bigint_from_bytes(h("80"), {signed: true}) === BigInt(-128)).toBeTruthy(); + expect(bigint_from_bytes(h("ff"), {signed: true}) === BigInt(-1)).toBeTruthy(); + expect(bigint_from_bytes(h("ffffffff"), {signed: true}) === BigInt(-1)).toBeTruthy(); + expect(bigint_from_bytes(h("ffffffffffffffff"), {signed: true}) === BigInt(-1)).toBeTruthy(); + expect(bigint_from_bytes(h("7fffffffffffffff"), {signed: true}) === BigInt(2)**BigInt(63) - BigInt(1)).toBeTruthy(); + }); + test("signed: false", () => { + expect(bigint_from_bytes(None, {signed: false}) === BigInt(0)).toBeTruthy(); + expect(bigint_from_bytes(h("01"), {signed: false}) === BigInt(1)).toBeTruthy(); + expect(bigint_from_bytes(h("7f"), {signed: false}) === BigInt(127)).toBeTruthy(); + expect(bigint_from_bytes(h("80"), {signed: false}) === BigInt(128)).toBeTruthy(); + expect(bigint_from_bytes(h("ff"), {signed: false}) === BigInt(255)).toBeTruthy(); + expect(bigint_from_bytes(h("ffffffff"), {signed: false}) === BigInt(4294967295)).toBeTruthy(); + expect(bigint_from_bytes(h("ffffffffffffffff"), {signed: false}) === BigInt("18446744073709551615")).toBeTruthy(); + expect(bigint_from_bytes(h("7fffffffffffffff"), {signed: false}) === BigInt(2)**BigInt(63) - BigInt(1)).toBeTruthy(); + }); + test("default option", () => { + expect(bigint_from_bytes(None) === bigint_from_bytes(None, {signed: false})).toBeTruthy(); + expect(bigint_from_bytes(h("01")) === bigint_from_bytes(h("01"), {signed: false})).toBeTruthy(); + expect(bigint_from_bytes(h("7f")) === bigint_from_bytes(h("7f"), {signed: false})).toBeTruthy(); + expect(bigint_from_bytes(h("80")) === bigint_from_bytes(h("80"), {signed: false})).toBeTruthy(); + expect(bigint_from_bytes(h("ff")) === bigint_from_bytes(h("ff"), {signed: false})).toBeTruthy(); + expect(bigint_from_bytes(h("ffffffff")) === bigint_from_bytes(h("ffffffff"), {signed: false})).toBeTruthy(); + expect(bigint_from_bytes(h("ffffffffffffffff"), {signed: false}) === BigInt("18446744073709551615")).toBeTruthy(); + expect(bigint_from_bytes(h("7fffffffffffffff")) === bigint_from_bytes(h("7fffffffffffffff"), {signed: false})).toBeTruthy(); + }); }); -test("int_to_bytes", () => { - expect(int_to_bytes(0).equal_to(h(""))).toBeTruthy(); - expect(int_to_bytes(1).equal_to(h("01"))).toBeTruthy(); - expect(int_to_bytes(-1).equal_to(h("ff"))).toBeTruthy(); - expect(int_to_bytes(127).equal_to(h("7f"))).toBeTruthy(); - expect(int_to_bytes(-128).equal_to(h("80"))).toBeTruthy(); - expect(int_to_bytes(255).equal_to(h("00ff"))).toBeTruthy(); - expect(int_to_bytes(-256).equal_to(h("ff00"))).toBeTruthy(); - expect(int_to_bytes(65535).equal_to(h("00ffff"))).toBeTruthy(); - expect(int_to_bytes(65536).equal_to(h("010000"))).toBeTruthy(); - expect(int_to_bytes(-65535).equal_to(h("ff0001"))).toBeTruthy(); - expect(int_to_bytes(-65534).equal_to(h("ff0002"))).toBeTruthy(); - expect(int_to_bytes(-65536).equal_to(h("ff0000"))).toBeTruthy(); +describe("int_to_bytes", () => { + test("signed: true", () => { + expect(int_to_bytes(0, {signed: true}).equal_to(h(""))).toBeTruthy(); + expect(int_to_bytes(1, {signed: true}).equal_to(h("01"))).toBeTruthy(); + expect(int_to_bytes(-1, {signed: true}).equal_to(h("ff"))).toBeTruthy(); + expect(int_to_bytes(127, {signed: true}).equal_to(h("7f"))).toBeTruthy(); + expect(int_to_bytes(-128, {signed: true}).equal_to(h("80"))).toBeTruthy(); + expect(int_to_bytes(255, {signed: true}).equal_to(h("00ff"))).toBeTruthy(); + expect(int_to_bytes(-256, {signed: true}).equal_to(h("ff00"))).toBeTruthy(); + expect(int_to_bytes(65535, {signed: true}).equal_to(h("00ffff"))).toBeTruthy(); + expect(int_to_bytes(65536, {signed: true}).equal_to(h("010000"))).toBeTruthy(); + expect(int_to_bytes(-65535, {signed: true}).equal_to(h("ff0001"))).toBeTruthy(); + expect(int_to_bytes(-65534, {signed: true}).equal_to(h("ff0002"))).toBeTruthy(); + expect(int_to_bytes(-65536, {signed: true}).equal_to(h("ff0000"))).toBeTruthy(); + }); + test("signed: false", () => { + expect(int_to_bytes(0, {signed: false}).equal_to(h(""))).toBeTruthy(); + expect(int_to_bytes(1, {signed: false}).equal_to(h("01"))).toBeTruthy(); + expect(() => int_to_bytes(-1, {signed: false})).toThrow(); + expect(int_to_bytes(127, {signed: false}).equal_to(h("7f"))).toBeTruthy(); + expect(() => int_to_bytes(-128, {signed: false})).toThrow(); + expect(int_to_bytes(255, {signed: false}).equal_to(h("ff"))).toBeTruthy(); + expect(() => int_to_bytes(-256, {signed: false})).toThrow(); + expect(int_to_bytes(65535, {signed: false}).equal_to(h("ffff"))).toBeTruthy(); + expect(int_to_bytes(65536, {signed: false}).equal_to(h("010000"))).toBeTruthy(); + expect(() => int_to_bytes(-65535, {signed: false})).toThrow(); + }); + test("default option", () => { + expect(int_to_bytes(0).equal_to(int_to_bytes(0, {signed: false}))).toBeTruthy(); + expect(int_to_bytes(1).equal_to(int_to_bytes(1, {signed: false}))).toBeTruthy(); + expect(() => int_to_bytes(-1)).toThrow(); + expect(int_to_bytes(127).equal_to(int_to_bytes(127, {signed: false}))).toBeTruthy(); + expect(() => int_to_bytes(-128)).toThrow(); + expect(int_to_bytes(255).equal_to(int_to_bytes(255, {signed: false}))).toBeTruthy(); + expect(() => int_to_bytes(-256)).toThrow(); + expect(int_to_bytes(65535).equal_to(int_to_bytes(65535, {signed: false}))).toBeTruthy(); + expect(int_to_bytes(65536).equal_to(int_to_bytes(65536, {signed: false}))).toBeTruthy(); + expect(() => int_to_bytes(-65535)).toThrow(); + }); }); -test("bigint_to_bytes", () => { - expect(bigint_to_bytes(BigInt(0)).equal_to(h(""))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(1)).equal_to(h("01"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(-1)).equal_to(h("ff"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(127)).equal_to(h("7f"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(-128)).equal_to(h("80"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(255)).equal_to(h("00ff"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(-256)).equal_to(h("ff00"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(65535)).equal_to(h("00ffff"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(65536)).equal_to(h("010000"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(-65535)).equal_to(h("ff0001"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(-65534)).equal_to(h("ff0002"))).toBeTruthy(); - expect(bigint_to_bytes(BigInt(-65536)).equal_to(h("ff0000"))).toBeTruthy(); - expect(bigint_to_bytes( - BigInt("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001")).equal_to( +describe("bigint_to_bytes", () => { + test("signed: true", () => { + expect(bigint_to_bytes(BigInt(0), {signed: true}).equal_to(h(""))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(1), {signed: true}).equal_to(h("01"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(-1), {signed: true}).equal_to(h("ff"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(127), {signed: true}).equal_to(h("7f"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(-128), {signed: true}).equal_to(h("80"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(255), {signed: true}).equal_to(h("00ff"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(-256), {signed: true}).equal_to(h("ff00"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(65535), {signed: true}).equal_to(h("00ffff"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(65536), {signed: true}).equal_to(h("010000"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(-65535), {signed: true}).equal_to(h("ff0001"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(-65534), {signed: true}).equal_to(h("ff0002"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(-65536), {signed: true}).equal_to(h("ff0000"))).toBeTruthy(); + expect(bigint_to_bytes( + BigInt("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001"), {signed: true}).equal_to( + h("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001") + ) + ).toBeTruthy(); + }); + test("signed: false", () => { + expect(bigint_to_bytes(BigInt(0), {signed: false}).equal_to(h(""))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(1), {signed: false}).equal_to(h("01"))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-1), {signed: false})).toThrow(); + expect(bigint_to_bytes(BigInt(127), {signed: false}).equal_to(h("7f"))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-128), {signed: false})).toThrow(); + expect(bigint_to_bytes(BigInt(255), {signed: false}).equal_to(h("ff"))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-256), {signed: false})).toThrow(); + expect(bigint_to_bytes(BigInt(65535), {signed: false}).equal_to(h("ffff"))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(65536), {signed: false}).equal_to(h("010000"))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-65535), {signed: false})).toThrow(); + expect(bigint_to_bytes( + BigInt("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001"), {signed: false}).equal_to( h("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001") ) - ).toBeTruthy(); + ).toBeTruthy(); + }); + test("default option", () => { + expect(bigint_to_bytes(BigInt(0)).equal_to(bigint_to_bytes(BigInt(0), {signed: false}))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(1)).equal_to(bigint_to_bytes(BigInt(1), {signed: false}))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-1))).toThrow(); + expect(bigint_to_bytes(BigInt(127)).equal_to(bigint_to_bytes(BigInt(127), {signed: false}))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-128))).toThrow(); + expect(bigint_to_bytes(BigInt(255)).equal_to(bigint_to_bytes(BigInt(255), {signed: false}))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-256))).toThrow(); + expect(bigint_to_bytes(BigInt(65535)).equal_to(bigint_to_bytes(BigInt(65535), {signed: false}))).toBeTruthy(); + expect(bigint_to_bytes(BigInt(65536)).equal_to(bigint_to_bytes(BigInt(65536), {signed: false}))).toBeTruthy(); + expect(() => bigint_to_bytes(BigInt(-65535))).toThrow(); + expect( + bigint_to_bytes(BigInt("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001")).equal_to( + bigint_to_bytes(BigInt("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001"), {signed: false}) + ) + ).toBeTruthy(); + }); }); test("limbs_for_int", () => {