Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@
* [BresenhamLine](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/BresenhamLine.java)
* [ConvexHull](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/ConvexHull.java)
* [GrahamScan](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/GrahamScan.java)
* [MidpointEllipse](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java)
* [Point](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/Point.java)
* graph
* [StronglyConnectedComponentOptimized](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java)
Expand Down Expand Up @@ -360,6 +361,7 @@
* [Average](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Average.java)
* [BinaryPow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/BinaryPow.java)
* [BinomialCoefficient](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java)
* [CatalanNumbers](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CatalanNumbers.java)
* [Ceil](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Ceil.java)
* [ChineseRemainderTheorem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ChineseRemainderTheorem.java)
* [CircularConvolutionFFT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java)
Expand Down Expand Up @@ -967,6 +969,7 @@
* [BresenhamLineTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java)
* [ConvexHullTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/ConvexHullTest.java)
* [GrahamScanTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/GrahamScanTest.java)
* [MidpointEllipseTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java)
* graph
* [StronglyConnectedComponentOptimizedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java)
* greedyalgorithms
Expand Down Expand Up @@ -1003,6 +1006,7 @@
* [AverageTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AverageTest.java)
* [BinaryPowTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/BinaryPowTest.java)
* [BinomialCoefficientTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/BinomialCoefficientTest.java)
* [CatalanNumbersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java)
* [CeilTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CeilTest.java)
* [ChineseRemainderTheoremTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ChineseRemainderTheoremTest.java)
* [CollatzConjectureTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,64 @@
package com.thealgorithms.conversions;

/**
* A utility class to perform affine transformations of the form:
* y = slope * x + intercept.
*
* This class supports inversion and composition of affine transformations.
* It is immutable, meaning each instance represents a fixed transformation.
*/
public final class AffineConverter {
private final double slope;
private final double intercept;

/**
* Constructs an AffineConverter with the given slope and intercept.
*
* @param inSlope The slope of the affine transformation.
* @param inIntercept The intercept (constant term) of the affine transformation.
* @throws IllegalArgumentException if either parameter is NaN.
*/
public AffineConverter(final double inSlope, final double inIntercept) {
if (Double.isNaN(inSlope) || Double.isNaN(inIntercept)) {
throw new IllegalArgumentException("Slope and intercept must be valid numbers.");
}
slope = inSlope;
intercept = inIntercept;
}

/**
* Converts the given input value using the affine transformation:
* result = slope * inValue + intercept.
*
* @param inValue The input value to convert.
* @return The transformed value.
*/
public double convert(final double inValue) {
return slope * inValue + intercept;
}

/**
* Returns a new AffineConverter representing the inverse of the current transformation.
* The inverse of y = slope * x + intercept is x = (y - intercept) / slope.
*
* @return A new AffineConverter representing the inverse transformation.
* @throws AssertionError if the slope is zero, as the inverse would be undefined.
*/
public AffineConverter invert() {
assert slope != 0.0;
assert slope != 0.0 : "Slope cannot be zero for inversion.";
return new AffineConverter(1.0 / slope, -intercept / slope);
}

/**
* Composes this affine transformation with another, returning a new AffineConverter.
* If this transformation is f(x) and the other is g(x), the result is f(g(x)).
*
* @param other Another AffineConverter to compose with.
* @return A new AffineConverter representing the composition of the two transformations.
*/
public AffineConverter compose(final AffineConverter other) {
return new AffineConverter(slope * other.slope, slope * other.intercept + intercept);
double newSlope = slope * other.slope;
double newIntercept = slope * other.intercept + intercept;
return new AffineConverter(newSlope, newIntercept);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,39 @@ void setUp() {
}

@Test
void testConstructor() {
void testConstructorWithValidValues() {
assertEquals(3.0, converter.convert(0.0), "Expected value when input is 0.0");
assertEquals(5.0, converter.convert(1.0), "Expected value when input is 1.0");
assertEquals(7.0, converter.convert(2.0), "Expected value when input is 2.0");
}

@Test
void testConvert() {
assertEquals(3.0, converter.convert(0.0), "Conversion at 0.0 should equal the intercept");
assertEquals(7.0, converter.convert(2.0), "2.0 should convert to 7.0");
assertEquals(11.0, converter.convert(4.0), "4.0 should convert to 11.0");
void testConstructorWithInvalidValues() {
assertThrows(IllegalArgumentException.class, () -> new AffineConverter(Double.NaN, 3.0), "Constructor should throw IllegalArgumentException for NaN slope");
}

@Test
void testConvertWithNegativeValues() {
assertEquals(-1.0, converter.convert(-2.0), "Negative input should convert correctly");
assertEquals(-3.0, new AffineConverter(-1.0, -1.0).convert(2.0), "Slope and intercept can be negative");
}

@Test
void testConvertWithFloatingPointPrecision() {
double result = new AffineConverter(1.3333, 0.6667).convert(3.0);
assertEquals(4.6666, result, 1e-4, "Conversion should maintain floating-point precision");
}

@Test
void testInvert() {
AffineConverter inverted = converter.invert();
assertEquals(0.0, inverted.convert(3.0), "Inverted converter should return 0.0 for input 3.0");
assertEquals(1.0, inverted.convert(5.0), "Inverted converter should return 1.0 for input 5.0");
assertEquals(2.0, inverted.convert(7.0), "Inverted converter should return 2.0 for input 7.0");
assertEquals(0.0, inverted.convert(3.0), "Inverted should return 0.0 for input 3.0");
assertEquals(1.0, inverted.convert(5.0), "Inverted should return 1.0 for input 5.0");
}

@Test
void testInvertWithZeroSlope() {
AffineConverter zeroSlopeConverter = new AffineConverter(0.0, 3.0);
assertThrows(AssertionError.class, zeroSlopeConverter::invert, "Invert should throw assertion error when slope is zero");
assertThrows(AssertionError.class, zeroSlopeConverter::invert, "Invert should throw AssertionError when slope is zero");
}

@Test
Expand All @@ -50,6 +58,30 @@ void testCompose() {

assertEquals(7.0, composed.convert(0.0), "Expected composed conversion at 0.0");
assertEquals(9.0, composed.convert(1.0), "Expected composed conversion at 1.0");
assertEquals(11.0, composed.convert(2.0), "Expected composed conversion at 2.0");
}

@Test
void testMultipleCompositions() {
AffineConverter c1 = new AffineConverter(2.0, 1.0);
AffineConverter c2 = new AffineConverter(3.0, -2.0);
AffineConverter c3 = c1.compose(c2); // (2x + 1) ∘ (3x - 2) => 6x - 1

assertEquals(-3.0, c3.convert(0.0), "Composed transformation should return -3.0 at 0.0");
assertEquals(3.0, c3.convert(1.0), "Composed transformation should return 3.0 at 1.0");
}

@Test
void testIdentityComposition() {
AffineConverter identity = new AffineConverter(1.0, 0.0);
AffineConverter composed = converter.compose(identity);

assertEquals(3.0, composed.convert(0.0), "Identity composition should not change the transformation");
assertEquals(7.0, composed.convert(2.0), "Identity composition should behave like the original");
}

@Test
void testLargeInputs() {
double largeValue = 1e6;
assertEquals(2.0 * largeValue + 3.0, converter.convert(largeValue), "Should handle large input values without overflow");
}
}