diff --git a/moo/lib/Makefile.am b/moo/lib/Makefile.am index 693f99b..1b5be18 100644 --- a/moo/lib/Makefile.am +++ b/moo/lib/Makefile.am @@ -80,6 +80,7 @@ libmoo_la_SOURCES = \ gc.c \ heap.c \ moo.c \ + number.c \ obj.c \ opt-impl.h \ opt.c \ diff --git a/moo/lib/Makefile.in b/moo/lib/Makefile.in index 57d7a04..17bfdac 100644 --- a/moo/lib/Makefile.in +++ b/moo/lib/Makefile.in @@ -171,10 +171,10 @@ am_libmoo_la_OBJECTS = libmoo_la-bigint.lo libmoo_la-comp.lo \ libmoo_la-debug.lo libmoo_la-decode.lo libmoo_la-dic.lo \ libmoo_la-err.lo libmoo_la-exec.lo libmoo_la-logfmt.lo \ libmoo_la-gc.lo libmoo_la-heap.lo libmoo_la-moo.lo \ - libmoo_la-obj.lo libmoo_la-opt.lo libmoo_la-pf-basic.lo \ - libmoo_la-pf-sys.lo libmoo_la-pf-utf8.lo libmoo_la-rbt.lo \ - libmoo_la-sym.lo libmoo_la-utf8.lo libmoo_la-utf16.lo \ - libmoo_la-utl.lo libmoo_la-std.lo + libmoo_la-number.lo libmoo_la-obj.lo libmoo_la-opt.lo \ + libmoo_la-pf-basic.lo libmoo_la-pf-sys.lo libmoo_la-pf-utf8.lo \ + libmoo_la-rbt.lo libmoo_la-sym.lo libmoo_la-utf8.lo \ + libmoo_la-utf16.lo libmoo_la-utl.lo libmoo_la-std.lo libmoo_la_OBJECTS = $(am_libmoo_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -404,6 +404,7 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -471,6 +472,7 @@ libmoo_la_SOURCES = \ gc.c \ heap.c \ moo.c \ + number.c \ obj.c \ opt-impl.h \ opt.c \ @@ -656,6 +658,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmoo_la-heap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmoo_la-logfmt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmoo_la-moo.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmoo_la-number.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmoo_la-obj.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmoo_la-opt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmoo_la-pf-basic.Plo@am__quote@ @@ -771,6 +774,13 @@ libmoo_la-moo.lo: moo.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmoo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmoo_la-moo.lo `test -f 'moo.c' || echo '$(srcdir)/'`moo.c +libmoo_la-number.lo: number.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmoo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmoo_la-number.lo -MD -MP -MF $(DEPDIR)/libmoo_la-number.Tpo -c -o libmoo_la-number.lo `test -f 'number.c' || echo '$(srcdir)/'`number.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmoo_la-number.Tpo $(DEPDIR)/libmoo_la-number.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='number.c' object='libmoo_la-number.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmoo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmoo_la-number.lo `test -f 'number.c' || echo '$(srcdir)/'`number.c + libmoo_la-obj.lo: obj.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmoo_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmoo_la-obj.lo -MD -MP -MF $(DEPDIR)/libmoo_la-obj.Tpo -c -o libmoo_la-obj.lo `test -f 'obj.c' || echo '$(srcdir)/'`obj.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmoo_la-obj.Tpo $(DEPDIR)/libmoo_la-obj.Plo diff --git a/moo/lib/gc.c b/moo/lib/gc.c index d4d68eb..f2f8af2 100644 --- a/moo/lib/gc.c +++ b/moo/lib/gc.c @@ -376,6 +376,15 @@ static kernel_class_info_t kernel_classes[] = MOO_OBJ_TYPE_LIWORD, MOO_OFFSETOF(moo_t, _large_negative_integer) }, + { 17, + { 'F','i','x','e','d','P','o','i','n','t','D','e','c','i','m','a','l' }, + MOO_CLASS_SELFSPEC_FLAG_LIMITED, + 0, + MOO_FPDEC_NAMED_INSTVARS, + MOO_CLASS_SPEC_FLAG_INDEXED, + MOO_OBJ_TYPE_OOP, + MOO_OFFSETOF(moo_t, _fixed_point_decimal) }, + { 12, { 'S','m','a','l','l','P','o','i','n','t','e','r' }, 0, diff --git a/moo/lib/moo.h b/moo/lib/moo.h index 03fa2bf..4fe2c52 100644 --- a/moo/lib/moo.h +++ b/moo/lib/moo.h @@ -977,6 +977,17 @@ struct moo_process_scheduler_t } suspended; }; + +#define MOO_FPDEC_NAMED_INSTVARS 2 +typedef struct moo_fpdec_t moo_fpdec_t; +typedef struct moo_fpdec_t* moo_oop_fpdec_t; +struct moo_fpdec_t +{ + MOO_OBJ_HEADER; + moo_oop_t value; /* smooi or bigint */ + moo_oop_t scale; /* smooi, positive */ +}; + /** * The MOO_CLASSOF() macro return the class of an object including a numeric * object encoded into a pointer. @@ -1520,6 +1531,7 @@ struct moo_t moo_oop_class_t _large_positive_integer; /* LargePositiveInteger */ moo_oop_class_t _large_negative_integer; /* LargeNegativeInteger */ + moo_oop_class_t _fixed_point_decimal; /* FixedPointDecimal */ moo_oop_class_t _small_pointer; moo_oop_class_t _system; diff --git a/moo/lib/number.c b/moo/lib/number.c new file mode 100644 index 0000000..173a2b8 --- /dev/null +++ b/moo/lib/number.c @@ -0,0 +1,402 @@ +/* + * $Id$ + * + Copyright (c) 2014-2018 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "moo-prv.h" + +#define MOO_IS_FPDEC(moo, x) (MOO_OBJ_GET_CLASS(x) == (moo)->_fixed_point_decimal) +moo_oop_t moo_makefpdec (moo_t* moo, moo_oop_t value, moo_oop_t scale) +{ + /* TODO: */ +} + +static moo_ooi_t equalize_scale (moo_t* moo, moo_oop_t* x, moo_oop_t* y) +{ + moo_ooi_t xs, ys; + moo_oop_t nv; + moo_oop_t xv, yv; + + /* this function assumes that x and y are protected by the caller */ + + xs = 0; + xv = *x; + if (MOO_IS_FPDEC(moo, xv)) + { + xs = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)xv)->scale); + xv = ((moo_oop_fpdec_t)xv)->value; + } + else if (!moo_isint(moo, xv)) + { + moo_seterrbfmt (moo, MOO_EINVAL, "parameter not numeric - %O", xv); + return -1; + } + + ys = 0; + yv = *y; + if (MOO_IS_FPDEC(moo, *y)) + { + ys = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)yv)->scale); + yv = ((moo_oop_fpdec_t)yv)->value; + } + else if (!moo_isint(moo, yv)) + { + moo_seterrbfmt (moo, MOO_EINVAL, "parameter not numeric - %O", yv); + return -1; + } + + if (xs < ys) + { + nv = xv; + while (xs < ys) + { + /* TODO: optmize this. less multiplications */ + nv = moo_mulints(moo, nv, MOO_SMOOI_TO_OOP(10)); + if (!nv) return -1; + xs++; + } + + nv = moo_makefpdec(moo, nv, xs); + if (!nv) return -1; + + *x = nv; + } + else if (xs > ys) + { + nv = yv; + while (ys < xs) + { + nv = moo_mulints(moo, nv, MOO_SMOOI_TO_OOP(10)); + if (!nv) return -1; + ys++; + } + + nv = moo_makefpdec(moo, nv, ys); + if (!nv) return -1; + + *y = nv; + } + + return xs; +} + +moo_oop_t moo_truncfpdecval (moo_t* moo, moo_oop_t iv, moo_ooi_t cs, moo_ooi_t ns) +{ + /* this function truncates an existing fixed-point decimal. + * it doesn't create a new object */ + + if (cs > ns) + { + do + { + /* TODO: optimization... less divisions */ + iv = moo_divints(moo, iv, MOO_SMOOI_TO_OOP(10), 0, MOO_NULL); + if (!iv) return MOO_NULL; + cs--; + } + while (cs > ns); + } + + return iv; +} + +moo_oop_t moo_addnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + if (!MOO_IS_FPDEC(moo, x) && !MOO_IS_FPDEC(moo, y)) + { + /* both are probably integers */ + return moo_addints(moo, x, y); + } + else + { + moo_oop_t v; + moo_ooi_t scale; + + moo_pushtmp (moo, &x); + moo_pushtmp (moo, &y); + + scale = equalize_scale(moo, &x, &y); + if (scale <= -1) + { + moo_poptmps (moo, 2); + return MOO_NULL; + } + v = moo_addints(moo, ((moo_oop_fpdec_t)x)->value, ((moo_oop_fpdec_t)y)->value); + moo_poptmps (moo, 2); + if (!v) return MOO_NULL; + + return moo_makefpdec(moo, v, scale); + } +} + +moo_oop_t moo_subnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + if (!MOO_IS_FPDEC(moo, x) && !MOO_IS_FPDEC(moo, y)) + { + /* both are probably integers */ + return moo_subints(moo, x, y); + } + else + { + moo_oop_t v; + moo_ooi_t scale; + + moo_pushtmp (moo, &x); + moo_pushtmp (moo, &y); + + scale = equalize_scale(moo, &x, &y); + if (scale <= -1) + { + moo_poptmps (moo, 2); + return MOO_NULL; + } + v = moo_subints(moo, ((moo_oop_fpdec_t)x)->value, ((moo_oop_fpdec_t)y)->value); + moo_poptmps (moo, 2); + if (!v) return MOO_NULL; + + return moo_makefpdec(moo, v, scale); + } +} + +static moo_oop_t mul_nums (moo_t* moo, moo_oop_t x, moo_oop_t y, int mult) +{ + moo_ooi_t xs, ys, cs, ns; + moo_oop_t nv; + moo_oop_t xv, yv; + + xs = 0; + xv = x; + if (MOO_IS_FPDEC(moo, xv)) + { + xs = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)xv)->scale); + xv = ((moo_oop_fpdec_t)xv)->value; + } + else if (!moo_isint(moo, xv)) + { + moo_seterrbfmt (moo, MOO_EINVAL, "parameter not numeric - %O", xv); + return MOO_NULL; + } + + ys = 0; + yv = y; + if (MOO_IS_FPDEC(moo, y)) + { + ys = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)yv)->scale); + yv = ((moo_oop_fpdec_t)yv)->value; + } + else if (!moo_isint(moo, yv)) + { + moo_seterrbfmt (moo, MOO_EINVAL, "parameter not numeric - %O", yv); + return MOO_NULL; + } + + nv = moo_mulints(moo, xv, yv); + if (!nv) return MOO_NULL; + + cs = xs + ys; + if (cs <= 0) return nv; /* the result must be an integer */ + + ns = (mult || xs > ys)? xs: ys; + + /* cs may be larger than MOO_SMOOI_MAX. but ns is guaranteed to be + * equal to or less than MOO_SMOOI_MAX */ + MOO_ASSERT (moo, ns <= MOO_SMOOI_MAX); + + nv = moo_truncfpdecval(moo, nv, cs, ns); + if (!nv) return MOO_NULL; + + return (ns <= 0)? nv: moo_makefpdec(moo, nv, ns); +} + +moo_oop_t moo_mulnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + /* (* 1.00 12.123) => 12.123 */ + return mul_nums(moo, x, y, 0); +} + +moo_oop_t moo_mltnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + /* (mlt 1.00 12.123) => 12.12 */ + return mul_nums(moo, x, y, 1); +} + +moo_oop_t moo_divnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + moo_ooi_t xs, ys, i; + moo_oop_t nv; + moo_oop_t xv, yv; + + xs = 0; + xv = x; + if (MOO_IS_FPDEC(moo, xv)) + { + xs = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)xv)->scale); + xv = ((moo_oop_fpdec_t)xv)->value; + } + else if (!moo_isint(moo, xv)) + { + moo_seterrbfmt (moo, MOO_EINVAL, "parameter not numeric - %O", xv); + return MOO_NULL; + } + + ys = 0; + yv = y; + if (MOO_IS_FPDEC(moo, y)) + { + ys = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)yv)->scale); + yv = ((moo_oop_fpdec_t)yv)->value; + } + else if (!moo_isint(moo, yv)) + { + moo_seterrbfmt (moo, MOO_EINVAL, "parameter not numeric - %O", yv); + return MOO_NULL; + } + + nv = xv; + + moo_pushtmp (moo, &yv); + for (i = 0; i < ys; i++) + { + nv = moo_mulints(moo, nv, MOO_SMOOI_TO_OOP(10)); + if (!nv) + { + moo_poptmp (moo); + return MOO_NULL; + } + } + + nv = moo_divints(moo, nv, yv, 0, MOO_NULL); + moo_poptmp (moo); + if (!nv) return MOO_NULL; + + return moo_makefpdec(moo, nv, xs); +} + +static moo_oop_t comp_nums (moo_t* moo, moo_oop_t x, moo_oop_t y, moo_oop_t (*comper) (moo_t*, moo_oop_t, moo_oop_t)) +{ + if (!MOO_IS_FPDEC(moo, x) && !MOO_IS_FPDEC(moo, y)) + { + /* both are probably integers */ + return comper(moo, x, y); + } + else + { + moo_oop_t v; + moo_ooi_t scale; + + moo_pushtmp (moo, &x); + moo_pushtmp (moo, &y); + + scale = equalize_scale(moo, &x, &y); + if (scale <= -1) + { + moo_poptmps (moo, 2); + return MOO_NULL; + } + v = comper(moo, ((moo_oop_fpdec_t)x)->value, ((moo_oop_fpdec_t)y)->value); + moo_poptmps (moo, 2); + return v; + } +} + + +moo_oop_t moo_gtnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + return comp_nums(moo, x, y, moo_gtints); +} +moo_oop_t moo_genums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + return comp_nums(moo, x, y, moo_geints); +} + + +moo_oop_t moo_ltnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + return comp_nums(moo, x, y, moo_ltints); +} +moo_oop_t moo_lenums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + return comp_nums(moo, x, y, moo_leints); +} + +moo_oop_t moo_eqnums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + return comp_nums(moo, x, y, moo_eqints); +} +moo_oop_t moo_nenums (moo_t* moo, moo_oop_t x, moo_oop_t y) +{ + return comp_nums(moo, x, y, moo_neints); +} + +moo_oop_t moo_sqrtnum (moo_t* moo, moo_oop_t x) +{ + if (!MOO_IS_FPDEC(moo, x)) + { + return moo_sqrtint(moo, x); + } + else + { + moo_oop_t v; + moo_ooi_t i, scale; + + scale = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)x)->scale); + + v = ((moo_oop_fpdec_t)x)->value; + for (i = 0; i < scale ; i++) + { + v = moo_mulints(moo, v, MOO_SMOOI_TO_OOP(10)); + if (!v) + { + moo_poptmp (moo); + return MOO_NULL; + } + } + + v = moo_sqrtint(moo, v); + if (!v) return MOO_NULL; + + return moo_makefpdec(moo, v, scale); + } +} + +moo_oop_t moo_absnum (moo_t* moo, moo_oop_t x) +{ + if (!MOO_IS_FPDEC(moo, x)) + { + return moo_absint(moo, x); + } + else + { + moo_oop_t v; + moo_ooi_t scale; + + scale = MOO_OOP_TO_SMOOI(((moo_oop_fpdec_t)x)->scale); + v = ((moo_oop_fpdec_t)x)->value; + + v = moo_absint(moo, v); + if (!v) return MOO_NULL; + + return moo_makefpdec(moo, v, scale); + } +}