mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* bignum.c: Move functions.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42140 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
ca3f5b533d
commit
a84ea11986
2 changed files with 207 additions and 202 deletions
|
|
@ -1,3 +1,7 @@
|
|||
Tue Jul 23 20:47:36 2013 Tanaka Akira <akr@fsij.org>
|
||||
|
||||
* bignum.c: Move functions.
|
||||
|
||||
Tue Jul 23 20:14:55 2013 Tanaka Akira <akr@fsij.org>
|
||||
|
||||
* bignum.c (bary_divmod): Add special cases for x < y easily detected
|
||||
|
|
|
|||
405
bignum.c
405
bignum.c
|
|
@ -2548,6 +2548,209 @@ bary_mul(BDIGIT *zds, size_t zl, BDIGIT *xds, size_t xl, BDIGIT *yds, size_t yl)
|
|||
bary_mul_toom3_start(zds, zl, xds, xl, yds, yl, NULL, 0);
|
||||
}
|
||||
|
||||
struct big_div_struct {
|
||||
long nx, ny, j, nyzero;
|
||||
BDIGIT *yds, *zds;
|
||||
volatile VALUE stop;
|
||||
};
|
||||
|
||||
static void *
|
||||
bigdivrem1(void *ptr)
|
||||
{
|
||||
struct big_div_struct *bds = (struct big_div_struct*)ptr;
|
||||
long ny = bds->ny;
|
||||
long j;
|
||||
long nyzero = bds->nyzero;
|
||||
BDIGIT *yds = bds->yds, *zds = bds->zds;
|
||||
BDIGIT_DBL_SIGNED num;
|
||||
BDIGIT q;
|
||||
|
||||
j = bds->j;
|
||||
do {
|
||||
if (bds->stop) {
|
||||
bds->j = j;
|
||||
return 0;
|
||||
}
|
||||
if (zds[j] == yds[ny-1]) q = BDIGMAX;
|
||||
else q = (BDIGIT)((BIGUP(zds[j]) + zds[j-1])/yds[ny-1]);
|
||||
if (q) {
|
||||
num = bigdivrem_mulsub(zds+j-(ny-nyzero), ny-nyzero+1,
|
||||
q,
|
||||
yds+nyzero, ny-nyzero);
|
||||
while (num) { /* "add back" required */
|
||||
q--;
|
||||
num = bary_add(zds+j-(ny-nyzero), ny-nyzero,
|
||||
zds+j-(ny-nyzero), ny-nyzero,
|
||||
yds+nyzero, ny-nyzero);
|
||||
num--;
|
||||
}
|
||||
}
|
||||
zds[j] = q;
|
||||
} while (--j >= ny);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rb_big_stop(void *ptr)
|
||||
{
|
||||
struct big_div_struct *bds = ptr;
|
||||
bds->stop = Qtrue;
|
||||
}
|
||||
|
||||
static inline int
|
||||
bigdivrem_num_extra_words(long nx, long ny)
|
||||
{
|
||||
int ret = nx==ny ? 2 : 1;
|
||||
assert(ret <= BIGDIVREM_EXTRA_WORDS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BDIGIT
|
||||
bigdivrem_single(BDIGIT *qds, BDIGIT *xds, long nx, BDIGIT y)
|
||||
{
|
||||
long i;
|
||||
BDIGIT_DBL t2;
|
||||
t2 = 0;
|
||||
i = nx;
|
||||
while (i--) {
|
||||
t2 = BIGUP(t2) + xds[i];
|
||||
qds[i] = (BDIGIT)(t2 / y);
|
||||
t2 %= y;
|
||||
}
|
||||
return (BDIGIT)t2;
|
||||
}
|
||||
|
||||
static void
|
||||
bigdivrem_normal(BDIGIT *zds, long nz, BDIGIT *xds, long nx, BDIGIT *yds, long ny, int needs_mod)
|
||||
{
|
||||
struct big_div_struct bds;
|
||||
BDIGIT q;
|
||||
int shift;
|
||||
|
||||
q = yds[ny-1];
|
||||
shift = nlz(q);
|
||||
if (shift) {
|
||||
bary_small_lshift(yds, yds, ny, shift);
|
||||
zds[nx] = bary_small_lshift(zds, xds, nx, shift);
|
||||
}
|
||||
else {
|
||||
MEMCPY(zds, xds, BDIGIT, nx);
|
||||
zds[nx] = 0;
|
||||
}
|
||||
if (nx+1 < nz) zds[nx+1] = 0;
|
||||
|
||||
bds.nx = nx;
|
||||
bds.ny = ny;
|
||||
bds.zds = zds;
|
||||
bds.yds = yds;
|
||||
bds.stop = Qfalse;
|
||||
bds.j = nz - 1;
|
||||
for (bds.nyzero = 0; !yds[bds.nyzero]; bds.nyzero++);
|
||||
if (nx > 10000 || ny > 10000) {
|
||||
retry:
|
||||
bds.stop = Qfalse;
|
||||
rb_thread_call_without_gvl(bigdivrem1, &bds, rb_big_stop, &bds);
|
||||
|
||||
if (bds.stop == Qtrue) {
|
||||
/* execute trap handler, but exception was not raised. */
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bigdivrem1(&bds);
|
||||
}
|
||||
|
||||
if (needs_mod && shift) {
|
||||
bary_small_rshift(zds, zds, ny, shift, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bary_divmod(BDIGIT *qds, size_t nq, BDIGIT *rds, size_t nr, BDIGIT *xds, size_t nx, BDIGIT *yds, size_t ny)
|
||||
{
|
||||
assert(nx <= nq);
|
||||
assert(ny <= nr);
|
||||
|
||||
while (0 < ny && !yds[ny-1]) ny--;
|
||||
if (ny == 0)
|
||||
rb_num_zerodiv();
|
||||
|
||||
while (0 < nx && !xds[nx-1]) nx--;
|
||||
if (nx == 0) {
|
||||
BDIGITS_ZERO(qds, nq);
|
||||
BDIGITS_ZERO(rds, nr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nx < ny || (nx == ny && xds[nx - 1] < yds[ny - 1])) {
|
||||
MEMCPY(rds, xds, BDIGIT, nx);
|
||||
BDIGITS_ZERO(rds+nx, nr-nx);
|
||||
BDIGITS_ZERO(qds, nq);
|
||||
}
|
||||
else if (ny == 1) {
|
||||
MEMCPY(qds, xds, BDIGIT, nx);
|
||||
BDIGITS_ZERO(qds+nx, nq-nx);
|
||||
rds[0] = bigdivrem_single(qds, xds, nx, yds[0]);
|
||||
BDIGITS_ZERO(rds+1, nr-1);
|
||||
}
|
||||
else if (nx == 2 && ny == 2) {
|
||||
BDIGIT_DBL x = xds[0] | BIGUP(xds[1]);
|
||||
BDIGIT_DBL y = yds[0] | BIGUP(yds[1]);
|
||||
BDIGIT_DBL q = x / y;
|
||||
BDIGIT_DBL r = x % y;
|
||||
qds[0] = BIGLO(q);
|
||||
qds[1] = BIGLO(BIGDN(q));
|
||||
BDIGITS_ZERO(qds+2, nq-2);
|
||||
rds[0] = BIGLO(r);
|
||||
rds[1] = BIGLO(BIGDN(r));
|
||||
BDIGITS_ZERO(rds+2, nr-2);
|
||||
}
|
||||
else {
|
||||
int extra_words;
|
||||
long j;
|
||||
long nz;
|
||||
BDIGIT *zds;
|
||||
VALUE tmpz = 0;
|
||||
BDIGIT *tds;
|
||||
|
||||
extra_words = bigdivrem_num_extra_words(nx, ny);
|
||||
nz = nx + extra_words;
|
||||
if (nx + extra_words <= nq)
|
||||
zds = qds;
|
||||
else
|
||||
zds = ALLOCV_N(BDIGIT, tmpz, nx + extra_words);
|
||||
MEMCPY(zds, xds, BDIGIT, nx);
|
||||
BDIGITS_ZERO(zds+nx, nz-nx);
|
||||
|
||||
if (BDIGIT_MSB(yds[ny-1])) {
|
||||
/* bigdivrem_normal will not modify y.
|
||||
* So use yds directly. */
|
||||
tds = yds;
|
||||
}
|
||||
else {
|
||||
/* bigdivrem_normal will modify y.
|
||||
* So use rds as a temporary buffer. */
|
||||
MEMCPY(rds, yds, BDIGIT, ny);
|
||||
tds = rds;
|
||||
}
|
||||
|
||||
bigdivrem_normal(zds, nz, xds, nx, tds, ny, 1);
|
||||
|
||||
/* copy remainder */
|
||||
MEMCPY(rds, zds, BDIGIT, ny);
|
||||
BDIGITS_ZERO(rds+ny, nr-ny);
|
||||
|
||||
/* move quotient */
|
||||
j = nz - ny;
|
||||
MEMMOVE(qds, zds+ny, BDIGIT, j);
|
||||
BDIGITS_ZERO(qds+j, nq-j);
|
||||
|
||||
if (tmpz)
|
||||
ALLOCV_END(tmpz);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define BIGNUM_DEBUG 0
|
||||
#if BIGNUM_DEBUG
|
||||
#define ON_DEBUG(x) do { x; } while (0)
|
||||
|
|
@ -5132,208 +5335,6 @@ rb_big_mul(VALUE x, VALUE y)
|
|||
return bignorm(bigmul0(x, y));
|
||||
}
|
||||
|
||||
struct big_div_struct {
|
||||
long nx, ny, j, nyzero;
|
||||
BDIGIT *yds, *zds;
|
||||
volatile VALUE stop;
|
||||
};
|
||||
|
||||
static void *
|
||||
bigdivrem1(void *ptr)
|
||||
{
|
||||
struct big_div_struct *bds = (struct big_div_struct*)ptr;
|
||||
long ny = bds->ny;
|
||||
long j;
|
||||
long nyzero = bds->nyzero;
|
||||
BDIGIT *yds = bds->yds, *zds = bds->zds;
|
||||
BDIGIT_DBL_SIGNED num;
|
||||
BDIGIT q;
|
||||
|
||||
j = bds->j;
|
||||
do {
|
||||
if (bds->stop) {
|
||||
bds->j = j;
|
||||
return 0;
|
||||
}
|
||||
if (zds[j] == yds[ny-1]) q = BDIGMAX;
|
||||
else q = (BDIGIT)((BIGUP(zds[j]) + zds[j-1])/yds[ny-1]);
|
||||
if (q) {
|
||||
num = bigdivrem_mulsub(zds+j-(ny-nyzero), ny-nyzero+1,
|
||||
q,
|
||||
yds+nyzero, ny-nyzero);
|
||||
while (num) { /* "add back" required */
|
||||
q--;
|
||||
num = bary_add(zds+j-(ny-nyzero), ny-nyzero,
|
||||
zds+j-(ny-nyzero), ny-nyzero,
|
||||
yds+nyzero, ny-nyzero);
|
||||
num--;
|
||||
}
|
||||
}
|
||||
zds[j] = q;
|
||||
} while (--j >= ny);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rb_big_stop(void *ptr)
|
||||
{
|
||||
struct big_div_struct *bds = ptr;
|
||||
bds->stop = Qtrue;
|
||||
}
|
||||
|
||||
static inline int
|
||||
bigdivrem_num_extra_words(long nx, long ny)
|
||||
{
|
||||
int ret = nx==ny ? 2 : 1;
|
||||
assert(ret <= BIGDIVREM_EXTRA_WORDS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BDIGIT
|
||||
bigdivrem_single(BDIGIT *qds, BDIGIT *xds, long nx, BDIGIT y)
|
||||
{
|
||||
long i;
|
||||
BDIGIT_DBL t2;
|
||||
t2 = 0;
|
||||
i = nx;
|
||||
while (i--) {
|
||||
t2 = BIGUP(t2) + xds[i];
|
||||
qds[i] = (BDIGIT)(t2 / y);
|
||||
t2 %= y;
|
||||
}
|
||||
return (BDIGIT)t2;
|
||||
}
|
||||
|
||||
static void
|
||||
bigdivrem_normal(BDIGIT *zds, long nz, BDIGIT *xds, long nx, BDIGIT *yds, long ny, int needs_mod)
|
||||
{
|
||||
struct big_div_struct bds;
|
||||
BDIGIT q;
|
||||
int shift;
|
||||
|
||||
q = yds[ny-1];
|
||||
shift = nlz(q);
|
||||
if (shift) {
|
||||
bary_small_lshift(yds, yds, ny, shift);
|
||||
zds[nx] = bary_small_lshift(zds, xds, nx, shift);
|
||||
}
|
||||
else {
|
||||
MEMCPY(zds, xds, BDIGIT, nx);
|
||||
zds[nx] = 0;
|
||||
}
|
||||
if (nx+1 < nz) zds[nx+1] = 0;
|
||||
|
||||
bds.nx = nx;
|
||||
bds.ny = ny;
|
||||
bds.zds = zds;
|
||||
bds.yds = yds;
|
||||
bds.stop = Qfalse;
|
||||
bds.j = nz - 1;
|
||||
for (bds.nyzero = 0; !yds[bds.nyzero]; bds.nyzero++);
|
||||
if (nx > 10000 || ny > 10000) {
|
||||
retry:
|
||||
bds.stop = Qfalse;
|
||||
rb_thread_call_without_gvl(bigdivrem1, &bds, rb_big_stop, &bds);
|
||||
|
||||
if (bds.stop == Qtrue) {
|
||||
/* execute trap handler, but exception was not raised. */
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bigdivrem1(&bds);
|
||||
}
|
||||
|
||||
if (needs_mod && shift) {
|
||||
bary_small_rshift(zds, zds, ny, shift, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bary_divmod(BDIGIT *qds, size_t nq, BDIGIT *rds, size_t nr, BDIGIT *xds, size_t nx, BDIGIT *yds, size_t ny)
|
||||
{
|
||||
assert(nx <= nq);
|
||||
assert(ny <= nr);
|
||||
|
||||
while (0 < ny && !yds[ny-1]) ny--;
|
||||
if (ny == 0)
|
||||
rb_num_zerodiv();
|
||||
|
||||
while (0 < nx && !xds[nx-1]) nx--;
|
||||
if (nx == 0) {
|
||||
BDIGITS_ZERO(qds, nq);
|
||||
BDIGITS_ZERO(rds, nr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nx < ny || (nx == ny && xds[nx - 1] < yds[ny - 1])) {
|
||||
MEMCPY(rds, xds, BDIGIT, nx);
|
||||
BDIGITS_ZERO(rds+nx, nr-nx);
|
||||
BDIGITS_ZERO(qds, nq);
|
||||
}
|
||||
else if (ny == 1) {
|
||||
MEMCPY(qds, xds, BDIGIT, nx);
|
||||
BDIGITS_ZERO(qds+nx, nq-nx);
|
||||
rds[0] = bigdivrem_single(qds, xds, nx, yds[0]);
|
||||
BDIGITS_ZERO(rds+1, nr-1);
|
||||
}
|
||||
else if (nx == 2 && ny == 2) {
|
||||
BDIGIT_DBL x = xds[0] | BIGUP(xds[1]);
|
||||
BDIGIT_DBL y = yds[0] | BIGUP(yds[1]);
|
||||
BDIGIT_DBL q = x / y;
|
||||
BDIGIT_DBL r = x % y;
|
||||
qds[0] = BIGLO(q);
|
||||
qds[1] = BIGLO(BIGDN(q));
|
||||
BDIGITS_ZERO(qds+2, nq-2);
|
||||
rds[0] = BIGLO(r);
|
||||
rds[1] = BIGLO(BIGDN(r));
|
||||
BDIGITS_ZERO(rds+2, nr-2);
|
||||
}
|
||||
else {
|
||||
int extra_words;
|
||||
long j;
|
||||
long nz;
|
||||
BDIGIT *zds;
|
||||
VALUE tmpz = 0;
|
||||
BDIGIT *tds;
|
||||
|
||||
extra_words = bigdivrem_num_extra_words(nx, ny);
|
||||
nz = nx + extra_words;
|
||||
if (nx + extra_words <= nq)
|
||||
zds = qds;
|
||||
else
|
||||
zds = ALLOCV_N(BDIGIT, tmpz, nx + extra_words);
|
||||
MEMCPY(zds, xds, BDIGIT, nx);
|
||||
BDIGITS_ZERO(zds+nx, nz-nx);
|
||||
|
||||
if (BDIGIT_MSB(yds[ny-1])) {
|
||||
/* bigdivrem_normal will not modify y.
|
||||
* So use yds directly. */
|
||||
tds = yds;
|
||||
}
|
||||
else {
|
||||
/* bigdivrem_normal will modify y.
|
||||
* So use rds as a temporary buffer. */
|
||||
MEMCPY(rds, yds, BDIGIT, ny);
|
||||
tds = rds;
|
||||
}
|
||||
|
||||
bigdivrem_normal(zds, nz, xds, nx, tds, ny, 1);
|
||||
|
||||
/* copy remainder */
|
||||
MEMCPY(rds, zds, BDIGIT, ny);
|
||||
BDIGITS_ZERO(rds+ny, nr-ny);
|
||||
|
||||
/* move quotient */
|
||||
j = nz - ny;
|
||||
MEMMOVE(qds, zds+ny, BDIGIT, j);
|
||||
BDIGITS_ZERO(qds+j, nq-j);
|
||||
|
||||
if (tmpz)
|
||||
ALLOCV_END(tmpz);
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE
|
||||
bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue