/** Add an IntNum and an int, yielding a new IntNum. */ public static IntNum add(IntNum x, int y) { if (x.words == null) return IntNum.add (x.ival, y); IntNum result = new IntNum (0); result.setAdd (x, y); return result.canonicalize (); }
/** Divide two integers, yielding quotient and remainder. * @param x the numerator in the division * @param y the denominator in the division * @param quotient is set to the quotient of the result (iff quotient!=null) * @param remainder is set to the remainder of the result * (iff remainder!=null) * @param rounding_mode one of FLOOR, CEILING, TRUNCATE, or ROUND. */ public static void divide(IntNum x, IntNum y, IntNum quotient, IntNum remainder, int rounding_mode) { if ((x.words == null || x.ival <= 2) && (y.words == null || y.ival <= 2)) { long x_l = x.longValue (); long y_l = y.longValue (); if (x_l != long.MinValue && y_l != long.MinValue) { divide (x_l, y_l, quotient, remainder, rounding_mode); return; } } bool xNegative = x.isNegative (); bool yNegative = y.isNegative (); bool qNegative = xNegative ^ yNegative; int ylen = y.words == null ? 1 : y.ival; int[] ywords = new int[ylen]; y.getAbsolute (ywords); while (ylen > 1 && ywords[ylen-1] == 0) ylen--; int xlen = x.words == null ? 1 : x.ival; int[] xwords = new int[xlen+2]; x.getAbsolute (xwords); while (xlen > 1 && xwords[xlen-1] == 0) xlen--; int qlen, rlen; int cmpval = MPN.cmp (xwords, xlen, ywords, ylen); if (cmpval < 0) // abs(x) < abs(y) { // quotient = 0; remainder = num. int[] rwords = xwords; xwords = ywords; ywords = rwords; rlen = xlen; qlen = 1; xwords[0] = 0; } else if (cmpval == 0) // abs(x) == abs(y) { xwords[0] = 1; qlen = 1; // quotient = 1 ywords[0] = 0; rlen = 1; // remainder = 0; } else if (ylen == 1) { qlen = xlen; rlen = 1; ywords[0] = MPN.divmod_1 (xwords, xwords, xlen, ywords[0]); } else // abs(x) > abs(y) { // Normalize the denominator, i.e. make its most significant bit set by // shifting it normalization_steps bits to the left. Also shift the // numerator the same number of steps (to keep the quotient the same!). int nshift = MPN.count_leading_zeros (ywords[ylen-1]); if (nshift != 0) { // Shift up the denominator setting the most significant bit of // the most significant word. MPN.lshift (ywords, 0, ywords, ylen, nshift); // Shift up the numerator, possibly introducing a new most // significant word. int x_high = MPN.lshift (xwords, 0, xwords, xlen, nshift); xwords[xlen++] = x_high; } if (xlen == ylen) xwords[xlen++] = 0; MPN.divide (xwords, xlen, ywords, ylen); rlen = ylen; MPN.rshift0 (ywords, xwords, 0, rlen, nshift); qlen = xlen + 1 - ylen; if (quotient != null) { for (int i = 0; i < qlen; i++) xwords[i] = xwords[i+ylen]; } } while (rlen > 1 && ywords[rlen-1] == 0) rlen--; if (ywords[rlen-1] < 0) { ywords[rlen] = 0; rlen++; } // Now the quotient is in xwords, and the remainder is in ywords. bool add_one = false; if (rlen > 1 || ywords[0] != 0) { // Non-zero remainder i.e. in-exact quotient. switch (rounding_mode) { case TRUNCATE: break; case CEILING: if (qNegative == (rounding_mode == FLOOR)) add_one = true; break; case FLOOR: if (qNegative == (rounding_mode == FLOOR)) add_one = true; break; case ROUND: // int cmp = compare (remainder<<1, abs(y)); IntNum tmp = remainder == null ? new IntNum() : remainder; tmp.set (ywords, rlen); tmp = shift (tmp, 1); if (yNegative) tmp.setNegative(); int cmp = compare (tmp, y); // Now cmp == compare(sign(y)*(remainder<<1), y) if (yNegative) cmp = -cmp; add_one = (cmp == 1) || (cmp == 0 && (xwords[0]&1) != 0); break; } } if (quotient != null) { if (xwords[qlen-1] < 0) { xwords[qlen] = 0; qlen++; } quotient.set (xwords, qlen); if (qNegative) { if (add_one) // -(quotient + 1) == ~(quotient) quotient.setInvert (); else quotient.setNegative (); } else if (add_one) quotient.setAdd (1); } if (remainder != null) { // The remainder is by definition: X-Q*Y remainder.set (ywords, rlen); if (add_one) { // Subtract the remainder from Y: // abs(R) = abs(Y) - abs(orig_rem) = -(abs(orig_rem) - abs(Y)). IntNum tmp; if (y.words == null) { tmp = remainder; tmp.set(yNegative ? ywords[0] + y.ival : ywords[0] - y.ival); } else tmp = IntNum.add(remainder, y, yNegative ? 1 : -1); // Now tmp <= 0. // In this case, abs(Q) = 1 + floor(abs(X)/abs(Y)). // Hence, abs(Q*Y) > abs(X). // So sign(remainder) = -sign(X). if (xNegative) remainder.setNegative(tmp); else remainder.set(tmp); } else { // If !add_one, then: abs(Q*Y) <= abs(X). // So sign(remainder) = sign(X). if (xNegative) remainder.setNegative (); } } }