From 91e7fb334e62f4dc4c8206539a32721f53c7542f Mon Sep 17 00:00:00 2001 From: Kelly Norton Date: Wed, 1 May 2024 18:26:40 -0400 Subject: [PATCH] finishing up Interval2D --- bin/algs4.php | 1 + src/Commands/Interval2D.php | 92 +++++++++++++++++++++++++++++++++++++ src/Interval2D.php | 69 ++++++++++++++++++++++++---- src/Point2D.php | 2 +- tests/Interval2DTest.php | 69 ++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 9 deletions(-) create mode 100644 src/Commands/Interval2D.php diff --git a/bin/algs4.php b/bin/algs4.php index 69f2aa9..4b93c70 100755 --- a/bin/algs4.php +++ b/bin/algs4.php @@ -14,5 +14,6 @@ new Commands\AllowList(), new Commands\Average(), new Commands\RandomSeq(), + new Commands\Interval2D(), ]); $app->run(); \ No newline at end of file diff --git a/src/Commands/Interval2D.php b/src/Commands/Interval2D.php new file mode 100644 index 0000000..fff590a --- /dev/null +++ b/src/Commands/Interval2D.php @@ -0,0 +1,92 @@ +addArgument( + 'xmin', + InputArgument::REQUIRED, + 'the minimum x coordinate' + ); + $this->addArgument( + 'xmax', + InputArgument::REQUIRED, + 'the maximum x coordinate' + ); + $this->addArgument( + 'ymin', + InputArgument::REQUIRED, + 'the minimum y coordinate' + ); + $this->addArgument( + 'ymax', + InputArgument::REQUIRED, + 'the maximum y coordinate' + ); + $this->addArgument( + 'trials', + InputArgument::REQUIRED, + 'the number of trials' + ); + } + + #[Override] + protected function execute( + InputInterface $input, + OutputInterface $output + ): int { + $x_min = parse_float($input->getArgument('xmin')); + $x_max = parse_float($input->getArgument('xmax')); + $y_min = parse_float($input->getArgument('ymin')); + $y_max = parse_float($input->getArgument('ymax')); + $trials = parse_int($input->getArgument('trials')); + + $x_interval = Interval1D::fromMinMax($x_min, $x_max); + $y_interval = Interval1D::fromMinMax($y_min, $y_max); + $box = \Kellegous\Algs4\Interval2D::fromXY($x_interval, $y_interval); + // $box->draw(); + + $counter = new Counter('hits'); + $random = Random::withEngine(new Mt19937()); + for ($i = 0; $i < $trials; $i++) { + $x = $random->uniformFloat(); + $y = $random->uniformFloat(); + $point = new Point2D($x, $y); + if ($box->contains($point)) { + $counter->increment(); + } + // else $point->draw(); + } + + Stdio::out()->println($counter); + Stdio::out()->printf("box area = %.2f\n", $box->area()); + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/src/Interval2D.php b/src/Interval2D.php index 0554e05..f2bcd93 100644 --- a/src/Interval2D.php +++ b/src/Interval2D.php @@ -4,14 +4,43 @@ namespace Kellegous\Algs4; +/** + * The {@code Interval2D} class represents a closed two-dimensional interval, + * which represents all points (x, y) with both {@code xmin <= x <= xmax} and + * {@code ymin <= y <= ymax}. + * Two-dimensional intervals are immutable: their values cannot be changed + * after they are created. + * The class {@code Interval2D} includes methods for checking whether + * a two-dimensional interval contains a point and determining whether + * two two-dimensional intervals intersect. + *

+ * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + * @author Kelly Norton + */ final readonly class Interval2D { + /** + * Initializes a two-dimensional interval. + * @param Interval1D $x + * @param Interval1D $y + */ public function __construct( private Interval1D $x, private Interval1D $y ) { } + /** + * Initializes a two-dimensional interval. + * @param Interval1D $x + * @param Interval1D $y + * @return self + */ public static function fromXY( Interval1D $x, Interval1D $y @@ -19,29 +48,53 @@ public static function fromXY( return new self($x, $y); } + /** + * Does this two-dimensional interval intersect that two-dimensional interval? + * @param Interval2D $that + * @return bool + */ public function intersects(self $that): bool { return $this->x->intersects($that->x) && $this->y->intersects($that->y); } - public function x(): Interval1D + /** + * Returns the area of this two-dimensional interval. + * @return float + */ + public function area(): float { - return $this->x; + return $this->x->length() * $this->y->length(); } - public function y(): Interval1D + /** + * Returns a string representation of this two-dimensional interval. + * @return string + */ + public function __toString(): string { - return $this->y; + return sprintf('%s x %s', $this->x, $this->y); } - public function area(): float + /** + * Does this two-dimensional interval contain the point p? + * @param Point2D $point + * @return bool + */ + public function contains(Point2D $point): bool { - return $this->x->length() * $this->y->length(); + return $this->x->contains($point->x()) + && $this->y->contains($point->y()); } - public function __toString(): string + public function x(): Interval1D { - return sprintf('%s x %s', $this->x, $this->y); + return $this->x; + } + + public function y(): Interval1D + { + return $this->y; } } \ No newline at end of file diff --git a/src/Point2D.php b/src/Point2D.php index 0ff34b3..c6d2fad 100644 --- a/src/Point2D.php +++ b/src/Point2D.php @@ -4,7 +4,7 @@ final readonly class Point2D { - private function __construct( + public function __construct( private float $x, private float $y ) { diff --git a/tests/Interval2DTest.php b/tests/Interval2DTest.php index 45d0d79..053368a 100644 --- a/tests/Interval2DTest.php +++ b/tests/Interval2DTest.php @@ -126,6 +126,66 @@ public static function asStringTests(): iterable ]; } + /** + * @return iterable + */ + public static function containsTests(): iterable + { + yield 'center' => [ + Interval2D::fromXY( + Interval1D::fromMinMax(-1, 1), + Interval1D::fromMinMax(-1, 1) + ), + Point2D::fromXY(0, 0), + true + ]; + + yield '< x' => [ + Interval2D::fromXY( + Interval1D::fromMinMax(-1, 1), + Interval1D::fromMinMax(-1, 1) + ), + Point2D::fromXY(-2, 0), + false + ]; + + yield '> x' => [ + Interval2D::fromXY( + Interval1D::fromMinMax(-1, 1), + Interval1D::fromMinMax(-1, 1) + ), + Point2D::fromXY(2, 0), + false + ]; + + yield '< y' => [ + Interval2D::fromXY( + Interval1D::fromMinMax(-1, 1), + Interval1D::fromMinMax(-1, 1) + ), + Point2D::fromXY(0, -2), + false + ]; + + yield '> y' => [ + Interval2D::fromXY( + Interval1D::fromMinMax(-1, 1), + Interval1D::fromMinMax(-1, 1) + ), + Point2D::fromXY(0, 2), + false + ]; + + yield 'on border' => [ + Interval2D::fromXY( + Interval1D::fromMinMax(-1, 1), + Interval1D::fromMinMax(-1, 1) + ), + Point2D::fromXY(1, 1), + true + ]; + } + /** * @param Interval1D $x * @param Interval1D $y @@ -185,4 +245,13 @@ public function asString( ): void { self::assertEquals($expected, (string)$interval); } + + #[Test, DataProvider('containsTests')] + public function contains( + Interval2D $interval, + Point2D $point, + bool $expected + ): void { + self::assertEquals($expected, $interval->contains($point)); + } } \ No newline at end of file