klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit 8b7448513049c48bad659906b321f76376ab192c
parent 641b5ac451f08a28afaf736b5db525b8b538c733
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Fri, 20 May 2011 00:30:25 -0300

Merged inexact branch.

Diffstat:
Msrc/Makefile | 26++++++++++++++------------
Msrc/kenvironment.h | 2+-
Msrc/kgchars.c | 2+-
Msrc/kgnumbers.c | 1690++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/kgnumbers.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/kgpair_mut.c | 8++++----
Msrc/kgpairs_lists.c | 8++++----
Msrc/kground.c | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/kgstrings.c | 14+++++++-------
Msrc/kobject.c | 8+++++++-
Msrc/kobject.h | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Asrc/kreal.c | 743+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/kreal.h | 38++++++++++++++++++++++++++++++++++++++
Msrc/kstate.c | 7+++++++
Msrc/kstate.h | 12++++++++++++
Msrc/ksymbol.c | 1-
Msrc/ktoken.c | 31+++++++++++++++++++++----------
Msrc/kwrite.c | 39++++++++++++++++++++++++++++++++++++---
18 files changed, 2445 insertions(+), 428 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -11,7 +11,7 @@ CORE_O= kobject.o ktoken.o kpair.o kstring.o ksymbol.o kread.o \ kwrite.o kstate.o kmem.o kerror.o kauxlib.o kenvironment.o \ kcontinuation.o koperative.o kapplicative.o keval.o krepl.o \ kencapsulation.o kpromise.o kport.o kinteger.o krational.o \ - ktable.o kgc.o imath.o imrat.o \ + kreal.o ktable.o kgc.o imath.o imrat.o \ kground.o kghelpers.o kgbooleans.o kgeqp.o kgequalp.o \ kgsymbols.o kgcontrol.o kgpairs_lists.o kgpair_mut.o kgenvironments.o \ kgenv_mut.o kgcombiners.o kgcontinuations.o kgencapsulations.o \ @@ -45,21 +45,22 @@ klisp.o: klisp.c klisp.h kobject.h kread.h kwrite.h klimits.h kstate.h kmem.h \ kapplicative.h koperative.h keval.h krepl.h kground.h kobject.o: kobject.c kobject.h klimits.h klispconf.h ktoken.o: ktoken.c ktoken.h kobject.h kstate.h kpair.h kstring.h ksymbol.h \ - kerror.h klisp.h kinteger.h krational.h kport.h + kerror.h klisp.h kinteger.h krational.h kreal.h kport.h kinteger.o: kinteger.c kinteger.h kobject.h kstate.h kmem.h klisp.h imath.h \ kgc.h -krational.o: krational.c krational.h kinteger.h kobject.h kstate.h kmem.h klisp.h \ - imrat.h kgc.h +krational.o: krational.c krational.h kinteger.h kobject.h kstate.h kmem.h \ + klisp.h imrat.h kgc.h +kreal.o: kreal.c kreal.h krational.h kinteger.h kobject.h kstate.h kmem.h \ + klisp.h imrat.h kgc.h kerror.h kpair.h kpair.o: kpair.c kpair.h kobject.h kstate.h kmem.h klisp.h kgc.h kstring.o: kstring.c kstring.h kobject.h kstate.h kmem.h klisp.h kgc.h -# XXX: kpair.h because of use of list as symbol table -ksymbol.o: ksymbol.c ksymbol.h kobject.h kpair.h kstring.h kstate.h kmem.h \ +ksymbol.o: ksymbol.c ksymbol.h kobject.h kstring.h kstate.h kmem.h \ klisp.h kgc.h kread.o: kread.c kread.h kobject.h ktoken.h kpair.h kstate.h kerror.h klisp.h \ kport.h ktable.h klispconf.h kwrite.o: kwrite.c kwrite.h kobject.h kpair.h kstring.h kstate.h kerror.h \ - klisp.h kport.h kinteger.h krational.h ktable.h klispconf.h \ - kenvironment.h + klisp.h kport.h kinteger.h krational.h kreal.h ktable.h klispconf.h \ + kenvironment.h # XXX: now that all dealloc code is in gc, many of these are unnecessary kstate.o: kstate.c kstate.h klisp.h kobject.h kmem.h kstring.h klisp.h \ kenvironment.h kpair.h keval.h koperative.h kground.h \ @@ -103,7 +104,7 @@ kgbooleans.o: kgbooleans.c kgbooleans.c kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kpair.h kcontinuation.h ksymbol.h kgeqp.o: kgeqp.c kgeqp.c kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kpair.h kcontinuation.h kapplicative.h \ - kinteger.h krational.h + kinteger.h krational.h kreal.h kgequalp.o: kgequalp.c kgequalp.c kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kpair.h kcontinuation.h kgeqp.h kstring.h kgsymbols.o: kgsymbols.c kgsymbols.c kghelpers.h kstate.h klisp.h \ @@ -114,7 +115,8 @@ kgpairs_lists.o: kgpairs_lists.c kgpairs_lists.h kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kpair.h ksymbol.h kcontinuation.h kgequalp.h \ kenvironment.h kgnumbers.h kinteger.h kgpair_mut.o: kgpair_mut.c kgpair_mut.h kghelpers.h kstate.h klisp.h \ - kobject.h kerror.h kpair.h ksymbol.h kcontinuation.h kgeqp.h + kobject.h kerror.h kpair.h ksymbol.h kcontinuation.h kgeqp.h \ + kgnumbers.h kgenvironments.o: kgenvironments.c kgenvironments.h kghelpers.h kstate.h \ klisp.h kobject.h kerror.h kpair.h ksymbol.h kcontinuation.h \ kenvironment.h kgenv_mut.h kgpair_mut.h kgcontrol.h @@ -148,11 +150,11 @@ kgchars.o: kgchars.c kgchars.h kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kapplicative.h koperative.h kcontinuation.h kgnumbers.o: kgnumbers.c kgnumbers.h kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kapplicative.h koperative.h kcontinuation.h \ - ksymbol.h kinteger.h krational.h + ksymbol.h kinteger.h krational.h kreal.h kgkd_vars.h kgstrings.o: kgstrings.c kgstrings.h kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kapplicative.h koperative.h kcontinuation.h \ kstring.h ksymbol.h kgnumbers.h imath.o: kobject.h kstate.h kmem.h kerror.h imrath.o: kobject.h kstate.h kmem.h kerror.h kgc.o: kgc.c kgc.h kobject.h kmem.h kstate.h kport.h imath.h imrat.h \ - ktable.h kstring.h kerror.h + ktable.h kstring.h kerror.h kinteger.h krational.h diff --git a/src/kenvironment.h b/src/kenvironment.h @@ -25,7 +25,7 @@ TValue kget_keyed_static_var(klisp_State *K, TValue env, TValue key); /* environments with hashtable bindings */ /* TEMP: for now only for ground environment TODO: Should profile too see when it makes sense & should add code - to all operatives creating environments to see when it's appropiate + to all operatives creating environments to see when it's appropriate or should add code to add binding to at certain point move over to hashtable */ TValue kmake_table_environment(klisp_State *K, TValue parents); diff --git a/src/kgchars.c b/src/kgchars.c @@ -54,7 +54,7 @@ void kinteger_to_char(klisp_State *K, TValue *xparams, TValue ptree, { UNUSED(xparams); UNUSED(denv); - bind_1tp(K, ptree, "integer", ttisinteger, itv); + bind_1tp(K, ptree, "exact integer", ttiseinteger, itv); if (ttisbigint(itv)) { klispE_throw_simple(K, "integer out of ASCII range [0 - 127]"); diff --git a/src/kgnumbers.c b/src/kgnumbers.c @@ -4,6 +4,11 @@ ** See Copyright Notice in klisp.h */ +/* +** TODO: Many real operations are done by converting to bigint/bigrat +** (like numerator and gcd), these should be done in doubles directly +*/ + #include <assert.h> #include <stdio.h> #include <stdlib.h> @@ -19,287 +24,522 @@ #include "ksymbol.h" #include "kinteger.h" #include "krational.h" +#include "kreal.h" #include "kghelpers.h" #include "kgnumbers.h" +#include "kgkd_vars.h" /* for strict arith flag */ /* 15.5.1? number?, finite?, integer? */ /* use ftypep & ftypep_predp */ /* Helpers for typed predicates */ -bool knumberp(TValue obj) { return ttype(obj) <= K_LAST_NUMBER_TYPE; } +bool knumberp(TValue obj) { return ttisnumber(obj); } +/* TEMP used in =? for type predicate (XXX it's not actually a type + error, but it's close enough and otherwise should define a + new bpredp for numeric predicates...) */ +bool knumber_wpvp(TValue obj) +{ + return ttisnumber(obj) && !ttisrwnpv(obj) && !ttisundef(obj); +} /* This is used in gcd & lcm */ -bool kimp_intp(TValue obj) { return ttisinteger(obj) || ttiseinf(obj); } +bool kimp_intp(TValue obj) { return ttisinteger(obj) || ttisinf(obj); } /* obj is known to be a number */ -bool kfinitep(TValue obj) { return (!ttiseinf(obj) && !ttisiinf(obj)); } -/* TEMP: for now only fixint, bigints & rational, should also include inexact - integers */ +bool kfinitep(TValue obj) { return !ttisinf(obj); } +/* fixint, bigints & inexact integers */ bool kintegerp(TValue obj) { return ttisinteger(obj); } +/* only exact integers (like for indices), bigints & fixints */ +bool keintegerp(TValue obj) { return ttiseinteger(obj); } bool krationalp(TValue obj) { return ttisrational(obj); } -/* all real are rationals in klisp */ -bool krealp(TValue obj) +bool krealp(TValue obj) { return ttisreal(obj); } +/* TEMP used in <? & co for type predicate (XXX it's not actually a type + error, but it's close enough and otherwise should define a + new bpredp for numeric predicates...) */ +bool kreal_wpvp(TValue obj) { return ttisreal(obj) && !ttisrwnpv(obj); } + +bool kexactp(TValue obj) { return ttisexact(obj); } +bool kinexactp(TValue obj) { return ttisinexact(obj); } +bool kundefinedp(TValue obj) { return ttisundef(obj); } +bool krobustp(TValue obj) { return ttisrobust(obj); } + +/* 12.5.2 =? */ +/* uses typed_bpredp */ + +/* 12.5.3 <?, <=?, >?, >=? */ +/* use typed_bpredp */ + +/* Helpers for typed binary predicates */ +/* XXX: this should probably be in a file knumber.h but there is no real need for + that file yet */ + +/* this will come handy when there are more numeric types, + it is intended to be used in switch */ +/* MAYBE: change to return -1, 0, 1 to indicate which type is bigger, and + return min & max in two extra pointers passed in. Change name to + classify_types */ +inline int32_t max_ttype(TValue obj1, TValue obj2) +{ + int32_t t1 = ttype(obj1); + int32_t t2 = ttype(obj2); + + return (t1 > t2? t1 : t2); +} + +inline int32_t min_ttype(TValue obj1, TValue obj2) +{ + int32_t t1 = ttype(obj1); + int32_t t2 = ttype(obj2); + + return (t1 < t2? t1 : t2); +} + +/* helper to make both arguments inexact if one of them is, + n1 & n2 should be variable names that may be overwritten */ +/* GC: There is no problem because for now all inexact are stack + allocated */ +#define kensure_same_exactness(K, n1, n2) \ + ({if (ttisinexact(n1) || ttisinexact(n2)) { \ + n1 = kexact_to_inexact(K, n1); \ + n2 = kexact_to_inexact(K, n2); \ + }}) + + +/* ASK John: this isn't quite right I think. The problem is with implicit + conversion to inexact. This can cause issues for example if two different + exact numbers are compared with an inexact number that could correspong to + both (because it is too big and lacks precission for example), this would + behave differently depending on the order (=? #e1 #i #e2) would return + true & (=? #e1 #e2 #i) wourld return false. Maybe all numbers should be + converted to inexact. Also what happens with over & underflows? */ + +/* ASK John: the same will probably apply to many combiners..., MAYBE shuld + check scheme implementations... */ + +/* TEMP: for now only reals, no complex numbers */ +bool knum_eqp(klisp_State *K, TValue n1, TValue n2) +{ + /* for simplicity if one is inexact convert the other to inexact */ + /* ASK John what happens on under & overflow, probably an error shouldn't + be signaled but instead inexact should be converted to exact to perform + the check?? */ + kensure_same_exactness(K, n1, n2); + + switch(max_ttype(n1, n2)) { + case K_TFIXINT: + return ivalue(n1) == ivalue(n2); + case K_TBIGINT: + if (min_ttype(n1, n2) != K_TBIGINT) { + /* NOTE: no fixint is =? to a bigint */ + return false; + } else { + /* both are bigints */ + return kbigint_eqp(n1, n2); + } + case K_TBIGRAT: + if (min_ttype(n1, n2) != K_TBIGRAT) { + /* NOTE: no fixint or bigint is =? to a bigrat */ + return false; + } else { + /* both are bigints */ + return kbigrat_eqp(K, n1, n2); + } + case K_TEINF: + return (tv_equal(n1, n2)); + case K_TDOUBLE: + return (tv_equal(n1, n2)); + case K_TIINF: /* if the other was exact it was converted already */ + return (tv_equal(n1, n2)); + case K_TRWNPV: + case K_TUNDEFINED: /* no primary value, should throw an error */ + /* TEMP: this was already contemplated in type predicate */ + default: + klispE_throw_simple(K, "unsupported type"); + return false; + } +} + +bool knum_ltp(klisp_State *K, TValue n1, TValue n2) +{ + /* for simplicity if one is inexact convert the other to inexact */ + kensure_same_exactness(K, n1, n2); + + switch(max_ttype(n1, n2)) { + case K_TFIXINT: + return ivalue(n1) < ivalue(n2); + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_ltp(n1, n2); + } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_ltp(K, n1, n2); + } + case K_TDOUBLE: /* both must be double, all inferior types + convert to either double or inexact infinity */ + return (dvalue(n1) < dvalue(n2)); + case K_TEINF: + return !tv_equal(n1, n2) && (tv_equal(n1, KEMINF) || + tv_equal(n2, KEPINF)); + case K_TIINF: /* if the other was exact it was converted already */ + return !tv_equal(n1, n2) && (tv_equal(n1, KIMINF) || + tv_equal(n2, KIPINF)); + case K_TRWNPV: + case K_TUNDEFINED: /* no primary value, should throw an error */ + /* TEMP: this was already contemplated in type predicate */ + default: + klispE_throw_simple(K, "unsupported type"); + return false; + } +} + +bool knum_lep(klisp_State *K, TValue n1, TValue n2) +{ + return !knum_ltp(K, n2, n1); +} +bool knum_gtp(klisp_State *K, TValue n1, TValue n2) +{ + return knum_ltp(K, n2, n1); +} +bool knum_gep(klisp_State *K, TValue n1, TValue n2) { - return ttisrational(obj) || ttiseinf(obj) || ttisiinf(obj); + return !knum_ltp(K, n1, n2); +} + +/* +** Helper to check strict arithmetic flag if the result may not +** have a primary value +*/ +/* may evaluate K & n more than once */ +#define arith_return(K, n) \ + ({ if (ttisnwnpv(n) && kcurr_strict_arithp(K)) { \ + klispE_throw_simple_with_irritants(K, "result has no " \ + "primary value", \ + 1, n); \ + return KINERT; \ + } else { return n;}}) + +/* may evaluate K & n more than once */ +#define arith_kapply_cc(K, n) \ + ({ if (ttisnwnpv(n) && kcurr_strict_arithp(K)) { \ + klispE_throw_simple_with_irritants(K, "result has no " \ + "primary value", \ + 1, n); \ + return; \ + } else { kapply_cc(K, n); return;}}) + + + +/* REFACTOR/MAYBE: add small inlineable plus that + first tries fixint addition and if that fails calls knum_plus */ + +/* May throw an error */ +/* GC: assumes n1 & n2 rooted */ +TValue knum_plus(klisp_State *K, TValue n1, TValue n2) +{ + kensure_same_exactness(K, n1, n2); + TValue res; /* used for results with no primary value */ + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) + (int64_t) ivalue(n2); + if (res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_plus(K, n1, n2); + } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_plus(K, n1, n2); + } + case K_TDOUBLE: { + double res = dvalue(n1) + dvalue(n2); + /* check under & overflow */ + if (kcurr_strict_arithp(K)) { + if (res == 0 && dvalue(n1) != -dvalue(n2)) { + klispE_throw_simple(K, "underflow"); + return KINERT; + } else if (isinf(res)) { + klispE_throw_simple(K, "overflow"); + return KINERT; + } + } + /* correctly encapsulate infinities and -0.0 */ + return ktag_double(res); + } + case K_TEINF: + if (!ttiseinf(n1)) + return n2; + else if (!ttiseinf(n2)) + return n1; + if (tv_equal(n1, n2)) + return n1; + else { /* no primary value; handle error at the end of function */ + res = KRWNPV; + break; + } + case K_TIINF: + if (!ttisiinf(n1)) + return n2; + else if (!ttisiinf(n2)) + return n1; + if (tv_equal(n1, n2)) + return n1; + else { /* no primary value; handle error at the end of function */ + res = KRWNPV; + break; + } + case K_TRWNPV: /* no primary value */ + res = KRWNPV; + break; + case K_TUNDEFINED: /* undefined */ + res = KUNDEF; + break; + default: + klispE_throw_simple(K, "unsupported type"); + return KINERT; + } + + /* check for no primary value and value of strict arith */ + arith_return(K, res); +} + +/* May throw an error */ +/* GC: assumes n1 & n2 rooted */ +TValue knum_times(klisp_State *K, TValue n1, TValue n2) +{ + kensure_same_exactness(K, n1, n2); + TValue res; /* used for results with no primary value */ + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) * (int64_t) ivalue(n2); + if (res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_times(K, n1, n2); + } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_times(K, n1, n2); + } + case K_TDOUBLE: { + double res = dvalue(n1) * dvalue(n2); + /* check under & overflow */ + if (kcurr_strict_arithp(K)) { + if (res == 0 && dvalue(n1) != 0.0 && dvalue(n2) != 0.00) { + klispE_throw_simple(K, "underflow"); + return KINERT; + } else if (isinf(res)) { + klispE_throw_simple(K, "overflow"); + return KINERT; + } + } + /* correctly encapsulate infinities and -0.0 */ + return ktag_double(res); + } + case K_TEINF: + if (!ttiseinf(n1) || !ttiseinf(n2)) { + if (kfast_zerop(n1) || kfast_zerop(n2)) { + /* report: #e+infinity * 0 has no primary value */ + res = KRWNPV; + break; + } else if (ttisexact(n1) && ttisexact(n2)) + return knum_same_signp(K, n1, n2)? KEPINF : KEMINF; + else + return knum_same_signp(K, n1, n2)? KIPINF : KIMINF; + } else + return (tv_equal(n1, n2))? KEPINF : KEMINF; + case K_TIINF: + if (!ttisiinf(n1) || !ttisiinf(n2)) { + if (kfast_zerop(n1) || kfast_zerop(n2)) { + /* report: #i[+-]infinity * 0 has no primary value */ + res = KRWNPV; + break; + } else + return knum_same_signp(K, n1, n2)? KIPINF : KIMINF; + } else + return (tv_equal(n1, n2))? KIPINF : KIMINF; + case K_TRWNPV: + res = KRWNPV; + break; + case K_TUNDEFINED: + res = KUNDEF; + break; + default: + klispE_throw_simple(K, "unsupported type"); + return KINERT; + } + + /* check for no primary value and value of strict arith */ + arith_return(K, res); } - /* 12.5.2 =? */ - /* uses typed_bpredp */ - - /* 12.5.3 <?, <=?, >?, >=? */ - /* use typed_bpredp */ - - /* Helpers for typed binary predicates */ - /* XXX: this should probably be in a file knumber.h but there is no real need for - that file yet */ - - /* this will come handy when there are more numeric types, - it is intended to be used in switch */ - /* MAYBE: change to return -1, 0, 1 to indicate which type is bigger, and - return min & max in two extra pointers passed in. Change name to - classify_types */ - inline int32_t max_ttype(TValue obj1, TValue obj2) - { - int32_t t1 = ttype(obj1); - int32_t t2 = ttype(obj2); - - return (t1 > t2? t1 : t2); - } - - inline int32_t min_ttype(TValue obj1, TValue obj2) - { - int32_t t1 = ttype(obj1); - int32_t t2 = ttype(obj2); - - return (t1 < t2? t1 : t2); - } - - /* TEMP: for now only fixints, bigints, bigrats and exact infinities */ - bool knum_eqp(klisp_State *K, TValue n1, TValue n2) - { - switch(max_ttype(n1, n2)) { - case K_TFIXINT: - return ivalue(n1) == ivalue(n2); - case K_TBIGINT: - if (min_ttype(n1, n2) != K_TBIGINT) { - /* NOTE: no fixint is =? to a bigint */ - return false; - } else { - /* both are bigints */ - return kbigint_eqp(n1, n2); - } - case K_TBIGRAT: - if (min_ttype(n1, n2) != K_TBIGRAT) { - /* NOTE: no fixint or bigint is =? to a bigrat */ - return false; - } else { - /* both are bigints */ - return kbigrat_eqp(K, n1, n2); - } - case K_TEINF: - return (tv_equal(n1, n2)); - default: - /* shouldn't happen */ - assert(0); - return false; - } - } - - bool knum_ltp(klisp_State *K, TValue n1, TValue n2) - { - switch(max_ttype(n1, n2)) { - case K_TFIXINT: - return ivalue(n1) < ivalue(n2); - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_ltp(n1, n2); - } - case K_TBIGRAT: { - kensure_bigrat(n1); - kensure_bigrat(n2); - return kbigrat_ltp(K, n1, n2); - } - case K_TEINF: - return !tv_equal(n1, n2) && (tv_equal(n1, KEMINF) || - tv_equal(n2, KEPINF)); - default: - /* shouldn't happen */ - assert(0); - return false; - } - } - - bool knum_lep(klisp_State *K, TValue n1, TValue n2) - { - return !knum_ltp(K, n2, n1); - } - bool knum_gtp(klisp_State *K, TValue n1, TValue n2) - { - return knum_ltp(K, n2, n1); - } - bool knum_gep(klisp_State *K, TValue n1, TValue n2) - { - return !knum_ltp(K, n1, n2); - } - - /* REFACTOR/MAYBE: add small inlineable plus that - first tries fixint addition and if that fails calls knum_plus */ - - /* May throw an error */ - /* GC: assumes n1 & n2 rooted */ - TValue knum_plus(klisp_State *K, TValue n1, TValue n2) - { - switch(max_ttype(n1, n2)) { - case K_TFIXINT: { - int64_t res = (int64_t) ivalue(n1) + (int64_t) ivalue(n2); - if (res >= (int64_t) INT32_MIN && - res <= (int64_t) INT32_MAX) { - return i2tv((int32_t) res); - } /* else fall through */ - } - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_plus(K, n1, n2); - } - case K_TBIGRAT: { - kensure_bigrat(n1); - kensure_bigrat(n2); - return kbigrat_plus(K, n1, n2); - } - case K_TEINF: - if (!ttiseinf(n1)) - return n2; - else if (!ttiseinf(n2)) - return n1; - if (tv_equal(n1, n2)) - return n1; - else { - klispE_throw_simple(K, "no primary value"); - return KINERT; - } - default: - klispE_throw_simple(K, "unsopported type"); - return KINERT; - } - } - - /* May throw an error */ - /* GC: assumes n1 & n2 rooted */ - TValue knum_times(klisp_State *K, TValue n1, TValue n2) - { - switch(max_ttype(n1, n2)) { - case K_TFIXINT: { - int64_t res = (int64_t) ivalue(n1) * (int64_t) ivalue(n2); - if (res >= (int64_t) INT32_MIN && - res <= (int64_t) INT32_MAX) { - return i2tv((int32_t) res); - } /* else fall through */ - } - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_times(K, n1, n2); - } - case K_TBIGRAT: { - kensure_bigrat(n1); - kensure_bigrat(n2); - return kbigrat_times(K, n1, n2); - } - case K_TEINF: - if (!ttiseinf(n1) || !ttiseinf(n2)) { - if (kfast_zerop(n1) || kfast_zerop(n2)) { - /* report: #e+infinity * 0 has no primary value */ - klispE_throw_simple(K, "result has no primary value"); - return KINERT; - } else - return knum_same_signp(n1, n2)? KEPINF : KEMINF; - } else - return (tv_equal(n1, n2))? KEPINF : KEMINF; - default: - klispE_throw_simple(K, "unsopported type"); - return KINERT; - } - } - - /* May throw an error */ - /* GC: assumes n1 & n2 rooted */ - TValue knum_minus(klisp_State *K, TValue n1, TValue n2) - { - switch(max_ttype(n1, n2)) { - case K_TFIXINT: { - int64_t res = (int64_t) ivalue(n1) - (int64_t) ivalue(n2); - if (res >= (int64_t) INT32_MIN && - res <= (int64_t) INT32_MAX) { - return i2tv((int32_t) res); - } /* else fall through */ - } - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_minus(K, n1, n2); - } - case K_TBIGRAT: { - kensure_bigrat(n1); - kensure_bigrat(n2); - return kbigrat_minus(K, n1, n2); - } - case K_TEINF: - if (!ttiseinf(n1)) - return kneg_inf(n2); - else if (!ttiseinf(n2)) - return n1; - if (tv_equal(n1, n2)) { - klispE_throw_simple(K, "no primary value"); - return KINERT; +/* May throw an error */ +/* GC: assumes n1 & n2 rooted */ +TValue knum_minus(klisp_State *K, TValue n1, TValue n2) +{ + kensure_same_exactness(K, n1, n2); + TValue res; /* used for results with no primary value */ + + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) - (int64_t) ivalue(n2); + if (res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_minus(K, n1, n2); + } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_minus(K, n1, n2); + } + case K_TDOUBLE: { + /* both are double */ + double res = dvalue(n1) - dvalue(n2); + /* check under & overflow */ + if (kcurr_strict_arithp(K)) { + if (res == 0 && dvalue(n1) != dvalue(n2)) { + klispE_throw_simple(K, "underflow"); + return KINERT; + } else if (isinf(res)) { + klispE_throw_simple(K, "overflow"); + return KINERT; + } + } + /* correctly encapsulate infinities and -0.0 */ + return ktag_double(res); + } + case K_TEINF: + if (!ttiseinf(n1)) + return kneg_inf(n2); + else if (!ttiseinf(n2)) + return n1; + if (tv_equal(n1, n2)) { + /* no primary value; handle error at the end of function */ + res = KRWNPV; + break; } else return n1; + case K_TIINF: + if (!ttisiinf(n1)) + return kneg_inf(n2); + else if (!ttisiinf(n2)) + return n1; + if (tv_equal(n1, n2)) { + /* no primary value; handle error at the end of function */ + res = KRWNPV; + break; + } else + return n1; + case K_TRWNPV: /* no primary value */ + res = KRWNPV; + break; + case K_TUNDEFINED: /* undefined */ + res = KUNDEF; + break; default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } + + /* check for no primary value and value of strict arith */ + arith_return(K, res); } - /* May throw an error */ - /* GC: assumes n1 & n2 rooted */ - TValue knum_divided(klisp_State *K, TValue n1, TValue n2) - { - /* first check the most common error, division by zero */ - if (kfast_zerop(n2)) { - klispE_throw_simple(K, "division by zero (no primary value)"); - return KINERT; - } - - switch(max_ttype(n1, n2)) { - case K_TFIXINT: { - int64_t res = (int64_t) ivalue(n1) / (int64_t) ivalue(n2); - int64_t rem = (int64_t) ivalue(n1) % (int64_t) ivalue(n2); - if (rem == 0 && res >= (int64_t) INT32_MIN && - res <= (int64_t) INT32_MAX) { - return i2tv((int32_t) res); - } /* else fall through */ - } - case K_TBIGINT: /* just handle it as a rational */ - case K_TBIGRAT: { - kensure_bigrat(n1); - kensure_bigrat(n2); - return kbigrat_divided(K, n1, n2); - } - case K_TEINF: { - if (ttiseinf(n1) && ttiseinf(n2)) { - klispE_throw_simple(K, "(infinity divided by infinity) " - "no primary value"); - return KINERT; - } else if (ttiseinf(n1)) { - return knum_same_signp(n1, n2)? KEPINF : KEMINF; - } else { /* ttiseinf(n2) */ - return i2tv(0); - } - } - default: - klispE_throw_simple(K, "unsopported type"); - return KINERT; - } +/* May throw an error */ +/* GC: assumes n1 & n2 rooted */ +TValue knum_divided(klisp_State *K, TValue n1, TValue n2) +{ + kensure_same_exactness(K, n1, n2); + TValue res; /* used for results with no primary value */ + + /* first check the most common error, division by zero */ + if (kfast_zerop(n2)) { + klispE_throw_simple(K, "division by zero"); + return KINERT; + } + + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) / (int64_t) ivalue(n2); + int64_t rem = (int64_t) ivalue(n1) % (int64_t) ivalue(n2); + if (rem == 0 && res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: /* just handle it as a rational */ + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_divided(K, n1, n2); + } + case K_TDOUBLE: { + double res = dvalue(n1) / dvalue(n2); + /* check under & overflow */ + if (kcurr_strict_arithp(K)) { + if (res == 0 && dvalue(n1) != 0.0) { + klispE_throw_simple(K, "underflow"); + return KINERT; + } else if (isinf(res)) { + klispE_throw_simple(K, "overflow"); + return KINERT; + } + } + /* correctly encapsulate infinities and -0.0 */ + return ktag_double(res); + } + case K_TEINF: { + if (ttiseinf(n1) && ttiseinf(n2)) { + klispE_throw_simple(K, "infinity divided by infinity"); + return KINERT; + } else if (ttiseinf(n1)) { + return knum_same_signp(K, n1, n2)? KEPINF : KEMINF; + } else { /* ttiseinf(n2) */ + return i2tv(0); + } + } + case K_TIINF: + if (ttisiinf(n1) && ttisiinf(n2)) { + klispE_throw_simple(K, "infinity divided by infinity"); + return KINERT; + } else if (ttisiinf(n1)) { + return knum_same_signp(K, n1, n2)? KIPINF : KIMINF; + } else { /* ttiseinf(n2) */ + /* NOTE: I guess this doens't count as underflow */ + return d2tv(0.0); + } + case K_TRWNPV: + res = KRWNPV; + break; + case K_TUNDEFINED: + res = KUNDEF; + break; + default: + klispE_throw_simple(K, "unsupported type"); + return KINERT; + } + + /* check for no primary value and value of strict arith */ + arith_return(K, res); } /* GC: assumes n rooted */ @@ -312,32 +552,44 @@ TValue knum_abs(klisp_State *K, TValue n) return (i < 0? i2tv(-i) : n); /* if i == INT32_MIN, fall through */ /* MAYBE: we could cache the bigint INT32_MAX+1 */ + /* else fall through */ } case K_TBIGINT: { - /* this is needed for INT32_MIN, can't be in previous - case because it should be in the same block, remember - the bigint is allocated on the stack. */ + /* this is needed for INT32_MIN, can't be in previous + case because it should be in the same block, remember + the bigint is allocated on the stack. */ kensure_bigint(n); return kbigint_abs(K, n); } case K_TBIGRAT: { - kensure_bigrat(n); return kbigrat_abs(K, n); } + case K_TDOUBLE: { + return ktag_double(fabs(dvalue(n))); + } case K_TEINF: return KEPINF; + case K_TIINF: + return KIPINF; + case K_TRWNPV: + /* ASK John: is the error here okay */ + arith_return(K, KRWNPV); default: /* shouldn't happen */ - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } /* unlike the kernel gcd this returns |n| for gcd(n, 0) and gcd(0, n) and - 0 for gcd(0, 0) */ + 0 for gcd(0, 0) */ /* GC: assumes n1 & n2 rooted */ TValue knum_gcd(klisp_State *K, TValue n1, TValue n2) { + /* this is not so nice but simplifies some cases */ + /* XXX: this may cause overflows! */ + kensure_same_exactness(K, n1, n2); + switch(max_ttype(n1, n2)) { case K_TFIXINT: { int64_t gcd = kgcd32_64(ivalue(n1), ivalue(n2)); @@ -352,6 +604,19 @@ TValue knum_gcd(klisp_State *K, TValue n1, TValue n2) kensure_bigint(n2); return kbigint_gcd(K, n1, n2); } + case K_TDOUBLE: { + krooted_vars_push(K, &n1); + krooted_vars_push(K, &n2); + n1 = kinexact_to_exact(K, n1); + n2 = kinexact_to_exact(K, n2); + TValue res = knum_gcd(K, n1, n2); + krooted_tvs_push(K, res); + res = kexact_to_inexact(K, res); + krooted_tvs_pop(K); + krooted_vars_pop(K); + krooted_vars_pop(K); + return res; + } case K_TEINF: if (kfast_zerop(n2) || !ttiseinf(n1)) return knum_abs(K, n1); @@ -359,8 +624,15 @@ TValue knum_gcd(klisp_State *K, TValue n1, TValue n2) return knum_abs(K, n2); else return KEPINF; + case K_TIINF: + if (kfast_zerop(n2) || !ttisiinf(n1)) + return knum_abs(K, n1); + else if (kfast_zerop(n1) || !ttisiinf(n2)) + return knum_abs(K, n2); + else + return KIPINF; default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } @@ -369,16 +641,19 @@ TValue knum_gcd(klisp_State *K, TValue n1, TValue n2) /* GC: assumes n1 & n2 rooted */ TValue knum_lcm(klisp_State *K, TValue n1, TValue n2) { + /* this is not so nice but simplifies some cases */ + /* XXX: this may cause overflows! */ + kensure_same_exactness(K, n1, n2); + /* get this out of the way first */ if (kfast_zerop(n1) || kfast_zerop(n2)) { - klispE_throw_simple(K, "no primary value"); - return KINERT; + arith_return(K, KRWNPV); } switch(max_ttype(n1, n2)) { case K_TFIXINT: { int64_t lcm = klcm32_64(ivalue(n1), ivalue(n2)); - /* May fail for lcm(INT32_MIN, 1) because + /* May fail for lcm(INT32_MIN, 1) because it would return INT32_MAX+1 */ if (kfit_int32_t(lcm)) return i2tv((int32_t) lcm); @@ -389,10 +664,25 @@ TValue knum_lcm(klisp_State *K, TValue n1, TValue n2) kensure_bigint(n2); return kbigint_lcm(K, n1, n2); } + case K_TDOUBLE: { + krooted_vars_push(K, &n1); + krooted_vars_push(K, &n2); + n1 = kinexact_to_exact(K, n1); + n2 = kinexact_to_exact(K, n2); + TValue res = knum_lcm(K, n1, n2); + krooted_tvs_push(K, res); + res = kexact_to_inexact(K, res); + krooted_tvs_pop(K); + krooted_vars_pop(K); + krooted_vars_pop(K); + return res; + } case K_TEINF: return KEPINF; + case K_TIINF: + return KIPINF; default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } @@ -406,9 +696,17 @@ TValue knum_numerator(klisp_State *K, TValue n) return n; case K_TBIGRAT: return kbigrat_numerator(K, n); + case K_TDOUBLE: { + TValue res = kinexact_to_exact(K, n); + krooted_vars_push(K, &res); + res = knum_numerator(K, res); + res = kexact_to_inexact(K, res); + krooted_vars_pop(K); + return res; + } /* case K_TEINF: infinities are not rational! */ default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } @@ -422,9 +720,17 @@ TValue knum_denominator(klisp_State *K, TValue n) return i2tv(1); /* denominator of integer is always (+)1 */ case K_TBIGRAT: return kbigrat_denominator(K, n); + case K_TDOUBLE: { + TValue res = kinexact_to_exact(K, n); + krooted_vars_push(K, &res); + res = knum_denominator(K, res); + res = kexact_to_inexact(K, res); + krooted_vars_pop(K); + return res; + } /* case K_TEINF: infinities are not rational! */ default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } @@ -438,21 +744,33 @@ TValue knum_real_to_integer(klisp_State *K, TValue n, kround_mode mode) return n; /* integers are easy */ case K_TBIGRAT: return kbigrat_to_integer(K, n, mode); + case K_TDOUBLE: + return kdouble_to_integer(K, n, mode); case K_TEINF: klispE_throw_simple(K, "infinite value"); return KINERT; + case K_TIINF: + klispE_throw_simple(K, "infinite value"); + return KINERT; + case K_TRWNPV: + arith_return(K, KRWNPV); + case K_TUNDEFINED: + /* undefined in not a real, shouldn't get here, fall through */ default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } TValue knum_simplest_rational(klisp_State *K, TValue n1, TValue n2) { + /* this is not so nice but simplifies some cases */ + /* XXX: this may cause overflows! */ + kensure_same_exactness(K, n1, n2); + /* first check that case that n1 > n2 */ if (knum_gtp(K, n1, n2)) { - klispE_throw_simple(K, "result with no primary value " - "(n1 > n2)"); + klispE_throw_simple(K, "x0 doesn't exists (n1 > n2)"); return KINERT; } @@ -466,14 +784,47 @@ TValue knum_simplest_rational(klisp_State *K, TValue n1, TValue n2) kensure_bigrat(n2); return kbigrat_simplest_rational(K, n1, n2); } + case K_TDOUBLE: { + /* both are double, for now just convert to rational */ + krooted_vars_push(K, &n1); + krooted_vars_push(K, &n2); + n1 = kinexact_to_exact(K, n1); + n2 = kinexact_to_exact(K, n2); + TValue res = knum_simplest_rational(K, n1, n2); + krooted_tvs_push(K, res); + res = kexact_to_inexact(K, res); + krooted_tvs_pop(K); + krooted_vars_pop(K); + krooted_vars_pop(K); + return res; + } case K_TEINF: /* we know that n1 <= n2 */ if (tv_equal(n1, n2)) { - klispE_throw_simple(K, "result with no primary value"); + klispE_throw_simple(K, "x0 doesn't exists (n1 == n2 & " + "irrational)"); return KINERT; - } else if (knegativep(n1) && kpositivep(n2)) { + } else if (knegativep(K, n1) && kpositivep(K, n2)) { return i2tv(0); - } else if (knegativep(n1)) { + } else if (knegativep(K, n1)) { + /* n1 -inf, n2 finite negative */ + /* ASK John: is this behaviour for infinities ok? */ + /* Also in the report example both 1/3 & 1/2 are simpler than + 2/5... */ + return knum_real_to_integer(K, n2, K_FLOOR); + } else { + /* n1 finite positive, n2 +inf */ + /* ASK John: is this behaviour for infinities ok? */ + return knum_real_to_integer(K, n1, K_CEILING); + } + case K_TIINF: + /* we know that n1 <= n2 */ + if (tv_equal(n1, n2)) { + klispE_throw_simple(K, "result with no primary value"); + return KINERT; + } else if (knegativep(K, n1) && kpositivep(K, n2)) { + return d2tv(0.0); + } else if (knegativep(K, n1)) { /* n1 -inf, n2 finite negative */ /* ASK John: is this behaviour for infinities ok? */ /* Also in the report example both 1/3 & 1/2 are simpler than @@ -484,14 +835,21 @@ TValue knum_simplest_rational(klisp_State *K, TValue n1, TValue n2) /* ASK John: is this behaviour for infinities ok? */ return knum_real_to_integer(K, n1, K_CEILING); } + case K_TRWNPV: + arith_return(K, KRWNPV); + /* complex and undefined should be captured by type predicate */ default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } TValue knum_rationalize(klisp_State *K, TValue n1, TValue n2) { + /* this is not so nice but simplifies some cases */ + /* XXX: this may cause overflows! */ + kensure_same_exactness(K, n1, n2); + switch(max_ttype(n1, n2)) { case K_TFIXINT: case K_TBIGINT: /* for now do all with bigrat */ @@ -501,16 +859,38 @@ TValue knum_rationalize(klisp_State *K, TValue n1, TValue n2) kensure_bigrat(n2); return kbigrat_rationalize(K, n1, n2); } + case K_TDOUBLE: { + /* both are double, for now just convert to rational */ + krooted_vars_push(K, &n1); + krooted_vars_push(K, &n2); + n1 = kinexact_to_exact(K, n1); + n2 = kinexact_to_exact(K, n2); + TValue res = knum_rationalize(K, n1, n2); + krooted_tvs_push(K, res); + res = kexact_to_inexact(K, res); + krooted_tvs_pop(K); + krooted_vars_pop(K); + krooted_vars_pop(K); + return res; + } case K_TEINF: if (kfinitep(n1) || !kfinitep(n2)) { return i2tv(0); } else { /* infinite n1, finite n2 */ /* ASK John: is this behaviour for infinities ok? */ - klispE_throw_simple(K, "result with no primary value"); + klispE_throw_simple(K, "x0 doesn't exists"); + return KINERT; + } + case K_TIINF: + if (kfinitep(n1) || !kfinitep(n2)) { + return d2tv(0.0); + } else { /* infinite n1, finite n2 */ + /* ASK John: is this behaviour for infinities ok? */ + klispE_throw_simple(K, "x0 doesn't exists"); return KINERT; } default: - klispE_throw_simple(K, "unsopported type"); + klispE_throw_simple(K, "unsupported type"); return KINERT; } } @@ -522,8 +902,8 @@ void kplus(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) UNUSED(xparams); /* cycles are allowed, loop counting pairs */ int32_t cpairs; - int32_t pairs = check_typed_list(K, "+", "number", knumberp, true, - ptree, &cpairs); + int32_t pairs = check_typed_list(K, "+", "number", knumberp, + true, ptree, &cpairs); int32_t apairs = pairs - cpairs; TValue res; @@ -544,12 +924,14 @@ void kplus(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* next the cyclic part */ TValue cres = i2tv(0); /* push it only if needed */ - if (cpairs == 0) { - /* speed things up if there is no cycle */ + if (cpairs == 0 && !ttisnwnpv(ares)) { /* #undefined or #real */ + /* speed things up if there is no cycle and + no possible error (on no primary value) */ res = ares; krooted_vars_pop(K); } else { bool all_zero = true; + bool all_exact = true; krooted_vars_push(K, &cres); while(cpairs--) { @@ -557,18 +939,22 @@ void kplus(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) tail = kcdr(tail); all_zero = all_zero && kfast_zerop(first); + all_exact = all_exact && ttisexact(first); cres = knum_plus(K, cres, first); } - if (kfast_zerop(cres)) { - if (!all_zero) { - /* report */ - klispE_throw_simple(K, "result has no primary value"); - return; - } - } else - cres = knegativep(cres)? KEMINF : KEPINF; + if (ttisnwnpv(cres)) /* #undefined or #real */ + ; /* do nothing, check is made later */ + else if (kfast_zerop(cres)) { + if (!all_zero) + cres = KRWNPV; /* check is made later */ + } else if (all_exact) + cres = knegativep(K, cres)? KEMINF : KEPINF; + else + cres = knegativep(K, cres)? KIMINF : KIPINF; + + /* here if any of the two has no primary an error is signaled */ res = knum_plus(K, ares, cres); krooted_vars_pop(K); krooted_vars_pop(K); @@ -603,43 +989,50 @@ void ktimes(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* next the cyclic part */ TValue cres = i2tv(1); - if (cpairs == 0) { + if (cpairs == 0 && !ttisnwnpv(ares)) { /* #undefined or #real */ /* speed things up if there is no cycle */ res = ares; krooted_vars_pop(K); } else { bool all_one = true; + bool all_exact = true; krooted_vars_push(K, &cres); while(cpairs--) { TValue first = kcar(tail); tail = kcdr(tail); all_one = all_one && kfast_onep(first); + all_exact = all_exact && ttisexact(first); cres = knum_times(K, cres, first); } /* think of cres as the product of an infinite series */ + if (ttisnwnpv(ares)) + ; /* do nothing */ if (kfast_zerop(cres)) ; /* do nothing */ - else if (kpositivep(cres) && knum_ltp(K, cres, i2tv(1))) - cres = i2tv(0); + else if (kpositivep(K, cres) && knum_ltp(K, cres, i2tv(1))) { + if (all_exact) + cres = i2tv(0); + else + cres = d2tv(0.0); + } else if (kfast_onep(cres)) { - if (all_one) - cres = i2tv(1); - else { - klispE_throw_simple(K, "result has no primary value"); - return; - } + if (all_one) { + if (all_exact) + cres = i2tv(1); + else + cres = d2tv(1.0); + } else + cres = KRWNPV; } else if (knum_gtp(K, cres, i2tv(1))) { /* ASK JOHN: this is as per the report, but maybe we should check that all elements are positive... */ - cres = KEPINF; - } else { - /* cycle result less than zero */ - klispE_throw_simple(K, "result has no primary value"); - return; - } + cres = all_exact? KEPINF : KIPINF; + } else + cres = KRWNPV; + /* this will throw error if necessary on no primary value */ res = knum_times(K, ares, cres); krooted_vars_pop(K); krooted_vars_pop(K); @@ -683,35 +1076,43 @@ void kminus(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) } /* next the cyclic part */ - TValue cres = i2tv(0); + TValue cres = i2tv(0); /* push it only if needed */ - if (cpairs == 0) { - /* speed things up if there is no cycle */ + if (cpairs == 0 && !ttisnwnpv(ares)) { /* #undefined or #real */ + /* speed things up if there is no cycle and + no possible error (on no primary value) */ res = ares; krooted_vars_pop(K); } else { bool all_zero = true; + bool all_exact = true; krooted_vars_push(K, &cres); while(cpairs--) { TValue first = kcar(tail); tail = kcdr(tail); + all_zero = all_zero && kfast_zerop(first); + all_exact = all_exact && ttisexact(first); + cres = knum_plus(K, cres, first); } - if (kfast_zerop(cres)) { - if (!all_zero) { - /* report */ - klispE_throw_simple(K, "result has no primary value"); - return; - } - } else - cres = knegativep(cres)? KEMINF : KEPINF; + if (ttisnwnpv(cres)) /* #undefined or #real */ + ; /* do nothing, check is made later */ + else if (kfast_zerop(cres)) { + if (!all_zero) + cres = KRWNPV; /* check is made later */ + } else if (all_exact) + cres = knegativep(K, cres)? KEMINF : KEPINF; + else + cres = knegativep(K, cres)? KIMINF : KIPINF; + + /* here if any of the two has no primary an error is signaled */ res = knum_plus(K, ares, cres); krooted_vars_pop(K); krooted_vars_pop(K); - } + } /* now substract the sum of all the elements in the list to the first value */ krooted_tvs_push(K, res); @@ -807,6 +1208,8 @@ void kdiv_mod(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) TValue tv_div, tv_mod; + kensure_same_exactness(K, tv_n, tv_d); + if (kfast_zerop(tv_d)) { klispE_throw_simple(K, "division by zero"); return; @@ -849,6 +1252,17 @@ void kdiv_mod(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) else tv_div = kbigrat_div0_mod0(K, tv_n, tv_d, &tv_mod); break; + case K_TDOUBLE: { + /* both are double */ + double div, mod; + if ((flags & FDIV_ZERO) == 0) + div = kdouble_div_mod(dvalue(tv_n), dvalue(tv_d), &mod); + else + div = kdouble_div0_mod0(dvalue(tv_n), dvalue(tv_d), &mod); + tv_div = ktag_double(div); + tv_mod = ktag_double(mod); + break; + } case K_TEINF: if (ttiseinf(tv_n)) { klispE_throw_simple(K, "non finite dividend"); @@ -874,10 +1288,49 @@ void kdiv_mod(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) klispE_throw_simple(K, "non finite divisor"); return; } - default: - klispE_throw_simple(K, "unsopported type"); - return; - } + case K_TIINF: + if (ttisiinf(tv_n)) { + klispE_throw_simple(K, "non finite dividend"); + return; + } else { /* if (ttiseinf(tv_d)) */ + /* The semantics here are unclear, following the general + guideline of the report that says that if an infinity is + involved it should be understand as a limit. In that + case once the divisor is greater in magnitude than the + dividend the division stabilizes itself at q = 0; r = n + if both have the same sign, and q = 1; r = +infinity if + both have different sign (but in that case !(r < |d|) + !!) */ + /* RATIONALE: if q were 0 we can't accomplish + q * d + r = n because q * d is undefined, if q isn't zero + then, either q*d + r is infinite or undefined so + there's no good q. on the other hand if we want + n - q*d = r & 0 <= r < d, r can't be infinite because it + would be equal to d, but q*d is infinite, so there's no + way out */ + /* throw an exception, until this is resolved */ + /* ASK John */ + klispE_throw_simple(K, "non finite divisor"); + return; + } + case K_TRWNPV: { /* no primary value */ + /* ASK John: what happens with undefined & real with no primary values */ + TValue n = ttisrwnpv(tv_n)? tv_n : tv_d; + if (kcurr_strict_arithp(K)) { + klispE_throw_simple_with_irritants(K, "operand has no primary " + "value", 1, n); + return; + } else { + tv_div = KRWNPV; + tv_mod = KRWNPV; + break; + } + } + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + TValue res; if (flags & FDIV_DIV) { @@ -903,41 +1356,53 @@ void kdiv_mod(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* use ftyped_predp */ /* Helpers for positive?, negative?, odd? & even? */ -bool kpositivep(TValue n) +bool kpositivep(klisp_State *K, TValue n) { switch (ttype(n)) { case K_TFIXINT: case K_TEINF: + case K_TIINF: return ivalue(n) > 0; case K_TBIGINT: return kbigint_positivep(n); case K_TBIGRAT: return kbigrat_positivep(n); + case K_TDOUBLE: + return dvalue(n) > 0.0; + case K_TRWNPV: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return false; + /* complex and undefined should be captured by type predicate */ default: - /* shouldn't happen */ - assert(0); + klispE_throw_simple(K, "unsupported type"); return false; } } -bool knegativep(TValue n) +bool knegativep(klisp_State *K, TValue n) { switch (ttype(n)) { case K_TFIXINT: case K_TEINF: + case K_TIINF: return ivalue(n) < 0; case K_TBIGINT: return kbigint_negativep(n); case K_TBIGRAT: return kbigrat_negativep(n); + case K_TDOUBLE: + return dvalue(n) < 0.0; + case K_TRWNPV: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return false; + /* complex and undefined should be captured by type predicate */ default: - /* shouldn't happen */ - assert(0); + klispE_throw_simple(K, "unsupported type"); return false; } } -/* n is finite */ +/* n is finite, integer */ bool koddp(TValue n) { switch (ttype(n)) { @@ -945,8 +1410,9 @@ bool koddp(TValue n) return (ivalue(n) & 1) != 0; case K_TBIGINT: return kbigint_oddp(n); + case K_TDOUBLE: + return fmod(dvalue(n), 2.0) != 0.0; default: - /* shouldn't happen */ assert(0); return false; } @@ -959,8 +1425,9 @@ bool kevenp(TValue n) return (ivalue(n) & 1) == 0; case K_TBIGINT: return kbigint_evenp(n); + case K_TDOUBLE: + return fmod(dvalue(n), 2.0) == 0.0; default: - /* shouldn't happen */ assert(0); return false; } @@ -980,7 +1447,7 @@ void kabs(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* 12.5.13 min, max */ /* NOTE: this does two passes, one for error checking and one for doing - the actual work */ + the actual work */ void kmin_max(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { /* @@ -995,7 +1462,7 @@ void kmin_max(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* cycles are allowed, loop counting pairs */ int32_t dummy; /* don't care about count of cycle pairs */ int32_t pairs = check_typed_list(K, name, "number", knumberp, true, ptree, - &dummy); + &dummy); TValue res; @@ -1041,19 +1508,16 @@ void kgcd(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) TValue first = kcar(tail); tail = kcdr(tail); seen_finite_non_zero |= - (!ttiseinf(first) && !kfast_zerop(first)); + (!ttisinf(first) && !kfast_zerop(first)); res = knum_gcd(K, res, first); } - if (!seen_finite_non_zero) { - /* report */ - klispE_throw_simple(K, "result has no primary value"); - return; - } + if (!seen_finite_non_zero) + res = KRWNPV; } krooted_vars_pop(K); - kapply_cc(K, res); + arith_kapply_cc(K, res); } void klcm(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) @@ -1081,7 +1545,142 @@ void klcm(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) } -/* TODO: remaining of rational module */ +/* 12.6.1 exact?, inexact?, robust?, undefined? */ +/* use fyped_predp */ + +/* 12.6.2 get-real-internal-bounds, get-real-exact-bounds */ +void kget_real_internal_bounds(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv) +{ + bind_1tp(K, ptree, "real", krealp, tv_n); + /* TEMP: do it here directly, for now all inexact objects have + [-inf, +inf] bounds */ + TValue res; + if (ttisexact(tv_n)) { + res = klist(K, 2, tv_n, tv_n); + } else { + res = klist(K, 2, KIMINF, KIPINF); + } + kapply_cc(K, res); +} + +void kget_real_exact_bounds(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv) +{ + bind_1tp(K, ptree, "real", krealp, tv_n); + /* TEMP: do it here directly, for now all inexact objects have + [-inf, +inf] bounds, when bounded reals are implemented this + should take care to round the min towards -inf and the max towards + +inf when converting to exact */ + TValue res; + if (ttisexact(tv_n)) { + res = klist(K, 2, tv_n, tv_n); + } else { + res = klist(K, 2, KEMINF, KEPINF); + } + kapply_cc(K, res); +} + +/* 12.6.3 get-real-internal-primary, get-real-exact-primary */ +void kget_real_internal_primary(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv) +{ + bind_1tp(K, ptree, "real", krealp, tv_n); + /* TEMP: do it here directly */ + if (ttisrwnpv(tv_n)) { + klispE_throw_simple_with_irritants(K, "no primary value", 1, tv_n); + return; + } else { + kapply_cc(K, tv_n); + } +} + +void kget_real_exact_primary(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv) +{ + bind_1tp(K, ptree, "real", krealp, tv_n); + + /* NOTE: this handles no primary value errors & exact cases just fine */ + TValue res = kinexact_to_exact(K, tv_n); + kapply_cc(K, res); +} + +/* 12.6.4 make-inexact */ +void kmake_inexact(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + bind_3tp(K, ptree, "real", krealp, real1, + "real", krealp, real2, "real", krealp, real3); + + TValue res; + UNUSED(real1); + UNUSED(real3); + if (ttisinexact(real2)) { + res = real2; + } else { + /* TEMP: for now bounds are ignored */ + /* NOTE: this handles overflow and underflow */ + res = kexact_to_inexact(K, real2); + } + kapply_cc(K, res); +} + +/* 12.6.5 real->inexact, real->exact */ +void kreal_to_inexact(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + bind_1tp(K, ptree, "real", krealp, tv_n); + + /* NOTE: this handles overflow and underflow */ + TValue res = kexact_to_inexact(K, tv_n); + kapply_cc(K, res); +} + +void kreal_to_exact(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + bind_1tp(K, ptree, "real", krealp, tv_n); + + TValue res = kinexact_to_exact(K, tv_n); + kapply_cc(K, res); +} + +/* 12.6.6 with-strict-arithmetic, get-strict-arithmetic? */ +void kwith_strict_arithmetic(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv) +{ + bind_2tp(K, ptree, "bool", ttisboolean, strictp, + "combiner", ttiscombiner, comb); + + TValue op = kmake_operative(K, do_bind, 1, K->kd_strict_arith_key); + krooted_tvs_push(K, op); + + TValue args = klist(K, 2, strictp, comb); + + krooted_tvs_pop(K); + + /* even if we call with denv, do_bind calls comb in an empty env */ + /* XXX: what to pass for source info?? */ + ktail_call(K, op, args, denv); +} + +void kget_strict_arithmeticp(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + check_0p(K, ptree); + + /* can access directly, no need to call do_access */ + TValue res = b2tv(kcurr_strict_arithp(K)); + kapply_cc(K, res); +} /* 12.8.1 rational? */ /* uses ftypep */ @@ -1124,47 +1723,55 @@ void kdivided(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* next the cyclic part */ TValue cres = i2tv(1); - if (cpairs == 0) { + if (cpairs == 0 && !ttisnwnpv(ares)) { /* #undefined or #real */ /* speed things up if there is no cycle */ res = ares; krooted_vars_pop(K); } else { bool all_one = true; + bool all_exact = true; krooted_vars_push(K, &cres); while(cpairs--) { TValue first = kcar(tail); tail = kcdr(tail); all_one = all_one && kfast_onep(first); + all_exact = all_exact && ttisexact(first); cres = knum_times(K, cres, first); } /* think of cres as the product of an infinite series */ + if (ttisnwnpv(ares)) + ; /* do nothing */ if (kfast_zerop(cres)) ; /* do nothing */ - else if (kpositivep(cres) && knum_ltp(K, cres, i2tv(1))) - cres = i2tv(0); + else if (kpositivep(K, cres) && knum_ltp(K, cres, i2tv(1))) { + if (all_exact) + cres = i2tv(0); + else + cres = d2tv(0.0); + } else if (kfast_onep(cres)) { - if (all_one) - cres = i2tv(1); - else { - klispE_throw_simple(K, "result has no primary value"); - return; - } + if (all_one) { + if (all_exact) + cres = i2tv(1); + else + cres = d2tv(1.0); + } else + cres = KRWNPV; } else if (knum_gtp(K, cres, i2tv(1))) { /* ASK JOHN: this is as per the report, but maybe we should check that all elements are positive... */ - cres = KEPINF; - } else { - /* cycle result less than zero */ - klispE_throw_simple(K, "result has no primary value"); - return; - } + cres = all_exact? KEPINF : KIPINF; + } else + cres = KRWNPV; + /* this will throw error if necessary on no primary value */ res = knum_times(K, ares, cres); krooted_vars_pop(K); krooted_vars_pop(K); } + /* now divide first value by the product of all the elements in the list */ krooted_tvs_push(K, res); res = knum_divided(K, first_val, res); @@ -1239,3 +1846,370 @@ void ksimplest_rational(klisp_State *K, TValue *xparams, TValue ptree, TValue res = knum_simplest_rational(K, n1, n2); kapply_cc(K, res); } + +void kexp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + bind_1tp(K, ptree, "number", knumberp, n); + + /* TEMP: do it inline for now */ + TValue res = i2tv(0); + switch(ttype(n)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + /* for now, all go to double */ + n = kexact_to_inexact(K, n); /* no need to root it */ + /* fall through */ + case K_TDOUBLE: { + double d = exp(dvalue(n)); + res = ktag_double(d); + break; + } + case K_TEINF: /* in any case return inexact result (e is inexact) */ + case K_TIINF: + res = kpositivep(K, n)? KIPINF : d2tv(0.0); + break; + case K_TRWNPV: + case K_TUNDEFINED: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return; + /* complex and undefined should be captured by type predicate */ + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + kapply_cc(K, res); +} + +void klog(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + bind_1tp(K, ptree, "number", knumberp, n); + + /* ASK John: error or no primary value, or undefined */ + if (kfast_zerop(n)) { + klispE_throw_simple_with_irritants(K, "zero argument", 1, n); + return; + } else if (knegativep(K, n)) { + klispE_throw_simple_with_irritants(K, "negative argument", 1, n); + return; + } + + /* TEMP: do it inline for now */ + TValue res = i2tv(0); + switch(ttype(n)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + /* for now, all go to double */ + n = kexact_to_inexact(K, n); /* no need to root it */ + /* fall through */ + case K_TDOUBLE: { + double d = log(dvalue(n)); + res = ktag_double(d); + break; + } + case K_TEINF: /* in any case return inexact result (e is inexact) */ + case K_TIINF: + /* is this ok? */ + res = KIPINF; + break; + case K_TRWNPV: + case K_TUNDEFINED: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return; + /* complex and undefined should be captured by type predicate */ + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + kapply_cc(K, res); +} + +void ktrig(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + /* + ** xparams[0]: trig function + */ + double (*fn)(double) = pvalue(xparams[0]); + + bind_1tp(K, ptree, "number", knumberp, n); + + /* TEMP: do it inline for now */ + TValue res = i2tv(0); + switch(ttype(n)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + /* for now, all go to double */ + n = kexact_to_inexact(K, n); /* no need to root it */ + /* fall through */ + case K_TDOUBLE: { + double d = (*fn)(dvalue(n)); + res = ktag_double(d); + break; + } + case K_TEINF: + case K_TIINF: + /* is this ok? */ + res = KRWNPV; + break; + case K_TRWNPV: + case K_TUNDEFINED: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return; + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + arith_kapply_cc(K, res); +} + +void katrig(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + /* + ** xparams[0]: trig function + */ + double (*fn)(double) = pvalue(xparams[0]); + + bind_1tp(K, ptree, "number", knumberp, n); + + /* TEMP: do it inline for now */ + TValue res = i2tv(0); + switch(ttype(n)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + /* for now, all go to double */ + n = kexact_to_inexact(K, n); /* no need to root it */ + /* fall through */ + case K_TDOUBLE: { + double d = dvalue(n); + if (d >= -1.0 && d <= 1.0) { + d = (*fn)(dvalue(n)); + res = ktag_double(d); + } else { + res = KUNDEF; /* ASK John: is this ok, or should throw error? */ + } + break; + } + case K_TEINF: + case K_TIINF: + /* ASK John: is this ok? */ + res = KRWNPV; + break; + case K_TRWNPV: + case K_TUNDEFINED: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return; + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + arith_kapply_cc(K, res); +} + +void katan(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + bind_al1tp(K, ptree, "number", knumberp, n1, rest); + bool two_params; + TValue n2; + if (ttisnil(rest)) { + two_params = false; + n2 = n1; + } else { + two_params = true; + if (!ttispair(rest) || !ttisnil(kcdr(rest))) { + klispE_throw_simple(K, "Bad ptree structure (in optional " + "argument)"); + return; + } else if (!ttisnumber(kcar(rest))) { + klispE_throw_simple(K, "Bad type on optional argument " + "(expected number)"); + return; + } else { + n2 = kcar(rest); + kensure_same_exactness(K, n1, n2); + } + } + + /* TEMP: do it inline for now */ + TValue res = i2tv(0); + switch(max_ttype(n1, n2)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + /* for now, all go to double */ + n1 = kexact_to_inexact(K, n1); /* no need to root it */ + if (two_params) + n2 = kexact_to_inexact(K, n2); /* no need to root it */ + /* fall through */ + case K_TDOUBLE: { + double d1 = dvalue(n1); + if (two_params) { + double d2 = dvalue(n2); + d1 = atan2(d1, d2); + } else { + d1 = atan(d1); + } + res = ktag_double(d1); + break; + } + case K_TEINF: + case K_TIINF: + /* ASK John: is this ok? */ + if (two_params) { + if (kfinitep(n1)) { + res = ktag_double(0.0); + } else if (!kfinitep(n2)) { + klispE_throw_simple_with_irritants(K, "infinite divisor & " + "dividend", 2, n1, n2); + return; + } else { + /* XXX either pi/2 or -pi/2, but we don't have the constant */ + double d = knum_same_signp(K, n1, n2)? atan(INFINITY) : + atan(-INFINITY); + res = ktag_double(d); + } + } else { + /* XXX either pi/2 or -pi/2, but we don't have the constant */ + double d = kpositivep(K, n1)? atan(INFINITY) : atan(-INFINITY); + res = ktag_double(d); + } + break; + case K_TRWNPV: + case K_TUNDEFINED: + if (two_params) { + klispE_throw_simple_with_irritants(K, "no primary value", 2, + n1, n2); + } else { + klispE_throw_simple_with_irritants(K, "no primary value", 1, n1); + } + return; + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + arith_kapply_cc(K, res); +} + +void ksqrt(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + bind_1tp(K, ptree, "number", knumberp, n); + + /* TEMP: do it inline for now */ + TValue res = i2tv(0); + switch(ttype(n)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + /* TEMP: for now, all go to double */ + n = kexact_to_inexact(K, n); /* no need to root it */ + /* fall through */ + case K_TDOUBLE: { + double d = dvalue(n); + if (d < 0.0) + res = KUNDEF; /* ASK John: is this ok, or should throw error? */ + else { + d = sqrt(d); + res = ktag_double(d); + } + break; + } + case K_TEINF: + case K_TIINF: + res = knegativep(K, n)? KUNDEF : KIPINF; + break; + case K_TRWNPV: + case K_TUNDEFINED: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return; + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + arith_kapply_cc(K, res); +} + +void kexpt(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + + bind_2tp(K, ptree, "number", knumberp, n1, + "number", knumberp, n2); + + kensure_same_exactness(K, n1, n2); + + /* TEMP: do it inline for now */ + TValue res = i2tv(0); + switch(max_ttype(n1, n2)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + /* TEMP: for now, all go to double */ + n1 = kexact_to_inexact(K, n1); /* no need to root it */ + n2 = kexact_to_inexact(K, n2); /* no need to root it */ + /* fall through */ + case K_TDOUBLE: { + double d1 = dvalue(n1); + double d2 = dvalue(n2); + d1 = pow(d1, d2); + res = ktag_double(d1); + break; + } + case K_TEINF: + case K_TIINF: + if (ttisinf(n1) && ttisinf(n2)) { + if (knegativep(K, n1) && knegativep(K, n2)) + res = d2tv(0.0); + else if (knegativep(K, n1) || knegativep(K, n2)) + res = KUNDEF; /* ASK John: is this ok? */ + else + res = KIPINF; + } else if (ttisinf(n1)) { + if (knegativep(K, n1)) { + if (knegativep(K, n2)) + res = d2tv(0.0); + else { + TValue num = knum_numerator(K, n2); + krooted_tvs_push(K, num); + res = kevenp(num)? KIPINF : KIMINF; + krooted_tvs_pop(K); + } + } else { + res = KIPINF; + } + } else { /* ttisinf(n2) */ + if (knegativep(K, n2)) + res = d2tv(0.0); + else if (knegativep(K, n1)) + res = KUNDEF; /* ASK John: is this ok? */ + else + res = KIPINF; + } + break; + case K_TRWNPV: + case K_TUNDEFINED: + klispE_throw_simple_with_irritants(K, "no primary value", 2, + n1, n2); + return; + default: + klispE_throw_simple(K, "unsupported type"); + return; + } + arith_kapply_cc(K, res); +} diff --git a/src/kgnumbers.h b/src/kgnumbers.h @@ -25,10 +25,17 @@ /* XXX: this should probably be in a file knumber.h but there is no real need for that file yet */ bool knumberp(TValue obj); +bool knumber_wpvp(TValue obj); bool kfinitep(TValue obj); bool kintegerp(TValue obj); +bool keintegerp(TValue obj); bool krationalp(TValue obj); bool krealp(TValue obj); +bool kreal_wpvp(TValue obj); +bool kexactp(TValue obj); +bool kinexactp(TValue obj); +bool kundefinedp(TValue obj); +bool krobustp(TValue obj); /* 12.5.2 =? */ @@ -77,8 +84,8 @@ bool kzerop(TValue n); /* use ftyped_predp */ /* Helpers for positive?, negative?, odd? & even? */ -bool kpositivep(TValue n); -bool knegativep(TValue n); +bool kpositivep(klisp_State *K, TValue n); +bool knegativep(klisp_State *K, TValue n); bool koddp(TValue n); bool kevenp(TValue n); @@ -111,6 +118,37 @@ void kmin_max(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); void kgcd(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); void klcm(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); +/* 12.6.1 exact?, inexact?, robust?, undefined? */ +/* use fyped_predp */ + +/* 12.6.2 get-real-internal-bounds, get-real-exact-bounds */ +void kget_real_internal_bounds(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv); +void kget_real_exact_bounds(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv); + +/* 12.6.3 get-real-internal-primary, get-real-exact-primary */ +void kget_real_internal_primary(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv); +void kget_real_exact_primary(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv); + +/* 12.6.4 make-inexact */ +void kmake_inexact(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + +/* 12.6.5 real->inexact, real->exact */ +void kreal_to_inexact(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv); +void kreal_to_exact(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv); + +/* 12.6.6 with-strict-arithmetic, get-strict-arithmetic? */ +void kwith_strict_arithmetic(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv); + +void kget_strict_arithmeticp(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv); + /* 12.8.1 rational? */ /* uses ftypep */ @@ -132,21 +170,55 @@ void krationalize(klisp_State *K, TValue *xparams, TValue ptree, void ksimplest_rational(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + +/* 12.9.1 real? */ +/* uses ftypep */ + +/* 12.9.2 exp, log */ +void kexp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); +void klog(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + +/* 12.9.3 sin, cos, tan */ +void ktrig(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + +/* 12.9.4 asin, acos, atan */ +void katrig(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); +void katan(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + +/* 12.9.5 sqrt */ +void ksqrt(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + +/* 12.9.6 expt */ +void kexpt(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + + /* REFACTOR: These should be in a knumber.h header */ /* Misc Helpers */ -/* TEMP: only infinities, fixints and bigints for now */ -inline bool kfast_zerop(TValue n) { return ttisfixint(n) && ivalue(n) == 0; } -inline bool kfast_onep(TValue n) { return ttisfixint(n) && ivalue(n) == 1; } -/* TEMP: only exact infinties */ +/* TEMP: only reals (no complex numbers) */ +inline bool kfast_zerop(TValue n) +{ + return (ttisfixint(n) && ivalue(n) == 0) || + (ttisdouble(n) && dvalue(n) == 0.0); +} + +inline bool kfast_onep(TValue n) +{ + return (ttisfixint(n) && ivalue(n) == 1) || + (ttisdouble(n) && dvalue(n) == 1.0); +} + inline TValue kneg_inf(TValue i) { - return tv_equal(i, KEPINF)? KEMINF : KEPINF; + if (ttiseinf(i)) + return tv_equal(i, KEPINF)? KEMINF : KEPINF; + else /* ttisiinf(i) */ + return tv_equal(i, KIPINF)? KIMINF : KIPINF; } -inline bool knum_same_signp(TValue n1, TValue n2) +inline bool knum_same_signp(klisp_State *K, TValue n1, TValue n2) { - return kpositivep(n1) == kpositivep(n2); + return kpositivep(K, n1) == kpositivep(K, n2); } #endif diff --git a/src/kgpair_mut.c b/src/kgpair_mut.c @@ -20,7 +20,7 @@ #include "kghelpers.h" #include "kgpair_mut.h" #include "kgeqp.h" /* eq? checking in memq and assq */ -#include "kgnumbers.h" /* for kpositivep and kintegerp */ +#include "kgnumbers.h" /* for kpositivep and keintegerp */ /* 4.7.1 set-car!, set-cdr! */ void set_carB(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) @@ -167,10 +167,10 @@ void encycleB(klisp_State *K, TValue *xparams, TValue ptree, UNUSED(xparams); bind_3tp(K, ptree, "any", anytype, obj, - "integer", kintegerp, tk1, - "integer", kintegerp, tk2); + "exact integer", keintegerp, tk1, + "exact integer", keintegerp, tk2); - if (knegativep(tk1) || knegativep(tk2)) { + if (knegativep(K, tk1) || knegativep(K, tk2)) { klispE_throw_simple(K, "negative index"); return; } diff --git a/src/kgpairs_lists.c b/src/kgpairs_lists.c @@ -239,9 +239,9 @@ void list_tail(klisp_State *K, TValue *xparams, TValue ptree, UNUSED(xparams); UNUSED(denv); bind_2tp(K, ptree, "any", anytype, obj, - "integer", kintegerp, tk); + "exact integer", keintegerp, tk); - if (knegativep(tk)) { + if (knegativep(K, tk)) { klispE_throw_simple(K, "negative index"); return; } @@ -292,9 +292,9 @@ void list_ref(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) UNUSED(xparams); bind_2tp(K, ptree, "any", anytype, obj, - "integer", kintegerp, tk); + "exact integer", keintegerp, tk); - if (knegativep(tk)) { + if (knegativep(K, tk)) { klispE_throw_simple(K, "negative index"); return; } diff --git a/src/kground.c b/src/kground.c @@ -9,6 +9,7 @@ #include <stdlib.h> #include <stdbool.h> #include <stdint.h> +#include <math.h> #include "kstate.h" #include "kobject.h" @@ -46,6 +47,7 @@ #include "keval.h" #include "krepl.h" + /* ** BEWARE: this is highly unhygienic, it assumes variables "symbol" and ** "value", both of type TValue. symbol will be bound to a symbol named by @@ -735,17 +737,17 @@ void kinit_ground_env(klisp_State *K) /* 12.5.2 =? */ add_applicative(K, ground_env, "=?", ftyped_kbpredp, 3, - symbol, p2tv(knumberp), p2tv(knum_eqp)); + symbol, p2tv(knumber_wpvp), p2tv(knum_eqp)); /* 12.5.3 <?, <=?, >?, >=? */ add_applicative(K, ground_env, "<?", ftyped_kbpredp, 3, - symbol, p2tv(krealp), p2tv(knum_ltp)); + symbol, p2tv(kreal_wpvp), p2tv(knum_ltp)); add_applicative(K, ground_env, "<=?", ftyped_kbpredp, 3, - symbol, p2tv(krealp), p2tv(knum_lep)); + symbol, p2tv(kreal_wpvp), p2tv(knum_lep)); add_applicative(K, ground_env, ">?", ftyped_kbpredp, 3, - symbol, p2tv(krealp), p2tv(knum_gtp)); + symbol, p2tv(kreal_wpvp), p2tv(knum_gtp)); add_applicative(K, ground_env, ">=?", ftyped_kbpredp, 3, - symbol, p2tv(krealp), p2tv(knum_gep)); + symbol, p2tv(kreal_wpvp), p2tv(knum_gep)); /* 12.5.4 + */ add_applicative(K, ground_env, "+", kplus, 0); @@ -800,6 +802,45 @@ void kinit_ground_env(klisp_State *K) add_applicative(K, ground_env, "lcm", klcm, 0); /* + ** 12.8 Inexact features + */ + + /* 12.6.1 exact?, inexact?, robust?, undefined? */ + add_applicative(K, ground_env, "exact?", ftyped_predp, 3, symbol, + p2tv(knumberp), p2tv(kexactp)); + add_applicative(K, ground_env, "inexact?", ftyped_predp, 3, symbol, + p2tv(knumberp), p2tv(kinexactp)); + add_applicative(K, ground_env, "robust?", ftyped_predp, 3, symbol, + p2tv(knumberp), p2tv(krobustp)); + add_applicative(K, ground_env, "undefined?", ftyped_predp, 3, symbol, + p2tv(knumberp), p2tv(kundefinedp)); + + /* 12.6.2 get-real-internal-bounds, get-real-exact-bounds */ + add_applicative(K, ground_env, "get-real-internal-bounds", + kget_real_internal_bounds, 0); + add_applicative(K, ground_env, "get-real-exact-bounds", + kget_real_exact_bounds, 0); + + /* 12.6.3 get-real-internal-primary, get-real-exact-primary */ + add_applicative(K, ground_env, "get-real-internal-primary", + kget_real_internal_primary, 0); + add_applicative(K, ground_env, "get-real-exact-primary", + kget_real_exact_primary, 0); + + /* 12.6.4 make-inexact */ + add_applicative(K, ground_env, "make-inexact", kmake_inexact, 0); + + /* 12.6.5 real->inexact, real->exact */ + add_applicative(K, ground_env, "real->inexact", kreal_to_inexact, 0); + add_applicative(K, ground_env, "real->exact", kreal_to_exact, 0); + + /* 12.6.6 with-strict-arithmetic, get-strict-arithmetic? */ + add_applicative(K, ground_env, "with-strict-arithmetic", + kwith_strict_arithmetic, 0); + add_applicative(K, ground_env, "get-strict-arithmetic?", + kget_strict_arithmeticp, 0); + + /* ** 12.8 Rational features */ @@ -828,6 +869,34 @@ void kinit_ground_env(klisp_State *K) add_applicative(K, ground_env, "rationalize", krationalize, 0); add_applicative(K, ground_env, "simplest-rational", ksimplest_rational, 0); + /* + ** 12.9 Real features + */ + + /* 12.9.1 real? */ + add_applicative(K, ground_env, "real?", ftypep, 2, symbol, + p2tv(krealp)); + + /* 12.9.2 exp, log */ + add_applicative(K, ground_env, "exp", kexp, 0); + add_applicative(K, ground_env, "log", klog, 0); + + /* 12.9.3 sin, cos, tan */ + add_applicative(K, ground_env, "sin", ktrig, 1, sin); + add_applicative(K, ground_env, "cos", ktrig, 1, cos); + add_applicative(K, ground_env, "tan", ktrig, 1, tan); + + /* 12.9.4 asin, acos, atan */ + add_applicative(K, ground_env, "asin", katrig, 1, asin); + add_applicative(K, ground_env, "acos", katrig, 1, acos); + add_applicative(K, ground_env, "atan", katan, 0); + + /* 12.9.5 sqrt */ + add_applicative(K, ground_env, "sqrt", ksqrt, 0); + + /* 12.9.6 expt */ + add_applicative(K, ground_env, "expt", kexpt, 0); + /* ** ** 13 Strings diff --git a/src/kgstrings.c b/src/kgstrings.c @@ -24,7 +24,7 @@ #include "kghelpers.h" #include "kgchars.h" /* for kcharp */ #include "kgstrings.h" -#include "kgnumbers.h" /* for kintegerp & knegativep */ +#include "kgnumbers.h" /* for keintegerp & knegativep */ /* 13.1.1? string? */ /* uses typep */ @@ -34,14 +34,14 @@ void make_string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); UNUSED(denv); - bind_al1tp(K, ptree, "integer", kintegerp, tv_s, + bind_al1tp(K, ptree, "exact integer", keintegerp, tv_s, maybe_char); char fill = ' '; if (get_opt_tpar(K, "make-string", K_TCHAR, &maybe_char)) fill = chvalue(maybe_char); - if (knegativep(tv_s)) { + if (knegativep(K, tv_s)) { klispE_throw_simple(K, "negative size"); return; } else if (!ttisfixint(tv_s)) { @@ -71,7 +71,7 @@ void string_ref(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) UNUSED(xparams); UNUSED(denv); bind_2tp(K, ptree, "string", ttisstring, str, - "integer", kintegerp, tv_i); + "exact integer", keintegerp, tv_i); if (!ttisfixint(tv_i)) { /* TODO show index */ @@ -96,7 +96,7 @@ void string_setS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) UNUSED(xparams); UNUSED(denv); bind_3tp(K, ptree, "string", ttisstring, str, - "integer", kintegerp, tv_i, "char", ttischar, tv_ch); + "exact integer", keintegerp, tv_i, "char", ttischar, tv_ch); if (!ttisfixint(tv_i)) { /* TODO show index */ @@ -245,8 +245,8 @@ void substring(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) UNUSED(xparams); UNUSED(denv); bind_3tp(K, ptree, "string", ttisstring, str, - "integer", kintegerp, tv_start, - "integer", kintegerp, tv_end); + "exact integer", keintegerp, tv_start, + "exact integer", keintegerp, tv_end); if (!ttisfixint(tv_start) || ivalue(tv_start) < 0 || ivalue(tv_start) > kstring_size(str)) { diff --git a/src/kobject.c b/src/kobject.c @@ -20,6 +20,10 @@ const TValue ktrue = KTRUE_; const TValue kfalse = KFALSE_; const TValue kepinf = KEPINF_; const TValue keminf = KEMINF_; +const TValue kipinf = KIPINF_; +const TValue kiminf = KIMINF_; +const TValue krwnpv = KRWNPV_; +const TValue kundef = KUNDEF_; const TValue kspace = KSPACE_; const TValue knewline = KNEWLINE_; const TValue kfree = KFREE_; @@ -36,7 +40,9 @@ char *ktv_names[] = { [K_TDOUBLE] = "double", [K_TBDOUBLE] = "bdouble", [K_TIINF] = "iinf", - [K_TRWNPN] = "rwnpn", + + [K_TRWNPV] = "rwnpv", + [K_TUNDEFINED] = "undefined", [K_TCOMPLEX] = "complex", [K_TNIL] = "nil", diff --git a/src/kobject.h b/src/kobject.h @@ -31,6 +31,7 @@ #include <stdbool.h> #include <stdint.h> #include <stdio.h> +#include <math.h> #include "klimits.h" #include "klispconf.h" @@ -130,12 +131,13 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define K_TBIGINT 1 #define K_TFIXRAT 2 #define K_TBIGRAT 3 -#define K_TEINF 4 -#define K_TDOUBLE 5 -#define K_TBDOUBLE 6 +#define K_TDOUBLE 4 +#define K_TBDOUBLE 5 +#define K_TEINF 6 #define K_TIINF 7 -#define K_TRWNPN 8 +#define K_TRWNPV 8 #define K_TCOMPLEX 9 +#define K_TUNDEFINED 10 #define K_TNIL 20 #define K_TIGNORE 21 @@ -164,7 +166,7 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define K_TDEADKEY 60 /* this is used to test for numbers, as returned by ttype */ -#define K_LAST_NUMBER_TYPE K_TCOMPLEX +#define K_LAST_NUMBER_TYPE K_TUNDEFINED /* this is used to if the object is collectable */ #define K_FIRST_GC_TYPE K_TPAIR @@ -176,13 +178,15 @@ typedef struct __attribute__ ((__packed__)) GCheader { ** ** - decide if inexact infinities and reals with no ** primary values are included in K_TDOUBLE -** - For now we will only use fixints, bigints, bigrats and exact infinities +** - All types except complexs, bounded reals and fixrats */ #define K_TAG_FIXINT K_MAKE_VTAG(K_TFIXINT) #define K_TAG_BIGINT K_MAKE_VTAG(K_TBIGINT) #define K_TAG_BIGRAT K_MAKE_VTAG(K_TBIGRAT) #define K_TAG_EINF K_MAKE_VTAG(K_TEINF) #define K_TAG_IINF K_MAKE_VTAG(K_TIINF) +#define K_TAG_RWNPV K_MAKE_VTAG(K_TRWNPV) +#define K_TAG_UNDEFINED K_MAKE_VTAG(K_TUNDEFINED) #define K_TAG_NIL K_MAKE_VTAG(K_TNIL) #define K_TAG_IGNORE K_MAKE_VTAG(K_TIGNORE) @@ -229,15 +233,38 @@ typedef struct __attribute__ ((__packed__)) GCheader { /* Simple types (value in TValue struct) */ #define ttisfixint(o) (tbasetype_(o) == K_TAG_FIXINT) #define ttisbigint(o) (tbasetype_(o) == K_TAG_BIGINT) -#define ttisinteger(o_) ({ int32_t t_ = tbasetype_(o_); \ +#define ttiseinteger(o_) ({ int32_t t_ = tbasetype_(o_); \ t_ == K_TAG_FIXINT || t_ == K_TAG_BIGINT;}) +#define ttisinteger(o) ({ TValue o__ = (o); \ + (ttiseinteger(o__) || \ + (ttisdouble(o__) && (floor(dvalue(o__)) == dvalue(o__))));}) #define ttisbigrat(o) (tbasetype_(o) == K_TAG_BIGRAT) -#define ttisrational(o_) ({ int32_t t_ = tbasetype_(o_); \ - t_ == K_TAG_BIGRAT || t_== K_TAG_BIGINT || \ - t_ == K_TAG_FIXINT;}) -#define ttisnumber(o) (ttype(o) <= K_LAST_NUMBER_TYPE); }) +#define ttisrational(o_) \ + ({ TValue t_ = o_; \ + (ttype(t_) <= K_TBIGRAT) || ttisdouble(t_); }) +#define ttisdouble(o) ((ttag(o) & K_TAG_BASE_MASK) != K_TAG_TAGGED) +#define ttisreal(o) (ttype(o) < K_TCOMPLEX) +#define ttisexact(o_) \ + ({ TValue t_ = o_; \ + (ttiseinf(t_) || ttype(t_) <= K_TBIGRAT); }) +/* MAYBE this is ugly..., maybe add exact/inexact flag, real, rational flag */ +#define ttisinexact(o_) \ + ({ TValue t_ = o_; \ + (ttisundef(t_) || ttisdouble(t_) || ttisrwnpv(t_) || ttisiinf(t_)); }) +/* For now, all inexact numbers are not robust and have -inf & +inf bounds */ +#define ttisrobust(o) (ttisexact(o)) +#define ttisnumber(o) (ttype(o) <= K_LAST_NUMBER_TYPE) #define ttiseinf(o) (tbasetype_(o) == K_TAG_EINF) #define ttisiinf(o) (tbasetype_(o) == K_TAG_IINF) +#define ttisinf(o_) \ + ({ TValue t_ = o_; \ + (ttiseinf(t_) || ttisiinf(t_)); }) +#define ttisrwnpv(o) (tbasetype_(o) == K_TAG_RWNPV) +#define ttisundef(o) (tbasetype_(o) == K_TAG_UNDEFINED) +#define ttisnwnpv(o_) \ + ({ TValue t_ = o_; \ + (ttisundef(t_) || ttisrwnpv(t_)); }) + #define ttisnil(o) (tbasetype_(o) == K_TAG_NIL) #define ttisignore(o) (tbasetype_(o) == K_TAG_IGNORE) #define ttisinert(o) (tbasetype_(o) == K_TAG_INERT) @@ -245,7 +272,6 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define ttisboolean(o) (tbasetype_(o) == K_TAG_BOOLEAN) #define ttischar(o) (tbasetype_(o) == K_TAG_CHAR) #define ttisfree(o) (tbasetype_(o) == K_TAG_FREE) -#define ttisdouble(o) ((ttag(o) & K_TAG_BASE_MASK) != K_TAG_TAGGED) /* Complex types (value in heap), (bigints, rationals, etc could be collectable) @@ -524,6 +550,10 @@ union GCObject { #define KFALSE_ {.tv = {.t = K_TAG_BOOLEAN, .v = { .b = false }}} #define KEPINF_ {.tv = {.t = K_TAG_EINF, .v = { .i = 1 }}} #define KEMINF_ {.tv = {.t = K_TAG_EINF, .v = { .i = -1 }}} +#define KIPINF_ {.tv = {.t = K_TAG_IINF, .v = { .i = 1 }}} +#define KIMINF_ {.tv = {.t = K_TAG_IINF, .v = { .i = -1 }}} +#define KRWNPV_ {.tv = {.t = K_TAG_RWNPV, .v = { .i = 0 }}} +#define KUNDEF_ {.tv = {.t = K_TAG_UNDEFINED, .v = { .i = 0 }}} #define KSPACE_ {.tv = {.t = K_TAG_CHAR, .v = { .ch = ' ' }}} #define KNEWLINE_ {.tv = {.t = K_TAG_CHAR, .v = { .ch = '\n' }}} #define KFREE_ {.tv = {.t = K_TAG_FREE, .v = { .i = 0 }}} @@ -538,6 +568,10 @@ union GCObject { #define KFALSE ((TValue) KFALSE_) #define KEPINF ((TValue) KEPINF_) #define KEMINF ((TValue) KEMINF_) +#define KIPINF ((TValue) KIPINF_) +#define KIMINF ((TValue) KIMINF_) +#define KRWNPV ((TValue) KRWNPV_) +#define KUNDEF ((TValue) KUNDEF_) #define KSPACE ((TValue) KSPACE_) #define KNEWLINE ((TValue) KNEWLINE_) #define KFREE ((TValue) KFREE_) @@ -551,6 +585,10 @@ const TValue ktrue; const TValue kfalse; const TValue kepinf; const TValue keminf; +const TValue kipinf; +const TValue kiminf; +const TValue krwnpv; +const TValue kundef; const TValue kspace; const TValue knewline; const TValue kfree; @@ -560,12 +598,24 @@ const TValue kfree; #define i2tv_(i_) {.tv = {.t = K_TAG_FIXINT, .v = { .i = (i_) }}} #define b2tv_(b_) {.tv = {.t = K_TAG_BOOLEAN, .v = { .b = (b_) }}} #define p2tv_(p_) {.tv = {.t = K_TAG_USER, .v = { .p = (p_) }}} +#define d2tv_(d_) {.d = d_} +#define ktag_double(d_) \ + ({ double d__ = d_; \ + TValue res__; \ + if (isnan(d__)) res__ = KRWNPV; \ + else if (isinf(d__)) res__ = (d__ == INFINITY)? \ + KIPINF : KIMINF; \ + /* +0.0 == -0.0 too, but that doesn't hurt */ \ + else if (d__ == -0.0) res__ = d2tv(+0.0); \ + else res__ = d2tv(d__); \ + res__;}) /* Macros to create TValues of non-heap allocated types */ #define ch2tv(ch_) ((TValue) ch2tv_(ch_)) #define i2tv(i_) ((TValue) i2tv_(i_)) #define b2tv(b_) ((TValue) b2tv_(b_)) #define p2tv(p_) ((TValue) p2tv_(p_)) +#define d2tv(d_) ((TValue) d2tv_(d_)) /* Macros to convert a GCObject * into a tagged value */ /* TODO: add assertions */ @@ -620,6 +670,7 @@ const TValue kfree; #define chvalue(o_) ((o_).tv.v.ch) #define gcvalue(o_) ((o_).tv.v.gc) #define pvalue(o_) ((o_).tv.v.p) +#define dvalue(o_) ((o_).d) /* Macro to obtain a string describing the type of a TValue */# #define ttname(tv_) (ktv_names[ttype(tv_)]) diff --git a/src/kreal.c b/src/kreal.c @@ -0,0 +1,743 @@ +/* +** kreal.c +** Kernel Reals (doubles) +** See Copyright Notice in klisp.h +*/ + +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <inttypes.h> +#include <ctype.h> +#include <math.h> +#include <fenv.h> /* for setting round direction */ + +#include "kreal.h" +#include "krational.h" +#include "kinteger.h" +#include "kobject.h" +#include "kstate.h" +#include "kmem.h" +#include "kgc.h" +#include "kpair.h" /* for list in throw error */ +#include "kerror.h" + +double kbigint_to_double(Bigint *bigint) +{ + double radix = (double) UINT32_MAX + 1.0; + uint32_t ndigits = bigint->used; + double accum = 0.0; + + /* bigint is in little endian format, but we traverse in + big endian */ + do { + --ndigits; + accum = accum * radix + (double) bigint->digits[ndigits]; + } while (ndigits > 0); /* have to compare like this, it's unsigned */ + return mp_int_compare_zero(bigint) < 0? -accum : accum; +} + +/* bigrat is rooted */ +double kbigrat_to_double(klisp_State *K, Bigrat *bigrat) +{ + /* TODO: check rounding in extreme cases */ + TValue tv_rem = kbigrat_copy(K, gc2bigrat(bigrat)); + krooted_tvs_push(K, tv_rem); + Bigrat *rem = tv2bigrat(tv_rem); + UNUSED(mp_rat_abs(K, rem, rem)); + + TValue int_radix = kbigint_make_simple(K); + krooted_tvs_push(K, int_radix); + /* cant do UINT32_MAX and then +1 because _value functions take + int32_t arguments */ + UNUSED(mp_int_set_value(K, tv2bigint(int_radix), INT32_MAX)); + UNUSED(mp_int_add_value(K, tv2bigint(int_radix), 1, + tv2bigint(int_radix))); + UNUSED(mp_int_add(K, tv2bigint(int_radix), tv2bigint(int_radix), + tv2bigint(int_radix))); + + TValue int_part = kbigint_make_simple(K); + krooted_tvs_push(K, int_part); + + double accum = 0.0; + double radix = (double) UINT32_MAX + 1.0; + uint32_t digit = 0; + /* inside there is a check to avoid problem with continuing fractions */ + while(mp_rat_compare_zero(rem) > 0) { + UNUSED(mp_int_div(K, MP_NUMER_P(rem), MP_DENOM_P(rem), + tv2bigint(int_part), NULL)); + + double di = kbigint_to_double(tv2bigint(int_part)); + bool was_zero = di == 0.0; + for (uint32_t i = 0; i < digit; i++) { + di /= radix; + } + /* if last di became 0.0 we will exit next loop, + this is to avoid problem with continuing fractions */ + if (!was_zero && di == 0.0) + break; + + ++digit; + accum += di; + + UNUSED(mp_rat_sub_int(K, rem, tv2bigint(int_part), rem)); + UNUSED(mp_rat_mul_int(K, rem, tv2bigint(int_radix), rem)); + } + krooted_tvs_pop(K); /* int_part */ + krooted_tvs_pop(K); /* int_radix */ + krooted_tvs_pop(K); /* rem */ + + return mp_rat_compare_zero(bigrat) < 0? -accum : accum; +} + +/* TODO test strict arithmetic and throw errors on overflow and underflow? + if set */ +TValue kexact_to_inexact(klisp_State *K, TValue n) +{ + bool strictp = kcurr_strict_arithp(K); + + switch(ttype(n)) { + case K_TFIXINT: + /* NOTE: can't over or underflow, and can't give NaN */ + return d2tv((double) ivalue(n)); + case K_TBIGINT: { + Bigint *bigint = tv2bigint(n); + double d = kbigint_to_double(bigint); + if (strictp && (d == 0.0 || isinf(d) || isnan(d))) { + /* NOTE: bigints can't be zero */ + char *msg; + if (isnan(d)) + msg = "unexpected error"; + else if (isinf(d)) + msg = "overflow"; + else + msg = "undeflow"; + klispE_throw_simple_with_irritants(K, msg, 1, n); + return KUNDEF; + } else { + /* d may be inf, ktag_double will handle it */ + return ktag_double(d); + } + } + case K_TBIGRAT: { + Bigrat *bigrat = tv2bigrat(n); + double d = kbigrat_to_double(K, bigrat); + /* REFACTOR: this code is the same for bigints... */ + if (strictp && (d == 0.0 || isinf(d) || isnan(d))) { + /* NOTE: bigrats can't be zero */ + char *msg; + if (isnan(d)) + msg = "unexpected error"; + else if (isinf(d)) + msg = "overflow"; + else + msg = "undeflow"; + klispE_throw_simple_with_irritants(K, msg, 1, n); + return KUNDEF; + } else { + /* d may be inf, ktag_double will handle it */ + return ktag_double(d); + } + } + case K_TEINF: + return tv_equal(n, KEPINF)? KIPINF : KIMINF; + /* all of these are already inexact */ + case K_TDOUBLE: + case K_TIINF: + case K_TRWNPV: + case K_TUNDEFINED: + return n; + default: + klisp_assert(0); + return KUNDEF; + } +} + +/* assume d is integer and doesn't fit in a fixint */ +TValue kdouble_to_bigint(klisp_State *K, double d) +{ + bool neg = d < 0; + if (neg) + d = -d; + + TValue tv_res = kbigint_make_simple(K); + krooted_tvs_push(K, tv_res); + Bigint *res = tv2bigint(tv_res); + mp_int_set_value(K, res, 0); + + TValue tv_digit = kbigint_make_simple(K); + krooted_tvs_push(K, tv_digit); + Bigint *digit = tv2bigint(tv_digit); + + /* do it 32 bits at a time */ + double radix = ((double) UINT32_MAX) + 1.0; + int power = 0; + while(d > 0) { + double dd = fmod(d, radix); + d = floor(d / radix); + /* load in two moves because set_value takes signed ints */ + uint32_t id = (uint32_t) dd; + int32_t id1 = (int32_t) (id >> 1); + int32_t id2 = (int32_t) (id - id1); + + mp_int_set_value(K, digit, id1); + mp_int_add_value(K, digit, id2, digit); + + mp_int_mul_pow2(K, digit, power, digit); + mp_int_add(K, res, digit, res); + + power += 32; + } + + if (neg) + mp_int_neg(K, res, res); + + krooted_tvs_pop(K); /* digit */ + krooted_tvs_pop(K); /* res */ + + return tv_res; /* can't be fixint except when coming from + kdouble_to_bigrat, so don't convert */ +} + +/* TODO: should do something like rationalize with range +/- 1/2ulp) */ +TValue kdouble_to_bigrat(klisp_State *K, double d) +{ + bool neg = d < 0; + if (neg) + d = -d; + + /* find an integer, convert it and divide by + an adequate power of 2 */ + TValue tv_res = kbigrat_make_simple(K); + krooted_tvs_push(K, tv_res); + Bigrat *res = tv2bigrat(tv_res); + UNUSED(mp_rat_set_value(K, res, 0, 1)); + + TValue tv_den = kbigint_make_simple(K); + krooted_tvs_push(K, tv_den); + Bigint *den = tv2bigint(tv_den); + UNUSED(mp_int_set_value(K, den, 1)); + + /* XXX could be made a lot more efficiently reading ieee + fields directly */ + int ie; + d = frexp(d, &ie); + + while(d != floor(d)) { + d *= 2.0; + --ie; + } + UNUSED(mp_int_mul_pow2(K, den, -ie, den)); + + TValue tv_num = kdouble_to_bigint(K, d); + krooted_tvs_push(K, tv_num); + Bigint *num = tv2bigint(tv_num); + + TValue tv_den2 = kbigrat_make_simple(K); + krooted_tvs_push(K, tv_den2); + Bigrat *den2 = tv2bigrat(tv_den2); + + UNUSED(mp_rat_set_value(K, den2, 0, 1)); + UNUSED(mp_rat_add_int(K, den2, den, den2)); + UNUSED(mp_rat_set_value(K, res, 0, 1)); + UNUSED(mp_rat_add_int(K, res, num, res)); + UNUSED(mp_rat_div(K, res, den2, res)); + + if (neg) + UNUSED(mp_rat_neg(K, res, res)); + + /* now create a value corresponding to 1/2 ulp + for rationalize */ + UNUSED(mp_int_mul_pow2(K, den, 1, den)); + UNUSED(mp_rat_set_value(K, den2, 0, 1)); + UNUSED(mp_rat_add_int(K, den2, den, den2)); + UNUSED(mp_rat_recip(K, den2, den2)); + /* den2 now has 1/2 ulp */ + + TValue rationalized = kbigrat_rationalize(K, tv_res, tv_den2); + + krooted_tvs_pop(K); /* den2 */ + krooted_tvs_pop(K); /* num */ + krooted_tvs_pop(K); /* den */ + krooted_tvs_pop(K); /* res */ + + return rationalized; +} + +TValue kinexact_to_exact(klisp_State *K, TValue n) +{ + switch(ttype(n)) { + case K_TFIXINT: + case K_TBIGINT: + case K_TBIGRAT: + case K_TEINF: + /* all of these are already exact */ + return n; + case K_TDOUBLE: { + double d = dvalue(n); + klisp_assert(!isnan(d) && !isinf(d)); + if (d == floor(d)) { /* integer */ + if (d <= (double) INT32_MAX && + d >= (double) INT32_MIN) { + return i2tv((int32_t) d); /* fixint */ + } else { + return kdouble_to_bigint(K, d); + } + } else { /* non integer rational */ + return kdouble_to_bigrat(K, d); + } + } + case K_TIINF: + return tv_equal(n, KIPINF)? KEPINF : KEMINF; + case K_TRWNPV: + case K_TUNDEFINED: + klispE_throw_simple_with_irritants(K, "no primary value", 1, n); + return KUNDEF; + default: + klisp_assert(0); + return KUNDEF; + } +} + +/* +** read/write interface +*/ + +/* +** SOURCE NOTE: This is a more or less vanilla implementation of the algorithm +** described in "How to Print Floating-Point Numbers Accurately" by +** Guy L. Steele Jr. & John L. White. +*/ + +/* +** TODO add awareness of read rounding (e.g. problem with 1.0e23) +** TODO add exponent if too small or too big +*/ + +mp_result shift_2(klisp_State *K, Bigint *x, Bigint *n, Bigint *r) +{ + mp_small nv; + mp_result res = mp_int_to_int(n, &nv); + klisp_assert(res == MP_OK); + + if (nv >= 0) + return mp_int_mul_pow2(K, x, nv, r); + else + return mp_int_div_pow2(K, x, -nv, r, NULL); +} + +/* returns k, modifies all parameters (except f & p) */ +int32_t simple_fixup(klisp_State *K, Bigint *f, Bigint *p, Bigint *r, + Bigint *s, Bigint *mm, Bigint *mp) +{ + mp_result res; + Bigint tmpz, tmpz2; + Bigint *tmp = &tmpz; + Bigint *tmp2 = &tmpz2; + Bigint onez; + Bigint *one = &onez; + res = mp_int_init(tmp); + res = mp_int_init(tmp2); + res = mp_int_init_value(K, one, 1); + res = mp_int_sub(K, p, one, tmp); + res = shift_2(K, one, tmp, tmp); + + if (mp_int_compare(f, tmp) == 0) { + res = shift_2(K, mp, one, mp); + res = shift_2(K, r, one, r); + res = shift_2(K, s, one, s); + } + + int k = 0; + + /* tmp = ceiling (s/10), for while guard */ + res = mp_int_add_value(K, s, 9, tmp); + res = mp_int_div_value(K, tmp, 10, tmp, NULL); + + while(mp_int_compare(r, tmp) < 0) { + --k; + res = mp_int_mul_value(K, r, 10, r); + res = mp_int_mul_value(K, mm, 10, mm); + res = mp_int_mul_value(K, mp, 10, mp); + /* tmp = ceiling (s/10), for while guard */ + res = mp_int_add_value(K, s, 9, tmp); + res = mp_int_div_value(K, tmp, 10, tmp, NULL); + } + + /* tmp = 2r + mp; tmp2 = 2s */ + res = mp_int_mul_value(K, r, 2, tmp); + res = mp_int_add(K, tmp, mp, tmp); + res = mp_int_mul_value(K, s, 2, tmp2); + while(mp_int_compare(tmp, tmp2) >= 0) { + + res = mp_int_mul_value(K, s, 10, s); + ++k; + + /* tmp = 2r + mp; tmp2 = 2s */ + res = mp_int_mul_value(K, r, 2, tmp); + res = mp_int_add(K, tmp, mp, tmp); + res = mp_int_mul_value(K, s, 2, tmp2); + } + + mp_int_clear(K, tmp); + mp_int_clear(K, tmp2); + mp_int_clear(K, one); + return k; +} + +/* TEMP: for now upoint is passed indicating where the least significant + integer digit should be (10^0 position) */ +#define digit_pos(k_, upoint_) ((k_) + (upoint_)) + +bool dtoa(klisp_State *K, double d, char *buf, int32_t upoint, int32_t *out_h, + int32_t *out_k) +{ + assert(sizeof(mp_small) == 4); + mp_result res; + Bigint e, p, f; + + assert(d > 0.0); + + /* convert d to three bigints m: significand, e: exponent & p: precision */ + /* d = m^(e-p) & m < 2^p */ + int ie, ip; + double mantissa = frexp(d, &ie); + ip = 0; + + klisp_assert(mantissa * pow(2.0, ie) == d); + /* now 0.5 <= mantissa < 1 & mantissa * 2^expt = d */ +/* this could be a binary search, it could also be done reading the exponent + field of ieee754 directly... */ + while(mantissa != floor(mantissa)) { + mantissa *= 2.0; + ++ip; + } + + /* mantissa is int & < 2^ip (was < 1=2^0 and by induction...) */ + klisp_assert(mantissa * pow(2.0, ie - ip) == d); + /* mantissa is at most 53 bits long as an int, load it in two parts + to f */ + int64_t im = (int64_t) mantissa; + /* f */ + /* cant load 32 bits at a time, second param is signed!, + but we know it's positive so load 32 then 31 */ + res = mp_int_init_value(K, &f, (mp_small) (im >> 31)); + res = mp_int_mul_pow2(K, &f, 31, &f); + res = mp_int_add_value(K, &f, (mp_small) im & 0x7fffffff, &f); + + /* adjust f & p so that p is 53 TODO do in one step */ + /* XXX: is this is ok for denorms?? */ + while(ip < 53) { + ++ip; + res = mp_int_mul_value(K, &f, 2, &f); + } + + /* e */ + res = mp_int_init_value(K, &e, (mp_small) ie); + + /* p */ + res = mp_int_init_value(K, &p, (mp_small) ip); + + /* start of FPP^2 algorithm */ + Bigint r, s; + Bigint mp, mm; + Bigint e_p; + Bigint zero, one; + + res = mp_int_init_value(K, &zero, 0); + res = mp_int_init_value(K, &one, 1); + + res = mp_int_init(&r); + res = mp_int_init(&s); + res = mp_int_init(&mm); + res = mp_int_init(&mp); + res = mp_int_init(&e_p); + + res = mp_int_sub(K, &e, &p, &e_p); + +// shift_2(f, max(e-p, 0), r); +// shift_2(1, max(-(e-p), 0), r); + if (mp_int_compare_zero(&e_p) >= 0) { + res = shift_2(K, &f, &e_p, &r); + res = shift_2(K, &one, &zero, &s); /* nop */ + res = shift_2(K, &one, &e_p, &mm); + } else { + res = shift_2(K, &f, &zero, &r); /* nop */ + res = mp_int_neg(K, &e_p, &e_p); + res = shift_2(K, &one, &e_p, &s); + res = shift_2(K, &one, &zero, &mm); + } + mp_int_copy(K, &mm, &mp); + + int32_t k = simple_fixup(K, &f, &p, &r, &s, &mm, &mp); + int32_t h = k-1; + + Bigint u, tmp, tmp2; + res = mp_int_init(&u); + res = mp_int_init(&tmp); + res = mp_int_init(&tmp2); + bool low, high; + + do { + --k; + res = mp_int_mul_value(K, &r, 10, &tmp); + res = mp_int_div(K, &tmp, &s, &u, &r); + res = mp_int_mul_value(K, &mm, 10, &mm); + res = mp_int_mul_value(K, &mp, 10, &mp); + + /* low/high flags */ + /* XXX try to make 1e23 round correctly, + it causes tmp == tmp2 but should probably + check oddness of digit and (may result in a digit + with value 10?, needing to backtrack) + In general make it so that if rounding done at reading + (should be round to even) is accounted for and the minimal + length number is generated */ + + res = mp_int_mul_value(K, &r, 2, &tmp); + + low = mp_int_compare(&tmp, &mm) < 0; + + res = mp_int_mul_value(K, &s, 2, &tmp2); + res = mp_int_sub(K, &tmp2, &mp, &tmp2); + + high = mp_int_compare(&tmp, &tmp2) > 0; + + if (!low && !high) { + mp_small digit; + res = mp_int_to_int(&u, &digit); + klisp_assert(res == MP_OK); + klisp_assert(digit >= 0 && digit <= 9); + buf[digit_pos(k, upoint)] = '0' + digit; + } + } while(!low && !high); + + mp_small digit; + res = mp_int_to_int(&u, &digit); + klisp_assert(res == MP_OK); + klisp_assert(digit >= 0 && digit <= 9); + + if (low && high) { + res = mp_int_mul_value(K, &r, 2, &tmp); + int cmp = mp_int_compare(&tmp, &s); + if ((cmp == 0 && (digit & 1) != 0) || cmp > 0) + ++digit; + } else if (low) { + /* nothing */ + } else if (high) { + ++digit; + } else { + assert(0); + } + /* double check in case there was an increment */ + klisp_assert(digit >= 0 && digit <= 9); + buf[digit_pos(k, upoint)] = '0' + digit; + + *out_h = h; + *out_k = k; + /* add '\0' to both sides */ + buf[digit_pos(k-1, upoint)] = '\0'; + buf[digit_pos(h+1, upoint)] = '\0'; + + mp_int_clear(K, &f); + mp_int_clear(K, &e); + mp_int_clear(K, &p); + mp_int_clear(K, &r); + mp_int_clear(K, &s); + mp_int_clear(K, &mp); + mp_int_clear(K, &mm); + mp_int_clear(K, &e_p); + mp_int_clear(K, &zero); + mp_int_clear(K, &one); + mp_int_clear(K, &u); + mp_int_clear(K, &tmp); + mp_int_clear(K, &tmp2); + + /* NOTE: digits are reversed! */ + return true; +} + + +/* TEMP: this is a stub for now, always return sufficiently large + number */ +int32_t kdouble_print_size(TValue tv_double) +{ + UNUSED(tv_double); + return 1024; +} + +void kdouble_print_string(klisp_State *K, TValue tv_double, + char *buf, int32_t limit) +{ + /* TODO: add exponent to values too large or too small */ + /* TEMP */ + int32_t h = 0; + int32_t k = 0; + int32_t upoint = limit/2; + double od = dvalue(tv_double); + klisp_assert(!isnan(od) && !isinf(od)); + klisp_assert(limit > 10); + + /* dtoa only works for d > 0 */ + if (od == 0.0) { + buf[0] = '0'; + buf[1] = '.'; + buf[2] = '0'; + buf[3] = '\0'; + return; + } + + double d; + if (od < 0.0) + d = -od; + else d = od; + + /* XXX this doesn't check limit, it should be large enough */ + UNUSED(dtoa(K, d, buf, upoint, &h, &k)); + + klisp_assert(upoint + k >= 0 && upoint + h + 1 < limit); + + /* rearrange the digits */ + /* TODO do this more efficiently */ + + + int32_t size = h - k + 1; + int32_t start = upoint+k; + /* first reverse the digits */ + for (int32_t i = upoint+k, j = upoint+h; i < j; i++, j--) { + char ch = buf[i]; + buf[i] = buf[j]; + buf[j] = ch; + } + + /* TODO use exponents */ + + /* if necessary make room for leading zeros and sign, + move all to the left */ + + int extra_size = (od < 0? 1 : 0) + (h < 0? 2 + (-h-1) : 0); + + klisp_assert(extra_size + size + 2 < limit); + memmove(buf+extra_size, buf+start, size); + + int32_t i = 0; + if (od < 0) + buf[i++] = '-'; + + if (h < 0) { + /* fraction with leading 0. and with possibly more leading zeros */ + buf[i++] = '0'; + buf[i++] = '.'; + for (int32_t j = -1; j > h; j--) { + buf[i++] = '0'; + } + int frac_size = size; + i += frac_size; + buf[i++] = '\0'; + } else if (k >= 0) { + /* integer with possibly trailing zeros */ + klisp_assert(size+extra_size+k+4 < limit); + int int_size = size; + i += int_size; + for (int32_t j = 0; j < k; j++) { + buf[i++] = '0'; + } + buf[i++] = '.'; + buf[i++] = '0'; + buf[i++] = '\0'; + } else { /* both integer and fractional part, make room for the point */ + /* k < 0, h >= 0 */ + int32_t int_size = h+1; + int32_t frac_size = -k; + memmove(buf+i+int_size+1, buf+i+int_size, frac_size); + i += int_size; + buf[i++] = '.'; + i += frac_size; + buf[i++] = '\0'; + } + return; +} + +double kdouble_div_mod(double n, double d, double *res_mod) +{ + double div = floor(n / d); + double mod = fmod(n, d); + + /* div, mod or div-and-mod */ + /* 0 <= mod0 < |d| */ + if (mod < 0.0) { + if (d < 0.0) { + mod -= d; + div += 1.0; + } else { + mod += d; + div -= 1.0; + } + } + *res_mod = mod; + return div; +} + +double kdouble_div0_mod0(double n, double d, double *res_mod) +{ + double div = floor(n / d); + double mod = fmod(n, d); + + /* div0, mod0 or div-and-mod0 */ + /* + ** Adjust q and r so that: + ** -|d/2| <= mod0 < |d/2| which is the same as + ** dmin <= mod0 < dmax, where + ** dmin = -|d/2| and dmax = |d/2| + */ + double dmin = -((d<0.0? -d : d) / 2.0); + double dmax = (d<0.0? -d : d) / 2.0; + + if (mod < dmin) { + if (d < 0) { + mod -= d; + div += 1.0; + } else { + mod += d; + div -= 1.0; + } + } else if (mod >= dmax) { + if (d < 0) { + mod += d; + div += 1.0; + } else { + mod -= d; + div -= 1.0; + } + } + *res_mod = mod; + return div; +} + +TValue kdouble_to_integer(klisp_State *K, TValue tv_double, kround_mode mode) +{ + double d = dvalue(tv_double); + switch(mode) { + case K_TRUNCATE: + d = trunc(d); + break; + case K_CEILING: + d = ceil(d); + break; + case K_FLOOR: + d = floor(d); + break; + case K_ROUND_EVEN: { + int res = fesetround(FE_TONEAREST); /* REFACTOR: should be done once only... */ + klisp_assert(res == 0); + d = nearbyint(d); + } + } + /* ASK John: we currently return inexact if given inexact is this ok? + or should it return an exact integer? */ + return ktag_double(d); +#if 0 + tv_double = ktag_double(d); /* won't alloc mem so no need to root */ + return kinexact_to_exact(K, tv_double); +#endif +} diff --git a/src/kreal.h b/src/kreal.h @@ -0,0 +1,38 @@ +/* +** kreal.c +** Kernel Reals (doubles) +** See Copyright Notice in klisp.h +*/ + +#ifndef kreal_h +#define kreal_h + +#include <stdbool.h> +#include <stdint.h> +#include <inttypes.h> + +#include "kobject.h" +#include "kstate.h" +#include "kinteger.h" +#include "krational.h" +#include "imrat.h" + +/* REFACTOR rename. These can take any real, but + kreal_to_... is taken by kgnumbers... */ +TValue kexact_to_inexact(klisp_State *K, TValue n); +TValue kinexact_to_exact(klisp_State *K, TValue n); + + +double kdouble_div_mod(double n, double d, double *res_mod); +double kdouble_div0_mod0(double n, double d, double *res_mod); + +TValue kdouble_to_integer(klisp_State *K, TValue tv_double, kround_mode mode); + +/* +** read/write interface +*/ +int32_t kdouble_print_size(TValue tv_double); +void kdouble_print_string(klisp_State *K, TValue tv_double, + char *buf, int32_t limit); + +#endif diff --git a/src/kstate.c b/src/kstate.c @@ -91,6 +91,10 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { K->kd_in_port_key = KINERT; K->kd_out_port_key = KINERT; + /* strict arithmetic dynamic key */ + /* this is init later */ + K->kd_strict_arith_key = KINERT; + /* GC */ K->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); K->gcstate = GCSpause; @@ -188,6 +192,9 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { K->kd_in_port_key = kcons(K, KTRUE, in_port); K->kd_out_port_key = kcons(K, KTRUE, out_port); + /* strict arithmetic key, (starts as false) */ + K->kd_strict_arith_key = kcons(K, KTRUE, KFALSE); + /* create the ground environment and the eval operative */ int32_t line_number; TValue si; diff --git a/src/kstate.h b/src/kstate.h @@ -107,6 +107,9 @@ struct klisp_State { TValue kd_in_port_key; TValue kd_out_port_key; + /* for strict-arithmetic */ + TValue kd_strict_arith_key; + /* Strings */ TValue empty_string; @@ -485,5 +488,14 @@ void klisp_close (klisp_State *K); void do_interception(klisp_State *K, TValue *xparams, TValue obj); +/* simple accessors for dynamic keys */ + +/* XXX: this is ugly but we can't include kpair.h here so... */ +/* MAYBE: move car & cdr to kobject.h */ +/* TODO: use these where appropriate */ +#define kcurr_input_port(K) (tv2pair((K)->kd_in_port_key)->cdr) +#define kcurr_output_port(K) (tv2pair((K)->kd_out_port_key)->cdr) +#define kcurr_strict_arithp(K) bvalue(tv2pair((K)->kd_strict_arith_key)->cdr) + #endif diff --git a/src/ksymbol.c b/src/ksymbol.c @@ -10,7 +10,6 @@ #include "kobject.h" /* for identifier checking */ #include "ktoken.h" -#include "kpair.h" #include "kstate.h" #include "kmem.h" #include "kgc.h" diff --git a/src/ktoken.c b/src/ktoken.c @@ -38,6 +38,7 @@ #include "kstate.h" #include "kinteger.h" #include "krational.h" +#include "kreal.h" #include "kpair.h" #include "kstring.h" #include "ksymbol.h" @@ -399,14 +400,19 @@ TValue ktok_read_number(klisp_State *K, char *buf, int32_t len, { UNUSED(len); /* not needed really, buf ends with '\0' */ TValue n; - if (has_exactp && radix == 10) { - /* TEMP: while there are no inexacts */ - /* allow decimals if has #e prefix */ - if (!krational_read_decimal(K, buf, radix, &n, NULL, NULL)) { + if (radix == 10) { + /* only allow decimals with radix 10 */ + bool decimalp = false; + if (!krational_read_decimal(K, buf, radix, &n, NULL, &decimalp)) { /* TODO throw meaningful error msgs, use last param */ ktok_error(K, "Bad format in number"); return KINERT; } + if (decimalp && !has_exactp) { + /* handle decimal format as an explicit #i */ + has_exactp = true; + exactp = false; + } } else { if (!krational_read(K, buf, radix, &n, NULL)) { /* TODO throw meaningful error msgs, use last param */ @@ -415,6 +421,12 @@ TValue ktok_read_number(klisp_State *K, char *buf, int32_t len, } } ks_tbclear(K); + + if (has_exactp && !exactp) { + krooted_tvs_push(K, n); + n = kexact_to_inexact(K, n); + krooted_tvs_pop(K); + } return n; } @@ -518,8 +530,10 @@ struct kspecial_token { { "#inert", KINERT_ }, { "#e+infinity", KEPINF_ }, { "#e-infinity", KEMINF_ }, - /* TODO add undefined, real with on primary value, - and inexact infinities */ + { "#i+infinity", KIPINF_ }, + { "#i-infinity", KIMINF_ }, + { "#real", KRWNPV_ }, + { "#undefined", KUNDEF_ }, { "#\\space", KSPACE_ }, { "#\\newline", KNEWLINE_ } }; @@ -691,10 +705,7 @@ TValue ktok_read_special(klisp_State *K) case '5': case '6': case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case '+': case '-': { /* read the number */ - if (has_exactp && !exactp) { - ktok_error(K, "inexact numbers not supported"); - return KINERT; - } else if (idx == buf_len) { + if (idx == buf_len) { ktok_error(K, "no digits found in number"); } else { return ktok_read_number(K, buf+idx, buf_len - idx, diff --git a/src/kwrite.c b/src/kwrite.c @@ -8,11 +8,13 @@ #include <stdlib.h> #include <assert.h> #include <inttypes.h> +#include <string.h> #include "kwrite.h" #include "kobject.h" #include "kinteger.h" #include "krational.h" +#include "kreal.h" #include "kpair.h" #include "kstring.h" #include "ksymbol.h" @@ -90,6 +92,23 @@ void kw_print_bigrat(klisp_State *K, TValue bigrat) krooted_tvs_pop(K); } +void kw_print_double(klisp_State *K, TValue tv_double) +{ + int32_t size = kdouble_print_size(tv_double); + krooted_tvs_push(K, tv_double); + /* here we are using 1 byte extra, because size already includes + 1 for the terminator, but better be safe than sorry */ + TValue buf_str = kstring_new_s(K, size); + krooted_tvs_push(K, buf_str); + + char *buf = kstring_buf(buf_str); + kdouble_print_string(K, tv_double, buf, size); + kw_printf(K, "%s", buf); + + krooted_tvs_pop(K); + krooted_tvs_pop(K); +} + /* ** Helper for printing strings (correcly escapes backslashes and ** double quotes & prints embedded '\0's). It includes the surrounding @@ -271,9 +290,6 @@ void kwrite_simple(klisp_State *K, TValue obj) kwrite_error(K, "string type found in kwrite-simple"); /* avoid warning */ return; - case K_TEINF: - kw_printf(K, "#e%cinfinity", tv_equal(obj, KEPINF)? '+' : '-'); - break; case K_TFIXINT: kw_printf(K, "%" PRId32, ivalue(obj)); break; @@ -283,6 +299,23 @@ void kwrite_simple(klisp_State *K, TValue obj) case K_TBIGRAT: kw_print_bigrat(K, obj); break; + case K_TEINF: + kw_printf(K, "#e%cinfinity", tv_equal(obj, KEPINF)? '+' : '-'); + break; + case K_TIINF: + kw_printf(K, "#i%cinfinity", tv_equal(obj, KIPINF)? '+' : '-'); + break; + case K_TDOUBLE: { + kw_print_double(K, obj); + break; + } + case K_TRWNPV: + /* ASK John/TEMP: until John tells me what should this be... */ + kw_printf(K, "#real"); + break; + case K_TUNDEFINED: + kw_printf(K, "#undefined"); + break; case K_TNIL: kw_printf(K, "()"); break;