1 package org.djutils.math.functions;
2
3 import java.util.Objects;
4 import java.util.SortedSet;
5 import java.util.TreeSet;
6
7 import org.djutils.exceptions.Throw;
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 public class Power implements MathFunction
23 {
24
25 private final double weight;
26
27
28 private final double power;
29
30
31 private final MathFunction chain;
32
33
34 private static final SuperScript SUPER_SCRIPT = new SuperScript();
35
36
37
38
39
40
41
42 public Power(final MathFunction chain, final double weight, final double power)
43 {
44 this.weight = weight;
45 this.power = weight == 0.0 ? 1.0 : power;
46 this.chain = chain;
47 }
48
49
50
51
52
53
54 public Power(final double weight, final double power)
55 {
56 this(null, weight, power);
57 }
58
59
60
61
62
63
64 public Power(final MathFunction chain, final double power)
65 {
66 this(chain, 1.0, power);
67 }
68
69
70
71
72
73 public Power(final double power)
74 {
75 this(1.0, power);
76 }
77
78 @Override
79 public double get(final double x)
80 {
81 if (this.weight == 0.0)
82 {
83 return 0.0;
84 }
85
86 if (this.power == 0.0)
87 {
88 return this.weight;
89 }
90 double xValue = this.chain == null ? x : this.chain.get(x);
91 if (this.power == 1.0)
92 {
93 return this.weight * xValue;
94 }
95 return this.weight * Math.pow(xValue, this.power);
96 }
97
98 @Override
99 public MathFunction getDerivative()
100 {
101 if (this.weight == 0.0 || this.power == 0.0)
102 {
103 return Constant.ZERO;
104 }
105 if (this.power == 1.0)
106 {
107 if (this.weight == 1.0)
108 {
109 return (Constant.ONE);
110 }
111 return new Constant(this.weight);
112 }
113 Power myDerivative = new Power(this.chain, this.weight * this.power, this.power - 1.0);
114 if (this.chain == null)
115 {
116 return myDerivative.simplify();
117 }
118 MathFunction myChainDerivative = new Power(this.chain, myDerivative.weight, myDerivative.power);
119 return new Product(myChainDerivative, this.chain.getDerivative()).simplify();
120 }
121
122 @Override
123 public MathFunction simplify()
124 {
125 if (this.weight == 0.0)
126 {
127 return Constant.ZERO;
128 }
129 if (this.power == 0.0)
130 {
131 if (this.weight == 1.0)
132 {
133 return Constant.ONE;
134 }
135 return new Constant(this.weight);
136 }
137 if (this.chain != null && this.chain instanceof Constant)
138 {
139 return new Constant(get(0)).simplify();
140 }
141 if (this.power == 1.0 && this.chain != null)
142 {
143 return this.chain.scaleBy(this.weight);
144 }
145 return this;
146 }
147
148 @Override
149 public double getScale()
150 {
151 return this.weight;
152 }
153
154 @Override
155 public MathFunction scaleBy(final double factor)
156 {
157 if (factor == 0.0)
158 {
159 return Constant.ZERO;
160 }
161 if (factor == 1.0)
162 {
163 return this;
164 }
165 return new Power(this.chain, factor * this.weight, this.power);
166 }
167
168 @Override
169 public int sortPriority()
170 {
171 return 1;
172 }
173
174 @Override
175 public int compareWithinSubType(final MathFunction other)
176 {
177 Throw.when(!(other instanceof Power), IllegalArgumentException.class, "other is of wrong type");
178 Power otherPowerFunction = (Power) other;
179 if (otherPowerFunction.power < this.power)
180 {
181 return -1;
182 }
183 if (otherPowerFunction.power > this.power)
184 {
185 return 1;
186 }
187 if (this.weight < otherPowerFunction.weight)
188 {
189 return 1;
190 }
191 if (this.weight > otherPowerFunction.weight)
192 {
193 return -1;
194 }
195 return compareChains(this.chain, otherPowerFunction.chain);
196 }
197
198 @Override
199 public MathFunction mergeAdd(final MathFunction other)
200 {
201 if (other instanceof Power)
202 {
203 Power otherPowerFunction = (Power) other;
204 if (this.power == otherPowerFunction.power && (this.chain == null && otherPowerFunction.chain == null
205 || (this.chain != null && this.chain.equals(otherPowerFunction.chain))))
206 {
207 return new Power(this.chain, this.weight + otherPowerFunction.weight, this.power);
208 }
209 }
210 return null;
211 }
212
213 @Override
214 public MathFunction mergeMultiply(final MathFunction other)
215 {
216 if (other instanceof Power)
217 {
218 Power otherPowerFunction = (Power) other;
219 if (this.chain == null && otherPowerFunction.chain == null
220 || (this.chain != null && this.chain.equals(otherPowerFunction.chain)))
221 {
222 return new Power(this.chain, this.weight * otherPowerFunction.weight, this.power + otherPowerFunction.power);
223 }
224 else if (this.power == otherPowerFunction.power)
225 {
226 double resultWeight = this.weight * otherPowerFunction.weight;
227 if (this.chain != null && otherPowerFunction.chain != null)
228 {
229 return new Power(new Product(this.chain, otherPowerFunction.chain), resultWeight, this.power);
230 }
231
232 return new Power(new Product(new Power(1, 1), this.chain == null ? otherPowerFunction.chain : this.chain),
233 resultWeight, this.power);
234 }
235 }
236 return null;
237 }
238
239 @Override
240 public MathFunction mergeDivide(final MathFunction other)
241 {
242 if (other instanceof Power)
243 {
244 Power otherPowerFunction = (Power) other;
245 if (this.chain == null && otherPowerFunction.chain == null
246 || (this.chain != null && this.chain.equals(otherPowerFunction.chain)))
247 {
248 return new Power(this.chain, this.weight / otherPowerFunction.weight, this.power - otherPowerFunction.power);
249 }
250 }
251 return null;
252 }
253
254 @Override
255 public KnotReport getKnotReport(final Interval<?> interval)
256 {
257 boolean integerPower = this.power == Math.ceil(this.power);
258 if (this.chain == null && (!integerPower) && interval.low() < 0.0)
259 {
260 return KnotReport.KNOWN_INFINITE;
261 }
262 if (this.chain != null && (!integerPower))
263 {
264 return KnotReport.UNKNOWN;
265 }
266
267 return this.chain == null ? KnotReport.NONE : this.chain.getKnotReport(interval);
268 }
269
270 @Override
271 public SortedSet<Double> getKnots(final Interval<?> interval)
272 {
273 boolean integerPower = this.power == Math.ceil(this.power);
274 if (this.chain == null && (!integerPower) && interval.low() < 0.0)
275 {
276 throw new UnsupportedOperationException("There are infinitely many knots in " + interval);
277 }
278 if (this.chain != null && (!integerPower))
279 {
280 throw new UnsupportedOperationException("Cannot report knots in " + interval
281 + " because I do not know where the chained function is negative or zero");
282 }
283 return this.chain == null ? new TreeSet<Double>() : this.chain.getKnots(interval);
284 }
285
286 @Override
287 public String toString()
288 {
289 if (this.weight == 0)
290 {
291 return ("0");
292 }
293 StringBuilder result = new StringBuilder();
294 if (this.weight == -1.0)
295 {
296 result.append("-");
297 }
298 else if (this.weight != 1.0 || this.power == 0.0)
299 {
300 result.append(printValue(this.weight));
301 }
302 if (this.power != 0.0)
303 {
304 result.append(this.chain == null ? "x" : ("(" + this.chain.toString() + ")"));
305 if (this.power != 1)
306 {
307 result.append(SUPER_SCRIPT.translate(printValue(this.power)));
308 }
309 }
310 return result.toString();
311 }
312
313 @Override
314 public int hashCode()
315 {
316 return Objects.hash(this.chain, this.power, this.weight);
317 }
318
319 @SuppressWarnings("checkstyle:needbraces")
320 @Override
321 public boolean equals(final Object obj)
322 {
323 if (this == obj)
324 return true;
325 if (obj == null)
326 return false;
327 if (getClass() != obj.getClass())
328 return false;
329 Power other = (Power) obj;
330 return Objects.equals(this.chain, other.chain)
331 && Double.doubleToLongBits(this.power) == Double.doubleToLongBits(other.power)
332 && Double.doubleToLongBits(this.weight) == Double.doubleToLongBits(other.weight);
333 }
334
335 }