
use Math::Intersection::Circle::Line q(:all);
use Test::More tests=>77;
use warnings FATAL => q(all);
use strict;
use utf8;

#-------------------------------------------------------------------------------
# Useful values
#-------------------------------------------------------------------------------

my $f = sqrt(5);
my $h = sqrt(1/2);
my $t = sqrt(3);

#-------------------------------------------------------------------------------
# Tests
#-------------------------------------------------------------------------------

# Useful functions
ok $Math::Intersection::Circle::Line::near;
 {local $Math::Intersection::Circle::Line::near = 10;
	ok near(1);
 }
ok near(0);
ok near(1,1);
ok 5 == vectorLength(3, 4);
ok "@{[midPoint(-2, -1, 4, 3)]}" eq "1 1";
ok "@{[rotate90CW(-1,  1)]}" eq "1 1";
ok "@{[rotate90CCW(1, -1)]}" eq "1 1";
ok "@{[&rotate90CCW(rotate90CW(11,12))]}" eq "11 12";
ok "@{[rotate90AroundMidPoint(-1, -1, 1, 1)]}" eq "-1 1 1 -1";

# Circle/Circle
# Two identical
ok (((eval {intersectionCircles{ok !@_} 1, 1, 1,   1, 1, 1} || $@) =~ /Duplicate circles/));

# No intersection
intersectionCircles{ok !@_} 1, 2, 1,   1,  2, 2;
intersectionCircles{ok !@_} 1, 1, 1,  -1, -1, 1;
intersectionCircles{ok !@_} 0, 0, 1,   0,  0, 2;
# Centre of one outside other
intersectionCircles{ok &near2(@_, 0, 0)} -1, 0, 1,  1,  0, 1;
intersectionCircles{ok &near2(@_, 0, 0)}  0, 1, 1,  0, -1, 1;
intersectionCircles{ok &near4(@_, 0, +$t, 0, -$t)} -1, 0, 2,  1,  0, 2;
intersectionCircles{ok &near4(@_, 0, -$t, 0, +$t)} +1, 0, 2, -1,  0, 2;
intersectionCircles{ok &near4(@_, 0, 2, 2, 0)}      0, 0, 2,  2,  2, 2;
intersectionCircles{ok &near4(@_, 2, 0, 0, 2)}      2, 2, 2,  0,  0, 2;
# Centre of one inside other
intersectionCircles{ok &near2(@_, 0, 0)} 0, 2, 2, 0, 1, 1;                      # Low y
intersectionCircles{ok &near2(@_, 0, 4)} 0, 2, 2, 0, 3, 1;                      # High y
intersectionCircles{ok &near2(@_, 0, 0)} 2, 0, 2, 1, 0, 1;                      # Low x
intersectionCircles{ok &near2(@_, 4, 0)} 2, 0, 2, 3, 0, 1;                      # High x

intersectionCircles{ok &near4(@_, 1, 0, 0, 1)} 2, 2, $f,  0,  0, 1;             # Outside
intersectionCircles{ok &near4(@_, 1, 0, 0, 1)} 2, 2, $f,  1,  1, 1;             # Inside
intersectionCircles{ok &near4(@_, 0, 3, 1, 4)} 2, 2, $f,  1,  3, 1;             # Inside
intersectionCircles{ok &near4(@_, 3, 4, 4, 3)} 2, 2, $f,  3,  3, 1;             # Inside
intersectionCircles{ok &near4(@_, 4, 1, 3, 0)} 2, 2, $f,  3,  1, 1;             # Inside

# Line, Point

intersectionLinePoint{ok &near2(@_, 0, 0)} -9,  -9,  1,  1,  0,  0;             # On top

intersectionLinePoint{ok &near2(@_, 2, 1)} -9,  1,  9,  1,  2,  2;              # Inside
intersectionLinePoint{ok &near2(@_, 0, 0)} -9, -9,  1,  1,  1, -1;
intersectionLinePoint{ok &near2(@_, 0, 0)} -1,  1,  9, -9,  1,  1;

intersectionLinePoint{ok &near2(@_, 2, 1)} -9,  1, -8,  1,  2,  2;              # Outside
intersectionLinePoint{ok &near2(@_, 0, 0)} -9, -9, -8, -8,  1, -1;
intersectionLinePoint{ok &near2(@_, 0, 0)} -1,  1, -9,  9,  1,  1;

intersectionLinePoint{ok &near2(@_, -9, 0)} -1, 0, 1, 0, -9, 0;

# Lines
ok (((eval {intersectionLines{ok !@_} -1, 0,  0, 0,  -9, 0, -8, 0} || $@) =~ /Collinear/));
ok intersectionLines{ok !@_} -1, 1,  1, 1,  0, 0, 1, 0;                         # Parallel
ok intersectionLines{ok !@_} 0, 0, 2, 1,  0, 1, 2, 2;

intersectionLines{ok &near2(@_, 0, 0)} -1,  1,  1, -1,   -1, -1,  1,  1;
intersectionLines{ok &near2(@_, 0, 0)} -1, -1,  1,  1,   -1,  1,  1, -1;

intersectionLines{ok &near2(@_, 1, 1)}  0,  0,  2,  2,   0,  2,  2, 0;
intersectionLines{ok &near2(@_, 2, 2)}  1,  1,  3,  3,   1,  3,  3, 1;

