001/* 002 * $Id$ 003 */ 004 005package edu.jas.arith; 006 007 008import java.math.MathContext; 009 010 011/** 012 * Root computation algorithms. Roots for BigInteger and BigDecimals. 013 * @author Heinz Kredel 014 */ 015public class Roots { 016 017 018 /** 019 * Integer n-th root. Uses BigDecimal and newton iteration. R is the n-th 020 * root of A. 021 * @param A big integer. 022 * @param n long. 023 * @return the n-th root of A. 024 */ 025 public static BigInteger root(BigInteger A, int n) { 026 if (n == 1) { 027 return A; 028 } 029 if (n == 2) { 030 return sqrt(A); 031 } 032 if (n < 1) { 033 throw new IllegalArgumentException("negative root not defined"); 034 } 035 if (A == null || A.isZERO() || A.isONE()) { 036 return A; 037 } 038 if (A.signum() < 0) { 039 throw new ArithmeticException("root of negative not defined: " + A); 040 } 041 // ensure enough precision 042 int s = A.val.bitLength() + 2; 043 MathContext mc = new MathContext(s); 044 //System.out.println("mc = " + mc); 045 BigDecimal Ap = new BigDecimal(A.val, mc); 046 //System.out.println("Ap = " + Ap); 047 BigDecimal Ar = root(Ap, n); 048 //System.out.println("Ar = " + Ar); 049 java.math.BigInteger RP = Ar.val.toBigInteger(); 050 BigInteger R = new BigInteger(RP); 051 while (true) { 052 BigInteger P = R.power(n); //Power.positivePower(R, n); 053 //System.out.println("P = " + P); 054 if (A.compareTo(P) >= 0) { 055 break; 056 } 057 R = R.subtract(BigInteger.ONE); 058 } 059 return R; 060 } 061 062 063 /** 064 * Integer square root. Uses BigDecimal and newton iteration. R is the 065 * square root of A. 066 * @param A big integer. 067 * @return the square root of A. 068 */ 069 public static BigInteger sqrt(BigInteger A) { 070 if (A == null || A.isZERO() || A.isONE()) { 071 return A; 072 } 073 if (A.signum() < 0) { 074 throw new ArithmeticException("root of negative not defined: " + A); 075 } 076 // ensure enough precision 077 int s = A.val.bitLength() + 2; 078 MathContext mc = new MathContext(s); 079 //System.out.println("mc = " + mc); 080 // newton iteration 081 BigDecimal Ap = new BigDecimal(A.val, mc); 082 //System.out.println("Ap = " + Ap); 083 BigDecimal Ar = sqrt(Ap); 084 //System.out.println("Ar = " + Ar); 085 java.math.BigInteger RP = Ar.val.toBigInteger(); 086 BigInteger R = new BigInteger(RP); 087 while (true) { 088 BigInteger P = R.multiply(R); 089 //System.out.println("P = " + P); 090 if (A.compareTo(P) >= 0) { 091 break; 092 } 093 R = R.subtract(BigInteger.ONE); 094 } 095 return R; 096 } 097 098 099 /** 100 * Integer square root. Uses BigInteger only. R is the square root of A. 101 * @param A big integer. 102 * @return the square root of A. 103 */ 104 public static BigInteger sqrtInt(BigInteger A) { 105 if (A == null || A.isZERO() || A.isONE()) { 106 return A; 107 } 108 int s = A.signum(); 109 if (s < 0) { 110 throw new ArithmeticException("root of negative not defined: " + A); 111 } 112 BigInteger R, R1, d; 113 int log2 = A.val.bitLength(); 114 //System.out.println("A = " + A + ", log2 = " + log2); 115 int rootlog2 = log2 - log2 / 2; 116 R = new BigInteger(A.val.shiftRight(rootlog2)); 117 //System.out.println("R = " + R + ", rootlog2 = " + rootlog2); 118 d = R; 119 while (!d.isZERO()) { 120 d = new BigInteger(d.val.shiftRight(1)); // div 2 121 R1 = R.sum(d); 122 s = A.compareTo(R1.multiply(R1)); 123 if (s == 0) { 124 return R1; 125 } 126 if (s > 0) { 127 R = R1; 128 } 129 //System.out.println("R1 = " + R1); 130 //System.out.println("d = " + d); 131 } 132 while (true) { 133 R1 = R.sum(BigInteger.ONE); 134 //System.out.println("R1 = " + R1); 135 s = A.compareTo(R1.multiply(R1)); 136 if (s == 0) { 137 return R1; 138 } 139 if (s > 0) { 140 R = R1; 141 } 142 if (s < 0) { 143 return R; 144 } 145 } 146 //return R; 147 } 148 149 150 /** 151 * Square root. R is the square root of A. 152 * @param A big decimal. 153 * @return the square root of A. 154 */ 155 public static BigDecimal sqrt(BigDecimal A) { 156 if (A == null || A.isZERO() || A.isONE()) { 157 return A; 158 } 159 if (A.signum() < 0) { 160 throw new ArithmeticException("root of negative not defined: " + A); 161 } 162 // for small A use root of inverse 163 if (A.abs().val.compareTo(BigDecimal.ONE.val) < 0) { 164 BigDecimal Ap = A.inverse(); 165 Ap = sqrt(Ap); 166 Ap = Ap.inverse(); 167 //System.out.println("sqrt(A).inverse() = " + Ap); 168 return Ap; 169 } 170 // ensure enough precision 171 MathContext mc = A.context; 172 BigDecimal eps = new BigDecimal("0.1"); 173 int p = Math.max(mc.getPrecision(), java.math.MathContext.DECIMAL64.getPrecision()); 174 //java.math.MathContext.UNLIMITED.getPrecision() == 0 175 eps = eps.power(p / 2); 176 // newton iteration 177 BigDecimal Ap = new BigDecimal(A.val, mc); 178 BigDecimal ninv = new BigDecimal(0.5, mc); 179 BigDecimal R1, R = Ap.multiply(ninv); // initial guess 180 BigDecimal d; 181 int i = 0; 182 while (true) { 183 R1 = R.sum(Ap.divide(R)); 184 R1 = R1.multiply(ninv); // div n 185 d = R.subtract(R1).abs(); 186 R = R1; 187 if (d.val.compareTo(eps.val) <= 0) { 188 //System.out.println("d = " + d + ", R = " + R); 189 break; 190 } 191 if (i++ % 11 == 0) { 192 eps = eps.sum(eps); 193 } 194 //System.out.println("eps = " + eps + ", d = " + d); 195 } 196 return R; 197 } 198 199 200 /** 201 * N-th root. R is the n-th root of A. 202 * @param A big decimal. 203 * @param n long. 204 * @return the n-th root of A. 205 */ 206 public static BigDecimal root(BigDecimal A, int n) { 207 if (n == 1) { 208 return A; 209 } 210 if (n == 2) { 211 return sqrt(A); 212 } 213 if (n < 1) { 214 throw new IllegalArgumentException("negative root not defined"); 215 } 216 if (A == null || A.isZERO() || A.isONE()) { 217 return A; 218 } 219 if (A.signum() < 0) { 220 throw new ArithmeticException("root of negative not defined: " + A); 221 } 222 // for small A use root of inverse 223 if (A.abs().val.compareTo(BigDecimal.ONE.val) < 0) { 224 BigDecimal Ap = A.inverse(); 225 //System.out.println("A.inverse() = " + Ap); 226 Ap = root(Ap, n); 227 return Ap.inverse(); 228 } 229 // ensure enough precision 230 MathContext mc = A.context; 231 BigDecimal eps = new BigDecimal("0.1"); 232 int p = Math.max(mc.getPrecision(), java.math.MathContext.DECIMAL64.getPrecision()); 233 //java.math.MathContext.UNLIMITED.getPrecision() == 0 234 eps = eps.power((p / 3) * 2); 235 // newton iteration 236 BigDecimal Ap = A; 237 BigDecimal N = new BigDecimal(n, mc); 238 BigDecimal ninv = new BigDecimal(1.0 / n, mc); 239 BigDecimal nsub = new BigDecimal(1.0, mc); // because of precision 240 nsub = nsub.subtract(ninv); 241 BigDecimal P, R1, R = Ap.multiply(ninv); // initial guess 242 BigDecimal d; 243 int i = 0; 244 while (true) { 245 P = R.power(n - 1); //Power.positivePower(R, n - 1); 246 R1 = Ap.divide(P.multiply(N)); 247 R1 = R.multiply(nsub).sum(R1); 248 d = R.subtract(R1).abs(); 249 R = R1; 250 if (d.val.compareTo(eps.val) <= 0) { 251 //System.out.println("d.val = " + d.val); 252 break; 253 } 254 if (i++ % 11 == 0) { 255 eps = eps.sum(eps); 256 } 257 } 258 // System.out.println("eps = " + eps + ", d = " + d); 259 return R; 260 } 261 262 263 /** 264 * Complex decimal number square root. 265 * @param a big decimal complex. 266 * @return sqrt(a). 267 */ 268 public static BigDecimalComplex sqrt(BigDecimalComplex a) { 269 if (a.isZERO() || a.isONE()) { 270 return a; 271 } 272 if (a.im.isZERO() && a.re.signum() > 0) { 273 BigDecimal v = Roots.sqrt(a.re); 274 return new BigDecimalComplex(v); 275 } 276 BigDecimal r = a.re.abs().sum(a.abs().re); 277 BigDecimal t = new BigDecimal(2); 278 BigDecimal ti = new BigDecimal("0.5"); 279 //BigDecimal u = r.divide(t); 280 BigDecimal u = r.multiply(ti); 281 BigDecimal v = Roots.sqrt(u); 282 //System.out.println("r = " + r + ", a = " + a); 283 //System.out.println("v = " + v + ", u = " + u); 284 if (a.re.signum() >= 0) { 285 return new BigDecimalComplex(v, a.im.divide(v.multiply(t))); 286 } 287 u = v; 288 if (a.im.signum() < 0) { 289 u = u.negate(); 290 } 291 return new BigDecimalComplex(a.im.abs().divide(v.multiply(t)), u); 292 } 293 294 295 /** 296 * Square root. R is the square root approximation of A. 297 * Convert to BigDecimal and compute square root. 298 * @param A big rational. 299 * @return the square root approximation of A. 300 */ 301 public static BigRational sqrt(BigRational A) { 302 if (A == null || A.isZERO() || A.isONE()) { 303 return A; 304 } 305 if (A.signum() < 0) { 306 throw new ArithmeticException("root of negative not defined: " + A); 307 } 308 BigDecimal ad = new BigDecimal(A); 309 BigDecimal s = sqrt(ad); 310 //System.out.println("s = " + s); 311 BigRational S = new BigRational(s.toString()); 312 return S; 313 } 314 315 316 /** 317 * Square root. R is the square root approximation of A. 318 * Convert to BigDecimalComplex and compute square root. 319 * @param A big complex rational. 320 * @return the square root approximation of A. 321 */ 322 public static BigComplex sqrt(BigComplex A) { 323 if (A == null || A.isZERO() || A.isONE()) { 324 return A; 325 } 326 BigDecimalComplex ad = new BigDecimalComplex(A); 327 BigDecimalComplex s = sqrt(ad); 328 //System.out.println("s = " + s); 329 BigComplex S = new BigComplex(s.toString()); 330 return S; 331 } 332 333}