commit 2fef85618d6017a883269993b109f4840f42b34f
parent ba3e6bebfcb7f348fb380665a25ee68ed38a44d3
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Sat, 23 Apr 2011 18:40:08 -0300
Added support for bigrats to +, - & *. Added / & rational?. Bugfixes: changed the types of some applicatives from number to real.
Diffstat:
5 files changed, 244 insertions(+), 28 deletions(-)
diff --git a/src/kgnumbers.c b/src/kgnumbers.c
@@ -18,24 +18,26 @@
 #include "kerror.h"
 #include "ksymbol.h"
 #include "kinteger.h"
- #include "krational.h"
-
- #include "kghelpers.h"
- #include "kgnumbers.h"
-
- /* 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; }
- /* This is used in gcd & lcm */
- bool kimp_intp(TValue obj) { return ttisinteger(obj) || ttiseinf(obj); }
- /* obj is known to be a number */
- bool kfinitep(TValue obj) { return (!ttiseinf(obj) && !ttisiinf(obj)); }
- /* TEMP: for now only fixint & bigints, should also include inexact 
-    integers */
+#include "krational.h"
+
+#include "kghelpers.h"
+#include "kgnumbers.h"
+
+/* 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; }
+/* This is used in gcd & lcm */
+bool kimp_intp(TValue obj) { return ttisinteger(obj) || ttiseinf(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 kintegerp(TValue obj) { return ttisinteger(obj); }
 bool krationalp(TValue obj) { return ttisrational(obj); }
+/* all real are rationals in klisp */
+bool krealp(TValue obj) { return ttisrational(obj); }
 
  /* 12.5.2 =? */
  /* uses typed_bpredp */
@@ -157,6 +159,11 @@ bool krationalp(TValue obj) { return ttisrational(obj); }
 	 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;
@@ -191,6 +198,11 @@ bool krationalp(TValue obj) { return ttisrational(obj); }
 	 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)) {
@@ -224,6 +236,11 @@ bool krationalp(TValue obj) { return ttisrational(obj); }
 	 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);
@@ -240,6 +257,48 @@ bool krationalp(TValue obj) { return ttisrational(obj); }
     }
 }
 
+ /* 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(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(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(K, "/: unsopported type");
+	 return KINERT;
+     }
+}
+
 /* GC: assumes n rooted */
 TValue knum_abs(klisp_State *K, TValue n)
 {
@@ -623,8 +682,8 @@ void kdiv_mod(klisp_State *K, TValue *xparams, TValue ptree, TValue denv)
 
     UNUSED(denv);
 
-    bind_2tp(K, name, ptree, "number", knumberp, tv_n,
-	     "number", knumberp, tv_d);
+    bind_2tp(K, name, ptree, "number", krealp, tv_n,
+	     "number", krealp, tv_d);
 
     TValue tv_div, tv_mod;
 
@@ -895,3 +954,95 @@ void klcm(klisp_State *K, TValue *xparams, TValue ptree, TValue denv)
     kapply_cc(K, res);
 }
 
+
+/* TODO: remaining of rational module */
+
+/* 12.8.1 rational? */
+/* uses ftypep */
+
+/* 12.8.2 / */
+void kdivided(klisp_State *K, TValue *xparams, TValue ptree, TValue denv)
+{
+    UNUSED(denv);
+    UNUSED(xparams);
+    /* cycles are allowed, loop counting pairs */
+    int32_t cpairs;
+    
+    /* / in kernel (and unlike in scheme) requires at least 2 arguments */
+    if (!ttispair(ptree) || !ttispair(kcdr(ptree))) {
+	klispE_throw(K, "/: at least two values are required");
+	return;
+    } else if (!knumberp(kcar(ptree))) {
+	klispE_throw(K, "/: bad type on first argument (expected number)");
+	return;
+    }
+    TValue first_val = kcar(ptree);
+    int32_t pairs = check_typed_list(K, "/", "number", knumberp, true,
+				     kcdr(ptree), &cpairs);
+    int32_t apairs = pairs - cpairs;
+
+    TValue res;
+
+    /* first the acyclic part */
+    TValue ares = i2tv(1);
+    TValue tail = kcdr(ptree);
+
+    krooted_vars_push(K, &ares);
+
+    while(apairs--) {
+	TValue first = kcar(tail);
+	tail = kcdr(tail);
+	ares = knum_times(K, ares, first);
+    }
+
+    /* next the cyclic part */
+    TValue cres = i2tv(1);
+
+    if (cpairs == 0) {
+	/* speed things up if there is no cycle */
+	res = ares;
+	krooted_vars_pop(K);
+    } else {
+	bool all_one = true;
+
+	krooted_vars_push(K, &cres);
+	while(cpairs--) {
+	    TValue first = kcar(tail);
+	    tail = kcdr(tail);
+	    all_one = all_one && kfast_onep(first);
+	    cres = knum_times(K, cres, first);
+	}
+
+	/* think of cres as the product of an infinite series */
+	if (kfast_zerop(cres)) 
+	    ; /* do nothing */
+	else if (kpositivep(cres) && knum_ltp(K, cres, i2tv(1)))
+	    cres = i2tv(0);
+	else if (kfast_onep(cres)) {
+	    if (all_one)
+		cres = i2tv(1);
+	    else {
+		klispE_throw(K, "/: result has no primary value");
+		return;
+	    }
+	} 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(K, "/: result has no primary value");
+	    return;
+	}
+
+	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);
+    krooted_tvs_pop(K);
+
+    kapply_cc(K, res);
+}
diff --git a/src/kgnumbers.h b/src/kgnumbers.h
@@ -28,6 +28,7 @@ bool knumberp(TValue obj);
 bool kfinitep(TValue obj);
 bool kintegerp(TValue obj);
 bool krationalp(TValue obj);
+bool krealp(TValue obj);
 
 
 /* 12.5.2 =? */
@@ -110,6 +111,20 @@ 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.8.1 rational? */
+/* uses ftypep */
+
+/* 12.8.2 / */
+void kdivided(klisp_State *K, TValue *xparams, TValue ptree, TValue denv);
+
+/* 12.8.3 numerator, denominator */
+/* TODO */
+
+/* 12.8.4 floor, ceiling, truncate, round */
+/* TODO */
+
+/* 12.8.5 rationalize, simplest-rational */
+/* TODO */
 
 /* REFACTOR: These should be in a knumber.h header */
 
diff --git a/src/kground.c b/src/kground.c
@@ -671,24 +671,21 @@ void kinit_ground_env(klisp_State *K)
     
     /* 12.5.3 <?, <=?, >?, >=? */
     add_applicative(K, ground_env, "<?", ftyped_kbpredp, 3,
-		    symbol, p2tv(knumberp), p2tv(knum_ltp));
+		    symbol, p2tv(krealp), p2tv(knum_ltp));
     add_applicative(K, ground_env, "<=?", ftyped_kbpredp, 3,
-		    symbol, p2tv(knumberp),  p2tv(knum_lep));
+		    symbol, p2tv(krealp),  p2tv(knum_lep));
     add_applicative(K, ground_env, ">?", ftyped_kbpredp, 3,
-		    symbol, p2tv(knumberp), p2tv(knum_gtp));
+		    symbol, p2tv(krealp), p2tv(knum_gtp));
     add_applicative(K, ground_env, ">=?", ftyped_kbpredp, 3,
-		    symbol, p2tv(knumberp), p2tv(knum_gep));
+		    symbol, p2tv(krealp), p2tv(knum_gep));
 
     /* 12.5.4 + */
-    /* TEMP: for now only accept two arguments */
     add_applicative(K, ground_env, "+", kplus, 0);
 
     /* 12.5.5 * */
-    /* TEMP: for now only accept two arguments */
     add_applicative(K, ground_env, "*", ktimes, 0);
 
     /* 12.5.6 - */
-    /* TEMP: for now only accept two arguments */
     add_applicative(K, ground_env, "-", kminus, 0);
 
     /* 12.5.7 zero? */