intersectionLines{ok &near2(@_, 1, 1/2)}  0,  0,  2,  1,   0,  1,  2, 0;
intersectionLines{ok &near2(@_, 1, 1)  }  0,  0,  2,  2,   0,  2,  2, 0;
intersectionLines{ok &near2(@_, 1.5, 3/4)}  0,  0,  2,  1,   1,  1,  3, 0;
intersectionLines{ok &near2(@_, 1.5, 3/4)}  2,  1,  0,  0,   3,  0,  1, 1;
intersectionLines{ok &near2(@_, 1.5, 3/4)}  0,  0,  2,  1,   1,  1,  3, 0;
intersectionLines{ok &near2(@_, 2/3, 1/3)}  0,  0,  2,  1,   0,  1,  1, 0;

# Circle/Line
ok ((eval {intersectionCircleLine {1} 0, 0, 0,  0, 1,  0, 0} || $@) =~ /Radius is too small!/);
ok ((eval {intersectionCircleLine {1} 0, 0, 1,  0, 0,  0, 0} || $@) =~ /Points on line are too close!/);
# On top
intersectionCircleLine {ok &near4(@_,  0,   1,  0,  -1)}          0,   0,   1,    0,  0,   0,  1;
intersectionCircleLine {ok &near4(@_,  1,   0, -1,   0)}          0,   0,   1,    0,  0,   1,  0;

intersectionCircleLine {ok &near2(@_,  0,   1)}                   0,   0,   1,   -1,  1,   1,  1;
intersectionCircleLine {ok &near4(@_, $h, -$h,   -$h,  $h  )}     0,   0,   1,   -1,  1,   1, -1;
intersectionCircleLine {ok &near4(@_,  2,   1,     0,   1  )}     1,   1,   1,   -1,  1,   1,  1;
intersectionCircleLine {ok &near4(@_,  1,   2,     1,   0  )}     1,   1,   1,    1,  0,   1,  1;
intersectionCircleLine {ok &near4(@_, -0.5, 0,     0.5, 0)}       0, -$t/2, 1,   -1,  0,   1,  0;
intersectionCircleLine {ok &near4(@_,  0,   0.5,   0,  -0.5)} -$t/2,   0,   1,    0, -1,   0,  1;
intersectionCircleLine {ok &near4(@_, 18,   0,     0,   0  )}     9,   0,   9,    0,  0,   1,  0;
intersectionCircleLine {ok &near4(@_,  0,  18,     0,   0  )}     0,   9,   9,    0,  0,   0,  1;
intersectionCircleLine {ok !@_}                                   0,   0,   1,   -2,  2,   2,  2;

# A line across a circle is never longer than a diameter

if (1)                                                                          # Random circle and random line
 {my ($x, $y, $r, $𝘅, $𝘆, $𝕩, $𝕪) = map {rand()} 1..7;
  intersectionCircleLine                                                        # Find intersection of a circle and a line
   {return ok 1 unless @_ == 4;                                                 # Ignore line unless it crosses circle
    ok &vectorLength(@_) <= 2*$r;                                               # Length if line segment is less than or equal to that of a diameter
	 } $x, $y, $r, $𝘅, $𝘆, $𝕩, $𝕪;                                                # Circle and line to be intersected
 }

# The length of a side of a hexagon is the radius of a circle inscribed through
# its vertices

if (1)
 {my ($x, $y, $r) = map {rand()} 1..3;                                          # Random circle
  my @p = intersectionCircles {@_} $x, $y, $r, $x+$r, $y, $r;                   # First step of one radius
	my @𝗽 = intersectionCircles {@_} $x, $y, $r, $p[0], $p[1], $r;                # Second step of one radius
	my @q = !&near($x+$r, $y, @𝗽[0,1]) ? @𝗽[0,1] : @𝗽[2,3];                       # away from start point
	my @𝗾 = intersectionCircles {@_} $x, $y, $r, $q[0], $q[1], $r;                # Third step of one radius
  ok &near2(@𝗾[0,1], $x-$r, $y) or                                              # Brings us to a point
     &near2(@𝗾[2,3], $x-$r, $y);                                                # opposite to the start point
 }

# Circle through three points chosen at random has the same centre regardless of
# the pairing of the points

sub circleThrough3
 {my ($x, $y, $𝘅, $𝘆, $𝕩, $𝕪) = @_;                                             # Three points
	&intersectionLines
	 (sub                                                                         # Intersection of bisectors is the centre of the circle
	   {my @r =(&vectorLength(@_, $x, $y),                                        # Radii from centre of circle to each point
	            &vectorLength(@_, $𝘅, $𝘆),
	            &vectorLength(@_, $𝕩, $𝕪));
	    ok &near(@r[0,1]);                                                        # Check radii are equal
	    ok &near(@r[1,2]);
      @_                                                                        # Return centre
		 }, rotate90AroundMidPoint($x, $y, $𝘅, $𝘆),                                 # Bisectors between pairs of points
		    rotate90AroundMidPoint($𝕩, $𝕪, $𝘅, $𝘆));
 }

if (1)
 {my (@points) = map {rand()} 1..6;                                             # Three points chosen at random
  ok &near2(circleThrough3(@points), circleThrough3(@points[2..5, 0..1]));      # Circle has same centre regardless
  ok &near2(circleThrough3(@points), circleThrough3(@points[4..5, 0..3]));      # of the pairing of the points
 }








