aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--challenge-165/jo-37/perl/ch-2.in6
-rwxr-xr-xchallenge-165/jo-37/perl/ch-2.pl78
2 files changed, 84 insertions, 0 deletions
diff --git a/challenge-165/jo-37/perl/ch-2.in b/challenge-165/jo-37/perl/ch-2.in
new file mode 100644
index 0000000000..9e5ecc346a
--- /dev/null
+++ b/challenge-165/jo-37/perl/ch-2.in
@@ -0,0 +1,6 @@
+333,129 39,189 140,156 292,134 393,52 160,166 362,122 13,193
+341,104 320,113 109,177 203,152 343,100 225,110 23,186 282,102
+284,98 205,133 297,114 292,126 339,112 327,79 253,136 61,169
+128,176 346,72 316,103 124,162 65,181 159,137 212,116 337,86
+215,136 153,137 390,104 100,180 76,188 77,181 69,195 92,186
+275,96 250,147 34,174 213,134 186,129 189,154 361,82 363,89
diff --git a/challenge-165/jo-37/perl/ch-2.pl b/challenge-165/jo-37/perl/ch-2.pl
new file mode 100755
index 0000000000..23282b39c7
--- /dev/null
+++ b/challenge-165/jo-37/perl/ch-2.pl
@@ -0,0 +1,78 @@
+#!/usr/bin/perl -s
+
+use v5.16;
+use warnings;
+use PDL;
+use PDL::Slatec;
+use experimental 'signatures';
+
+our ($norun, $svg, $help);
+
+die <<EOS if $help;
+usage: $0 [-help] [-svg] [FILE]
+
+-help
+ Print this help.
+
+-svg
+ Print an SVG containing the points and the regression line on STDOUT.
+
+FILE
+ Name of a file holding (x, y) pairs. The values of a single pair
+ shall be separated by comma and pairs by whitespace.
+
+ Call "./ch-2.pl ch-2.in" to solve task 2. (Output: regression line
+ endpoints.)
+
+ Call "./ch-2.sh -svg ch-2.in" to solve task 2 and generate an SVG
+ for visualization using the solution to task 1.
+
+EOS
+
+
+### Input and Output
+
+if ($svg) {
+ # Import the SVG generator from task 1.
+ local $norun = 1;
+ do "./ch-1.pl" or die $@;
+ say scalar least_square_linear_regression(\*ARGV);
+} else {
+ say join ", ", least_square_linear_regression(\*ARGV);
+}
+
+
+### Implementation
+
+sub least_square_linear_regression ($fh) {
+ # Slurp the file content, remove trailing whitespace, convert
+ # whitespace to semicolons and use the resulting string as a piddle
+ # constructor argument. This results in a 2xN piddle of coordinate
+ # pairs.
+ my $points = do {
+ local $/;
+ pdl <$fh> =~ s/\s+\z//r =~ s/\s+/;/gr
+ };
+ # Split the points' coordinates in separate piddles along the first
+ # dimension resulting in piddles for x and y values as required by
+ # "polyfit". Then find the best fit with a polynomial of degree one
+ # (i.e. a line). Here only the solution's internal representation
+ # is needed as the result.
+ my $fit = (polyfit $points->xchg(0, 1)->dog, ones($points->dim(1)), 1)[3];
+ # Get the x range.
+ my $x = pdl minmax $points->slice(0);
+ # Get the corresponding y values.
+ my $y = (polyvalue(1, 0, $x, $fit))[0];
+ # Create a line piddle having x and y as endpoint coordinates.
+ my $line = pdl($x, $y)->xchg(0, 1);
+
+ if (wantarray) {
+ # Flatten the line piddle.
+ return $line->list;
+ } else {
+ # Convert the line endpoints (2x2) into the shape expected by
+ # gen_svg (2x2x1) from task 1 and generate an SVG from the given
+ # points and the regression line.
+ return gen_svg($points, $line->dummy(2));
+ }
+}