- 2.3.1
+ 2.4.0
17
@@ -54,26 +54,26 @@
2.17.1
- 3.2.5
+ 3.3.0
- 3.10.1
+ 3.11.0
3.1.0
- 8.0.1.Final
6.0.0
+ 8.0.1.Final
- 0.8.11
+ 0.8.12
5.10.2
3.25.3
1.6.0
3.6.3
- 3.3.0
+ 3.3.1
1.6.13
- 3.2.2
- 3.1.6
+ 3.2.5
+ 3.2.0
synerset
diff --git a/unitility-core/src/main/java/com/synerset/unitility/unitsystem/CalculableQuantity.java b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/CalculableQuantity.java
index 02df31c..52d8b43 100644
--- a/unitility-core/src/main/java/com/synerset/unitility/unitsystem/CalculableQuantity.java
+++ b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/CalculableQuantity.java
@@ -1,6 +1,7 @@
package com.synerset.unitility.unitsystem;
import com.synerset.unitility.unitsystem.exceptions.UnitSystemArgumentException;
+import com.synerset.unitility.unitsystem.util.ValueFormatter;
/**
* Interface representing a calculable quantity with operations for basic arithmetics.
@@ -16,6 +17,7 @@ public interface CalculableQuantity
+ * This method calculates the value of the physical quantity raised to the specified exponent
+ * and returns a new instance of the physical quantity with the updated value.
+ *
+ * @param exponent The exponent to which the current value is raised.
+ * @return A new physical quantity with the value raised to the given exponent.
+ */
+ default Q power(double exponent) {
+ double newValue = Math.pow(getValue(), exponent);
+ return withValue(newValue);
+ }
+
+ /**
+ * Calculates the square root of the physical quantity's value.
+ * This method computes the square root of the current value of the physical quantity
+ * and returns a new instance of the physical quantity with the updated value.
+ *
+ * @return A new physical quantity with the value as the square root of the original value.
+ */
+ default Q sqrt() {
+ double newValue = Math.sqrt(getValue());
+ return withValue(newValue);
+ }
+
+ /**
+ * Calculates the natural logarithm of the physical quantity's value.
+ * This method computes the natural logarithm of the current value of the physical quantity
+ * and returns a new instance of the physical quantity with the updated value.
+ * IMPORTANT: In some parts of the world (like Europe) natural logarithm is expressed as 'ln' symbol,
+ * and log means logarithm with base of 10. In this app, consistency with Math library was maintained,
+ * therefore log is natural logarithm (with e number in a base).
+ *
+ * @return A new physical quantity with the value as the natural logarithm of the original value.
+ * @throws UnitSystemArgumentException if the current value is not greater than zero.
+ */
+ default Q log() {
+ double value = getValue();
+ if (value <= 0) {
+ throw new UnitSystemArgumentException("Cannot calculate logarithm for non-positive value: " + value);
+ }
+ double newValue = Math.log(value);
+ return withValue(newValue);
+ }
+
+ /**
+ * Calculates the base-10 logarithm of the physical quantity's value.
+ * This method computes the base-10 logarithm of the current value of the physical quantity
+ * and returns a new instance of the physical quantity with the updated value.
+ * IMPORTANT: In some parts of the world (like Europe) natural logarithm is expressed as 'ln' symbol,
+ * and log means logarithm with base of 10. In this app, consistency with Math library was maintained,
+ * therefore log is natural logarithm (with e number in a base).
+ *
+ * @return A new physical quantity with the value as the base-10 logarithm of the original value.
+ * @throws UnitSystemArgumentException if the current value is not greater than zero.
+ */
+ default Q log10() {
+ double value = getValue();
+ if (value <= 0) {
+ throw new UnitSystemArgumentException("Cannot calculate logarithm for non-positive value: " + value);
+ }
+ double newValue = Math.log10(value);
+ return withValue(newValue);
+ }
+
+ // Ceiling, and rounding up
+
+ /**
+ * Returns a new physical quantity with the value rounded up to the nearest integer.
+ * Examples:
+ * ceil() for 10.123456 -> will result to 11
+ * ceil() for 0.123456 -> will result to 1
+ * ceil() for -10.123456- > will result to 10 (this one is contr intuitive)
+ *
+ * @return A new physical quantity with the value rounded up.
+ */
+ default Q ceil() {
+ double newValue = Math.ceil(getValue());
+ return withValue(newValue);
+ }
+
+ /**
+ * Returns a new physical quantity with the value rounded down to the nearest integer.
+ * Examples:
+ * floor() for 10.123456 -> will result to 10
+ * floor() for 0.123456 -> will result to 0
+ * floor() for -10.123456- > will result to -11 (this one is contr intuitive)
+ *
+ * @return A new physical quantity with the value rounded down.
+ */
+ default Q floor() {
+ double newValue = Math.floor(getValue());
+ return withValue(newValue);
+ }
+
+ /**
+ * Returns a new physical quantity with the value rounded in HALF_EVEN way to the specified number of relevant digits.
+ * Absolute value of an input argument for relevant digits is used.
+ * Examples:
+ * roundHalfEven(0) for 10.123456 -> will result to 10
+ * roundHalfEven(1) for 0.153456 -> will result to 0.2
+ * roundHalfEven(2) for -10.123456- > will result to -10.12
+ *
+ * @param relevantDigits The number of relevant digits to round to.
+ * @return A new physical quantity with the value rounded to the specified number of relevant digits.
+ */
+ default Q roundHalfEven(int relevantDigits) {
+ String formattedValue = ValueFormatter.toStringWithRelevantDigits(getValue(), relevantDigits);
+ double newValue = Double.parseDouble(formattedValue);
+ return withValue(newValue);
+ }
+
// Handling PhysicalQuantity as input argument
/**
@@ -203,4 +317,20 @@ default > double div(T inputQuanti
return divide(inputQuantity);
}
+ /**
+ * Raises this physical quantity's value to the power of another physical quantity's value.
+ * This method calculates the result of raising this quantity's value to the power of the value
+ * of the input quantity, and returns the result.
+ *
+ * @param inputQuantity The other physical quantity for raising to the power.
+ * @return The result of raising this quantity's value to the power of the other quantity's value.
+ */
+ default > double power(T inputQuantity) {
+ if (inputQuantity == null) {
+ return getValue();
+ }
+ double thisValue = getValue();
+ return Math.pow(thisValue, inputQuantity.getValue());
+ }
+
}
\ No newline at end of file
diff --git a/unitility-core/src/main/java/com/synerset/unitility/unitsystem/PhysicalQuantity.java b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/PhysicalQuantity.java
index 1ccee8f..b35d119 100644
--- a/unitility-core/src/main/java/com/synerset/unitility/unitsystem/PhysicalQuantity.java
+++ b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/PhysicalQuantity.java
@@ -11,6 +11,14 @@
*/
public interface PhysicalQuantity extends Comparable> {
+ /**
+ * Create a new physical quantity with new value of the same unit.
+ *
+ * @param value The value for the new physical quantity.
+ * @return A new physical quantity with the specified value.
+ */
+ PhysicalQuantity withValue(double value);
+
/**
* Get the value of the physical quantity.
*
diff --git a/unitility-core/src/main/java/com/synerset/unitility/unitsystem/TrigonometricQuantity.java b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/TrigonometricQuantity.java
new file mode 100644
index 0000000..115988c
--- /dev/null
+++ b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/TrigonometricQuantity.java
@@ -0,0 +1,179 @@
+package com.synerset.unitility.unitsystem;
+
+import com.synerset.unitility.unitsystem.common.AngleUnit;
+import com.synerset.unitility.unitsystem.common.AngleUnits;
+import com.synerset.unitility.unitsystem.exceptions.UnitSystemArgumentException;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * Interface representing a calculable quantity with operations for basic arithmetics.
+ *
+ * @param The type of {@link PhysicalQuantity} implementing this interface.
+ */
+public interface TrigonometricQuantity> extends CalculableQuantity {
+
+ // Trigonometric
+ /**
+ * Calculate the sine of the physical quantity's value in a current unit.
+ * If the quantity is an instance of {@link AngleUnit} its value will be automatically
+ * converted to radians before calculating resulting value.
+ *
+ * @return A new physical quantity with the sine of the current value in a current unit.
+ */
+ default Q sin() {
+ return applyTrigonometricFunction(Math::sin);
+ }
+
+ /**
+ * Calculate the hyperbolic sine of the physical quantity's value.
+ *
+ * @return A new physical quantity with the hyperbolic sine of the current value.
+ */
+ default Q sinh() {
+ return applyTrigonometricFunction(Math::sinh);
+ }
+
+ /**
+ * Calculate the arcsine of the physical quantity's value.
+ *
+ * @return A new physical quantity with the arcsine of the current value.
+ * @throws UnitSystemArgumentException if the value is out of the range [-1, 1].
+ */
+ default Q asin() {
+ double value = getValueInRadians();
+ if (value < -1 || value > 1) {
+ throw new UnitSystemArgumentException("Value out of range for arcsine. Valid range is [-1, 1].");
+ }
+ return applyTrigonometricFunction(Math::asin);
+ }
+
+ /**
+ * Calculate the cosine of the physical quantity's value in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the cosine of the current value.
+ */
+ default Q cos() {
+ return applyTrigonometricFunction(Math::cos);
+ }
+
+ /**
+ * Calculate the hyperbolic cosine of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the hyperbolic cosine of the current value.
+ */
+ default Q cosh() {
+ return applyTrigonometricFunction(Math::cosh);
+ }
+
+ /**
+ * Calculate the arccosine of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the arccosine of the current value.
+ * @throws UnitSystemArgumentException if the value is out of the range [-1, 1].
+ */
+ default Q acos() {
+ double value = getValueInRadians();
+ if (value < -1 || value > 1) {
+ throw new UnitSystemArgumentException("Value out of range for arccosine. Valid range is [-1, 1].");
+ }
+ return applyTrigonometricFunction(Math::acos);
+ }
+
+ /**
+ * Calculate the tangent of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the tangent of the current value.
+ * @throws UnitSystemArgumentException if the value is an odd multiple of π/2.
+ */
+ default Q tan() {
+ double value = getValueInRadians();
+ if (isMultipleOfPiOverTwo(value)) {
+ throw new UnitSystemArgumentException("Tangent is undefined for odd multiples of π/2.");
+ }
+ return applyTrigonometricFunction(Math::tan);
+ }
+
+ /**
+ * Calculate the hyperbolic tangent of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the hyperbolic tangent of the current value.
+ */
+ default Q tanh() {
+ return applyTrigonometricFunction(Math::tanh);
+ }
+
+ /**
+ * Calculate the arctangent of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the arctangent of the current value.
+ */
+ default Q atan() {
+ return applyTrigonometricFunction(Math::atan);
+ }
+
+ /**
+ * Calculate the cotangent of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the cotangent of the current value.
+ * @throws UnitSystemArgumentException if the value is a multiple of π.
+ */
+ default Q cot() {
+ double value = getValueInRadians();
+ if (isMultipleOfPi(value)) {
+ throw new UnitSystemArgumentException("Cotangent is undefined for multiples of π.");
+ }
+ return withValue(1 / tan().getValue());
+ }
+
+ /**
+ * Calculate the hyperbolic cotangent of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the hyperbolic cotangent of the current value.
+ * @throws UnitSystemArgumentException if the value is 0.
+ */
+ default Q coth() {
+ double value = getValueInRadians();
+ if (value == 0) {
+ throw new UnitSystemArgumentException("Hyperbolic cotangent is undefined for 0.");
+ }
+ return withValue(1 / tanh().getValue());
+ }
+
+ /**
+ * Calculate the arccotangent of the physical quantity's in a current unit.
+ * Use toUnit(Unit) if intended to convert value in other supported unit.
+ *
+ * @return A new physical quantity with the arccotangent of the current value.
+ */
+ default Q acot() {
+ return withValue(1 / atan().getValue());
+ }
+
+ @SuppressWarnings("unchecked")
+ private Q applyTrigonometricFunction(DoubleUnaryOperator unaryOperator) {
+ PhysicalQuantity unitInRadians = toUnit(AngleUnits.RADIANS);
+ double resultingValue = unaryOperator.applyAsDouble(unitInRadians.getValue());
+ return (Q) unitInRadians.withValue(resultingValue).toUnit(getUnitType());
+ }
+
+ private double getValueInRadians() {
+ return toUnit(AngleUnits.RADIANS).getValue();
+ }
+
+ private boolean isMultipleOfPiOverTwo(double value) {
+ return Math.abs((value / (Math.PI / 2)) % 1) < 1e-10;
+ }
+
+ private boolean isMultipleOfPi(double value) {
+ return Math.abs((value / Math.PI) % 1) < 1e-10;
+ }
+}
diff --git a/unitility-core/src/main/java/com/synerset/unitility/unitsystem/common/Angle.java b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/common/Angle.java
index 86c8273..b943e1d 100644
--- a/unitility-core/src/main/java/com/synerset/unitility/unitsystem/common/Angle.java
+++ b/unitility-core/src/main/java/com/synerset/unitility/unitsystem/common/Angle.java
@@ -1,10 +1,10 @@
package com.synerset.unitility.unitsystem.common;
-import com.synerset.unitility.unitsystem.CalculableQuantity;
+import com.synerset.unitility.unitsystem.TrigonometricQuantity;
import java.util.Objects;
-public class Angle implements CalculableQuantity {
+public class Angle implements TrigonometricQuantity {
private final double value;
private final double baseValue;
diff --git a/unitility-core/src/test/java/com/synerset/unitility/unitsystem/CalculableQuantityTest.java b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/CalculableQuantityTest.java
new file mode 100644
index 0000000..5a8d2a4
--- /dev/null
+++ b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/CalculableQuantityTest.java
@@ -0,0 +1,303 @@
+package com.synerset.unitility.unitsystem;
+
+import com.synerset.unitility.unitsystem.exceptions.UnitSystemArgumentException;
+import com.synerset.unitility.unitsystem.thermodynamic.Pressure;
+import com.synerset.unitility.unitsystem.thermodynamic.Temperature;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class CalculableQuantityTest {
+
+ // Basic math operations
+
+ @Test
+ @DisplayName("Add value: should correctly add value to quantity")
+ void add_shouldAddValueToQuantity() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(20);
+
+ // When
+ Temperature actualTemperature = temperature.plus(20);
+
+ // Then
+ Temperature exptectedTemperature = Temperature.ofCelsius(40);
+ assertThat(actualTemperature).isEqualTo(exptectedTemperature);
+ }
+
+ @Test
+ @DisplayName("Add quantity: should correctly subtract value from quantity")
+ void add_shouldSubtractValueToQuantity() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(20);
+
+ // When
+ Temperature actualTemperature = temperature.minus(20);
+
+ // Then
+ Temperature exptectedTemperature = Temperature.ofCelsius(0);
+ assertThat(actualTemperature).isEqualTo(exptectedTemperature);
+ }
+
+ @Test
+ @DisplayName("Subtract value: should correctly add quantities of the same type, but different units")
+ void subtract_shouldAddQuantityToSourceQuantity() {
+ // Given
+ Temperature sourceTemperature = Temperature.ofCelsius(20);
+ Temperature temperatureToAdd = Temperature.ofKelvins(20 + 273.15);
+
+ // When
+ Temperature actualTemperature = sourceTemperature.plus(temperatureToAdd);
+
+ // Then
+ Temperature exptectedTemperature = Temperature.ofCelsius(40);
+ assertThat(actualTemperature).isEqualTo(exptectedTemperature);
+ }
+
+ @Test
+ @DisplayName("Subtract quantity: should correctly subtract quantities of the same type, but different units")
+ void subtract_shouldSubtractQuantityToSourceQuantity() {
+ // Given
+ Temperature sourceTemperature = Temperature.ofCelsius(20);
+ Temperature temperatureToAdd = Temperature.ofKelvins(20 + 273.15);
+
+ // When
+ Temperature actualTemperature = sourceTemperature.minus(temperatureToAdd);
+
+ // Then
+ Temperature exptectedTemperature = Temperature.ofCelsius(0);
+ assertThat(actualTemperature).isEqualTo(exptectedTemperature);
+ }
+
+ @Test
+ @DisplayName("Multiply by value: should correctly multiply quantity by value")
+ void multiply_shouldMultiplyValueToQuantity() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(20);
+
+ // When
+ Temperature actualTemperature = temperature.multiply(2);
+ Temperature actualTemperatureTimes = temperature.times(2);
+
+ // Then
+ Temperature exptectedTemperature = Temperature.ofCelsius(40);
+ assertThat(actualTemperature).isEqualTo(exptectedTemperature).isEqualTo(actualTemperatureTimes);
+ }
+
+ @Test
+ @DisplayName("Multiply by quantity: should correctly multiply quantities of the same type, but different units")
+ void multiply_shouldMultiplyQuantityToSourceQuantity() {
+ // Given
+ Temperature sourceTemperature = Temperature.ofCelsius(20);
+ Pressure pressure = Pressure.ofPascal(2);
+
+ // When
+ double actualMultiplyResult = sourceTemperature.multiply(pressure);
+ double actualMultiplyResultTimes = sourceTemperature.times(pressure);
+
+ // Then
+ double expectedMultiplyResult = 40;
+ assertThat(actualMultiplyResult).isEqualTo(expectedMultiplyResult).isEqualTo(actualMultiplyResultTimes);
+ }
+
+ @Test
+ @DisplayName("Divide by value: should correctly divide quantity by value")
+ void divide_shouldDivideValueToQuantity() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(20);
+
+ // When
+ Temperature actualTemperature = temperature.div(2);
+
+ // Then
+ Temperature exptectedTemperature = Temperature.ofCelsius(10);
+ assertThat(actualTemperature).isEqualTo(exptectedTemperature);
+ }
+
+ @Test
+ @DisplayName("Divide by 0: should throw an exception if divided by 0")
+ void divide_shouldNotDivideByZeroThrowingException() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(20);
+
+ // Then
+ assertThatThrownBy(() -> temperature.div(0))
+ .isInstanceOf(UnitSystemArgumentException.class)
+ .hasMessage("Division by zero is not allowed. Please provide a non-zero divider value.");
+ }
+
+ @Test
+ @DisplayName("Divide by quantity: should correctly divide quantities of the same type, but different units")
+ void divide_shouldDivideQuantityToSourceQuantity() {
+ // Given
+ Temperature sourceTemperature = Temperature.ofCelsius(20);
+ Pressure pressure = Pressure.ofPascal(2);
+
+ // When
+ double actualDivideResult = sourceTemperature.div(pressure);
+
+ // Then
+ double expectedDivideResult = 10;
+ assertThat(actualDivideResult).isEqualTo(expectedDivideResult);
+ }
+
+ @Test
+ @DisplayName("Absolute value: should change wrapped value to absolute Math.abs(value)")
+ void abs_shouldChangeValueToAbsolute() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(-20);
+
+ // When
+ Temperature acutalTemperature = temperature.abs();
+
+ // Then
+ assertThat(acutalTemperature.getValue()).isEqualTo(20);
+ }
+
+ @Test
+ @DisplayName("Power to exponent: should correctly raise quantity to the power of the given exponent")
+ void power_shouldRaiseQuantityToExponent() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(2);
+
+ // When
+ Temperature poweredTemperature = temperature.power(3);
+
+ // Then
+ Temperature expectedTemperature = Temperature.ofCelsius(8);
+ assertThat(poweredTemperature).isEqualTo(expectedTemperature);
+ }
+
+ // Square Root
+
+ @Test
+ @DisplayName("Square root: should correctly calculate square root of the quantity's value")
+ void sqrt_shouldCalculateSquareRoot() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(25);
+
+ // When
+ Temperature sqrtTemperature = temperature.sqrt();
+
+ // Then
+ Temperature expectedTemperature = Temperature.ofCelsius(5);
+ assertThat(sqrtTemperature).isEqualTo(expectedTemperature);
+ }
+
+ // Logarithms
+
+ @Test
+ @DisplayName("Natural logarithm: should correctly calculate natural logarithm of the quantity's value")
+ void log_shouldCalculateNaturalLogarithm() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(10);
+
+ // When
+ Temperature logTemperature = temperature.log();
+
+ // Then
+ Temperature expectedTemperature = Temperature.ofCelsius(Math.log(10));
+ assertThat(logTemperature).isEqualTo(expectedTemperature);
+ }
+
+ @Test
+ @DisplayName("Natural logarithm of negative value: should throw exception when calculating natural logarithm of non-positive value")
+ void log_shouldThrowExceptionForNonPositiveValue() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(0);
+
+ // Then
+ assertThatThrownBy(temperature::log)
+ .isInstanceOf(UnitSystemArgumentException.class)
+ .hasMessageContaining("Cannot calculate logarithm for non-positive value");
+ }
+
+ @Test
+ @DisplayName("Logarithm of 10 base: should correctly calculate base-10 logarithm of the quantity's value")
+ void log10_shouldCalculateBase10Logarithm() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(1000);
+
+ // When
+ Temperature log10Temperature = temperature.log10();
+
+ // Then
+ Temperature expectedTemperature = Temperature.ofCelsius(3);
+ assertThat(log10Temperature).isEqualTo(expectedTemperature);
+ }
+
+ @Test
+ @DisplayName("Logarithm of 10 base of negative value: should throw exception when calculating base-10 logarithm of non-positive value")
+ void log10_shouldThrowExceptionForNonPositiveValue() {
+ // Given
+ Temperature temperature = Temperature.ofCelsius(-10);
+
+ // Then
+ assertThatThrownBy(temperature::log10)
+ .isInstanceOf(UnitSystemArgumentException.class);
+
+
+ }
+
+ // Ceiling, and rounding up
+
+ @Test
+ @DisplayName("Ceil: should correctly ceil up to the nearest integer")
+ void ceil_shouldCeilToNearestInteger() {
+ // Given
+ Temperature temperature1 = Temperature.ofCelsius(10.123456);
+ Temperature temperature2 = Temperature.ofCelsius(0.123456);
+ Temperature temperature3 = Temperature.ofCelsius(-10.123456);
+
+ // When
+ Temperature ceilTemp1 = temperature1.ceil();
+ Temperature ceilTemp2 = temperature2.ceil();
+ Temperature ceilTemp3 = temperature3.ceil();
+
+ // Then
+ assertThat(ceilTemp1).isEqualTo(Temperature.ofCelsius(11));
+ assertThat(ceilTemp2).isEqualTo(Temperature.ofCelsius(1));
+ assertThat(ceilTemp3).isEqualTo(Temperature.ofCelsius(-10));
+ }
+
+ @Test
+ @DisplayName("Floor: should correctly floor down to the nearest integer")
+ void floor_shouldFloorToNearestInteger() {
+ // Given
+ Temperature temperature1 = Temperature.ofCelsius(10.123456);
+ Temperature temperature2 = Temperature.ofCelsius(0.123456);
+ Temperature temperature3 = Temperature.ofCelsius(-10.123456);
+
+ // When
+ Temperature floorTemp1 = temperature1.floor();
+ Temperature floorTemp2 = temperature2.floor();
+ Temperature floorTemp3 = temperature3.floor();
+
+ // Then
+ assertThat(floorTemp1).isEqualTo(Temperature.ofCelsius(10));
+ assertThat(floorTemp2).isEqualTo(Temperature.ofCelsius(0));
+ assertThat(floorTemp3).isEqualTo(Temperature.ofCelsius(-11));
+ }
+
+ @Test
+ @DisplayName("Round half even: should correctly round up to half even for specified relevant digits")
+ void roundHalfEven_shouldFloorToNearestInteger() {
+ // Given
+ Temperature temperature1 = Temperature.ofCelsius(10.123456);
+ Temperature temperature2 = Temperature.ofCelsius(0.153456);
+ Temperature temperature3 = Temperature.ofCelsius(-10.123456);
+
+ // When
+ Temperature roundTemp1 = temperature1.roundHalfEven(0);
+ Temperature roundTemp2 = temperature2.roundHalfEven(1);
+ Temperature roundTemp3 = temperature3.roundHalfEven(2);
+
+ // Then
+ assertThat(roundTemp1).isEqualTo(Temperature.ofCelsius(10.0));
+ assertThat(roundTemp2).isEqualTo(Temperature.ofCelsius(0.2));
+ assertThat(roundTemp3).isEqualTo(Temperature.ofCelsius(-10.12));
+ }
+
+}
\ No newline at end of file
diff --git a/unitility-core/src/test/java/com/synerset/unitility/unitsystem/PhysicalQuantityTest.java b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/PhysicalQuantityTest.java
index 4cfdfca..cac09e8 100644
--- a/unitility-core/src/test/java/com/synerset/unitility/unitsystem/PhysicalQuantityTest.java
+++ b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/PhysicalQuantityTest.java
@@ -3,7 +3,6 @@
import com.synerset.unitility.unitsystem.common.Distance;
import com.synerset.unitility.unitsystem.common.DistanceUnits;
import com.synerset.unitility.unitsystem.dimensionless.BypassFactor;
-import com.synerset.unitility.unitsystem.exceptions.UnitSystemArgumentException;
import com.synerset.unitility.unitsystem.flow.VolumetricFlow;
import com.synerset.unitility.unitsystem.flow.VolumetricFlowUnit;
import com.synerset.unitility.unitsystem.thermodynamic.Pressure;
@@ -15,7 +14,6 @@
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
class PhysicalQuantityTest {
@@ -105,165 +103,6 @@ void isCloseToZero_shouldEvaluateIfCloseToZero(){
assertThat(closeToZeroFalse).isFalse();
}
- // Transformations
-
- @Test
- @DisplayName("should correctly add value to quantity")
- void add_shouldAddValueToQuantity() {
- // Given
- Temperature temperature = Temperature.ofCelsius(20);
-
- // When
- Temperature actualTemperature = temperature.plus(20);
-
- // Then
- Temperature exptectedTemperature = Temperature.ofCelsius(40);
- assertThat(actualTemperature).isEqualTo(exptectedTemperature);
- }
-
- @Test
- @DisplayName("should correctly subtract value from quantity")
- void add_shouldSubtractValueToQuantity() {
- // Given
- Temperature temperature = Temperature.ofCelsius(20);
-
- // When
- Temperature actualTemperature = temperature.minus(20);
-
- // Then
- Temperature exptectedTemperature = Temperature.ofCelsius(0);
- assertThat(actualTemperature).isEqualTo(exptectedTemperature);
- }
-
- @Test
- @DisplayName("should correctly add quantities of the same type, but different units")
- void subtract_shouldAddQuantityToSourceQuantity() {
- // Given
- Temperature sourceTemperature = Temperature.ofCelsius(20);
- Temperature temperatureToAdd = Temperature.ofKelvins(20 + 273.15);
-
- // When
- Temperature actualTemperature = sourceTemperature.plus(temperatureToAdd);
-
- // Then
- Temperature exptectedTemperature = Temperature.ofCelsius(40);
- assertThat(actualTemperature).isEqualTo(exptectedTemperature);
- }
-
- @Test
- @DisplayName("should correctly subtract quantities of the same type, but different units")
- void subtract_shouldSubtractQuantityToSourceQuantity() {
- // Given
- Temperature sourceTemperature = Temperature.ofCelsius(20);
- Temperature temperatureToAdd = Temperature.ofKelvins(20 + 273.15);
-
- // When
- Temperature actualTemperature = sourceTemperature.minus(temperatureToAdd);
-
- // Then
- Temperature exptectedTemperature = Temperature.ofCelsius(0);
- assertThat(actualTemperature).isEqualTo(exptectedTemperature);
- }
-
- @Test
- @DisplayName("should correctly multiply quantity by value")
- void multiply_shouldMultiplyValueToQuantity() {
- // Given
- Temperature temperature = Temperature.ofCelsius(20);
-
- // When
- Temperature actualTemperature = temperature.multiply(2);
- Temperature actualTemperatureTimes = temperature.times(2);
-
- // Then
- Temperature exptectedTemperature = Temperature.ofCelsius(40);
- assertThat(actualTemperature).isEqualTo(exptectedTemperature).isEqualTo(actualTemperatureTimes);
- }
-
- @Test
- @DisplayName("should correctly multiply quantities of the same type, but different units")
- void multiply_shouldMultiplyQuantityToSourceQuantity() {
- // Given
- Temperature sourceTemperature = Temperature.ofCelsius(20);
- Pressure pressure = Pressure.ofPascal(2);
-
- // When
- double actualMultiplyResult = sourceTemperature.multiply(pressure);
- double actualMultiplyResultTimes = sourceTemperature.times(pressure);
-
- // Then
- double expectedMultiplyResult = 40;
- assertThat(actualMultiplyResult).isEqualTo(expectedMultiplyResult).isEqualTo(actualMultiplyResultTimes);
- }
-
- @Test
- @DisplayName("should correctly divide quantity by value")
- void divide_shouldDivideValueToQuantity() {
- // Given
- Temperature temperature = Temperature.ofCelsius(20);
-
- // When
- Temperature actualTemperature = temperature.div(2);
-
- // Then
- Temperature exptectedTemperature = Temperature.ofCelsius(10);
- assertThat(actualTemperature).isEqualTo(exptectedTemperature);
- }
-
- @Test
- @DisplayName("should throw an exception if divided by 0")
- void divide_shouldNotDivideByZeroThrowingException() {
- // Given
- Temperature temperature = Temperature.ofCelsius(20);
-
- // Then
- assertThatThrownBy(() -> temperature.div(0))
- .isInstanceOf(UnitSystemArgumentException.class)
- .hasMessage("Division by zero is not allowed. Please provide a non-zero divider value.");
- }
-
- @Test
- @DisplayName("should correctly divide quantities of the same type, but different units")
- void divide_shouldDivideQuantityToSourceQuantity() {
- // Given
- Temperature sourceTemperature = Temperature.ofCelsius(20);
- Pressure pressure = Pressure.ofPascal(2);
-
- // When
- double actualDivideResult = sourceTemperature.div(pressure);
-
- // Then
- double expectedDivideResult = 10;
- assertThat(actualDivideResult).isEqualTo(expectedDivideResult);
- }
-
- @Test
- @DisplayName("should subtract value of current unit from number")
- void subtract_shouldSubtractFromValue() {
- // Given
- Temperature sourceTemperature = Temperature.ofCelsius(0.5);
-
- // When
- Temperature actualTemperature = sourceTemperature.minusFromValue(1);
-
- // Then
- Temperature expectedTemperature = Temperature.ofCelsius((1 - 0.5));
- assertThat(actualTemperature).isEqualTo(expectedTemperature);
- }
-
- @Test
- @DisplayName("should change wrapped value to absolute abs(value)")
- void abs_shouldChangeValueToAbsolute(){
- // Given
- Temperature temperature = Temperature.ofCelsius(-20);
-
- // When
- Temperature acutalTemperature = temperature.abs();
-
- // Then
- assertThat(acutalTemperature.getValue()).isEqualTo(20);
- }
-
// Others
@Test
diff --git a/unitility-core/src/test/java/com/synerset/unitility/unitsystem/TrigonometricQuantityTest.java b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/TrigonometricQuantityTest.java
new file mode 100644
index 0000000..81f8871
--- /dev/null
+++ b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/TrigonometricQuantityTest.java
@@ -0,0 +1,177 @@
+package com.synerset.unitility.unitsystem;
+
+import com.synerset.unitility.unitsystem.common.Angle;
+import com.synerset.unitility.unitsystem.exceptions.UnitSystemArgumentException;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class TrigonometricQuantityTest {
+
+ @Test
+ @DisplayName("Sine: should correctly calculate sine of the quantity's value in radians")
+ void sin_shouldCalculateSine() {
+ // Given
+ Angle angle = Angle.ofRadians(Math.PI / 2);
+
+ // When
+ Angle sinAngle = angle.sin();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.sin(Math.PI / 2));
+ assertThat(sinAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Cosine: should correctly calculate cosine of the quantity's value in radians")
+ void cos_shouldCalculateCosine() {
+ // Given
+ Angle angle = Angle.ofRadians(0);
+
+ // When
+ Angle cosAngle = angle.cos();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.cos(0));
+ assertThat(cosAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Tangent: should correctly calculate tangent of the quantity's value in radians")
+ void tan_shouldCalculateTangent() {
+ // Given
+ Angle angle = Angle.ofRadians(Math.PI / 4);
+
+ // When
+ Angle tanAngle = angle.tan();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.tan(Math.PI / 4));
+ assertThat(tanAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Arcsine: should correctly calculate arcsine of the quantity's value")
+ void asin_shouldCalculateArcsine() {
+ // Given
+ Angle angle = Angle.ofRadians(Math.sin(Math.PI / 6));
+
+ // When
+ Angle asinAngle = angle.asin();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.asin(Math.sin(Math.PI / 6)));
+ assertThat(asinAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Arcsine out of range: should throw exception when calculating arcsine for out of range value")
+ void asin_shouldThrowExceptionForOutOfRangeValue() {
+ // Given
+ Angle angle = Angle.ofRadians(2); // sin(2) > 1, which is out of range for asin
+ // Then
+ assertThatThrownBy(angle::asin)
+ .isInstanceOf(UnitSystemArgumentException.class)
+ .hasMessageContaining("Value out of range for arcsine");
+ }
+
+ @Test
+ @DisplayName("Hyperbolic sine: should correctly calculate hyperbolic sine of the quantity's value")
+ void sinh_shouldCalculateHyperbolicSine() {
+ // Given
+ Angle angle = Angle.ofRadians(1);
+
+ // When
+ Angle sinhAngle = angle.sinh();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.sinh(1));
+ assertThat(sinhAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Hyperbolic cosine: should correctly calculate hyperbolic cosine of the quantity's value")
+ void cosh_shouldCalculateHyperbolicCosine() {
+ // Given
+ Angle angle = Angle.ofRadians(1);
+
+ // When
+ Angle coshAngle = angle.cosh();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.cosh(1));
+ assertThat(coshAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Cotangent: should correctly calculate cotangent of the quantity's value")
+ void cot_shouldCalculateCotangent() {
+ // Given
+ Angle angle = Angle.ofRadians(Math.PI / 4);
+
+ // When
+ Angle ctgAngle = angle.cot();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(1 / Math.tan(Math.PI / 4));
+ assertThat(ctgAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Hyperbolic cotangent: should correctly calculate hyperbolic cotangent of the quantity's value")
+ void coth_shouldCalculateHyperbolicCotangent() {
+ // Given
+ Angle angle = Angle.ofRadians(1);
+
+ // When
+ Angle ctghAngle = angle.coth();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(1 / Math.tanh(1));
+ assertThat(ctghAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Arccosine: should correctly calculate arccosine of the quantity's value")
+ void acos_shouldCalculateArccosine() {
+ // Given
+ Angle angle = Angle.ofRadians(Math.cos(Math.PI / 3));
+
+ // When
+ Angle acosAngle = angle.acos();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.acos(Math.cos(Math.PI / 3)));
+ assertThat(acosAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Arctangent: should correctly calculate arctangent of the quantity's value")
+ void atan_shouldCalculateArctangent() {
+ // Given
+ Angle angle = Angle.ofRadians(Math.tan(Math.PI / 4));
+
+ // When
+ Angle atanAngle = angle.atan();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(Math.atan(Math.tan(Math.PI / 4)));
+ assertThat(atanAngle).isEqualTo(expectedAngle);
+ }
+
+ @Test
+ @DisplayName("Arccotangent: should correctly calculate arccotangent of the quantity's value")
+ void acot_shouldCalculateArccotangent() {
+ // Given
+ Angle angle = Angle.ofRadians(Math.tan(Math.PI / 4));
+
+ // When
+ Angle actgAngle = angle.acot();
+
+ // Then
+ Angle expectedAngle = Angle.ofRadians(1 / Math.atan(Math.tan(Math.PI / 4)));
+ assertThat(actgAngle).isEqualTo(expectedAngle);
+ }
+}
diff --git a/unitility-core/src/test/java/com/synerset/unitility/unitsystem/customunit/CustomAngle.java b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/customunit/CustomAngle.java
index 8f8b7d1..f032fe6 100644
--- a/unitility-core/src/test/java/com/synerset/unitility/unitsystem/customunit/CustomAngle.java
+++ b/unitility-core/src/test/java/com/synerset/unitility/unitsystem/customunit/CustomAngle.java
@@ -1,12 +1,12 @@
package com.synerset.unitility.unitsystem.customunit;
-import com.synerset.unitility.unitsystem.CalculableQuantity;
+import com.synerset.unitility.unitsystem.TrigonometricQuantity;
import com.synerset.unitility.unitsystem.common.AngleUnit;
import com.synerset.unitility.unitsystem.common.AngleUnits;
import java.util.Objects;
-public class CustomAngle implements CalculableQuantity {
+public class CustomAngle implements TrigonometricQuantity {
private final double value;
private final double baseValue;