![JAR search and dependency download from the Maven repository](/logo.png)
io.github.frogif.calculator.number.impl.PositiveIntegerUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of calculator-number Show documentation
Show all versions of calculator-number Show documentation
support general calculate for rational number and integer number, and precision lossless.
The newest version!
package io.github.frogif.calculator.number.impl;
import io.github.frogif.calculator.number.exception.DivideByZeroException;
import io.github.frogif.calculator.number.exception.NumericOverflowException;
/**
* 正整数工具类 所有 正整数的大数操作, 都将在这里完成 包内可见, 主要是这个类并不符合知道最少原则, 暴露出去没有意义,
* 主要是为IntegerNumber服务
*/
class PositiveIntegerUtil {
private PositiveIntegerUtil(){
// hide constructor
}
static final int[] ZERO = new int[1];
static final int[] ONE = new int[]{1};
/**
* 该对象表示的数的实际进制为SCALE
* 这里选择每9个十进制位作为一个进制位
*/
static final int SCALE = 1000000000;
/**
* int数组中单个元素中所能存的数字的可读长度
* 当前值是: 9
*/
static final int SINGLE_ELEMENT_LEN;
static {
int i = 1;
int len = 0;
while(i < SCALE){
i *= 10;
len++;
}
SINGLE_ELEMENT_LEN = len;
}
/**
* 获取一个正整数非0首位位置
* @param arr 待判断的正整数
* @return 返回非0首位位置, 如果该数是0, 则也会返回0
*/
static int highPos(int[] arr){
int l = arr.length - 1;
while(arr[l] < 1 && l > 0){ l--; }
return l;
}
/**
* 正整数相加
* 模拟手算加法
* @param left 左侧数
* @param lh left数非0高位位置
* @param right 右侧数
* @param rh right数非0高位位置
* @return 返回加和结果, 高位可能存在冗余
*/
static int[] add(int[] left, int lh, int[] right, int rh) {
int[] longer;
int[] shorter;
int longLen;
int shortLen;
if(lh > rh){
longer = left;
longLen = lh + 1;
shorter = right;
shortLen = rh + 1;
}else{
longer = right;
longLen = rh + 1;
shorter = left;
shortLen = lh + 1;
}
int m = 0;
int[] result = new int[longLen + 1];
int cursor = 0;
long carry = 0;
for(; m < shortLen; m++){
long mr = shorter[m] + longer[m] + carry;
carry = mr / SCALE;
result[cursor++] = (int) (mr % SCALE);
}
for(; m < longLen; m++){
long mr = longer[m] + carry;
carry = mr / SCALE;
result[cursor++] = (int) (mr % SCALE);
}
if(carry > 0){
result[m] = (int) carry;
}
return result;
}
/**
* 一个加数为单字的正整数加法
* @param left 正整数
* @param lh left的非0高位位置
* @param right 正整数
* @return 和
*/
static int[] addOneWord(int[] left, int lh, int right){
if(right < 0){
throw new IllegalArgumentException("can't support operate for " + right);
}
if(right == 0){ return left; }
int[] result = new int[lh + 2];
int carry = right;
for(int i = 0; i < lh + 1; i++){
int s = left[i] + carry;
if(s < SCALE){
carry = 0;
result[i] = s;
}else{
result[i] = s - SCALE;
carry = s / SCALE;
}
}
if(carry == 1){
result[result.length - 1] = 1;
}
return result;
}
/**
* 比较两个正整数的大小
* @param a 待比较数a
* @param highA 待比较数a的非0高位的位置
* @param b 待比较数b
* @param highB 待比较数b的非0高位的位置
* @return 比较结果, 等于0, 则两数相等; 小于0则, a < b; 大于0, 则a > b
*/
static int compare(int[] a, int highA, int[] b, int highB){
if(a == b){ return 0; }
if(highA == highB){
// 从高位开始遍历
for(int i = highA; i > -1; i--){
if(a[i] != b[i]){
return a[i] - b[i];
}
}
return 0;
}else{
return highA - highB;
}
}
/**
* 正整数相减
* 输入参数需要保证minuend一定大于subtrahend(因为不会返回负数)
* @param minuend 被减数
* @param hm 被减数的非0高位位置
* @param subtrahend 减数
* @param hs 减数非0高位位置
* @return 差, 高位可能存在冗余
*/
static int[] subtract(int[] minuend, int hm, int[] subtrahend, int hs) {
int mLen = hm + 1;
int sLen = hs + 1;
int[] result = new int[mLen];
int borrow = 0; // 借位
int m = 0;
int more = 0; // 记录result数组中有多少位冗余
int r;
int stepRes; // 记录每一步运算的运算结果
for(; m < sLen; m++){
stepRes = minuend[m] - subtrahend[m] - borrow;
if(stepRes < 0){
r = stepRes + SCALE;
borrow = 1;
}else{
r = stepRes;
borrow = 0;
}
more = r == 0 ? (more + 1) : 0;
result[m] = r;
}
for(; m < mLen; m++){
stepRes = minuend[m] - borrow;
if(stepRes < 0){
r = stepRes + SCALE;
borrow = 1;
}else{
borrow = 0;
r = stepRes;
}
more = r == 0 ? (more + 1) : 0;
result[m] = r;
}
if(more == result.length){
result = ZERO;
}
return result;
}
/**
* 减数为单字的正整数减法
* @param minuend 被减数
* @param h minuend非0高位位置
* @param subtrahend 减数
* @return 差
*/
static int[] subtractOneWord(int[] minuend, int h, int subtrahend){
if(subtrahend < 0){
throw new IllegalArgumentException("can't support operate for " + subtrahend);
}
if(subtrahend == 0){ return minuend; }
if(h == 0 && minuend[0] < subtrahend){
throw new IllegalArgumentException("can't subtract.");
}
int[] result = new int[h + 1];
int borrow = subtrahend;
for(int i = 0; i < result.length; i++){
if(minuend[i] < borrow){
result[i] = SCALE + minuend[i] - borrow;
borrow = 1;
}else{
result[i] = minuend[i] - borrow;
borrow = 0;
}
}
return result;
}
/**
* 正整数乘法
* @param left 左侧因数
* @param lh left的非0高位位置
* @param right 右侧因数
* @param rh right的非零高位位置
* @return 积, 高位可能存在冗余
*/
static int[] multiply(int[] left, int lh, int[] right, int rh){
if(lh == 0 || rh == 0){
if(lh == 0 && left[0] == 1){
return right;
}else if(rh == 0 && right[0] == 1){
return left;
}else if(lh == rh && (left[0] == 0 || right[0] == 0)){
return ZERO;
}
}
return ordinaryMultiply(left, lh, right, rh);
}
/**
* 平凡正数乘法
* @param left 左侧因数
* @param lh left的非0高位位置
* @param right 右侧因数
* @param rh right的非零高位位置
* @return 积, 高位可能存在冗余
*/
private static int[] ordinaryMultiply(int[] left, int lh, int[] right, int rh){
int[] result = new int[]{0};
int offset = 0;
for(int j = 0; j < rh + 1; j++){
int rn = right[j];
long carry = 0;
int[] midResult = new int[lh + 2 + offset];
int i = 0;
for(; i < lh + 1; i++){
long res = (long)left[i] * (long)rn + carry;
carry = res / SCALE;
midResult[i + offset] = (int) (res % SCALE);
}
int midHigh;
if(carry > 0){
midResult[i + offset] = (int) carry;
midHigh = midResult.length - 1;
}else{
midResult[i + offset] = -1;
midHigh = midResult.length - 2;
}
result = add(result, highPos(result), midResult, midHigh);
offset++;
}
return result;
}
/**
* 正整数除法
* @param dividend 被除数
* @param deh dividend非0高位位置
* @param divisor 除数
* @param drh divisor非0高位位置
* @return 商和余数, 数组的第一个元素是商, 第二个元素是余数
*/
static int[][] divide(int[] dividend, int deh, int[] divisor, int drh){
if(drh == 0) {
if(divisor[0] == 0){
throw new DivideByZeroException();
}
return divideOneWord(dividend, deh, divisor[0]);
}
int cmp = compare(dividend, deh, divisor, drh);
if(cmp < 0){ // 被除数小于除数
return new int[][]{ZERO, dividend};
}else if(cmp == 0){
return new int[][]{ONE, ZERO};
}
return divideKnuth(dividend, deh, divisor, drh);
}
/**
* Knuth D正整数除法(估商法)
* @param dividend 被除数
* @param deh dividend非0高位位置
* @param divisor 除数
* @param drh divisor非0高位位置
* @return 商和余数, 数组的第一个元素是商, 第二个元素是余数
*/
private static int[][] divideKnuth(int[] dividend, int deh, int[] divisor, int drh){
int m = deh - drh;
int n = drh + 1;
int[] quotient = new int[m + 1];
/*
* 估商法理论基础:
* 若令 qhat = min(ceil((u0*b-u1)/v1), b - 1), 当 v1 >= ceil(b / 2)时, qhat - 2 <= q <= qhat
* 因此可以在小于等于2次迭代的情况下, 求出真正的商值
*/
// D1 规范化
int d = SCALE / (divisor[drh] + 1);
int[] u = new int[deh + 2];
copyAndShift(dividend, deh, d, u);
int[] v = new int[drh + 2];
copyAndShift(divisor, drh, d, v);
for(int j = m; j > -1; j--){ // D2 初始化j
// D3 计算qhat
long uH = u[j + n] * (long)SCALE + u[j + n - 1];
int vH = v[n - 1];
long qhat = uH / vH;
long rhat = uH % vH;
int v2 = v[n - 2];
int u2 = u[j + n - 2];
while((qhat == SCALE || qhat * v2 > (SCALE * rhat + u2)) && (rhat < SCALE)){ // assert 这个循环最多执行2次
qhat--;
rhat += vH;
}
// D4 乘和减
long borrow = 0;
for(int i = 0; i < n + 1; i++){
long a = u[j + i];
long b = qhat * v[i] + borrow;
if(a < b){
borrow = b / SCALE;
a = SCALE * borrow + a;
if(a < b){
a += SCALE;
borrow++;
}
}else{
borrow = 0;
}
u[j + i] = (int) (a - b);
}
// D6 往回加
if(borrow > 0){
qhat--;
long carry = 0;
for(int i = 0; i < n + 1; i++){
int a = u[j + i];
int b = v[i];
long sum = carry + a + b;
carry = sum / SCALE;
u[j + i] = (int) (sum % SCALE);
}
}
quotient[j] = (int) qhat;
}
// 去除高位0
int i = highPos(quotient);
if(i < quotient.length - 1){
int[] nq = new int[i + 1];
for(; i > -1; i--){
nq[i] = quotient[i];
}
quotient = nq;
}
return new int[][]{quotient, divideOneWord(u, highPos(u), d)[0]};
}
/**
* Knuth D算法 整数规范化
* @param arr 待规范化整数
* @param h 非0高位位置
* @param d 规范化因数d
* @param dest 目标整数容器
*/
private static void copyAndShift(int[] arr, int h, int d, int[] dest){
/*
* 因为d = power(2, shift)
* 索引 u * d = u << shift
* 又由于u * d的过程可能产生进位
* 进位为 u >>> (SCALE_LEAD_ZERO_COUNT - shift)
*/
long carry = 0;
int i = 0;
for(; i < h + 1; i++){
long r = ((long)arr[i] * d) + carry;
dest[i] = (int) (r % SCALE);
carry = r / SCALE;
}
if(carry > 0){
dest[i] = (int) carry;
}
}
/**
* 除数为单字的正整数除法
* @param dividend 被除数
* @param dh dividend非0高位位置
* @param divisor 除数
* @return 商和余数, 数组的第一个元素是商, 第二个元素是余数
*/
private static int[][] divideOneWord(int[] dividend, int dh, int divisor){
int[] result;
int len = dh + 1;
long q = dividend[len - 1] / divisor;
long rem;
if(q == 0 && len > 1){
result = new int[len - 1];
long d = ((long)dividend[len - 1] * SCALE + dividend[len - 2]);
result[len - 2] = (int) (d / divisor);
rem = (int) (d % divisor);
}else{
result = new int[len];
result[len - 1] = (int) q;
rem = dividend[len - 1] % divisor;
}
for(int i = result.length - 2; i > -1; i--){
long d = dividend[i] + rem * SCALE;
if(d < divisor && d != 0){
d = SCALE + d;
q = d / divisor;
rem = d % divisor;
result[i + 1]--;
}else{
q = d / divisor;
rem = d % divisor;
}
result[i] = (int) q;
}
return new int[][]{result, new int[]{(int) rem}};
}
/**
* 判断一个元素是否为奇数
* @param values 待判断整数
* @return true : 奇数; false : 偶数
*/
static boolean isEven(int[] values){
return (values[0] & 1) == 0;
}
/**
* 获取两个正整数的最大公约数
* 1. a, b都是偶数, 则gcd(a, b) = 2 * gcd(a/2, b/2)
* 2. a是偶数, b是奇数, 则gcd(a, b) = gcd(a/2, b)
* 3. a是奇数, b是偶数, 则gcd(a, b) = gcd(a, b/2)
* 4. 如果a, b都是奇数, 则a - b是偶数, gcd(a, b) = gcd(|a - b|, min(a, b))
* @param a 数a
* @param ah a的非0高位位置
* @param b 数b
* @param bh b的非0高位位置
* @return 最大公约数, 如果入参中有一个为0, 则返回另一个
*/
static int[] gcd(int[] a, int ah, int[] b, int bh){
if(ah == 0 && a[0] == 0){ return b; }
if(bh == 0 && b[0] == 0){ return a; }
int[] k = ONE;
while(isEven(a) && isEven(b)){
k = twice(k, highPos(k));
a = half(a, highPos(a));
b = half(b, highPos(b));
}
while(true){
while(isEven(a)){
a = half(a, highPos(a));
}
while(isEven(b)){
b = half(b, highPos(b));
}
int mark = compare(a, (ah = highPos(a)), b, (bh = highPos(b)));
if(mark == 0){
break;
}else if(mark > 0){ // a > b
a = subtract(a, ah, b, bh);
}else{
b = subtract(b, bh, a, ah);
}
}
return multiply(k, highPos(k), b, highPos(b));
}
/**
* 一个数乘以2
* @param num 待乘以2的数
* @param h num非0高位位置
* @return 乘以2之后的结果
*/
private static int[] twice(int[] num, int h){
int[] result = new int[h + 2];
int carry = 0;
int i = 0;
for(; i < h + 1; i++){
int m = (num[i] << 1) + carry;
result[i] = m % SCALE;
carry = m / SCALE;
}
if(carry > 0){
result[i] = carry;
}
return result;
}
/**
* 除以2
* @param num 待除以2的数
* @param h num非0高位位置
* @return 除以2之后的结果
*/
private static int[] half(int[] num, int h){
if(h == 0){
return new int[]{ num[0] >> 1 };
}
int d = num[h];
int[] result;
if(d > 1){
result = new int[h + 1];
}else{
result = new int[h];
d = (SCALE + num[--h]);
}
result[h] = d >> 1;
int rem = d & 1;
for(int i = h - 1; i > -1; i--){
d = rem * SCALE + num[i];
result[i] = d >> 1;
rem = d & 1;
}
return result;
}
/**
* 十进制左移
* @param num 待左移的数
* @param highPos num非0高位位置
* @param bcount 左移位数
* @return 左移的结果
*/
public static int[] decLeftShift(int[] num, int highPos, int bcount){
int len = highPos + 1 + bcount / SINGLE_ELEMENT_LEN;
int r = bcount % SINGLE_ELEMENT_LEN;
int[] res;
if(r > 0){
len += 1;
res = new int[len];
int f = 1;
while(r > 0){
f *= 10;
r--;
}
int e = SCALE / f;
int up = 0;
int j = len - 1;
for(int i = highPos; i >= 0; i--, j--){
res[j] = num[i] / e + up * f;
up = num[i] % e;
}
if(up > 0){
res[j] = up * f;
}
}else{
res = new int[len];
int j = len - 1;
for(int i = highPos; i >= 0; i--, j--){
res[j] = num[i];
}
}
return res;
}
/**
* 十进制左移
* @param num 待左移的数
* @param highPos num非0高位位置
* @param bcount 左移位数
* @param bHighPos 左移位数高位
* @return 左移的结果
*/
public static int[] decLeftShift(int[] num, int highPos, int[] bcount, int bHighPos){
if(bHighPos == 0){
return decLeftShift(num, highPos, bcount[0]);
}else{
int[][] divideResult = divideOneWord(bcount, bHighPos, 9);
int[] q = divideResult[0];
int[] r = divideResult[1]; // 显然r < SCALE
int rVal = r[0];
long qInt = 0;
if(q.length > 2){ // 超出数组最大容量
throw new NumericOverflowException();
}else if(q.length == 2){
long l = q[1] * 1L * SCALE + q[0];
if(l > Integer.MAX_VALUE){
throw new NumericOverflowException();
}
qInt = (int)l;
}else{
qInt = q[0];
}
num = decLeftShift(num, highPos, rVal);
highPos = highPos(num);
long len = qInt + highPos + 1;
if(len > Integer.MAX_VALUE){
throw new NumericOverflowException();
}
int[] result = new int[(int)len];
for(int j = highPos, i = (int)len - 1; j >= 0; i--, j--){
result[i] = num[j];
}
return result;
}
}
/**
* 将正整数转为数组形式
* @param absNum 正整数
* @return 数值数组
*/
public static int[] convertToNumberArray(long absNum){
int[] result;
if(absNum < PositiveIntegerUtil.SCALE){
result = new int[1];
result[0] = (int)absNum;
}else if(absNum < (long) PositiveIntegerUtil.SCALE * PositiveIntegerUtil.SCALE){
result = new int[2];
result[0] = (int)(absNum % PositiveIntegerUtil.SCALE);
result[1] = (int)(absNum / PositiveIntegerUtil.SCALE);
}else{
result = new int[3];
result[0] = (int)(absNum % PositiveIntegerUtil.SCALE);
result[1] = (int)(absNum / PositiveIntegerUtil.SCALE % PositiveIntegerUtil.SCALE);
result[2] = (int)(absNum / PositiveIntegerUtil.SCALE / PositiveIntegerUtil.SCALE);
}
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy