diff --git a/moo/kernel/test-001.moo b/moo/kernel/test-001.moo index e0016fa..e854333 100644 --- a/moo/kernel/test-001.moo +++ b/moo/kernel/test-001.moo @@ -262,6 +262,27 @@ extend MyObject [ (811306333091350399588761 div: 128) = 6338330727276174996787 ], [ (811306333091350399588761 rem: 128) = 25 ], [ (811306333091350399588761 mod: 128) = 25 ], + [ (-811306333091350399588761 div: -16) = 50706645818209399974297 ], + + ## 100-104 + [ (-811306333091350399588761 rem: -16) = -9 ], + [ (-811306333091350399588761 mod: -16) = -9 ], + [ (-811306333091350399588761 div: -128) = 6338330727276174996787 ], + [ (-811306333091350399588761 rem: -128) = -25 ], + [ (-811306333091350399588761 mod: -128) = -25 ], + + ## 105-109 + [ (-8113063330913503995887611892379812731289731289312898971231 div: -1024) = 7922913409095218745983995988652160870400128212219627901 ], + [ (-8113063330913503995887611892379812731289731289312898971231 rem: -1024) = -607 ], + [ (-8113063330913503995887611892379812731289731289312898971231 mod: -1024) = -607 ], + [ (-8113063330913503995887611892379812731289731289312898971231 div: -65535) = 123797411015693964994088836383303772507663558240831600 ], + [ (-8113063330913503995887611892379812731289731289312898971231 rem: -65535) = -65231 ], + + ## 110-114 + [ (-8113063330913503995887611892379812731289731289312898971231 mod: -65535) = -65231 ], + [ (-8113063330913503995887611892379812731289731289312898971231 div: -65536) = 123795522017112792905999937322690013600002003315931685 ], + [ (-8113063330913503995887611892379812731289731289312898971231 rem: -65536) = -63071 ], + [ (-8113063330913503995887611892379812731289731289312898971231 mod: -65536) = -63071 ], ## ========================= [ diff --git a/moo/lib/bigint.c b/moo/lib/bigint.c index a94e220..6a9040b 100644 --- a/moo/lib/bigint.c +++ b/moo/lib/bigint.c @@ -2192,47 +2192,91 @@ moo_oop_t moo_divints (moo_t* moo, moo_oop_t x, moo_oop_t y, int modulo, moo_oop return z; default: - /* TODO: do division by shifting if both x & y are negative here */ - if (yv > 0 && IS_POW2(yv) && !MOO_POINTER_IS_NBIGINT(moo, x)) + /* TODO: do division by shifting if both x & y are in different sign */ + if (yv < 0) { - moo_oow_t nshifts; - - /* - 2**x = v - x = log2(v) - x is the number of shift to make */ - nshifts = LOG2_FOR_POW2(yv); - - moo_pushvolat (moo, &x); - moo_pushvolat (moo, &y); - z = clone_bigint(moo, x, MOO_OBJ_GET_SIZE(x)); - moo_popvolats (moo, 2); - if (!z) return MOO_NULL; - - rshift_unsigned_array (MOO_OBJ_GET_LIWORD_SLOT(z), MOO_OBJ_GET_SIZE(z), nshifts); - - moo_pushvolat (moo, &x); - moo_pushvolat (moo, &y); - z = normalize_bigint(moo, z); - moo_popvolats (moo, 2); - if (!z) return MOO_NULL; - - if (rem) + moo_oow_t yv_neg = -yv; + if (IS_POW2(yv_neg) && MOO_POINTER_IS_NBIGINT(moo, x)) { + moo_oow_t nshifts; + + nshifts = LOG2_FOR_POW2(yv_neg); + moo_pushvolat (moo, &x); - moo_pushvolat (moo, &z); - r = moo_mulints(moo, y, z); - moo_popvolats (moo, 2); - if (!r) return MOO_NULL; - - moo_pushvolat (moo, &z); - r = moo_subints(moo, x, r); + z = clone_bigint_negated(moo, x, MOO_OBJ_GET_SIZE(x)); moo_popvolat (moo); - if (!r) return MOO_NULL; + if (!z) return MOO_NULL; - *rem = r; + rshift_unsigned_array (MOO_OBJ_GET_LIWORD_SLOT(z), MOO_OBJ_GET_SIZE(z), nshifts); + + moo_pushvolat (moo, &x); + z = normalize_bigint(moo, z); + moo_popvolat (moo); + if (!z) return MOO_NULL; + + if (rem) + { + moo_pushvolat (moo, &x); + moo_pushvolat (moo, &z); + r = moo_mulints(moo, MOO_SMOOI_TO_OOP(yv_neg), z); + moo_popvolats (moo, 2); + if (!r) return MOO_NULL; + + moo_pushvolat (moo, &z); + r = moo_addints(moo, x, r); + moo_popvolat (moo); + if (!r) return MOO_NULL; + + *rem = r; + } + + return z; + } + } + else + { + /* yv > 0 */ + if (IS_POW2(yv) && MOO_POINTER_IS_PBIGINT(moo, x)) + { + moo_oow_t nshifts; + + /* + 2**x = v + x = log2(v) + x is the number of shift to make */ + nshifts = LOG2_FOR_POW2(yv); + + /* no pushvolat() on y as y is SMOOI here */ + moo_pushvolat (moo, &x); + z = clone_bigint(moo, x, MOO_OBJ_GET_SIZE(x)); + moo_popvolat (moo); + if (!z) return MOO_NULL; + + rshift_unsigned_array (MOO_OBJ_GET_LIWORD_SLOT(z), MOO_OBJ_GET_SIZE(z), nshifts); + + moo_pushvolat (moo, &x); + z = normalize_bigint(moo, z); + moo_popvolat (moo); + if (!z) return MOO_NULL; + + if (rem) + { + moo_pushvolat (moo, &x); + moo_pushvolat (moo, &z); + r = moo_mulints(moo, y, z); + moo_popvolats (moo, 2); + if (!r) return MOO_NULL; + + moo_pushvolat (moo, &z); + r = moo_subints(moo, x, r); + moo_popvolat (moo); + if (!r) return MOO_NULL; + + *rem = r; + } + + return z; } - return z; } break; }