From 317038a34735d49a19c8e04c4d3c8be6942928be Mon Sep 17 00:00:00 2001 From: Bill Hails Date: Wed, 3 Apr 2024 11:08:18 +0100 Subject: [PATCH 1/4] moved arithmetic out of step.c --- src/arithmetic.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++ src/arithmetic.h | 34 ++++++++++ src/common.h | 6 ++ src/step.c | 148 +----------------------------------------- 4 files changed, 207 insertions(+), 146 deletions(-) create mode 100644 src/arithmetic.c create mode 100644 src/arithmetic.h diff --git a/src/arithmetic.c b/src/arithmetic.c new file mode 100644 index 0000000..87ec169 --- /dev/null +++ b/src/arithmetic.c @@ -0,0 +1,165 @@ +/* + * CEKF - VM supporting amb + * Copyright (C) 2022-2023 Bill Hails + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "common.h" +#include "bigint.h" +#include "arithmetic.h" + +IntegerBinOp add; +IntegerBinOp sub; +IntegerBinOp mul; +IntegerBinOp divide; +IntegerBinOp power; +IntegerBinOp modulo; + +static Value intValue(int i) { + Value value; + value.type = VALUE_TYPE_STDINT; + value.val = VALUE_VAL_STDINT(i); + return value; +} + +static Value bigIntValue(BigInt *i) { + Value value; + value.type = VALUE_TYPE_BIGINT; + value.val = VALUE_VAL_BIGINT(i); + return value; +} + +static Value bigAdd(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_BIGINT); + ASSERT(right.type == VALUE_TYPE_BIGINT); +#endif + BigInt *result = addBigInt(left.val.bigint, right.val.bigint); + return bigIntValue(result); +} + +static Value littleAdd(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_STDINT); + ASSERT(right.type == VALUE_TYPE_STDINT); +#endif + return intValue(left.val.stdint + right.val.stdint); +} + +static Value bigMul(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_BIGINT); + ASSERT(right.type == VALUE_TYPE_BIGINT); +#endif + BigInt *result = mulBigInt(left.val.bigint, right.val.bigint); + return bigIntValue(result); +} + +static Value littleMul(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_STDINT); + ASSERT(right.type == VALUE_TYPE_STDINT); +#endif + return intValue(left.val.stdint * right.val.stdint); +} + +static Value bigSub(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_BIGINT); + ASSERT(right.type == VALUE_TYPE_BIGINT); +#endif + BigInt *result = subBigInt(left.val.bigint, right.val.bigint); + return bigIntValue(result); +} + +static Value littleSub(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_STDINT); + ASSERT(right.type == VALUE_TYPE_STDINT); +#endif + return intValue(left.val.stdint - right.val.stdint); +} + +static Value bigDivide(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_BIGINT); + ASSERT(right.type == VALUE_TYPE_BIGINT); +#endif + BigInt *result = divBigInt(left.val.bigint, right.val.bigint); + return bigIntValue(result); +} + +static Value littleDivide(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_STDINT); + ASSERT(right.type == VALUE_TYPE_STDINT); +#endif + return intValue(left.val.stdint / right.val.stdint); +} + +static Value bigPower(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_BIGINT); + ASSERT(right.type == VALUE_TYPE_BIGINT); +#endif + BigInt *result = powBigInt(left.val.bigint, right.val.bigint); + return bigIntValue(result); +} + +static Value littlePower(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_STDINT); + ASSERT(right.type == VALUE_TYPE_STDINT); +#endif + return intValue(pow(left.val.stdint, right.val.stdint)); +} + +static Value bigModulo(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_BIGINT); + ASSERT(right.type == VALUE_TYPE_BIGINT); +#endif + BigInt *result = modBigInt(left.val.bigint, right.val.bigint); + return bigIntValue(result); +} + +static Value littleModulo(Value left, Value right) { +#ifdef SAFETY_CHECKS + ASSERT(left.type == VALUE_TYPE_STDINT); + ASSERT(right.type == VALUE_TYPE_STDINT); +#endif + return intValue(left.val.stdint % right.val.stdint); +} + + +void init_arithmetic() { + if (bigint_flag) { + add = bigAdd; + mul = bigMul; + sub = bigSub; + divide = bigDivide; + power = bigPower; + modulo = bigModulo; + } else { + add = littleAdd; + mul = littleMul; + sub = littleSub; + divide = littleDivide; + power = littlePower; + modulo = littleModulo; + } +} diff --git a/src/arithmetic.h b/src/arithmetic.h new file mode 100644 index 0000000..7506864 --- /dev/null +++ b/src/arithmetic.h @@ -0,0 +1,34 @@ +#ifndef cekf_arithmetic_h +#define cekf_arithmetic_h +/* + * CEKF - VM supporting amb + * Copyright (C) 2022-2023 Bill Hails + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "value.h" + +typedef Value (*IntegerBinOp)(Value, Value); + +extern IntegerBinOp add; +extern IntegerBinOp sub; +extern IntegerBinOp mul; +extern IntegerBinOp divide; +extern IntegerBinOp power; +extern IntegerBinOp modulo; + +void init_arithmetic(void); + +#endif diff --git a/src/common.h b/src/common.h index 9f0372b..04e9017 100644 --- a/src/common.h +++ b/src/common.h @@ -73,4 +73,10 @@ bool hadErrors(void); # define PAD_WIDTH 2 +#define ASSERT(assertion) do {\ + if (!(assertion)) { \ + cant_happen("assertion failed " #assertion); \ + } \ +} while (0); + #endif diff --git a/src/step.c b/src/step.c index 782ef53..1a5e96c 100644 --- a/src/step.c +++ b/src/step.c @@ -23,13 +23,13 @@ #include #include #include -#include #include "common.h" #include "debug.h" #include "cekf.h" #include "step.h" #include "hash.h" +#include "arithmetic.h" int dump_bytecode_flag = 0; @@ -131,141 +131,11 @@ static inline int readCurrentOffsetAt(int i) { return readOffsetAt(&state.B, state.C, i); } -static Value intValue(int i) { - Value value; - value.type = VALUE_TYPE_STDINT; - value.val = VALUE_VAL_STDINT(i); - return value; -} - -static Value bigIntValue(BigInt *i) { - Value value; - value.type = VALUE_TYPE_BIGINT; - value.val = VALUE_VAL_BIGINT(i); - return value; -} - static bool truthy(Value v) { return !((v.type == VALUE_TYPE_STDINT && v.val.stdint == 0) || v.type == VALUE_TYPE_VOID); } -typedef Value (*IntegerBinOp)(Value, Value); - -static IntegerBinOp add; - -static Value bigAdd(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_BIGINT); - assert(right.type == VALUE_TYPE_BIGINT); -#endif - BigInt *result = addBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); -} - -static Value littleAdd(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_STDINT); - assert(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint + right.val.stdint); -} - -static IntegerBinOp mul; - -static Value bigMul(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_BIGINT); - assert(right.type == VALUE_TYPE_BIGINT); -#endif - BigInt *result = mulBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); -} - -static Value littleMul(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_STDINT); - assert(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint * right.val.stdint); -} - -static IntegerBinOp sub; - -static Value bigSub(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_BIGINT); - assert(right.type == VALUE_TYPE_BIGINT); -#endif - BigInt *result = subBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); -} - -static Value littleSub(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_STDINT); - assert(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint - right.val.stdint); -} - -static IntegerBinOp divide; - -static Value bigDivide(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_BIGINT); - assert(right.type == VALUE_TYPE_BIGINT); -#endif - BigInt *result = divBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); -} - -static Value littleDivide(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_STDINT); - assert(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint / right.val.stdint); -} - -static IntegerBinOp power; - -static Value bigPower(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_BIGINT); - assert(right.type == VALUE_TYPE_BIGINT); -#endif - BigInt *result = powBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); -} - -static Value littlePower(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_STDINT); - assert(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(pow(left.val.stdint, right.val.stdint)); -} - -static IntegerBinOp modulo; - -static Value bigModulo(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_BIGINT); - assert(right.type == VALUE_TYPE_BIGINT); -#endif - BigInt *result = modBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); -} - -static Value littleModulo(Value left, Value right) { -#ifdef SAFETY_CHECKS - assert(left.type == VALUE_TYPE_STDINT); - assert(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint % right.val.stdint); -} - static int _cmp(Value left, Value right); static int _vecCmp(Vec *left, Vec *right) { @@ -532,21 +402,7 @@ void reportSteps(void) { static void step() { if (dump_bytecode_flag) dumpByteCode(&state.B); - if (bigint_flag) { - add = bigAdd; - mul = bigMul; - sub = bigSub; - divide = bigDivide; - power = bigPower; - modulo = bigModulo; - } else { - add = littleAdd; - mul = littleMul; - sub = littleSub; - divide = littleDivide; - power = littlePower; - modulo = littleModulo; - } + init_arithmetic(); state.C = 0; while (state.C != UINT64_MAX) { ++count; From 1c0026e404057218e795755cff6196a8270d2b5e Mon Sep 17 00:00:00 2001 From: Bill Hails Date: Wed, 3 Apr 2024 19:33:23 +0100 Subject: [PATCH 2/4] rationals work for + - * / but % is probably incorrect --- fn/rational.fn | 7 + src/arithmetic.c | 419 ++++++++++++++++++++++++++++++++++++++--------- src/arithmetic.h | 6 + src/bigint.c | 11 ++ src/bigint.h | 2 + src/cekf.c | 22 ++- src/cekf.h | 2 + src/common.h | 3 + src/debug.c | 1 + src/main.c | 3 + src/memory.c | 2 + src/step.c | 34 ++-- src/value.h | 3 + 13 files changed, 414 insertions(+), 101 deletions(-) create mode 100644 fn/rational.fn diff --git a/fn/rational.fn b/fn/rational.fn new file mode 100644 index 0000000..4076c24 --- /dev/null +++ b/fn/rational.fn @@ -0,0 +1,7 @@ +print((2 + 2)/6); +print((2 / 3) + 4); +print(4 + (2 / 3) + 4); +print((2 / 3) + (4 / 5)); +print((2 / 3) * (4 / 5)); +print((2 / 3) / (4 / 5)); +print((2 / 3) % (4 / 5)) diff --git a/src/arithmetic.c b/src/arithmetic.c index 87ec169..d06a0ff 100644 --- a/src/arithmetic.c +++ b/src/arithmetic.c @@ -21,6 +21,19 @@ #include "common.h" #include "bigint.h" #include "arithmetic.h" +#include "cekf.h" +#include "debug.h" + +#ifdef DEBUG_ARITHMETIC +# include "debugging_on.h" +#else +# include "debugging_off.h" +#endif + +#define NUMERATOR 0 +#define DENOMINATOR 1 + +int rational_flag = 0; IntegerBinOp add; IntegerBinOp sub; @@ -29,6 +42,47 @@ IntegerBinOp divide; IntegerBinOp power; IntegerBinOp modulo; +static IntegerBinOp int_add; +static IntegerBinOp int_sub; +static IntegerBinOp int_mul; +static IntegerBinOp int_divide; +static IntegerBinOp int_power; +static IntegerBinOp int_modulo; + +static IntegerBinOp int_gcd; +static CmpBinOp int_cmp; + +static Value One = { + .type = VALUE_TYPE_BIGINT, + .val = VALUE_VAL_BIGINT(NULL) +}; + +#ifdef DEBUG_ARITHMETIC +static void ppNumber(Value number) { + switch (number.type) { + case VALUE_TYPE_STDINT: + eprintf("%d", number.val.stdint); + break; + case VALUE_TYPE_BIGINT: + eprintf("["); + fprintBigInt(errout, number.val.bigint); + eprintf("]"); + break; + case VALUE_TYPE_RATIONAL: + ppNumber(number.val.vec->values[0]); + eprintf("/"); + ppNumber(number.val.vec->values[1]); + break; + default: + eprintf("??? %d ???", number.type); + } +} +#endif + +//////////////////////////////// +// fixed size integer operations +//////////////////////////////// + static Value intValue(int i) { Value value; value.type = VALUE_TYPE_STDINT; @@ -36,6 +90,94 @@ static Value intValue(int i) { return value; } +#ifdef SAFETY_CHECKS +# define ASSERT_STDINT(x) ASSERT((x).type == VALUE_TYPE_STDINT) +#else +# define ASSERT_STDINT(x) +#endif + +static Value littleAdd(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return intValue(left.val.stdint + right.val.stdint); +} + +static Value littleMul(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return intValue(left.val.stdint * right.val.stdint); +} + +static Value littleSub(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return intValue(left.val.stdint - right.val.stdint); +} + +static Value littleDivide(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return intValue(left.val.stdint / right.val.stdint); +} + +static Value littlePower(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return intValue(pow(left.val.stdint, right.val.stdint)); +} + +static Value littleModulo(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return intValue(left.val.stdint % right.val.stdint); +} + +static int gcd (int a, int b) { + int i = 0, min_num = a, gcd = 1; + if (a > b) { + min_num = b; + } + for (i = 1; i <= min_num; i++) { + if (a % i == 0 && b % i == 0) { + gcd = i; + } + } + return gcd; +} + +static Value littleGcd(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return intValue(gcd(left.val.stdint, right.val.stdint)); +} + +static int littleCmp(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return left.val.stdint < right.val.stdint ? -1 : left.val.stdint == right.val.stdint ? 0 : 1; +} + +static Value makeRational(Value numerator, Value denominator) { + Vec *vec = newVec(2); + vec->values[NUMERATOR] = numerator; + vec->values[DENOMINATOR] = denominator; + Value res = { + .type = VALUE_TYPE_RATIONAL, + .val = VALUE_VAL_RATIONAL(vec) + }; + return res; +} + +//////////////////////////////////// +// arbitrary size integer operations +//////////////////////////////////// + +#ifdef SAFETY_CHECKS +# define ASSERT_BIGINT(x) ASSERT((x).type == VALUE_TYPE_STDINT) +#else +# define ASSERT_BIGINT(x) +#endif + static Value bigIntValue(BigInt *i) { Value value; value.type = VALUE_TYPE_BIGINT; @@ -44,122 +186,239 @@ static Value bigIntValue(BigInt *i) { } static Value bigAdd(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_BIGINT); - ASSERT(right.type == VALUE_TYPE_BIGINT); -#endif + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); BigInt *result = addBigInt(left.val.bigint, right.val.bigint); return bigIntValue(result); } -static Value littleAdd(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_STDINT); - ASSERT(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint + right.val.stdint); -} - static Value bigMul(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_BIGINT); - ASSERT(right.type == VALUE_TYPE_BIGINT); -#endif + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); BigInt *result = mulBigInt(left.val.bigint, right.val.bigint); return bigIntValue(result); } -static Value littleMul(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_STDINT); - ASSERT(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint * right.val.stdint); -} - static Value bigSub(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_BIGINT); - ASSERT(right.type == VALUE_TYPE_BIGINT); -#endif + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); BigInt *result = subBigInt(left.val.bigint, right.val.bigint); return bigIntValue(result); } -static Value littleSub(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_STDINT); - ASSERT(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint - right.val.stdint); -} - static Value bigDivide(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_BIGINT); - ASSERT(right.type == VALUE_TYPE_BIGINT); -#endif + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); BigInt *result = divBigInt(left.val.bigint, right.val.bigint); return bigIntValue(result); } -static Value littleDivide(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_STDINT); - ASSERT(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(left.val.stdint / right.val.stdint); -} - static Value bigPower(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_BIGINT); - ASSERT(right.type == VALUE_TYPE_BIGINT); -#endif + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); BigInt *result = powBigInt(left.val.bigint, right.val.bigint); return bigIntValue(result); } -static Value littlePower(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_STDINT); - ASSERT(right.type == VALUE_TYPE_STDINT); -#endif - return intValue(pow(left.val.stdint, right.val.stdint)); -} - static Value bigModulo(Value left, Value right) { -#ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_BIGINT); - ASSERT(right.type == VALUE_TYPE_BIGINT); -#endif + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); BigInt *result = modBigInt(left.val.bigint, right.val.bigint); return bigIntValue(result); } -static Value littleModulo(Value left, Value right) { +static Value bigGcd(Value left, Value right) { + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); + BigInt *gcd = gcdBigInt(left.val.bigint, right.val.bigint); + return bigIntValue(gcd); +} + +static int bigCmp(Value left, Value right) { + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); + return cmpBigInt(left.val.bigint, right.val.bigint); +} + +//////////////////////////////////// +// arbitrary size integer operations +//////////////////////////////////// + #ifdef SAFETY_CHECKS - ASSERT(left.type == VALUE_TYPE_STDINT); - ASSERT(right.type == VALUE_TYPE_STDINT); +# define ASSERT_RATIONAL(x) ASSERT((x).type == VALUE_TYPE_RATIONAL) +#else +# define ASSERT_RATIONAL(x) #endif - return intValue(left.val.stdint % right.val.stdint); + +static Value ratOp(Value left, Value right, ParameterizedBinOp op, IntegerBinOp intOp, bool simplify) { + ENTER(ratOp); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); + Value res; + int save = protectValue(left); + protectValue(right); + if (left.type == VALUE_TYPE_RATIONAL) { + if (right.type == VALUE_TYPE_RATIONAL) { + // both rational + res = op(intOp, left, right); + } else { + // only left rational + right = makeRational(right, One); + protectValue(right); + res = ratOp(left, right, op, intOp, false); + } + } else if (right.type == VALUE_TYPE_RATIONAL) { + // only right rational + left = makeRational(left, One); + protectValue(left); + res = ratOp(left, right, op, intOp, false); + } else { + // neither rational + if (simplify) { + res = intOp(left, right); + } else { + left = makeRational(left, One); + protectValue(left); + right = makeRational(right, One); + protectValue(right); + res = ratOp(left, right, op, intOp, false); + } + } + LEAVE(ratOp); + UNPROTECT(save); + return res; } +static Value ratSimplify(Value numerator, Value denominator) { + Value gcd = int_gcd(numerator, denominator); + int save = protectValue(gcd); + Value numerator2 = int_divide(numerator, gcd); + protectValue(numerator2); + if (int_cmp(gcd, denominator) == 0) { + UNPROTECT(save); + return numerator2; + } else { + Value denominator2 = int_divide(denominator, gcd); + protectValue(denominator2); + Value res = makeRational(numerator2, denominator2); + UNPROTECT(save); + return res; + } +} + +static Value _rat_add_sub(IntegerBinOp base_op, Value left, Value right) { + ASSERT_RATIONAL(left); + ASSERT_RATIONAL(right); + Value a1b2 = + int_mul(left.val.vec->values[NUMERATOR], + right.val.vec->values[DENOMINATOR]); + int save = protectValue(a1b2); + Value a2b1 = + int_mul(left.val.vec->values[DENOMINATOR], + right.val.vec->values[NUMERATOR]); + protectValue(a2b1); + Value numerator = base_op(a1b2, a2b1); + protectValue(numerator); + Value denominator = + int_mul(left.val.vec->values[DENOMINATOR], + right.val.vec->values[DENOMINATOR]); + protectValue(denominator); + Value res = ratSimplify(numerator, denominator); + UNPROTECT(save); + return res; +} + +static Value ratAdd(Value left, Value right) { + return ratOp(left, right, _rat_add_sub, int_add, true); +} + +static Value ratSub(Value left, Value right) { + return ratOp(left, right, _rat_add_sub, int_sub, true); +} + +static Value _rat_mul(IntegerBinOp base_op, Value left, Value right) { + ASSERT_RATIONAL(left); + ASSERT_RATIONAL(right); + Value numerator = + base_op(left.val.vec->values[NUMERATOR], + right.val.vec->values[NUMERATOR]); + int save = protectValue(numerator); + Value denominator = + base_op(left.val.vec->values[DENOMINATOR], + right.val.vec->values[DENOMINATOR]); + protectValue(denominator); + Value res = ratSimplify(numerator, denominator); + UNPROTECT(save); + return res; +} + +static Value ratMul(Value left, Value right) { + return ratOp(left, right, _rat_mul, int_mul, true); +} + +static Value _rat_div(IntegerBinOp base_op, Value left, Value right) { + ASSERT_RATIONAL(left); + ASSERT_RATIONAL(right); + Value newRight = makeRational(right.val.vec->values[DENOMINATOR], right.val.vec->values[NUMERATOR]); + int save = protectValue(newRight); + Value res = _rat_mul(base_op, left, newRight); + UNPROTECT(save); + return res; +} + +static Value ratDivide(Value left, Value right) { + ENTER(ratDivide); + // N.B. int_mul not int_div + Value res = ratOp(left, right, _rat_div, int_mul, false); + LEAVE(ratDivide); + IFDEBUG(ppNumber(res)); + return res; +} + +static Value ratModulo(Value left, Value right) { + return ratOp(left, right, _rat_add_sub, int_modulo, true); +} void init_arithmetic() { if (bigint_flag) { - add = bigAdd; - mul = bigMul; - sub = bigSub; - divide = bigDivide; - power = bigPower; - modulo = bigModulo; + int_add = bigAdd; + int_mul = bigMul; + int_sub = bigSub; + int_divide = bigDivide; + int_power = bigPower; + int_modulo = bigModulo; + int_gcd = bigGcd; + int_cmp = bigCmp; + BigInt *one = bigIntFromInt(1); + One.val = VALUE_VAL_BIGINT(one); } else { - add = littleAdd; - mul = littleMul; - sub = littleSub; - divide = littleDivide; - power = littlePower; - modulo = littleModulo; + int_add = littleAdd; + int_mul = littleMul; + int_sub = littleSub; + int_divide = littleDivide; + int_power = littlePower; + int_modulo = littleModulo; + int_gcd = littleGcd; + int_cmp = littleCmp; + One.type = VALUE_TYPE_STDINT; + One.val = VALUE_VAL_STDINT(1); } + if (rational_flag) { + add = ratAdd; + sub = ratSub; + mul = ratMul; + divide = ratDivide; + modulo = ratModulo; + } else { + add = int_add; + mul = int_mul; + sub = int_sub; + divide = int_divide; + power = int_power; + modulo = int_modulo; + } +} + +void markArithmetic() { + markValue(One); } diff --git a/src/arithmetic.h b/src/arithmetic.h index 7506864..fe8a2f8 100644 --- a/src/arithmetic.h +++ b/src/arithmetic.h @@ -20,7 +20,12 @@ #include "value.h" +typedef int (*CmpBinOp)(Value, Value); typedef Value (*IntegerBinOp)(Value, Value); +typedef Value (*IntegerUnOp)(Value); +typedef Value (*ParameterizedBinOp)(IntegerBinOp, Value, Value); + +extern int rational_flag; extern IntegerBinOp add; extern IntegerBinOp sub; @@ -30,5 +35,6 @@ extern IntegerBinOp power; extern IntegerBinOp modulo; void init_arithmetic(void); +void markArithmetic(void); #endif diff --git a/src/bigint.c b/src/bigint.c index 1b64c59..5e6e77c 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -1288,6 +1288,13 @@ BigInt *fakeBigInt(int little) { return x; } +BigInt *bigIntFromInt(int i) { + bigint c; + bigint_init(&c); + bigint_from_int(&c, i); + return newBigInt(c); +} + void markBigInt(BigInt *x) { if (x == NULL) return; @@ -1380,6 +1387,10 @@ BigInt *modBigInt(BigInt *a, BigInt *b) { return _opBigInt(bigint_mod, a, b); } +BigInt *gcdBigInt(BigInt *a, BigInt *b) { + return _opBigInt(bigint_gcd, a, b); +} + BigInt *powBigInt(BigInt *a, BigInt *b) { int save = PROTECT(a); PROTECT(b); diff --git a/src/bigint.h b/src/bigint.h index e36483a..f63d037 100644 --- a/src/bigint.h +++ b/src/bigint.h @@ -50,6 +50,7 @@ extern "C" { BigInt *newBigInt(bigint bi); BigInt *fakeBigInt(int little); + BigInt *bigIntFromInt(int c); void markBigInt(BigInt *bi); void freeBigInt(BigInt *bi); void printBigInt(BigInt *bi, int depth); @@ -65,6 +66,7 @@ extern "C" { BigInt *divBigInt(BigInt *a, BigInt *b); BigInt *modBigInt(BigInt *a, BigInt *b); BigInt *powBigInt(BigInt *a, BigInt *b); + BigInt *gcdBigInt(BigInt *a, BigInt *b); void bigint_fprint(FILE *f, bigint * bi); // END CEKF additions diff --git a/src/cekf.c b/src/cekf.c index cbb1dfb..551fe38 100644 --- a/src/cekf.c +++ b/src/cekf.c @@ -132,6 +132,7 @@ void markValue(Value x) { case VALUE_TYPE_CONT: markKont(x.val.kont); break; + case VALUE_TYPE_RATIONAL: case VALUE_TYPE_VEC: markVec(x.val.vec); break; @@ -217,6 +218,9 @@ void markFail(Fail *x) { void markCekfObj(Header *h) { switch (h->type) { + case OBJTYPE_VEC: + markVec((Vec *) h); + break; case OBJTYPE_CLO: markClo((Clo *) h); break; @@ -233,7 +237,7 @@ void markCekfObj(Header *h) { markValueList((ValueList *) h); break; default: - cant_happen("unrecognised header type in markCekfObj"); + cant_happen("unrecognised header type %d in markCekfObj", h->type); } } @@ -276,3 +280,19 @@ void freeCekfObj(Header *h) { cant_happen("unrecognised header type in freeCekfObj"); } } + +int protectValue(Value v) { + switch (v.type) { + case VALUE_TYPE_CLO: + return PROTECT(v.val.clo); + case VALUE_TYPE_CONT: + return PROTECT(v.val.kont); + case VALUE_TYPE_VEC: + case VALUE_TYPE_RATIONAL: + return PROTECT(v.val.vec); + case VALUE_TYPE_BIGINT: + return PROTECT(v.val.bigint); + default: + return PROTECT(NULL); + } +} diff --git a/src/cekf.h b/src/cekf.h index be26193..44ea116 100644 --- a/src/cekf.h +++ b/src/cekf.h @@ -97,6 +97,8 @@ typedef struct Vec { struct Value values[0]; } Vec; +int protectValue(Value v); + void snapshotClo(Stack *stack, struct Clo *target, int letRecOffset); void patchClo(Stack *stack, struct Clo *target); void snapshotKont(Stack *stack, struct Kont *target); diff --git a/src/common.h b/src/common.h index 04e9017..83966c7 100644 --- a/src/common.h +++ b/src/common.h @@ -54,6 +54,7 @@ // # define DEBUG_ALLOC // # define DEBUG_PRINT_GENERATOR // # define DEBUG_PRINT_COMPILER +// # define DEBUG_ARITHMETIC // define this to turn on additional safety checks for things that shouldn't but just possibly might happen # define SAFETY_CHECKS # endif @@ -61,7 +62,9 @@ # ifndef __GNUC__ # define __attribute__(x) # endif + # define errout stdout + void _cant_happen(char *file, int line, const char *message, ...) __attribute__((noreturn, format(printf, 3, 4))); void can_happen(const char *message, ...) diff --git a/src/debug.c b/src/debug.c index 713ef83..cbc8141 100644 --- a/src/debug.c +++ b/src/debug.c @@ -74,6 +74,7 @@ void printContainedValue(Value x, int depth) { case VALUE_TYPE_CONT: printKont(x.val.kont, depth); break; + case VALUE_TYPE_RATIONAL: case VALUE_TYPE_VEC: printPad(depth); printVec(x.val.vec); diff --git a/src/main.c b/src/main.c index f1a4024..bce70ef 100644 --- a/src/main.c +++ b/src/main.c @@ -43,6 +43,7 @@ #include "tc_analyze.h" #include "tc_debug.h" #include "tpmc_mermaid.h" +#include "arithmetic.h" int report_flag = 0; static int help_flag = 0; @@ -54,6 +55,7 @@ static void processArgs(int argc, char *argv[]) { while (1) { static struct option long_options[] = { { "bigint", no_argument, &bigint_flag, 1 }, + { "rational", no_argument, &rational_flag, 1 }, { "report", no_argument, &report_flag, 1 }, { "anf", no_argument, &anf_flag, 1 }, { "dump-bytecode", no_argument, &dump_bytecode_flag, 1 }, @@ -81,6 +83,7 @@ static void processArgs(int argc, char *argv[]) { if (help_flag) { printf("%s", "--bigint use arbitrary precision integers\n" + "--rational use precision-preserving rational numbers\n" "--report report statistics\n" "--anf display the generated ANF\n" "--lambda=function display the intermediate code\n" diff --git a/src/memory.c b/src/memory.c index af4292d..7608f2c 100644 --- a/src/memory.c +++ b/src/memory.c @@ -31,6 +31,7 @@ #include "cekf.h" #include "module.h" #include "symbol.h" +#include "arithmetic.h" static int bytesAllocated = 0; static int nextGC = 0; @@ -371,6 +372,7 @@ static void markProtected() { static void mark() { markCEKF(); markProtected(); + markArithmetic(); #ifdef DEBUG_LOG_GC eprintf("starting markVarTable\n"); #endif diff --git a/src/step.c b/src/step.c index 1a5e96c..3db72b3 100644 --- a/src/step.c +++ b/src/step.c @@ -45,7 +45,6 @@ int dump_bytecode_flag = 0; static void step(); static Value lookup(int frame, int offset); -static int protectValue(Value v); void putValue(Value x); static CEKF state; @@ -284,21 +283,6 @@ static Value lookup(int frame, int offset) { return env->values[offset]; } -static int protectValue(Value v) { - switch (v.type) { - case VALUE_TYPE_CLO: - return PROTECT(v.val.clo); - case VALUE_TYPE_CONT: - return PROTECT(v.val.kont); - case VALUE_TYPE_VEC: - return PROTECT(v.val.vec); - case VALUE_TYPE_BIGINT: - return PROTECT(v.val.bigint); - default: - return PROTECT(NULL); - } -} - /** * on reaching this point, the stack will contain a number * of arguments, and the callable on top. @@ -472,10 +456,20 @@ static void step() { // peek value, print it DEBUGPRINTF("PUTN\n"); Value b = tos(); - if (b.type == VALUE_TYPE_BIGINT) { - fprintBigInt(stdout, b.val.bigint); - } else { - printf("%d", b.val.stdint); + switch (b.type) { + case VALUE_TYPE_BIGINT: + fprintBigInt(stdout, b.val.bigint); + break; + case VALUE_TYPE_STDINT: + printf("%d", b.val.stdint); + break; + case VALUE_TYPE_RATIONAL: + putValue(b.val.vec->values[0]); + printf("/"); + putValue(b.val.vec->values[1]); + break; + default: + cant_happen("unrecognised type %d", b.type); } } break; diff --git a/src/value.h b/src/value.h index 731eb2e..83adb4c 100644 --- a/src/value.h +++ b/src/value.h @@ -24,6 +24,7 @@ typedef enum { VALUE_TYPE_VOID, VALUE_TYPE_STDINT, VALUE_TYPE_BIGINT, + VALUE_TYPE_RATIONAL, VALUE_TYPE_CHARACTER, VALUE_TYPE_CLO, VALUE_TYPE_PCLO, @@ -53,7 +54,9 @@ typedef struct Value { # define VALUE_VAL_CLO(x) ((ValueVal){.clo = (x)}) # define VALUE_VAL_PCLO(x) ((ValueVal){.clo = (x)}) # define VALUE_VAL_CONT(x) ((ValueVal){.kont = (x)}) +// RATIONAL and VEC share the same Vec struct # define VALUE_VAL_VEC(x) ((ValueVal){.vec = (x)}) +# define VALUE_VAL_RATIONAL(x) ((ValueVal){.vec = (x)}) # define VALUE_VAL_NONE() ((ValueVal){.none = NULL}) // constants From b5cbf200d7807d0a845ebccb7680442c54cb08ca Mon Sep 17 00:00:00 2001 From: Bill Hails Date: Thu, 4 Apr 2024 19:19:03 +0100 Subject: [PATCH 3/4] rational ops including `%` but not `**` all work --- fn/rational.fn | 21 +++-- fn/test2.fn | 2 + src/arithmetic.c | 217 +++++++++++++++++++++++++++++++++++++---------- src/arithmetic.h | 2 + src/bigint.c | 16 ++++ src/bigint.h | 2 + src/step.c | 44 ++++++++++ 7 files changed, 251 insertions(+), 53 deletions(-) create mode 100644 fn/test2.fn diff --git a/fn/rational.fn b/fn/rational.fn index 4076c24..fbb09ea 100644 --- a/fn/rational.fn +++ b/fn/rational.fn @@ -1,7 +1,14 @@ -print((2 + 2)/6); -print((2 / 3) + 4); -print(4 + (2 / 3) + 4); -print((2 / 3) + (4 / 5)); -print((2 / 3) * (4 / 5)); -print((2 / 3) / (4 / 5)); -print((2 / 3) % (4 / 5)) +print(#( "1 * 2", 1 * 2 )); +print(#( "1 + 2", 1 + 2 )); +print(#( "2 / 3 + 4", 2 / 3 + 4 )); +print(#( "4 + 2 / 3 + 4", 4 + 2 / 3 + 4 )); +print(#( "4 + 2 / -3 + 4", 4 + 2 / -3 + 4 )); +print(#( "2 / 3 + 4 / 5", 2 / 3 + 4 / 5 )); +print(#( "(2 / 3) * (4 / 5)", (2 / 3) * (4 / 5) )); +print(#( "(2 / 3) / (4 / 5)", (2 / 3) / (4 / 5) )); +print(#( "(2 / 3) % (4 / 5)", (2 / 3) % (4 / 5) )); +print(#( "1914882942 ** 10", 1914882942 ** 10 )); +print(#( "1/3 % 8", 1/3 % 8 )); +print(#( "-9 % 8", -9 % 8 )); +print(#( "(1914882942 ** 5 / 5) % (2 / 3)", (1914882942 ** 5 / 5) % (2 / 3) )) + diff --git a/fn/test2.fn b/fn/test2.fn new file mode 100644 index 0000000..ea945ff --- /dev/null +++ b/fn/test2.fn @@ -0,0 +1,2 @@ +print((2 / 3) + 4) + diff --git a/src/arithmetic.c b/src/arithmetic.c index d06a0ff..c862a05 100644 --- a/src/arithmetic.c +++ b/src/arithmetic.c @@ -51,10 +51,12 @@ static IntegerBinOp int_modulo; static IntegerBinOp int_gcd; static CmpBinOp int_cmp; +static voidOp int_neg; +static boolOp int_isneg; static Value One = { - .type = VALUE_TYPE_BIGINT, - .val = VALUE_VAL_BIGINT(NULL) + .type = VALUE_TYPE_STDINT, + .val = VALUE_VAL_STDINT(1) }; #ifdef DEBUG_ARITHMETIC @@ -84,10 +86,10 @@ static void ppNumber(Value number) { //////////////////////////////// static Value intValue(int i) { - Value value; - value.type = VALUE_TYPE_STDINT; - value.val = VALUE_VAL_STDINT(i); - return value; + Value val; + val.type = VALUE_TYPE_STDINT; + val.val = VALUE_VAL_STDINT(i); + return val; } #ifdef SAFETY_CHECKS @@ -157,15 +159,14 @@ static int littleCmp(Value left, Value right) { return left.val.stdint < right.val.stdint ? -1 : left.val.stdint == right.val.stdint ? 0 : 1; } -static Value makeRational(Value numerator, Value denominator) { - Vec *vec = newVec(2); - vec->values[NUMERATOR] = numerator; - vec->values[DENOMINATOR] = denominator; - Value res = { - .type = VALUE_TYPE_RATIONAL, - .val = VALUE_VAL_RATIONAL(vec) - }; - return res; +static void littleNeg(Value v) { + ASSERT_STDINT(v); + v.val.stdint = -v.val.stdint; +} + +static bool littleIsNeg(Value v) { + ASSERT_STDINT(v); + return v.val.stdint < 0; } //////////////////////////////////// @@ -173,76 +174,131 @@ static Value makeRational(Value numerator, Value denominator) { //////////////////////////////////// #ifdef SAFETY_CHECKS -# define ASSERT_BIGINT(x) ASSERT((x).type == VALUE_TYPE_STDINT) +# define ASSERT_BIGINT(x) ASSERT((x).type == VALUE_TYPE_BIGINT) #else # define ASSERT_BIGINT(x) #endif static Value bigIntValue(BigInt *i) { - Value value; - value.type = VALUE_TYPE_BIGINT; - value.val = VALUE_VAL_BIGINT(i); - return value; + Value val; + val.type = VALUE_TYPE_BIGINT; + val.val = VALUE_VAL_BIGINT(i); + return val; } static Value bigAdd(Value left, Value right) { + ENTER(bigAdd); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); ASSERT_BIGINT(left); ASSERT_BIGINT(right); BigInt *result = addBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); + int save = PROTECT(result); + Value res = bigIntValue(result); + LEAVE(bigAdd); + UNPROTECT(save); + return res; } static Value bigMul(Value left, Value right) { + ENTER(bigMul); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); ASSERT_BIGINT(left); ASSERT_BIGINT(right); BigInt *result = mulBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); + int save = PROTECT(result); + Value res = bigIntValue(result); + LEAVE(bigMul); + UNPROTECT(save); + return res; } static Value bigSub(Value left, Value right) { + ENTER(bigSub); ASSERT_BIGINT(left); ASSERT_BIGINT(right); BigInt *result = subBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); + int save = PROTECT(result); + Value res = bigIntValue(result); + LEAVE(bigSub); + UNPROTECT(save); + return res; } static Value bigDivide(Value left, Value right) { + ENTER(bigDivide); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); ASSERT_BIGINT(left); ASSERT_BIGINT(right); BigInt *result = divBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); + int save = PROTECT(result); + Value res = bigIntValue(result); + protectValue(res); + LEAVE(bigDivide); + IFDEBUG(ppNumber(res)); + UNPROTECT(save); + return res; } static Value bigPower(Value left, Value right) { + ENTER(bigPower); ASSERT_BIGINT(left); ASSERT_BIGINT(right); BigInt *result = powBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); + int save = PROTECT(result); + Value res = bigIntValue(result); + LEAVE(bigPower); + UNPROTECT(save); + return res; } static Value bigModulo(Value left, Value right) { + ENTER(bigModulo); ASSERT_BIGINT(left); ASSERT_BIGINT(right); BigInt *result = modBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(result); + int save = PROTECT(result); + Value res = bigIntValue(result); + LEAVE(bigModulo); + UNPROTECT(save); + return res; } static Value bigGcd(Value left, Value right) { + ENTER(bigGcd); ASSERT_BIGINT(left); ASSERT_BIGINT(right); BigInt *gcd = gcdBigInt(left.val.bigint, right.val.bigint); - return bigIntValue(gcd); + int save = PROTECT(gcd); + Value res = bigIntValue(gcd); + LEAVE(bigGcd); + UNPROTECT(save); + return res; } static int bigCmp(Value left, Value right) { + ENTER(bigCmp); ASSERT_BIGINT(left); ASSERT_BIGINT(right); + LEAVE(bigCmp); return cmpBigInt(left.val.bigint, right.val.bigint); } -//////////////////////////////////// -// arbitrary size integer operations -//////////////////////////////////// +static void bigNeg(Value v) { + ASSERT_BIGINT(v); + negateBigInt(v.val.bigint); +} + +static bool bigIsNeg(Value v) { + ASSERT_BIGINT(v); + return isNegBigInt(v.val.bigint); +} + +/////////////////////////////////////// +// unspecified size rational operations +/////////////////////////////////////// #ifdef SAFETY_CHECKS # define ASSERT_RATIONAL(x) ASSERT((x).type == VALUE_TYPE_RATIONAL) @@ -250,6 +306,17 @@ static int bigCmp(Value left, Value right) { # define ASSERT_RATIONAL(x) #endif +static Value makeRational(Value numerator, Value denominator) { + Vec *vec = newVec(2); + vec->values[NUMERATOR] = numerator; + vec->values[DENOMINATOR] = denominator; + Value res = { + .type = VALUE_TYPE_RATIONAL, + .val = VALUE_VAL_RATIONAL(vec) + }; + return res; +} + static Value ratOp(Value left, Value right, ParameterizedBinOp op, IntegerBinOp intOp, bool simplify) { ENTER(ratOp); IFDEBUG(ppNumber(left)); @@ -261,52 +328,71 @@ static Value ratOp(Value left, Value right, ParameterizedBinOp op, IntegerBinOp if (right.type == VALUE_TYPE_RATIONAL) { // both rational res = op(intOp, left, right); + protectValue(res); } else { // only left rational right = makeRational(right, One); protectValue(right); res = ratOp(left, right, op, intOp, false); + protectValue(res); } } else if (right.type == VALUE_TYPE_RATIONAL) { // only right rational left = makeRational(left, One); protectValue(left); res = ratOp(left, right, op, intOp, false); + protectValue(res); } else { // neither rational if (simplify) { res = intOp(left, right); + protectValue(res); } else { left = makeRational(left, One); protectValue(left); right = makeRational(right, One); protectValue(right); res = ratOp(left, right, op, intOp, false); + protectValue(res); } } LEAVE(ratOp); + IFDEBUG(ppNumber(res)); UNPROTECT(save); return res; } static Value ratSimplify(Value numerator, Value denominator) { + ENTER(ratSimplify); + IFDEBUG(ppNumber(numerator)); + IFDEBUG(ppNumber(denominator)); Value gcd = int_gcd(numerator, denominator); int save = protectValue(gcd); - Value numerator2 = int_divide(numerator, gcd); - protectValue(numerator2); - if (int_cmp(gcd, denominator) == 0) { - UNPROTECT(save); - return numerator2; + Value res; + if (int_cmp(gcd, One) != 0) { + numerator = int_divide(numerator, gcd); + protectValue(numerator); + denominator = int_divide(denominator, gcd); + protectValue(denominator); + } + if (int_isneg(denominator)) { + int_neg(numerator); + int_neg(denominator); + } + if (int_cmp(denominator, One) == 0) { + res = numerator; } else { - Value denominator2 = int_divide(denominator, gcd); - protectValue(denominator2); - Value res = makeRational(numerator2, denominator2); - UNPROTECT(save); - return res; + res = makeRational(numerator, denominator); + protectValue(res); } + LEAVE(ratSimplify); + IFDEBUG(ppNumber(res)); + UNPROTECT(save); + return res; } static Value _rat_add_sub(IntegerBinOp base_op, Value left, Value right) { + ENTER(rat_add_sub); ASSERT_RATIONAL(left); ASSERT_RATIONAL(right); Value a1b2 = @@ -325,20 +411,32 @@ static Value _rat_add_sub(IntegerBinOp base_op, Value left, Value right) { protectValue(denominator); Value res = ratSimplify(numerator, denominator); UNPROTECT(save); + LEAVE(rat_add_sub); return res; } static Value ratAdd(Value left, Value right) { - return ratOp(left, right, _rat_add_sub, int_add, true); + ENTER(ratAdd); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); + Value res = ratOp(left, right, _rat_add_sub, int_add, true); + LEAVE(ratAdd); + return res; } static Value ratSub(Value left, Value right) { - return ratOp(left, right, _rat_add_sub, int_sub, true); + ENTER(ratSub); + Value res = ratOp(left, right, _rat_add_sub, int_sub, true); + LEAVE(ratSub); + return res; } static Value _rat_mul(IntegerBinOp base_op, Value left, Value right) { + ENTER(_rat_mul); ASSERT_RATIONAL(left); ASSERT_RATIONAL(right); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); Value numerator = base_op(left.val.vec->values[NUMERATOR], right.val.vec->values[NUMERATOR]); @@ -348,20 +446,37 @@ static Value _rat_mul(IntegerBinOp base_op, Value left, Value right) { right.val.vec->values[DENOMINATOR]); protectValue(denominator); Value res = ratSimplify(numerator, denominator); + protectValue(res); + LEAVE(_rat_mul); + IFDEBUG(ppNumber(res)); UNPROTECT(save); return res; } static Value ratMul(Value left, Value right) { - return ratOp(left, right, _rat_mul, int_mul, true); + ENTER(ratMul); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); + Value res = ratOp(left, right, _rat_mul, int_mul, true); + int save = protectValue(res); + LEAVE(ratMul); + IFDEBUG(ppNumber(res)); + UNPROTECT(save); + return res; } static Value _rat_div(IntegerBinOp base_op, Value left, Value right) { + ENTER(_rat_div); ASSERT_RATIONAL(left); ASSERT_RATIONAL(right); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); Value newRight = makeRational(right.val.vec->values[DENOMINATOR], right.val.vec->values[NUMERATOR]); int save = protectValue(newRight); Value res = _rat_mul(base_op, left, newRight); + protectValue(res); + LEAVE(_rat_div); + IFDEBUG(ppNumber(res)); UNPROTECT(save); return res; } @@ -370,13 +485,18 @@ static Value ratDivide(Value left, Value right) { ENTER(ratDivide); // N.B. int_mul not int_div Value res = ratOp(left, right, _rat_div, int_mul, false); + int save = protectValue(res); LEAVE(ratDivide); IFDEBUG(ppNumber(res)); + UNPROTECT(save); return res; } static Value ratModulo(Value left, Value right) { - return ratOp(left, right, _rat_add_sub, int_modulo, true); + ENTER(ratModulo); + Value res = ratOp(left, right, _rat_add_sub, int_modulo, true); + LEAVE(ratModulo); + return res; } void init_arithmetic() { @@ -389,7 +509,10 @@ void init_arithmetic() { int_modulo = bigModulo; int_gcd = bigGcd; int_cmp = bigCmp; + int_neg = bigNeg; + int_isneg = bigIsNeg; BigInt *one = bigIntFromInt(1); + One.type = VALUE_TYPE_BIGINT; One.val = VALUE_VAL_BIGINT(one); } else { int_add = littleAdd; @@ -400,14 +523,16 @@ void init_arithmetic() { int_modulo = littleModulo; int_gcd = littleGcd; int_cmp = littleCmp; - One.type = VALUE_TYPE_STDINT; - One.val = VALUE_VAL_STDINT(1); + int_neg = littleNeg; + int_isneg = littleIsNeg; } + if (rational_flag) { add = ratAdd; sub = ratSub; mul = ratMul; divide = ratDivide; + power = int_power; modulo = ratModulo; } else { add = int_add; diff --git a/src/arithmetic.h b/src/arithmetic.h index fe8a2f8..21249f2 100644 --- a/src/arithmetic.h +++ b/src/arithmetic.h @@ -24,6 +24,8 @@ typedef int (*CmpBinOp)(Value, Value); typedef Value (*IntegerBinOp)(Value, Value); typedef Value (*IntegerUnOp)(Value); typedef Value (*ParameterizedBinOp)(IntegerBinOp, Value, Value); +typedef void (*voidOp)(Value); +typedef bool (*boolOp)(Value); extern int rational_flag; diff --git a/src/bigint.c b/src/bigint.c index 5e6e77c..d0c1db7 100644 --- a/src/bigint.c +++ b/src/bigint.c @@ -1420,3 +1420,19 @@ void dumpBigInt(FILE *fp, BigInt *big) { } fprintf(fp, "\n"); } + +void negateBigInt(BigInt *b) { + if (bigint_flag) { + bigint_negate(&b->bi); + } else { + b->little = -b->little; + } +} + +bool isNegBigInt(BigInt *b) { + if (bigint_flag) { + return b->bi.neg != 0; + } else { + return b->little < 0; + } +} diff --git a/src/bigint.h b/src/bigint.h index f63d037..429dc9b 100644 --- a/src/bigint.h +++ b/src/bigint.h @@ -68,6 +68,8 @@ extern "C" { BigInt *powBigInt(BigInt *a, BigInt *b); BigInt *gcdBigInt(BigInt *a, BigInt *b); void bigint_fprint(FILE *f, bigint * bi); + void negateBigInt(BigInt *b); + bool isNegBigInt(BigInt *b); // END CEKF additions diff --git a/src/step.c b/src/step.c index 3db72b3..c56788a 100644 --- a/src/step.c +++ b/src/step.c @@ -477,119 +477,163 @@ static void step() { // pop two values, perform the binop and push the result DEBUGPRINTF("CMP\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(cmp(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_ADD:{ // pop two values, perform the binop and push the result DEBUGPRINTF("ADD\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(add(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_SUB:{ // pop two values, perform the binop and push the result DEBUGPRINTF("SUB\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(sub(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_MUL:{ // pop two values, perform the binop and push the result DEBUGPRINTF("MUL\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(mul(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_DIV:{ // pop two values, perform the binop and push the result DEBUGPRINTF("DIV\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(divide(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_POW:{ // pop two values, perform the binop and push the result DEBUGPRINTF("POW\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(power(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_MOD:{ // pop two values, perform the binop and push the result DEBUGPRINTF("MOD\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(modulo(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_EQ:{ // pop two values, perform the binop and push the result DEBUGPRINTF("EQ\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(eq(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_NE:{ // pop two values, perform the binop and push the result DEBUGPRINTF("NE\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(ne(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_GT:{ // pop two values, perform the binop and push the result DEBUGPRINTF("GT\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(gt(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_LT:{ // pop two values, perform the binop and push the result DEBUGPRINTF("LT\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(lt(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_GE:{ // pop two values, perform the binop and push the result DEBUGPRINTF("GE\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(ge(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_LE:{ // pop two values, perform the binop and push the result DEBUGPRINTF("LE\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(le(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_XOR:{ // pop two values, perform the binop and push the result DEBUGPRINTF("XOR\n"); Value right = pop(); + int save = protectValue(right); Value left = pop(); + protectValue(left); push(xor(left, right)); + UNPROTECT(save); } break; case BYTECODE_PRIM_NOT:{ // pop value, perform the op and push the result DEBUGPRINTF("NOT\n"); Value a = pop(); + int save = protectValue(a); push(not(a)); + UNPROTECT(save); } break; case BYTECODE_PRIM_VEC:{ From c77f3ce5dd11f2b92ab17c9e7f9dc95c21de33aa Mon Sep 17 00:00:00 2001 From: Bill Hails Date: Fri, 5 Apr 2024 13:07:56 +0100 Subject: [PATCH 4/4] allow rationals to be raised to integer powers, trap divide and mod by zero --- fn/rational.fn | 4 +- src/arithmetic.c | 97 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 16 deletions(-) diff --git a/fn/rational.fn b/fn/rational.fn index fbb09ea..3c1511f 100644 --- a/fn/rational.fn +++ b/fn/rational.fn @@ -5,10 +5,12 @@ print(#( "4 + 2 / 3 + 4", 4 + 2 / 3 + 4 )); print(#( "4 + 2 / -3 + 4", 4 + 2 / -3 + 4 )); print(#( "2 / 3 + 4 / 5", 2 / 3 + 4 / 5 )); print(#( "(2 / 3) * (4 / 5)", (2 / 3) * (4 / 5) )); -print(#( "(2 / 3) / (4 / 5)", (2 / 3) / (4 / 5) )); +print(#( "(6 / 5)", (6 / 5) )); +print(#( "(2 / 3) / (6 / 5)", (2 / 3) / (6 / 5) )); print(#( "(2 / 3) % (4 / 5)", (2 / 3) % (4 / 5) )); print(#( "1914882942 ** 10", 1914882942 ** 10 )); print(#( "1/3 % 8", 1/3 % 8 )); print(#( "-9 % 8", -9 % 8 )); +print(#( "(1/2) ** 2", (1/2) ** 2 )); print(#( "(1914882942 ** 5 / 5) % (2 / 3)", (1914882942 ** 5 / 5) % (2 / 3) )) diff --git a/src/arithmetic.c b/src/arithmetic.c index c862a05..063b561 100644 --- a/src/arithmetic.c +++ b/src/arithmetic.c @@ -59,6 +59,11 @@ static Value One = { .val = VALUE_VAL_STDINT(1) }; +static Value Zero = { + .type = VALUE_TYPE_STDINT, + .val = VALUE_VAL_STDINT(0) +}; + #ifdef DEBUG_ARITHMETIC static void ppNumber(Value number) { switch (number.type) { @@ -98,6 +103,12 @@ static Value intValue(int i) { # define ASSERT_STDINT(x) #endif +static int littleCmp(Value left, Value right) { + ASSERT_STDINT(left); + ASSERT_STDINT(right); + return left.val.stdint < right.val.stdint ? -1 : left.val.stdint == right.val.stdint ? 0 : 1; +} + static Value littleAdd(Value left, Value right) { ASSERT_STDINT(left); ASSERT_STDINT(right); @@ -119,6 +130,9 @@ static Value littleSub(Value left, Value right) { static Value littleDivide(Value left, Value right) { ASSERT_STDINT(left); ASSERT_STDINT(right); + if (littleCmp(right, Zero) == 0) { + cant_happen("attempted div zero"); + } return intValue(left.val.stdint / right.val.stdint); } @@ -131,6 +145,9 @@ static Value littlePower(Value left, Value right) { static Value littleModulo(Value left, Value right) { ASSERT_STDINT(left); ASSERT_STDINT(right); + if (littleCmp(right, Zero) == 0) { + cant_happen("attempted mod zero"); + } return intValue(left.val.stdint % right.val.stdint); } @@ -153,12 +170,6 @@ static Value littleGcd(Value left, Value right) { return intValue(gcd(left.val.stdint, right.val.stdint)); } -static int littleCmp(Value left, Value right) { - ASSERT_STDINT(left); - ASSERT_STDINT(right); - return left.val.stdint < right.val.stdint ? -1 : left.val.stdint == right.val.stdint ? 0 : 1; -} - static void littleNeg(Value v) { ASSERT_STDINT(v); v.val.stdint = -v.val.stdint; @@ -179,6 +190,14 @@ static bool littleIsNeg(Value v) { # define ASSERT_BIGINT(x) #endif +static int bigCmp(Value left, Value right) { + ENTER(bigCmp); + ASSERT_BIGINT(left); + ASSERT_BIGINT(right); + LEAVE(bigCmp); + return cmpBigInt(left.val.bigint, right.val.bigint); +} + static Value bigIntValue(BigInt *i) { Value val; val.type = VALUE_TYPE_BIGINT; @@ -232,6 +251,9 @@ static Value bigDivide(Value left, Value right) { IFDEBUG(ppNumber(right)); ASSERT_BIGINT(left); ASSERT_BIGINT(right); + if (bigCmp(right, Zero) == 0) { + cant_happen("attempted div zero"); + } BigInt *result = divBigInt(left.val.bigint, right.val.bigint); int save = PROTECT(result); Value res = bigIntValue(result); @@ -258,6 +280,9 @@ static Value bigModulo(Value left, Value right) { ENTER(bigModulo); ASSERT_BIGINT(left); ASSERT_BIGINT(right); + if (bigCmp(right, Zero) == 0) { + cant_happen("attempted mod zero"); + } BigInt *result = modBigInt(left.val.bigint, right.val.bigint); int save = PROTECT(result); Value res = bigIntValue(result); @@ -278,14 +303,6 @@ static Value bigGcd(Value left, Value right) { return res; } -static int bigCmp(Value left, Value right) { - ENTER(bigCmp); - ASSERT_BIGINT(left); - ASSERT_BIGINT(right); - LEAVE(bigCmp); - return cmpBigInt(left.val.bigint, right.val.bigint); -} - static void bigNeg(Value v) { ASSERT_BIGINT(v); negateBigInt(v.val.bigint); @@ -499,6 +516,52 @@ static Value ratModulo(Value left, Value right) { return res; } +static Value _ratPower(Value left, Value right) { + ENTER(_ratPower); + ASSERT_RATIONAL(left); + Value numerator = left.val.vec->values[NUMERATOR]; + Value denominator = left.val.vec->values[DENOMINATOR]; + numerator = int_power(numerator, right); + int save = protectValue(numerator); + denominator = int_power(denominator, right); + protectValue(denominator); + Value res = ratSimplify(numerator, denominator); + protectValue(res); + LEAVE(_ratPower); + IFDEBUG(ppNumber(res)); + UNPROTECT(save); + return res; +} + +static Value ratPower(Value left, Value right) { + ENTER(ratPower); + LEAVE(ratPower); + IFDEBUG(ppNumber(left)); + IFDEBUG(ppNumber(right)); + Value res; + int save = protectValue(left); + protectValue(right); + if (left.type == VALUE_TYPE_RATIONAL) { + if (right.type == VALUE_TYPE_RATIONAL) { + cant_happen("raising numbers to a rational power not supported yet"); + } else { + // only left rational + res = _ratPower(left, right); + protectValue(res); + } + } else if (right.type == VALUE_TYPE_RATIONAL) { + cant_happen("raising numbers to a rational power not supported yet"); + } else { + // neither rational + res = int_power(left, right); + protectValue(res); + } + LEAVE(ratPower); + IFDEBUG(ppNumber(res)); + UNPROTECT(save); + return res; +} + void init_arithmetic() { if (bigint_flag) { int_add = bigAdd; @@ -511,6 +574,9 @@ void init_arithmetic() { int_cmp = bigCmp; int_neg = bigNeg; int_isneg = bigIsNeg; + BigInt *zero = bigIntFromInt(0); + Zero.type = VALUE_TYPE_BIGINT; + Zero.val = VALUE_VAL_BIGINT(zero); BigInt *one = bigIntFromInt(1); One.type = VALUE_TYPE_BIGINT; One.val = VALUE_VAL_BIGINT(one); @@ -532,7 +598,7 @@ void init_arithmetic() { sub = ratSub; mul = ratMul; divide = ratDivide; - power = int_power; + power = ratPower; modulo = ratModulo; } else { add = int_add; @@ -545,5 +611,6 @@ void init_arithmetic() { } void markArithmetic() { + markValue(Zero); markValue(One); }