1 /*
2  * This file is part of ELKI:
3  * Environment for Developing KDD-Applications Supported by Index-Structures
4  *
5  * Copyright (C) 2018
6  * ELKI Development Team
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 package de.lmu.ifi.dbs.elki.math;
22 
23 import java.math.BigInteger;
24 import java.util.Random;
25 
26 import net.jafama.FastMath;
27 
28 /**
29  * A collection of math related utility functions.
30  *
31  * @author Arthur Zimek
32  * @author Erich Schubert
33  * @since 0.1
34  *
35  * @opt nodefillcolor LemonChiffon
36  */
37 public final class MathUtil {
38   /**
39    * Two times Pi.
40    */
41   public static final double TWOPI = 2. * Math.PI;
42 
43   /**
44    * Half the value of Pi.
45    */
46   public static final double HALFPI = .5 * Math.PI;
47 
48   /**
49    * One quarter of Pi.
50    */
51   public static final double QUARTERPI = .25 * Math.PI;
52 
53   /**
54    * 1.5 times Pi.
55    */
56   public static final double ONEHALFPI = 1.5 * Math.PI;
57 
58   /**
59    * Pi squared
60    */
61   public static final double PISQUARE = Math.PI * Math.PI;
62 
63   /**
64    * Square root of Pi.
65    */
66   public static final double SQRTPI = Math.sqrt(Math.PI);
67 
68   /**
69    * Square root of two times Pi.
70    */
71   public static final double SQRTTWOPI = Math.sqrt(TWOPI);
72 
73   /**
74    * Constant for sqrt(pi/2)
75    */
76   public static final double SQRTHALFPI = Math.sqrt(HALFPI);
77 
78   /**
79    * Square root of 2.
80    */
81   public static final double SQRT2 = Math.sqrt(2.);
82 
83   /**
84    * Square root of 3.
85    */
86   public static final double SQRT3 = Math.sqrt(3.);
87 
88   /**
89    * Square root of 5.
90    */
91   public static final double SQRT5 = Math.sqrt(5.);
92 
93   /**
94    * Square root of 0.5 == 1 / sqrt(2).
95    */
96   public static final double SQRTHALF = Math.sqrt(.5);
97 
98   /**
99    * Precomputed value of 1 / sqrt(pi).
100    */
101   public static final double ONE_BY_SQRTPI = 1. / SQRTPI;
102 
103   /**
104    * Precomputed value of 1 / sqrt(2 * pi).
105    */
106   public static final double ONE_BY_SQRTTWOPI = 1. / SQRTTWOPI;
107 
108   /**
109    * Precomputed value of log(1 / sqrt(2 * pi)) = -.5 * log(2*pi).
110    */
111   public static final double LOG_ONE_BY_SQRTTWOPI = -.5 * Math.log(TWOPI);
112 
113   /**
114    * 1. / log(2)
115    */
116   public static final double ONE_BY_LOG2 = 1. / Math.log(2.);
117 
118   /**
119    * One third.
120    */
121   public static final double ONE_THIRD = 1. / 3.;
122 
123   /**
124    * Square root of one third.
125    */
126   public static final double SQRTTHIRD = FastMath.sqrt(1 / 3.);
127 
128   /**
129    * Logarithm of 2 to the basis e, for logarithm conversion.
130    */
131   public static final double LOG2 = Math.log(2.);
132 
133   /**
134    * Logarithm of 3 to the basis e, for logarithm conversion.
135    */
136   public static final double LOG3 = Math.log(3.);
137 
138   /**
139    * Natural logarithm of 10.
140    */
141   public static final double LOG10 = Math.log(10.);
142 
143   /**
144    * log(PI).
145    */
146   public static final double LOGPI = Math.log(Math.PI);
147 
148   /**
149    * log(PI) / 2.
150    */
151   public static final double LOGPIHALF = LOGPI / 2.;
152 
153   /**
154    * log(2*PI).
155    */
156   public static final double LOGTWOPI = Math.log(TWOPI);
157 
158   /**
159    * log(sqrt(2*PI)).
160    */
161   public static final double LOGSQRTTWOPI = Math.log(SQRTTWOPI);
162 
163   /**
164    * log(log(2))
165    */
166   public static final double LOGLOG2 = Math.log(LOG2);
167 
168   /**
169    * Constant for degrees to radians.
170    */
171   public static final double DEG2RAD = Math.PI / 180.0;
172 
173   /**
174    * Constant for radians to degrees.
175    */
176   public static final double RAD2DEG = 180 / Math.PI;
177 
178   /**
179    * Fake constructor for static class.
180    */
MathUtil()181   private MathUtil() {
182     // Static methods only - do not instantiate!
183   }
184 
185   /**
186    * Compute the base 2 logarithm.
187    *
188    * @param x X
189    * @return Logarithm base 2.
190    */
log2(double x)191   public static double log2(double x) {
192     return FastMath.log(x) * ONE_BY_LOG2;
193   }
194 
195   /**
196    * Compute the Factorial of n, often written as <code>c!</code> in
197    * mathematics.
198    * <p>
199    * Use this method if for large values of <code>n</code>.
200    *
201    * @param n Note: n &gt;= 0. This {@link BigInteger} <code>n</code> will be 0
202    *        after this method finishes.
203    * @return n * (n-1) * (n-2) * ... * 1
204    */
factorial(BigInteger n)205   public static BigInteger factorial(BigInteger n) {
206     BigInteger nFac = BigInteger.ONE;
207     while(n.compareTo(BigInteger.ONE) > 0) {
208       nFac = nFac.multiply(n);
209       n = n.subtract(BigInteger.ONE);
210     }
211     return nFac;
212   }
213 
214   /**
215    * Compute the Factorial of n, often written as <code>c!</code> in
216    * mathematics.
217    *
218    * @param n Note: n &gt;= 0
219    * @return n * (n-1) * (n-2) * ... * 1
220    */
factorial(int n)221   public static long factorial(int n) {
222     long nFac = 1;
223     for(long i = n; i > 0; i--) {
224       nFac *= i;
225     }
226     return nFac;
227   }
228 
229   /**
230    * Binomial coefficient, also known as "n choose k".
231    *
232    * @param n Total number of samples. n &gt; 0
233    * @param k Number of elements to choose. <code>n &gt;= k</code>,
234    *        <code>k &gt;= 0</code>
235    * @return n! / (k! * (n-k)!)
236    */
binomialCoefficient(long n, long k)237   public static long binomialCoefficient(long n, long k) {
238     final long m = Math.max(k, n - k);
239     double temp = 1;
240     for(long i = n, j = 1; i > m; i--, j++) {
241       temp = temp * i / j;
242     }
243     return (long) temp;
244   }
245 
246   /**
247    * Compute the Factorial of n, often written as <code>c!</code> in
248    * mathematics.
249    *
250    * @param n Note: n &gt;= 0
251    * @return n * (n-1) * (n-2) * ... * 1
252    */
approximateFactorial(int n)253   public static double approximateFactorial(int n) {
254     double nFac = 1.0;
255     for(int i = n; i > 0; i--) {
256       nFac *= i;
257     }
258     return nFac;
259   }
260 
261   /**
262    * Binomial coefficent, also known as "n choose k").
263    *
264    * @param n Total number of samples. n &gt; 0
265    * @param k Number of elements to choose. <code>n &gt;= k</code>,
266    *        <code>k &gt;= 0</code>
267    * @return n! / (k! * (n-k)!)
268    */
approximateBinomialCoefficient(int n, int k)269   public static double approximateBinomialCoefficient(int n, int k) {
270     final int m = max(k, n - k);
271     long temp = 1;
272     for(int i = n, j = 1; i > m; i--, j++) {
273       temp = temp * i / j;
274     }
275     return temp;
276   }
277 
278   /**
279    * Compute the sum of the i first integers.
280    *
281    * @param i maximum summand (inclusive)
282    * @return Sum
283    */
sumFirstIntegers(final long i)284   public static long sumFirstIntegers(final long i) {
285     return ((i + 1L) * i) >> 1;
286   }
287 
288   /**
289    * Produce an array of random numbers in [0:1].
290    *
291    * @param len Length
292    * @param r Random generator
293    * @return Array
294    */
randomDoubleArray(int len, Random r)295   public static double[] randomDoubleArray(int len, Random r) {
296     final double[] ret = new double[len];
297     for(int i = 0; i < len; i++) {
298       ret[i] = r.nextDouble();
299     }
300     return ret;
301   }
302 
303   /**
304    * Convert Degree to Radians.
305    * <p>
306    * This is essentially the same as {@link Math#toRadians}, but we keep it for
307    * now, it might be marginally faster, but certainly not slower.
308    *
309    * @param deg Degree value
310    * @return Radian value
311    */
deg2rad(double deg)312   public static double deg2rad(double deg) {
313     return deg * DEG2RAD;
314   }
315 
316   /**
317    * Radians to Degree.
318    * <p>
319    * This is essentially the same as {@link Math#toRadians}, but we keep it for
320    * now, it might be marginally faster, but certainly not slower.
321    *
322    * @param rad Radians value
323    * @return Degree value
324    */
rad2deg(double rad)325   public static double rad2deg(double rad) {
326     return rad * RAD2DEG;
327   }
328 
329   /**
330    * Normalize an angle to [0:2pi[
331    *
332    * @param x Input angle
333    * @return Normalized angle
334    */
normAngle(double x)335   public static double normAngle(double x) {
336     x %= TWOPI;
337     return (x > 0) ? x : x + TWOPI;
338   }
339 
340   /**
341    * Find the next power of 2.
342    * <p>
343    * Classic bit operation, for signed 32-bit. Valid for positive integers only
344    * (0 otherwise).
345    *
346    * @param x original integer
347    * @return Next power of 2
348    */
nextPow2Int(int x)349   public static int nextPow2Int(int x) {
350     --x;
351     x |= x >>> 1;
352     x |= x >>> 2;
353     x |= x >>> 4;
354     x |= x >>> 8;
355     x |= x >>> 16;
356     return ++x;
357   }
358 
359   /**
360    * Find the next power of 2.
361    * <p>
362    * Classic bit operation, for signed 64-bit. Valid for positive integers only
363    * (0 otherwise).
364    *
365    * @param x original long integer
366    * @return Next power of 2
367    */
nextPow2Long(long x)368   public static long nextPow2Long(long x) {
369     --x;
370     x |= x >>> 1;
371     x |= x >>> 2;
372     x |= x >>> 4;
373     x |= x >>> 16;
374     x |= x >>> 32;
375     return ++x;
376   }
377 
378   /**
379    * Find the next larger number with all ones.
380    * <p>
381    * Classic bit operation, for signed 32-bit. Valid for positive integers only
382    * (-1 otherwise).
383    *
384    * @param x original integer
385    * @return Next number with all bits set
386    */
nextAllOnesInt(int x)387   public static int nextAllOnesInt(int x) {
388     x |= x >>> 1;
389     x |= x >>> 2;
390     x |= x >>> 4;
391     x |= x >>> 8;
392     x |= x >>> 16;
393     return x;
394   }
395 
396   /**
397    * Find the next larger number with all ones.
398    * <p>
399    * Classic bit operation, for signed 64-bit. Valid for positive integers only
400    * (-1 otherwise).
401    *
402    * @param x original long integer
403    * @return Next number with all bits set
404    */
nextAllOnesLong(long x)405   public static long nextAllOnesLong(long x) {
406     x |= x >>> 1;
407     x |= x >>> 2;
408     x |= x >>> 4;
409     x |= x >>> 16;
410     x |= x >>> 32;
411     return x;
412   }
413 
414   /**
415    * Return the largest double that rounds down to this float.
416    * <p>
417    * Note: Probably not always correct - subnormal values are quite tricky. So
418    * some of the bounds might not be tight.
419    *
420    * @param f Float value
421    * @return Double value
422    */
floatToDoubleUpper(float f)423   public static double floatToDoubleUpper(float f) {
424     if(Float.isNaN(f)) {
425       return Double.NaN;
426     }
427     if(Float.isInfinite(f)) {
428       return f > 0 ? Double.POSITIVE_INFINITY : Double.longBitsToDouble(0xc7efffffffffffffL);
429     }
430     long bits = Double.doubleToRawLongBits((double) f);
431     if((bits & 0x8000000000000000L) == 0) { // Positive
432       if(bits == 0L) {
433         return Double.longBitsToDouble(0x3690000000000000L);
434       }
435       if(f == Float.MIN_VALUE) {
436         // bits += 0x7_ffff_ffff_ffffl;
437         return Double.longBitsToDouble(0x36a7ffffffffffffL);
438       }
439       if(Float.MIN_NORMAL > f && f >= Double.MIN_NORMAL) {
440         // The most tricky case:
441         // a denormalized float, but a normalized double
442         final long bits2 = Double.doubleToRawLongBits((double) Math.nextUp(f));
443         bits = (bits >>> 1) + (bits2 >>> 1) - 1L;
444       }
445       else {
446         bits += 0xfffffffL; // 28 extra bits
447       }
448       return Double.longBitsToDouble(bits);
449     }
450     else {
451       if(bits == 0x8000000000000000L) {
452         return -0.0d;
453       }
454       if(f == -Float.MIN_VALUE) {
455         // bits -= 0xf_ffff_ffff_ffffl;
456         return Double.longBitsToDouble(0xb690000000000001L);
457       }
458       if(-Float.MIN_NORMAL < f && f <= -Double.MIN_NORMAL) {
459         // The most tricky case:
460         // a denormalized float, but a normalized double
461         final long bits2 = Double.doubleToRawLongBits((double) Math.nextUp(f));
462         bits = (bits >>> 1) + (bits2 >>> 1) + 1L;
463       }
464       else {
465         bits -= 0xfffffffL; // 28 extra bits
466       }
467       return Double.longBitsToDouble(bits);
468     }
469   }
470 
471   /**
472    * Return the largest double that rounds up to this float.
473    * <p>
474    * Note: Probably not always correct - subnormal values are quite tricky. So
475    * some of the bounds might not be tight.
476    *
477    * @param f Float value
478    * @return Double value
479    */
floatToDoubleLower(float f)480   public static double floatToDoubleLower(float f) {
481     if(Float.isNaN(f)) {
482       return Double.NaN;
483     }
484     if(Float.isInfinite(f)) {
485       return f < 0 ? Double.NEGATIVE_INFINITY : Double.longBitsToDouble(0x47efffffffffffffL);
486     }
487     long bits = Double.doubleToRawLongBits((double) f);
488     if((bits & 0x8000000000000000L) == 0) { // Positive
489       if(bits == 0L) {
490         return +0.0d;
491       }
492       if(f == Float.MIN_VALUE) {
493         // bits -= 0xf_ffff_ffff_ffffl;
494         return Double.longBitsToDouble(0x3690000000000001L);
495       }
496       if(Float.MIN_NORMAL > f /* && f >= Double.MIN_NORMAL */) {
497         // The most tricky case:
498         // a denormalized float, but a normalized double
499         final long bits2 = Double.doubleToRawLongBits((double) -Math.nextUp(-f));
500         bits = (bits >>> 1) + (bits2 >>> 1) + 1L; // + (0xfff_ffffL << 18);
501       }
502       else {
503         bits -= 0xfffffffL; // 28 extra bits
504       }
505       return Double.longBitsToDouble(bits);
506     }
507     else {
508       if(bits == 0x8000000000000000L) {
509         return Double.longBitsToDouble(0xb690000000000000L);
510       }
511       if(f == -Float.MIN_VALUE) {
512         // bits += 0x7_ffff_ffff_ffffl;
513         return Double.longBitsToDouble(0xb6a7ffffffffffffL);
514       }
515       if(-Float.MIN_NORMAL < f /* && f <= -Double.MIN_NORMAL */) {
516         // The most tricky case:
517         // a denormalized float, but a normalized double
518         final long bits2 = Double.doubleToRawLongBits((double) -Math.nextUp(-f));
519         bits = (bits >>> 1) + (bits2 >>> 1) - 1L;
520       }
521       else {
522         bits += 0xfffffffL; // 28 extra bits
523       }
524       return Double.longBitsToDouble(bits);
525     }
526   }
527 
528   /**
529    * More stable than {@code FastMath.log(1 - FastMath.exp(x))}
530    *
531    * @param x Value
532    * @return log(1-exp(x))
533    */
log1mexp(double x)534   public static double log1mexp(double x) {
535     return (x > -LOG2) ? FastMath.log(-FastMath.expm1(x)) : FastMath.log1p(-exp(x));
536   }
537 
538   /**
539    * Delegate to FastMath.exp.
540    *
541    * @param d Value
542    * @return FastMath.exp(d)
543    */
exp(double d)544   public static double exp(double d) {
545     return FastMath.exp(d);
546   }
547 
548   /**
549    * Delegate to FastMath.powFast
550    *
551    * @param x Base
552    * @param p Exponent
553    * @return {@code FastMath.powFast(x, p)}
554    */
powi(double x, int p)555   public static double powi(double x, int p) {
556     return FastMath.powFast(x, p);
557   }
558 
559   /**
560    * Fast loop for computing {@code pow(x, p)}
561    * for {@code p >= 0} integer and x integer.
562    *
563    * @param x Base
564    * @param p Exponent
565    * @return {@code pow(x, p)}
566    */
ipowi(int x, int p)567   public static int ipowi(int x, int p) {
568     if(p <= 2) {
569       return (int) FastMath.powFast(x, p);
570     }
571     int tmp = x, ret = (p & 1) == 1 ? x : 1;
572     while(true) {
573       tmp *= tmp;
574       p >>= 1;
575       if(p == 1) {
576         return ret * tmp;
577       }
578       if((p & 1) != 0) {
579         ret *= tmp;
580       }
581     }
582   }
583 
584   /**
585    * Empty integer array.
586    */
587   public static final int[] EMPTY_INTS = new int[0];
588 
589   /**
590    * Generate an array of integers.
591    *
592    * @param start First integer
593    * @param end Last integer (exclusive!)
594    * @return Array of integers of length end-start
595    */
sequence(int start, int end)596   public static int[] sequence(int start, int end) {
597     if(start >= end) {
598       return EMPTY_INTS;
599     }
600     int[] ret = new int[end - start];
601     for(int j = 0; start < end; start++, j++) {
602       ret[j] = start;
603     }
604     return ret;
605   }
606 
607   /**
608    * Binary max, <i>without</i> handling of special values.
609    * <p>
610    * Because of the lack of special case handling, this is faster than
611    * {@link Math#max}. But usually, it should be written inline as
612    * {@code (a >= b) ? a : b}
613    * <p>
614    * The result is asymmetric in case of {@code Double.NaN}:<br>
615    * {@code MathUtil.max(Double.NaN, 1.)} is 1, but <br>
616    * {@code MathUtil.max(1., Double.NaN)} is {@code Double.NaN}.
617    *
618    * @param a First value
619    * @param b Second value
620    * @return Maximum
621    */
max(double a, double b)622   public static double max(double a, double b) {
623     return a >= b ? a : b;
624   }
625 
626   /**
627    * Ternary max, <i>without</i> handling of special values.
628    * <p>
629    * Because of the lack of special case handling, this is faster than
630    * {@link Math#max}. But usually, it should be written inline.
631    *
632    * @param a First value
633    * @param b Second value
634    * @param c Third value
635    * @return Maximum
636    */
max(double a, double b, double c)637   public static double max(double a, double b, double c) {
638     return a >= b ? (a >= c ? a : c) : (b >= c ? b : c);
639   }
640 
641   /**
642    * Quadrary max, <i>without</i> handling of special values.
643    *
644    * Because of the lack of special case handling, this is faster than
645    * {@link Math#max}. But usually, it should be written inline.
646    *
647    * @param a First value
648    * @param b Second value
649    * @param c Third value
650    * @param d Fourth value
651    * @return Maximum
652    */
max(double a, double b, double c, double d)653   public static double max(double a, double b, double c, double d) {
654     return a >= b ? //
655         a >= c ? (a >= d ? a : d) : (c >= d ? c : d) : //
656         b >= c ? (b >= d ? b : d) : (c >= d ? c : d);
657   }
658 
659   /**
660    * Binary max. If possible, inline.
661    *
662    * @param a First value
663    * @param b Second value
664    * @return Maximum
665    */
max(int a, int b)666   public static int max(int a, int b) {
667     return a >= b ? a : b;
668   }
669 
670   /**
671    * Ternary max. If possible, inline.
672    *
673    * @param a First value
674    * @param b Second value
675    * @param c Third value
676    * @return Maximum
677    */
max(int a, int b, int c)678   public static int max(int a, int b, int c) {
679     return a >= b ? (a >= c ? a : c) : (b >= c ? b : c);
680   }
681 
682   /**
683    * Quadrary max, <i>without</i> handling of special values.
684    *
685    * Because of the lack of special case handling, this is faster than
686    * {@link Math#max}. But usually, it should be written inline.
687    *
688    * @param a First value
689    * @param b Second value
690    * @param c Third value
691    * @param d Fourth value
692    * @return Maximum
693    */
max(int a, int b, int c, int d)694   public static int max(int a, int b, int c, int d) {
695     return a >= b ? //
696         a >= c ? (a >= d ? a : d) : (c >= d ? c : d) : //
697         b >= c ? (b >= d ? b : d) : (c >= d ? c : d);
698   }
699 
700   /**
701    * Binary min, <i>without</i> handling of special values.
702    * <p>
703    * Because of the lack of special case handling, this is faster than
704    * {@link Math#min}. But usually, it should be written inline as
705    * {@code (a <= b) ? a : b}
706    * <p>
707    * The result is asymmetric in case of {@code Double.NaN}:<br>
708    * {@code MathUtil.min(Double.NaN, 1.)} is 1, but <br>
709    * {@code MathUtil.min(1., Double.NaN)} is {@code Double.NaN}.
710    *
711    * @param a First value
712    * @param b Second value
713    * @return minimum
714    */
min(double a, double b)715   public static double min(double a, double b) {
716     return a <= b ? a : b;
717   }
718 
719   /**
720    * Ternary min, <i>without</i> handling of special values.
721    * <p>
722    * Because of the lack of special case handling, this is faster than
723    * {@link Math#min}. But usually, it should be written inline.
724    *
725    * @param a First value
726    * @param b Second value
727    * @param c Third value
728    * @return minimum
729    */
min(double a, double b, double c)730   public static double min(double a, double b, double c) {
731     return a <= b ? (a <= c ? a : c) : (b <= c ? b : c);
732   }
733 
734   /**
735    * Quadrary min, <i>without</i> handling of special values.
736    * <p>
737    * Because of the lack of special case handling, this is faster than
738    * {@link Math#min}. But usually, it should be written inline.
739    *
740    * @param a First value
741    * @param b Second value
742    * @param c Third value
743    * @param d Fourth value
744    * @return minimum
745    */
min(double a, double b, double c, double d)746   public static double min(double a, double b, double c, double d) {
747     return a <= b ? //
748         a <= c ? (a <= d ? a : d) : (c <= d ? c : d) : //
749         b <= c ? (b <= d ? b : d) : (c <= d ? c : d);
750   }
751 
752   /**
753    * Binary min, <i>without</i> handling of special values.
754    * <p>
755    * Because of the lack of special case handling, this is faster than
756    * {@link Math#min}. But usually, it should be written inline.
757    *
758    * @param a First value
759    * @param b Second value
760    * @return minimum
761    */
min(int a, int b)762   public static int min(int a, int b) {
763     return a <= b ? a : b;
764   }
765 
766   /**
767    * Ternary min, <i>without</i> handling of special values.
768    * <p>
769    * Because of the lack of special case handling, this is faster than
770    * {@link Math#min}. But usually, it should be written inline.
771    *
772    * @param a First value
773    * @param b Second value
774    * @param c Third value
775    * @return minimum
776    */
min(int a, int b, int c)777   public static int min(int a, int b, int c) {
778     return a <= b ? (a <= c ? a : c) : (b <= c ? b : c);
779   }
780 
781   /**
782    * Quadrary min, <i>without</i> handling of special values.
783    * <p>
784    * Because of the lack of special case handling, this is faster than
785    * {@link Math#min}. But usually, it should be written inline.
786    *
787    * @param a First value
788    * @param b Second value
789    * @param c Third value
790    * @param d Fourth value
791    * @return minimum
792    */
min(int a, int b, int c, int d)793   public static int min(int a, int b, int c, int d) {
794     return a <= b ? //
795         a <= c ? (a <= d ? a : d) : (c <= d ? c : d) : //
796         b <= c ? (b <= d ? b : d) : (c <= d ? c : d);
797   }
798 }
799