diff a/src/java.base/share/classes/java/lang/Integral.java b/src/java.base/share/classes/java/lang/Integral.java --- /dev/null +++ b/src/java.base/share/classes/java/lang/Integral.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Indicates an integral type that supports: + * + * + * and participates in operator overloading of all those operators. + * + * @param The integral type + */ +public interface Integral + extends Numerics, OrderedComparison { + + /** + * {@return the AND-ing of the two operands, operator "{@code &}"} + * + * @param op1 the first operand + * @param op2 the second operand + */ + IT and(IT op1, IT op2); + + /** + * {@return the OR-ing of the two operands, operator "{@code |}"} + * + * @param op1 the first operand + * @param op2 the second operand + */ + IT or(IT op1, IT op2); + + /** + * {@return the XOR-ing of the two operands, operator "{@code ^}"} + * + * @param op1 the first operand + * @param op2 the second operand + */ + IT xor(IT op1, IT op2); + + /** + * {@return the complement of the operand, operator "{@code ~}"} + * + * @param op1 the operand + * @throws UnsupportedOperationException if complement is not supposed + */ + IT complement(IT op1); + + /** + * {@return the first operand left shifted by the distance + * indicated by the second operand, operator "{@code <<"}} + * + * @param op1 the operand to be shifted + * @param shiftDistance the shift distance + */ + IT shiftLeft(IT op1, int shiftDistance); + + /** + * {@return the first operand right shifted by the distance + * indicated by the second operand, operator "{@code >>}"} + * + * @param op1 the operand to be shifted + * @param shiftDistance the shift distance + */ + IT shiftRight(IT op1, int shiftDistance); + + /** + * {@return the first operand right shifted, unsigned, by the + * distance indicated by the second operand, operator "{@code >>>}"} + * + * @param op1 the operand to be shifted + * @param shiftDistance the shift distance + * @throws UnsupportedOperationException if unsigned right shift is not supposed + */ + IT shiftRightUnsigned(IT op1, int shiftDistance); +} diff a/src/java.base/share/classes/java/lang/Numerics.java b/src/java.base/share/classes/java/lang/Numerics.java --- /dev/null +++ b/src/java.base/share/classes/java/lang/Numerics.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Indicates a type supports the basic arithmetic operations of + * addition, subtractions, multiplication, and divide ({@code +}, + * {@code -}, {@code*}, {@code /}, respectively), as well as negation + * (unary {@code -}), and participates in operator overloading of those + * operators. + * + * @apiNote + * TODO: what about remainder? + * + *

In mathematical terms, various kinds of algebraic structures + * support the operations modeled by this interface. For example, + * integers with Euclidean division support the operations in question + * as do algebraic fields. Commonly used algebraic fields + * include rational numbers, real numbers, and complex numbers. A + * field has a set of values and operations on those values. The + * operations have various properties known as the field + * axioms. These include associativity of addition and + * multiplication, commutativity of addition and multiplication, and + * multiplication distributing over addition. Fields can be + * {@linkplain OrderedComparison ordered} (rational numbers, real + * numbers) or unordered (complex numbers). + * + *

Types used to approximate a field, such as a floating-point type + * used to approximate real numbers, will both approximate the set of + * values of the field and the set of properties over the supported + * operations. In particular, properties like associativity of + * addition are not expected to hold for a floating-point + * type. + * + *

