aboutsummaryrefslogtreecommitdiff
path: root/challenge-164
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2022-05-15 22:46:07 +0100
committerGitHub <noreply@github.com>2022-05-15 22:46:07 +0100
commit67e764b855073e925c21c65881b582bfecdc80d9 (patch)
tree43b7d97857ed18a1be5e6ffc626417570dbaacef /challenge-164
parent6fba696922d4c5eebff822aeaa4954e8dd31d45a (diff)
parent17cf0b3db8997736e028d455d99c96cd3e1a9515 (diff)
downloadperlweeklychallenge-club-67e764b855073e925c21c65881b582bfecdc80d9.tar.gz
perlweeklychallenge-club-67e764b855073e925c21c65881b582bfecdc80d9.tar.bz2
perlweeklychallenge-club-67e764b855073e925c21c65881b582bfecdc80d9.zip
Merge pull request #6111 from dcw803/master
imported my solutions to this week's tasks, in Perl and C
Diffstat (limited to 'challenge-164')
-rw-r--r--challenge-164/duncan-c-white/C/Makefile15
-rw-r--r--challenge-164/duncan-c-white/C/README12
-rw-r--r--challenge-164/duncan-c-white/C/ch-1.c93
-rw-r--r--challenge-164/duncan-c-white/C/ch-2-slow.c118
-rw-r--r--challenge-164/duncan-c-white/C/ch-2.c138
-rw-r--r--challenge-164/duncan-c-white/C/primes.c65
-rw-r--r--challenge-164/duncan-c-white/C/primes.h1
-rw-r--r--challenge-164/duncan-c-white/README83
-rw-r--r--challenge-164/duncan-c-white/pascal/Makefile12
-rw-r--r--challenge-164/duncan-c-white/perl/MakePrimes.pm91
-rwxr-xr-xchallenge-164/duncan-c-white/perl/ch-1.pl47
-rwxr-xr-xchallenge-164/duncan-c-white/perl/ch-2.pl82
12 files changed, 687 insertions, 70 deletions
diff --git a/challenge-164/duncan-c-white/C/Makefile b/challenge-164/duncan-c-white/C/Makefile
index 10b4a15841..d1491ca2ae 100644
--- a/challenge-164/duncan-c-white/C/Makefile
+++ b/challenge-164/duncan-c-white/C/Makefile
@@ -1,18 +1,23 @@
-BUILD = ch-1 ch-2
+BUILD = ch-1 ch-2-slow ch-2
CC = gcc
-CFLAGS = #-g
+CFLAGS = -Wall # -g
+LDLIBS = -lm
all: $(BUILD)
clean:
/bin/rm -f $(BUILD) *.o core a.out
-ch-1: ch-1.o
- $(CC) $(CFLAGS) ch-1.o -o ch-1
+primes.o: primes.c
+
+ch-1: ch-1.o primes.o
ch-1.o: ch-1.c
ch-2: ch-2.o
- $(CC) $(CFLAGS) ch-2.o -o ch-2
ch-2.o: ch-2.c
+
+ch-2-slow: ch-2-slow.o
+
+ch-2-slow.o: ch-2-slow.c
diff --git a/challenge-164/duncan-c-white/C/README b/challenge-164/duncan-c-white/C/README
index 074ff9ade8..99f4de30b7 100644
--- a/challenge-164/duncan-c-white/C/README
+++ b/challenge-164/duncan-c-white/C/README
@@ -1,3 +1,13 @@
-Thought I'd also have a go at translating ch-1a.pl and ch-2.pl into C..
+Thought I'd also have a go at translating ch-1.pl and ch-2.pl into C..
Both C versions produce identical (non-debugging) output to my Perl originals
+
+ch-1.c uses primes.[ch], a prime number generator module that I wrote ages ago.
+
+ch-2-slow.c was my original implementation of the ch02.pl in C, but it was
+hopelessly slow - The only tricky issue is how to represent the seen set.
+Here I went for the obvious bitmap approach, but initializing the seen bitmap
+in ishappy() takes a long time.
+
+ch-2.c is the tweaked version which stores the seen set as a "dynamically
+growable bitmap", this is very much faster than ch-2-slow.c
diff --git a/challenge-164/duncan-c-white/C/ch-1.c b/challenge-164/duncan-c-white/C/ch-1.c
new file mode 100644
index 0000000000..44a1f6924c
--- /dev/null
+++ b/challenge-164/duncan-c-white/C/ch-1.c
@@ -0,0 +1,93 @@
+/*
+ * TASK #1 - Prime Palindrome
+ *
+ * GUEST LANGUAGE: THIS IS THE C VERSION OF ch-1.pl.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "primes.h"
+
+
+
+bool debug=false;
+
+
+// int n = process_singlearg( argc, argv );
+// Process the -d flag, and check that there is a single
+// remaining argument, return it's numeric value.
+int 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: prime-palindrome [-d] firstN\n" );
+ exit(1);
+ }
+
+ // element is in argv[arg]
+
+ if( debug )
+ {
+ printf( "debug: remaining argument is in arg=%d, firstn=%s\n",
+ arg, argv[arg] );
+ }
+
+ return atoi( argv[arg] );
+}
+
+
+/*
+ * bool ispal = ispalindrome( x );
+ * Return 1 iff $x is palindromic in base 10, otherwise 0.
+ */
+bool ispalindrome( int x )
+{
+ char str[20];
+ sprintf( str, "%d", x );
+ int len = strlen(str);
+ for( int i=0; i<len/2; i++ )
+ {
+ if( str[i] != str[len-1-i] )
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+int main( int argc, char **argv )
+{
+ int firstn = process_singlearg( argc, argv );
+ int *primes = primes_upto( firstn );
+
+ //bool ispal = ispalindrome( firstn );
+ //printf( "ispal(%d)? %d\n", firstn, (int)ispal );
+
+ printf( "Palindromic primes up to %d\n", firstn );
+ for( int i=0; primes[i] > 0; i++ )
+ {
+ if( ispalindrome( primes[i] ) )
+ {
+ printf( "%d\n", primes[i] );
+ }
+ }
+
+ free( primes );
+ return 0;
+}
diff --git a/challenge-164/duncan-c-white/C/ch-2-slow.c b/challenge-164/duncan-c-white/C/ch-2-slow.c
new file mode 100644
index 0000000000..cf985a9c2b
--- /dev/null
+++ b/challenge-164/duncan-c-white/C/ch-2-slow.c
@@ -0,0 +1,118 @@
+/*
+ * TASK #2 - Happy Numbers
+ *
+ * GUEST LANGUAGE: THIS IS THE C VERSION OF ch-2.pl.
+ * The only tricky issue is how to represent the set of int.
+ * Here I've gone for the obvious bitmap approach, but initializing
+ * the seen bitmap in ishappy() takes a long time, making this hopelessly
+ * slow over all.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <ctype.h>
+
+
+
+bool debug=false;
+
+
+// int n = process_singlearg( argc, argv );
+// Process the -d flag, and check that there is a single
+// remaining argument, return it's numeric value.
+int 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: prime-palindrome [-d] firstN\n" );
+ exit(1);
+ }
+
+ // element is in argv[arg]
+
+ //if( debug )
+ //{
+ // printf( "debug: remaining argument is in arg=%d, firstn=%s\n",
+ // arg, argv[arg] );
+ //}
+
+ return atoi( argv[arg] );
+}
+
+
+static int seen[UINT32_MAX/32]; // bitmap set
+
+
+// bool ishap = ishappy( x );
+// Return 1 iff $x is a happy number (in base 10), otherwise return 0.
+bool ishappy( int x )
+{
+ for( int i=0; i<UINT32_MAX/32; i++ )
+ {
+ seen[i] = 0;
+ }
+ while( x > 1 )
+ {
+ int si = x/32;
+ int sm = x%32;
+ if( (seen[si] & (1<<sm)) )
+ {
+ return 0;
+ }
+ seen[si] |= (1<<sm);
+ //if( debug ) { printf( "debug: x=%d\n", x ); }
+
+ //$x = sum0( map { $_ * $_ } split(//,$x) );
+ int sum=0;
+ while( x>0 )
+ {
+ int digit = x%10;
+ x /= 10;
+ sum += digit*digit;
+ }
+ x = sum;
+ }
+ return 1;
+}
+
+
+int main( int argc, char **argv )
+{
+ int firstn = process_singlearg( argc, argv );
+
+ //printf( "ishappy(%d)? %d\n", firstn, (int)ishappy(firstn) );
+
+ int nfound = 0;
+ for( int n=1; nfound<firstn; n++ )
+ {
+ bool ishap = ishappy(n);
+ if( debug )
+ {
+ char *str = ishap?"":"un";
+ printf( "%d is %shappy\n", n, str );
+ }
+ if( ishap )
+ {
+ nfound++;
+ if( nfound==firstn )
+ {
+ printf( "%d is happy number #%d\n", n, firstn );
+ }
+ }
+ }
+ return 0;
+}
diff --git a/challenge-164/duncan-c-white/C/ch-2.c b/challenge-164/duncan-c-white/C/ch-2.c
new file mode 100644
index 0000000000..c459410062
--- /dev/null
+++ b/challenge-164/duncan-c-white/C/ch-2.c
@@ -0,0 +1,138 @@
+/*
+ * TASK #2 - Happy Numbers
+ *
+ * GUEST LANGUAGE: THIS IS THE C VERSION OF ch-2.pl.
+ * The only tricky issue is how to represent the set of int.
+ * Here I've gone for a slightly sneaky "dynamically growable bitmap approach"
+ * which is very much faster than the original ch-2-slow.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <ctype.h>
+
+
+
+bool debug=false;
+
+
+// int n = process_singlearg( argc, argv );
+// Process the -d flag, and check that there is a single
+// remaining argument, return it's numeric value.
+int 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: prime-palindrome [-d] firstN\n" );
+ exit(1);
+ }
+
+ // element is in argv[arg]
+
+ //if( debug )
+ //{
+ // printf( "debug: remaining argument is in arg=%d, firstn=%s\n",
+ // arg, argv[arg] );
+ //}
+
+ return atoi( argv[arg] );
+}
+
+
+// bool ishap = ishappy( x );
+// Return 1 iff $x is a happy number (in base 10), otherwise return 0.
+bool ishappy( int x )
+{
+ static int Nseen = 1; // entries in seen dynamic array
+
+ int *seen = malloc( Nseen * sizeof(int) );
+ assert( seen != NULL );
+
+ for( int i=0; i<Nseen; i++ )
+ {
+ seen[i] = 0;
+ }
+
+ while( x > 1 )
+ {
+ int si = x/32;
+ int sm = x%32;
+ if( si>Nseen )
+ {
+ int newnseen = si+10;
+ if( debug )
+ {
+ printf( "debug: reallocating seen from size "
+ "%d to size %d\n", Nseen, newnseen );
+ }
+ seen = realloc( seen, newnseen * sizeof(int) );
+ assert( seen != NULL );
+ for( int i=Nseen; i<newnseen; i++ )
+ {
+ seen[i] = 0;
+ }
+ Nseen = newnseen;
+ }
+ if( (seen[si] & (1<<sm)) )
+ {
+ free( seen );
+ return 0;
+ }
+ seen[si] |= (1<<sm);
+ //if( debug ) { printf( "debug: x=%d\n", x ); }
+
+ //$x = sum0( map { $_ * $_ } split(//,$x) );
+ int sum=0;
+ while( x>0 )
+ {
+ int digit = x%10;
+ x /= 10;
+ sum += digit*digit;
+ }
+ x = sum;
+ }
+ free( seen );
+ return 1;
+}
+
+
+int main( int argc, char **argv )
+{
+ int firstn = process_singlearg( argc, argv );
+
+ //printf( "ishappy(%d)? %d\n", firstn, (int)ishappy(firstn) );
+
+ int nfound = 0;
+ for( int n=1; nfound<firstn; n++ )
+ {
+ bool ishap = ishappy(n);
+ if( debug )
+ {
+ char *str = ishap?"":"un";
+ printf( "%d is %shappy\n", n, str );
+ }
+ if( ishap )
+ {
+ nfound++;
+ if( nfound==firstn )
+ {
+ printf( "%d is happy number #%d\n", n, firstn );
+ }
+ }
+ }
+ return 0;
+}
diff --git a/challenge-164/duncan-c-white/C/primes.c b/challenge-164/duncan-c-white/C/primes.c
new file mode 100644
index 0000000000..fa98e30073
--- /dev/null
+++ b/challenge-164/duncan-c-white/C/primes.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+
+
+// int *primes = primes_upto(n);
+// Generate a dynamic array of integers containing
+// all primes up to $n. The array is zero-terminated.
+// The caller should free(primes) when they've finished
+// using it.
+//
+int * primes_upto( int n )
+{
+ bool *isprime = (bool *) malloc( (n+1) * sizeof(bool) );
+ assert( isprime != NULL );
+
+ int i;
+ for( i=1; i<=n; i++ )
+ {
+ isprime[i] = 1; // initially
+ }
+
+ int upper = (int)(sqrt((double)(n)));
+ //printf( "debug: n=%d, upper=%d\n", n, upper );
+ for( i=2; i<=upper; i++ )
+ {
+ if( isprime[i] )
+ {
+ //printf( "debug: crossing out multiples of %d\n", i );
+ int j;
+ for( j=i*i; j<=n; j+=i )
+ {
+ isprime[j] = 0;
+ }
+ }
+ }
+
+ // count how many primes there are
+ int np = 0;
+ for( i=2; i<=n; i++ )
+ {
+ if( isprime[i] )
+ {
+ np++;
+ }
+ }
+
+ // dynamically allocate an array of np+1 ints, and fill it in
+ int *primes = malloc( (np+1) * sizeof(int) );
+ int p = 0;
+ for( i=2; i<=n; i++ )
+ {
+ if( isprime[i] )
+ {
+ primes[p++] = i;
+ }
+ }
+ primes[p] = 0;
+
+ free( isprime );
+
+ return primes;
+}
diff --git a/challenge-164/duncan-c-white/C/primes.h b/challenge-164/duncan-c-white/C/primes.h
new file mode 100644
index 0000000000..f031d1df06
--- /dev/null
+++ b/challenge-164/duncan-c-white/C/primes.h
@@ -0,0 +1 @@
+extern int * primes_upto( int n );
diff --git a/challenge-164/duncan-c-white/README b/challenge-164/duncan-c-white/README
index 8a87ed6df0..06b7a108f4 100644
--- a/challenge-164/duncan-c-white/README
+++ b/challenge-164/duncan-c-white/README
@@ -1,66 +1,45 @@
-TASK #1 - Sum Bitwise Operator
+TASK #1 - Prime Palindrome
-You are given list positive numbers, @n.
+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.
-Write script to calculate the sum of bitwise & operator for all unique pairs.
+MY NOTES: ok. Pretty easy.
-Example 1
+GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl
+into C (look in the C directory for that).
- Input: @n = (1, 2, 3)
- Output: 3
- Since (1 & 2) + (2 & 3) + (1 & 3) => 0 + 2 + 1 => 3.
+TASK #2 - Happy Numbers
-Example 2
+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
- Input: @n = (2, 3, 4)
- Output: 2
+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.
- Since (2 & 3) + (2 & 4) + (3 & 4) => 2 + 0 + 0 => 2.
+Those numbers for which this process end in 1 are happy numbers, while
+those numbers that do not end in 1 are unhappy numbers.
-MY NOTES: ok. Pretty easy. Identify all pairs, xor each, sum results.
- I did ch-1.pl and an optimized ch-1a.pl
+Example
-GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1a.pl
-into C (look in the C directory for that), and then into Pascal
-(look in the pascal directory for that).
+19 is a Happy Number in base 10, as shown:
+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
-TASK #2 - Summations
-
-You are given a list of positive numbers, @n.
-
-Write a script to find out the summations as described below.
-
-Example 1
-
-Input: @n = (1, 2, 3, 4, 5)
-Output: 42
-
- 1 2 3 4 5
- 2 5 9 14
- 5 14 28
- 14 42
- 42
-
-The nth Row starts with the second element of the (n-1)th row.
-The following element is sum of all elements except first element of previous row.
-You stop once you have just one element in the row.
-
-Example 2
-
-Input: @n = (1, 3, 5, 7, 9)
-Output: 70
-
- 1 3 5 7 9
- 3 8 15 24
- 8 23 47
- 23 70
- 70
-
-MY NOTES: hmmm. Terrible explanation, but I think the examples show what
-it means, which is quite easy. Could do that in different languages too:-)
+MY NOTES: seems very easy. Loop detection is surely just a set of values
+that we've already seen.
GUEST LANGUAGE: As a bonus, I also had a go at translating ch-2.pl
-into C (look in the C directory for that), and then into Pascal
-(look in the pascal directory for that).
+into C (look in the C directory for that).
diff --git a/challenge-164/duncan-c-white/pascal/Makefile b/challenge-164/duncan-c-white/pascal/Makefile
deleted file mode 100644
index 109c2a6e86..0000000000
--- a/challenge-164/duncan-c-white/pascal/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-BUILD = ch-1 ch-2
-
-all: $(BUILD)
-
-clean:
- /bin/rm -f $(BUILD) *.o core a.out
-
-ch-1: ch-1.pas
- fpc ch-1.pas
-
-ch-2: ch-2.pas
- fpc ch-2.pas
diff --git a/challenge-164/duncan-c-white/perl/MakePrimes.pm b/challenge-164/duncan-c-white/perl/MakePrimes.pm
new file mode 100644
index 0000000000..7261977c57
--- /dev/null
+++ b/challenge-164/duncan-c-white/perl/MakePrimes.pm
@@ -0,0 +1,91 @@
+#
+# mkprimes module (converted from mkprimes.c)
+#
+
+use strict;
+use warnings;
+use Function::Parameters;
+
+
+my $debug = 0;
+my @foundprimes; # remember all primes we've found..
+
+
+#
+# my @primes = primes_upto( $n );
+# Find all primes up to N; return a list of all such primes
+# (note that 1 is not usually considered a prime)
+#
+fun primes_upto( $n )
+{
+ my @isprime;
+
+ for( my $i=1; $i<=$n; $i++ )
+ {
+ $isprime[$i] = 1; # initially
+ }
+
+ # now sieve the non-primes out..
+ my $upper = int(sqrt($n));
+ printf( "debug: n=%d, upper=%d\n", $n, $upper ) if $debug;
+ for( my $i=2; $i<=$upper; $i++ )
+ {
+ if( $isprime[$i] )
+ {
+ #printf( "debug: crossing out multiples of %d\n", $i );
+ for( my $j=$i*$i; $j<=$n; $j+=$i )
+ {
+ $isprime[$j] = 0;
+ }
+ }
+ }
+
+ # after sieving, extract the primes
+ my @primes = grep { $isprime[$_] } 2..$n;
+
+ # remember them
+ @foundprimes = @primes;
+
+ return @primes;
+}
+
+
+#
+# my @moreprimes = more_primes( $n, $m );
+# Need more primes! Have @foundprimes up to $n, but need
+# to sieve primes from $n+1..$m, so re-sieve, return
+# a list of all new primes (in the range $n+1..$m) that we find.
+#
+fun more_primes( $n, $m )
+{
+ my %isprime;
+
+ print "finding more primes from ", $n+1, "..$m\n";
+
+ for( my $i=$n+1; $i<=$m; $i++ )
+ {
+ $isprime{$i} = 1; # pre-sieving
+ }
+
+ # now sieve the non-primes out..
+ foreach my $prime (@foundprimes)
+ {
+ # find first multiple of $prime > $n
+ my $mult = $prime * (int($n/$prime)+1);
+
+ #print "debug: xo multiples of $prime from $mult to $m\n";
+
+ for( my $j=$mult; $j<=$m; $j+=$prime )
+ {
+ delete $isprime{$j};
+ }
+ }
+
+ # after sieving, extract the primes
+ my @primes = grep { $isprime{$_} } $n+1..$m;
+ push @foundprimes, @primes;
+ return @primes;
+}
+
+
+1;
diff --git a/challenge-164/duncan-c-white/perl/ch-1.pl b/challenge-164/duncan-c-white/perl/ch-1.pl
new file mode 100755
index 0000000000..9f64639fb9
--- /dev/null
+++ b/challenge-164/duncan-c-white/perl/ch-1.pl
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+#
+# TASK #1 - Prime Palindrome
+#
+# 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.
+#
+# MY NOTES: ok. Pretty easy.
+#
+# 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 MakePrimes;
+
+my $debug=0;
+die "Usage: prime-palindromes [--debug] UpToN\n"
+ unless GetOptions( "debug"=>\$debug ) && @ARGV==1;
+my $n = shift;
+
+my @primes = primes_upto( $n );
+
+=pod
+
+=head2 my $ispal = ispalindrome( $x );
+
+Return 1 iff $x is palindromic in base 10, otherwise 0.
+
+=cut
+
+fun ispalindrome( $x )
+{
+ return $x eq reverse($x) ? 1 : 0;
+}
+
+say "Palindromic primes up to $n";
+say for grep { ispalindrome($_) } @primes;
diff --git a/challenge-164/duncan-c-white/perl/ch-2.pl b/challenge-164/duncan-c-white/perl/ch-2.pl
new file mode 100755
index 0000000000..849f007ccd
--- /dev/null
+++ b/challenge-164/duncan-c-white/perl/ch-2.pl
@@ -0,0 +1,82 @@
+#!/usr/bin/perl
+#
+# TASK #2 - Happy Numbers
+#
+# 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
+#
+# 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.
+#
+# Those numbers for which this process end in 1 are happy numbers, while
+# those numbers that do not end in 1 are unhappy numbers.
+#
+# Example
+#
+# 19 is a Happy Number in base 10, as shown:
+#
+# 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
+#
+# MY NOTES: seems very easy. Loop detection is surely just a set of values
+# that we've already seen.
+#
+# 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 List::Util qw(sum0);
+
+my $debug=0;
+die "Usage: happy-numbers [--debug] firstN\n"
+ unless GetOptions( "debug"=>\$debug ) && @ARGV==1;
+my $firstn = shift;
+
+=pod
+
+=head2 my $ishappy = ishappy( $x );
+
+Return 1 iff $x is a happy number (in base 10), otherwise return 0.
+
+=cut
+fun ishappy( $x )
+{
+ my %seen;
+ while( $x > 1 )
+ {
+ return 0 if $seen{$x}++;
+ #say "debug: x=$x" if $debug;
+ $x = sum0( map { $_ * $_ } split(//,$x) );
+ }
+ return 1;
+}
+
+#say ishappy( 19 );
+my $nfound = 0;
+for( my $n=1; $nfound<$firstn; $n++ )
+{
+ my $ishappy = ishappy($n);
+ if( $debug )
+ {
+ my $str = $ishappy?"":"un";
+ say "$n is ${str}happy";
+ }
+ next unless $ishappy;
+ $nfound++;
+ say "$n is happy number #${firstn}" if $nfound==$firstn;
+}