aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2021-03-14 23:58:24 +1000
committerPerlMonk Athanasius <PerlMonk.Athanasius@gmail.com>2021-03-14 23:58:24 +1000
commit11a2b09f72fd99fe67b57cb8b23cf4b4a6157040 (patch)
tree7eb4aac5ee5484a116dabcb0fabe3f5b0d30d11e
parent4a289a702db8c240e4362ae01c10c203cb19c968 (diff)
downloadperlweeklychallenge-club-11a2b09f72fd99fe67b57cb8b23cf4b4a6157040.tar.gz
perlweeklychallenge-club-11a2b09f72fd99fe67b57cb8b23cf4b4a6157040.tar.bz2
perlweeklychallenge-club-11a2b09f72fd99fe67b57cb8b23cf4b4a6157040.zip
Perl & Raku solutions to Tasks 1 & 2 of the Perl Weekly Challenge #103
On branch branch-for-challenge-103 Changes to be committed: new file: challenge-103/athanasius/perl/ch-1.pl new file: challenge-103/athanasius/perl/ch-2.pl new file: challenge-103/athanasius/perl/filelist.csv new file: challenge-103/athanasius/raku/ch-1.raku new file: challenge-103/athanasius/raku/ch-2.raku new file: challenge-103/athanasius/raku/filelist.csv
-rw-r--r--challenge-103/athanasius/perl/ch-1.pl139
-rw-r--r--challenge-103/athanasius/perl/ch-2.pl239
-rw-r--r--challenge-103/athanasius/perl/filelist.csv7
-rw-r--r--challenge-103/athanasius/raku/ch-1.raku125
-rw-r--r--challenge-103/athanasius/raku/ch-2.raku193
-rw-r--r--challenge-103/athanasius/raku/filelist.csv7
6 files changed, 710 insertions, 0 deletions
diff --git a/challenge-103/athanasius/perl/ch-1.pl b/challenge-103/athanasius/perl/ch-1.pl
new file mode 100644
index 0000000000..1c25e07fa6
--- /dev/null
+++ b/challenge-103/athanasius/perl/ch-1.pl
@@ -0,0 +1,139 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 103
+=========================
+
+Task #1
+-------
+*Chinese Zodiac*
+
+Submitted by: Mohammad S Anwar
+
+You are given a year $year.
+
+Write a script to determine the Chinese Zodiac for the given year $year. Please
+check out [ https://en.wikipedia.org/wiki/Chinese_zodiac |wikipage] for more
+information about it.
+
+The animal cycle: Rat, Ox, Tiger, Rabbit, Dragon, Snake, Horse, Goat, Monkey,
+Rooster, Dog, Pig.
+The element cycle: Wood, Fire, Earth, Metal, Water.
+
+Example 1:
+
+ Input: 2017
+ Output: Fire Rooster
+
+Example 2:
+
+ Input: 1938
+ Output: Earth Tiger
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+Assumptions
+-----------
+1. Years are AD (= CE), Gregorian calendar, only.
+2. Each year begins on Chinese New Year. For example, "1954" actually begins on
+ 5th February, 1954, and ends on 23rd January, 1955.
+
+Algorithm
+---------
+The Chinese lunar calendar is a sexagenary (60 year) cycle comprising two
+lesser cycles: a 12-year cycle of animal signs and a 10-year cycle in which
+each of 5 elements is repeated for 2 consecutive years.
+
+Given any "start year" (i.e., a year on which the sexagenary cycle begins, with
+animal sign Rat and element Wood) such as 1924 or 1984, the animal sign's
+index (zero-based) is calculated by finding the difference between the given
+year and the start year, then calculating that difference modulus 12. For
+example, 2017 - 1984 = 33, and 33 mod 12 = 9, so the animal sign for 2017 is
+the tenth in the series, namely Rooster.
+
+The element calculation is similar except that elements change biennially, so
+the difference must first be halved (and the remainder, if any, discarded)
+before the index is calculated as halved-difference modulus 5. Using 2017 again
+as an example, floor(33 / 2) = 16, and 16 mod 5 = 1, so the element for 2017 is
+the second in the series, namely Fire.
+
+Note that Perl's modulus operator produces the correct result for negative
+differences. From perlop: "If $n is positive, then $m % $n is $m minus the
+largest multiple of $n less than or equal to $m." So for (1938 - 1984) = -46,
+-46 % 12 = -46 - -48 = 2, which is the same result as would be obtained if the
+"start year" were 1924: 1938 - 1924 = 14, and 14 mod 12 = 2.
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use Regexp::Common qw( number );
+
+const my @ELEMENTS => qw( Wood Fire Earth Metal Water );
+const my @ANIMALS => qw( Rat Ox Tiger Rabbit Dragon Snake
+ Horse Goat Monkey Rooster Dog Pig );
+const my $START_YEAR => 1984;
+const my $USAGE =>
+"Usage:
+ perl $0 <year>
+
+ <year> Year AD (positive integer)\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 103, Task #1: Chinese Zodiac (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my $year = parse_command_line();
+
+ print "Input: $year\n";
+
+ my $el_index = int( ($year - $START_YEAR) / 2 ) % scalar @ELEMENTS;
+ my $an_index = ($year - $START_YEAR) % scalar @ANIMALS;
+
+ printf "Output: %s %s\n", $ELEMENTS[ $el_index ], $ANIMALS[ $an_index ];
+}
+
+#------------------------------------------------------------------------------
+sub parse_command_line
+#------------------------------------------------------------------------------
+{
+ my $args = scalar @ARGV;
+ $args == 1 or error( "Expected 1 command-line argument, found $args" );
+
+ my $year = $ARGV[ 0 ];
+ $year =~ / ^ $RE{num}{int} $ /x && $year > 0
+ or error( qq["$year" is not a positive integer] );
+
+ return $year;
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+###############################################################################
diff --git a/challenge-103/athanasius/perl/ch-2.pl b/challenge-103/athanasius/perl/ch-2.pl
new file mode 100644
index 0000000000..a03b25d564
--- /dev/null
+++ b/challenge-103/athanasius/perl/ch-2.pl
@@ -0,0 +1,239 @@
+#!perl
+
+###############################################################################
+=comment
+
+Perl Weekly Challenge 103
+=========================
+
+Task #2
+-------
+*What's playing?*
+
+Submitted by: Albert Croft
+
+Working from home, you decided that on occasion you wanted some background
+noise while working. You threw together a network streamer to continuously loop
+through the files and launched it in a tmux (or screen) session, giving it a
+directory tree of files to play. During the day, you connected an audio player
+to the stream, listening through the workday, closing it when done.
+
+For weeks you connect to the stream daily, slowly noticing a gradual drift of
+the media. After several weeks, you take vacation. When you return, you are
+pleasantly surprised to find the streamer still running. Before connecting,
+however, if you consider the puzzle of determining which track is playing.
+
+After looking at a few modules to read info regarding the media, a quick bit of
+coding gave you a file list. The file list is in a simple CSV format, each line
+containing two fields: the first the number of milliseconds in length, the
+latter the media's title (this example is of several episodes available from
+the MercuryTheatre.info):
+
+ 1709363,"Les Miserables Episode 1: The Bishop (broadcast date: 1937-07-23)"
+ 1723781,"Les Miserables Episode 2: Javert (broadcast date: 1937-07-30)"
+ 1723781,"Les Miserables Episode 3: The Trial (broadcast date: 1937-08-06)"
+ 1678356,"Les Miserables Episode 4: Cosette (broadcast date: 1937-08-13)"
+ 1646043,"Les Miserables Episode 5: The Grave (broadcast date: 1937-08-20)"
+ 1714640,"Les Miserables Episode 6: The Barricade (broadcast date: 1937-08-27)"
+ 1714640,"Les Miserables Episode 7: Conclusion (broadcast date: 1937-09-03)"
+
+For this script, you can assume to be provided the following information:
+
+ * the value of $^T ($BASETIME) of the streamer script,
+ * the value of time(), and
+ * a CSV file containing the media to play consisting of the length in
+ milliseconds and an identifier for the media (title, filename, or other).
+
+Write a program to output which file is currently playing. For purposes of this
+script, you may assume gapless playback, and format the output as you see fit.
+
+Optional: Also display the current position in the media as a time-like value.
+
+Example:
+
+UPDATED: Input parameters as reported by many members [2021-03-08 16:20 UK
+TIME].
+
+ Input: 3 command line parameters: start time, current time, file name
+
+ # starttime
+ 1606134123
+
+ # currenttime
+ 1614591276
+
+ # filelist.csv
+
+ Output:
+
+ "Les Miserables Episode 1: The Bishop (broadcast date: 1937-07-23)"
+ 00:10:24
+
+=cut
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=comment
+
+Notes
+-----
+1. Although it would be quite straightforward to hand-craft file-reading code
+ for this particular exercise, as a general rule it is advisable to use a
+ dedicated module for reading CSV files. The module chosen here is the
+ excellent Text::CSV (which automatically uses Text::CSV_XS if the latter is
+ available).
+2. The easiest way to determine the file currently playing is to begin by
+ calculating the total time elapsed modulo the total duration of the media
+ files. Perl's built-in mod operator "%" does not give the desired result
+ when the operands are real (i.e., floating point) numbers, so the subroutine
+ POSIX::fmod() is used for this purpose.
+3. The user may find it convenient to let the script determine the current
+ time, so an input value of zero for the current_time parameter is inter-
+ preted as "now".
+
+=cut
+#==============================================================================
+
+use strict;
+use warnings;
+use Const::Fast;
+use POSIX qw( fmod );
+use Regexp::Common qw( number );
+use Text::CSV;
+
+use enum qw( Length Name Cum_length );
+
+const my $USAGE =>
+"Usage:
+ perl $0 <start_time> <current_time> <file>
+
+ <start_time> Start time in seconds since the epoch
+ <current_time> Current time in seconds (0 means now)
+ <file> CSV filename\n";
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ $| = 1;
+ print "\nChallenge 103, Task #2: What's playing? (Perl)\n\n";
+}
+
+#==============================================================================
+MAIN:
+#==============================================================================
+{
+ my ($start_time, $current_time, $file) = parse_command_line();
+
+ my $current = $current_time || time;
+
+ print "Input:\n\n Start time: $start_time\n" .
+ " Current time: $current\n" .
+ " File list: $file\n\n";
+
+ my $data = read_data( $file );
+ my $total_s = int( $data->[ $#$data ][ Cum_length ] ) / 1e3;
+ my $difference = $current - $start_time;
+ my $offset = int( fmod($difference, $total_s) * 1e3 );
+ my $index = 0;
+
+ for (@$data)
+ {
+ last if $_->[ Cum_length ] > $offset;
+ ++$index;
+ }
+
+ $offset -= $data->[ $index - 1 ][ Cum_length ] if $index > 0;
+
+ printf "Output:\n\n %s\n %s\n",
+ $data->[ $index ][ Name ], hms( $offset );
+}
+
+#------------------------------------------------------------------------------
+sub read_data
+#------------------------------------------------------------------------------
+{
+ my ($file) = @_;
+
+ open( my $fh, '<', $file )
+ or error( qq[Cannot open file "$file" for reading] );
+
+ my $csv = Text::CSV->new;
+ my $data = $csv->getline_all( $fh );
+
+ close $fh or error( qq[Cannot close file "$file"] );
+
+ my $cum_total = 0;
+
+ for my $i (0 .. $#$data)
+ {
+ $cum_total += $data->[ $i ][ Length ];
+
+ push @{ $data->[ $i ] }, $cum_total;
+ }
+
+ return $data;
+}
+
+#------------------------------------------------------------------------------
+sub hms
+#------------------------------------------------------------------------------
+{
+ my ($ms) = @_;
+ my $seconds = int( $ms / 1e3 );
+ my $minutes = int( $seconds / 60 );
+ my $hours = int( $minutes / 60 );
+
+ $minutes -= $hours * 60;
+ $seconds -= $minutes * 60;
+
+ return sprintf '%02d:%02d:%02d', $hours, $minutes, $seconds;
+}
+
+#------------------------------------------------------------------------------
+sub parse_command_line
+#------------------------------------------------------------------------------
+{
+ my $args = scalar @ARGV;
+ $args == 3
+ or error( "Expected 3 command-line arguments, found $args" );
+
+ my ($start_time, $current_time, $file) = @ARGV;
+
+ for ($start_time, $current_time)
+ {
+ / ^ $RE{num}{int} $ /x && $_ >= 0
+ or error( qq["$_" is not a positive integer] );
+ }
+
+ if ($current_time > 0)
+ {
+ $current_time >= $start_time
+ or error( 'Current time is earlier than start time' );
+ }
+
+ $file =~ / \. csv $ /ix
+ or error( qq[File "$file" does not have a ".csv" extension] );
+
+ open( my $fh, '<', $file )
+ or error( qq[Cannot open file "$file" for reading] );
+
+ close $fh or die qq[Cannot close file "$file", stopped];
+
+ return ($start_time, $current_time, $file);
+}
+
+#------------------------------------------------------------------------------
+sub error
+#------------------------------------------------------------------------------
+{
+ my ($message) = @_;
+
+ die "ERROR: $message\n$USAGE";
+}
+
+###############################################################################
diff --git a/challenge-103/athanasius/perl/filelist.csv b/challenge-103/athanasius/perl/filelist.csv
new file mode 100644
index 0000000000..9428b93004
--- /dev/null
+++ b/challenge-103/athanasius/perl/filelist.csv
@@ -0,0 +1,7 @@
+1709363,"Les Miserables Episode 1: The Bishop (broadcast date: 1937-07-23)"
+1723781,"Les Miserables Episode 2: Javert (broadcast date: 1937-07-30)"
+1723781,"Les Miserables Episode 3: The Trial (broadcast date: 1937-08-06)"
+1678356,"Les Miserables Episode 4: Cosette (broadcast date: 1937-08-13)"
+1646043,"Les Miserables Episode 5: The Grave (broadcast date: 1937-08-20)"
+1714640,"Les Miserables Episode 6: The Barricade (broadcast date: 1937-08-27)"
+1714640,"Les Miserables Episode 7: Conclusion (broadcast date: 1937-09-03)"
diff --git a/challenge-103/athanasius/raku/ch-1.raku b/challenge-103/athanasius/raku/ch-1.raku
new file mode 100644
index 0000000000..9ed901dbdf
--- /dev/null
+++ b/challenge-103/athanasius/raku/ch-1.raku
@@ -0,0 +1,125 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 103
+=========================
+
+Task #1
+-------
+*Chinese Zodiac*
+
+Submitted by: Mohammad S Anwar
+
+You are given a year $year.
+
+Write a script to determine the Chinese Zodiac for the given year $year. Please
+check out [ https://en.wikipedia.org/wiki/Chinese_zodiac |wikipage] for more
+information about it.
+
+The animal cycle: Rat, Ox, Tiger, Rabbit, Dragon, Snake, Horse, Goat, Monkey,
+Rooster, Dog, Pig.
+The element cycle: Wood, Fire, Earth, Metal, Water.
+
+Example 1:
+
+ Input: 2017
+ Output: Fire Rooster
+
+Example 2:
+
+ Input: 1938
+ Output: Earth Tiger
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=begin comment
+
+Assumptions
+-----------
+1. Years are AD (= CE), Gregorian calendar, only.
+2. Each year begins on Chinese New Year. For example, "1954" actually begins on
+ 5th February, 1954, and ends on 23rd January, 1955.
+
+Algorithm
+---------
+The Chinese lunar calendar is a sexagenary (60 year) cycle comprising two
+lesser cycles: a 12-year cycle of animal signs and a 10-year cycle in which
+each of 5 elements is repeated for 2 consecutive years.
+
+Given any "start year" (i.e., a year on which the sexagenary cycle begins, with
+animal sign Rat and element Wood) such as 1924 or 1984, the animal sign's
+index (zero-based) is calculated by finding the difference between the given
+year and the start year, then calculating that difference modulus 12. For
+example, 2017 - 1984 = 33, and 33 mod 12 = 9, so the animal sign for 2017 is
+the tenth in the series, namely Rooster.
+
+The element calculation is similar except that elements change biennially, so
+the difference must first be halved (and the remainder, if any, discarded)
+before the index is calculated as halved-difference modulus 5. Using 2017 again
+as an example, floor(33 / 2) = 16, and 16 mod 5 = 1, so the element for 2017 is
+the second in the series, namely Fire.
+
+Note that Raku's modulus operator produces the correct result for negative
+differences. From https://docs.raku.org/routine/$PERCENT_SIGN :-
+
+ $x % $y == $x - floor($x / $y) * $y
+
+So for (1938 - 1984) = -46, -46 % 12 = -46 - floor(-46 / 12) * 12
+ = -46 - (-4 * 12) = -46 - -48 = 2
+
+which is the same result as would be obtained if the "start year" were 1924:
+1938 - 1924 = 14, and 14 mod 12 = 2.
+
+=end comment
+#==============================================================================
+
+my constant @ELEMENTS = Array[Str].new( < Wood Fire Earth Metal Water > );
+
+my constant @ANIMALS = Array[Str].new\
+ (
+ < Rat Ox Tiger Rabbit Dragon Snake Horse Goat Monkey Rooster Dog Pig >
+ );
+
+my UInt constant $START-YEAR = 1984;
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 103, Task #1: Chinese Zodiac (Raku)\n".put;
+}
+
+#==============================================================================
+sub MAIN
+(
+ UInt:D $year where { $year > 0 } #= Year AD (positive integer)
+)
+#==============================================================================
+{
+ "Input: $year".put;
+
+ my UInt $el-index = floor( ($year - $START-YEAR) / 2 ) % @ELEMENTS.elems;
+ my UInt $an-index = ($year - $START-YEAR) % @ANIMALS\.elems;
+
+ "Output: %s %s\n".printf: @ELEMENTS[ $el-index ], @ANIMALS[ $an-index ];
+}
+
+#------------------------------------------------------------------------------
+sub USAGE()
+#------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/;
+ $usage.put;
+}
+
+##############################################################################
diff --git a/challenge-103/athanasius/raku/ch-2.raku b/challenge-103/athanasius/raku/ch-2.raku
new file mode 100644
index 0000000000..fe4fca9ccf
--- /dev/null
+++ b/challenge-103/athanasius/raku/ch-2.raku
@@ -0,0 +1,193 @@
+use v6d;
+
+###############################################################################
+=begin comment
+
+Perl Weekly Challenge 103
+=========================
+
+Task #2
+-------
+*What's playing?*
+
+Submitted by: Albert Croft
+
+Working from home, you decided that on occasion you wanted some background
+noise while working. You threw together a network streamer to continuously loop
+through the files and launched it in a tmux (or screen) session, giving it a
+directory tree of files to play. During the day, you connected an audio player
+to the stream, listening through the workday, closing it when done.
+
+For weeks you connect to the stream daily, slowly noticing a gradual drift of
+the media. After several weeks, you take vacation. When you return, you are
+pleasantly surprised to find the streamer still running. Before connecting,
+however, if you consider the puzzle of determining which track is playing.
+
+After looking at a few modules to read info regarding the media, a quick bit of
+coding gave you a file list. The file list is in a simple CSV format, each line
+containing two fields: the first the number of milliseconds in length, the
+latter the media's title (this example is of several episodes available from
+the MercuryTheatre.info):
+
+ 1709363,"Les Miserables Episode 1: The Bishop (broadcast date: 1937-07-23)"
+ 1723781,"Les Miserables Episode 2: Javert (broadcast date: 1937-07-30)"
+ 1723781,"Les Miserables Episode 3: The Trial (broadcast date: 1937-08-06)"
+ 1678356,"Les Miserables Episode 4: Cosette (broadcast date: 1937-08-13)"
+ 1646043,"Les Miserables Episode 5: The Grave (broadcast date: 1937-08-20)"
+ 1714640,"Les Miserables Episode 6: The Barricade (broadcast date: 1937-08-27)"
+ 1714640,"Les Miserables Episode 7: Conclusion (broadcast date: 1937-09-03)"
+
+For this script, you can assume to be provided the following information:
+
+ * the value of $^T ($BASETIME) of the streamer script,
+ * the value of time(), and
+ * a CSV file containing the media to play consisting of the length in
+ milliseconds and an identifier for the media (title, filename, or other).
+
+Write a program to output which file is currently playing. For purposes of this
+script, you may assume gapless playback, and format the output as you see fit.
+
+Optional: Also display the current position in the media as a time-like value.
+
+Example:
+
+UPDATED: Input parameters as reported by many members [2021-03-08 16:20 UK
+TIME].
+
+ Input: 3 command line parameters: start time, current time, file name
+
+ # starttime
+ 1606134123
+
+ # currenttime
+ 1614591276
+
+ # filelist.csv
+
+ Output:
+
+ "Les Miserables Episode 1: The Bishop (broadcast date: 1937-07-23)"
+ 00:10:24
+
+=end comment
+###############################################################################
+
+#--------------------------------------#
+# Copyright © 2021 PerlMonk Athanasius #
+#--------------------------------------#
+
+#==============================================================================
+=begin comment
+
+Notes
+-----
+1. Although it would be quite straightforward to hand-craft file-reading code
+ for this particular exercise, as a general rule it is advisable to use a
+ dedicated module for reading CSV files. The module chosen here is the
+ excellent Text::CSV.
+2. The easiest way to determine the file currently playing is to begin by
+ calculating the total time elapsed modulo the total duration of the media
+ files. Unlike Perl's built-in mod operator, Raku's "%" gives the desired
+ result when the operands are real (i.e., floating point) numbers.
+3. The user may find it convenient to let the script determine the current
+ time, so an input value of zero for the current_time parameter is inter-
+ preted as "now".
+
+=end comment
+#==============================================================================
+
+use Text::CSV;
+
+enum Data-Fields < Length Name Cum-length >;
+
+#------------------------------------------------------------------------------
+BEGIN
+#------------------------------------------------------------------------------
+{
+ "\nChallenge 103, Task #2: What's playing? (Raku)\n".put;
+}
+
+#==============================================================================
+sub MAIN
+(
+ UInt:D $start-time, #= Start time in seconds since the epoch
+ UInt:D $current-time #= Current time in seconds (0 means now)
+ where { $_ == 0 || $_ >= $start-time },
+ Str:D $file where { $_ ~~ / \.csv $ / && .IO ~~ :r } #= CSV filename
+)
+#==============================================================================
+{
+ my UInt $current = $current-time || now.floor;
+
+ ("Input:\n\n Start time: $start-time\n" ~
+ " Current time: $current\n" ~
+ " File list: $file\n").put;
+
+ my Array[Str] @data = read-data( $file.IO );
+ my Real $total-s = @data[ @data.end; Cum-length ].Int / 1e3;
+ my UInt $difference = $current - $start-time;
+ my UInt $offset = floor( ($difference % $total-s) * 1e3 );
+ my UInt $index = 0;
+
+ for @data
+ {
+ last if $_[ Cum-length ] > $offset;
+ ++$index;
+ }
+
+ $offset -= @data[ $index - 1; Cum-length ] if $index > 0;
+
+ "Output:\n\n %s\n %s\n".printf:
+ @data[ $index; Name ], hms( $offset );
+}
+
+#------------------------------------------------------------------------------
+sub read-data( IO::Path:D $file --> Array:D[Array:D[Str:D]] )
+#------------------------------------------------------------------------------
+{
+ my Text::CSV $csv = Text::CSV.new;
+ my IO::Handle $io = $file.open: :r, chomp => True;
+ my Array[Str] @data;
+
+ for $csv.getline_all( $io )
+ {
+ my Str @entry = $_<>;
+ @data.push: @entry;
+ }
+
+ my UInt $cum-total = 0;
+
+ for @data
+ {
+ $cum-total += $_[ Length ];
+ .push: $cum-total.Str;
+ }
+
+ return @data;
+}
+
+#------------------------------------------------------------------------------
+sub hms( UInt:D $ms --> Str:D )
+#------------------------------------------------------------------------------
+{
+ my UInt $seconds = floor( $ms / 1000 );
+ my UInt $minutes = floor( $seconds / 60 );
+ my UInt $hours = floor( $minutes / 60 );
+
+ $minutes -= $hours * 60;
+ $seconds -= $minutes * 60;
+
+ return '%02d:%02d:%02d'.sprintf: $hours, $minutes, $seconds;
+}
+
+#------------------------------------------------------------------------------
+sub USAGE()
+#------------------------------------------------------------------------------
+{
+ my Str $usage = $*USAGE;
+
+ $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/;
+ $usage.put;
+}
+
+##############################################################################
diff --git a/challenge-103/athanasius/raku/filelist.csv b/challenge-103/athanasius/raku/filelist.csv
new file mode 100644
index 0000000000..9428b93004
--- /dev/null
+++ b/challenge-103/athanasius/raku/filelist.csv
@@ -0,0 +1,7 @@
+1709363,"Les Miserables Episode 1: The Bishop (broadcast date: 1937-07-23)"
+1723781,"Les Miserables Episode 2: Javert (broadcast date: 1937-07-30)"
+1723781,"Les Miserables Episode 3: The Trial (broadcast date: 1937-08-06)"
+1678356,"Les Miserables Episode 4: Cosette (broadcast date: 1937-08-13)"
+1646043,"Les Miserables Episode 5: The Grave (broadcast date: 1937-08-20)"
+1714640,"Les Miserables Episode 6: The Barricade (broadcast date: 1937-08-27)"
+1714640,"Les Miserables Episode 7: Conclusion (broadcast date: 1937-09-03)"