diff options
| author | Joelle Maslak <jmaslak@antelope.net> | 2019-09-29 12:12:32 -0600 |
|---|---|---|
| committer | Joelle Maslak <jmaslak@antelope.net> | 2019-09-29 12:12:32 -0600 |
| commit | a5f1133e945c4b36b4760e11225aa586aadba6b8 (patch) | |
| tree | 8c8e417603068e59ac26c71389f81c35bb7bffa8 /challenge-027 | |
| parent | a7f35b2af2f11866e19421eb8220116d45ce33af (diff) | |
| download | perlweeklychallenge-club-a5f1133e945c4b36b4760e11225aa586aadba6b8.tar.gz perlweeklychallenge-club-a5f1133e945c4b36b4760e11225aa586aadba6b8.tar.bz2 perlweeklychallenge-club-a5f1133e945c4b36b4760e11225aa586aadba6b8.zip | |
Joelle's solution to 27.1 in Perl 5
Diffstat (limited to 'challenge-027')
| -rwxr-xr-x | challenge-027/joelle-maslak/perl5/ch-1.pl | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/challenge-027/joelle-maslak/perl5/ch-1.pl b/challenge-027/joelle-maslak/perl5/ch-1.pl new file mode 100755 index 0000000000..258a2eb79f --- /dev/null +++ b/challenge-027/joelle-maslak/perl5/ch-1.pl @@ -0,0 +1,210 @@ +#!/usr/bin/env perl +use v5.26; +use strict; +use warnings; +use utf8; # For unicode comments + +# Turn on method signatures +use feature 'signatures'; +no warnings 'experimental::signatures'; + +# This program finds the intersection of two lines. While the word +# "ends" is used, I'm instead assuming that these are true mathematical +# lines, not line segments, so the intersection point may not be between +# the two points that define the line. +# +# The first thing we need to do is to find the slope of each line. +# +# Edge case 1: If either line is not fully defined, we return an error. +# This happens if both points are the same. +# +# Edge case 2: A vertical line can't be defined by the standard y = A × x₁ + C +# equation. In this case we handle it differently, see below. We use ∞ +# (undef) for the slope in this case. +# +# To find the slope (of non-vertical lines), it's just rise / run, which is +# easy to calculate. For the constant, we need to solve this: +# y = Ax + C +# A given point will define x and y, while the slope defines A, so we just +# solve for C. THus: +# C = y - Ax; +# +# If we have two equations: y₁ = A₁ × x₁ + C₁ +# y₂ = A₂ × x₂ + C₂ +# +# We can find the intersection: +# 1) If slope is identical, and constants differ, NO INTERSECTION +# 2) If slope is identical, and constants are the same, this is the +# same line. +# 3) If the slope is infinite of either line, just solve the other +# equation to determine y for that value of x. +# 4) Otherwise, we're looking for the point where x₁=x₂ and y₁=y₂ +# Thus, we can rewrite the equations as: +# y = A₁ × x + C₁ +# y = A₂ × x + C₂ +# We can solve this system for x: +# A₁ × x + C₁ = A₂ × x + C₂ +# Rewritten: +# A₁ × x - A₂ × x = C₂ - C₁ +# Rewritten: +# (A₁ - A₂) × x = C₂ - C₁ +# x = (C₂ - C₁) ÷ (A₁ - A₂) +# Once we have x, we can solve for y by plugging into either of the +# original equations. +# + +package Point { + use Moose; + use feature 'signatures'; + no warnings 'experimental::signatures'; + + use Regexp::Common; + + has x => ( + is => 'rw', + isa => 'Num', + required => 1, + ); + + has y => ( + is => 'rw', + isa => 'Num', + required => 1, + ); + + sub from_string ( $self, $point_str ) { + $point_str =~ s/^ \( [.*] \) $/$1/sx; # Remove parens + + my (@parts) = split ',', $point_str; + if ( scalar( grep { /^ $RE{num}{real} $/sx } @parts ) != scalar(@parts) ) { + die("Invalid point specification"); + } + die("Invalid point specification") unless scalar(@parts) == 2; + + return $self->new( x => $parts[0], y => $parts[1] ); + } + + sub eqv ( $self, $point ) { + return ( ( $self->x == $point->x ) && ( $self->y == $point->y ) ); + } +} + +package Line { + use Moose; + use feature 'signatures'; + no warnings 'experimental::signatures'; + + has slope => ( + is => 'rw', + isa => 'Maybe[Num]', + required => 1, + ); + + has point => ( + is => 'rw', + isa => 'Point', + required => 1, + ); + + sub solve_for_x ( $self, $y ) { + # Vertical line exception + return $self->x if !defined $self->slope; + + # Horizontal line exception + if ( $self->slope == 0 ) { + die "Cannot solve for $y" if $self->point->y != $y; + } + + return ( $y - $self->y_offset ) / $self->slope; + } + + sub solve_for_y ( $self, $x ) { + # Horizontal line exception + return $self->point->y if $self->slope == 0; + + # Vertical line exception + if ( !defined $self->slope ) { + die "Cannot solve for $x" if $self->point->x != $x; + } + + # Lines between horizontal and vertical + return $self->slope * $x + $self->y_offset; + } + + sub y_offset($self) { + # Vertical line exception + return if !defined $self->slope; + + # Non-vertical lines + return $self->point->y - $self->slope * $self->point->x; + } + + sub intersection ( $self, $line ) { + die "Lines are the same" if $self->eqv($line); + if ( defined $self->slope and defined $line->slope ) { + die "Lines do not intersect" if $self->slope == $line->slope; + } + + # If either line is vertical + if ( !defined $self->slope ) { + return Point->new( x => $self->point->x, y => $line->solve_for_y( $self->point->x ) ); + } elsif ( !defined $line->slope ) { + return Point->new( x => $line->point->x, y => $self->solve_for_y( $line->point->x ) ); + } + + # We're finding a normal intersection + my $x = ( $line->y_offset - $self->y_offset ) / ( $self->slope - $line->slope ); + my $y = $self->solve_for_y($x); + + return Point->new( x => $x, y => $y ); + } + + sub eqv ( $self, $line ) { + # Are both vertical? + if ( ( !defined $self->slope ) and ( !defined $line->slope ) ) { + return $self->point->x == $line->point->x; + } + + # Only one is vertical + return if ( !defined $self->slope ) or ( !defined $line->slope ); + + # All other lines + return if $self->slope != $line->slope; + return ( $self->point->y == $line->solve_for_y( $self->point->x ) ); + } + + sub from_points ( $self, $point1, $point2 ) { + # Handle same point + die "Lines must be defined with two different points" if $point1->eqv($point2); + + # Handle vertical line exception + return $self->new( point => $point1, slope => undef ) if $point1->x == $point2->x; + + # Handle other lines. + my $slope = ( $point1->y - $point2->y ) / ( $point1->x - $point2->x ); + return $self->new( point => $point1, slope => $slope ); + } +} + +# point1 and point2 define a line. You can determine which line by the +# letter +sub MAIN ( $point1a, $point2a, $point1b, $point2b ) { + my $line1 = Line->from_points( Point->from_string($point1a), Point->from_string($point2a), ); + my $line2 = Line->from_points( Point->from_string($point1b), Point->from_string($point2b), ); + + if ( $line1->eqv($line2) ) { + say "The two lines are the same"; + } elsif ( ( !defined $line1->slope ) and ( !defined $line2->slope ) ) { + say "The two lines don't intersect"; + } elsif ( defined $line1->slope and defined $line2->slope and $line1->slope == $line2->slope ) { + say "The two lines don't intersect"; + } else { + my $intersection = $line1->intersection($line2); + say "The lines intersect at (" . $intersection->x . "," . $intersection->y . ")"; + } + + return; +} + +MAIN(@ARGV); + |