@@ -713,9 +710,9 @@ void kinit_ground_env(klisp_State *K)
 
     /* 12.5.10 positive?, negative? */
     add_applicative(K, ground_env, "positive?", ftyped_predp, 3, symbol, 
-		    p2tv(knumberp), p2tv(kpositivep));
+		    p2tv(krealp), p2tv(kpositivep));
     add_applicative(K, ground_env, "negative?", ftyped_predp, 3, symbol, 
-		    p2tv(knumberp), p2tv(knegativep));
+		    p2tv(krealp), p2tv(knegativep));
 
     /* 12.5.11 odd?, even? */
     add_applicative(K, ground_env, "odd?", ftyped_predp, 3, symbol, 
@@ -734,6 +731,20 @@ void kinit_ground_env(klisp_State *K)
     add_applicative(K, ground_env, "gcd", kgcd, 0);
     add_applicative(K, ground_env, "lcm", klcm, 0);
 
+    /* 
+    ** 12.8 Rational features
+    */
+
+    /* 12.8.1 rational */
+    add_applicative(K, ground_env, "rational?", ftypep, 2, symbol, 
+		    p2tv(krationalp));
+
+    /* 12.8.2 / */
+    add_applicative(K, ground_env, "/", kdivided, 0);
+
+    /* TODO */
+    /* complete module rational */
+
     /*
     **
     ** 13 Strings
diff --git a/src/krational.c b/src/krational.c
@@ -187,6 +187,45 @@ bool kbigrat_gep(klisp_State *K, TValue tv_bigrat1, TValue tv_bigrat2)
 			   tv2bigrat(tv_bigrat2)) >= 0);
 }
 
+/*
+** GC: All of these assume the parameters are rooted 
+*/
+TValue kbigrat_plus(klisp_State *K, TValue n1, TValue n2)
+{
+    TValue res = kbigrat_make_simple(K);
+    krooted_tvs_push(K, res);
+    UNUSED(mp_rat_add(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res)));
+    krooted_tvs_pop(K);
+    return kbigrat_try_integer(K, res);
+}
+
+TValue kbigrat_times(klisp_State *K, TValue n1, TValue n2)
+{
+    TValue res = kbigrat_make_simple(K);
+    krooted_tvs_push(K, res);
+    UNUSED(mp_rat_mul(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res)));
+    krooted_tvs_pop(K);
+    return kbigrat_try_integer(K, res);
+}
+
+TValue kbigrat_minus(klisp_State *K, TValue n1, TValue n2)
+{
+    TValue res = kbigrat_make_simple(K);
+    krooted_tvs_push(K, res);
+    UNUSED(mp_rat_sub(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res)));
+    krooted_tvs_pop(K);
+    return kbigrat_try_integer(K, res);
+}
+
+TValue kbigrat_divided(klisp_State *K, TValue n1, TValue n2)
+{
+    TValue res = kbigrat_make_simple(K);
+    krooted_tvs_push(K, res);
+    UNUSED(mp_rat_div(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res)));
+    krooted_tvs_pop(K);
+    return kbigrat_try_integer(K, res);
+}
+
 bool kbigrat_negativep(TValue tv_bigrat)
 {
     return (mp_rat_compare_zero(tv2bigrat(tv_bigrat)) < 0);
diff --git a/src/krational.h b/src/krational.h
@@ -123,7 +123,7 @@ bool kbigrat_gep(klisp_State *K, TValue bigrat1, TValue bigrat2);
 TValue kbigrat_plus(klisp_State *K, TValue n1, TValue n2);
 TValue kbigrat_times(klisp_State *K, TValue n1, TValue n2);
 TValue kbigrat_minus(klisp_State *K, TValue n1, TValue n2);
-TValue kbigrat_divide(klisp_State *K, TValue n1, TValue n2);
+TValue kbigrat_divided(klisp_State *K, TValue n1, TValue n2);
 
 /* TODO: Kernel allows arbitrary reals for these... will have to define */
 #if 0