diff options
| author | Mohammad S Anwar <Mohammad.Anwar@yahoo.com> | 2022-05-23 01:17:51 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-23 01:17:51 +0100 |
| commit | 39a81edb43e1e69d9952d8e9fa7323b494031150 (patch) | |
| tree | a852efa74f9012e3f19649c65f73f50ac20ceb57 /challenge-165 | |
| parent | 4549a58c8f81306554d7097405ca3cfacc6f3522 (diff) | |
| parent | 965ff36347baff514295f48e7bf8178bd3e30b54 (diff) | |
| download | perlweeklychallenge-club-39a81edb43e1e69d9952d8e9fa7323b494031150.tar.gz perlweeklychallenge-club-39a81edb43e1e69d9952d8e9fa7323b494031150.tar.bz2 perlweeklychallenge-club-39a81edb43e1e69d9952d8e9fa7323b494031150.zip | |
Merge pull request #6141 from dcw803/master
imported my solutions to the SVG tasks, in C and Perl
Diffstat (limited to 'challenge-165')
| -rw-r--r-- | challenge-165/duncan-c-white/C/.build | 3 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/C/Makefile | 18 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/C/MySVG.c | 91 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/C/MySVG.h | 9 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/C/ch-1.c | 211 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/C/ch-2.c | 221 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/README | 75 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/bestfitdata | 48 | ||||
| -rw-r--r-- | challenge-165/duncan-c-white/perl/MySVG.pm | 123 | ||||
| -rwxr-xr-x | challenge-165/duncan-c-white/perl/ch-1.pl | 113 | ||||
| -rwxr-xr-x | challenge-165/duncan-c-white/perl/ch-2.pl | 145 |
11 files changed, 1027 insertions, 30 deletions
diff --git a/challenge-165/duncan-c-white/C/.build b/challenge-165/duncan-c-white/C/.build index 7a8295430e..295e8bb4c9 100644 --- a/challenge-165/duncan-c-white/C/.build +++ b/challenge-165/duncan-c-white/C/.build @@ -1,2 +1 @@ -BUILD = ch-1 -#CFLAGS = -g +BUILD = ch-1 ch-2 diff --git a/challenge-165/duncan-c-white/C/Makefile b/challenge-165/duncan-c-white/C/Makefile new file mode 100644 index 0000000000..fddc090ab6 --- /dev/null +++ b/challenge-165/duncan-c-white/C/Makefile @@ -0,0 +1,18 @@ +CC = gcc +CFLAGS = -Wall +BUILD = ch-1 ch-2 + +all: $(BUILD) + +clean: + /bin/rm -f $(BUILD) *.o core + +MySVG.o: MySVG.c MySVG.h + +ch-1: ch-1.o MySVG.o + +ch-1.o: ch-1.c MySVG.h + +ch-2: ch-2.o MySVG.o + +ch-2.o: ch-2.c MySVG.h diff --git a/challenge-165/duncan-c-white/C/MySVG.c b/challenge-165/duncan-c-white/C/MySVG.c new file mode 100644 index 0000000000..96ec7ce87a --- /dev/null +++ b/challenge-165/duncan-c-white/C/MySVG.c @@ -0,0 +1,91 @@ +// MySVG: simple SVG routines + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "MySVG.h" + +/* + * start_svg( w, h ); + * Generate the xml, doctype and svg tags, width $w by height $h + */ +void start_svg( int w, int h ) +{ + printf( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" ); + printf( "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" ); + printf( "<svg width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", w, h ); +} + + + +/* + * end_svg(); + * Generate the end svg tag at the end of our svg document + */ +void end_svg( void ) +{ + printf( "</svg>\n" ); +} + + +static char colour[100] = "red"; +static int linewidth = 4; + + +/* + * setcolour( colourname ); + * Set the current colour. + */ +void setcolour( char *colourname ) +{ + strcpy( colour, colourname ); +} + + + +/* + * setlinewidth( lw ); + * Set the line width. + */ +void setlinewidth( int lw ) +{ + linewidth = lw; +} + + + + + +/* + * point( x, y ); + * Plot a point. + */ +void point( int x, int y ) +{ + printf( " <circle cx=\"%d\" cy=\"%d\" r=\"1\" fill=\"%s\" stroke-width=\"0\" />\n", x, y, colour ); +} + + +/* + * line( x1, y1, x2, y2 ); + * Draw a line from (x1,y1) to (x2,y2) + */ +void line( int x1, int y1, int x2, int y2 ) +{ + printf( " <line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke=\"%s\" stroke-width=\"%d\" />\n", + x1, y1, x2, y2, colour, linewidth ); +} + + +/* + * rect( x1, y1, x2, y2 ); + * Draw a rectangle from (x1,y1) to (x2,y2) + */ +void rect( int x1, int y1, int x2, int y2 ) +{ + int w = 1+x2-x1; + int h = 1+y2-y1; + printf( " <rect x1=\"%d\" y1=\"%d\" width=\"%d\" height=\"%d\" fill=\"white\" stroke=\"%s\" stroke-width=\"%d\" />\n", + x1, y1, w, h, colour, linewidth ); +} diff --git a/challenge-165/duncan-c-white/C/MySVG.h b/challenge-165/duncan-c-white/C/MySVG.h new file mode 100644 index 0000000000..d74a927127 --- /dev/null +++ b/challenge-165/duncan-c-white/C/MySVG.h @@ -0,0 +1,9 @@ +// MySVG: simple SVG routines + +extern void start_svg( int w, int h ); +extern void end_svg( void ); +extern void setcolour( char *colourname ); +extern void setlinewidth( int lw ); +extern void point( int x, int y ); +extern void line( int x1, int y1, int x2, int y2 ); +extern void rect( int x1, int y1, int x2, int y2 ); diff --git a/challenge-165/duncan-c-white/C/ch-1.c b/challenge-165/duncan-c-white/C/ch-1.c new file mode 100644 index 0000000000..b175154f51 --- /dev/null +++ b/challenge-165/duncan-c-white/C/ch-1.c @@ -0,0 +1,211 @@ +/* + * TASK 1: Scalable Vector Graphics (SVG) + * + * Example: + * + * 53,10 + * 53,10,23,30 + * 23,30 + * + * GUEST LANGUAGE: This is my attempt to translate ch-1.pl and MySVG.pm into C + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <ctype.h> +#include <assert.h> + +#include "MySVG.h" + +bool debug=0; + +// process_noarg( argc, argv ); +// Process the -d flag, and check that there are NO +// arguments left. +void process_noarg( int argc, char **argv ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left != 0 ) + { + fprintf( stderr, + "Usage: mk-svg [-d] < inputfile\n" ); + exit(1); + } +} + + +typedef struct { int x, y; } datapoint; +typedef struct { int x1, y1, x2, y2; } dataline; + +#define MAXITEMS 1024 + +int npoints = 0; +datapoint pt[MAXITEMS]; // input points +int nlines = 0; +dataline ln[MAXITEMS]; // lines, each line an [x1,y1,x2,y2] tuple + +datapoint wh; // wh.x == width of our data, wh.y == height of our data + + +/* + * updatewh( x, y ); + * Update wh to include x and y. + */ +void updatewh( int x, int y ) +{ + assert( x>=0 ); + assert( y>=0 ); + if( x > wh.x ) wh.x = x; + if( y > wh.y ) wh.y = y; +} + + +/* + * bool lineread = readline( in, buf, maxlen ); + * read a single line from in (a FILE * open for input) + * into a character buffer (char *), reading no more than + * maxlen characters, removing a trailing '\n' from the + * buffer (if there is one). + * + * If the input line is longer than that, the REMAINDER OF + * THE LINE IS LEFT IN the file's input buffer for later reading. + * If readline() is called again immediately, the remainder of + * the line will be treated as a separate line. + * + * In caller-space, buf must be a writable character buffer of + * size >= maxlen, i.e. typically either a char [n] + * or a malloc(n * sizeof(char)) chunk, + * where (in both cases) n >= maxlen + */ +bool readline( FILE *in, char *buf, int maxlen ) +{ + if( fgets( buf, maxlen, in ) == 0 ) + { + return false; + } + int l = strlen(buf); + if( l>0 && buf[l-1]=='\n' ) + { + buf[l-1] = '\0'; + } + return true; +} + + +/* + * + * read_data( in ); + * Read data (points and lines) from in. + */ +void read_data( FILE *in ) +{ + #define MAXLINELEN 100 + char line[MAXLINELEN]; + + npoints = 0; + nlines = 0; + while( readline(in, line, MAXLINELEN ) ) + { + int x1, y1, x2, y2; + char *s; + char *p; + + s = line; + assert( isdigit(*s) ); + p = strchr(s,','); + assert( p != NULL ); + *p = '\0'; + x1 = atoi(s); + + s = p+1; + assert( isdigit(*s) ); + y1 = atoi(s); + p = strchr(s,','); + + if( p != NULL ) // must be a line + { + *p = '\0'; + + s = p+1; + assert( isdigit(*s) ); + p = strchr(s,','); + + assert( p != NULL ); + *p = '\0'; + x2 = atoi(s); + + s = p+1; + assert( isdigit(*s) ); + y2 = atoi(s); + + //printf( "debug: read line %d,%d, %d,%d\n", x1, y1, x2, y2 ); + ln[nlines].x1 = x1; + ln[nlines].y1 = y1; + ln[nlines].x2 = x2; + ln[nlines].y2 = y2; + nlines++; + updatewh( x1, y1 ); + updatewh( x2, y2 ); + } else + { + //printf( "debug: read point %d,%d\n", x1, y1 ); + pt[npoints].x = x1; + pt[npoints].y = y1; + npoints++; + updatewh( x1, y1 ); + } + } + +} + +int main( int argc, char **argv ) +{ + process_noarg( argc, argv ); + + wh.x = -1; + wh.y = -1; + + read_data( stdin ); + + int w = wh.x; + int h = wh.y; + + w += (10-w%10); + h += (10-h%10); + + int xscale = w < 200 ? 200/w : 1; + int yscale = h < 200 ? 200/h : 1; + + fprintf( stderr, "maxxy rectangle is w %d, h %d, xscale %d, yscale %d\n", w, h, xscale, yscale ); + + start_svg( w * xscale, h * yscale ); + + setcolour( "blue" ); + setlinewidth( 1 ); + for( int i=0; i<nlines; i++ ) + { + // printf( "debug: generating line for ln[%d]: %d,%d, %d,%d\n", + // i, ln[i].x1, ln[i].y1, ln[i].x2, ln[i].y2 ); + line( ln[i].x1*xscale, ln[i].y1*yscale, + ln[i].x2*xscale, ln[i].y2*yscale ); + } + + setcolour( "orange" ); + for( int i=0; i<npoints; i++ ) + { + point( pt[i].x*xscale, pt[i].y*yscale ); + } + + end_svg(); + + return 0; +} diff --git a/challenge-165/duncan-c-white/C/ch-2.c b/challenge-165/duncan-c-white/C/ch-2.c new file mode 100644 index 0000000000..7a71c5f957 --- /dev/null +++ b/challenge-165/duncan-c-white/C/ch-2.c @@ -0,0 +1,221 @@ +/* + * Task 2: Line of Best Fit + * + * GUEST LANGUAGE: This is my attempt to translate ch-2.pl into C + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <ctype.h> +#include <assert.h> + +#include "MySVG.h" + +bool debug=0; + +// char *s = process_singlearg( argc, argv ); +// Process the -d flag, and check that there is a single +// remaining argument, return it +char * process_singlearg( int argc, char **argv ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left != 1 ) + { + fprintf( stderr, + "Usage: best-fit [-d] filename\n" ); + exit(1); + } + + // element is in argv[arg] + + if( debug ) + { + printf( "debug: remaining argument is in pos %d, and is %s\n", + arg, argv[arg] ); + } + + return argv[arg]; +} + + +typedef struct { int x, y; } datapoint; +typedef struct { int x1, y1, x2, y2; } dataline; + +#define MAXITEMS 1024 + +int npoints = 0; +datapoint pt[MAXITEMS]; // input points + +datapoint wh; // wh.x == width of our data, wh.y == height of our data + + +/* + * updatewh( x, y ); + * Update wh to include x and y. + */ +void updatewh( int x, int y ) +{ + assert( x>=0 ); + assert( y>=0 ); + if( x > wh.x ) wh.x = x; + if( y > wh.y ) wh.y = y; +} + + +/* + * bool lineread = readline( in, buf, maxlen ); + * read a single line from in (a FILE * open for input) + * into a character buffer (char *), reading no more than + * maxlen characters, removing a trailing '\n' from the + * buffer (if there is one). + * + * If the input line is longer than that, the REMAINDER OF + * THE LINE IS LEFT IN the file's input buffer for later reading. + * If readline() is called again immediately, the remainder of + * the line will be treated as a separate line. + * + * In caller-space, buf must be a writable character buffer of + * size >= maxlen, i.e. typically either a char [n] + * or a malloc(n * sizeof(char)) chunk, + * where (in both cases) n >= maxlen + */ +bool readline( FILE *in, char *buf, int maxlen ) +{ + if( fgets( buf, maxlen, in ) == 0 ) + { + return false; + } + int l = strlen(buf); + if( l>0 && buf[l-1]=='\n' ) + { + buf[l-1] = '\0'; + } + return true; +} + + +/* + * + * read_data( in ); + * Read data (points) from in. + */ +void read_data( FILE *in ) +{ + #define MAXLINELEN 100 + char line[MAXLINELEN]; + + npoints = 0; + while( readline(in, line, MAXLINELEN ) ) + { + int x, y; + char *s; + char *p; + + s = line; + assert( isdigit(*s) ); + p = strchr(s,','); + assert( p != NULL ); + *p = '\0'; + x = atoi(s); + + s = p+1; + assert( isdigit(*s) ); + y = atoi(s); + + //fprintf( stderr, "debug: read point %d,%d\n", x, y ); + pt[npoints].x = x; + pt[npoints].y = y; + npoints++; + updatewh( x, y ); + } +} + + +/* + * bestfit( points, npoints, &m, &c ); + * Calculate y=mx+c, the best fit line of the data (each data point an [x,y] pair) + */ +void bestfit( datapoint *pt, int npoints, double *m, double *c ) +{ + int sumx = 0; + int sumy = 0; + int sumxy = 0; + int sumx2 = 0; + for( int i=0; i<npoints; i++ ) + { + int x = pt[i].x; + int y = pt[i].y; + sumx += x; + sumy += y; + sumxy += x * y; + sumx2 += x * x; + } + + *m = (npoints * sumxy - sumx * sumy) / (double) (npoints * sumx2 - sumx * sumx); + *c = (sumy - *m * sumx) / (double)npoints; + + if( debug ) + { + fprintf( stderr, "bestfit: n=%d, sumx=%d, sumy=%d, sumxy=%d, sumx^2=%d\n", + npoints, sumx, sumy, sumxy, sumx2 ); + fprintf( stderr, "bestfit: m=%.6f, c=%.6f\n", *m, *c ); + } +} + + +int main( int argc, char **argv ) +{ + char *filename = process_singlearg( argc, argv ); + + wh.x = -1; + wh.y = -1; + + FILE *in = fopen( filename, "r" ); + assert( in != NULL ); + read_data( in ); + fclose( in ); + + int w = wh.x; + int h = wh.y; + + w += (10-w%10); + h += (10-h%10); + + int xscale = w < 200 ? 200/w : 1; + int yscale = h < 200 ? 200/h : 1; + + if( debug ) + { + fprintf( stderr, "maxxy rectangle is w %d, h %d, xscale %d, yscale %d\n", w, h, xscale, yscale ); + } + + double m, c; + bestfit( pt, npoints, &m, &c ); + int y1 = (int) (m + c); // for x==1 + int y2 = (int) (m * (w-1) + c); // for x==w-1 + + start_svg( w * xscale, h * yscale ); + + setcolour( "blue" ); + setlinewidth( 1 ); + line( 1, y1*yscale, (w-1)*xscale, y2*yscale ); + + setcolour( "orange" ); + for( int i=0; i<npoints; i++ ) + { + point( pt[i].x*xscale, pt[i].y*yscale ); + } + + end_svg(); + + return 0; +} diff --git a/challenge-165/duncan-c-white/README b/challenge-165/duncan-c-white/README index 06b7a108f4..c04719179d 100644 --- a/challenge-165/duncan-c-white/README +++ b/challenge-165/duncan-c-white/README @@ -1,45 +1,64 @@ -TASK #1 - Prime Palindrome +TASK 1: Scalable Vector Graphics (SVG) -Write a script to find all prime numbers less than 1000, which are also -palindromes in base 10. Palindromic numbers are numbers whose digits -are the same in reverse. For example, 313 is a palindromic prime, but -337 is not, even though 733 (337 reversed) is also prime. +Scalable Vector Graphics (SVG) are not made of pixels, but lines, +ellipses, and curves, that can be scaled to any size without any loss +of quality. If you have ever tried to resize a small JPG or PNG, you +know what I mean by 'loss of quality'! What many people do not know +about SVG files is, they are simply XML files, so they can easily be +generated programmatically. -MY NOTES: ok. Pretty easy. +For this task, you may use external library, such as Perl's SVG library, +maintained in recent years by our very own Mohammad S Anwar. You can +instead generate the XML yourself; it's actually quite simple. The +source for the example image for Task #2 might be instructive. + +Your task is to accept a series of points and lines in the following format, one per line, in arbitrary order: + +Point: x,y +Line: x1,y1,x2,y2 + +Example: + +53,10 +53,10,23,30 +23,30 + +Then, generate an SVG file plotting all points, and all lines. If done +correctly, you can view the output .svg file in your browser. + +MY NOTES: ok. Having read the SVG Wikipedia page, it seems very easy (although I hate XML). GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl into C (look in the C directory for that). -TASK #2 - Happy Numbers +Task 2: Line of Best Fit -Write a script to find the first 8 Happy Numbers in base 10. For more -information, please check out https://en.wikipedia.org/wiki/Happy_number +When you have a scatter plot of points, a line of best fit is the line +that best describes the relationship between the points, and is very +useful in statistics. Otherwise known as linear regression, here is an +example of what such a line might look like: -Starting with any positive integer, replace the number by the sum of the -squares of its digits, and repeat the process until the number equals 1 -(where it will stay), or it loops endlessly in a cycle which does not -include 1. +[image showing red best fit line on graph of black data points] -Those numbers for which this process end in 1 are happy numbers, while -those numbers that do not end in 1 are unhappy numbers. +The method most often used is known as the least squares method, as +it is straightforward and efficient, but you may use any method that +generates the correct result. -Example +Calculate the line of best fit for the following 48 points: -19 is a Happy Number in base 10, as shown: +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 -19 => 1^2 + 9^2 - => 1 + 81 - => 82 => 8^2 + 2^2 - => 64 + 4 - => 68 => 6^2 + 8^2 - => 36 + 64 - => 100 => 1^2 + 0^2 + 0^2 - => 1 + 0 + 0 - => 1 +Using your rudimentary graphing engine from Task #1, graph all points, as well as the line of best fit. -MY NOTES: seems very easy. Loop detection is surely just a set of values -that we've already seen. +MY NOTES: seems pretty simple, https://www.mathsisfun.com/data/least-squares-regression.html has a good +explanation of the method. I've put the above data, one point per line, in "bestfitdata" in the +top-level directory. GUEST LANGUAGE: As a bonus, I also had a go at translating ch-2.pl into C (look in the C directory for that). diff --git a/challenge-165/duncan-c-white/bestfitdata b/challenge-165/duncan-c-white/bestfitdata new file mode 100644 index 0000000000..132a3da095 --- /dev/null +++ b/challenge-165/duncan-c-white/bestfitdata @@ -0,0 +1,48 @@ +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/duncan-c-white/perl/MySVG.pm b/challenge-165/duncan-c-white/perl/MySVG.pm new file mode 100644 index 0000000000..23e081c7ac --- /dev/null +++ b/challenge-165/duncan-c-white/perl/MySVG.pm @@ -0,0 +1,123 @@ +package MySVG; + +use strict; +use warnings; +use feature 'say'; +use Function::Parameters; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw( + start_svg + expand_macros + setcolour + setlinewidth + point + line + rect + end_svg + ); + + +=pod + +=head2 start_svg( $w, $h ); + +Generate the xml, doctype and svg tags, width $w by height $h + +=cut +fun start_svg( $w, $h ) +{ + say qq(<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="$w" height="$h" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">); +} + + +=pod + +=head2 end_svg(); + +Generate the end svg tag at the end of our svg document + +=cut +fun end_svg( ) +{ + say qq(</svg>); +} + + +my $colour = "red"; +my $linewidth = 4; + + +=pod + +=head2 setcolour( $colourname ); + +Set the current colour. + +=cut +fun setcolour( $colourname ) +{ + $colour = $colourname; +} + + +=pod + +=head2 setlinewidth( $n ); + +Set the line width. + +=cut +fun setlinewidth( $n ) +{ + $linewidth = $n; +} + + + +=pod + +=head2 point( $x, $y ); + +Plot a point. + +=cut +fun point( $x, $y ) +{ + #say qq( <line x1="$x" y1="$y" x2="$x" y2="$y" stroke="$colour" stroke-width="$linewidth" />); + say qq( <circle cx="$x" cy="$y" r="1" fill="$colour" stroke-width="0" />); +} + + +=pod + +=head2 line( $x1, $y1, $x2, $y2 ); + +Draw a line from (x1,y1) to (x2,y2) + +=cut +fun line( $x1, $y1, $x2, $y2 ) +{ + say qq( <line x1="$x1" y1="$y1" x2="$x2" y2="$y2" stroke="$colour" stroke-width="$linewidth" />); +} + + +=pod + +=head2 rect( $x1, $y1, $x2, $y2 ); + +Draw a rectangle from (x1,y1) to (x2,y2) + +=cut +fun rect( $x1, $y1, $x2, $y2 ) +{ + my $w = 1+$x2-$x1; + my $h = 1+$y2-$y1; + say qq( <rect x1="$x1" y1="$y1" width="$w" height="$h" fill="white" stroke="$colour" stroke-width="$linewidth" />); +} + + +1; diff --git a/challenge-165/duncan-c-white/perl/ch-1.pl b/challenge-165/duncan-c-white/perl/ch-1.pl new file mode 100755 index 0000000000..6ec5572613 --- /dev/null +++ b/challenge-165/duncan-c-white/perl/ch-1.pl @@ -0,0 +1,113 @@ +#!/usr/bin/perl +# +# TASK 1: Scalable Vector Graphics (SVG) +# +# Scalable Vector Graphics (SVG) are not made of pixels, but lines, +# ellipses, and curves, that can be scaled to any size without any loss +# of quality. If you have ever tried to resize a small JPG or PNG, you +# know what I mean by 'loss of quality'! What many people do not know +# about SVG files is, they are simply XML files, so they can easily be +# generated programmatically. +# +# For this task, you may use external library, such as Perl's SVG library, +# maintained in recent years by our very own Mohammad S Anwar. You can +# instead generate the XML yourself; it's actually quite simple. The +# source for the example image for Task #2 might be instructive. +# +# Your task is to accept a series of points and lines in the following format, one per line, in arbitrary order: +# +# Point: x,y +# Line: x1,y1,x2,y2 +# +# Example: +# +# 53,10 +# 53,10,23,30 +# 23,30 +# +# Then, generate an SVG file plotting all points, and all lines. If done +# correctly, you can view the output .svg file in your browser. +# +# MY NOTES: ok. Having read the SVG Wikipedia page, it seems very easy to do this myself (although I hate XML). +# +# GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl +# into C (look in the C directory for that). +# + +use strict; +use warnings; +use feature 'say'; +use Getopt::Long; +use Function::Parameters; +#use Data::Dumper; + +use lib qw(.); +use MySVG; + + +my $debug=0; +die "Usage: mk-svg [--debug] [inputfile]\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV<2; + +my @pt; # points, each point an [x,y] pair +my @ln; # lines, each line an [x1,y1,x2,y2] tuple + + # %wh will be the width and height of our data +my %wh = ( W => -1, H => -1 ); + +=pod + +=head2 updatewh( $x, $y ); + +Update %wh rectangle to include $x and $y. + +=cut +fun updatewh( $x, $y ) +{ + die "x is negative: $x\n" if $x<0; + die "y is negative: $y\n" if $y<0; + $wh{W} = $x if $x > $wh{W}; + $wh{H} = $y if $y > $wh{H}; +} + + +while( <> ) +{ + chomp; + s/\s+//g; + my @x = split(/,/); + if( @x==2 ) + { + push @pt, \@x; + updatewh( @x ); + } elsif( @x == 4 ) + { + push @ln, \@x; + updatewh( @x[0,1] ); + updatewh( @x[2,3] ); + } else + { + die "Error at line $.: 2 or 4 numbers expected: $_\n"; + } +} + +my( $w, $h ) = @wh{qw(W H)}; +$w += (10-$w%10); +$h += (10-$h%10); + +my $xscale = $w < 200 ? 200/$w : 1; +my $yscale = $h < 200 ? 200/$h : 1; + +warn "maxxy rectangle is w $w, h $h, xscale $xscale, yscale $yscale\n" if $debug; + +start_svg( $w * $xscale, $h * $yscale ); + +setcolour( "blue" ); +setlinewidth( 1 ); +line( $_->[0]*$xscale, $_->[1]*$yscale, + $_->[2]*$xscale, $_->[3]*$yscale ) foreach @ln; + +setcolour( "orange" ); +point( $_->[0]*$xscale, $_->[1]*$yscale ) foreach @pt; + +end_svg(); diff --git a/challenge-165/duncan-c-white/perl/ch-2.pl b/challenge-165/duncan-c-white/perl/ch-2.pl new file mode 100755 index 0000000000..f820af06be --- /dev/null +++ b/challenge-165/duncan-c-white/perl/ch-2.pl @@ -0,0 +1,145 @@ +#!/usr/bin/perl +# +# Task 2: Line of Best Fit +# +# When you have a scatter plot of points, a line of best fit is the line +# that best describes the relationship between the points, and is very +# useful in statistics. Otherwise known as linear regression, here is an +# example of what such a line might look like: +# +# [image showing red best fit line on graph of black data points] +# +# The method most often used is known as the least squares method, as +# it is straightforward and efficient, but you may use any method that +# generates the correct result. +# +# Calculate the line of best fit for the following 48 points: +# +# 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 +# +# Using your rudimentary graphing engine from Task #1, graph all points, as well as the line of best fit. +# +# MY NOTES: seems pretty simple, https://www.mathsisfun.com/data/least-squares-regression.html has a good +# explanation of the method. I've put the above data, one point per line, in "bestfitdata" in the +# top-level directory. +# +# GUEST LANGUAGE: As a bonus, I also had a go at translating ch-2.pl +# into C (look in the C directory for that). +# + +use strict; +use warnings; +use feature 'say'; +use Getopt::Long; +use Function::Parameters; +#use Data::Dumper; + +use lib qw(.); +use MySVG; + + +my $debug=0; +die "Usage: best-fit-points [--debug] [inputfile]\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV<2; + +my @pt; # points, each point an [x,y] pair + + # %wh will be the width and height of our data +my %wh = ( W => -1, H => -1 ); + +=pod + +=head2 updatewh( $x, $y ); + +Update %wh rectangle to include $x and $y. + +=cut +fun updatewh( $x, $y ) +{ + die "x is negative: $x\n" if $x<0; + die "y is negative: $y\n" if $y<0; + $wh{W} = $x if $x > $wh{W}; + $wh{H} = $y if $y > $wh{H}; +} + + +while( <> ) +{ + chomp; + s/\s+//g; + my @x = split(/,/); + if( @x==2 ) + { + push @pt, \@x; + updatewh( @x ); + } else + { + die "Error at line $.: x,y numbers expected: $_\n"; + } +} + +my( $w, $h ) = @wh{qw(W H)}; +$w += (10-$w%10); +$h += (10-$h%10); + +my $xscale = $w < 200 ? 200/$w : 1; +my $yscale = $h < 200 ? 200/$h : 1; + +warn "debug: maxxy rectangle is w $w, h $h, xscale $xscale, yscale $yscale\n" if $debug; + +=pod + +=head2 my( $m, $c ) = bestfit( @data ); + +Calculate y=mx+c, the best fit line of the data (each data point an [x,y] pair) + +=cut +fun bestfit( @data ) +{ + my $sumx = 0; + my $sumy = 0; + my $sumxy = 0; + my $sumx2 = 0; + foreach my $pt (@data) + { + my( $x, $y ) = @$pt; + $sumx += $x; + $sumy += $y; + $sumxy += $x * $y; + $sumx2 += $x * $x; + } + my $n = @data; + warn "bestfit: n=$n, sumx=$sumx, sumy=$sumy, sumxy=$sumxy, sumx^2=$sumx2\n" if $debug; + + my $m = ($n * $sumxy - $sumx * $sumy) / ($n * $sumx2 - $sumx * $sumx); + my $c = ($sumy - $m * $sumx) / $n; + + warn "bestfit: m=$m, c=$c\n" if $debug; + return( $m, $c ); +} + + +my( $m, $c ) = bestfit( @pt ); +my $y1 = $m + $c; # for x==1 +my $y2 = $m * ($w-1) + $c; # for x==w-1 + + +start_svg( $w * $xscale, $h * $yscale ); + +setcolour( "black" ); +setlinewidth( 1 ); +rect( 0, 0, $w-1, $h-1 ); + +setcolour( "blue" ); +setlinewidth( 1 ); +line( 1, $y1*$yscale, ($w-1)*$xscale, $y2*$yscale ); + +setcolour( "oran |
