00001 #include "multiprec.h"
00002 #include <string.h>
00003
00031 digit_t
00032 ymp_divmodabs_digit(digit_t *quotient, const digit_t *lhs, const digit_t rhs,
00033 size_t lhs_len)
00034 {
00035 double_digit_t tmp = 0;
00036 register size_t i = lhs_len;
00037
00038 while (i-- > 0)
00039 {
00040 tmp = LOW_DIGIT_TO_HIGH_DIGIT(tmp);
00041 tmp += lhs[i];
00042 if (quotient) quotient[i] = tmp/rhs;
00043 tmp %= rhs;
00044 }
00045 return LOW_DIGIT(tmp);
00046 }
00047
00048
00049
00050
00071 inline static void
00072 do_divmodabs(digit_t *quotient, digit_t *lhs, const digit_t *rhs,
00073 size_t lhs_len, size_t rhs_len)
00074
00075 {
00076 double_digit_t lhs_top;
00077 size_t i;
00078
00079 i = lhs_len; lhs_top = lhs[--i];
00080 while (i-- >= rhs_len)
00081 {
00082 size_t j;
00083 digit_t q;
00084 double_digit_t tmp;
00085
00086
00087 lhs_top = LOW_DIGIT_TO_HIGH_DIGIT(lhs_top);
00088 lhs_top += lhs[i];
00089 q = lhs_top / rhs[rhs_len-1];
00090
00091
00092 tmp = 0;
00093 for (j=0; j<rhs_len; ++j)
00094 {
00095 double_digit_t tmp2 = MULTIPREC_RADIX;
00096 tmp += ((double_digit_t)rhs[j]) * q;
00097
00098 tmp2 += lhs[i-rhs_len+j+1];
00099 tmp2 -= LOW_DIGIT(tmp);
00100 lhs[i-rhs_len+j+1] = LOW_DIGIT(tmp2);
00101
00102 tmp = HIGH_DIGIT(tmp);
00103 tmp += !HIGH_DIGIT(tmp2);
00104 }
00105 tmp = MULTIPREC_RADIX - tmp;
00106 tmp += lhs[i+1];
00107 lhs[i+1] = LOW_DIGIT(tmp);
00108
00109
00110 while (lhs[i+1] & DIGIT_HIGHEST_BIT)
00111 {
00112
00113 --q;
00114
00115
00116 tmp = 0;
00117 for (j=0; j<rhs_len; ++j)
00118 {
00119 tmp += lhs[i-rhs_len+j+1];
00120 tmp += rhs[j];
00121 lhs[i-rhs_len+j+1] = LOW_DIGIT(tmp);
00122 tmp = HIGH_DIGIT(tmp);
00123 }
00124 lhs[i+1] += LOW_DIGIT(tmp);
00125 }
00126
00127
00128 if (quotient) quotient[i+1-rhs_len] = q;
00129
00130 lhs_top = lhs[i];
00131 }
00132 }
00133
00134
00135
00158 size_t
00159 ymp_divmodabs(digit_t *quotient, digit_t *remainder,
00160 const digit_t *lhs, const digit_t *rhs,
00161 size_t lhs_len, size_t rhs_len)
00162 {
00163 int shift_width;
00164 digit_t hd;
00165 size_t i;
00166 digit_t *new_lhs, *new_rhs;
00167 double_digit_t tmp;
00168
00169
00170 if (rhs_len==0 || (rhs_len==1 && rhs[0]==0))
00171 {
00172 ymp_fatal("0による除算");
00173 return 0;
00174 }
00175
00176
00177 YMP_TEMP_ALLOCATE(new_lhs, digit_t, lhs_len+1);
00178 YMP_TEMP_ALLOCATE(new_rhs, digit_t, rhs_len);
00179
00180
00181 shift_width = 0;
00182 hd = rhs[rhs_len-1];
00183 while ( !(hd & DIGIT_HIGHEST_BIT) )
00184 {
00185 ++shift_width;
00186 hd <<= 1;
00187 }
00188
00189
00190 i = rhs_len; tmp = rhs[--i];
00191 while (i-- > 0)
00192 {
00193 tmp = LOW_DIGIT_TO_HIGH_DIGIT(tmp);
00194 tmp += rhs[i];
00195 tmp <<= shift_width;
00196 new_rhs[i+1] = HIGH_DIGIT(tmp);
00197 tmp = rhs[i];
00198 }
00199 new_rhs[0] = rhs[0] << shift_width;
00200
00201
00202 tmp = 0; i=lhs_len;
00203 while(i-- > 0)
00204 {
00205 tmp += lhs[i];
00206 tmp <<= shift_width;
00207 new_lhs[i+1] = HIGH_DIGIT(tmp);
00208
00209 tmp = lhs[i];
00210 tmp = LOW_DIGIT_TO_HIGH_DIGIT(tmp);
00211 }
00212 new_lhs[0] = lhs[0] << shift_width;
00213
00214
00215 do_divmodabs(quotient, new_lhs, new_rhs, lhs_len+1, rhs_len);
00216
00217
00218
00219 if (remainder)
00220 {
00221
00222 size_t rem_used = 0;
00223 tmp = 0; i=rhs_len;
00224 while (i-- > 0)
00225 {
00226 tmp += new_lhs[i];
00227 remainder[i] = LOW_DIGIT(tmp >> shift_width);
00228
00229 if (remainder[i] && !rem_used) rem_used = i+1;
00230
00231 tmp = new_lhs[i];
00232 tmp = LOW_DIGIT_TO_HIGH_DIGIT(tmp);
00233 }
00234 return rem_used;
00235 }
00236 else
00237 {
00238 return 0;
00239 }
00240 }
00261 void
00262 ymp_divmod(mp_ref_t quotient, mp_ref_t remainder,
00263 mp_cref_t self, mp_cref_t other)
00264 {
00265 enum mp_sign_t rem_sign;
00266 size_t rem_used;
00267
00268
00269 if (ymp_is_zero(other))
00270 {
00271 ymp_fatal("0による除算");
00272 return;
00273 }
00274
00275
00276
00277 if (self->used < other->used)
00278 {
00279 if (quotient) quotient->used = 0;
00280 if (self != remainder && remainder) ymp_assign(remainder, self);
00281 return;
00282 }
00283
00284
00285 if (quotient) ymp_reserve(quotient, self->used-other->used+1);
00286 if (remainder) ymp_reserve(remainder, other->used);
00287
00288
00289 rem_sign = self->sign;
00290 rem_used = ymp_divmodabs(quotient?quotient->digits:NULL,
00291 remainder?remainder->digits:NULL,
00292 self->digits, other->digits,
00293 self->used, other->used);
00294
00295
00296
00297
00298
00299
00300
00301 if (quotient)
00302 {
00303
00304 quotient->sign = self->sign != other->sign;
00305 quotient->used = self->used - other->used + 1;
00306 if (!quotient->digits[quotient->used-1]) --quotient->used;
00307 }
00308
00309 if (remainder)
00310 {
00311
00312 remainder->sign = rem_sign;
00313 remainder->used = rem_used;
00314 }
00315 }
00316
00317
00332 digit_t
00333 ymp_divmod_digit(mp_ref_t quotient, mp_cref_t self, digit_t other)
00334 {
00335 digit_t rem;
00336
00337
00338 if (other == 0)
00339 {
00340 ymp_fatal("0による除算");
00341 return 0;
00342 }
00343
00344
00345 if (self->used == 0)
00346 {
00347 if (quotient) quotient->used = 0;
00348 return 0;
00349 }
00350
00351
00352 if (quotient) ymp_reserve(quotient, self->used);
00353
00354
00355 rem = ymp_divmodabs_digit(quotient?quotient->digits:NULL,
00356 self->digits,
00357 other,
00358 self->used);
00359
00360
00361 if (quotient)
00362 {
00363 if (quotient->digits[quotient->used-1]) --quotient->used;
00364 quotient->sign = self->sign;
00365 }
00366
00367 if (self->sign && rem)
00368 {
00369
00370 if (quotient)
00371 {
00372 quotient->used = ymp_addabs(quotient->digits, quotient->digits,
00373 &other, quotient->len, 1);
00374 }
00375 return other - rem;
00376 }
00377 else
00378 {
00379 return rem;
00380 }
00381 }
00382
00398 int
00399 ymp_div_if_divisible(mp_ref_t quotient, mp_cref_t self, mp_cref_t other)
00400 {
00401 size_t rem_used;
00402 digit_t *quot_buf=NULL, *rem_buf=NULL;
00403
00404
00405 if (ymp_is_zero(other))
00406 {
00407 ymp_fatal("0による除算");
00408 return 2;
00409 }
00410
00411
00412 if (self->used < other->used)
00413 {
00414 if (ymp_is_zero(self))
00415 {
00416 if (quotient) quotient->used = 0;
00417 return 1;
00418 }
00419 else
00420 {
00421 return 0;
00422 }
00423 }
00424
00425
00426 if (quotient) YMP_TEMP_ALLOCATE(quot_buf, digit_t, self->used-other->used+1);
00427 YMP_TEMP_ALLOCATE(rem_buf, digit_t, other->used);
00428
00429
00430 rem_used = ymp_divmodabs(quot_buf, rem_buf, self->digits, other->digits,
00431 self->used, other->used);
00432
00433
00434 if (rem_used==0)
00435 {
00436 if (quotient)
00437 {
00438
00439 size_t quot_used;
00440 quotient->sign = self->sign != other->sign;
00441
00442 quot_used = self->used - other->used + 1;
00443 if (!quot_buf[quot_used-1]) --quot_used;
00444 quotient->used = quot_used;
00445 ymp_reserve(quotient, quot_used);
00446 memcpy(quotient->digits, quot_buf, quot_used);
00447 }
00448 return 1;
00449 }
00450 else
00451 {
00452 return 0;
00453 }
00454 }
00455
00456
00457
00473 int
00474 ymp_div_if_divisible_digit(mp_ref_t quotient, mp_cref_t self, digit_t other)
00475 {
00476 digit_t rem;
00477 digit_t *quot_buf=NULL;
00478
00479
00480 if (other == 0)
00481 {
00482 ymp_fatal("0による除算");
00483 return 2;
00484 }
00485
00486
00487 if (ymp_is_zero(self))
00488 {
00489 if (quotient) quotient->used = 0;
00490 return 1;
00491 }
00492
00493
00494 if (quotient) YMP_TEMP_ALLOCATE(quot_buf, digit_t, self->used);
00495
00496
00497 rem = ymp_divmodabs_digit(quot_buf, self->digits, other, self->used);
00498
00499
00500 if (rem == 0)
00501 {
00502 if (quotient)
00503 {
00504
00505 size_t quot_used;
00506 quotient->sign = self->sign;
00507
00508 quot_used = self->used;
00509 if (!quot_buf[quot_used-1]) --quot_used;
00510 quotient->used = quot_used;
00511 ymp_reserve(quotient, quot_used);
00512 memcpy(quotient->digits, quot_buf, quot_used);
00513 }
00514 return 1;
00515 }
00516 else
00517 {
00518 return 0;
00519 }
00520 }
00521