diff --git a/DIRECTORY.md b/DIRECTORY.md index b311b10fa177..2d124a72259e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -29,8 +29,10 @@ - 📄 [BcdConversion](src/main/java/com/thealgorithms/bitmanipulation/BcdConversion.java) - 📄 [BinaryPalindromeCheck](src/main/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheck.java) - 📄 [BitSwap](src/main/java/com/thealgorithms/bitmanipulation/BitSwap.java) + - 📄 [BitwiseGCD](src/main/java/com/thealgorithms/bitmanipulation/BitwiseGCD.java) - 📄 [BooleanAlgebraGates](src/main/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGates.java) - 📄 [ClearLeftmostSetBit](src/main/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBit.java) + - 📄 [CountBitsFlip](src/main/java/com/thealgorithms/bitmanipulation/CountBitsFlip.java) - 📄 [CountLeadingZeros](src/main/java/com/thealgorithms/bitmanipulation/CountLeadingZeros.java) - 📄 [CountSetBits](src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java) - 📄 [FindNthBit](src/main/java/com/thealgorithms/bitmanipulation/FindNthBit.java) @@ -98,6 +100,7 @@ - 📄 [BinaryToDecimal](src/main/java/com/thealgorithms/conversions/BinaryToDecimal.java) - 📄 [BinaryToHexadecimal](src/main/java/com/thealgorithms/conversions/BinaryToHexadecimal.java) - 📄 [BinaryToOctal](src/main/java/com/thealgorithms/conversions/BinaryToOctal.java) + - 📄 [CoordinateConverter](src/main/java/com/thealgorithms/conversions/CoordinateConverter.java) - 📄 [DecimalToAnyBase](src/main/java/com/thealgorithms/conversions/DecimalToAnyBase.java) - 📄 [DecimalToBinary](src/main/java/com/thealgorithms/conversions/DecimalToBinary.java) - 📄 [DecimalToHexadecimal](src/main/java/com/thealgorithms/conversions/DecimalToHexadecimal.java) @@ -118,6 +121,7 @@ - 📄 [PhoneticAlphabetConverter](src/main/java/com/thealgorithms/conversions/PhoneticAlphabetConverter.java) - 📄 [RgbHsvConversion](src/main/java/com/thealgorithms/conversions/RgbHsvConversion.java) - 📄 [RomanToInteger](src/main/java/com/thealgorithms/conversions/RomanToInteger.java) + - 📄 [TimeConverter](src/main/java/com/thealgorithms/conversions/TimeConverter.java) - 📄 [TurkishToLatinConversion](src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java) - 📄 [UnitConversions](src/main/java/com/thealgorithms/conversions/UnitConversions.java) - 📄 [UnitsConverter](src/main/java/com/thealgorithms/conversions/UnitsConverter.java) @@ -199,6 +203,7 @@ - 📄 [MinPriorityQueue](src/main/java/com/thealgorithms/datastructures/heaps/MinPriorityQueue.java) - 📁 **lists** - 📄 [CircleLinkedList](src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java) + - 📄 [CircularDoublyLinkedList](src/main/java/com/thealgorithms/datastructures/lists/CircularDoublyLinkedList.java) - 📄 [CountSinglyLinkedListRecursion](src/main/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursion.java) - 📄 [CreateAndDetectLoop](src/main/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoop.java) - 📄 [CursorLinkedList](src/main/java/com/thealgorithms/datastructures/lists/CursorLinkedList.java) @@ -343,11 +348,14 @@ - 📄 [BresenhamLine](src/main/java/com/thealgorithms/geometry/BresenhamLine.java) - 📄 [ConvexHull](src/main/java/com/thealgorithms/geometry/ConvexHull.java) - 📄 [GrahamScan](src/main/java/com/thealgorithms/geometry/GrahamScan.java) + - 📄 [Haversine](src/main/java/com/thealgorithms/geometry/Haversine.java) - 📄 [MidpointCircle](src/main/java/com/thealgorithms/geometry/MidpointCircle.java) - 📄 [MidpointEllipse](src/main/java/com/thealgorithms/geometry/MidpointEllipse.java) - 📄 [Point](src/main/java/com/thealgorithms/geometry/Point.java) - 📁 **graph** + - 📄 [BronKerbosch](src/main/java/com/thealgorithms/graph/BronKerbosch.java) - 📄 [ConstrainedShortestPath](src/main/java/com/thealgorithms/graph/ConstrainedShortestPath.java) + - 📄 [EdmondsKarp](src/main/java/com/thealgorithms/graph/EdmondsKarp.java) - 📄 [HopcroftKarp](src/main/java/com/thealgorithms/graph/HopcroftKarp.java) - 📄 [PredecessorConstrainedDfs](src/main/java/com/thealgorithms/graph/PredecessorConstrainedDfs.java) - 📄 [StronglyConnectedComponentOptimized](src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java) @@ -450,6 +458,7 @@ - 📄 [NonRepeatingElement](src/main/java/com/thealgorithms/maths/NonRepeatingElement.java) - 📄 [NthUglyNumber](src/main/java/com/thealgorithms/maths/NthUglyNumber.java) - 📄 [NumberOfDigits](src/main/java/com/thealgorithms/maths/NumberOfDigits.java) + - 📄 [NumberPersistence](src/main/java/com/thealgorithms/maths/NumberPersistence.java) - 📄 [PalindromeNumber](src/main/java/com/thealgorithms/maths/PalindromeNumber.java) - 📄 [ParseInteger](src/main/java/com/thealgorithms/maths/ParseInteger.java) - 📄 [PascalTriangle](src/main/java/com/thealgorithms/maths/PascalTriangle.java) @@ -486,6 +495,7 @@ - 📄 [SumOfArithmeticSeries](src/main/java/com/thealgorithms/maths/SumOfArithmeticSeries.java) - 📄 [SumOfDigits](src/main/java/com/thealgorithms/maths/SumOfDigits.java) - 📄 [SumOfOddNumbers](src/main/java/com/thealgorithms/maths/SumOfOddNumbers.java) + - 📄 [SumOfSquares](src/main/java/com/thealgorithms/maths/SumOfSquares.java) - 📄 [SumWithoutArithmeticOperators](src/main/java/com/thealgorithms/maths/SumWithoutArithmeticOperators.java) - 📄 [TrinomialTriangle](src/main/java/com/thealgorithms/maths/TrinomialTriangle.java) - 📄 [TwinPrime](src/main/java/com/thealgorithms/maths/TwinPrime.java) @@ -576,6 +586,7 @@ - 📁 **recursion** - 📄 [FibonacciSeries](src/main/java/com/thealgorithms/recursion/FibonacciSeries.java) - 📄 [GenerateSubsets](src/main/java/com/thealgorithms/recursion/GenerateSubsets.java) + - 📄 [SylvesterSequence](src/main/java/com/thealgorithms/recursion/SylvesterSequence.java) - 📁 **scheduling** - 📄 [AgingScheduling](src/main/java/com/thealgorithms/scheduling/AgingScheduling.java) - 📄 [EDFScheduling](src/main/java/com/thealgorithms/scheduling/EDFScheduling.java) @@ -641,6 +652,7 @@ - 📄 [MaxSumKSizeSubarray](src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java) - 📄 [MaximumSlidingWindow](src/main/java/com/thealgorithms/slidingwindow/MaximumSlidingWindow.java) - 📄 [MinSumKSizeSubarray](src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java) + - 📄 [MinimumWindowSubstring](src/main/java/com/thealgorithms/slidingwindow/MinimumWindowSubstring.java) - 📄 [ShortestCoprimeSegment](src/main/java/com/thealgorithms/slidingwindow/ShortestCoprimeSegment.java) - 📁 **sorts** - 📄 [AdaptiveMergeSort](src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java) @@ -719,6 +731,7 @@ - 📁 **strings** - 📄 [AhoCorasick](src/main/java/com/thealgorithms/strings/AhoCorasick.java) - 📄 [Alphabetical](src/main/java/com/thealgorithms/strings/Alphabetical.java) + - 📄 [AlternativeStringArrange](src/main/java/com/thealgorithms/strings/AlternativeStringArrange.java) - 📄 [Anagrams](src/main/java/com/thealgorithms/strings/Anagrams.java) - 📄 [CharactersSame](src/main/java/com/thealgorithms/strings/CharactersSame.java) - 📄 [CheckVowels](src/main/java/com/thealgorithms/strings/CheckVowels.java) @@ -726,6 +739,7 @@ - 📄 [CountWords](src/main/java/com/thealgorithms/strings/CountWords.java) - 📄 [HammingDistance](src/main/java/com/thealgorithms/strings/HammingDistance.java) - 📄 [HorspoolSearch](src/main/java/com/thealgorithms/strings/HorspoolSearch.java) + - 📄 [Isogram](src/main/java/com/thealgorithms/strings/Isogram.java) - 📄 [Isomorphic](src/main/java/com/thealgorithms/strings/Isomorphic.java) - 📄 [KMP](src/main/java/com/thealgorithms/strings/KMP.java) - 📄 [LetterCombinationsOfPhoneNumber](src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java) @@ -781,8 +795,10 @@ - 📄 [BcdConversionTest](src/test/java/com/thealgorithms/bitmanipulation/BcdConversionTest.java) - 📄 [BinaryPalindromeCheckTest](src/test/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheckTest.java) - 📄 [BitSwapTest](src/test/java/com/thealgorithms/bitmanipulation/BitSwapTest.java) + - 📄 [BitwiseGCDTest](src/test/java/com/thealgorithms/bitmanipulation/BitwiseGCDTest.java) - 📄 [BooleanAlgebraGatesTest](src/test/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGatesTest.java) - 📄 [ClearLeftmostSetBitTest](src/test/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBitTest.java) + - 📄 [CountBitsFlipTest](src/test/java/com/thealgorithms/bitmanipulation/CountBitsFlipTest.java) - 📄 [CountLeadingZerosTest](src/test/java/com/thealgorithms/bitmanipulation/CountLeadingZerosTest.java) - 📄 [CountSetBitsTest](src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java) - 📄 [FindNthBitTest](src/test/java/com/thealgorithms/bitmanipulation/FindNthBitTest.java) @@ -844,6 +860,7 @@ - 📄 [BinaryToDecimalTest](src/test/java/com/thealgorithms/conversions/BinaryToDecimalTest.java) - 📄 [BinaryToHexadecimalTest](src/test/java/com/thealgorithms/conversions/BinaryToHexadecimalTest.java) - 📄 [BinaryToOctalTest](src/test/java/com/thealgorithms/conversions/BinaryToOctalTest.java) + - 📄 [CoordinateConverterTest](src/test/java/com/thealgorithms/conversions/CoordinateConverterTest.java) - 📄 [DecimalToAnyBaseTest](src/test/java/com/thealgorithms/conversions/DecimalToAnyBaseTest.java) - 📄 [DecimalToBinaryTest](src/test/java/com/thealgorithms/conversions/DecimalToBinaryTest.java) - 📄 [DecimalToHexadecimalTest](src/test/java/com/thealgorithms/conversions/DecimalToHexadecimalTest.java) @@ -863,6 +880,7 @@ - 📄 [OctalToHexadecimalTest](src/test/java/com/thealgorithms/conversions/OctalToHexadecimalTest.java) - 📄 [PhoneticAlphabetConverterTest](src/test/java/com/thealgorithms/conversions/PhoneticAlphabetConverterTest.java) - 📄 [RomanToIntegerTest](src/test/java/com/thealgorithms/conversions/RomanToIntegerTest.java) + - 📄 [TimeConverterTest](src/test/java/com/thealgorithms/conversions/TimeConverterTest.java) - 📄 [TurkishToLatinConversionTest](src/test/java/com/thealgorithms/conversions/TurkishToLatinConversionTest.java) - 📄 [UnitConversionsTest](src/test/java/com/thealgorithms/conversions/UnitConversionsTest.java) - 📄 [UnitsConverterTest](src/test/java/com/thealgorithms/conversions/UnitsConverterTest.java) @@ -934,6 +952,7 @@ - 📄 [MinPriorityQueueTest](src/test/java/com/thealgorithms/datastructures/heaps/MinPriorityQueueTest.java) - 📁 **lists** - 📄 [CircleLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java) + - 📄 [CircularDoublyLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/CircularDoublyLinkedListTest.java) - 📄 [CountSinglyLinkedListRecursionTest](src/test/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursionTest.java) - 📄 [CreateAndDetectLoopTest](src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java) - 📄 [CursorLinkedListTest](src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java) @@ -990,6 +1009,9 @@ - 📄 [TrieTest](src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java) - 📄 [VerticalOrderTraversalTest](src/test/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversalTest.java) - 📄 [ZigzagTraversalTest](src/test/java/com/thealgorithms/datastructures/trees/ZigzagTraversalTest.java) + - 📁 **devutils** + - 📁 **entities** + - 📄 [ProcessDetailsTest](src/test/java/com/thealgorithms/devutils/entities/ProcessDetailsTest.java) - 📁 **divideandconquer** - 📄 [BinaryExponentiationTest](src/test/java/com/thealgorithms/divideandconquer/BinaryExponentiationTest.java) - 📄 [ClosestPairTest](src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java) @@ -1052,11 +1074,14 @@ - 📄 [BresenhamLineTest](src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java) - 📄 [ConvexHullTest](src/test/java/com/thealgorithms/geometry/ConvexHullTest.java) - 📄 [GrahamScanTest](src/test/java/com/thealgorithms/geometry/GrahamScanTest.java) + - 📄 [HaversineTest](src/test/java/com/thealgorithms/geometry/HaversineTest.java) - 📄 [MidpointCircleTest](src/test/java/com/thealgorithms/geometry/MidpointCircleTest.java) - 📄 [MidpointEllipseTest](src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java) - 📄 [PointTest](src/test/java/com/thealgorithms/geometry/PointTest.java) - 📁 **graph** + - 📄 [BronKerboschTest](src/test/java/com/thealgorithms/graph/BronKerboschTest.java) - 📄 [ConstrainedShortestPathTest](src/test/java/com/thealgorithms/graph/ConstrainedShortestPathTest.java) + - 📄 [EdmondsKarpTest](src/test/java/com/thealgorithms/graph/EdmondsKarpTest.java) - 📄 [HopcroftKarpTest](src/test/java/com/thealgorithms/graph/HopcroftKarpTest.java) - 📄 [PredecessorConstrainedDfsTest](src/test/java/com/thealgorithms/graph/PredecessorConstrainedDfsTest.java) - 📄 [StronglyConnectedComponentOptimizedTest](src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java) @@ -1150,6 +1175,7 @@ - 📄 [NonRepeatingElementTest](src/test/java/com/thealgorithms/maths/NonRepeatingElementTest.java) - 📄 [NthUglyNumberTest](src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java) - 📄 [NumberOfDigitsTest](src/test/java/com/thealgorithms/maths/NumberOfDigitsTest.java) + - 📄 [NumberPersistenceTest](src/test/java/com/thealgorithms/maths/NumberPersistenceTest.java) - 📄 [PalindromeNumberTest](src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java) - 📄 [ParseIntegerTest](src/test/java/com/thealgorithms/maths/ParseIntegerTest.java) - 📄 [PascalTriangleTest](src/test/java/com/thealgorithms/maths/PascalTriangleTest.java) @@ -1177,6 +1203,7 @@ - 📄 [SumOfArithmeticSeriesTest](src/test/java/com/thealgorithms/maths/SumOfArithmeticSeriesTest.java) - 📄 [SumOfDigitsTest](src/test/java/com/thealgorithms/maths/SumOfDigitsTest.java) - 📄 [SumOfOddNumbersTest](src/test/java/com/thealgorithms/maths/SumOfOddNumbersTest.java) + - 📄 [SumOfSquaresTest](src/test/java/com/thealgorithms/maths/SumOfSquaresTest.java) - 📄 [SumWithoutArithmeticOperatorsTest](src/test/java/com/thealgorithms/maths/SumWithoutArithmeticOperatorsTest.java) - 📄 [TestArmstrong](src/test/java/com/thealgorithms/maths/TestArmstrong.java) - 📄 [TwinPrimeTest](src/test/java/com/thealgorithms/maths/TwinPrimeTest.java) @@ -1227,6 +1254,7 @@ - 📄 [LinkListSortTest](src/test/java/com/thealgorithms/others/LinkListSortTest.java) - 📄 [LowestBasePalindromeTest](src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java) - 📄 [MaximumSumOfDistinctSubarraysWithLengthKTest](src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java) + - 📄 [MiniMaxAlgorithmTest](src/test/java/com/thealgorithms/others/MiniMaxAlgorithmTest.java) - 📄 [NewManShanksPrimeTest](src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java) - 📄 [NextFitTest](src/test/java/com/thealgorithms/others/NextFitTest.java) - 📄 [PasswordGenTest](src/test/java/com/thealgorithms/others/PasswordGenTest.java) @@ -1251,6 +1279,7 @@ - 📁 **recursion** - 📄 [FibonacciSeriesTest](src/test/java/com/thealgorithms/recursion/FibonacciSeriesTest.java) - 📄 [GenerateSubsetsTest](src/test/java/com/thealgorithms/recursion/GenerateSubsetsTest.java) + - 📄 [SylvesterSequenceTest](src/test/java/com/thealgorithms/recursion/SylvesterSequenceTest.java) - 📁 **scheduling** - 📄 [AgingSchedulingTest](src/test/java/com/thealgorithms/scheduling/AgingSchedulingTest.java) - 📄 [EDFSchedulingTest](src/test/java/com/thealgorithms/scheduling/EDFSchedulingTest.java) @@ -1317,6 +1346,7 @@ - 📄 [MaxSumKSizeSubarrayTest](src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java) - 📄 [MaximumSlidingWindowTest](src/test/java/com/thealgorithms/slidingwindow/MaximumSlidingWindowTest.java) - 📄 [MinSumKSizeSubarrayTest](src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java) + - 📄 [MinimumWindowSubstringTest](src/test/java/com/thealgorithms/slidingwindow/MinimumWindowSubstringTest.java) - 📄 [ShortestCoprimeSegmentTest](src/test/java/com/thealgorithms/slidingwindow/ShortestCoprimeSegmentTest.java) - 📁 **sorts** - 📄 [AdaptiveMergeSortTest](src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java) @@ -1393,6 +1423,7 @@ - 📁 **strings** - 📄 [AhoCorasickTest](src/test/java/com/thealgorithms/strings/AhoCorasickTest.java) - 📄 [AlphabeticalTest](src/test/java/com/thealgorithms/strings/AlphabeticalTest.java) + - 📄 [AlternativeStringArrangeTest](src/test/java/com/thealgorithms/strings/AlternativeStringArrangeTest.java) - 📄 [AnagramsTest](src/test/java/com/thealgorithms/strings/AnagramsTest.java) - 📄 [CharactersSameTest](src/test/java/com/thealgorithms/strings/CharactersSameTest.java) - 📄 [CheckVowelsTest](src/test/java/com/thealgorithms/strings/CheckVowelsTest.java) @@ -1400,6 +1431,7 @@ - 📄 [CountWordsTest](src/test/java/com/thealgorithms/strings/CountWordsTest.java) - 📄 [HammingDistanceTest](src/test/java/com/thealgorithms/strings/HammingDistanceTest.java) - 📄 [HorspoolSearchTest](src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java) + - 📄 [IsogramTest](src/test/java/com/thealgorithms/strings/IsogramTest.java) - 📄 [IsomorphicTest](src/test/java/com/thealgorithms/strings/IsomorphicTest.java) - 📄 [LetterCombinationsOfPhoneNumberTest](src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java) - 📄 [LongestCommonPrefixTest](src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java) diff --git a/ROTATING_CALIPERS_README.md b/ROTATING_CALIPERS_README.md new file mode 100644 index 000000000000..9cda367d65a5 --- /dev/null +++ b/ROTATING_CALIPERS_README.md @@ -0,0 +1,82 @@ +# Rotating Calipers Algorithm + +## Overview + +The Rotating Calipers algorithm is a computational geometry technique used to efficiently compute various properties of convex polygons. This implementation provides methods to calculate: + +- **Diameter**: The largest distance between any two points of a convex polygon +- **Width**: The smallest distance between two parallel lines enclosing the polygon +- **Minimum-area bounding rectangle**: The rectangle with minimal area that encloses all points + +## Time Complexity + +O(n) where n is the number of vertices in the convex polygon + +## Usage + +```java +import com.thealgorithms.geometry.RotatingCalipers; +import com.thealgorithms.geometry.Point; +import java.util.Arrays; +import java.util.List; + +// Create a convex polygon (triangle) +List triangle = Arrays.asList( + new Point(0, 0), + new Point(4, 0), + new Point(2, 3) +); + +// Calculate diameter +RotatingCalipers.PointPair diameter = RotatingCalipers.diameter(triangle); +System.out.println("Diameter: " + diameter.distance); + +// Calculate width (requires 3+ points) +double width = RotatingCalipers.width(triangle); +System.out.println("Width: " + width); + +// Calculate minimum bounding rectangle (requires 3+ points) +RotatingCalipers.Rectangle rect = RotatingCalipers.minimumBoundingRectangle(triangle); +System.out.println("Minimum area: " + rect.area); +``` + +## Key Features + +- **Static and final class**: Follows utility class pattern +- **Comprehensive JavaDoc**: Full documentation for all methods +- **Input validation**: Proper error handling for invalid inputs +- **Immutable data structures**: PointPair and Rectangle classes are immutable +- **Educational focus**: Clear, readable implementation suitable for learning + +## Algorithm Details + +The rotating calipers technique works by: + +1. Starting with a pair of parallel lines (calipers) touching the convex polygon +2. Rotating the calipers around the polygon while maintaining contact +3. Computing the desired property at each rotation step +4. Returning the optimal result found during the rotation + +## References + +- Shamos, M. I. (1978). Computational Geometry +- [Wikipedia: Rotating Calipers](https://en.wikipedia.org/wiki/Rotating_calipers) + +## Files Added + +- `src/main/java/com/thealgorithms/geometry/RotatingCalipers.java` - Main algorithm implementation +- `src/test/java/com/thealgorithms/geometry/RotatingCalipersTest.java` - Comprehensive unit tests +- `src/main/java/com/thealgorithms/geometry/RotatingCalipersDemo.java` - Demonstration class + +## Testing + +The implementation includes comprehensive JUnit 5 tests covering: +- Simple geometric shapes (triangles, squares, hexagons) +- Edge cases (minimum point requirements) +- Input validation +- String representations of result objects + +Run the demo class to see the algorithm in action: +```bash +java com.thealgorithms.geometry.RotatingCalipersDemo +``` \ No newline at end of file diff --git a/src/main/java/com/thealgorithms/geometry/RotatingCalipers.java b/src/main/java/com/thealgorithms/geometry/RotatingCalipers.java new file mode 100644 index 000000000000..baa2d87db77e --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/RotatingCalipers.java @@ -0,0 +1,245 @@ +package com.thealgorithms.geometry; + +import java.util.List; + +/** + * Implementation of the Rotating Calipers algorithm for convex polygons. + * + * The Rotating Calipers algorithm is used to compute various geometric properties + * of convex polygons efficiently, including: + * - Diameter: the largest distance between any two points + * - Width: the smallest distance between two parallel lines enclosing the polygon + * - Minimum-area bounding rectangle: the rectangle with minimal area that encloses all points + * + * Time complexity: O(n) where n is the number of vertices in the convex polygon + * + * Reference: Shamos, M. I. (1978). Computational Geometry. + * + * @author TheAlgorithms + */ +public final class RotatingCalipers { + + private RotatingCalipers() { + // Utility class + } + + /** + * Represents a pair of points. + */ + public static final class PointPair { + public final Point first; + public final Point second; + public final double distance; + + public PointPair(Point first, Point second) { + this.first = first; + this.second = second; + this.distance = euclideanDistance(first, second); + } + + @Override + public String toString() { + return String.format("PointPair{%s, %s, distance=%.2f}", first, second, distance); + } + } + + /** + * Represents a rectangle defined by four points. + */ + public static final class Rectangle { + public final Point[] vertices; + public final double area; + + public Rectangle(Point[] vertices) { + this.vertices = vertices.clone(); + this.area = calculateArea(vertices); + } + + private static double calculateArea(Point[] vertices) { + if (vertices.length != 4) { + return 0; + } + double width = euclideanDistance(vertices[0], vertices[1]); + double height = euclideanDistance(vertices[1], vertices[2]); + return width * height; + } + + @Override + public String toString() { + return String.format("Rectangle{area=%.2f}", area); + } + } + + /** + * Computes the diameter of a convex polygon using rotating calipers. + * The diameter is the maximum distance between any two vertices. + * + * @param convexHull List of points representing the convex hull in counter-clockwise order + * @return PointPair containing the two points that form the diameter + * @throws IllegalArgumentException if the hull has fewer than 2 points + */ + public static PointPair diameter(List convexHull) { + if (convexHull.size() < 2) { + throw new IllegalArgumentException("Convex hull must have at least 2 points"); + } + + if (convexHull.size() == 2) { + return new PointPair(convexHull.get(0), convexHull.get(1)); + } + + // Find maximum distance between all pairs of points + PointPair maxPair = new PointPair(convexHull.get(0), convexHull.get(1)); + + for (int i = 0; i < convexHull.size(); i++) { + for (int j = i + 1; j < convexHull.size(); j++) { + PointPair candidate = new PointPair(convexHull.get(i), convexHull.get(j)); + if (candidate.distance > maxPair.distance) { + maxPair = candidate; + } + } + } + + return maxPair; + } + + /** + * Computes the width of a convex polygon using rotating calipers. + * The width is the minimum distance between two parallel supporting lines. + * + * @param convexHull List of points representing the convex hull in counter-clockwise order + * @return The minimum width of the polygon + * @throws IllegalArgumentException if the hull has fewer than 3 points + */ + public static double width(List convexHull) { + if (convexHull.size() < 3) { + throw new IllegalArgumentException("Convex hull must have at least 3 points for width calculation"); + } + + int n = convexHull.size(); + double minWidth = Double.MAX_VALUE; + + int j = 1; + for (int i = 0; i < n; i++) { + Point p1 = convexHull.get(i); + Point p2 = convexHull.get((i + 1) % n); + + // Find the farthest point from edge p1-p2 + while (true) { + int nextJ = (j + 1) % n; + if (distanceToLine(p1, p2, convexHull.get(nextJ)) > distanceToLine(p1, p2, convexHull.get(j))) { + j = nextJ; + } else { + break; + } + } + + double currentWidth = distanceToLine(p1, p2, convexHull.get(j)); + minWidth = Math.min(minWidth, currentWidth); + } + + return minWidth; + } + + /** + * Computes the minimum-area bounding rectangle of a convex polygon. + * + * @param convexHull List of points representing the convex hull in counter-clockwise order + * @return Rectangle with minimum area that encloses all points + * @throws IllegalArgumentException if the hull has fewer than 3 points + */ + public static Rectangle minimumBoundingRectangle(List convexHull) { + if (convexHull.size() < 3) { + throw new IllegalArgumentException("Convex hull must have at least 3 points"); + } + + int n = convexHull.size(); + Rectangle minRect = null; + double minArea = Double.MAX_VALUE; + + for (int i = 0; i < n; i++) { + Point p1 = convexHull.get(i); + Point p2 = convexHull.get((i + 1) % n); + + // Create rectangle aligned with edge p1-p2 + Rectangle rect = createAlignedRectangle(convexHull, p1, p2); + + if (rect.area < minArea) { + minArea = rect.area; + minRect = rect; + } + } + + return minRect; + } + + /** + * Creates a rectangle aligned with the given edge that encloses all points. + */ + private static Rectangle createAlignedRectangle(List points, Point p1, Point p2) { + // Vector along the edge + double dx = p2.x() - p1.x(); + double dy = p2.y() - p1.y(); + double length = Math.sqrt(dx * dx + dy * dy); + + if (length == 0) { + // Degenerate case + return new Rectangle(new Point[]{p1, p1, p1, p1}); + } + + // Unit vectors + double ux = dx / length; + double uy = dy / length; + double vx = -uy; // Perpendicular vector + double vy = ux; + + double minU = 0; + double maxU = 0; + double minV = 0; + double maxV = 0; + + for (Point p : points) { + double u = (p.x() - p1.x()) * ux + (p.y() - p1.y()) * uy; + double v = (p.x() - p1.x()) * vx + (p.y() - p1.y()) * vy; + + minU = Math.min(minU, u); + maxU = Math.max(maxU, u); + minV = Math.min(minV, v); + maxV = Math.max(maxV, v); + } + + // Calculate rectangle corners + Point[] corners = new Point[4]; + corners[0] = new Point((int) Math.round(p1.x() + minU * ux + minV * vx), (int) Math.round(p1.y() + minU * uy + minV * vy)); + corners[1] = new Point((int) Math.round(p1.x() + maxU * ux + minV * vx), (int) Math.round(p1.y() + maxU * uy + minV * vy)); + corners[2] = new Point((int) Math.round(p1.x() + maxU * ux + maxV * vx), (int) Math.round(p1.y() + maxU * uy + maxV * vy)); + corners[3] = new Point((int) Math.round(p1.x() + minU * ux + maxV * vx), (int) Math.round(p1.y() + minU * uy + maxV * vy)); + + return new Rectangle(corners); + } + + /** + * Calculates the Euclidean distance between two points. + */ + private static double euclideanDistance(Point p1, Point p2) { + double dx = p1.x() - p2.x(); + double dy = p1.y() - p2.y(); + return Math.sqrt(dx * dx + dy * dy); + } + + /** + * Calculates the perpendicular distance from a point to a line defined by two points. + */ + private static double distanceToLine(Point lineStart, Point lineEnd, Point point) { + double dx = lineEnd.x() - lineStart.x(); + double dy = lineEnd.y() - lineStart.y(); + + if (dx == 0 && dy == 0) { + return euclideanDistance(lineStart, point); + } + + double numerator = Math.abs(dy * point.x() - dx * point.y() + lineEnd.x() * lineStart.y() - lineEnd.y() * lineStart.x()); + double denominator = Math.sqrt(dx * dx + dy * dy); + + return numerator / denominator; + } +} diff --git a/src/test/java/com/thealgorithms/geometry/RotatingCalipersTest.java b/src/test/java/com/thealgorithms/geometry/RotatingCalipersTest.java new file mode 100644 index 000000000000..5a5d7ba9d886 --- /dev/null +++ b/src/test/java/com/thealgorithms/geometry/RotatingCalipersTest.java @@ -0,0 +1,191 @@ +package com.thealgorithms.geometry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class RotatingCalipersTest { + + private static final double EPSILON = 1e-6; + + @Test + void testDiameterSimpleTriangle() { + List triangle = Arrays.asList( + new Point(0, 0), + new Point(3, 4), + new Point(0, 4) + ); + + RotatingCalipers.PointPair diameter = RotatingCalipers.diameter(triangle); + assertEquals(5.0, diameter.distance, EPSILON); + } + + @Test + void testDiameterSquare() { + List square = Arrays.asList( + new Point(0, 0), + new Point(2, 0), + new Point(2, 2), + new Point(0, 2) + ); + + RotatingCalipers.PointPair diameter = RotatingCalipers.diameter(square); + assertEquals(Math.sqrt(8), diameter.distance, EPSILON); + } + + @Test + void testDiameterTwoPoints() { + List twoPoints = Arrays.asList( + new Point(0, 0), + new Point(3, 4) + ); + + RotatingCalipers.PointPair diameter = RotatingCalipers.diameter(twoPoints); + assertEquals(5.0, diameter.distance, EPSILON); + } + + @Test + void testDiameterInvalidInput() { + List onePoint = Arrays.asList(new Point(0, 0)); + assertThrows(IllegalArgumentException.class, () -> RotatingCalipers.diameter(onePoint)); + } + + @Test + void testWidthTriangle() { + List triangle = Arrays.asList( + new Point(0, 0), + new Point(4, 0), + new Point(2, 3) + ); + + double width = RotatingCalipers.width(triangle); + assertTrue(width > 0); + assertTrue(width <= 4.0); // Width should be less than or equal to base + } + + @Test + void testWidthSquare() { + List square = Arrays.asList( + new Point(0, 0), + new Point(2, 0), + new Point(2, 2), + new Point(0, 2) + ); + + double width = RotatingCalipers.width(square); + assertEquals(2.0, width, EPSILON); + } + + @Test + void testWidthInvalidInput() { + List twoPoints = Arrays.asList( + new Point(0, 0), + new Point(1, 1) + ); + assertThrows(IllegalArgumentException.class, () -> RotatingCalipers.width(twoPoints)); + } + + @Test + void testMinimumBoundingRectangleTriangle() { + List triangle = Arrays.asList( + new Point(0, 0), + new Point(4, 0), + new Point(2, 3) + ); + + RotatingCalipers.Rectangle rect = RotatingCalipers.minimumBoundingRectangle(triangle); + assertTrue(rect.area > 0); + assertTrue(rect.area <= 12.0); // Should be less than axis-aligned bounding box + } + + @Test + void testMinimumBoundingRectangleSquare() { + List square = Arrays.asList( + new Point(0, 0), + new Point(2, 0), + new Point(2, 2), + new Point(0, 2) + ); + + RotatingCalipers.Rectangle rect = RotatingCalipers.minimumBoundingRectangle(square); + assertEquals(4.0, rect.area, EPSILON); + } + + @Test + void testMinimumBoundingRectangleInvalidInput() { + List twoPoints = Arrays.asList( + new Point(0, 0), + new Point(1, 1) + ); + assertThrows(IllegalArgumentException.class, () -> RotatingCalipers.minimumBoundingRectangle(twoPoints)); + } + + @Test + void testComplexPolygon() { + // Hexagon + List hexagon = Arrays.asList( + new Point(2, 0), + new Point(4, 1), + new Point(4, 3), + new Point(2, 4), + new Point(0, 3), + new Point(0, 1) + ); + + RotatingCalipers.PointPair diameter = RotatingCalipers.diameter(hexagon); + assertTrue(diameter.distance > 0); + + double width = RotatingCalipers.width(hexagon); + assertTrue(width > 0); + + RotatingCalipers.Rectangle rect = RotatingCalipers.minimumBoundingRectangle(hexagon); + assertTrue(rect.area > 0); + } + + @Test + void testPointPairToString() { + Point p1 = new Point(0, 0); + Point p2 = new Point(3, 4); + RotatingCalipers.PointPair pair = new RotatingCalipers.PointPair(p1, p2); + + String expected = "PointPair{(0, 0), (3, 4), distance=5.00}"; + assertEquals(expected, pair.toString()); + } + + @Test + void testRectangleToString() { + Point[] vertices = { + new Point(0, 0), + new Point(2, 0), + new Point(2, 2), + new Point(0, 2) + }; + RotatingCalipers.Rectangle rect = new RotatingCalipers.Rectangle(vertices); + + assertTrue(rect.toString().contains("Rectangle{area=")); + } + + @Test + void testRectangleAreaCalculation() { + Point[] vertices = {new Point(0, 0), new Point(1, 0), new Point(1, 1)}; + RotatingCalipers.Rectangle rect = new RotatingCalipers.Rectangle(vertices); + assertEquals(0.0, rect.area, EPSILON); + } + + @Test + void testDegenerateCase() { + List samePoints = Arrays.asList( + new Point(1, 1), + new Point(1, 1), + new Point(1, 1) + ); + + RotatingCalipers.Rectangle rect = RotatingCalipers.minimumBoundingRectangle(samePoints); + assertEquals(0.0, rect.area, EPSILON); + } +} +