Skip to content

Commit

Permalink
feat: add log2 and log functions in stdlib/math.tact (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gusarich authored Mar 21, 2024
1 parent 6bd585c commit 729d3f6
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
- Update the `dump` function to handle addresses: PR [#175](https://github.com/tact-lang/tact/pull/175)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3099,6 +3099,38 @@ 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": "if (num < base) {
return 0;
}
int result = 0;
while (num >= base) {
num /= base;
result += 1;
}
return result;",
"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;
Expand Down Expand Up @@ -7049,6 +7081,38 @@ 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": "if (num < base) {
return 0;
}
int result = 0;
while (num >= base) {
num /= base;
result += 1;
}
return result;",
"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;
Expand Down Expand Up @@ -10999,6 +11063,38 @@ 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": "if (num < base) {
return 0;
}
int result = 0;
while (num >= base) {
num /= base;
result += 1;
}
return result;",
"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;
Expand Down
24 changes: 24 additions & 0 deletions src/generator/writers/writeStdlib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1325,4 +1325,28 @@ 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(`
if (num < base) {
return 0;
}
int result = 0;
while (num >= base) {
num /= base;
result += 1;
}
return result;
`);
});
});
}
4 changes: 3 additions & 1 deletion src/imports/stdlib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ files['std/math.tact'] =
'CmlubGluZSBmdW4gcmFuZG9tSW50KCk6IEludCB7CiAgICBuYXRpdmVQcmVwYXJlUmFuZG9tKCk7CiAgICByZXR1cm4gbmF0aXZlUmFuZG9tKCk7Cn0KCmlubGluZSBm' +
'dW4gcmFuZG9tKG1pbjogSW50LCBtYXg6IEludCk6IEludCB7CiAgICBuYXRpdmVQcmVwYXJlUmFuZG9tKCk7CiAgICByZXR1cm4gbWluICsgbmF0aXZlUmFuZG9tSW50' +
'ZXJ2YWwobWF4IC0gbWluKTsKfQoKLy8gTWF0aAoKQG5hbWUobWluKQpuYXRpdmUgbWluKHg6IEludCwgeTogSW50KTogSW50OwoKQG5hbWUobWF4KQpuYXRpdmUgbWF4' +
'KHg6IEludCwgeTogSW50KTogSW50OwoKQG5hbWUoYWJzKQpuYXRpdmUgYWJzKHg6IEludCk6IEludDsKCkBuYW1lKG5vdykKbmF0aXZlIG5vdygpOiBJbnQ7';
'KHg6IEludCwgeTogSW50KTogSW50OwoKQG5hbWUoYWJzKQpuYXRpdmUgYWJzKHg6IEludCk6IEludDsKCkBuYW1lKG5vdykKbmF0aXZlIG5vdygpOiBJbnQ7CgpAbmFt' +
'ZShfX3RhY3RfbG9nMikKbmF0aXZlIGxvZzIobnVtOiBJbnQpOiBJbnQ7CgpAbmFtZShfX3RhY3RfbG9nKQpuYXRpdmUgbG9nKG51bTogSW50LCBiYXNlOiBJbnQpOiBJ' +
'bnQ7';
files['std/primitives.tact'] =
'cHJpbWl0aXZlIEludDsKcHJpbWl0aXZlIEJvb2w7CnByaW1pdGl2ZSBCdWlsZGVyOwpwcmltaXRpdmUgU2xpY2U7CnByaW1pdGl2ZSBDZWxsOwpwcmltaXRpdmUgQWRk' +
'cmVzczsKcHJpbWl0aXZlIFN0cmluZzsKcHJpbWl0aXZlIFN0cmluZ0J1aWxkZXI7';
Expand Down
63 changes: 57 additions & 6 deletions src/test/feature-math.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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<bigint, bigint>().set(0n, 0n);
const dictB = Dictionary.empty<bigint, bigint>().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
Expand Down Expand Up @@ -335,5 +338,53 @@ 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 <= 10n; num++) {
for (let base = 2n; base <= 10; base++) {
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;

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 = 2; base <= 10; base++) {
expect(await contract.getLog(num, BigInt(base))).toBe(
BigInt(num.toString(base).length - 1)
);
}
}

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 = 2; base <= 10; base++) {
expect(await contract.getLog(num, BigInt(base))).toBe(
BigInt(num.toString(base).length - 1)
);
}
}
});
});
});
12 changes: 12 additions & 0 deletions src/test/features/math.tact
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
8 changes: 7 additions & 1 deletion stdlib/std/math.tact
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ native max(x: Int, y: Int): Int;
native abs(x: Int): Int;

@name(now)
native now(): Int;
native now(): Int;

@name(__tact_log2)
native log2(num: Int): Int;

@name(__tact_log)
native log(num: Int, base: Int): Int;

0 comments on commit 729d3f6

Please sign in to comment.