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 public class Logarithm implements MathFunction
22 {
23
24
25
26
27 private final double logBaseRecip;
28
29
30 private final MathFunction chain;
31
32
33
34
35
36
37
38 private Logarithm(final boolean notUsed, final MathFunction chain, final double logBaseRecip)
39 {
40 this.logBaseRecip = logBaseRecip;
41 this.chain = chain;
42 }
43
44
45
46
47 public Logarithm()
48 {
49 this(Math.E);
50 }
51
52
53
54
55
56 public Logarithm(final double base)
57 {
58 this(null, base);
59 }
60
61
62
63
64
65 public Logarithm(final MathFunction chain)
66 {
67 this(chain, Math.E);
68 }
69
70
71
72
73
74
75
76 public Logarithm(final MathFunction chain, final double base)
77 {
78 this.logBaseRecip = base == Math.E ? 1.0 : (1.0 / Math.log(base));
79 this.chain = chain;
80 }
81
82 @Override
83 public double get(final double x)
84 {
85 double xValue = this.chain == null ? x : this.chain.get(x);
86 return Math.log(xValue) * this.logBaseRecip;
87 }
88
89 @Override
90 public MathFunction getDerivative()
91 {
92 if (this.chain == null)
93 {
94 return new Power(this.logBaseRecip, -1);
95 }
96
97 MathFunction result = new Quotient(this.chain.getDerivative().scaleBy(this.logBaseRecip), this.chain);
98 return result.simplify();
99 }
100
101 @Override
102 public MathFunction simplify()
103 {
104 if (this.logBaseRecip == 0.0)
105 {
106 return Constant.ZERO;
107 }
108 if (this.chain != null && this.chain instanceof Constant)
109 {
110 return new Constant(get(0)).simplify();
111 }
112 return this;
113 }
114
115 @Override
116 public MathFunction scaleBy(final double scaleFactor)
117 {
118 if (scaleFactor == 0.0)
119 {
120 return Constant.ZERO;
121 }
122 if (scaleFactor == 1.0)
123 {
124 return this;
125 }
126 return new Logarithm(true, this.chain, this.logBaseRecip * scaleFactor);
127 }
128
129 @Override
130 public int sortPriority()
131 {
132 return 6;
133 }
134
135 @Override
136 public int compareWithinSubType(final MathFunction other)
137 {
138 Throw.when(!(other instanceof Logarithm), IllegalArgumentException.class, "other is of wrong type");
139 Logarithm otherLog = (Logarithm) other;
140 if (this.logBaseRecip > otherLog.logBaseRecip)
141 {
142 return 1;
143 }
144 if (this.logBaseRecip < otherLog.logBaseRecip)
145 {
146 return -1;
147 }
148 return compareChains(this.chain, otherLog.chain);
149 }
150
151 @Override
152 public MathFunction mergeAdd(final MathFunction other)
153 {
154 if (other instanceof Logarithm)
155 {
156 Logarithm otherLog = (Logarithm) other;
157 if (this.chain == null && otherLog.chain == null || this.chain != null && this.chain.equals(otherLog.chain))
158 {
159 return new Logarithm(true, this.chain, this.logBaseRecip + otherLog.logBaseRecip);
160 }
161 }
162 return null;
163 }
164
165 @Override
166 public KnotReport getKnotReport(final Interval<?> interval)
167 {
168 if (this.chain != null)
169 {
170 return KnotReport.UNKNOWN;
171 }
172 return interval.low() > 0.0 ? KnotReport.NONE
173 : interval.low() == 0.0 ? KnotReport.KNOWN_FINITE : KnotReport.KNOWN_INFINITE;
174 }
175
176 @Override
177 public SortedSet<Double> getKnots(final Interval<?> interval)
178 {
179 if (this.chain != null)
180 {
181 throw new UnsupportedOperationException("Cannot report knots in " + interval
182 + " because I do not know where the chained function is negative or zero");
183 }
184 if (interval.low() > 0.0)
185 {
186 return new TreeSet<Double>();
187 }
188 if (interval.low() == 0.0)
189 {
190 SortedSet<Double> result = new TreeSet<>();
191 result.add(0.0);
192 return result;
193 }
194 throw new UnsupportedOperationException("There are infinitely many knots in " + interval);
195 }
196
197 @Override
198 public String toString()
199 {
200 StringBuilder result = new StringBuilder();
201 if (this.logBaseRecip != 1.0)
202 {
203 result.append(printValue(this.logBaseRecip));
204 }
205 if (this.logBaseRecip != 0.0)
206 {
207 result.append("ln(");
208 result.append(this.chain == null ? "x" : this.chain.toString());
209 result.append(")");
210 }
211 return result.toString();
212 }
213
214 @Override
215 public int hashCode()
216 {
217 return Objects.hash(this.chain, this.logBaseRecip);
218 }
219
220 @SuppressWarnings("checkstyle:needbraces")
221 @Override
222 public boolean equals(final Object obj)
223 {
224 if (this == obj)
225 return true;
226 if (obj == null)
227 return false;
228 if (getClass() != obj.getClass())
229 return false;
230 Logarithm other = (Logarithm) obj;
231 return Objects.equals(this.chain, other.chain)
232 && Double.doubleToLongBits(this.logBaseRecip) == Double.doubleToLongBits(other.logBaseRecip);
233 }
234
235 }