1 package org.djutils.math.functions;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.Objects;
8 import java.util.SortedSet;
9 import java.util.TreeSet;
10
11 import org.djutils.exceptions.Throw;
12
13
14
15
16
17
18
19
20
21
22
23
24
25 public class Product implements MathFunction
26 {
27
28 private final List<MathFunction> factors;
29
30
31
32
33
34
35
36 public Product(final MathFunction... functions)
37 {
38 this(Arrays.asList(functions));
39 }
40
41
42
43
44
45
46
47 public Product(final List<MathFunction> functions)
48 {
49 Throw.when(functions.size() == 0, IllegalArgumentException.class, "Product needs at least one object to multiply");
50 this.factors = simplify(functions);
51 }
52
53
54
55
56
57
58 private List<MathFunction> simplify(final List<MathFunction> functions)
59 {
60 List<MathFunction> result = new ArrayList<>(functions);
61
62
63 for (int index = 0; index < result.size(); index++)
64 {
65 MathFunction function = result.get(index);
66 Throw.whenNull(function, "function");
67 if (function instanceof Product)
68 {
69
70 result.remove(index);
71 index--;
72 result.addAll(((Product) function).factors);
73 }
74 }
75
76 for (int index = 0; index < result.size(); index++)
77 {
78 MathFunction function = result.get(index);
79 MathFunction optimized = function.simplify();
80 if (!function.equals(optimized))
81 {
82 result.remove(index);
83 result.add(index, optimized);
84 }
85 }
86 Collections.sort(result);
87
88
89 for (int index = 0; index < result.size(); index++)
90 {
91 MathFunction function = result.get(index);
92 if (index < result.size() - 1)
93 {
94 MathFunction nextFunction = result.get(index + 1);
95 MathFunction merged = function.mergeMultiply(nextFunction);
96 if (merged != null)
97 {
98 result.remove(index);
99 result.remove(index);
100 result.add(index, merged);
101 index--;
102 }
103 }
104 }
105 double productOfConstants = 1.0;
106 for (int index = 0; index < result.size(); index++)
107 {
108 MathFunction function = result.get(index);
109 double scale = function.getScale();
110 if (function instanceof Constant)
111 {
112 if (0.0 == scale)
113 {
114
115 result.clear();
116 result.add(Constant.ZERO);
117 return result;
118 }
119
120 productOfConstants *= scale;
121 result.remove(index);
122 index--;
123 }
124 else if (scale != 1.0)
125 {
126
127 productOfConstants *= scale;
128 function = function.scaleBy(1.0 / scale);
129 result.remove(index);
130 result.add(index, function);
131 }
132 }
133 if (productOfConstants != 1.0)
134 {
135 if (result.size() > 0)
136 {
137
138 MathFunction function = result.get(0);
139 result.remove(0);
140 function = function.scaleBy(productOfConstants);
141 result.add(0, function);
142 }
143 else
144 {
145 result.add(new Constant(productOfConstants));
146 }
147 }
148 if (result.size() == 0)
149 {
150 result.add(Constant.ONE);
151 }
152 return result;
153 }
154
155 @Override
156 public double get(final double x)
157 {
158 double result = 1.0;
159 for (MathFunction fi : this.factors)
160 {
161 result *= fi.get(x);
162 }
163 return result;
164 }
165
166 @Override
167 public MathFunction getDerivative()
168 {
169 List<MathFunction> result = new ArrayList<>();
170 for (int i = 0; i < this.factors.size(); i++)
171 {
172 List<MathFunction> termFactors = new ArrayList<>(this.factors.size());
173 for (int j = 0; j < this.factors.size(); j++)
174 {
175 if (j == i)
176 {
177 termFactors.add(this.factors.get(j).getDerivative());
178 }
179 else
180 {
181 termFactors.add(this.factors.get(j));
182 }
183 }
184 result.add(new Product(termFactors).simplify());
185 }
186 return new Sum(result).simplify();
187 }
188
189 @Override
190 public MathFunction simplify()
191 {
192 List<MathFunction> simplifiedFactors = simplify(this.factors);
193 if (simplifiedFactors.size() == 1)
194 {
195 return simplifiedFactors.get(0);
196 }
197 return this;
198 }
199
200 @Override
201 public int sortPriority()
202 {
203 return 100;
204 }
205
206 @Override
207 public int compareWithinSubType(final MathFunction other)
208 {
209 Throw.when(!(other instanceof Product), IllegalArgumentException.class, "other is of wrong type");
210 Product otherProduct = (Product) other;
211 for (int index = 0; index < this.factors.size(); index++)
212 {
213 if (index >= otherProduct.factors.size())
214 {
215 return 1;
216 }
217 int result = this.factors.get(index).compareTo(otherProduct.factors.get(index));
218 if (result != 0)
219 {
220 return result;
221 }
222 }
223 if (otherProduct.factors.size() > this.factors.size())
224 {
225 return -1;
226 }
227 return 0;
228 }
229
230 @Override
231 public MathFunction scaleBy(final double scaleFactor)
232 {
233 if (scaleFactor == 0.0)
234 {
235 return Constant.ZERO;
236 }
237 if (scaleFactor == 1.0)
238 {
239 return this;
240 }
241 List<MathFunction> result = new ArrayList<>(this.factors);
242 MathFunction scaledFactor = result.get(0).scaleBy(scaleFactor);
243 result.remove(0);
244 result.add(0, scaledFactor);
245 return new Product(result);
246 }
247
248 @Override
249 public KnotReport getKnotReport(final Interval<?> interval)
250 {
251 KnotReport result = KnotReport.NONE;
252 for (MathFunction factor : this.factors)
253 {
254 result = result.combineWith(factor.getKnotReport(interval));
255 }
256 return result;
257 }
258
259 @Override
260 public SortedSet<Double> getKnots(final Interval<?> interval)
261 {
262 SortedSet<Double> result = new TreeSet<>();
263 for (MathFunction factor : this.factors)
264 {
265 result.addAll(factor.getKnots(interval));
266 }
267 return result;
268 }
269
270 @Override
271 public String toString()
272 {
273 StringBuilder result = new StringBuilder();
274 result.append("\u03A0(");
275 for (int i = 0; i < this.factors.size(); i++)
276 {
277 if (i > 0)
278 {
279 result.append(", ");
280 }
281 result.append(this.factors.get(i).toString());
282 }
283 result.append(")");
284 return result.toString();
285 }
286
287 @Override
288 public int hashCode()
289 {
290 return Objects.hash(this.factors);
291 }
292
293 @SuppressWarnings("checkstyle:needbraces")
294 @Override
295 public boolean equals(final Object obj)
296 {
297 if (this == obj)
298 return true;
299 if (obj == null)
300 return false;
301 if (getClass() != obj.getClass())
302 return false;
303 Product other = (Product) obj;
304 return Objects.equals(this.factors, other.factors);
305 }
306
307 }