diff --git a/package.json b/package.json index 7f373bc0..56e12a21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@helios-lang/compiler", - "version": "0.17.0-79", + "version": "0.17.0-80", "description": "Helios is a Domain Specific Language that compiles to Plutus-Core (i.e. Cardano on-chain validator scripts). Helios is a non-Haskell alternative to Plutus. With this library you can compile Helios scripts and build Cardano transactions, all you need to build 100% client-side dApps for Cardano.", "main": "src/index.js", "types": "types/index.d.ts", diff --git a/src/codegen/makeRawFuncs.js b/src/codegen/makeRawFuncs.js index 85cc4dfc..64323d51 100644 --- a/src/codegen/makeRawFuncs.js +++ b/src/codegen/makeRawFuncs.js @@ -1192,7 +1192,7 @@ export function makeRawFunctions(simplify, isTestnet) { `(a, b) -> { bt = __helios__ratio__top(b); bb = __helios__ratio__bottom(b); - __helios__ratio__new( + __helios__ratio__new_internal( __helios__int____sub( __helios__int____mul(a, bb), bt @@ -2059,7 +2059,7 @@ export function makeRawFunctions(simplify, isTestnet) { ) add( new RawFunc( - "__helios__ratio__new", + "__helios__ratio__new_internal", `(top, bottom) -> { __core__listData( __core__mkCons( @@ -2073,6 +2073,22 @@ export function makeRawFunctions(simplify, isTestnet) { }` ) ) + add( + new RawFunc( + "__helios__ratio__new", + `(top, bottom) -> { + __core__ifThenElse( + __core__lessThanInteger(bottom, 0), + () -> { + __helios__ratio__new_internal(__core__multiplyInteger(top, -1), __core__multiplyInteger(bottom, -1)) + }, + () -> { + __helios__ratio__new_internal(top, bottom) + } + )() + }` + ) + ) add( new RawFunc( "__helios__ratio__top", @@ -2102,7 +2118,7 @@ export function makeRawFunctions(simplify, isTestnet) { __helios__int____mul(at, bb), __helios__int____mul(bt, ab) ); - __helios__ratio__new(new_top, new_bottom) + __helios__ratio__new_internal(new_top, new_bottom) }` ) ) @@ -2116,7 +2132,7 @@ export function makeRawFunctions(simplify, isTestnet) { at, __helios__int____mul(b, ab) ); - __helios__ratio__new(new_top, ab) + __helios__ratio__new_internal(new_top, ab) }` ) ) @@ -2133,7 +2149,7 @@ export function makeRawFunctions(simplify, isTestnet) { __helios__int____mul(at, bb), __helios__int____mul(bt, ab) ); - __helios__ratio__new(new_top, new_bottom) + __helios__ratio__new_internal(new_top, new_bottom) }` ) ) @@ -2147,7 +2163,7 @@ export function makeRawFunctions(simplify, isTestnet) { at, __helios__int____mul(b, ab) ); - __helios__ratio__new(new_top, ab) + __helios__ratio__new_internal(new_top, ab) }` ) ) @@ -2161,7 +2177,7 @@ export function makeRawFunctions(simplify, isTestnet) { bb = __helios__ratio__bottom(b); new_bottom = __helios__int____mul(ab, bb); new_top = __helios__int____mul(at, bt); - __helios__ratio__new(new_top, new_bottom) + __helios__ratio__new_internal(new_top, new_bottom) }` ) ) @@ -2172,7 +2188,7 @@ export function makeRawFunctions(simplify, isTestnet) { at = __helios__ratio__top(a); ab = __helios__ratio__bottom(a); new_top = __helios__int____mul(at, b); - __helios__ratio__new(new_top, ab) + __helios__ratio__new_internal(new_top, ab) }` ) ) @@ -2361,6 +2377,31 @@ export function makeRawFunctions(simplify, isTestnet) { }` ) ) + add( + new RawFunc( + "__helios__ratio__round", + `(self) -> { + () -> { + top = __helios__ratio__top(self); + bottom = __helios__ratio__bottom(self); + __core__quotientInteger( + __core__addInteger( + top, + __core__quotientInteger( + bottom, + __core__ifThenElse( + __core__lessThanInteger(top, 0), + -2, + 2 + ) + ) + ), + bottom + ) + } + }` + ) + ) add( new RawFunc( "__helios__ratio__to_real", @@ -2369,7 +2410,17 @@ export function makeRawFunctions(simplify, isTestnet) { top = __helios__ratio__top(self); bottom = __helios__ratio__bottom(self); __core__quotientInteger( - __core__multiplyInteger(top, __helios__real__ONE), + __core__addInteger( + __core__multiplyInteger(top, __helios__real__ONE), + __core__quotientInteger( + bottom, + __core__ifThenElse( + __core__lessThanInteger(top, 0), + -2, + 2 + ) + ) + ), bottom ) } @@ -2398,6 +2449,12 @@ export function makeRawFunctions(simplify, isTestnet) { "5" + new Array(REAL_PRECISION - 1).fill("0").join("") ) ) + add( + new RawFunc( + "__helios__real__MINUS_HALF", + "-5" + new Array(REAL_PRECISION - 1).fill("0").join("") + ) + ) add( new RawFunc( "__helios__real__NEARLY_ONE", @@ -2587,8 +2644,8 @@ export function makeRawFunctions(simplify, isTestnet) { "__helios__real__round", `(self) -> { () -> { - __core__divideInteger( - __core__addInteger(self, __helios__real__HALF), + __core__quotientInteger( + __core__addInteger(self, __core__ifThenElse(__core__lessThanInteger(self, 0), __helios__real__MINUS_HALF, __helios__real__HALF)), __helios__real__ONE ) } @@ -2639,7 +2696,7 @@ export function makeRawFunctions(simplify, isTestnet) { "__helios__real__to_ratio", `(self) -> { () -> { - __helios__ratio__new( + __helios__ratio__new_internal( self, __helios__real__ONE ) diff --git a/src/program/Program.js b/src/program/Program.js index 838cb4d4..009acc33 100644 --- a/src/program/Program.js +++ b/src/program/Program.js @@ -20,6 +20,7 @@ import { UserFunc } from "./UserFunc.js" /** * @typedef {import("@helios-lang/compiler-utils").Site} Site + * @typedef {import("@helios-lang/ir").OptimizeOptions} OptimizeOptions * @typedef {import("@helios-lang/ir").ParseOptions} ParseOptions * @typedef {import("@helios-lang/uplc").UplcData} UplcData * @typedef {import("@helios-lang/uplc").UplcValue} UplcValue @@ -44,7 +45,7 @@ import { UserFunc } from "./UserFunc.js" /** * @typedef {{ - * optimize?: boolean + * optimize?: boolean | OptimizeOptions * dependsOnOwnHash?: boolean * hashDependencies?: Record * validatorIndices?: Record @@ -275,7 +276,7 @@ export class Program { : optimizeOrOptions const hashDependencies = options.hashDependencies ?? {} - const optimize = options.optimize ?? false + const optimize = !!(options.optimize ?? false) const ir = this.toIR({ dependsOnOwnHash: options.dependsOnOwnHash ?? false, @@ -285,7 +286,11 @@ export class Program { const uplc = compileIR(ir, { optimize: optimize, - parseOptions: IR_PARSE_OPTIONS + parseOptions: IR_PARSE_OPTIONS, + optimizeOptions: + options.optimize && typeof options.optimize != "boolean" + ? options.optimize + : undefined }) // userfuncs might depend on own hash, which is easer to inject after compilation of main program diff --git a/src/program/version.js b/src/program/version.js index cbd41239..9d1bdbf7 100644 --- a/src/program/version.js +++ b/src/program/version.js @@ -1 +1 @@ -export const VERSION = "0.17.0-79" +export const VERSION = "0.17.0-80" diff --git a/src/typecheck/primitives.js b/src/typecheck/primitives.js index edb32b63..2fb31680 100644 --- a/src/typecheck/primitives.js +++ b/src/typecheck/primitives.js @@ -318,7 +318,8 @@ export const RatioType = new GenericType({ ceil: new FuncType([], IntType), floor: new FuncType([], IntType), to_real: new FuncType([], RealType), - trunc: new FuncType([], IntType) + trunc: new FuncType([], IntType), + round: new FuncType([], IntType) }), genTypeMembers: (self) => ({ ...genCommonTypeMembers(self), diff --git a/test/Int.test.js b/test/Int.test.js index fa14e5d6..4a13b010 100644 --- a/test/Int.test.js +++ b/test/Int.test.js @@ -265,6 +265,10 @@ describe("Int", () => { runner3([int(-9), int(10)], int(0)) }) + it("-9999999999999 / 10000000000000 == 0", () => { + runner3([int(-9999999999999), int(10000000000000)], int(0)) + }) + it("-10 / 10 == -1", () => { runner3([int(-10), int(10)], int(-1)) }) diff --git a/test/Ratio.test.js b/test/Ratio.test.js index 6ea37410..93e5e2e2 100644 --- a/test/Ratio.test.js +++ b/test/Ratio.test.js @@ -422,6 +422,128 @@ describe("Ratio", () => { }) }) + describe("Ratio.trunc", () => { + const runner = compileForRun(`testing ratio_trunc + func main(a: Int, b: Int) -> Int { + Ratio::new(a, b).trunc() + }`) + + it("3/2.trunc() == 1", () => { + runner([int(3), int(2)], int(1)) + }) + + it("-3/2.trunc() == -1", () => { + runner([int(-3), int(2)], int(-1)) + }) + + it("-1/1000000.trunc() == 0", () => { + runner([int(-1), int(1000000)], int(0)) + }) + + it("1/1000000.trunc() == 0", () => { + runner([int(1), int(1000000)], int(0)) + }) + + it("4999999/10000000.trunc() == 0", () => { + runner([int(4999999), int(10000000)], int(0)) + }) + + it("5000001/10000000.trunc() == 0", () => { + runner([int(5000001), int(10000000)], int(0)) + }) + + it("9999999/10000000.trunc() == 0", () => { + runner([int(9999999), int(10000000)], int(0)) + }) + + it("-4999999/10000000.trunc() == 0", () => { + runner([int(-4999999), int(10000000)], int(0)) + }) + + it("-5000001/10000000.trunc() == 0", () => { + runner([int(-5000001), int(10000000)], int(0)) + }) + + it("4999999/-10000000.trunc() == 0", () => { + runner([int(4999999), int(-10000000)], int(0)) + }) + + it("5000001/-10000000.trunc() == 0", () => { + runner([int(5000001), int(-10000000)], int(0)) + }) + + it("-4999999/-10000000.trunc() == 0", () => { + runner([int(-4999999), int(-10000000)], int(0)) + }) + + it("-5000001/-10000000.trunc() == 0", () => { + runner([int(-5000001), int(-10000000)], int(0)) + }) + + it("140/120.trunc() == 1", () => { + runner([int(140), int(120)], int(1)) + }) + }) + + describe("Ratio.round", () => { + const runner = compileForRun(`testing ratio_round + func main(a: Int, b: Int) -> Int { + Ratio::new(a, b).round() + }`) + + it("3/2.round() == 2", () => { + runner([int(3), int(2)], int(2)) + }) + + it("-3/2.round() == -2", () => { + runner([int(-3), int(2)], int(-2)) + }) + + it("-1/1000000.round() == 0", () => { + runner([int(-1), int(1000000)], int(0)) + }) + + it("1/1000000.round() == 0", () => { + runner([int(1), int(1000000)], int(0)) + }) + + it("4999999/10000000.round() == 0", () => { + runner([int(4999999), int(10000000)], int(0)) + }) + + it("5000001/10000000.round() == 1", () => { + runner([int(5000001), int(10000000)], int(1)) + }) + + it("-4999999/10000000.round() == 0", () => { + runner([int(-4999999), int(10000000)], int(0)) + }) + + it("-5000001/10000000.round() == -1", () => { + runner([int(-5000001), int(10000000)], int(-1)) + }) + + it("4999999/-10000000.round() == 0", () => { + runner([int(4999999), int(-10000000)], int(0)) + }) + + it("5000001/-10000000.round() == -1", () => { + runner([int(5000001), int(-10000000)], int(-1)) + }) + + it("-4999999/-10000000.round() == 0", () => { + runner([int(-4999999), int(-10000000)], int(0)) + }) + + it("-5000001/-10000000.round() == 1", () => { + runner([int(-5000001), int(-10000000)], int(1)) + }) + + it("140/120.round() == 1", () => { + runner([int(140), int(120)], int(1)) + }) + }) + describe("Ratio.to_real", () => { const runner = compileForRun(`testing ratio_to_real func main(a: Int, b: Int) -> Real { @@ -443,6 +565,82 @@ describe("Ratio", () => { it("-1/10000000.to_real() == 0", () => { runner([int(-1), int(10000000)], real(0)) }) + + it("1/1000000.to_real() == 0.000001", () => { + runner([int(1), int(1000000)], real(0.000001)) + }) + + it("1/10000000.to_real() == 0", () => { + runner([int(1), int(10000000)], real(0)) + }) + + it("4999999/10000000.to_real() == 0.5", () => { + runner([int(4999999), int(10000000)], real(0.5)) + }) + + it("5000001/10000000.to_real() == 0.5", () => { + runner([int(5000001), int(10000000)], real(0.5)) + }) + + it("5000004/10000000.to_real() == 0.5", () => { + runner([int(5000004), int(10000000)], real(0.5)) + }) + + it("5000005/10000000.to_real() == 0.500001", () => { + runner([int(5000005), int(10000000)], real(0.500001)) + }) + + it("-4999999/10000000.to_real() == -0.5", () => { + runner([int(-4999999), int(10000000)], real(-0.5)) + }) + + it("-5000001/10000000.to_real() == -0.5", () => { + runner([int(-5000001), int(10000000)], real(-0.5)) + }) + + it("-5000004/10000000.to_real() == -0.5", () => { + runner([int(-5000004), int(10000000)], real(-0.5)) + }) + + it("-5000005/10000000.to_real() == -0.500001", () => { + runner([int(-5000005), int(10000000)], real(-0.500001)) + }) + + it("4999999/-10000000.to_real() == -0.5", () => { + runner([int(4999999), int(-10000000)], real(-0.5)) + }) + + it("5000001/-10000000.to_real() == -0.5", () => { + runner([int(5000001), int(-10000000)], real(-0.5)) + }) + + it("5000004/-10000000.to_real() == -0.5", () => { + runner([int(5000004), int(-10000000)], real(-0.5)) + }) + + it("5000005/-10000000.to_real() == -0.500001", () => { + runner([int(5000005), int(-10000000)], real(-0.500001)) + }) + + it("-4999999/-10000000.to_real() == 0.5", () => { + runner([int(-4999999), int(-10000000)], real(0.5)) + }) + + it("-5000001/-10000000.to_real() == 0.5", () => { + runner([int(-5000001), int(-10000000)], real(0.5)) + }) + + it("-5000004/-10000000.to_real() == 0.5", () => { + runner([int(-5000004), int(-10000000)], real(0.5)) + }) + + it("-5000005/-10000000.to_real() == 0.500001", () => { + runner([int(-5000005), int(-10000000)], real(0.500001)) + }) + + it("140/120.to_real() == 1.166667", () => { + runner([int(140), int(120)], real(1.166667)) + }) }) describe("Ratio::is_valid_data", () => { diff --git a/test/Real.test.js b/test/Real.test.js index 1e01e76e..e6bebc49 100644 --- a/test/Real.test.js +++ b/test/Real.test.js @@ -430,6 +430,53 @@ describe("Real", () => { }) }) + describe("Real.round", () => { + const runner = compileForRun(`testing real_round + func main(a: Real) -> Int { + a.round() + }`) + + it("(0.5).round() == 1", () => { + runner([real(0.5)], int(1)) + }) + + it("(0.499999).round() == 0", () => { + runner([real(0.499999)], int(0)) + }) + + it("(-0.5).round() == -1", () => { + runner([real(-0.5)], int(-1)) + }) + + it("(-0.499999).round() == 0", () => { + runner([real(-0.499999)], int(0)) + }) + + it("1.000010.round() == 1", () => { + runner([real(1.00001)], int(1.0)) + }) + + it("1.999999.round() == 2", () => { + runner([real(1.999999)], int(2)) + }) + + it("2.round() == 2", () => { + runner([real(2)], int(2)) + }) + + it("-1.000010.round() == -1", () => { + runner([real(-1.00001)], int(-1)) + }) + + it("-1.999999.round() == -2", () => { + runner([real(-1.999999)], int(-2)) + }) + + it("-2.round() == -2", () => { + runner([real(-2)], int(-2)) + }) + }) + describe("Real.show", () => { const runner = compileForRun(`testing real_show func main(a: Real) -> String {