aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2021-02-28 23:34:47 +1000
committerPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2021-02-28 23:34:47 +1000
commit94c912a2a48371980cbc6986fe81840e987153f5 (patch)
tree2c093bae033f3ca36ade6de8700c9e5ab724040d
parent37247271a1d6f0cefa14b7847b3fecf4b1dd7894 (diff)
downloadperlweeklychallenge-club-94c912a2a48371980cbc6986fe81840e987153f5.tar.gz
perlweeklychallenge-club-94c912a2a48371980cbc6986fe81840e987153f5.tar.bz2
perlweeklychallenge-club-94c912a2a48371980cbc6986fe81840e987153f5.zip
Perl & Raku solutions to Tasks 1 & 2 of the Perl Weekly Challenge #101
On branch branch-for-challenge-101 Changes to be committed: new file: challenge-101/athanasius/perl/ch-1.pl new file: challenge-101/athanasius/perl/ch-2.pl new file: challenge-101/athanasius/raku/ch-1.raku new file: challenge-101/athanasius/raku/ch-2.raku
-rw-r--r--challenge-101/athanasius/perl/ch-1.pl297
-rw-r--r--challenge-101/athanasius/perl/ch-2.pl205
-rw-r--r--challenge-101/athanasius/raku/ch-1.raku305
-rw-r--r--challenge-101/athanasius/raku/ch-2.raku182
4 files changed, 989 insertions, 0 deletions
diff --git a/challenge-101/athanasius/perl/ch-1.pl b/challenge-101/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..464e868175
--- /dev/null
+++ b/challenge-101/athanasius/perl/ch-1.pl
@@ -0,0 +1,297 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 101
+=========================
+
+Task #1
+-------
+*Pack a Spiral*
+
+Submitted by: Stuart Little
+
+You are given an array @A of items (integers say, but they can be anything).
+
+Your task is to pack that array into an MxN matrix spirally counterclockwise,
+as tightly as possible.
+
+ 'Tightly' means the absolute value |M-N| of the difference has to be as
+ small as possible.
+
+Example 1:
+
+ Input: @A = (1,2,3,4)
+
+ Output:
+
+ 4 3
+ 1 2
+
+ Since the given array is already a 1x4 matrix on its own, but that's not as
+ tight as possible. Instead, you'd spiral it counterclockwise into
+
+ 4 3
+ 1 2
+
+Example 2:
+
+ Input: @A = (1..6)
+
+ Output:
+
+ 6 5 4
+ 1 2 3
+
+ or
+
+ 5 4
+ 6 3
+ 1 2
+
+ Either will do as an answer, because they're equally tight.
+
+Example 3:
+
+ Input: @A = (1..12)
+
+ Output:
+
+ 9 8 7 6
+ 10 11 12 5
+ 1 2 3 4
+
+ or
+
+ 8 7 6
+ 9 12 5
+ 10 11 4
+ 1 2 3
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+use strict;
+use warnings;
+use Const::Fast;
+use enum qw( RIGHT UP LEFT DOWN );
+use Regexp::Common qw( number );
+
+const my $USAGE =>
+"Usage:
+ perl $0 [<A> ...]
+
+ [<A> ...] A non-empty array of integers and/or integer ranges: I..J\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 101, Task #1: Pack a Spiral (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ # (1) Populate and validate the array
+
+ my @A = get_array();
+
+ printf "Input: \@A = (%s)\n", join ',', @A;
+
+ # (2) Find M <= N such that M * N == @A.elems and |M - N| is a minimum
+
+ my ($M, $N) = find_dimensions( \@A );
+
+ # (3) Create and populate the M x N matrix
+
+ my $matrix = pack_matrix( \@A, $M, $N );
+
+ # (4) Print the matrix
+
+ print "\nOutput:\n\n";
+
+ print_matrix( $matrix );
+}
+
+#------------------------------------------------------------------------------
+sub get_array
+#------------------------------------------------------------------------------
+{
+ scalar @ARGV > 0 or error( 'Empty array' );
+
+ my @A;
+
+ for my $item (@ARGV)
+ {
+ if (my ($lhs, $rhs) = $item =~ / ^ (.+) \.\. (.+) $ /x)
+ {
+ for ($lhs, $rhs)
+ {
+ / ^ $RE{num}{int} $ /x
+ or error( qq[Item "$_" is not an integer] );
+ }
+
+ push @A, $lhs .. $rhs;
+ }
+ else
+ {
+ $item =~ / ^ $RE{num}{int} $ /x
+ or error( qq[Item "$item" is not an integer] );
+
+ push @A, $item;
+ }
+ }
+
+ return @A;
+}
+
+#------------------------------------------------------------------------------
+sub find_dimensions
+#------------------------------------------------------------------------------
+{
+ my ($A) = @_;
+ my ($M, $N) = (1, scalar @$A);
+ my $root = int sqrt $N;
+
+ if ($root * $root == $N)
+ {
+ ($M, $N) = ($root, $root);
+ }
+ else
+ {
+ for my $div1 (reverse 2 .. $root)
+ {
+ my $div2 = int($N / $div1);
+
+ if ($div1 * $div2 == $N)
+ {
+ ($M, $N) = ($div1, $div2);
+ last;
+ }
+ }
+ }
+
+ return $M, $N;
+}
+
+#------------------------------------------------------------------------------
+sub pack_matrix
+#------------------------------------------------------------------------------
+{
+ my ($A, $M, $N) = @_;
+ my $max_row = $M - 1;
+ my $min_row = 0;
+ my $max_col = $N - 1;
+ my $min_col = 0;
+ my $row = $max_row;
+ my $col = -1;
+ my $dir = RIGHT;
+ my @matrix;
+
+ for my $item (@$A)
+ {
+ if ($dir == RIGHT)
+ {
+ if (++$col > $max_col)
+ {
+ $col = $max_col;
+ $dir = UP;
+ --$row;
+ --$max_row;
+ }
+ }
+ elsif ($dir == UP)
+ {
+ if (--$row < $min_row)
+ {
+ $row = $min_row;
+ $dir = LEFT;
+ --$col;
+ --$max_col;
+ }
+ }
+ elsif ($dir == LEFT)
+ {
+ if (--$col < $min_col)
+ {
+ $col = $min_col;
+ $dir = DOWN;
+ ++$row;
+ ++$min_row;
+ }
+ }
+ else # DOWN
+ {
+ if (++$row > $max_row)
+ {
+ $row = $max_row;
+ $dir = RIGHT;
+ ++$col;
+ ++$min_col;
+ }
+ }
+
+ $matrix[ $row ][ $col ] = $item;
+ }
+
+ return \@matrix;
+}
+
+#------------------------------------------------------------------------------
+sub print_matrix
+#------------------------------------------------------------------------------
+{
+ my ($matrix) = @_;
+ my $max_row = $#$matrix;
+ my $max_col = $#{ $matrix->[ 0 ] };
+ my @widths;
+
+ # (1) Calculate maximum column widths
+
+ for my $col (0 .. $max_col)
+ {
+ my $max = length $matrix->[ 0 ][ $col ];
+
+ for my $row (1 .. $max_row)
+ {
+ my $len = length $matrix->[ $row ][ $col ];
+
+ $max = $len if $len > $max;
+ }
+
+ push @widths, $max;
+ }
+
+ # (2) Print the matrix
+
+ for my $row (0 .. $max_row)
+ {
+ print ' ' x 5;
+
+ for my $col (0 .. $max_col)
+ {
+ printf " %*d", $widths[ $col ], $matrix->[ $row ][ $col ];
+ }
+
+ print "\n";
+ }
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+###############################################################################
diff --git a/challenge-101/athanasius/perl/ch-2.pl b/challenge-101/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..1497525933
--- /dev/null
+++ b/challenge-101/athanasius/perl/ch-2.pl
@@ -0,0 +1,205 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 101
+=========================
+
+Task #2
+-------
+*Origin-containing Triangle*
+
+Submitted by: Stuart Little
+
+You are given three points in the plane, as a list of six co-ordinates:
+A=(x1,y1), B=(x2,y2) and C=(x3,y3).
+
+Write a script to find out if the triangle formed by the given three co-
+ordinates contain origin (0,0).
+
+Print 1 if found otherwise 0.
+
+Example 1:
+
+ Input: A=(0,1), B=(1,0) and C=(2,2)
+
+ Output: 0 because that triangle does not contain (0,0).
+
+Example 2:
+
+ Input: A=(1,1), B=(-1,1) and C=(0,-3)
+
+ Output: 1 because that triangle contains (0,0) in its interior.
+
+Example 3:
+
+ Input: A=(0,1), B=(2,0) and C=(-6,0)
+
+ Output: 1 because (0,0) is on the edge connecting B and C.
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+The algorithm in subroutine collinear() is adapted from:
+
+ Eric W. Weisstein, "Collinear". MathWorld--A Wolfram Web Resource:
+ https://mathworld.wolfram.com/Collinear.html
+
+The algorithm in subroutine point_in_triangle() is adapted from:
+
+ "1st method: barycentric coordinate system"
+ in Cédric Jules, "Accurate point in triangle test". Totologic blog:
+ http://totologic.blogspot.com/2014/01/accurate-point-in-triangle-test.html
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use Regexp::Common qw( number );
+
+const my $VERBOSE => 1;
+const my $USAGE =>
+"Usage:
+ perl $0 [<coords> ...]
+
+ [<coords> ...] X-Y co-ordinates of the 3 vertices of a triangle\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 101, Task #2: Origin-containing Triangle (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my ($A, $B, $C) = parse_command_line();
+
+ print 'Input: A=(', $A->[ 0 ], ',', $A->[ 1 ],
+ '), B=(', $B->[ 0 ], ',', $B->[ 1 ],
+ ') and C=(', $C->[ 0 ], ',', $C->[ 1 ], ")\n\n";
+
+ my ($result, $explanation) = contains_origin( $A, $B, $C );
+
+ printf "Output: $result%s\n", $VERBOSE ? ' because ' . $explanation : '';
+}
+
+#------------------------------------------------------------------------------
+sub contains_origin
+#------------------------------------------------------------------------------
+{
+ my ($A, $B, $C) = @_;
+
+ for my $point ($A, $B, $C)
+ {
+ return (1, '(0,0) is a vertex of the triangle')
+ if $point->[ 0 ] == 0 && $point->[ 1 ] == 0;
+ }
+
+ my $origin = [ 0, 0 ];
+
+ return (1, '(0,0) is on the edge connecting A and B')
+ if collinear( $A, $B, $origin );
+
+ return (1, '(0,0) is on the edge connecting A and C')
+ if collinear( $A, $C, $origin );
+
+ return (1, '(0,0) is on the edge connecting B and C')
+ if collinear( $B, $C, $origin );
+
+ return (1, 'that triangle contains (0,0) in its interior')
+ if point_in_triangle( $A, $B, $C );
+
+ return (0, 'that triangle does not contain (0,0)');
+}
+
+#------------------------------------------------------------------------------
+sub point_in_triangle
+#------------------------------------------------------------------------------
+{
+ my ($A, $B, $C) = @_;
+
+ my $den = ($B->[1] - $C->[1]) * ($A->[0] - $C->[0]) +
+ ($C->[0] - $B->[0]) * ($A->[1] - $C->[1]);
+
+ my $x = (($B->[1] - $C->[1]) * -$C->[0] +
+ ($C->[0] - $B->[0]) * -$C->[1]) / $den;
+
+ my $y = (($C->[1] - $A->[1]) * -$C->[0] +
+ ($A->[0] - $C->[0]) * -$C->[1]) / $den;
+
+ my $z = 1 - $x - $y;
+
+ return 0 <= $x <= 1 &&
+ 0 <= $y <= 1 &&
+ 0 <= $z <= 1;
+}
+
+#------------------------------------------------------------------------------
+sub parse_command_line
+#------------------------------------------------------------------------------
+{
+ my $args = scalar @ARGV;
+ $args == 6 or error( "Expected 6 command-line arguments, found $args" );
+
+ for (@ARGV)
+ {
+ / ^ $RE{num}{real} $ /x or error( qq[Item "$_" is not a real number] );
+ }
+
+ my @A = @ARGV[ 0, 1 ];
+ my @B = @ARGV[ 2, 3 ];
+ my @C = @ARGV[ 4, 5 ];
+
+ # The 3 points defining a triangle must be distinct ...
+
+ error( 'Points A and B are identical' )
+ if $A[ 0 ] == $B[ 0 ] && $A[ 1 ] == $B[ 1 ];
+
+ error( 'Points A and C are identical' )
+ if $A[ 0 ] == $C[ 0 ] && $A[ 1 ] == $C[ 1 ];
+
+ error( 'Points B and C are identical' )
+ if $B[ 0 ] == $C[ 0 ] && $B[ 1 ] == $C[ 1 ];
+
+ # ... and not collinear
+
+ error( 'Points A, B, and C are collinear' ) if collinear( \@A, \@B, \@C );
+
+ return (\@A, \@B, \@C);
+}
+
+#------------------------------------------------------------------------------
+sub collinear
+#------------------------------------------------------------------------------
+{
+ my ($A, $B, $C) = @_;
+
+ return $A->[0] * ($B->[1] - $C->[1]) +
+ $B->[0] * ($C->[1] - $A->[1]) +
+ $C->[0] * ($A->[1] - $B->[1]) == 0;
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+###############################################################################
diff --git a/challenge-101/athanasius/raku/ch-1.raku b/challenge-101/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..8722464417
--- /dev/null
+++ b/challenge-101/athanasius/raku/ch-1.raku
@@ -0,0 +1,305 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 101
+=========================
+
+Task #1
+-------
+*Pack a Spiral*
+
+Submitted by: Stuart Little
+
+You are given an array @A of items (integers say, but they can be anything).
+
+Your task is to pack that array into an MxN matrix spirally counterclockwise,
+as tightly as possible.
+
+ 'Tightly' means the absolute value |M-N| of the difference has to be as
+ small as possible.
+
+Example 1:
+
+ Input: @A = (1,2,3,4)
+
+ Output:
+
+ 4 3
+ 1 2
+
+ Since the given array is already a 1x4 matrix on its own, but that's not as
+ tight as possible. Instead, you'd spiral it counterclockwise into
+
+ 4 3
+ 1 2
+
+Example 2:
+
+ Input: @A = (1..6)
+
+ Output:
+
+ 6 5 4
+ 1 2 3
+
+ or
+
+ 5 4
+ 6 3
+ 1 2
+
+ Either will do as an answer, because they're equally tight.
+
+Example 3:
+
+ Input: @A = (1..12)
+
+ Output:
+
+ 9 8 7 6
+ 10 11 12 5
+ 1 2 3 4
+
+ or
+
+ 8 7 6
+ 9 12 5
+ 10 11 4
+ 1 2 3
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 101, Task #1: Pack a Spiral (Raku)\n".put;
+}
+
+#==============================================================================
+sub MAIN
+(
+ *@A where { @A.elems > 0 } #= A non-empty array of integers and/or
+ #= integer ranges: I..J
+)
+#==============================================================================
+{
+ # (1) Populate and validate the array
+
+ my Int @a = get-array( @A );
+
+ "Input: \@A = (%s)\n".printf: @a.join: ',';
+
+ # (2) Find M <= N such that M * N == @A.elems and |M - N| is a minimum
+
+ my UInt ($M, $N) = find-dimensions( @a );
+
+ # (3) Create and populate the M x N matrix
+
+ my Array[Int] @matrix = pack-matrix( @a, $M, $N );
+
+ # (4) Print the matrix
+
+ "\nOutput:\n".put;
+
+ print-matrix( @matrix );
+}
+
+#------------------------------------------------------------------------------
+sub get-array( Array:D[IntStr:D] $A --> Array:D[Int:D] )
+#------------------------------------------------------------------------------
+{
+ my Int @a;
+
+ for @$A -> Str $item
+ {
+ if $item ~~ / ^ (.+) \.\. (.+) $ /
+ {
+ $0.Int ~~ Int:D or error( qq[Item "$0" is not an integer] );
+ $1.Int ~~ Int:D or error( qq[Item "$1" is not an integer] );
+ @a.append: $0 .. $1;
+ }
+ else
+ {
+ $item ~~ Int:D or error( qq[Item "$item" is not an integer] );
+ @a.push: $item.Int;
+ }
+ }
+
+ return @a;
+}
+
+#------------------------------------------------------------------------------
+sub find-dimensions( Array:D[Int:D] $A --> List:D[UInt:D, UInt:D] )
+#------------------------------------------------------------------------------
+{
+ my UInt ($M, $N) = 1, $A.elems;
+
+ unless $N.is-prime
+ {
+ my UInt $root = $N.sqrt.Int;
+
+ if $root * $root == $N
+ {
+ ($M, $N) = $root, $root;
+ }
+ else
+ {
+ for (2 .. $root).reverse -> UInt $div1
+ {
+ my UInt $div2 = ($N / $div1).Int;
+
+ if $div1 * $div2 == $N
+ {
+ ($M, $N) = $div1, $div2;
+ last;
+ }
+ }
+ }
+ }
+
+ return $M, $N;
+}
+
+#------------------------------------------------------------------------------
+sub pack-matrix
+(
+ Array:D[Int:D] $A,
+ UInt:D $M,
+ UInt:D $N
+--> Array:D[Array:D[Int:D]]
+)
+#------------------------------------------------------------------------------
+{
+ enum DIR < RIGHT UP LEFT DOWN >;
+
+ my Array[Int] @matrix = Array[Int].new xx $M;
+ my UInt $max-row = $M - 1;
+ my UInt $min-row = 0;
+ my UInt $max-col = $N - 1;
+ my UInt $min-col = 0;
+ my Int $row = $max-row;
+ my Int $col = -1;
+ my DIR $dir = RIGHT;
+
+ for @$A -> Int $item
+ {
+ given $dir
+ {
+ when RIGHT
+ {
+ if ++$col > $max-col
+ {
+ $col = $max-col;
+ $dir = UP;
+ --$row;
+ --$max-row;
+ }
+ }
+
+ when UP
+ {
+ if --$row < $min-row
+ {
+ $row = $min-row;
+ $dir = LEFT;
+ --$col;
+ --$max-col;
+ }
+ }
+
+ when LEFT
+ {
+ if --$col < $min-col
+ {
+ $col = $min-col;
+ $dir = DOWN;
+ ++$row;
+ ++$min-row;
+ }
+ }
+
+ when DOWN
+ {
+ if ++$row > $max-row
+ {
+ $row = $max-row;
+ $dir = RIGHT;
+ ++$col;
+ ++$min-col;
+ }
+ }
+ }
+
+ @matrix[ $row; $col ] = $item;
+ }
+
+ return @matrix;
+}
+
+#------------------------------------------------------------------------------
+sub print-matrix( Array:D[Array:D[Int:D]] $matrix )
+#------------------------------------------------------------------------------
+{
+ my UInt @widths;
+ my UInt $max-row = $matrix.end;
+ my UInt $max-col = $matrix[ 0 ].end;
+
+ # (1) Calculate maximum column widths
+
+ for 0 .. $max-col -> UInt $col
+ {
+ my UInt $max = $matrix[ 0; $col ].chars;
+
+ for 1 .. $max-row -> UInt $row
+ {
+ $max = ( $max, $matrix[ $row; $col ].chars ).max;
+ }
+
+ @widths.push: $max;
+ }
+
+ # (2) Print the matrix
+
+ for 0 .. $max-row -> UInt $row
+ {
+ (' ' x 5).print;
+
+ for 0 .. $max-col -> UInt $col
+ {
+ " %*d".printf: @widths[ $col ], $matrix[ $row; $col ];
+ }
+
+ ''.put;
+ }
+}
+
+#------------------------------------------------------------------------------
+sub error( Str:D $message )
+#------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit;
+}
+
+#------------------------------------------------------------------------------
+sub USAGE()
+#------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/;
+ $usage.put;
+}
+
+##############################################################################
diff --git a/challenge-101/athanasius/raku/ch-2.raku b/challenge-101/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..a49cf7b631
--- /dev/null
+++ b/challenge-101/athanasius/raku/ch-2.raku
@@ -0,0 +1,182 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 101
+=========================
+
+Task #2
+-------
+*Origin-containing Triangle*
+
+Submitted by: Stuart Little
+
+You are given three points in the plane, as a list of six co-ordinates:
+A=(x1,y1), B=(x2,y2) and C=(x3,y3).
+
+Write a script to find out if the triangle formed by the given three co-
+ordinates contain origin (0,0).
+
+Print 1 if found otherwise 0.
+
+Example 1:
+
+ Input: A=(0,1), B=(1,0) and C=(2,2)
+
+ Output: 0 because that triangle does not contain (0,0).
+
+Example 2:
+
+ Input: A=(1,1), B=(-1,1) and C=(0,-3)
+
+ Output: 1 because that triangle contains (0,0) in its interior.
+
+Example 3:
+
+ Input: A=(0,1), B=(2,0) and C=(-6,0)
+
+ Output: 1 because (0,0) is on the edge connecting B and C.
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=begin comment
+
+The algorithm in subroutine collinear() is adapted from:
+
+ Eric W. Weisstein, "Collinear". MathWorld--A Wolfram Web Resource:
+ https://mathworld.wolfram.com/Collinear.html
+
+The algorithm in subroutine point-in-triangle() is adapted from:
+
+ "1st method: barycentric coordinate system"
+ in Cédric Jules, "Accurate point in triangle test". Totologic blog:
+ http://totologic.blogspot.com/2014/01/accurate-point-in-triangle-test.html
+
+=end comment
+#==============================================================================
+
+my Bool constant $VERBOSE = True;
+
+subset Point of List where (Real, Real);
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 101, Task #2: Origin-containing Triangle (Raku)\n".put;
+}
+
+#==============================================================================
+sub MAIN
+(
+ #| X-Y co-ordinates of the 3 vertices of a triangle
+ *@coords where { @coords.elems == 6 && .all ~~ Real:D }
+)
+#==============================================================================
+{
+ my Point $A = [ @coords[ 0 ].Real, @coords[ 1 ].Real ];
+ my Point $B = [ @coords[ 2 ].Real, @coords[ 3 ].Real ];
+ my Point $C = [ @coords[ 4 ].Real, @coords[ 5 ].Real ];
+
+ # The 3 points defining a triangle must be distinct, and not collinear
+
+ error( 'Identical points' ) if $A eq $B || $A eq $C || $B eq $C;
+ error( 'Collinear points' ) if collinear( $A, $B, $C );
+
+ "Input: A=($A[0],$A[1]), B=($B[0],$B[1]) and C=($C[0],$C[1])\n".put;
+
+ my (Bool $result, Str $explanation) = contains-origin( $A, $B, $C );
+
+ "Output: %d%s\n".printf: $result ?? 1 !! 0,
+ $VERBOSE ?? ' because ' ~ $explanation !! '';
+}
+
+#------------------------------------------------------------------------------
+sub contains-origin
+(
+ Point:D $A,
+ Point:D $B,
+ Point:D $C,
+--> List:D[Bool:D, Str:D]
+)
+#------------------------------------------------------------------------------
+{
+ for $A, $B, $C -> Point $point
+ {
+ return True, '(0,0) is a vertex of the triangle'
+ if $point[0] == 0 && $point[1] == 0;
+ }
+
+ my Point $origin = [ 0, 0 ];
+
+ return True, '(0,0) is on the edge connecting A and B'
+ if collinear( $A, $B, $origin );
+
+ return True, '(0,0) is on the edge connecting A and C'
+ if collinear( $A, $C, $origin );
+
+ return True, '(0,0) is on the edge connecting B and C'
+ if collinear( $B, $C, $origin );
+
+ return True, 'that triangle contains (0,0) in its interior'
+ if point-in-triangle( $A, $B, $C );
+
+ return False, 'that triangle does not contain (0,0)';
+}
+
+#------------------------------------------------------------------------------
+sub point-in-triangle( Point:D $A, Point:D $B, Point:D $C --> Bool:D )
+#------------------------------------------------------------------------------
+{
+ my Real $d = ($B[1] - $C[1]) * ($A[0] - $C[0]) +
+ ($C[0] - $B[0]) * ($A[1] - $C[1]);
+
+ my Real $x = (($B[1] - $C[1]) * -$C[0] + ($C[0] - $B[0]) * -$C[1]) / $d;
+
+ my Real $y = (($C[1] - $A[1]) * -$C[0] + ($A[0] - $C[0]) * -$C[1]) / $d;
+
+ my Real $z = 1 - $x - $y;
+
+ return 0 <= $x <= 1 &&
+ 0 <= $y <= 1 &&
+ 0 <= $z <= 1;
+}
+
+#------------------------------------------------------------------------------
+sub collinear( Point:D $A, Point:D $B, Point:D $C --> Bool:D )
+#------------------------------------------------------------------------------
+{
+ return $A[0] * ($B[1] - $C[1]) +
+ $B[0] * ($C[1] - $A[1]) +
+ $C[0] * ($A[1] - $B[1]) == 0;
+}
+
+#------------------------------------------------------------------------------
+sub error( Str:D $message )
+#------------------------------------------------------------------------------
+{
+ "ERROR: $message".put;
+
+ USAGE();
+
+ exit;
+}
+
+#------------------------------------------------------------------------------
+sub USAGE()
+#------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/;
+ $usage.put;
+}
+
+##############################################################################