aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordcw <d.white@imperial.ac.uk>2022-08-28 23:46:27 +0100
committerdcw <d.white@imperial.ac.uk>2022-08-28 23:46:27 +0100
commit023cace3dc87483c840617e2a2a85df30d2c2817 (patch)
treed46d5717c7af6bb8e6a95e2ac407dd82807edb9b
parent3a88c896214c8a3d239ac059a176373fe717b63b (diff)
downloadperlweeklychallenge-club-023cace3dc87483c840617e2a2a85df30d2c2817.tar.gz
perlweeklychallenge-club-023cace3dc87483c840617e2a2a85df30d2c2817.tar.bz2
perlweeklychallenge-club-023cace3dc87483c840617e2a2a85df30d2c2817.zip
just had a go at task 1, nice challenge, did it in C and Perl. as to the 2nd task, I'm not a fan of Unicode, I don't know what a "Sparkline" is, and I'm tired:-)
-rw-r--r--challenge-179/duncan-c-white/C/.cbuild3
-rw-r--r--challenge-179/duncan-c-white/C/Makefile12
-rw-r--r--challenge-179/duncan-c-white/C/README8
-rw-r--r--challenge-179/duncan-c-white/C/args.c207
-rw-r--r--challenge-179/duncan-c-white/C/args.h11
-rw-r--r--challenge-179/duncan-c-white/C/ch-1.c261
-rw-r--r--challenge-179/duncan-c-white/README51
-rwxr-xr-xchallenge-179/duncan-c-white/perl/ch-1.pl156
8 files changed, 674 insertions, 35 deletions
diff --git a/challenge-179/duncan-c-white/C/.cbuild b/challenge-179/duncan-c-white/C/.cbuild
index aebcd2040a..624a95ebfb 100644
--- a/challenge-179/duncan-c-white/C/.cbuild
+++ b/challenge-179/duncan-c-white/C/.cbuild
@@ -1,3 +1,4 @@
-BUILD = ch-1 ch-2
+BUILD = ch-1
CFLAGS = -Wall -g
+#LDFLAGS = -lm
#CFLAGS = -g
diff --git a/challenge-179/duncan-c-white/C/Makefile b/challenge-179/duncan-c-white/C/Makefile
new file mode 100644
index 0000000000..3574b1eed4
--- /dev/null
+++ b/challenge-179/duncan-c-white/C/Makefile
@@ -0,0 +1,12 @@
+BUILD = ch-1
+CC = gcc
+CFLAGS = -Wall -g
+#LDLIBS = -lm
+
+all: $(BUILD)
+
+clean:
+ /bin/rm -f $(BUILD) *.o core a.out
+
+ch-1: ch-1.o args.o
+ch-1.o: ch-1.c args.h
diff --git a/challenge-179/duncan-c-white/C/README b/challenge-179/duncan-c-white/C/README
new file mode 100644
index 0000000000..12ccf8a6e3
--- /dev/null
+++ b/challenge-179/duncan-c-white/C/README
@@ -0,0 +1,8 @@
+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 originals.
+
+It uses the command-line argument processing module args.[ch].
+
+It was very straightforward to translate, it's just that we have to write
+a lot more low-level support code that Perl does for us..
diff --git a/challenge-179/duncan-c-white/C/args.c b/challenge-179/duncan-c-white/C/args.c
new file mode 100644
index 0000000000..d4a2d38b9a
--- /dev/null
+++ b/challenge-179/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-179/duncan-c-white/C/args.h b/challenge-179/duncan-c-white/C/args.h
new file mode 100644
index 0000000000..8844a8f9c4
--- /dev/null
+++ b/challenge-179/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-179/duncan-c-white/C/ch-1.c b/challenge-179/duncan-c-white/C/ch-1.c
new file mode 100644
index 0000000000..8b51c26db2
--- /dev/null
+++ b/challenge-179/duncan-c-white/C/ch-1.c
@@ -0,0 +1,261 @@
+//
+// Task 1: Ordinal Number Spelling
+//
+// C version.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "args.h"
+
+
+// rules to deal with "formwords"..
+
+char *oneto19[] = {
+ "one", "two", "three", "four", "five", "six", "seven", "eight",
+ "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
+ };
+
+char *tens[] = {
+ "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
+ "eighty", "ninety"
+ };
+
+
+// char words[1000];
+// inner_formwords( n, words );
+// Generate the English form of n, writing it into words.
+// eg if n=17, set words to "seventeen".
+void inner_formwords( int n, char *words )
+{
+ if( n < 20 )
+ {
+ strcpy( words, oneto19[n-1] );
+ return;
+ }
+
+ if( n < 100 )
+ {
+ char *t = tens[n/10-2];
+ if( debug )
+ {
+ printf( "debug: n=%d, tens=%s\n", n, t );
+ }
+ strcpy( words, t );
+ if( n % 10 != 0 )
+ {
+ strcat( words, "-" );
+ strcat( words, oneto19[n%10-1] );
+ }
+ return;
+ }
+
+ if( n < 1000 )
+ {
+ int mod = n%100;
+ n /= 100;
+ strcpy( words, oneto19[n-1] );
+ strcat( words, "-hundred" );
+ if( mod != 0 )
+ {
+ strcat( words, "-and-" );
+ inner_formwords( mod, words+strlen(words) );
+ }
+ return;
+ }
+
+ if( n < 1000000 )
+ {
+ int mod = n%1000;
+ n /= 1000;
+ inner_formwords( n, words );
+ strcat( words, "-thousand" );
+ if( mod != 0 )
+ {
+ strcat( words, "-and-" );
+ inner_formwords(mod, words+strlen(words) );
+ }
+ return;
+ }
+}
+
+
+// remove_extra_ands( words );
+// If the string words contains more than one "-and-",
+// remove excess "and-"s leaving only the last.
+//
+void remove_extra_ands( char *words )
+{
+ for(;;)
+ {
+ // locate the first -and-, if any
+ char *fst = strstr( words, "-and-" );
+ if( fst == NULL ) return;
+
+ // locate the second -and-, if any
+ char *snd = strstr( fst+1, "-and-" );
+ if( snd == NULL ) return;
+
+ if( debug )
+ {
+ printf( "found two -and-s in %s\n", words );
+ }
+
+ // remove the first
+ char *d = fst+1;
+ for( char *s = fst+5; *s; *d++ = *s++ )
+ {
+ /*EMPTY*/
+ }
+ *d = '\0';
+
+ if( debug )
+ {
+ printf( "removed the first leaving %s\n", words );
+ }
+
+ }
+}
+
+
+//
+// char words[1000];
+// formwords( n, words );
+// Generate the English form of n, writing it into words,
+// checking the range of n, and removing surplus "-and-"s.
+// eg if n=17, set words to "seventeen".
+//
+void formwords( int n, char *words )
+{
+ if( n < 1 )
+ {
+ fprintf( stderr, "formwords: n (%d) must be > 0\n", n );
+ exit(1);
+ }
+ if( n >= 1000000 )
+ {
+ fprintf( stderr, "formwords: n (%d) must be < 1000000\n", n );
+ exit(1);
+ }
+
+ inner_formwords( n, words );
+
+ remove_extra_ands( words );
+}
+
+
+// rules to do with "nth" logic
+
+// a useful type..
+typedef struct { char *from; char *to; } pair;
+
+
+// special cases, eg "one" -> "first"..
+pair special[] = {
+ { "one", "first" },
+ { "two", "second" },
+ { "three", "third" },
+ { "five", "fifth" },
+ { "eight", "eighth" },
+ { "twelve", "twelfth" },
+};
+
+
+#define NSPECIALS (sizeof(special)/sizeof(special[0]))
+
+
+//
+// char *spec = lookup_special( word );
+// Lookup word to see if it's special;
+// return NULL if it's not, or the equivalent
+// of the special word if it is.
+//
+char *lookup_special( char *word )
+{
+ for( int i=0; i < NSPECIALS; i++ )
+ {
+ if( strcmp( special[i].from, word ) == 0 )
+ {
+ return special[i].to;
+ }
+ }
+ return NULL;
+}
+
+
+//
+// char words[1000];
+// nth( n, words );
+// Generate the "nth" English form of n in words,
+// eg if n=17, set words to "seventeenth". Do it using formwords()
+// and then mucking about with the ending..
+//
+void nth( int n, char *words )
+{
+ formwords( n, words );
+
+ char *dash = strchr( words, '-' );
+ char prefix[1000];
+ char lastbit[1000];
+
+ if( dash != NULL )
+ {
+ *dash = '\0';
+ strcpy( prefix, words );
+ strcpy( lastbit, dash+1 );
+ } else
+ {
+ strcpy( prefix, "" );
+ strcpy( lastbit, words );
+ }
+
+ char *spec = lookup_special( lastbit );
+ if( spec != NULL )
+ {
+ strcpy( lastbit, spec );
+ }
+ else
+ {
+ char *last = lastbit+strlen(lastbit)-1;
+ if( *last == 'y' )
+ {
+ strcpy( last, "ie" );
+ }
+ strcat( lastbit, "th" );
+ }
+
+ if( *prefix != '\0' )
+ {
+ sprintf( words, "%s-%s", prefix, lastbit );
+ } else
+ {
+ strcpy( words, lastbit );
+ }
+}
+
+
+int main( int argc, char **argv )
+{
+ int argno = process_flag_n_args( "nth", argc, argv,
+ 1, "N (0..999999)" );
+ char *arg = argv[argno]; // N
+
+ int n;
+ if( !check_unsigned_int(arg,&n) )
+ {
+ fprintf( stderr, "nth: arg %s must be +int\n", arg );
+ exit(1);
+ }
+ char words[1000];
+ //formwords( n, words );
+ nth( n, words );
+ //printf( "n=%d, word=%s\n", n, words );
+ printf( "%s\n", words );
+ return 0;
+}
diff --git a/challenge-179/duncan-c-white/README b/challenge-179/duncan-c-white/README
index 500870a343..635f772de2 100644
--- a/challenge-179/duncan-c-white/README
+++ b/challenge-179/duncan-c-white/README
@@ -1,46 +1,29 @@
-Task 1: Permuted Multiples
+Task 1: Ordinal Number Spelling
-Write a script to find the smallest positive integer x such that x, 2x,
-3x, 4x, 5x and 6x are permuted multiples of each other.
+You are given a positive number, $n.
+Write a script to spell the ordinal number.
-For example, the integers 125874 and 251748 are permutated multiples of
-each other as
-
-251784 = 2 x 125874
-
-and also both have the same digits but in different order.
-
-Output
+For example,
- 142857
+11 => eleventh
+62 => sixty-second
+99 => ninety-ninth
-MY NOTES: ok, sounds pretty easy. Compare permutation either by forming
-bags of digits and comparing them, or simply by sorting the digits
-numerically (a signature) and comparing signatures.
+MY NOTES: ok, sounds pretty easy for n>0; let's arbitrarily deal with
+n < 1000000 (although millions, billions etc would be pretty easy to add).
+Let's do this via formwords(n) which converts (eg) n=17 to "seventeen" and
+then use that to generate the final "seventeeth" (nth, ordinal, form) by
+mucking about with the ending (and using some special case rules for English,
+eg mapping one->first)
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: Reversible Numbers
-
-Write a script to find out all Reversible Numbers below 100.
+Task 2: Unicode Sparkline
-A number is said to be a reversible if sum of the number and its reverse
-had only odd digits.
+You are given a list of positive numbers, @n.
-For example,
+Write a script to print sparkline in Unicode for the given list of numbers.
-36 is reversible number as 36 + 63 = 99 i.e. all digits are odd.
-17 is not reversible as 17 + 71 = 88, none of the digits are odd.
-
-Output
-
-10, 12, 14, 16, 18, 21, 23, 25, 27,
-30, 32, 34, 36, 41, 43, 45, 50, 52,
-54, 61, 63, 70, 72, 81, 90
-
-MY NOTES: Unusually, this seems even easier than task 1.
-
-GUEST LANGUAGE: As a bonus, I also had a go at translating ch-2.pl
-into C (look in the C directory for that).
+MY NOTES: What on earth is "sparkline"? Not a fan of Unicode either, forget it.
diff --git a/challenge-179/duncan-c-white/perl/ch-1.pl b/challenge-179/duncan-c-white/perl/ch-1.pl
new file mode 100755
index 0000000000..4a343ffb73
--- /dev/null
+++ b/challenge-179/duncan-c-white/perl/ch-1.pl
@@ -0,0 +1,156 @@
+#!/usr/bin/perl
+#
+# Task 1: Ordinal Number Spelling
+#
+# You are given a positive number, $n.
+# Write a script to spell the ordinal number.
+#
+# For example,
+#
+# 11 => eleventh
+# 62 => sixty-second
+# 99 => ninety-ninth
+#
+# MY NOTES: ok, sounds pretty easy for n>0; let's arbitrarily deal with
+# n < 1000000 (although millions, billions etc would be pretty easy to add).
+# Let's do this via formwords(n) which converts (eg) n=17 to "seventeen" and
+# then use that to generate the final "seventeeth" by mucking about with the
+# ending (and using some special case rules for English, eg one->first)
+#
+# 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;
+
+
+my $debug=0;
+die "Usage: nth [--debug] N\n"
+ unless GetOptions( "debug"=>\$debug ) && @ARGV==1;
+
+
+# rules to deal with "formwords"..
+
+my @oneto19 = qw(one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen);
+my @tens = qw(twenty thirty forty fifty sixty seventy eighty ninety);
+
+
+#
+# my $count = formwords( $n );
+# Generate and return the English form of $n,
+# eg if n=17, return "seventeen".
+#
+fun formwords( $n )
+{
+ die "formwords: n ($n) must be > 0\n" if $n<1;
+
+ my $count = inner_formwords( $n );
+
+ # let's have no more than one "-and-"..
+ $count =~ s/and-// while $count =~ /-and-.*-and-/;
+
+ return $count;
+}
+
+
+#
+# my $count = inner_formwords( $n );
+# Generate and return the English form of $n,
+# eg if n=17, return "seventeen".
+#
+fun inner_formwords( $n )
+{
+ die "formwords: n ($n) must be > 0\n" if $n<1;
+ return $oneto19[$n-1] if $n<20;
+ if( $n < 100 )
+ {
+ my $tens = $tens[int($n/10)-2];
+ say "debug: n=$n, tens=$tens" if $debug;
+ if( $n % 10 == 0 )
+ {
+ return $tens;
+ }
+ my $units = $oneto19[$n%10-1];
+ return "$tens-$units";
+ }
+ if( $n < 1000 )
+ {
+ my $mod = $n%100;
+ $n = int($n/100);
+ my $hundreds = $oneto19[$n-1];
+ return "$hundreds-hundred" if $mod==0;
+ my $fw = inner_formwords($mod);
+ return "$hundreds-hundred-and-$fw";
+ }
+ if( $n < 1000000 )
+ {
+ my $mod = $n%1000;
+ $n = int($n/1000);
+ my $thousands = inner_formwords($n);
+ my $result = "$thousands-thousand";
+ return $result if $mod==0;
+
+ $result .= "-and-". inner_formwords($mod);
+ return $result;
+ }
+ return "$n >= 1000000 to do";
+}
+
+
+my %special = qw(one first two second three third five fifth
+ eight eighth twelve twelfth);
+
+#
+# my $nth = nth( $n );
+# Generate and return the "nth" English form of $n,
+# eg if n=17, return "seventeenth". Do it using formwords()
+# and then mucking about with the ending..
+#
+fun nth( $n )
+{
+ my $fw = formwords( $n );
+
+ my( $prefix, $lastbit );
+ if( $fw =~ /^(.+)-([^-]+)$/ )
+ {
+ $prefix = $1;
+ $lastbit = $2;
+ } else
+ {
+ $prefix = "";
+ $lastbit = $fw;
+ }
+
+ if( defined $special{$lastbit} )
+ {
+ $lastbit = $special{$lastbit};
+ }
+ else
+ {
+ $lastbit =~ s/y$/ie/;
+ $lastbit .= "th";
+ }
+
+ if( $prefix )
+ {
+ return "$prefix-$lastbit";
+ } else
+ {
+ return $lastbit;
+ }
+}
+
+
+
+my $n = shift;
+die "nth: n ($n) must be > 0 and < 1,000,000\n" if $n<=0 || $n>1000000;
+
+#my $count = formwords( $n );
+#say $count;
+my $nth = nth($n);
+say $nth;