1 /*
2  * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 package test.java.lang.Math;
25 
26 /* @test
27    @bug 8301226
28    @summary Add clamp() methods to java.lang.Math
29  */
30 
31 
32 public class Clamp {
main(String[] args)33     public static void main(String[] args) {
34         int failures = 0;
35 
36         failures += testIntClamp();
37         failures += testLongClamp();
38         failures += testDoubleClamp();
39         failures += testFloatClamp();
40 
41         if (failures > 0) {
42             System.err.println("Testing clamp incurred " + failures + " failures.");
43             throw new RuntimeException();
44         }
45     }
46 
testIntClamp()47     private static int testIntClamp() {
48         int failures = 0;
49         long[][] tests = {
50                 // value, min, max, expected
51                 {0, 1, 2, 1},
52                 {0, 0, 2, 0},
53                 {1, 0, 2, 1},
54                 {2, 0, 2, 2},
55                 {3, 0, 2, 2},
56                 {0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0},
57                 {Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE},
58                 {Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
59                 {Long.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
60                 {Long.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE},
61                 {0, 1, 1, 1},
62                 {Long.MAX_VALUE, 1, 1, 1}
63         };
64         long[][] exceptionTests = {
65                 // value, min, max
66                 {1, 2, 0},
67                 {1, Integer.MAX_VALUE, Integer.MIN_VALUE}
68         };
69         for (long[] test : tests) {
70             long value = test[0];
71             int min = Math.toIntExact(test[1]);
72             int max = Math.toIntExact(test[2]);
73             int expected = Math.toIntExact(test[3]);
74             failures += checkEquals("(int) Math.clamp(" + value + ", " + min + ", " + max + ")", Math.clamp(value, min, max), expected);
75             failures += checkEquals("(int) StrictMath.clamp(" + value + ", " + min + ", " + max + ")", StrictMath.clamp(value, min, max), expected);
76         }
77         for (long[] test : exceptionTests) {
78             long value = test[0];
79             int min = Math.toIntExact(test[1]);
80             int max = Math.toIntExact(test[2]);
81             failures += checkIllegalArgumentException("(int) Math.clamp(" + value + ", " + min + ", " + max + ")",
82                     () -> Math.clamp(value, min, max));
83             failures += checkIllegalArgumentException("(int) StrictMath.clamp(" + value + ", " + min + ", " + max + ")",
84                     () -> StrictMath.clamp(value, min, max));
85         }
86         return failures;
87     }
88 
testLongClamp()89     private static int testLongClamp() {
90         int failures = 0;
91         long[][] tests = {
92                 // value, min, max, expected
93                 {0L, 1L, 2L, 1L},
94                 {0L, 0L, 2L, 0L},
95                 {1L, 0L, 2L, 1L},
96                 {2L, 0L, 2L, 2L},
97                 {3L, 0L, 2L, 2L},
98                 {0L, Long.MIN_VALUE, Long.MAX_VALUE, 0},
99                 {Long.MIN_VALUE, Long.MIN_VALUE, Long.MAX_VALUE, Long.MIN_VALUE},
100                 {Long.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE, Long.MAX_VALUE},
101                 {0, 1, 1, 1},
102                 {Long.MAX_VALUE, 1, 1, 1}
103         };
104         long[][] exceptionTests = {
105                 // value, min, max
106                 {1L, 2L, 0L},
107                 {1, Long.MAX_VALUE, Long.MIN_VALUE}
108         };
109         for (long[] test : tests) {
110             long value = test[0];
111             long min = test[1];
112             long max = test[2];
113             long expected = test[3];
114             failures += checkEquals("(long) Math.clamp(" + value + ", " + min + ", " + max + ")", Math.clamp(value, min, max), expected);
115             failures += checkEquals("(long) StrictMath.clamp(" + value + ", " + min + ", " + max + ")", StrictMath.clamp(value, min, max), expected);
116         }
117         for (long[] test : exceptionTests) {
118             long value = test[0];
119             long min = test[1];
120             long max = test[2];
121             failures += checkIllegalArgumentException("(long) Math.clamp(" + value + ", " + min + ", " + max + ")",
122                     () -> Math.clamp(value, min, max));
123             failures += checkIllegalArgumentException("(long) StrictMath.clamp(" + value + ", " + min + ", " + max + ")",
124                     () -> StrictMath.clamp(value, min, max));
125         }
126         return failures;
127     }
128 
testDoubleClamp()129     private static int testDoubleClamp() {
130         int failures = 0;
131         double[][] tests = {
132                 // value, min, max, expected
133                 {-0.1, 0.0, 0.5, 0.0},
134                 {-0.0, 0.0, 0.5, 0.0},
135                 {0.0, 0.0, 0.5, 0.0},
136                 {Double.MIN_VALUE, 0.0, 0.5, Double.MIN_VALUE},
137                 {0.2, 0.0, 0.5, 0.2},
138                 {Math.nextDown(0.5), 0.0, 0.5, Math.nextDown(0.5)},
139                 {0.5, 0.0, 0.5, 0.5},
140                 {Math.nextUp(0.5), 0.0, 0.5, 0.5},
141                 {0.6, 0.0, 0.5, 0.5},
142 
143                 {Double.MAX_VALUE, 0.0, Double.POSITIVE_INFINITY, Double.MAX_VALUE},
144                 {Double.POSITIVE_INFINITY, 0.0, Double.MAX_VALUE, Double.MAX_VALUE},
145                 {-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 0.0, -Double.MAX_VALUE},
146                 {Double.NEGATIVE_INFINITY, -Double.MAX_VALUE, 0.0, -Double.MAX_VALUE},
147 
148                 {-1.0, -0.0, 0.0, -0.0},
149                 {-0.0, -0.0, 0.0, -0.0},
150                 {0.0, -0.0, 0.0, 0.0},
151                 {1.0, -0.0, 0.0, 0.0},
152                 {-1.0, 0.0, 0.0, 0.0},
153                 {-0.0, 0.0, 0.0, 0.0},
154                 {0.0, 0.0, 0.0, 0.0},
155                 {1.0, 0.0, 0.0, 0.0},
156                 {-1.0, -0.0, -0.0, -0.0},
157                 {-0.0, -0.0, -0.0, -0.0},
158                 {0.0, -0.0, -0.0, -0.0},
159                 {1.0, -0.0, -0.0, -0.0},
160 
161                 {Double.NaN, 0.0, 1.0, Double.NaN},
162                 {Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN}
163         };
164         double[][] exceptionTests = {
165                 // value, min, max
166                 {0.0, Double.NaN, Double.NaN},
167                 {0.0, 0.0, Double.NaN},
168                 {0.0, Double.NaN, 0.0},
169                 {Double.NaN, 1.0, 0.0},
170                 {0.0, 0.0, -0.0},
171                 {0.0, 1.0, -1.0}
172         };
173         for (double[] test : tests) {
174             double value = test[0];
175             double min = test[1];
176             double max = test[2];
177             double expected = test[3];
178             failures += checkEquals("(double) Math.clamp(" + value + ", " + min + ", " + max + ")", Math.clamp(value, min, max), expected);
179             failures += checkEquals("(double) StrictMath.clamp(" + value + ", " + min + ", " + max + ")", StrictMath.clamp(value, min, max), expected);
180         }
181         for (double[] test : exceptionTests) {
182             double value = test[0];
183             double min = test[1];
184             double max = test[2];
185             failures += checkIllegalArgumentException("(double) Math.clamp(" + value + ", " + min + ", " + max + ")",
186                     () -> Math.clamp(value, min, max));
187             failures += checkIllegalArgumentException("(double) StrictMath.clamp(" + value + ", " + min + ", " + max + ")",
188                     () -> StrictMath.clamp(value, min, max));
189         }
190         return failures;
191     }
192 
testFloatClamp()193     private static int testFloatClamp() {
194         int failures = 0;
195         float[][] tests = {
196                 // value, min, max, expected
197                 {-0.1f, 0.0f, 0.5f, 0.0f},
198                 {-0.0f, 0.0f, 0.5f, 0.0f},
199                 {0.0f, 0.0f, 0.5f, 0.0f},
200                 {Float.MIN_VALUE, 0.0f, 0.5f, Float.MIN_VALUE},
201                 {0.2f, 0.0f, 0.5f, 0.2f},
202                 {Math.nextDown(0.5f), 0.0f, 0.5f, Math.nextDown(0.5f)},
203                 {0.5f, 0.0f, 0.5f, 0.5f},
204                 {Math.nextUp(0.5f), 0.0f, 0.5f, 0.5f},
205                 {0.6f, 0.0f, 0.5f, 0.5f},
206 
207                 {Float.MAX_VALUE, 0.0f, Float.POSITIVE_INFINITY, Float.MAX_VALUE},
208                 {Float.POSITIVE_INFINITY, 0.0f, Float.MAX_VALUE, Float.MAX_VALUE},
209                 {-Float.MAX_VALUE, Float.NEGATIVE_INFINITY, 0.0f, -Float.MAX_VALUE},
210                 {Float.NEGATIVE_INFINITY, -Float.MAX_VALUE, 0.0f, -Float.MAX_VALUE},
211 
212                 {-1.0f, -0.0f, 0.0f, -0.0f},
213                 {-0.0f, -0.0f, 0.0f, -0.0f},
214                 {0.0f, -0.0f, 0.0f, 0.0f},
215                 {1.0f, -0.0f, 0.0f, 0.0f},
216                 {-1.0f, 0.0f, 0.0f, 0.0f},
217                 {-0.0f, 0.0f, 0.0f, 0.0f},
218                 {0.0f, 0.0f, 0.0f, 0.0f},
219                 {1.0f, 0.0f, 0.0f, 0.0f},
220                 {-1.0f, -0.0f, -0.0f, -0.0f},
221                 {-0.0f, -0.0f, -0.0f, -0.0f},
222                 {0.0f, -0.0f, -0.0f, -0.0f},
223                 {1.0f, -0.0f, -0.0f, -0.0f},
224 
225                 {Float.NaN, 0.0f, 1.0f, Float.NaN},
226                 {Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN}
227         };
228         float[][] exceptionTests = {
229                 // value, min, max
230                 {0.0f, Float.NaN, Float.NaN},
231                 {0.0f, 0.0f, Float.NaN},
232                 {0.0f, Float.NaN, 0.0f},
233                 {Float.NaN, 1.0f, 0.0f},
234                 {0.0f, 0.0f, -0.0f},
235                 {0.0f, 1.0f, -1.0f}
236         };
237         for (float[] test : tests) {
238             float value = test[0];
239             float min = test[1];
240             float max = test[2];
241             float expected = test[3];
242             failures += checkEquals("(float) Math.clamp(" + value + ", " + min + ", " + max + ")", Math.clamp(value, min, max), expected);
243             failures += checkEquals("(float) StrictMath.clamp(" + value + ", " + min + ", " + max + ")", StrictMath.clamp(value, min, max), expected);
244         }
245         for (float[] test : exceptionTests) {
246             float value = test[0];
247             float min = test[1];
248             float max = test[2];
249             failures += checkIllegalArgumentException("(float) Math.clamp(" + value + ", " + min + ", " + max + ")",
250                     () -> Math.clamp(value, min, max));
251             failures += checkIllegalArgumentException("(float) StrictMath.clamp(" + value + ", " + min + ", " + max + ")",
252                     () -> StrictMath.clamp(value, min, max));
253         }
254         return failures;
255     }
256 
checkIllegalArgumentException(String what, Runnable r)257     private static int checkIllegalArgumentException(String what, Runnable r) {
258         try {
259             r.run();
260         }
261         catch (IllegalArgumentException ex) {
262             return 0;
263         }
264         System.err.println(what+": missing expected exception");
265         return 1;
266     }
267 
checkEquals(String what, double actual, double expected)268     private static int checkEquals(String what, double actual, double expected) {
269         if (Double.doubleToLongBits(actual) != Double.doubleToLongBits(expected)) {
270             System.err.println(what + ": expected = " + expected + "; actual = " + actual);
271             return 1;
272         }
273         return 0;
274     }
275 
checkEquals(String what, long actual, long expected)276     private static int checkEquals(String what, long actual, long expected) {
277         if (actual != expected) {
278             System.err.println(what + ": expected = " + expected + "; actual = " + actual);
279             return 1;
280         }
281         return 0;
282     }
283 }
284