mpdiv.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #include "os.h"
  2. #include <mp.h>
  3. #include "dat.h"
  4. // division ala knuth, seminumerical algorithms, pp 237-238
  5. // the numbers are stored backwards to what knuth expects so j
  6. // counts down rather than up.
  7. void
  8. mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder)
  9. {
  10. int j, s, vn, sign;
  11. mpdigit qd, *up, *vp, *qp;
  12. mpint *u, *v, *t;
  13. // divide bv zero
  14. if(divisor->top == 0)
  15. abort();
  16. // quick check
  17. if(mpmagcmp(dividend, divisor) < 0){
  18. if(remainder != nil)
  19. mpassign(dividend, remainder);
  20. if(quotient != nil)
  21. mpassign(mpzero, quotient);
  22. return;
  23. }
  24. // D1: shift until divisor, v, has hi bit set (needed to make trial
  25. // divisor accurate)
  26. qd = divisor->p[divisor->top-1];
  27. for(s = 0; (qd & mpdighi) == 0; s++)
  28. qd <<= 1;
  29. u = mpnew((dividend->top+2)*Dbits + s);
  30. if(s == 0 && divisor != quotient && divisor != remainder) {
  31. mpassign(dividend, u);
  32. v = divisor;
  33. } else {
  34. mpleft(dividend, s, u);
  35. v = mpnew(divisor->top*Dbits);
  36. mpleft(divisor, s, v);
  37. }
  38. up = u->p+u->top-1;
  39. vp = v->p+v->top-1;
  40. vn = v->top;
  41. // D1a: make sure high digit of dividend is less than high digit of divisor
  42. if(*up >= *vp){
  43. *++up = 0;
  44. u->top++;
  45. }
  46. // storage for multiplies
  47. t = mpnew(4*Dbits);
  48. qp = nil;
  49. if(quotient != nil){
  50. mpbits(quotient, (u->top - v->top)*Dbits);
  51. quotient->top = u->top - v->top;
  52. qp = quotient->p+quotient->top-1;
  53. }
  54. // D2, D7: loop on length of dividend
  55. for(j = u->top; j > vn; j--){
  56. // D3: calculate trial divisor
  57. mpdigdiv(up-1, *vp, &qd);
  58. // D3a: rule out trial divisors 2 greater than real divisor
  59. if(vn > 1) for(;;){
  60. memset(t->p, 0, 3*Dbytes); // mpvecdigmuladd adds to what's there
  61. mpvecdigmuladd(vp-1, 2, qd, t->p);
  62. if(mpveccmp(t->p, 3, up-2, 3) > 0)
  63. qd--;
  64. else
  65. break;
  66. }
  67. // D4: u -= v*qd << j*Dbits
  68. sign = mpvecdigmulsub(v->p, vn, qd, up-vn);
  69. if(sign < 0){
  70. // D6: trial divisor was too high, add back borrowed
  71. // value and decrease divisor
  72. mpvecadd(up-vn, vn+1, v->p, vn, up-vn);
  73. qd--;
  74. }
  75. // D5: save quotient digit
  76. if(qp != nil)
  77. *qp-- = qd;
  78. // push top of u down one
  79. u->top--;
  80. *up-- = 0;
  81. }
  82. if(qp != nil){
  83. mpnorm(quotient);
  84. if(dividend->sign != divisor->sign)
  85. quotient->sign = -1;
  86. }
  87. if(remainder != nil){
  88. mpright(u, s, remainder); // u is the remainder shifted
  89. remainder->sign = dividend->sign;
  90. }
  91. mpfree(t);
  92. mpfree(u);
  93. if(v != divisor)
  94. mpfree(v);
  95. }