aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2022-09-25 22:46:10 +0100
committerGitHub <noreply@github.com>2022-09-25 22:46:10 +0100
commit264375d6845779a44b5466fb2b373e069e08f628 (patch)
tree63be5d2d3b61c0ece8b1f6cff26b5dc88eba5d66
parent5fbe4e12c2c88f4c62b39b140e14946e1831005a (diff)
parent13379480eb3a64609180d0b5e068470ea2e76e04 (diff)
downloadperlweeklychallenge-club-264375d6845779a44b5466fb2b373e069e08f628.tar.gz
perlweeklychallenge-club-264375d6845779a44b5466fb2b373e069e08f628.tar.bz2
perlweeklychallenge-club-264375d6845779a44b5466fb2b373e069e08f628.zip
Merge pull request #6792 from dcw803/master
imported my solutions to both tasks in Perl, and to task 1 in C
-rw-r--r--challenge-183/duncan-c-white/C/.cbuild2
-rw-r--r--challenge-183/duncan-c-white/C/Makefile18
-rw-r--r--challenge-183/duncan-c-white/C/README16
-rw-r--r--challenge-183/duncan-c-white/C/args.c207
-rw-r--r--challenge-183/duncan-c-white/C/args.h11
-rw-r--r--challenge-183/duncan-c-white/C/ch-1.c125
-rw-r--r--challenge-183/duncan-c-white/C/parseints.c146
-rw-r--r--challenge-183/duncan-c-white/C/parseints.h2
-rw-r--r--challenge-183/duncan-c-white/C/printarray.c39
-rw-r--r--challenge-183/duncan-c-white/C/printarray.h1
-rw-r--r--challenge-183/duncan-c-white/README101
-rw-r--r--challenge-183/duncan-c-white/inputpaths5
-rw-r--r--challenge-183/duncan-c-white/inputpaths25
-rw-r--r--challenge-183/duncan-c-white/inputpaths35
-rw-r--r--challenge-183/duncan-c-white/inputpaths45
-rwxr-xr-xchallenge-183/duncan-c-white/perl/ch-1.pl52
-rwxr-xr-xchallenge-183/duncan-c-white/perl/ch-2.pl102
17 files changed, 786 insertions, 56 deletions
diff --git a/challenge-183/duncan-c-white/C/.cbuild b/challenge-183/duncan-c-white/C/.cbuild
index a14ec76520..624a95ebfb 100644
--- a/challenge-183/duncan-c-white/C/.cbuild
+++ b/challenge-183/duncan-c-white/C/.cbuild
@@ -1,4 +1,4 @@
-BUILD = ch-1 ch-2
+BUILD = ch-1
CFLAGS = -Wall -g
#LDFLAGS = -lm
#CFLAGS = -g
diff --git a/challenge-183/duncan-c-white/C/Makefile b/challenge-183/duncan-c-white/C/Makefile
new file mode 100644
index 0000000000..fd1bdb32c7
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/Makefile
@@ -0,0 +1,18 @@
+# Makefile rules generated by CB
+CC = gcc
+CFLAGS = -Wall -g
+BUILD = ch-1
+
+all: $(BUILD)
+
+clean:
+ /bin/rm -f $(BUILD) *.o core a.out
+
+args.o: args.c
+ch-1: ch-1.o args.o parseints.o printarray.o
+ch-1.o: ch-1.c args.h parseints.h printarray.h
+parseints.o: parseints.c args.h parseints.h printarray.h
+printarray.o: printarray.c
+readline.o: readline.c readline.h
+trim.o: trim.c
+
diff --git a/challenge-183/duncan-c-white/C/README b/challenge-183/duncan-c-white/C/README
new file mode 100644
index 0000000000..497e0dfdd5
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/README
@@ -0,0 +1,16 @@
+Thought I'd also have a go at translating ch-1.pl into C..
+
+ch-1.c produces identical (non-debugging) output to my Perl original.
+
+It uses the command-line argument processing module args.[ch], and the
+integer-list parsing (parseints.[ch]) module, and the integer-list printing
+module (printarray.[ch]) from earlier challenges, tweaked slightly.
+
+Note that it's not a direct translation of the Perl solution because that
+involves joined strings and then storing a set of such joined strings.
+Instead it does it the obvious low-tech way: finding duplicate arrays and
+deleting them.
+
+I didn't have time to try ch-2.pl into C, although that should be
+straightforward using mktime() and friends like I did in challenge 176.
+Maybe I'll do that a bit later.
diff --git a/challenge-183/duncan-c-white/C/args.c b/challenge-183/duncan-c-white/C/args.c
new file mode 100644
index 0000000000..d4a2d38b9a
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/args.c
@@ -0,0 +1,207 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+
+bool debug = false;
+
+
+// process_flag_noarg( name, argc, argv );
+// Process the -d flag, and check that there are no
+// remaining arguments.
+void process_flag_noarg( char *name, 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: %s [-d]\n", name );
+ exit(1);
+ }
+}
+
+
+// int argno = process_flag_n_args( name, argc, argv, n, argmsg );
+// Process the -d flag, and check that there are exactly
+// n remaining arguments, return the index position of the first
+// argument. If not, generate a fatal Usage error using the argmsg.
+//
+int process_flag_n_args( char *name, int argc, char **argv, int n, char *argmsg )
+{
+ int arg=1;
+ if( argc>1 && strcmp( argv[arg], "-d" ) == 0 )
+ {
+ debug = true;
+ arg++;
+ }
+
+ int left = argc-arg;
+ if( left != n )
+ {
+ fprintf( stderr, "Usage: %s [-d] %s\n Exactly %d "
+ "arguments needed\n", name, argmsg, n );
+ exit(1);
+ }
+ return arg;
+}
+
+
+// int argno = process_flag_n_m_args( name, argc, argv, min, max, argmsg );
+// Process the -d flag, and check that there are between
+// min and max remaining arguments, return the index position of the first
+// argument. If not, generate a fatal Usage error using the argmsg.
+//
+int process_flag_n_m_args( char *name, int argc, char **argv, int min, int max, char *argmsg )
+{
+ int arg=1;
+ if( argc>1 && strcmp( argv[arg], "-d" ) == 0 )
+ {
+ debug = true;
+ arg++;
+ }
+
+ int left = argc-arg;
+ if( left < min || left > max )
+ {
+ fprintf( stderr, "Usage: %s [-d] %s\n Between %d and %d "
+ "arguments needed\n", name, argmsg, min, max );
+ exit(1);
+ }
+ return arg;
+}
+
+
+// process_onenumarg_default( name, argc, argv, defvalue, &n );
+// Process the -d flag, and check that there is a single
+// remaining numeric argument (or no arguments, in which case
+// we use the defvalue), putting it into n
+void process_onenumarg_default( char *name, int argc, char **argv, int defvalue, int *n )
+{
+ char argmsg[100];
+ sprintf( argmsg, "[int default %d]", defvalue );
+ int arg = process_flag_n_m_args( name, argc, argv, 0, 1, argmsg );
+
+ *n = arg == argc ? defvalue : atoi( argv[arg] );
+}
+
+
+// process_onenumarg( name, argc, argv, &n );
+// Process the -d flag, and check that there is a single
+// remaining numeric argument, putting it into n
+void process_onenumarg( char *name, int argc, char **argv, int *n )
+{
+ int arg = process_flag_n_args( name, argc, argv, 1, "int" );
+
+ // argument is in argv[arg]
+ *n = atoi( argv[arg] );
+}
+
+
+// process_twonumargs( name, argc, argv, &m, &n );
+// Process the -d flag, and check that there are 2
+// remaining numeric arguments, putting them into m and n
+void process_twonumargs( char *name, int argc, char **argv, int *m, int *n )
+{
+ int arg = process_flag_n_args( name, argc, argv, 2, "int" );
+
+ // arguments are in argv[arg] and argv[arg+1]
+ *m = atoi( argv[arg++] );
+ *n = atoi( argv[arg] );
+}
+
+
+// process_twostrargs() IS DEPRECATED: use process_flag_n_m_args() instead
+
+
+// int arr[100];
+// int nel = process_listnumargs( name, argc, argv, arr, 100 );
+// Process the -d flag, and check that there are >= 2
+// remaining numeric arguments, putting them into arr[0..nel-1]
+// and returning nel.
+int process_listnumargs( char *name, int argc, char **argv, int *arr, int maxel )
+{
+ int arg=1;
+ if( argc>1 && strcmp( argv[arg], "-d" ) == 0 )
+ {
+ debug = true;
+ arg++;
+ }
+
+ int left = argc-arg;
+ if( left < 2 )
+ {
+ fprintf( stderr, "Usage: %s [-d] list_of_numeric_args\n", name );
+ exit(1);
+ }
+ if( left > maxel )
+ {
+ fprintf( stderr, "%s: more than %d args\n", name, maxel );
+ exit(1);
+ }
+
+ // elements are in argv[arg], argv[arg+1]...
+
+ if( debug )
+ {
+ printf( "debug: remaining arguments are in arg=%d, "
+ "firstn=%s, secondn=%s..\n",
+ arg, argv[arg], argv[arg+1] );
+ }
+
+ int nel = 0;
+ for( int i=arg; i<argc; i++ )
+ {
+ arr[nel++] = atoi( argv[i] );
+ }
+ arr[nel] = -1;
+ return nel;
+}
+
+
+//
+// bool isint = check_unsigned_int( char *val, int *n );
+// Given an string val, check that there's an unsigned integer
+// in it (after optional whitespace). If there is a valid
+// unsigned integer value, store that integer value in *n and
+// return true; otherwise return false (and don't alter *n).
+bool check_unsigned_int( char *val, int *n )
+{
+ // skip whitespace in val
+ char *p;
+ for( p=val; isspace(*p); p++ )
+ {
+ /*EMPTY*/
+ }
+ if( ! isdigit(*p) ) return false;
+ *n = atoi(p);
+ return true;
+}
+
+
+//
+// bool ok = check_unsigned_real( char *val, double *n );
+// Given an string val, check that there's an unsigned real
+// in it (after optional whitespace). If there is a valid
+// unsigned real value, store that value in *n and
+// return true; otherwise return false (and don't alter *n).
+bool check_unsigned_real( char *val, double *n )
+{
+ // skip whitespace in val
+ char *p;
+ for( p=val; isspace(*p); p++ )
+ {
+ /*EMPTY*/
+ }
+ if( ! isdigit(*p) ) return false;
+ *n = atof(p);
+ return true;
+}
diff --git a/challenge-183/duncan-c-white/C/args.h b/challenge-183/duncan-c-white/C/args.h
new file mode 100644
index 0000000000..8844a8f9c4
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/args.h
@@ -0,0 +1,11 @@
+extern bool debug;
+
+extern void process_flag_noarg( char * name, int argc, char ** argv );
+extern int process_flag_n_args( char * name, int argc, char ** argv, int n, char * argmsg );
+extern int process_flag_n_m_args( char * name, int argc, char ** argv, int min, int max, char * argmsg );
+extern void process_onenumarg_default( char * name, int argc, char ** argv, int defvalue, int * n );
+extern void process_onenumarg( char * name, int argc, char ** argv, int * n );
+extern void process_twonumargs( char * name, int argc, char ** argv, int * m, int * n );
+extern int process_listnumargs( char * name, int argc, char ** argv, int * arr, int maxel );
+extern bool check_unsigned_int( char * val, int * n );
+extern bool check_unsigned_real( char * val, double * n );
diff --git a/challenge-183/duncan-c-white/C/ch-1.c b/challenge-183/duncan-c-white/C/ch-1.c
new file mode 100644
index 0000000000..087547cc2c
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/ch-1.c
@@ -0,0 +1,125 @@
+//
+// Task 1: Unique Array
+//
+// C version.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "args.h"
+#include "parseints.h"
+#include "printarray.h"
+
+
+typedef struct { int nel; int *list; } sizedarray;
+
+
+//
+// sizedarray a = ...;
+// sizedarray b = ...;
+// bool issame = same_list( &a, &b );
+// Return true iff a and b are the same list; else return false.
+//
+static bool same_list( sizedarray *a, sizedarray *b )
+{
+ if( a->nel != b->nel ) return false;
+ for( int i=0; i<a->nel; i++ )
+ {
+ if( a->list[i] != b->list[i] ) return false;
+ }
+ return true;
+}
+
+
+// sizedarray lists[nlists] = ...;
+// int dup = find_dup( lists, lno, nlists );
+// Determine whether lists[lno] has any duplicates in
+// lists[lno+1..nlists-1], return the duplicate list index no
+// if it does, or -1 if it doesn't.
+static int find_dup( sizedarray *lists, int lno, int nlists )
+{
+ for( int i=lno+1; i<nlists; i++ )
+ {
+ if( same_list( &lists[lno], &lists[i] ) )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+// sizedarray lists[nlists] = ...;
+// remove_duplicate_arrays( lists, &nlists );
+// Remove all duplicate arrays from lists[0..nlists-1],
+// modifying both lists[] and the value of nlists.
+static void remove_duplicate_arrays( sizedarray *lists, int *nlists )
+{
+ for( int i=0; i<*nlists; i++ )
+ {
+ // while lists[i] has a duplicate in lists[i+1..]
+ int dup;
+ while( (dup = find_dup( lists, i, *nlists )) != -1 )
+ {
+ if( debug )
+ {
+ printf( "rda: list[%d] is duplicate of list[%d], deleting list[%d]\n",
+ dup, i, dup );
+ }
+ // delete lists[dup] by shifting lists[dup+1..] down
+ for( int j=dup+1; j<*nlists; j++ )
+ {
+ lists[j-1] = lists[j];
+ }
+ (*nlists)--;
+ }
+ }
+}
+
+
+int main( int argc, char **argv )
+{
+ int argno = process_flag_n_m_args( "unique-array", argc, argv,
+ 1, 100000, "array of ints" );
+
+ int numarrays = argc-argno;
+ if( debug )
+ {
+ printf( "debug: numarrays=%d\n", numarrays );
+ }
+ sizedarray *alllists = malloc( numarrays * sizeof(sizedarray) );
+ assert( alllists );
+
+ int i;
+ for( i=argno; i<argc; i++ )
+ {
+ int nel;
+ int *l = parse_int_arg( argv[i], &nel );
+ alllists[i-argno].nel = nel;
+ alllists[i-argno].list = l;
+ }
+ assert( i==argc );
+
+ remove_duplicate_arrays( alllists, &numarrays );
+
+ for( int i=0; i<numarrays; i++ )
+ {
+ int nel = alllists[i].nel;
+ int *l = alllists[i].list;
+ print_int_array( 80, nel, l, ',', stdout );
+ putchar( '\n' );
+ }
+
+ for( int i=0; i<numarrays; i++ )
+ {
+ free( alllists[i].list );
+ }
+ free( alllists );
+
+ return 0;
+}
diff --git a/challenge-183/duncan-c-white/C/parseints.c b/challenge-183/duncan-c-white/C/parseints.c
new file mode 100644
index 0000000000..32d1143de8
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/parseints.c
@@ -0,0 +1,146 @@
+// Simple routines to parse one (or one or more) arguments,
+// looking for individual +ints or comma-separated
+// lists of +ints.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "args.h"
+#include "printarray.h"
+#include "parseints.h"
+
+typedef struct
+{
+ int nel; // current number of elements
+ int maxel; // maximum number of elements allocated
+ int *list; // malloc()d list of integers
+} intlist;
+
+
+//
+// intlist il.. then initialize il.. then:
+// add_one( element, &il );
+//
+static void add_one( int x, intlist *p )
+{
+ if( p->nel > p->maxel )
+ {
+ p->maxel += 128;
+ p->list = realloc( p->list, p->maxel );
+ assert( p->list );
+ }
+ if( debug )
+ {
+ printf( "PIA: appending %d to result at "
+ "pos %d\n", x, p->nel );
+ }
+ p->list[p->nel++] = x;
+}
+
+
+//
+// intlist il.. then initialize il.. then:
+// add_one_arg( argstr, &il );
+//
+static void add_one_arg( char *argstr, intlist *p )
+{
+ int x;
+ if( !check_unsigned_int(argstr,&x) )
+ {
+ fprintf( stderr, "PIA: arg %s must be +int\n", argstr );
+ exit(1);
+ }
+ add_one( x, p );
+}
+
+
+//
+// int nel;
+// int *ilist = parse_int_arg( argstr, &nel );
+// process argstr, extracting either a single int or a
+// comma-separated lists of ints from it,
+// accumulate all integers in a dynarray list, storing the total number
+// of elements in nel. This list must be freed by the caller.
+int *parse_int_arg( char *argstr, int *nel )
+{
+ int *result = malloc( 128 * sizeof(int) );
+ assert( result );
+ intlist il = { 0, 128, result };
+
+ if( debug )
+ {
+ printf( "PIA: parsing ints from arg %s\n", argstr );
+ }
+
+ assert( strlen(argstr) < 1024 );
+ char copy[1024];
+ strcpy( copy, argstr );
+ char *com;
+ char *s;
+ for( s=copy; (com = strchr(s,',')) != NULL; s=com+1 )
+ {
+ *com = '\0';
+ add_one_arg( s, &il );
+ }
+ add_one_arg( s, &il );
+
+ if( debug )
+ {
+ printf( "PIA: final list is " );
+ print_int_array( 80, il.nel, il.list, ',', stdout );
+ putchar( '\n' );
+ }
+
+ *nel = il.nel;
+ return il.list;
+}
+
+
+//
+// int nel;
+// int *ilist = parse_int_args( argc, argv, argno, &nel );
+// process all arguments argv[argno..argc-1], extracting either
+// single ints or comma-separated lists of ints from those arguments,
+// accumulate all integers into a single dynarray list, storing the total
+// number of elements in nel. This list must be freed by the caller.
+int *parse_int_args( int argc, char **argv, int argno, int *nel )
+{
+ int *result = malloc( 128 * sizeof(int) );
+ assert( result );
+ intlist il = { 0, 128, result };
+
+ if( debug )
+ {
+ printf( "PIA: parsing ints from args %d..%d\n", argno, argc-1 );
+ }
+ for( int i=argno; i<argc; i++ )
+ {
+ assert( strlen(argv[i]) < 1024 );
+ char copy[1024];
+ strcpy( copy, argv[i] );
+ char *com;
+ char *s;
+ for( s=copy; (com = strchr(s,',')) != NULL; s=com+1 )
+ {
+ *com = '\0';
+ add_one_arg( s, &il );
+ }
+ add_one_arg( s, &il );
+ }
+
+ add_one( -1, &il );
+
+ if( debug )
+ {
+ printf( "PIA: final list is " );
+ print_int_array( 80, il.nel, il.list, ',', stdout );
+ }
+
+ *nel = il.nel;
+ return il.list;
+}
diff --git a/challenge-183/duncan-c-white/C/parseints.h b/challenge-183/duncan-c-white/C/parseints.h
new file mode 100644
index 0000000000..fd756440a7
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/parseints.h
@@ -0,0 +1,2 @@
+extern int * parse_int_arg( char * argstr, int * nel );
+extern int * parse_int_args( int argc, char ** argv, int argno, int * nel );
diff --git a/challenge-183/duncan-c-white/C/printarray.c b/challenge-183/duncan-c-white/C/printarray.c
new file mode 100644
index 0000000000..ddee597df3
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/printarray.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <string.h>
+
+
+// print_int_array( maxw, nelements, results[], sep, outfile );
+// format results[0..nelements-1] as a <sep> separated
+// list onto outfile with lines <= maxw chars long.
+// produces a whole number of lines of output - without the trailing '\n'
+void print_int_array( int maxw, int nel, int *results, char sep, FILE *out )
+{
+ int linelen = 0;
+ for( int i=0; i<nel; i++ )
+ {
+ char buf[100];
+ sprintf( buf, "%d", results[i] );
+ int len = strlen(buf);
+ if( linelen + len + 2 > maxw )
+ {
+ fputc( '\n', out );
+ linelen = 0;
+ } else if( i>0 )
+ {
+ fputc( ' ', out );
+ linelen++;
+ }
+
+ linelen += len;
+ fprintf( out, "%s", buf );
+ if( i<nel-1 )
+ {
+ fputc( sep, out );
+ linelen++;
+ }
+ }
+ //if( linelen>0 )
+ //{
+ // fputc( '\n', out );
+ //}
+}
diff --git a/challenge-183/duncan-c-white/C/printarray.h b/challenge-183/duncan-c-white/C/printarray.h
new file mode 100644
index 0000000000..40efb83277
--- /dev/null
+++ b/challenge-183/duncan-c-white/C/printarray.h
@@ -0,0 +1 @@
+extern void print_int_array( int maxw, int nel, int * results, char sep, FILE * out );
diff --git a/challenge-183/duncan-c-white/README b/challenge-183/duncan-c-white/README
index e8856e3c8d..8b1fb7e81b 100644
--- a/challenge-183/duncan-c-white/README
+++ b/challenge-183/duncan-c-white/README
@@ -1,51 +1,82 @@
-Task 1: Max Index
+Task 1: Unique Array
-You are given a list of integers.
-Write a script to find the index of the first biggest number in the list.
+You are given list of arrayrefs.
+Write a script to remove the duplicate arrayrefs from the given list.
-Example
+Example 1
-Input: @n = (5, 2, 9, 1, 7, 6)
-Output: 2 (as 3rd element in the list is the biggest number)
+Input: @list = ([1,2], [3,4], [5,6], [1,2])
+Output: ([1,2], [3,4], [5,6])
+Example 2
-Input: @n = (4, 2, 3, 1, 5, 0)
-Output: 4 (as 5th element in the list is the biggest number)
+Input: @list = ([9,1], [3,7], [2,5], [2,5])
+Output: ([9, 1], [3,7], [2,5])
-MY NOTES: nice and easy.
+MY NOTES: nice and easy, only challenge is how to store a representation
+of an entire array as a hash key: obvious option is to join(',',@items)
-GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl
-into C (look in the C directory for that).
+GUEST LANGUAGE: As a bonus, I also had a go at implementing ch-1.pl
+into C (look in the C directory for that). It's not a direct translation
+of the Perl solution because that involves sets of joined strings, instead
+it does it the obvious low-tech way: finding duplicate arrays and deleting
+them.
-Task 2: Common Path
-Submitted by: Julien Fiegehenn
+Task 2: Date Difference
-Given a list of absolute Linux file paths, determine the deepest path
-to the directory that contains all of them.
+You are given two dates, $date1 and $date2 in the format YYYY-MM-DD.
+Write a script to find the difference between the given dates in terms on years and days only.
-Example
+Example 1
-Input:
- /a/b/c/1/x.pl
- /a/b/c/d/e/2/x.pl
- /a/b/c/d/3/x.pl
- /a/b/c/4/x.pl
- /a/b/c/d/5/x.pl
+Input: $date1 = '2019-02-10'
+ $date2 = '2022-11-01'
+Output: 3 years 264 days
-Output:
- /a/b/c
+Example 2
-MY NOTES: that's a really interesting problem, and I like ones related to
-filesystems as I'm a sysadmin. Now, what data structure would be perfect
-to help us answer this question? Is it as simple as "longest common path
-prefix so far", i.e. start as the whole dirname (remove only the file part),
-and then for each new filepath entry check whether the new entry shares the
-whole "longest common path prefix", and if not, repeatedly remove the last
-dir part until it does:-) The point is that the common path only ever gets
-shorter as we process more paths. Will need to ensure that the code also
-handles the / (no common path other than root) case properly.
+Input: $date1 = '2020-09-15'
+ $date2 = '2022-03-29'
+Output: 1 year 195 days
-GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl
-into C (look in the C directory for that).
+Example 3
+
+Input: $date1 = '2019-12-31'
+ $date2 = '2020-01-01'
+Output: 1 day
+
+Example 4
+
+Input: $date1 = '2019-12-01'
+ $date2 = '2019-12-31'
+Output: 30 days
+
+Example 5
+
+Input: $date1 = '2019-12-31'
+ $date2 = '2020-12-31'
+Output: 1 year
+
+Example 6
+Input: $date1 = '2019-12-31'
+ $date2 = '2021-12-31'
+Output: 2 years
+
+Example 7
+
+Input: $date1 = '2020-09-15'
+ $date2 = '2021-09-16'
+Output: 1 year 1 day
+
+Example 8
+
+Input: $date1 = '2019-09-15'
+ $date2 = '2021-09-16'
+Output: 2 years 1 day
+
+MY NOTES: Should be a simple task for Date::Simple or Date::Manup.
+
+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-183/duncan-c-white/inputpaths b/challenge-183/duncan-c-white/inputpaths
deleted file mode 100644
index e0678fd698..0000000000
--- a/challenge-183/duncan-c-white/inputpaths
+++ /dev/null
@@ -1,5 +0,0 @@
-/a/b/c/1/x.pl
-/a/b/c/d/e/2/x.pl
-/a/b/c/d/3/x.pl
-/a/b/c/4/x.pl
-/a/b/c/d/5/x.pl
diff --git a/challenge-183/duncan-c-white/inputpaths2 b/challenge-183/duncan-c-white/inputpaths2
deleted file mode 100644
index 4935ac2148..0000000000
--- a/challenge-183/duncan-c-white/inputpaths2
+++ /dev/null
@@ -1,5 +0,0 @@
-/a/b/c/1/x.pl
-/a/b/c/d/e/2/x.pl
-/a/b/notc/d/3/x.pl
-/a/b/c/4/x.pl
-/a/b/c/d/5/x.pl
diff --git a/challenge-183/duncan-c-white/inputpaths3 b/challenge-183/duncan-c-white/inputpaths3
deleted file mode 100644
index 99e7f9b66f..0000000000
--- a/challenge-183/duncan-c-white/inputpaths3
+++ /dev/null
@@ -1,5 +0,0 @@
-/a/b/c/1/x.pl
-/a/b/c/d/e/2/x.pl
-/a/b/notc/d/3/x.pl
-/a/notb/c/4/x.pl
-/a/b/c/d/5/x.pl
diff --git a/challenge-183/duncan-c-white/inputpaths4 b/challenge-183/duncan-c-white/inputpaths4
deleted file mode 100644
index c1c2925562..0000000000
--- a/challenge-183/duncan-c-white/inputpaths4
+++ /dev/null
@@ -1,5 +0,0 @@
-/a/b/c/1/x.pl
-/a/b/c/d/e/2/x.pl
-/a/b/notc/d/3/x.pl
-/a/notb/c/4/x.pl
-/nota/b/c/d/5/x.pl
diff --git a/challenge-183/duncan-c-white/perl/ch-1.pl b/challenge-183/duncan-c-white/perl/ch-1.pl
new file mode 100755
index 0000000000..56e9a93c83
--- /dev/null
+++ b/challenge-183/duncan-c-white/perl/ch-1.pl
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+#
+# Task 1: Unique Array
+#
+# You are given list of arrayrefs.
+# Write a script to remove the duplicate arrayrefs from the given list.
+#
+# Example 1
+#
+# Input: @list = ([1,2], [3,4], [5,6], [1,2])
+# Output: ([1,2], [3,4], [5,6])
+#
+# Example 2
+#
+# Input: @list = ([9,1], [3,7], [2,5], [2,5])
+# Output: ([9, 1], [3,7], [2,5])
+#
+# MY NOTES: nice and easy, only challenge is how to store a representation
+# of an entire array as a hash key: obvious option is to join(',',@items).
+# Also need to decide how to specify the inputs as command line args, let's
+# go with each list as a single csv item (which means we could cheat and
+# nerver split them into arrays, but we won't..)
+#
+# GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl
+# into C (look in the C directory for that). It's not a direct translation
+# of the Perl solution because that involves sets of joined strings, instead
+# it does it the obvious low-tech way: finding duplicate arrays and deleting
+# them.
+#
+
+use strict;
+use warnings;
+use feature 'say';
+use Getopt::Long;
+use Data::Dumper;
+
+
+my $debug=0;
+die "Usage: unique-array [--debug] comma_sep_list_of_ints+\n"
+ unless GetOptions( "debug"=>\$debug ) && @ARGV>0;
+
+# all arguments are comma-separated lists of ints.
+my @list = map { [ split(/,/, $_) ] } @ARGV;
+
+#die Dumper(\@list);
+
+# filter out duplicate sub-arrays
+my %seen;
+@list = grep { ! $seen{join(',',@$_)}++ } @list;
+
+my $str = join( ', ', map { '['.join(',',@$_).']' } @list );
+say $str;
diff --git a/challenge-183/duncan-c-white/perl/ch-2.pl b/challenge-183/duncan-c-white/perl/ch-2.pl
new file mode 100755
index 0000000000..266dd0ada8
--- /dev/null
+++ b/challenge-183/duncan-c-white/perl/ch-2.pl
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+#
+# Task 2: Date Difference
+#
+# You are given two dates, $date1 and $date2 in the format YYYY-MM-DD.
+# Write a script to find the difference between the given dates in terms
+# of years and days only.
+#
+# Example 1
+#
+# Input: $date1 = '2019-02-10'
+# $date2 = '2022-11-01'
+# Output: 3 years 264 days
+#
+# Example 2
+#
+# Input: $date1 = '2020-09-15'
+# $date2 = '2022-03-29'
+# Output: 1 year 195 days
+#
+# Example 3
+#
+# Input: $date1 = '2019-12-31'
+# $date2 = '2020-01-01'
+# Output: 1 day
+#
+# Example 4
+#
+# Input: $date1 = '2019-12-01'
+# $date2 = '2019-12-31'
+# Output: 30 days
+#
+# Example 5
+#
+# Input: $date1 = '2019-12-31'
+# $date2 = '2020-12-31'
+# Output: 1 year
+#
+# Example 6
+#
+# Input: $date1 = '2019-12-31'
+# $date2 = '2021-12-31'
+# Output: 2 years
+#
+# Example 7
+#
+# Input: $date1 = '2020-09-15'
+# $date2 = '2021-09-16'
+# Output: 1 year 1 day
+#
+# Example 8
+#
+# Input: $date1 = '2019-09-15'
+# $date2 = '2021-09-16'
+# Output: 2 years 1 day
+#
+# MY NOTES: Should be a simple task for Date::Simple or Date::Manup.
+#
+# 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 Data::Dumper;
+use Date::Simple qw(date);
+
+my $debug=0;
+die "Usage: date-diff [--debug] ISODATE1 ISODATE2\n"
+ unless GetOptions( "debug"=>\$debug ) && @ARGV==2;
+
+my $date1 = date(shift);
+my $date2 = date(shift);
+if( $date1 > $date2 )
+{
+ ($date1,$date2) = ($date2,$date1); # swap dates
+}
+
+# first, find number of whole years difference
+# by finding the "date 1 year ahead of date1"
+my $oneyearahead = date($date1->year+1, $date1->month, $date1->day );
+say "debug: oneyearahead=$oneyearahead" if $debug;
+
+my $years = 0;
+while( $date2 >= $oneyearahead )
+{
+ $years++;
+ $date2 = date($date2->year-1, $date2->month, $date2->day );
+ say "debug: years=$years, date1=$date1, date2=$date2" if $debug;
+}
+
+# second, find number of remaining days difference
+my $days = $date2 - $date1;
+
+my $outstr = "";
+$outstr = "$years year " if $years==1;
+$outstr = "$years years " if $years>1;
+$outstr .= "$days day" if $days>0 || $years==0;
+$outstr .= "s" if $days>1;
+say $outstr;