1 package calculator.operations;
2
3 import java.math.BigDecimal;
4 import java.math.BigInteger;
5 import java.util.List;
6
7 import calculator.Expression;
8 import calculator.IllegalConstruction;
9 import calculator.atoms.*;
10 import calculator.atoms.visitor.AtomCaster;
11 import ch.obermuhlner.math.big.BigDecimalMath;
12
13 import org.apache.commons.numbers.fraction.Fraction;
14
15 public final class Power extends Operation {
16
17 public Power(List<Expression> elist) throws IllegalConstruction {
18 super(elist);
19 symbol = "**";
20 neutral = 1;
21 }
22
23
24
25
26
27
28
29
30 @Override
31 public Real op(Real r1, Real r2) {
32
33 if (r1.isNan() || r2.isNan())
34 return Real.nan();
35
36 BigDecimal base = r1.getValue();
37 BigDecimal exponent = r2.getValue();
38
39
40 if (base.compareTo(new BigDecimal(0)) == 0 && exponent.compareTo(new BigDecimal(0)) <= 0) {
41 return Real.nan();
42 }
43
44 if (r2.isPlusInf()) {
45 if (r1.isMinusInf())
46 return Real.nan();
47 else if (base.compareTo(new BigDecimal(0)) > 0 && base.compareTo(new BigDecimal(1)) < 0)
48 return new Real(0);
49
50 else if (base.compareTo(new BigDecimal(0)) == 0 || base.compareTo(new BigDecimal(1)) == 0)
51 return Real.nan();
52
53 return Real.plusInf();
54 }
55
56 if (r2.isMinusInf()) {
57 if (r1.isMinusInf())
58 return Real.nan();
59 if (base.compareTo(new BigDecimal(0)) > 0 && base.compareTo(new BigDecimal(1)) < 0)
60 return Real.plusInf();
61 return new Real(0);
62 }
63
64
65
66
67 if (base.compareTo(new BigDecimal(0)) < 0) {
68
69 BigInteger[] expFrac = toRational(exponent);
70 BigDecimal numerator = new BigDecimal(expFrac[0]);
71 BigDecimal denominator = new BigDecimal(expFrac[1]);
72
73 if (expFrac[1].mod(new BigInteger("2")).equals(BigInteger.ONE)) {
74
75 BigDecimal root = BigDecimalMath.pow(base.abs(), BigDecimal.ONE.divide(denominator), Real.context)
76 .negate();
77 BigDecimal powed = BigDecimalMath.pow(root, numerator, Real.context);
78 return new Real(powed);
79 }
80
81
82 if (r2.getValue().stripTrailingZeros().scale() > 0)
83 return Real.nan();
84 }
85 BigDecimal powed = BigDecimalMath.pow(base, exponent, Real.context);
86 return new Real(powed);
87 }
88
89 public static BigInteger[] toRational(BigDecimal decimal) {
90 int scale = decimal.scale();
91 if (scale <= 0) {
92 return new BigInteger[] { decimal.toBigInteger(), BigInteger.ONE };
93 } else {
94 BigInteger denominator = BigInteger.TEN.pow(scale);
95 BigInteger numerator = decimal.unscaledValue();
96 BigInteger d = numerator.gcd(denominator);
97 return new BigInteger[] { numerator.divide(d), denominator.divide(d) };
98 }
99 }
100
101
102
103
104
105
106
107
108 @Override
109 public Real op(IntegerAtom i1, IntegerAtom i2) {
110 int base = i1.getValue();
111 int exp = i2.getValue();
112 if (base == 0 && exp < 0) {
113 return Real.nan();
114 }
115 try {
116 if (exp < 0) {
117 long denom = Math.powExact(base, -exp);
118 return new Real(1.0 / denom);
119 } else {
120 return new Real(Math.powExact(base, exp));
121 }
122 } catch (ArithmeticException e) {
123 throw new ArithmeticException("Overflow");
124 }
125 }
126
127
128
129
130
131
132
133
134
135 @Override
136 public Complex op(Complex c1, Complex c2) {
137 throw new ArithmeticException("The exponentiation of two complex numbers is impossible");
138 }
139
140
141
142
143
144
145
146
147 @Override
148 public Real op(Rationnal q1, Rationnal q2) {
149 AtomCaster aC = new AtomCaster(AtomType.REAL);
150 aC.visit(q1);
151 Real r1 = (Real) aC.getResult();
152 aC.visit(q2);
153 Real r2 = (Real) aC.getResult();
154 return op(r1, r2);
155 }
156 }