Skip to content

Commit

Permalink
finishing up Interval2D
Browse files Browse the repository at this point in the history
  • Loading branch information
kellegous committed May 1, 2024
1 parent 3be89be commit 91e7fb3
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 9 deletions.
1 change: 1 addition & 0 deletions bin/algs4.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
new Commands\AllowList(),
new Commands\Average(),
new Commands\RandomSeq(),
new Commands\Interval2D(),
]);
$app->run();
92 changes: 92 additions & 0 deletions src/Commands/Interval2D.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

declare(strict_types=1);

namespace Kellegous\Algs4\Commands;

use Kellegous\Algs4\Counter;
use Kellegous\Algs4\Interval1D;
use Kellegous\Algs4\Point2D;
use Kellegous\Algs4\Random;
use Kellegous\Algs4\Stdio;
use Override;
use Random\Engine\Mt19937;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function Kellegous\Algs4\parse_float;
use function Kellegous\Algs4\parse_int;

#[AsCommand(
name: 'interval2d',
description: 'command associated with Interval2D'
)]
final class Interval2D extends Command
{

#[Override]
protected function configure(): void
{
$this->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;
}
}
69 changes: 61 additions & 8 deletions src/Interval2D.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,97 @@

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.
* <p>
* For additional documentation,
* see <a href="https://algs4.cs.princeton.edu/12oop">Section 1.2</a> of
* <i>Algorithms, 4th Edition</i> 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
): self {
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;
}
}
2 changes: 1 addition & 1 deletion src/Point2D.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

final readonly class Point2D
{
private function __construct(
public function __construct(
private float $x,
private float $y
) {
Expand Down
69 changes: 69 additions & 0 deletions tests/Interval2DTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,66 @@ public static function asStringTests(): iterable
];
}

/**
* @return iterable<string, array{Interval2D, Point2D, bool}>
*/
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
Expand Down Expand Up @@ -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));
}
}

0 comments on commit 91e7fb3

Please sign in to comment.