aboutsummaryrefslogtreecommitdiff
path: root/challenge-016
diff options
context:
space:
mode:
authorMohammad S Anwar <mohammad.anwar@yahoo.com>2019-07-14 13:09:59 +0100
committerMohammad S Anwar <mohammad.anwar@yahoo.com>2019-07-14 13:09:59 +0100
commit0f531cb9e0bd3c41bef47c5645d6ba25f77c9730 (patch)
tree12f82ec02c9406f0e48c4192c531a283e3f68b94 /challenge-016
parentf61185a0238f14a02308a06440e20c2b3556a967 (diff)
downloadperlweeklychallenge-club-0f531cb9e0bd3c41bef47c5645d6ba25f77c9730.tar.gz
perlweeklychallenge-club-0f531cb9e0bd3c41bef47c5645d6ba25f77c9730.tar.bz2
perlweeklychallenge-club-0f531cb9e0bd3c41bef47c5645d6ba25f77c9730.zip
- Added solutions by Athanasius.
Diffstat (limited to 'challenge-016')
-rw-r--r--challenge-016/athanasius/blog.txt1
-rw-r--r--challenge-016/athanasius/perl5/ch-1.pl44
-rw-r--r--challenge-016/athanasius/perl5/ch-2.pl111
-rw-r--r--challenge-016/athanasius/perl6/ch-1.p628
-rw-r--r--challenge-016/athanasius/perl6/ch-2.p6109
5 files changed, 293 insertions, 0 deletions
diff --git a/challenge-016/athanasius/blog.txt b/challenge-016/athanasius/blog.txt
new file mode 100644
index 0000000000..f60d4bc7dc
--- /dev/null
+++ b/challenge-016/athanasius/blog.txt
@@ -0,0 +1 @@
+http://blogs.perl.org/users/athanasius/2019/07/perl-weekly-challenge-016.html
diff --git a/challenge-016/athanasius/perl5/ch-1.pl b/challenge-016/athanasius/perl5/ch-1.pl
new file mode 100644
index 0000000000..b6789357a8
--- /dev/null
+++ b/challenge-016/athanasius/perl5/ch-1.pl
@@ -0,0 +1,44 @@
+use strict;
+use warnings;
+use Const::Fast;
+use constant DEBUG => 0;
+
+const my $GUESTS => 100;
+
+MAIN:
+{
+ my $pie = 1;
+ my $max_guest = 1;
+ my $max_piece = 1 / $GUESTS;
+ my $sum = $max_piece if DEBUG;
+ $pie -= $max_piece;
+
+ printf("\nGuest %*d gets %43.40f%% of the original pie\n",
+ length $GUESTS, $max_guest, $max_piece * 100) if DEBUG;
+
+ for my $guest (2 .. $GUESTS)
+ {
+ my $piece = ($guest / $GUESTS) * $pie;
+ $pie -= $piece;
+
+ if (DEBUG)
+ {
+ printf "Guest %*d gets %43.40f%% of the original pie\n",
+ length $GUESTS, $guest, $piece * 100;
+ $sum += $piece;
+ }
+
+ if ($piece > $max_piece)
+ {
+ $max_piece = $piece;
+ $max_guest = $guest;
+ }
+ }
+
+ printf "\nGuest %d of %d gets the largest piece, which is " .
+ "%.2f%% of the original pie\n", $max_guest, $GUESTS,
+ $max_piece * 100;
+
+ printf("\nCheck: sum of pieces = %.13f%%\n", $sum * 100)
+ if DEBUG;
+}
diff --git a/challenge-016/athanasius/perl5/ch-2.pl b/challenge-016/athanasius/perl5/ch-2.pl
new file mode 100644
index 0000000000..c61a6f3d1c
--- /dev/null
+++ b/challenge-016/athanasius/perl5/ch-2.pl
@@ -0,0 +1,111 @@
+use strict;
+use warnings;
+use Const::Fast;
+use Crypt::Misc qw( decode_b58b );
+use Digest::SHA;
+
+const my $DEFAULT_ADDR => '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2';
+const my %ADDR_FORMATS => (1 => 'P2PKH', 3 => 'P2SH',
+ bc1 => 'Bech32');
+const my $INVALID_CHARS => qr{([^1-9A-HJ-NP-Za-km-z])};
+const my $MAX_CHARS => 35;
+const my $MIN_CHARS => 26;
+const my $SHA_ALGORITHM => 256;
+const my $TAB => ' ';
+
+MAIN:
+{
+ my $address = $ARGV[0] // $DEFAULT_ADDR;
+
+ print "\nBitcoin address: \"$address\"\n";
+
+ if (my $error = validate_format($address, \my $format))
+ {
+ print "${TAB}Format validation FAILED: $error\n";
+ }
+ elsif ($format eq 'Bech32')
+ {
+ print "${TAB}Bech32 format not currently supported\n";
+ }
+ else
+ {
+ print "${TAB}Format is \"$format\"\n";
+
+ if ($error = validate_chars($address))
+ {
+ print "${TAB}Character validation FAILED: $error\n";
+ }
+ elsif (validate_checksum($address))
+ {
+ print "${TAB}Checksum validation PASSED\n";
+ }
+ else
+ {
+ print "${TAB}Checksum validation FAILED\n";
+ }
+ }
+}
+
+sub validate_format
+{
+ my ($address, $format) = @_;
+ my $error;
+
+ for my $prefix (keys %ADDR_FORMATS)
+ {
+ if ($address =~ qr/^$prefix/)
+ {
+ $$format = $ADDR_FORMATS{$prefix};
+ last;
+ }
+ }
+
+ unless (defined $$format)
+ {
+ my $len = substr($address, 0, 1) eq 'b' ?
+ substr($address, 1, 1) eq 'c' ? 3 : 2 : 1;
+ $error = 'invalid prefix "' . substr($address, 0, $len) .
+ '", unknown format';
+ }
+
+ return $error;
+}
+
+sub validate_chars
+{
+ my ($address) = @_;
+ my $chars = length $address;
+ my $error;
+
+ if ($chars < $MIN_CHARS)
+ {
+ $error = "invalid length $chars (minimum is $MIN_CHARS)";
+ }
+ elsif ($chars > $MAX_CHARS)
+ {
+ $error = "invalid length $chars (maximum is $MAX_CHARS)";
+ }
+ elsif ($address =~ $INVALID_CHARS)
+ {
+ $error = "invalid character \"$1\"";
+ }
+
+ return $error;
+}
+
+sub validate_checksum
+{
+ my ($address) = @_;
+ my $rawdata = decode_b58b($address); # Base58 to bytes
+ my $hexdata = unpack 'H*', $rawdata; # Bytes to hex
+ my $checksum1 = substr $hexdata, -8; # Checksum 1 in hex
+ my $payload = substr $hexdata, 0, -8; # Payload in hex
+ my $sha_obj = Digest::SHA->new($SHA_ALGORITHM);
+ $sha_obj->add(pack 'H*', $payload); # Hex to bytes
+ my $digest1 = $sha_obj->hexdigest; # 1st digest in hex
+ $sha_obj->add(pack 'H*', $digest1); # hex to bytes
+ my $digest2 = $sha_obj->hexdigest; # 2nd digest in hex
+ my $checksum2 = substr $digest2, 0, 8; # Checksum 2 in hex
+
+ return $checksum1 eq $checksum2; # Compare checksums
+}
diff --git a/challenge-016/athanasius/perl6/ch-1.p6 b/challenge-016/athanasius/perl6/ch-1.p6
new file mode 100644
index 0000000000..6e66ce3648
--- /dev/null
+++ b/challenge-016/athanasius/perl6/ch-1.p6
@@ -0,0 +1,28 @@
+use v6;
+
+my UInt constant $GUESTS = 100;
+
+sub MAIN()
+{
+ my Real $pie = 1;
+ my UInt $max-guest = 1;
+ my Real $max-piece = 1 / $GUESTS;
+ $pie -= $max-piece;
+
+ for 2 .. $GUESTS -> UInt $guest
+ {
+ my Real $piece = ($guest / $GUESTS) * $pie;
+ $pie -= $piece;
+
+ if ($piece > $max-piece)
+ {
+ $max-piece = $piece;
+ $max-guest = $guest;
+ }
+ }
+
+ my Real $max-piece-pc = $max-piece * 100;
+
+ say "\nGuest $max-guest of $GUESTS gets the largest piece, ",
+ "which is $max-piece-pc.round(0.01)% of the original pie";
+}
diff --git a/challenge-016/athanasius/perl6/ch-2.p6 b/challenge-016/athanasius/perl6/ch-2.p6
new file mode 100644
index 0000000000..6df7a7752a
--- /dev/null
+++ b/challenge-016/athanasius/perl6/ch-2.p6
@@ -0,0 +1,109 @@
+use v6;
+use experimental :pack;
+use Crypt::Misc:from<Perl5> <decode_b58b>;
+use Digest::SHA256::Native;
+
+my Sub $decode_b58b := &Crypt::Misc::decode_b58b;
+
+my constant %ADDR-FORMATS =
+ (1 => 'P2PKH', 3 => 'P2SH', bc1 => 'Bech32');
+my Str constant $DEFAULT-ADDR =
+ '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2';
+my Regex constant $INVALID-CHARS =
+ rx{ ( <-[1..9A..HJ..NP..Za..km..z]> ) };
+my UInt constant $MAX-CHARS = 35;
+my UInt constant $MIN-CHARS = 26;
+my Str constant $TAB = ' ';
+
+sub MAIN(Str:D $address = $DEFAULT-ADDR)
+{
+ print "\nBitcoin address: \"$address\"\n";
+ my Str:D $format = '';
+
+ if my $error = validate-format($address, $format)
+ {
+ print "{$TAB}Format validation FAILED: $error\n";
+ }
+ elsif $format eq 'Bech32'
+ {
+ print "{$TAB}Bech32 format not currently supported\n";
+ }
+ else
+ {
+ print "{$TAB}Format is \"$format\"\n";
+
+ if $error = validate-chars($address)
+ {
+ print "{$TAB}Character validation FAILED: $error\n";
+ }
+ elsif validate-checksum($address)
+ {
+ print "{$TAB}Checksum validation PASSED\n";
+ }
+ else
+ {
+ print "{$TAB}Checksum validation FAILED\n";
+ }
+ }
+}
+
+sub validate-format(Str:D $address, Str:D $format is rw)
+{
+ my Str $error;
+
+ for keys %ADDR-FORMATS -> Str $prefix
+ {
+ if $address ~~ /^$prefix/
+ {
+ $format = %ADDR-FORMATS{$prefix};
+ last;
+ }
+ }
+
+ unless $format
+ {
+ my UInt $len = $address.substr(0, 1) eq 'b' ??
+ $address.substr(1, 1) eq 'c' ?? 3 !! 2 !! 1;
+
+ $error = 'invalid prefix "' ~ $address.substr(0, $len) ~
+ '", unknown format';
+ }
+
+ return $error;
+}
+
+sub validate-chars(Str:D $address)
+{
+ my $chars = $address.chars;
+ my Str $error;
+
+ if $chars < $MIN-CHARS
+ {
+ $error = "invalid length $chars (minimum is $MIN-CHARS)";
+ }
+ elsif $chars > $MAX-CHARS
+ {
+ $error = "invalid length $chars (maximum is $MAX-CHARS)";
+ }
+ elsif $address ~~ $INVALID-CHARS
+ {
+ $error = "invalid character \"$0\"";
+ }
+
+ return $error;
+}
+
+sub validate-checksum(Str:D $address)
+{
+ my Blob[uint8] $raw-data = $decode_b58b($address);
+ my Str $hex-data = $raw-data.unpack('H*');
+ my Str $hex-checksum1 = $hex-data.substr(*-8);
+ my Str $hex-payload = $hex-data.substr(0, *-8);
+ my Blob[uint8] $raw-payload = pack('H*', $hex-payload);
+ my Str $hex-digest1 = sha256-hex($raw-payload);
+ my Blob[uint8] $raw_digest1 = pack('H*', $hex-digest1);
+ my Str $hex-digest2 = sha256-hex($raw_digest1);
+ my Str $hex-checksum2 = $hex-digest2.substr(0, 8);
+
+ return $hex-checksum1 eq $hex-checksum2;
+}