aboutsummaryrefslogtreecommitdiff
path: root/challenge-027
diff options
context:
space:
mode:
authordcw <d.white@imperial.ac.uk>2019-09-29 23:44:54 +0100
committerdcw <d.white@imperial.ac.uk>2019-09-29 23:44:54 +0100
commit5430708b11757850cb80b415f65bc3aed811e9d4 (patch)
treeca492698af5dc64036d7e42f498fc428fee4d816 /challenge-027
parentb85b95be86c1e2a250858f5c5022030470c09d02 (diff)
downloadperlweeklychallenge-club-5430708b11757850cb80b415f65bc3aed811e9d4.tar.gz
perlweeklychallenge-club-5430708b11757850cb80b415f65bc3aed811e9d4.tar.bz2
perlweeklychallenge-club-5430708b11757850cb80b415f65bc3aed811e9d4.zip
imported my challenge-027 solutions..
Diffstat (limited to 'challenge-027')
-rw-r--r--challenge-027/duncan-c-white/README54
-rwxr-xr-xchallenge-027/duncan-c-white/perl5/ch-1.pl92
-rwxr-xr-xchallenge-027/duncan-c-white/perl5/ch-2.pl180
3 files changed, 299 insertions, 27 deletions
diff --git a/challenge-027/duncan-c-white/README b/challenge-027/duncan-c-white/README
index 4cd2bbfe01..69197e921e 100644
--- a/challenge-027/duncan-c-white/README
+++ b/challenge-027/duncan-c-white/README
@@ -1,36 +1,36 @@
-Challenge 1: "Create a script that prints nth order forward difference
-series. You should be a able to pass the list of numbers and order number
-as command line parameters. Let me show you with an example:
+Challenge 1: "Write a script to find the intersection of two straight
+lines. The co-ordinates of the two lines should be provided as command
+line parameter. For example:
-Suppose we have list (X) of numbers: 5, 9, 2, 8, 1, 6 and we would like
-to create 1st order forward difference series (Y). So using the formula
-Y(i) = X(i+1) - X(i), we get the following numbers: (9-5), (2-9), (8-2),
-(1-8), (6-1), ie 4, -7, 6, -7, 5.
-If you noticed, it has one less number than the original series.
-Similarly you can generate the 2nd order forward difference series like:
-(-7-4), (6+7), (-7-6), (5+7) => -11, 13, -13, 12.
+The two ends of Line 1 are represented as co-ordinates (a,b) and (c,d).
-My notes: Clearly defined, very easy - let's have a go..
+The two ends of Line 2 are represented as co-ordinates (p,q) and (r,s).
+The script should print the co-ordinates of point of intersection of
+the above two lines."
-Challenge 2: "Create a script that prints Prime Decomposition of a
-given number. The prime decomposition of a number is defined as a list
-of prime numbers which when all multiplied together, are equal to that
-number. For example, the Prime decomposition of 228 is 2,2,3,19 as 228 =
-2 * 2 * 3 * 19."
+My notes: Clearly defined, very easy if I can remember the formulae - let's
+have a go.. after doing it, I decided to run Gnuplot to display the results
+as it's an intensely graphical concept..
-My notes: So, prime factors then. Very easy again. In fact, haven't I
-already solved this in one of the other prime-based questions?
+Challenge 2: "Write a script that allows you to capture/display historical
+data. It could be an object or a scalar. For example
-Challenge 3: "Write a script to use Random Poems API:
-https://www.poemist.com/api/v1/randompoems
-This is the easiset API, I have come across so far. You don't need API
-key for this. They have only route to work with (GET). The API task is
-optional but we would love to see your solution."
+ my $x = 10; $x = 20; $x -= 5;
-My notes: ok, even I can't argue that obtaining an API key for an API
-I will literally never use again is too much hassle - when I don't need
-an API key, and the whole program appears to be an LWP::Simple get..
+After the above operations, it should list $x historical value in order."
-update: well, apart from the Unicode in the response, complicating life.
+My notes: The idea is easy, but we're not told the API to implement. I'm
+certainly not going to introspect into Perl to find out whenever a scalar
+variable is assigned to! I have chosen to implement this for numeric
+variables, using an input sequence of VARNAME OP VALUE triples (where OP='=',
+'+=', '-=', '*=', '/=' or '%='), where each input triple N occurs at time N,
+and we track the historic value of each variable over time.
+
+I did that, using __DATA__ (at the end of this file) for the default sequence,
+or the contents of a named input file if given.
+
+I also graphed the time-series results via Gnuplot as I seem to be having
+a Gnuplot kick this week:-) It's not really the ideal output format, but
+it's relatively cute.
diff --git a/challenge-027/duncan-c-white/perl5/ch-1.pl b/challenge-027/duncan-c-white/perl5/ch-1.pl
new file mode 100755
index 0000000000..fbd23aaff8
--- /dev/null
+++ b/challenge-027/duncan-c-white/perl5/ch-1.pl
@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+#
+# Challenge 1: "Write a script to find the intersection of two straight
+# lines. The co-ordinates of the two lines should be provided as command
+# line parameter. For example:
+#
+# The two ends of Line 1 are represented as co-ordinates (a,b) and (c,d).
+#
+# The two ends of Line 2 are represented as co-ordinates (p,q) and (r,s).
+#
+# The script should print the co-ordinates of point of intersection of
+# the above two lines."
+#
+# My notes: Clearly defined, very easy - let's have a go.. and then
+# let's run gnuplot to display the results..
+#
+
+use v5.10; # to get "say"
+use strict;
+use warnings;
+use Function::Parameters;
+
+die "Usage: ch-1.pl line1:a b c d line2:p q r s\n" unless @ARGV == 8;
+
+my ( $a, $b, $c, $d, @rest ) = @ARGV;
+my ( $p, $q, $r, $s ) = @rest;
+
+# general form of an infinite line is y = mx + c, and m = (p1y-p2y)/(p1x-p2x)
+# (unless p1x==p2x) for any two points p1(x,y) and p2(x,y) on the line, and
+# then c = p1y - m*p1x (or p2y - m*p2x)
+#
+# once we have calculated m1 and c1 for line (a,b)-(c,d), and m2 and c2
+# for line (p,q)->(r,s), we need to find the intersection point and check it's
+# within the line segments.
+
+say "a=$a, b=$b, c=$c, d=$d, p=$p, q=$q, r=$r, s=$s";
+
+die "line 1: ($a,$b)->($c,$d) is vertical\n" if $b==$d;
+my $m1 = ($d-$b)/($c-$a);
+my $c1 = $b - $m1 * $a;
+
+die "line 2: ($p,$q)->($r,$s) is vertical\n" if $p==$r;
+my $m2 = ($s-$q)/($r-$p);
+my $c2 = $q - $m2 * $p;
+
+say "line 1 : y = $m1 x + $c1";
+say "line 2 : y = $m2 x + $c2";
+
+# intersection of line 1 and line 2: set line1.y == line2.y:
+# (m1-m2) x + (c1-c2) == 0
+# (m1-m2) x = c2-c1
+# ix = c2-c1/(m1-m2)
+# subst in line 1:
+# iy = m1 * ix + c1
+
+die "No intersection between $m1 x + $c1 and $m2 x + $c2\n" if $m1==$m2;
+
+my $ix = ($c2-$c1)/($m1-$m2);
+my $iy = $m1 * $ix + $c1;
+say "intersection at ($ix,$iy)";
+
+print "show results in gnuplot? ";
+my $in = <STDIN>;
+
+prepare_gnuplot() if $in =~ /^y/i;
+
+
+#
+# prepare_gnuplot();
+# Prepare the gnuplot files, the data file will contain
+# the two lines, and the command file will continue
+# instuctions to plot both lines and the intersection point
+#
+fun prepare_gnuplot()
+{
+ # prepare the data file..
+ open( my $outfh, '>', '1.dat' ) || die;
+ say $outfh "$a $b $p $q\n$c $d $r $s";
+ close( $outfh );
+
+ # prepare the gnuplot command file..
+ open( $outfh, '>', '1.cmd' ) || die;
+ say $outfh "set grid";
+ say $outfh "ix = $ix";
+ say $outfh "iy = $iy";
+ say $outfh "plot '1.dat' using 1:2 with lines title 'line 1', '1.dat' using 3:4 with lines title 'line 2', '+' using (ix):(iy) title 'intersection'";
+ say $outfh "pause mouse key";
+ close( $outfh );
+
+ # run gnuplot
+ system( "gnuplot 1.cmd" );
+}
diff --git a/challenge-027/duncan-c-white/perl5/ch-2.pl b/challenge-027/duncan-c-white/perl5/ch-2.pl
new file mode 100755
index 0000000000..aed4e00bd3
--- /dev/null
+++ b/challenge-027/duncan-c-white/perl5/ch-2.pl
@@ -0,0 +1,180 @@
+#!/usr/bin/perl
+#
+# Challenge 2: "Write a script that allows you to capture/display historical
+# data. It could be an object or a scalar. For example
+#
+# my $x = 10; $x = 20; $x -= 5;
+#
+# After the above operations, it should list $x historical value in order."
+#
+# My notes: The idea is easy, but we're not told the API to implement. I'm
+# certainly not going to introspect into Perl to find out whenever a scalar
+# variable is assigned to! I have chosen to implement this for numeric
+# variables, using an input sequence of VARNAME OP VALUE triples (where OP='=',
+# '+=', '-=', '*=', '/=' or '%='), and track the historic value of each
+# variable over time.
+#
+# So let's try to do that, using __DATA__ (at the end of this file) for the
+# default sequence, or the contents of a named input file if given.
+#
+# Let's also graph the time-series results via Gnuplot as I seem to be having
+# a Gnuplot kick this week:-)
+#
+
+use v5.10; # for "say"
+use strict;
+use warnings;
+use Function::Parameters;
+use Data::Dumper;
+
+
+#
+# my $val = combine( $op, $oldval, $newval );
+# Combine an optional old value $oldval (or undefined)
+# and a new value $newval, under operation $op ('=',
+#
+fun combine( $op, $oldval, $newval )
+{
+ return $newval if $op eq "=";
+ return $oldval + $newval if $op eq "+=";
+ return $oldval - $newval if $op eq "-=";
+ return $oldval * $newval if $op eq "*=";
+ return $oldval / $newval if $op eq "/=";
+ return $oldval % $newval if $op eq "%=";
+}
+
+
+#
+# my( $varset, $input ) = process( $infh );
+# Process an input file (handle $infh), every line of which
+# should be of the form VARNAME OP VALUE (where OP='=', '+=',
+# '-=', '*=', '/=' or '%='), and build and return a set of all
+# variables named in that input file, and an array @$input of
+# all [VARNAME,OP,VALUE] decoded triples.
+#
+fun process( $infh )
+{
+ my %vars;
+ my @input;
+ while( <$infh> )
+ {
+ chomp;
+ my( $var, $op, $value ) = split( /\s+/, $_ );
+ push @input, [$var,$op,$value];
+ $vars{$var}++;
+ }
+ return ( \%vars, \@input );
+}
+
+die "Usage: ch-2.pl\nor ch-2.pl filename\n" if @ARGV>1;
+my $varset;
+my $input;
+if( @ARGV == 0 )
+{
+ ( $varset, $input ) = process( \*DATA );
+}
+else
+{
+ open( my $infh, '<', $ARGV[0] ) || die;
+ ( $varset, $input ) = process( $infh );
+ close( $infh );
+}
+
+
+my $time = 0; # current time
+my %currv = map { $_ => 0 } keys %$varset; # current values of variables
+$time++;
+
+my %track; # track all historical values of all variables
+foreach my $var (keys %$varset)
+{
+ $track{$var} = [ 0 ];
+}
+
+
+#
+# update( $triple );
+# Take a single triple $triple of the form [ VARNAME, OP, VALUE ]
+# (where OP='=', '+=', '-=', '*=', '/=' or '%='),
+# and update %currv and %track..
+#
+fun update( $triple )
+{
+ my( $var, $op, $value ) = @$triple;
+ $currv{$var} = combine( $op, $currv{$var}, $value );
+ foreach my $var (keys %$varset)
+ {
+ push @{$track{$var}}, $currv{$var};
+ }
+ $time++;
+}
+
+
+foreach my $triple (@$input)
+{
+ update( $triple );
+}
+
+say "Final values:";
+foreach my $var (sort keys %currv)
+{
+ say "$var: $currv{$var}";
+}
+
+say "Historical values:";
+foreach my $var (sort keys %track)
+{
+ say "$var: ", join(',', @{$track{$var}} );
+}
+
+print "show results in gnuplot? ";
+my $in = <STDIN>;
+prepare_gnuplot() if $in =~ /^y/i;
+
+
+#
+# prepare_gnuplot();
+# Prepare the gnuplot files, the data file will contain
+# track[var][time] ordered first by time, then by vars,
+# and the command file will continue instuctions to plot
+# each column as a separate named variable.
+#
+fun prepare_gnuplot()
+{
+ # prepare the data file..
+ open( my $outfh, '>', '2.dat' ) || die;
+ foreach my $t (0..$time-1)
+ {
+ my @line = ( $t );
+ foreach my $var (sort keys %track)
+ {
+ push @line, $track{$var}[$t];
+ }
+ say $outfh join(' ', @line);
+ }
+ close( $outfh );
+
+ # prepare the gnuplot command file..
+ open( $outfh, '>', '2.cmd' ) || die;
+ say $outfh "set grid";
+ my $pos = 1;
+ my @pl = map {
+ $pos++;
+ "'2.dat' using 1:$pos with lines title '$_'";
+ } sort keys %track;
+ say $outfh "plot ", join(', ', @pl);
+ say $outfh "pause mouse key";
+ close( $outfh );
+
+ # run gnuplot
+ system( "gnuplot 2.cmd" );
+}
+
+__DATA__
+x = 10
+y = 20
+x = 20
+y += 100
+x -= 5
+y *= 2
+x *= 3