aboutsummaryrefslogtreecommitdiff
path: root/challenge-167
diff options
context:
space:
mode:
authorPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2022-06-05 19:48:01 +1000
committerPerlMonk-Athanasius <PerlMonk.Athanasius@gmail.com>2022-06-05 19:48:01 +1000
commitd8391aa1e90bd8db6046b3d25421ab75a840dbeb (patch)
tree67d274c44d0d07b5404266c973ec78fd55674c44 /challenge-167
parent18a163895ca6d1cc72f209a9a9ba6a4713ab8ebc (diff)
downloadperlweeklychallenge-club-d8391aa1e90bd8db6046b3d25421ab75a840dbeb.tar.gz
perlweeklychallenge-club-d8391aa1e90bd8db6046b3d25421ab75a840dbeb.tar.bz2
perlweeklychallenge-club-d8391aa1e90bd8db6046b3d25421ab75a840dbeb.zip
Perl & Raku solutions to Tasks 1 & 2 for Week 167
Diffstat (limited to 'challenge-167')
-rw-r--r--challenge-167/athanasius/perl/ch-1.pl285
-rw-r--r--challenge-167/athanasius/perl/ch-2.pl326
-rw-r--r--challenge-167/athanasius/raku/ch-1.raku293
-rw-r--r--challenge-167/athanasius/raku/ch-2.raku296
4 files changed, 1200 insertions, 0 deletions
diff --git a/challenge-167/athanasius/perl/ch-1.pl b/challenge-167/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..f15ab60584
--- /dev/null
+++ b/challenge-167/athanasius/perl/ch-1.pl
@@ -0,0 +1,285 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 167
+=========================
+
+TASK #1
+-------
+*Circular Prime*
+
+Submitted by: Mohammad S Anwar
+
+Write a script to find out first 10 circular primes having at least 3 digits
+(base 10).
+
+Please checkout [ https://en.wikipedia.org/wiki/Circular_prime |wikipedia] for
+more information.
+
+
+ A circular prime is a prime number with the property that the number
+ generated at each intermediate step when cyclically permuting its (base
+ 10) digits will also be prime.
+
+
+Output
+
+ 113, 197, 199, 337, 1193, 3779, 11939, 19937, 193939, 199933
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+Note
+----
+There are 2 definitions of "circular primes":
+
+ 1. "Numbers such that every cyclic permutation is a prime" [2]:
+ 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, 97, 113, 131, 197, 199, ...
+ 2. As in 1, except that "Only the smallest member of the cyclic shift is
+ listed." [1]: 2, 3, 5, 7, 11, 13, 17, 37, 79, 113, 197, 199, 337, 1193, ...
+
+From the output shown in the Task description, it is evident that the circular
+primes required here are those which satisfy definition 2.
+
+Algorithm
+---------
+In base 10, all circular primes of 2 or more digits must be composed of the
+digits 1, 3, 7, or 9 only.
+ Rationale:
+ any 2+ -digit integer ending in 0 is divisible by 2 and 5;
+ any 2+ -digit integer ending in 2, 4, 6, or 8 is divisible by 2;
+ any 2+ -digit integer ending in 5 is divisible by 5;
+ and in any cycle, each digit must appear at least once in final position.
+
+Accordingly, the algorithm begins by *constructing* possible solutions (i.e.,
+integers containing just those digits):
+
+1. Let circ_primes = ()
+2. FOR d = 3, 4, 5, ... UNTIL circ_primes contains 10 elements
+ 3. Find all combinations of d digits drawn from the set {1, 3, 7, 9}.
+ As these are *combinations*, order is unimportant. For convenience,
+ the digits in each combination are sorted in ascending numerical
+ order.
+ 4. Filter out combinations containing all 3s, all 7s, and all 9s.
+ Rationale: Any integer of 2 or more digits in which the digits
+ are all 3, 7, or 9 is divisible by the repunit of the same
+ length. For example, 7777 = 1111 * 7.
+ 5. FOR each combination c
+ 6. Generate all the permutations of the digits in c
+ 7. WHILE there are permutations remaining
+ 8. Remove the first permutation and store as p
+ 9. Generate the cycle of integers beginning with p
+ 10. If every member of the cycle is prime, store p in
+ circ_primes
+ 11. Remove all members of the cycle from the remaining
+ permutations
+ ENDWHILE
+ ENDFOR
+ ENDFOR
+12. Display the contents of circ_primes
+
+Table of Circular Primes
+------------------------
+From [1]: 2, 3, 5, 7, 11, 13, 17, 37, 79,
+ 113, 197, 199, 337, 1193, 3779, 11939, 19937, 193939, 199933,
+ 1111111111111111111, 11111111111111111111111
+
+References
+----------
+[1] "A016114 Circular primes (numbers that remain prime under cyclic shifts of
+ digits).", OEIS, https://oeis.org/A016114
+[2] "A068652 Numbers such that every cyclic permutation is a prime.", OEIS,
+ https://oeis.org/A068652
+[3] "Circular prime", Wikipedia, https://en.wikipedia.org/wiki/Circular_prime
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Algorithm::Loops qw( NestedLoops NextPermute );
+use Const::Fast;
+use List::MoreUtils qw( bremove );
+use Math::Prime::Util qw( is_prime );
+
+const my $MIN_DIGITS => 3;
+const my $TARGET => 10;
+const my $USAGE => "Usage:\n perl $0\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 167, Task #1: Circular Prime (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my $args = scalar @ARGV;
+ $args == 0 or die 'ERROR: Expected 0 command line arguments, found ' .
+ "$args\n$USAGE";
+
+ my @circ_primes;
+
+ L_OUTER: for (my $digits = $MIN_DIGITS; ; ++$digits)
+ {
+ my $combs = get_combinations( $digits );
+ $combs = filter_reps( $combs );
+
+ for my $comb (@$combs)
+ {
+ my $new_circ_primes = find_circular_primes( $comb );
+
+ push @circ_primes, @$new_circ_primes;
+
+ last L_OUTER if scalar @circ_primes >= $TARGET;
+ }
+ }
+
+ printf "Output: %s\n", join ', ', @circ_primes[ 0 .. $TARGET - 1 ];
+}
+
+#------------------------------------------------------------------------------
+sub find_circular_primes
+#------------------------------------------------------------------------------
+{
+ my ($num) = @_;
+ my $perms = get_permutations( $num );
+ my @circ_primes;
+
+ while (scalar @$perms)
+ {
+ my $perm = shift @$perms;
+ my $cycle = get_cycle( $perm );
+ my $all_prime = 1;
+
+ for my $n (@$cycle)
+ {
+ unless (is_prime( $n ))
+ {
+ $all_prime = 0;
+ last;
+ }
+ }
+
+ push @circ_primes, $perm if $all_prime;
+
+ for my $n (@$cycle)
+ {
+ bremove { $_ <=> $n } @$perms;
+ }
+ }
+
+ return \@circ_primes;
+}
+
+#------------------------------------------------------------------------------
+sub get_permutations
+#------------------------------------------------------------------------------
+{
+ my ($n) = @_;
+ my @digits = split '', $n; # Note: the digits are already sorted (asc.)
+ my @perms;
+
+ do
+ {
+ my $perm = join '', @digits;
+
+ push @perms, $perm;
+
+ } while (NextPermute( @digits ));
+
+ return \@perms;
+}
+
+#------------------------------------------------------------------------------
+sub get_cycle # E.g., 137 -> 713 -> 371 [-> 137]
+#------------------------------------------------------------------------------
+{
+ my ($first) = @_;
+ my @cycle = $first;
+ my @digits = split '', $first;
+ my $next;
+
+ do
+ {
+ my $last = pop @digits;
+ @digits = ($last, @digits);
+ $next = join '', @digits;
+
+ push @cycle, $next;
+
+ } until ($next eq $first);
+
+ pop @cycle;
+
+ return \@cycle;
+}
+
+#------------------------------------------------------------------------------
+sub get_combinations
+#------------------------------------------------------------------------------
+{
+ my ($depth) = @_;
+ my @combs;
+
+ NestedLoops
+ (
+ [
+ [ 1, 3, 7, 9 ],
+ (
+ sub
+ {
+ $_ == 1 ? [ 1, 3, 7, 9 ] :
+ $_ == 3 ? [ 3, 7, 9 ] :
+ $_ == 7 ? [ 7, 9 ] :
+ [ 9 ];
+ }
+
+ ) x ($depth - 1),
+ ],
+ sub
+ {
+ push @combs, join '', @_;
+ },
+ );
+
+ return \@combs;
+}
+
+#------------------------------------------------------------------------------
+sub filter_reps # Remove 333, 777, 999, 3333, ..., but NOT 111, 1111, ...
+#------------------------------------------------------------------------------
+{
+ my ($combs) = @_;
+ my @filtered;
+
+ for my $comb (@$combs)
+ {
+ my @digits = split '', $comb;
+ my %count;
+
+ ++$count{ $_ } for @digits;
+
+ if (scalar( keys %count ) > 1 || exists $count{ '1' })
+ {
+ push @filtered, $comb;
+ }
+ }
+
+ return \@filtered;
+}
+
+###############################################################################
diff --git a/challenge-167/athanasius/perl/ch-2.pl b/challenge-167/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..d56f2156f1
--- /dev/null
+++ b/challenge-167/athanasius/perl/ch-2.pl
@@ -0,0 +1,326 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 167
+=========================
+
+TASK #2
+-------
+*Gamma Function*
+
+Submitted by: Mohammad S Anwar
+
+Implement subroutine gamma() using the [ https://en.wikipedia.org/wiki/Lanczos_
+approximation |Lanczos approximation] method.
+
+[2022-05-31]
+ Ryan Thompson wrote an interesting blog explaining the subject in details.
+ Highly recommended if you are looking for more information. [ https://ry.ca/
+ 2022/ 05/lanczos-approximation |BLOG].
+
+Example
+
+ print gamma(3); # 1.99
+ print gamma(5); # 24
+ print gamma(7); # 719.99
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+Command-Line Interface
+----------------------
+2 arguments: Real and imaginary parts of the complex argument to the gamma
+ function
+1 argument: Real part as above; the imaginary part defaults to zero
+0 arguments: Run the test suite (expected values obtained from [4] and [2]).
+
+Algorithm
+---------
+A port from Python to Perl of the "Simple implementation" in [3]. Note: I have
+departed from the Task specification by implementing a separate factorial
+calculation for positive integers. This gives more accurate results: e.g.,
+gamma(3) = 2 (which is exactly correct) rather than 1.99 as in the Example.
+
+References
+----------
+[1] "Gamma function", Wikipedia, https://en.wikipedia.org/wiki/Gamma_function
+[2] "Gamma function Calculator", ke!san Online Calculator,
+ https://keisan.casio.com/exec/system/1180573444
+[3] "Lanczos approximation", Wikipedia, https://en.wikipedia.org/wiki/Lanczos_
+ approximation
+[4] "Particular values of the gamma function", Wikipedia,
+ https://en.wikipedia.org/wiki/Particular_values_of_the_gamma_function
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use Devel::Assert qw( on );
+use Math::Complex qw( cplx Inf pi );
+use Regexp::Common;
+
+const my $ACCURACY => 13; # Significant digits
+const my $EPSILON => 1e-12;
+const my $INIT_X => 0.99999999999980993;
+const my @P_COEFS =>
+ (
+ 676.5203681218851,
+ -1259.1392167224028,
+ 771.32342877765313,
+ -176.61502916214059,
+ 12.507343278686905,
+ -0.13857109526572012,
+ 9.9843695780195716e-6,
+ 1.5056327351493116e-7,
+ );
+const my $COMPLEX_RE => qr/ ($RE{num}{real}) ([+-]) ($RE{num}{real}) i /x;
+const my $USAGE =>
+"Usage:
+ perl $0 [<re>] [<im>]
+
+ [<re>] Real part
+ [<im>] Imaginary part [default: 0]\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 167, Task #2: Gamma Function (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my $z = parse_command_line();
+
+ if (defined $z)
+ {
+ assert( ref $z eq 'Math::Complex' );
+
+ printf "gamma(%s) = %s\n", $z, gamma( $z );
+ }
+ else
+ {
+ test();
+ }
+}
+
+#------------------------------------------------------------------------------
+sub gamma
+#------------------------------------------------------------------------------
+{
+ my ($z) = @_;
+
+ assert( ref $z eq 'Math::Complex' );
+
+ my $y = cplx( Inf, 0 ); # Assume failure (use Inf to represent NaN)
+
+ if (is_int( $z ))
+ {
+ # Calculate the factorial directly, for better accuracy
+
+ $y = cplx( factorial( $z->Re - 1 ), 0 ) if $z->Re > 0;
+ }
+ elsif ($z->Re < 0.5) # Reflexion: recursive call
+ {
+ $y = pi / (sin( pi * $z ) * gamma( 1 - $z ));
+ }
+ else # Apply the Lanczos approximation
+ {
+ $z -= 1;
+ my $x = $INIT_X;
+ my $i = 0;
+
+ $x += $_ / ($z + $i++ + 1) for @P_COEFS;
+
+ my $t = $z + scalar( @P_COEFS ) - 0.5;
+ $y = sqrt( 2 * pi ) * $t ** ($z + 0.5) * exp( -$t ) * $x;
+ }
+
+ $y = cplx( $y->Re, 0 ) if abs( $y->Im ) <= $EPSILON; # Drop imag. part
+
+ return $y;
+}
+
+#------------------------------------------------------------------------------
+sub is_int
+#------------------------------------------------------------------------------
+{
+ my ($z) = @_;
+
+ assert( ref $z eq 'Math::Complex' );
+
+ my $real = $z->Re;
+
+ return abs( $z->Im ) < $EPSILON &&
+ abs( $real - int $real ) < $EPSILON;
+}
+
+#------------------------------------------------------------------------------
+sub factorial
+#------------------------------------------------------------------------------
+{
+ my ($n) = @_;
+
+ assert( $n >= 0 );
+
+ my $fact = 1;
+
+ for my $i (2 .. $n)
+ {
+ $fact *= $i;
+ }
+
+ return $fact;
+}
+
+#------------------------------------------------------------------------------
+sub test
+#------------------------------------------------------------------------------
+{
+ require Test::More;
+ Test::More->import;
+
+ while (my $line = <DATA>)
+ {
+ chomp $line;
+ $line =~ s/ ^ \s+ //x;
+ $line =~ s/ \s+ $ //x;
+
+ my ($inp, $exp) = split / \s+ /x, $line, 2;
+ my $z_inp = extract_cplx( $inp );
+ my $z_exp = ($exp eq 'NaN') ? cplx( Inf ) : extract_cplx( $exp );
+ my $z_got = round( gamma( $z_inp ) );
+ $z_exp = round( $z_exp );
+
+ ok( cplx_eq( $z_got, $z_exp ), "gamma($z_inp)" )
+ or printf "Expected %s,\n Got %s\n", $z_exp, $z_got;
+ }
+
+ done_testing();
+}
+
+#------------------------------------------------------------------------------
+sub extract_cplx
+#------------------------------------------------------------------------------
+{
+ my ($str) = @_;
+
+ my ($re, $sign, $im) = $str =~ $COMPLEX_RE;
+
+ $im = -$im if $sign eq '-';
+
+ return cplx( $re, $im );
+}
+
+#------------------------------------------------------------------------------
+sub round
+#------------------------------------------------------------------------------
+{
+ my ($z) = @_;
+
+ assert( ref $z eq 'Math::Complex' );
+
+ my $real = sprintf '%.*f', $ACCURACY - length int( $z->Re ), $z->Re;
+ my $imag = sprintf '%.*f', $ACCURACY - length int( $z->Im ), $z->Im;
+
+ $real =~ s/ \. .* (0+) $ //x; # Trim any trailing "0" decimals
+ $imag =~ s/ \. .* (0+) $ //x;
+ $real =~ s/ \. $ //x; # Trim final decimal point (if any)
+ $imag =~ s/ \. $ //x;
+
+ return cplx( $real, $imag );
+}
+
+#------------------------------------------------------------------------------
+sub cplx_eq
+#------------------------------------------------------------------------------
+{
+ my ($za, $zb) = @_;
+
+ assert( ref $za eq 'Math::Complex' &&
+ ref $zb eq 'Math::Complex' );
+
+ return ( $za->Re == Inf && $zb->Re == Inf ) ||
+ (abs( $za->Re - $zb->Re ) < $EPSILON &&
+ abs( $za->Im - $zb->Im ) < $EPSILON);
+}
+
+#------------------------------------------------------------------------------
+sub parse_command_line
+#------------------------------------------------------------------------------
+{
+ my $args = scalar @ARGV;
+ 0 <= $args <= 2
+ or error( "Expected 0, 1, or 2 command line arguments, found $args" );
+
+ return if $args == 0;
+
+ my $re = $ARGV[ 0 ];
+
+ $re =~ / ^ $RE{num}{real} $ /x
+ or error( qq[Real part "$re" is not a valid real number] );
+
+ my $im = ($args == 2) ? $ARGV[ 1 ] : 0;
+
+ $im =~ / ^ $RE{num}{real} $ /x
+ or error( qq[Imaginary part "$im" is not a valid real number] );
+
+ return cplx( $re, $im );
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+###############################################################################
+
+__DATA__
+ 3+0i 2+0i
+ 5+0i 24+0i
+ 7+0i 720+0i
+ 1+0i 1+0i
+ 2+0i 1+0i
+ 4+0i 6+0i
+ 0+0i NaN
+-1+0i NaN
+ 0.5+0i 1.77245385090551+0i
+ 1.5+0i 0.88622692545275+0i
+ 2.5+0i 1.32934038817913+0i
+ 3.5+0i 3.32335097044784+0i
+-0.5+0i -3.54490770181103+0i
+-1.5+0i 2.36327180120735+0i
+-2.5+0i -0.94530872048294+0i
+ 0+1i -0.15494982830181-0.49801566811835i
+ 1+1i 0.49801566811835-0.15494982830181i
+ 1-1i 0.49801566811835+0.15494982830181i
+ 0.5+0.5i 0.81816399954174-0.76331382871398i
+ 0.5-0.5i 0.81816399954174+0.76331382871398i
+ 5+3i 0.01604188274165-9.43329328975598i
+ 5-3i 0.01604188274165+9.43329328975598i
+ 1.46163214496836+0i 0.88560319441088+0i
+ 0.75+0.75i 0.59665430268224-0.37676090720240i
+ 0.75-2.75i 0.03902232213674-0.01782079536849i
+ 3.14159265358979+3.14159265358979i -0.42793915904009-0.21380749181397i
+10+0i 362880+0i
+12-11.5i -60802.08246207147008+249916.64966162368028i
+ 0.33333333333333+0i 2.67893853470774+0i
+ 0.14285714285714+0i 6.54806294024782+0i
diff --git a/challenge-167/athanasius/raku/ch-1.raku b/challenge-167/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..23d73befdd
--- /dev/null
+++ b/challenge-167/athanasius/raku/ch-1.raku
@@ -0,0 +1,293 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 167
+=========================
+
+TASK #1
+-------
+*Circular Prime*
+
+Submitted by: Mohammad S Anwar
+
+Write a script to find out first 10 circular primes having at least 3 digits
+(base 10).
+
+Please checkout [ https://en.wikipedia.org/wiki/Circular_prime |wikipedia] for
+more information.
+
+
+ A circular prime is a prime number with the property that the number
+ generated at each intermediate step when cyclically permuting its (base
+ 10) digits will also be prime.
+
+
+Output
+
+ 113, 197, 199, 337, 1193, 3779, 11939, 19937, 193939, 199933
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=begin comment
+
+Note
+----
+There are 2 definitions of "circular primes":
+
+ 1. "Numbers such that every cyclic permutation is a prime" [2]:
+ 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, 97, 113, 131, 197, 199, ...
+ 2. As in 1, except that "Only the smallest member of the cyclic shift is
+ listed." [1]: 2, 3, 5, 7, 11, 13, 17, 37, 79, 113, 197, 199, 337, 1193, ...
+
+From the output shown in the Task description, it is evident that the circular
+primes required here are those which satisfy definition 2.
+
+Algorithm
+---------
+In base 10, all circular primes of 2 or more digits must be composed of the
+digits 1, 3, 7, or 9 only.
+ Rationale:
+ any 2+ -digit integer ending in 0 is divisible by 2 and 5;
+ any 2+ -digit integer ending in 2, 4, 6, or 8 is divisible by 2;
+ any 2+ -digit integer ending in 5 is divisible by 5;
+ and in any cycle, each digit must appear at least once in final position.
+
+Accordingly, the algorithm begins by *constructing* possible solutions (i.e.,
+integers containing just those digits):
+
+1. Let circ_primes = ()
+2. Find all combinations of 3-, 4-, 5-, and 6-digits drawn from the set
+ {1, 3, 7, 9}. Note: as these are *combinations*, order is unimportant. For
+ convenience, the digits in each combination are sorted in ascending numeri-
+ cal order.
+3. Filter out combinations containing all 3s, all 7s, and all 9s.
+ Rationale: Any integer of 2 or more digits in which the digits are all
+ 3, 7, or 9 is divisible by the repunit of the same length.
+ For example, 7777 = 1111 * 7.
+4. FOR each combination c
+ 5. Generate all the permutations of the digits in c
+ 6. WHILE there are permutations remaining
+ 7. Remove the first permutation and store as p
+ 8. Generate the cycle of integers beginning with p
+ 9. If every member of the cycle is prime, store p in
+ circ_primes
+ 10. Remove all members of the cycle from the remaining permuta-
+ tions
+ ENDWHILE
+ ENDFOR
+11. Display the contents of circ_primes
+
+Table of Circular Primes
+------------------------
+From [1]: 2, 3, 5, 7, 11, 13, 17, 37, 79,
+ 113, 197, 199, 337, 1193, 3779, 11939, 19937, 193939, 199933,
+ 1111111111111111111, 11111111111111111111111
+
+References
+----------
+[1] "A016114 Circular primes (numbers that remain prime under cyclic shifts of
+ digits).", OEIS, https://oeis.org/A016114
+[2] "A068652 Numbers such that every cyclic permutation is a prime.", OEIS,
+ https://oeis.org/A068652
+[3] "Circular prime", Wikipedia, https://en.wikipedia.org/wiki/Circular_prime
+
+=end comment
+#==============================================================================
+
+my constant @DIGITS = 1, 3, 7, 9;
+my UInt constant $TARGET = 10;
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 167, Task #1: Circular Prime (Raku)\n".put;
+}
+
+#==============================================================================
+sub MAIN()
+#==============================================================================
+{
+ my UInt @combs = get-combinations();
+ @combs = filter-reps( @combs );
+ my UInt @circ-primes;
+
+ for @combs -> UInt $comb
+ {
+ @circ-primes.push: |find-circular-primes( $comb );
+
+ last if +@circ-primes >= $TARGET;
+ }
+
+ "Output: %s\n".printf: @circ-primes[ 0 .. $TARGET - 1 ].join: ', ';
+}
+
+#------------------------------------------------------------------------------
+sub find-circular-primes( UInt:D $num --> Array:D[UInt:D] )
+#------------------------------------------------------------------------------
+{
+ my UInt @circ-primes;
+ my UInt @perms = get-permutations( $num );
+
+ while @perms
+ {
+ my UInt $perm = @perms.shift;
+ my UInt @cycle = get-cycle( $perm );
+ my Bool $all-prime = True;
+
+ for @cycle -> UInt $n
+ {
+ unless $n.is-prime
+ {
+ $all-prime = False;
+ last;
+ }
+ }
+
+ @circ-primes.push: $perm if $all-prime;
+
+ for @cycle -> UInt $n
+ {
+ my UInt $i = @perms.first: * == $n, :k;
+
+ @perms.splice: $i, 1 if $i.defined;
+ }
+ }
+
+ return @circ-primes;
+}
+
+#------------------------------------------------------------------------------
+# E.g., 137 -> 713 -> 371 [-> 137]
+#
+sub get-cycle( UInt:D $first --> Array:D[UInt:D] )
+#------------------------------------------------------------------------------
+{
+ my UInt @cycle = $first;
+ my UInt @digits = $first.split( '', :skip-empty ).map: { .Int };
+ my UInt $next;
+
+ repeat
+ {
+ my $last = @digits.pop;
+ @digits = $last, |@digits;
+ $next = @digits.join( '' ).Int;
+
+ @cycle.push: $next;
+
+ } until $next == $first;
+
+ @cycle.pop;
+
+ return @cycle;
+}
+
+#------------------------------------------------------------------------------
+sub get-permutations( UInt:D $n --> Array:D[UInt:D] )
+#------------------------------------------------------------------------------
+{
+ # Note: the digits are already sorted (ascending)
+
+ my UInt @digits = $n.split( '', :skip-empty ).map: { .Int };
+
+ my Array[UInt] @perms = Array[Array[UInt]].new:
+ @digits.permutations.map: { Array[UInt].new: $_ };
+
+ my UInt %perm{UInt};
+
+ ++%perm{ .join.Int } for @perms;
+
+ return Array[UInt].new: %perm.keys.sort;
+}
+
+#------------------------------------------------------------------------------
+sub get-combinations( --> Array:D[UInt:D] )
+#------------------------------------------------------------------------------
+{
+ my UInt @combs;
+
+ for @DIGITS -> UInt $a
+ {
+ for @DIGITS -> UInt $b
+ {
+ next if $b < $a;
+
+ for @DIGITS -> UInt $c
+ {
+ next if $c < $b;
+
+ @combs.push: "$a$b$c".Int;
+
+ for @DIGITS -> UInt $d
+ {
+ next if $d < $c;
+
+ @combs.push: "$a$b$c$d".Int;
+
+ for @DIGITS -> UInt $e
+ {
+ next if $e < $d;
+
+ @combs.push: "$a$b$c$d$e".Int;
+
+ for @DIGITS -> UInt $f
+ {
+ next if $f < $e;
+
+ @combs.push: "$a$b$c$d$e$f".Int;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return @combs;
+}
+
+#------------------------------------------------------------------------------
+# Remove 333, 777, 999, 3333, ..., but NOT 111, 1111, ...
+#
+sub filter-reps( Array:D[UInt:D] $combs --> Array:D[UInt:D] )
+#------------------------------------------------------------------------------
+{
+ my UInt @filtered;
+
+ for @$combs -> $comb
+ {
+ my UInt @digits = $comb.split( '', :skip-empty ).map: { .Int };
+ my UInt %count;
+
+ ++%count{ $_ } for @digits;
+
+ if %count{ 1 }:exists || %count.keys.elems > 1
+ {
+ @filtered.push: $comb;
+ }
+ }
+
+ @filtered.= sort: { .chars, .Int };
+
+ return @filtered;
+}
+
+#------------------------------------------------------------------------------
+sub USAGE()
+#------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/;
+
+ $usage.put;
+}
+
+###############################################################################
diff --git a/challenge-167/athanasius/raku/ch-2.raku b/challenge-167/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..96d9738464
--- /dev/null
+++ b/challenge-167/athanasius/raku/ch-2.raku
@@ -0,0 +1,296 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 167
+=========================
+
+TASK #2
+-------
+*Gamma Function*
+
+Submitted by: Mohammad S Anwar
+
+Implement subroutine gamma() using the [ https://en.wikipedia.org/wiki/Lanczos_
+approximation |Lanczos approximation] method.
+
+[2022-05-31]
+ Ryan Thompson wrote an interesting blog explaining the subject in details.
+ Highly recommended if you are looking for more information. [ https://ry.ca/
+ 2022/ 05/lanczos-approximation |BLOG].
+
+Example
+
+ print gamma(3); # 1.99
+ print gamma(5); # 24
+ print gamma(7); # 719.99
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2022 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=begin comment
+
+Command-Line Interface
+----------------------
+2 arguments: Real and imaginary parts of the complex argument to the gamma
+ function
+1 argument: Real part as above; the imaginary part defaults to zero
+0 arguments: Run the test suite (expected values obtained from [4] and [2]).
+
+Note: If the first argument (the real part) is negative, its minus sign will be
+interpreted as a command-line flag. To prevent this, it is necessary to precede
+the first argument with a double dash:
+
+ raku ch-2.raku -- -0.5
+
+Algorithm
+---------
+A port from Python to Perl of the "Simple implementation" in [3]. Note: I have
+departed from the Task specification by implementing a separate factorial
+calculation for positive integers. This gives more accurate results: e.g.,
+gamma(3) = 2 (which is exactly correct) rather than 1.99 as in the Example.
+
+References
+----------
+[1] "Gamma function", Wikipedia, https://en.wikipedia.org/wiki/Gamma_function
+[2] "Gamma function Calculator", ke!san Online Calculator,
+ https://keisan.casio.com/exec/system/1180573444
+[3] "Lanczos approximation", Wikipedia, https://en.wikipedia.org/wiki/Lanczos_
+ approximation
+[4] "Particular values of the gamma function", Wikipedia,
+ https://en.wikipedia.org/wiki/Particular_values_of_the_gamma_function
+
+=end comment
+#==============================================================================
+
+my UInt constant $ACCURACY = 13; # Significant digits
+my Real constant $EPSILON = 1e-12;
+my Real constant $INIT-X = 0.99999999999980993;
+my constant @P-COEFS = 676.5203681218851,
+ -1259.1392167224028,
+ 771.32342877765313,
+ -176.61502916214059,
+ 12.507343278686905,
+ -0.13857109526572012,
+ 9.9843695780195716e-6,
+ 1.5056327351493116e-7;
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 167, Task #2: Gamma Function (Raku)\n".put;
+}
+
+#==============================================================================
+sub MAIN
+(
+ Real $re?, #= Real part
+ Real $im = 0, #= Imaginary part
+)
+#==============================================================================
+{
+ if $re.defined
+ {
+ my Complex $z = Complex.new: $re, $im;
+
+ "gamma($z) = { gamma( $z ) }".put;
+ }
+ else
+ {
+ test();
+ }
+}
+
+#------------------------------------------------------------------------------
+sub gamma( Complex:D $z is rw --> Complex:D )
+#------------------------------------------------------------------------------
+{
+ my Complex $y = Complex.new: NaN, 0; # Assume failure
+
+ if is-integer( $z )
+ {
+ if $z.re > 0
+ {
+ # Calculate the factorial directly, for better accuracy
+