The intention of this interface is to enable types that + * customarily support numerical notions of addition, subtraction, + * multiplication and division to enjoy operator overloading syntax + * even if the underlying algebraic properties do not hold because of + * limitations in approximation. + * + * @param The numerical type + */ +public interface Numerics extends java.lang.runtime.Monoid { + /** + * Addition operation, operator binary "{@code +}". + * + * @param addend the first operand + * @param augend the second operand + * @return the sum of the operands + */ + @Override + NT add(NT addend, NT augend); + + /** + * Subtraction operation, operator binary "{@code -}". + * + * @implSpec + * The default implementation returns the sum of the first + * argument with the negation of the second argument. + * + * @param minuend the first operand + * @param subtrahend the second operand + * @return the difference of the operands + */ + default NT subtract(NT minuend, NT subtrahend) { + return this.add(minuend, this.negate(subtrahend)); + } + + /** + * Multiplication operation, operator "{@code *}". + * + * @param multiplier the first operand + * @param multiplicand the second operand + * @return the product of the operands + */ + NT multiply(NT multiplier, NT multiplicand); + + /** + * Division operation, operator "{@code /}". + * + * @param dividend the first operand + * @param divisor the second operand + * @return the quotient of the operands + */ + NT divide(NT dividend, NT divisor); + + /** + * Unary plus operation, operator unary "{@code +}". + * + * @apiNote + * It this needed? Default to returning this/operand? Or just to + * be be no-op not recognized for overloading? + * + * @param operand the operand + * @return unary plus of the operand + */ + NT plus(NT operand); + + /** + * Negation operation, operator unary "{@code -}". + * @param operand the operand + * @return the negation of the operand + */ + NT negate(NT operand); +} diff a/src/java.base/share/classes/java/lang/OrderedComparison.java b/src/java.base/share/classes/java/lang/OrderedComparison.java --- /dev/null +++ b/src/java.base/share/classes/java/lang/OrderedComparison.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Indicates a type supports ordered comparison operations ({@code + * <}, {@code <=}, {@code >}, {@code >=}) and participates in operator + * overloading of those operators. + * + * @param The type supporting ordered comparison + */ +public interface OrderedComparison { + /** + * {@return {@code true} if the first operand is less than the second + * operand; {@code false} otherwise, operator "{@code <}"} + * + * @param op1 the first operand + * @param op2 the second operand + */ + boolean lessThan(OC op1, OC op2); + + /** + * {@return {@code true} if the first operand is less than or + * equal to the second operand; {@code false} otherwise, operator + * "{@code <=}"} + * + * @param op1 the first operand + * @param op2 the second operand + */ + boolean lessThanEqual(OC op1, OC op2); + + /** + * {@return {@code true} if the first operand is greater than the + * second operand; {@code false} otherwise, operator "{@code >}"} + * + * @param op1 the first operand + * @param op2 the second operand + */ + boolean greaterThan(OC op1, OC op2); + + /** + * {@return {@code true} if the first operand is greater than or + * equal to the second operand; {@code false} otherwise, operator + * "{@code >=}"} + * + * @param op1 the first operand + * @param op2 the second operand + */ + boolean greaterThanEqual(OC op1, OC op2); + + /** + * {@return the smaller of the two operands} + * + * @apiNote + * Subtypes of this interface can define policies concerning which + * operand to return if they are the same size. + * + * @param op1 the first operand + * @param op2 the second operand + */ + OC min(OC op1, OC op2); + + /** + * {@return the larger of the two operands} + * + * @apiNote + * Subtypes of this interface can define policies concerning which + * operand to return if they are the same size. + * + * @param op1 the first operand + * @param op2 the second operand + */ + OC max(OC op1, OC op2); +} diff a/src/java.base/share/classes/java/lang/StandardFloatingPoint.java b/src/java.base/share/classes/java/lang/StandardFloatingPoint.java --- /dev/null +++ b/src/java.base/share/classes/java/lang/StandardFloatingPoint.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Indicate a floating-point type in the style of an IEEE 754 standard + * floating-point type. + * + * @apiNote + * Possible future work: separate subinterface for decimal IEEE 754 + * types. + * + * @param The standard floating-point type + */ +public interface StandardFloatingPoint + extends Numerics, OrderedComparison { + + /** + * {@inheritDoc OrderedComparison} + * + * @apiNote + * Explain all the IEEE 754-isms. + * + * @param op1 {@inheritDoc OrderedComparison} + * @param op2 {@inheritDoc OrderedComparison} + */ + @Override + boolean lessThan(SFP op1, SFP op2); + + /** + * {@inheritDoc OrderedComparison} + * + * @apiNote + * Explain all the IEEE 754-isms. + * + * @param op1 the {@inheritDoc OrderedComparison} + * @param op2 the {@inheritDoc OrderedComparison} + */ + @Override + boolean lessThanEqual(SFP op1, SFP op2); + + /** + * {@inheritDoc OrderedComparison} + * + * @apiNote + * Explain all the IEEE 754-isms. + * + * @param op1 {@inheritDoc OrderedComparison} + * @param op2 {@inheritDoc OrderedComparison} + */ + @Override + boolean greaterThan(SFP op1, SFP op2); + + /** + * {@inheritDoc OrderedComparison} + * + * @apiNote + * Explain all the IEEE 754-isms. + * + * @param op1 {@inheritDoc OrderedComparison} + * @param op2 {@inheritDoc OrderedComparison} + */ + @Override + boolean greaterThanEqual(SFP op1, SFP op2); + + /** + * {@inheritDoc OrderedComparison} + * + * @apiNote + * Explain all the IEEE 754-isms. + * + * @param op1 {@inheritDoc OrderedComparison} + * @param op2 {@inheritDoc OrderedComparison} + */ + @Override + SFP min(SFP op1, SFP op2); + + /** + * {@inheritDoc OrderedComparison} + * + * @apiNote + * Explain all the IEEE 754-isms. + * + * @param op1 {@inheritDoc OrderedComparison} + * @param op2 {@inheritDoc OrderedComparison} + */ + @Override + SFP max(SFP op1, SFP op2); + + /** + * {@return the square root of the operand} The square root is + * computed using the round to nearest rounding policy. + * + * @apiNote + * This method corresponds to the squareRoot operation defined in + * IEEE 754. + * + * @param radicand the argument to have its square root taken + * + */ + SFP sqrt(SFP radicand); + + /** + * Returns the fused multiply add of the three arguments; that is, + * returns the exact product of the first two arguments summed + * with the third argument and then rounded once to the nearest + * floating-point value. + * + * @apiNote This method corresponds to the fusedMultiplyAdd + * operation defined in IEEE 754. + * + * @param a a value + * @param b a value + * @param c a value + * + * @return (a × b + c) + * computed, as if with unlimited range and precision, and rounded + * once to the nearest floating-point value + */ + SFP fma(SFP a, SFP b, SFP c); + + /** + * Returns {@code true} if the specified number is a + * Not-a-Number (NaN) value, {@code false} otherwise. + * + * @apiNote + * This method corresponds to the isNaN operation defined in IEEE + * 754. + * + * @param operand the value to be tested. + * @return {@code true} if the argument is NaN; + * {@code false} otherwise. + */ + boolean isNaN(SFP operand); + + /** + * Returns {@code true} if the specified number is infinitely + * large in magnitude, {@code false} otherwise. + * + * @apiNote + * This method corresponds to the isInfinite operation defined in + * IEEE 754. + * + * @param operand the value to be tested. + * @return {@code true} if the argument is positive infinity or + * negative infinity; {@code false} otherwise. + */ + boolean isInfinite(SFP operand); + + /** + * Returns {@code true} if the argument is a finite floating-point + * value; returns {@code false} otherwise (for NaN and infinity + * arguments). + * + * @apiNote + * This method corresponds to the isFinite operation defined in + * IEEE 754. + * + * @param operand the {@code SFP} value to be tested + * @return {@code true} if the argument is a finite + * floating-point value, {@code false} otherwise. + */ + boolean isFinite(SFP operand); + + /** + * Returns the size of an ulp of the argument. + * + *

Special Cases: + *

    + *
  • If the argument is NaN, then the result is NaN. + *
  • If the argument is positive or negative infinity, then the + * result is positive infinity. + *
  • If the argument is positive or negative zero, then the result is + * the minimum value of the format. + *
+ * + * @param operand the floating-point value whose ulp is to be returned + * @return the size of an ulp of the argument + */ + SFP ulp(SFP operand); + + /** + * Returns a hexadecimal string representation of the argument. + * + * @param operand the value to be converted. + * @return a hex string representation of the argument. + * + */ + String toHexString(SFP operand); + + // Possible TODO: + // scaleBy + // nextUp/nextDown +} diff a/src/java.base/share/classes/java/lang/UnsignedInt.java b/src/java.base/share/classes/java/lang/UnsignedInt.java --- /dev/null +++ b/src/java.base/share/classes/java/lang/UnsignedInt.java @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +import java.lang.runtime.Monoid; + +/** + * Unsigned 32-bit integers. + */ +public class /*value record*/ UnsignedInt { + private int value; + + private UnsignedInt(int value) { + this.value = value; + } + + /** + * An {@code Integral} witness for {@code UnsignedInt}. + */ + // Fails at runtime if Integral is used instead of Monoid + public static __witness Monoid INTEGRAL = + new Integral<>() { + @Override + public UnsignedInt zero() { return UnsignedInt.valueOf(0); } + + @Override + @SuppressWarnings("deprecation") + public UnsignedInt add(UnsignedInt b1, UnsignedInt b2) { + return UnsignedInt.add(b1, b2); + } + + @Override + public UnsignedInt subtract(UnsignedInt minuend, UnsignedInt subtrahend) { + return UnsignedInt.subtract(minuend, subtrahend); + } + + @Override + public UnsignedInt multiply(UnsignedInt multiplier, UnsignedInt multiplicand) { + return UnsignedInt.multiply(multiplier, multiplicand); + } + + @Override + public UnsignedInt divide(UnsignedInt dividend, UnsignedInt divisor) { + return UnsignedInt.divide(dividend, divisor); + } + + @Override + public UnsignedInt plus(UnsignedInt operand) { + return operand; + } + + @Override + public UnsignedInt negate(UnsignedInt operand) { + throw new UnsupportedOperationException("Cannot negate unsigned value"); + } + + @Override + public UnsignedInt and(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.and(op1, op2); + } + + @Override + public UnsignedInt or(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.or(op1, op2); + } + + @Override + public UnsignedInt xor(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.xor(op1, op2); + } + + @Override + public UnsignedInt complement(UnsignedInt op1) { + return UnsignedInt.complement(op1); + } + + @Override + public UnsignedInt shiftLeft(UnsignedInt op1, int shiftDistance) { + return UnsignedInt.shiftLeft(op1, shiftDistance); + } + + @Override + public UnsignedInt shiftRight(UnsignedInt op1, int shiftDistance){ + return UnsignedInt.shiftRight(op1, shiftDistance); + } + + @Override + public UnsignedInt shiftRightUnsigned(UnsignedInt op1, int shiftDistance) { + return UnsignedInt.shiftRightUnsigned(op1, shiftDistance); + } + + @Override + public boolean lessThan(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.lessThan(op1, op2); + } + + @Override + public boolean lessThanEqual(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.lessThanEqual(op1, op2); + } + + @Override + public boolean greaterThan(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.greaterThan(op1, op2); + } + + @Override + public boolean greaterThanEqual(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.greaterThanEqual(op1, op2); + } + + @Override + public UnsignedInt min(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.min(op1, op2); + } + + @Override + public UnsignedInt max(UnsignedInt op1, UnsignedInt op2) { + return UnsignedInt.max(op1, op2); + } + }; + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + */ + public static UnsignedInt valueOf(int x) { + return new UnsignedInt(x < 0 ? 0 : x ); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @throws ArithmeticException lorem ipsum + */ + public static UnsignedInt valueOfExact(int x) { + if (x < 0) { + throw new ArithmeticException("Attempt to convert negative value to unsigned"); + } + return new UnsignedInt(x); + } + + /** + * Addition operation, binary "{@code +}". + * + * @param addend the first operand + * @param augend the second operand + * @return the sum of the operands + */ + public static UnsignedInt add(UnsignedInt addend, UnsignedInt augend) { + // signed and unsigned addition is the same for 2's complement + return new UnsignedInt(addend.value + augend.value); + } + + /** + * Subtraction operation, binary "{@code -}". + * @param minuend the first operand + * @param subtrahend the second operand + * @return the difference of the operands + */ + public static UnsignedInt subtract(UnsignedInt minuend, UnsignedInt subtrahend) { + // signed and unsigned subtraction is the same for 2's complement + return new UnsignedInt(minuend.value - subtrahend.value); + } + + /** + * Multiplication operation, "{@code *}". + * + * @param multiplier the first operand + * @param multiplicand the second operand + * @return the product of the operands + */ + public static UnsignedInt multiply(UnsignedInt multiplier, + UnsignedInt multiplicand) { + // signed and unsigned multiply is the same for 2's complement + return new UnsignedInt(multiplier.value * multiplicand.value); + } + + /** + * Division operation, "{@code /}". + * + * @param dividend the first operand + * @param divisor the second operand + * @return the quotient of the operands + * @see Integer#divideUnsigned(int, int) + */ + public static UnsignedInt divide(UnsignedInt dividend, + UnsignedInt divisor) { + return new UnsignedInt(Integer.divideUnsigned(dividend.value, + divisor.value)); + } + + /** + * Remainder operation, "{@code %}". + * + * @param dividend the first operand + * @param divisor the second operand + * @return the remainder of the operands + * @see Integer#remainderUnsigned(int, int) + */ + public static UnsignedInt remainder(UnsignedInt dividend, + UnsignedInt divisor) { + return new UnsignedInt((Integer.remainderUnsigned(dividend.value, + divisor.value))); + } + + /** + * Unary plus operation, "{@code +}". + * + * @param operand the operand + * @return unary plus of the operand + */ + public static UnsignedInt plus(UnsignedInt operand) { + return operand; + } + + /** + * Negation operation, unary "{@code -}". + * @param operand the operand + * @return the negation of the operand + */ + public static UnsignedInt negate(UnsignedInt operand) { + // What does negating an unsigned int do in C? + // From the draft C standard 6.5.3: + // + // "The result of the unary - operator is the negative of its + // (promoted) operand. The integer promotions are performed on + // the operand, and the result has the promoted type." + + throw new UnsupportedOperationException("Cannot negate unsigned value"); + } + + /** + * {@return lorem ipsum} + */ + @Override + public String toString() { + return toString(this); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @see Integer#toUnsignedString(int) + */ + public static String toString(UnsignedInt x) { + return Integer.toUnsignedString(x.value); + } + + /** + * {@return lorem ipsum} + * + * @param x lorem ipsum + * @param radix lorem ipsum + * @see Integer#toUnsignedString(int, int) + */ + public static String toString(UnsignedInt x, int radix) { + return Integer.toUnsignedString(x.value, radix); + } + + /** + * {@return lorem ipsum} + * @param s lorem ipsum + */ + public static UnsignedInt valueOf(String s) { + return new UnsignedInt(Integer.parseUnsignedInt(s)); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static int compare(UnsignedInt x, UnsignedInt y) { + return Integer.compareUnsigned(x.value, y.value); + } + + // todo: replace ordered comparison implementation with better code + // from "Hackers Delight" or similar. + + // OrderedComparison + // min, max, lessThan, lessThanEqual, greaterThan, greaterThanEqual, + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static boolean lessThan(UnsignedInt x, UnsignedInt y) { + return Integer.compareUnsigned(x.value, y.value) < 0; + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static boolean lessThanEqual(UnsignedInt x, UnsignedInt y) { + return Integer.compareUnsigned(x.value, y.value) <= 0; + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static boolean greaterThan(UnsignedInt x, UnsignedInt y) { + return Integer.compareUnsigned(x.value, y.value) > 0; + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static boolean greaterThanEqual(UnsignedInt x, UnsignedInt y) { + return Integer.compareUnsigned(x.value, y.value) >= 0; + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static UnsignedInt min(UnsignedInt x, UnsignedInt y) { + return (Integer.compareUnsigned(x.value, y.value) <= 0) ? x : y; + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static UnsignedInt max(UnsignedInt x, UnsignedInt y) { + return (Integer.compareUnsigned(x.value, y.value) >= 0) ? x : y; + } + + // Integral + // and, or, xor, complement, shiftLeft, shiftRight, shiftRightUnsigned + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static UnsignedInt and(UnsignedInt x, UnsignedInt y) { + return new UnsignedInt(x.value & y.value); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static UnsignedInt or(UnsignedInt x, UnsignedInt y) { + return new UnsignedInt(x.value | y.value); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param y lorem ipsum + */ + public static UnsignedInt xor(UnsignedInt x, UnsignedInt y) { + return new UnsignedInt(x.value ^ y.value); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + */ + public static UnsignedInt complement(UnsignedInt x) { + return new UnsignedInt(~x.value); + } + + // Per the JLS, the shiftDistance is AND-ed with a 5 or 6 bit mask + // (for int and long value being shifted, respectively) so the + // distance always non-negative. + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param shiftDistance lorem ipsum + * @jls 15.19 Shift Operators + */ + public static UnsignedInt shiftLeft(UnsignedInt x, int shiftDistance) { + return new UnsignedInt(x.value << shiftDistance); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param shiftDistance lorem ipsum + * @jls 15.19 Shift Operators + */ + public static UnsignedInt shiftRight(UnsignedInt x, int shiftDistance) { + // Shifts of an unsigned value are always unsigned; use >>> *not* >>. + return new UnsignedInt(x.value >>> shiftDistance); + } + + /** + * {@return lorem ipsum} + * @param x lorem ipsum + * @param shiftDistance lorem ipsum + * @jls 15.19 Shift Operators + */ + public static UnsignedInt shiftRightUnsigned(UnsignedInt x, int shiftDistance) { + // Shifts of an unsigned value are always unsigned; use >>> *not* >>. + return new UnsignedInt(x.value >>> shiftDistance); + } +} diff a/src/java.base/share/classes/java/lang/runtime/Monoid.java b/src/java.base/share/classes/java/lang/runtime/Monoid.java --- /dev/null +++ b/src/java.base/share/classes/java/lang/runtime/Monoid.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.runtime; + +/** + * A monoid. Supports a zero element and addition, + * participates in operator overloading of {@code +}. + * + *

Say something about defining witnesses for the wrapper classes. + * + * @apiNote + * Fun fact: monoids are subcomponents of richer algebraic structures + * like rings and fields. + * + *

Say something about expected associativity of add operation? + *

A zero element is one where: + *

+ * monoid.add(element,       monoid.zero()) == 
+ * monoid.add(monoid.zero(), element) ==
+ * element
+ * 
+ * + * Besides occurring in numerical setting, a monoid can be defined + * in other contexts as well. For example, string concatenation with + * the empty string ({@code ""}) serving as the zero element forms a + * monoid. + * + * @param the monoid type. + */ +public interface Monoid { + /** + * {@return the zero element of the monoid} + * This element is an identity for the {@linkplain add add} + * operation. + */ + X zero(); + + /** + * Adds two monoid elements together. + * + * @param x1 the first monoid element + * @param x2 the second monoid element + * @return the result of adding the provided monoid elements + */ + X add(X x1, X x2); + + /** + * {@return a monoid for {@code byte}} + */ + __witness Monoid byteMonoid() { + return new Monoid<>() { + @Override + public Byte zero() { + return 0; + } + + @Override + public Byte add(Byte x1, Byte x2) { + return (byte)(x1 + x2); + } + }; + } + + /** + * {@return a monoid for {@code short}} + */ + __witness Monoid shortMonoid() { + return new Monoid<>() { + @Override + public Short zero() { + return 0; + } + + @Override + public Short add(Short x1, Short x2) { + return (short)(x1 + x2); + } + }; + } + + /** + * {@return a monoid for {@code int}} + */ + __witness Monoid intMonoid() { + return new Monoid<>() { + @Override + public Integer zero() { + return 0; + } + + @Override + public Integer add(Integer x1, Integer x2) { + return x1 + x2; + } + }; + } + + /** + * {@return a monoid for {@code long}} + */ + __witness Monoid longMonoid() { + return new Monoid<>() { + @Override + public Long zero() { + return 0L; + } + + @Override + public Long add(Long x1, Long x2) { + return x1 + x2; + } + }; + } + + /** + * {@return a monoid for {@code float}} + */ + __witness Monoid floatMonoid() { + return new Monoid<>() { + @Override + public Float zero() { + // More correct with respect to addition to -0.0: + // -0.0 + +0.0 => +0.0 + // -0.0 + -0.0 => -0.0 + return -0.0f; + } + + @Override + public Float add(Float x1, Float x2) { + return x1 + x2; + } + }; + } + + /** + * {@return a monoid for {@code double}} + */ + __witness Monoid doubleMonoid() { + return new Monoid<>() { + @Override + public Double zero() { + // More correct with respect to addition to -0.0: + // -0.0 + +0.0 => +0.0 + // -0.0 + -0.0 => -0.0 + return -0.0d; + } + + @Override + public Double add(Double x1, Double x2) { + return x1 + x2; + } + }; + } +} diff a/src/java.base/share/classes/java/lang/runtime/Witness.java b/src/java.base/share/classes/java/lang/runtime/Witness.java --- /dev/null +++ b/src/java.base/share/classes/java/lang/runtime/Witness.java @@ -0,0 +1,13 @@ +package java.lang.runtime; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used to mark type class witnesses in the classfile. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Witness { } diff a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -320,10 +320,116 @@ * use the intrinsic. */ private static final int MONTGOMERY_INTRINSIC_THRESHOLD = 512; + /** + * An {@code Integral} witness for {@code BigInteger}. + */ + // Fails at runtime if Integral is used instead of Monoid + public static __witness java.lang.runtime.Monoid INTEGRAL = + new Integral<>() { + @Override + public BigInteger zero() { return BigInteger.ZERO; } + + @Override + public BigInteger add(BigInteger b1, BigInteger b2) { + return b1.add(b2); + } + + @Override + public BigInteger subtract(BigInteger minuend, BigInteger subtrahend) { + return minuend.subtract(subtrahend); + } + + @Override + public BigInteger multiply(BigInteger multiplier, BigInteger multiplicand) { + return multiplier.multiply( multiplicand); + } + + @Override + public BigInteger divide(BigInteger dividend, BigInteger divisor) { + return dividend.divide(divisor); + } + + @Override + public BigInteger plus(BigInteger operand) { + return operand; + } + + @Override + public BigInteger negate(BigInteger operand) { + return operand.negate(); + } + + @Override + public BigInteger and(BigInteger op1, BigInteger op2) { + return op1.and(op2); + } + + @Override + public BigInteger or(BigInteger op1, BigInteger op2) { + return op1.or(op2); + } + + @Override + public BigInteger xor(BigInteger op1, BigInteger op2) { + return op1.xor(op2); + } + + @Override + public BigInteger complement(BigInteger op1) { + throw new UnsupportedOperationException(""); + } + + @Override + public BigInteger shiftLeft(BigInteger op1, int shiftDistance) { + return op1.shiftLeft(shiftDistance); + } + + @Override + public BigInteger shiftRight(BigInteger op1, int shiftDistance){ + return op1.shiftRight(shiftDistance); + } + + @Override + public BigInteger shiftRightUnsigned(BigInteger op1, int shiftDistance) { + // return op1.shiftRight(shiftDistance); + throw new UnsupportedOperationException(""); + } + + @Override + public boolean lessThan(BigInteger op1, BigInteger op2) { + return op1.compareTo(op2) < 0; + } + + @Override + public boolean lessThanEqual(BigInteger op1, BigInteger op2) { + return op1.compareTo(op2) <= 0; + } + + @Override + public boolean greaterThan(BigInteger op1, BigInteger op2) { + return op1.compareTo(op2) > 0; + } + + @Override + public boolean greaterThanEqual(BigInteger op1, BigInteger op2) { + return op1.compareTo(op2) >= 0; + } + + @Override + public BigInteger min(BigInteger op1, BigInteger op2) { + return op1.min(op2); + } + + @Override + public BigInteger max(BigInteger op1, BigInteger op2) { + return op1.max(op2); + } + }; + // Constructors /** * Translates a byte sub-array containing the two's-complement binary * representation of a BigInteger into a BigInteger. The sub-array is