From aedc81dcdb6086f592db96c90812e45b992c4cfc Mon Sep 17 00:00:00 2001 From: Gusarich Date: Mon, 4 Mar 2024 15:32:27 +0300 Subject: [PATCH 1/7] implement `log2` and `log` functions in math.tact --- src/generator/writers/writeStdlib.ts | 16 ++++++++++++++ src/imports/stdlib.ts | 4 +++- src/test/feature-math.spec.ts | 33 +++++++++++++++++++++++----- src/test/features/math.tact | 12 ++++++++++ stdlib/std/math.tact | 8 ++++++- 5 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index c1b2c6096..b53c349ff 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -1223,4 +1223,20 @@ export function writeStdlib(ctx: WriterContext) { `); }); }); + + ctx.fun(`__tact_log2`, () => { + ctx.signature(`int __tact_log2(int num)`); + ctx.context('stdlib'); + ctx.asm(`asm "UBITSIZE DEC"`); + }); + + ctx.fun(`__tact_log`, () => { + ctx.signature(`int __tact_log(int num, int base)`); + ctx.context('stdlib'); + ctx.body(() => { + ctx.write(` + return __tact_log2(num) / __tact_log2(base); + `); + }); + }); } \ No newline at end of file diff --git a/src/imports/stdlib.ts b/src/imports/stdlib.ts index 2431a8481..05b90b7fb 100644 --- a/src/imports/stdlib.ts +++ b/src/imports/stdlib.ts @@ -183,7 +183,9 @@ files['std/math.tact'] = 'CmlubGluZSBmdW4gcmFuZG9tSW50KCk6IEludCB7CiAgICBuYXRpdmVQcmVwYXJlUmFuZG9tKCk7CiAgICByZXR1cm4gbmF0aXZlUmFuZG9tKCk7Cn0KCmlubGluZSBm' + 'dW4gcmFuZG9tKG1pbjogSW50LCBtYXg6IEludCk6IEludCB7CiAgICBuYXRpdmVQcmVwYXJlUmFuZG9tKCk7CiAgICByZXR1cm4gbWluICsgbmF0aXZlUmFuZG9tSW50' + 'ZXJ2YWwobWF4IC0gbWluKTsKfQoKLy8gTWF0aAoKQG5hbWUobWluKQpuYXRpdmUgbWluKHg6IEludCwgeTogSW50KTogSW50OwoKQG5hbWUobWF4KQpuYXRpdmUgbWF4' + - 'KHg6IEludCwgeTogSW50KTogSW50OwoKQG5hbWUoYWJzKQpuYXRpdmUgYWJzKHg6IEludCk6IEludDsKCkBuYW1lKG5vdykKbmF0aXZlIG5vdygpOiBJbnQ7'; + 'KHg6IEludCwgeTogSW50KTogSW50OwoKQG5hbWUoYWJzKQpuYXRpdmUgYWJzKHg6IEludCk6IEludDsKCkBuYW1lKG5vdykKbmF0aXZlIG5vdygpOiBJbnQ7CgpAbmFt' + + 'ZShfX3RhY3RfbG9nMikKbmF0aXZlIGxvZzIobnVtOiBJbnQpOiBJbnQ7CgpAbmFtZShfX3RhY3RfbG9nKQpuYXRpdmUgbG9nKG51bTogSW50LCBiYXNlOiBJbnQpOiBJ' + + 'bnQ7'; files['std/primitives.tact'] = 'cHJpbWl0aXZlIEludDsKcHJpbWl0aXZlIEJvb2w7CnByaW1pdGl2ZSBCdWlsZGVyOwpwcmltaXRpdmUgU2xpY2U7CnByaW1pdGl2ZSBDZWxsOwpwcmltaXRpdmUgQWRk' + 'cmVzczsKcHJpbWl0aXZlIFN0cmluZzsKcHJpbWl0aXZlIFN0cmluZ0J1aWxkZXI7'; diff --git a/src/test/feature-math.spec.ts b/src/test/feature-math.spec.ts index 5eb3ed23a..0a24b9a81 100644 --- a/src/test/feature-math.spec.ts +++ b/src/test/feature-math.spec.ts @@ -7,8 +7,7 @@ describe('feature-math', () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should perform basic math operations correctly', async () => { - + it('should perform math operations correctly', async () => { // Init const system = await ContractSystem.create(); const treasure = system.treasure('treasure'); @@ -25,11 +24,15 @@ describe('feature-math', () => { .storeBit(1) .storeRef(beginCell().storeBit(1).endCell()) .endCell(); - const stringA = "foo"; - const stringB = "bar"; + const stringA = 'foo'; + const stringB = 'bar'; const dictA = Dictionary.empty().set(0n, 0n); const dictB = Dictionary.empty().set(0n, 2n); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Deploy', queryId: 0n }); + await contract.send( + treasure, + { value: toNano('10') }, + { $$type: 'Deploy', queryId: 0n } + ); await system.run(); // Tests @@ -335,5 +338,23 @@ describe('feature-math', () => { expect(await contract.getCompare28(dictA, dictB)).toBe(true); expect(await contract.getCompare28(dictB, dictA)).toBe(true); expect(await contract.getCompare28(dictA, dictA)).toBe(false); + + // Test advanced math operations + for (let num = 1n; num <= 100n; num++) { + expect(await contract.getLog2(num)).toBe( + BigInt(Math.floor(Math.log2(Number(num)))) + ); + } + + for (let num = 1n; num <= 100n; num++) { + for (let base = 1n; base <= 10; base++) { + try { + const a = Math.floor(Math.log(Number(num))); + const b = Math.floor(Math.log(Number(base))); + const c = BigInt(a / b); + expect(await contract.getLog(num, base)).toBe(c); + } catch (e) {} + } + } }); -}); \ No newline at end of file +}); diff --git a/src/test/features/math.tact b/src/test/features/math.tact index a66d497e9..661762a48 100644 --- a/src/test/features/math.tact +++ b/src/test/features/math.tact @@ -295,4 +295,16 @@ contract MathTester with Deployable { get fun isNotNull3(cell: Cell?): Bool { return cell != null; } + + // + // Advanced Math Operations + // + + get fun log2(num: Int): Int { + return log2(num); + } + + get fun log(num: Int, base: Int): Int { + return log(num, base); + } } \ No newline at end of file diff --git a/stdlib/std/math.tact b/stdlib/std/math.tact index 89457c594..5fa9ae833 100644 --- a/stdlib/std/math.tact +++ b/stdlib/std/math.tact @@ -39,4 +39,10 @@ native max(x: Int, y: Int): Int; native abs(x: Int): Int; @name(now) -native now(): Int; \ No newline at end of file +native now(): Int; + +@name(__tact_log2) +native log2(num: Int): Int; + +@name(__tact_log) +native log(num: Int, base: Int): Int; \ No newline at end of file From b4fb803c9d22bee06bae47c11e2d1e095209265d Mon Sep 17 00:00:00 2001 From: Gusarich Date: Mon, 4 Mar 2024 15:34:00 +0300 Subject: [PATCH 2/7] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fb250b6c..1145e3891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `log2` and `log` math functions in stdlib: PR [#166](https://github.com/tact-lang/tact/pull/166) + ### Changed ### Fixed From 14963bbb1985bf6f62909c9e99443c2a3ce8007d Mon Sep 17 00:00:00 2001 From: Gusarich Date: Tue, 5 Mar 2024 21:46:34 +0300 Subject: [PATCH 3/7] update snapshots --- .../writeSerialization.spec.ts.snap | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap index 22d73653d..79010f8b5 100644 --- a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap +++ b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap @@ -2972,6 +2972,30 @@ return b.end_cell().begin_parse();", "name": "__tact_float_to_string", "signature": "slice __tact_float_to_string(int src, int digits)", }, + { + "code": { + "code": "asm "UBITSIZE DEC"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_log2", + "signature": "int __tact_log2(int num)", + }, + { + "code": { + "code": "return __tact_log2(num) / __tact_log2(base);", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_log", + "signature": "int __tact_log(int num, int base)", + }, { "code": { "code": "var (v'a, v'b, v'c, v'd, v'e, v'f, v'g) = v; @@ -6795,6 +6819,30 @@ return b.end_cell().begin_parse();", "name": "__tact_float_to_string", "signature": "slice __tact_float_to_string(int src, int digits)", }, + { + "code": { + "code": "asm "UBITSIZE DEC"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_log2", + "signature": "int __tact_log2(int num)", + }, + { + "code": { + "code": "return __tact_log2(num) / __tact_log2(base);", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_log", + "signature": "int __tact_log(int num, int base)", + }, { "code": { "code": "var (v'a, v'b, v'c, v'd, v'e, v'f, v'g) = v; @@ -10618,6 +10666,30 @@ return b.end_cell().begin_parse();", "name": "__tact_float_to_string", "signature": "slice __tact_float_to_string(int src, int digits)", }, + { + "code": { + "code": "asm "UBITSIZE DEC"", + "kind": "asm", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_log2", + "signature": "int __tact_log2(int num)", + }, + { + "code": { + "code": "return __tact_log2(num) / __tact_log2(base);", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set {}, + "name": "__tact_log", + "signature": "int __tact_log(int num, int base)", + }, { "code": { "code": "var (v'a, v'b, v'c, v'd, v'e, v'f, v'g, v'h) = v; From 207d90c5a18a10fa8dc390197dd2cd00559410e5 Mon Sep 17 00:00:00 2001 From: Gusarich Date: Tue, 5 Mar 2024 22:39:28 +0300 Subject: [PATCH 4/7] fix test --- src/test/feature-math.spec.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/test/feature-math.spec.ts b/src/test/feature-math.spec.ts index 0a24b9a81..2221171e8 100644 --- a/src/test/feature-math.spec.ts +++ b/src/test/feature-math.spec.ts @@ -346,14 +346,12 @@ describe('feature-math', () => { ); } - for (let num = 1n; num <= 100n; num++) { - for (let base = 1n; base <= 10; base++) { - try { - const a = Math.floor(Math.log(Number(num))); - const b = Math.floor(Math.log(Number(base))); - const c = BigInt(a / b); - expect(await contract.getLog(num, base)).toBe(c); - } catch (e) {} + for (let num = 1n; num <= 10n; num++) { + for (let base = 2n; base <= 10; base++) { + const a = Math.floor(Math.log2(Number(num))); + const b = Math.floor(Math.log2(Number(base))); + const c = BigInt(Math.floor(a / b)); + expect(await contract.getLog(num, base)).toBe(c); } } }); From 708cadca8761d862dd584abc94ee7a495dec05ad Mon Sep 17 00:00:00 2001 From: Gusarich Date: Mon, 18 Mar 2024 03:41:08 +0300 Subject: [PATCH 5/7] rework `log` function --- .../writeSerialization.spec.ts.snap | 30 ++++++++++-- src/generator/writers/writeStdlib.ts | 10 +++- src/test/feature-math.spec.ts | 49 +++++++++++++++++-- 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap index 79010f8b5..2c57001ed 100644 --- a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap +++ b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap @@ -2986,7 +2986,15 @@ return b.end_cell().begin_parse();", }, { "code": { - "code": "return __tact_log2(num) / __tact_log2(base);", + "code": "if (num < base) { + return 0; +} +int result = 0; +while (num >= base) { + num = num / base; + result += 1; +} +return result;", "kind": "generic", }, "comment": null, @@ -6833,7 +6841,15 @@ return b.end_cell().begin_parse();", }, { "code": { - "code": "return __tact_log2(num) / __tact_log2(base);", + "code": "if (num < base) { + return 0; +} +int result = 0; +while (num >= base) { + num = num / base; + result += 1; +} +return result;", "kind": "generic", }, "comment": null, @@ -10680,7 +10696,15 @@ return b.end_cell().begin_parse();", }, { "code": { - "code": "return __tact_log2(num) / __tact_log2(base);", + "code": "if (num < base) { + return 0; +} +int result = 0; +while (num >= base) { + num = num / base; + result += 1; +} +return result;", "kind": "generic", }, "comment": null, diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index b53c349ff..05bdac4d3 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -1235,7 +1235,15 @@ export function writeStdlib(ctx: WriterContext) { ctx.context('stdlib'); ctx.body(() => { ctx.write(` - return __tact_log2(num) / __tact_log2(base); + if (num < base) { + return 0; + } + int result = 0; + while (num >= base) { + num = num / base; + result += 1; + } + return result; `); }); }); diff --git a/src/test/feature-math.spec.ts b/src/test/feature-math.spec.ts index 2221171e8..9e7b660b4 100644 --- a/src/test/feature-math.spec.ts +++ b/src/test/feature-math.spec.ts @@ -348,10 +348,51 @@ describe('feature-math', () => { for (let num = 1n; num <= 10n; num++) { for (let base = 2n; base <= 10; base++) { - const a = Math.floor(Math.log2(Number(num))); - const b = Math.floor(Math.log2(Number(base))); - const c = BigInt(Math.floor(a / b)); - expect(await contract.getLog(num, base)).toBe(c); + const logarithm = BigInt( + Math.floor(Math.log2(Number(num)) / Math.log2(Number(base))) + ); + expect(await contract.getLog(num, base)).toBe(logarithm); + } + } + + expect(await contract.getLog2(0n)).toBe(-1n); + expect(await contract.getLog(0n, 2n)).toBe(0n); + + const maxint = 2n ** 256n - 1n; + + function bigIntLogBase(num: bigint, base: bigint) { + let result = 0n; + while (num >= base) { + num = num / base; + result += 1n; + } + return result; + } + + for (let num = maxint - 100n; num <= maxint; num++) { + expect(await contract.getLog2(num)).toBe(255n); + } + + for (let num = maxint - 10n; num <= maxint; num++) { + for (let base = 2n; base <= 10; base++) { + expect(await contract.getLog(num, base)).toBe( + bigIntLogBase(num, base) + ); + } + } + + for (let num = maxint / 2n - 50n; num <= maxint / 2n; num++) { + expect(await contract.getLog2(num)).toBe(254n); + } + for (let num = maxint / 2n + 1n; num <= maxint / 2n + 50n; num++) { + expect(await contract.getLog2(num)).toBe(255n); + } + + for (let num = maxint / 2n - 5n; num <= maxint / 2n + 5n; num++) { + for (let base = 2n; base <= 10; base++) { + expect(await contract.getLog(num, base)).toBe( + bigIntLogBase(num, base) + ); } } }); From dc741aebd7c9ed6dbf75b7d4bec723af08ef3046 Mon Sep 17 00:00:00 2001 From: Daniil Sedov <42098239+Gusarich@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:13:19 +0300 Subject: [PATCH 6/7] Update src/generator/writers/writeStdlib.ts Co-authored-by: Anton Trunov --- src/generator/writers/writeStdlib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index 05bdac4d3..43a79d05d 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -1240,7 +1240,7 @@ export function writeStdlib(ctx: WriterContext) { } int result = 0; while (num >= base) { - num = num / base; + num /= base; result += 1; } return result; From 5e914a50a22e3b652e9235995aed60f22bab2464 Mon Sep 17 00:00:00 2001 From: Gusarich Date: Thu, 21 Mar 2024 18:21:59 +0300 Subject: [PATCH 7/7] fix --- .../writeSerialization.spec.ts.snap | 6 +++--- src/test/feature-math.spec.ts | 21 ++++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap index 2c57001ed..eabe16777 100644 --- a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap +++ b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap @@ -2991,7 +2991,7 @@ return b.end_cell().begin_parse();", } int result = 0; while (num >= base) { - num = num / base; + num /= base; result += 1; } return result;", @@ -6846,7 +6846,7 @@ return b.end_cell().begin_parse();", } int result = 0; while (num >= base) { - num = num / base; + num /= base; result += 1; } return result;", @@ -10701,7 +10701,7 @@ return b.end_cell().begin_parse();", } int result = 0; while (num >= base) { - num = num / base; + num /= base; result += 1; } return result;", diff --git a/src/test/feature-math.spec.ts b/src/test/feature-math.spec.ts index 9e7b660b4..740df83b4 100644 --- a/src/test/feature-math.spec.ts +++ b/src/test/feature-math.spec.ts @@ -360,23 +360,14 @@ describe('feature-math', () => { const maxint = 2n ** 256n - 1n; - function bigIntLogBase(num: bigint, base: bigint) { - let result = 0n; - while (num >= base) { - num = num / base; - result += 1n; - } - return result; - } - for (let num = maxint - 100n; num <= maxint; num++) { expect(await contract.getLog2(num)).toBe(255n); } for (let num = maxint - 10n; num <= maxint; num++) { - for (let base = 2n; base <= 10; base++) { - expect(await contract.getLog(num, base)).toBe( - bigIntLogBase(num, base) + for (let base = 2; base <= 10; base++) { + expect(await contract.getLog(num, BigInt(base))).toBe( + BigInt(num.toString(base).length - 1) ); } } @@ -389,9 +380,9 @@ describe('feature-math', () => { } for (let num = maxint / 2n - 5n; num <= maxint / 2n + 5n; num++) { - for (let base = 2n; base <= 10; base++) { - expect(await contract.getLog(num, base)).toBe( - bigIntLogBase(num, base) + for (let base = 2; base <= 10; base++) { + expect(await contract.getLog(num, BigInt(base))).toBe( + BigInt(num.toString(base).length - 1) ); } }