|  | // Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | package backport; | 
|  |  | 
|  | import com.android.tools.r8.TestParameters; | 
|  | import com.android.tools.r8.TestRuntime.CfVm; | 
|  | import com.android.tools.r8.desugar.backports.AbstractBackportTest; | 
|  | import com.android.tools.r8.utils.AndroidApiLevel; | 
|  | import org.junit.runner.RunWith; | 
|  | import org.junit.runners.Parameterized; | 
|  | import org.junit.runners.Parameterized.Parameters; | 
|  |  | 
|  | @RunWith(Parameterized.class) | 
|  | public final class MathJava21Test extends AbstractBackportTest { | 
|  | @Parameters(name = "{0}") | 
|  | public static Iterable<?> data() { | 
|  | return getTestParameters() | 
|  | .withDexRuntimes() | 
|  | .withCfRuntimesStartingFromIncluding(CfVm.JDK21) | 
|  | .withAllApiLevelsAlsoForCf() | 
|  | .build(); | 
|  | } | 
|  |  | 
|  | public MathJava21Test(TestParameters parameters) { | 
|  | super(parameters, Math.class, Main.class); | 
|  | registerTarget(AndroidApiLevel.V, 92); | 
|  | } | 
|  |  | 
|  | static final class Main extends MiniAssert { | 
|  |  | 
|  | public static void main(String[] args) { | 
|  | testClampInt(); | 
|  | testClampLong(); | 
|  | testClampDouble(); | 
|  | testClampFloat(); | 
|  |  | 
|  | testCeilDivIntInt(); | 
|  | testCeilDivIntIntExact(); | 
|  | testCeilDivLongLong(); | 
|  | testCeilDivLongLongExact(); | 
|  | testCeilDivLongInt(); | 
|  |  | 
|  | testCeilModIntInt(); | 
|  | testCeilModLongLong(); | 
|  | testCeilModLongInt(); | 
|  |  | 
|  | testDivideExactInt(); | 
|  | testDivideExactLong(); | 
|  |  | 
|  | testFloorDivExactInt(); | 
|  | testFloorDivExactLong(); | 
|  |  | 
|  | testUnsignedMultiplyHigh(); | 
|  | } | 
|  |  | 
|  | private static void testClampInt() { | 
|  | assertEquals(1, Math.clamp(1, 0, 5)); | 
|  | assertEquals(0, Math.clamp(-1, 0, 5)); | 
|  | assertEquals(5, Math.clamp(10, 0, 5)); | 
|  | try { | 
|  | Math.clamp(1, 10, 5); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testClampLong() { | 
|  | assertEquals(1, Math.clamp(1L, 0L, 5L)); | 
|  | assertEquals(0, Math.clamp(-1L, 0L, 5L)); | 
|  | assertEquals(5, Math.clamp(10L, 0L, 5L)); | 
|  | try { | 
|  | Math.clamp(1L, 10L, 5L); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testClampDouble() { | 
|  | assertEquals(1.0, Math.clamp(1.0, 0.0, 5.0)); | 
|  | assertEquals(0.0, Math.clamp(-1.0, 0.0, 5.0)); | 
|  | assertEquals(5.0, Math.clamp(10.0, 0.0, 5.0)); | 
|  | try { | 
|  | Math.clamp(1.0, 10.0, 5.0); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  |  | 
|  | } | 
|  | // Check for -/+0.0. | 
|  | assertEquals(0.0, Math.clamp(-0.0, 0.0, 1.0)); | 
|  | assertEquals(0.0, Math.clamp(0.0, -0.0, 1.0)); | 
|  | assertEquals(-0.0, Math.clamp(-0.0, -1.0, 0.0)); | 
|  | assertEquals(-0.0, Math.clamp(0.0, -1.0, -0.0)); | 
|  | // Check for NaN. | 
|  | assertEquals(Double.NaN, Math.clamp(Double.NaN, 0.0, 1.0)); | 
|  | try { | 
|  | Math.clamp(1.0, Double.NaN, 5.0); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  | } | 
|  | try { | 
|  | Math.clamp(1.0, 10.0, Double.NaN); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testClampFloat() { | 
|  | assertEquals(1.0f, Math.clamp(1.0f, 0.0f, 5.0f)); | 
|  | assertEquals(0.0f, Math.clamp(-1.0f, 0.0f, 5.0f)); | 
|  | assertEquals(5.0f, Math.clamp(10.0f, 0.0f, 5.0f)); | 
|  | try { | 
|  | Math.clamp(1.0f, 10.0f, 5.0f); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  |  | 
|  | } | 
|  | // Check for -/+0.0f. | 
|  | assertEquals(0.0f, Math.clamp(-0.0f, 0.0f, 1.0f)); | 
|  | assertEquals(0.0f, Math.clamp(0.0f, -0.0f, 1.0f)); | 
|  | assertEquals(-0.0f, Math.clamp(-0.0f, -1.0f, 0.0f)); | 
|  | assertEquals(-0.0f, Math.clamp(0.0f, -1.0f, -0.0f)); | 
|  | // Check for NaN. | 
|  | assertEquals(Float.NaN, Math.clamp(Float.NaN, 0.0f, 1.0f)); | 
|  | try { | 
|  | Math.clamp(1.0f, Float.NaN, 5.0f); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  | } | 
|  | try { | 
|  | Math.clamp(1.0f, 10.0f, Float.NaN); | 
|  | fail("Should have thrown"); | 
|  | } catch (IllegalArgumentException ignored) { | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testCeilDivIntInt() { | 
|  | assertEquals(1, Math.ceilDiv(7, 7)); | 
|  | assertEquals(-1, Math.ceilDiv(-7, 7)); | 
|  | assertEquals(1, Math.ceilDiv(3, 7)); | 
|  | assertEquals(0, Math.ceilDiv(-3, 7)); | 
|  | assertEquals(-2147483648, Math.ceilDiv(Integer.MIN_VALUE, -1)); | 
|  | } | 
|  |  | 
|  | private static void testCeilDivIntIntExact() { | 
|  | assertEquals(1, Math.ceilDivExact(7, 7)); | 
|  | assertEquals(-1, Math.ceilDivExact(-7, 7)); | 
|  | assertEquals(1, Math.ceilDivExact(3, 7)); | 
|  | assertEquals(0, Math.ceilDivExact(-3, 7)); | 
|  | try { | 
|  | Math.ceilDivExact(Integer.MIN_VALUE, -1); | 
|  | fail("Should have thrown"); | 
|  | } catch (ArithmeticException ae) { | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testCeilDivLongLong() { | 
|  | assertEquals(1L, Math.ceilDiv(7L, 7L)); | 
|  | assertEquals(-1L, Math.ceilDiv(-7L, 7L)); | 
|  | assertEquals(1L, Math.ceilDiv(3L, 7L)); | 
|  | assertEquals(0, Math.ceilDiv(-3L, 7L)); | 
|  | assertEquals(-9223372036854775808L, Math.ceilDiv(Long.MIN_VALUE, -1L)); | 
|  | } | 
|  |  | 
|  | private static void testCeilDivLongLongExact() { | 
|  | assertEquals(1L, Math.ceilDivExact(7L, 7L)); | 
|  | assertEquals(-1L, Math.ceilDivExact(-7L, 7L)); | 
|  | assertEquals(1L, Math.ceilDivExact(3L, 7L)); | 
|  | assertEquals(0, Math.ceilDivExact(-3L, 7L)); | 
|  | try { | 
|  | Math.ceilDivExact(Long.MIN_VALUE, -1L); | 
|  | fail("Should have thrown"); | 
|  | } catch (ArithmeticException ae) { | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testCeilDivLongInt() { | 
|  | assertEquals(1L, Math.ceilDiv(7L, 7)); | 
|  | assertEquals(-1L, Math.ceilDiv(-7L, 7)); | 
|  | assertEquals(1L, Math.ceilDiv(3L, 7)); | 
|  | assertEquals(0, Math.ceilDiv(-3L, 7)); | 
|  | assertEquals(-9223372036854775808L, Math.ceilDiv(Long.MIN_VALUE, -1)); | 
|  | } | 
|  |  | 
|  | private static void testCeilModIntInt() { | 
|  | assertEquals(0, Math.ceilMod(7, 7)); | 
|  | assertEquals(0, Math.ceilMod(-7, 7)); | 
|  | assertEquals(-4, Math.ceilMod(3, 7)); | 
|  | assertEquals(-3, Math.ceilMod(-3, 7)); | 
|  | assertEquals(0, Math.ceilMod(Integer.MIN_VALUE, -1)); | 
|  | } | 
|  |  | 
|  | private static void testCeilModLongLong() { | 
|  | assertEquals(0L, Math.ceilMod(7L, 7L)); | 
|  | assertEquals(0L, Math.ceilMod(-7L, 7L)); | 
|  | assertEquals(-4L, Math.ceilMod(3L, 7L)); | 
|  | assertEquals(-3L, Math.ceilMod(-3L, 7L)); | 
|  | assertEquals(0L, Math.ceilMod(Long.MIN_VALUE, -1L)); | 
|  | } | 
|  |  | 
|  | private static void testCeilModLongInt() { | 
|  | assertEquals(0L, Math.ceilMod(7L, 7)); | 
|  | assertEquals(0L, Math.ceilMod(-7L, 7)); | 
|  | assertEquals(-4L, Math.ceilMod(3L, 7)); | 
|  | assertEquals(-3L, Math.ceilMod(-3L, 7)); | 
|  | assertEquals(0L, Math.ceilMod(Long.MIN_VALUE, -1)); | 
|  | } | 
|  |  | 
|  | private static void testDivideExactInt() { | 
|  | assertEquals(1, Math.divideExact(7, 7)); | 
|  | assertEquals(-1, Math.divideExact(-7, 7)); | 
|  | assertEquals(0, Math.divideExact(3, 7)); | 
|  | assertEquals(0, Math.divideExact(-3, 7)); | 
|  | try { | 
|  | Math.divideExact(Integer.MIN_VALUE, -1); | 
|  | fail("Should have thrown"); | 
|  | } catch (ArithmeticException ae) { | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testDivideExactLong() { | 
|  | assertEquals(1L, Math.divideExact(7L, 7L)); | 
|  | assertEquals(-1L, Math.divideExact(-7L, 7L)); | 
|  | assertEquals(0, Math.divideExact(3L, 7L)); | 
|  | assertEquals(0, Math.divideExact(-3L, 7L)); | 
|  | try { | 
|  | Math.divideExact(Long.MIN_VALUE, -1L); | 
|  | fail("Should have thrown"); | 
|  | } catch (ArithmeticException ae) { | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testFloorDivExactInt() { | 
|  | assertEquals(1, Math.floorDivExact(7, 7)); | 
|  | assertEquals(-1, Math.floorDivExact(-7, 7)); | 
|  | assertEquals(0, Math.floorDivExact(3, 7)); | 
|  | assertEquals(-1, Math.floorDivExact(-3, 7)); | 
|  | try { | 
|  | Math.floorDivExact(Integer.MIN_VALUE, -1); | 
|  | fail("Should have thrown"); | 
|  | } catch (ArithmeticException ae) { | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testFloorDivExactLong() { | 
|  | assertEquals(1L, Math.floorDivExact(7L, 7L)); | 
|  | assertEquals(-1L, Math.floorDivExact(-7L, 7L)); | 
|  | assertEquals(0L, Math.floorDivExact(3L, 7L)); | 
|  | assertEquals(-1L, Math.floorDivExact(-3L, 7L)); | 
|  | try { | 
|  | Math.floorDivExact(Long.MIN_VALUE, -1L); | 
|  | fail("Should have thrown"); | 
|  | } catch (ArithmeticException ae) { | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void testUnsignedMultiplyHigh() { | 
|  | assertEquals( | 
|  | 31696182030193L, Math.unsignedMultiplyHigh(8222222222222222L, 71111111111111111L)); | 
|  | assertEquals( | 
|  | 71079414929080917L, Math.unsignedMultiplyHigh(-8222222222222222L, 71111111111111111L)); | 
|  | } | 
|  | } | 
|  | } |