View Javadoc
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  	 * The actual exponentiation of two Reals
25  	 *
26  	 * @param r1 the base
27  	 * @param r2 the exponent
28  	 * @return the result of the operation
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  		// case b^-1 with b=0 or neg
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  		// if negative base, result undefined is irrational exponent or rational
65  		// exponent with even denominator
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  			// is the expo in not an integer
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 	 * The actual exponentiation of two Integers.
103 	 *
104 	 * @param i1 the base
105 	 * @param i2 the exponent
106 	 * @return the result of the operation
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 	 * The exponentiation of complex numbers
129 	 *
130 	 * @param c1 the base
131 	 * @param c2 the exponent
132 	 * @throws ArithmeticException since the exponentiation of complex numbers is
133 	 *                             impossible
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 	 * The actual exponentiation of two rational numbers
142 	 *
143 	 * @param q1 the base
144 	 * @param q2 the exponent
145 	 * @return the result of the exponentiation
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 }