diff options
| author | Flavio Poletti <flavio@polettix.it> | 2022-08-17 00:01:36 +0200 |
|---|---|---|
| committer | Flavio Poletti <flavio@polettix.it> | 2022-08-17 00:01:36 +0200 |
| commit | 876ca5efc8ad875b1bb4d543073cd5d67caa8337 (patch) | |
| tree | 0b77e7724820843d81ec5deb6f73be3fb9bd3912 /challenge-178 | |
| parent | 4f806bc5039c3ceee8f45a056110e880bdf0db1e (diff) | |
| download | perlweeklychallenge-club-876ca5efc8ad875b1bb4d543073cd5d67caa8337.tar.gz perlweeklychallenge-club-876ca5efc8ad875b1bb4d543073cd5d67caa8337.tar.bz2 perlweeklychallenge-club-876ca5efc8ad875b1bb4d543073cd5d67caa8337.zip | |
Add polettix's solution to challenge-178
Diffstat (limited to 'challenge-178')
| -rw-r--r-- | challenge-178/polettix/blog.txt | 1 | ||||
| -rw-r--r-- | challenge-178/polettix/blog1.txt | 1 | ||||
| -rw-r--r-- | challenge-178/polettix/perl/ch-1.pl | 45 | ||||
| -rw-r--r-- | challenge-178/polettix/perl/ch-2.pl | 75 | ||||
| -rw-r--r-- | challenge-178/polettix/raku/ch-1.raku | 63 | ||||
| -rw-r--r-- | challenge-178/polettix/raku/ch-2.raku | 68 |
6 files changed, 253 insertions, 0 deletions
diff --git a/challenge-178/polettix/blog.txt b/challenge-178/polettix/blog.txt new file mode 100644 index 0000000000..66cb445e10 --- /dev/null +++ b/challenge-178/polettix/blog.txt @@ -0,0 +1 @@ +https://etoobusy.polettix.it/2022/08/17/pwc178-quater-imaginary-base/ diff --git a/challenge-178/polettix/blog1.txt b/challenge-178/polettix/blog1.txt new file mode 100644 index 0000000000..9de560a0c8 --- /dev/null +++ b/challenge-178/polettix/blog1.txt @@ -0,0 +1 @@ +https://etoobusy.polettix.it/2022/08/18/pwc178-business-date/ diff --git a/challenge-178/polettix/perl/ch-1.pl b/challenge-178/polettix/perl/ch-1.pl new file mode 100644 index 0000000000..965330c0fa --- /dev/null +++ b/challenge-178/polettix/perl/ch-1.pl @@ -0,0 +1,45 @@ +#!/usr/bin/env perl +use v5.24; +use warnings; +use experimental 'signatures'; +no warnings 'experimental::signatures'; + +say to_m4i(shift // 4); + +sub to_m4i ($x) { + my ($real, $img) = parse_complex($x); + $real = join('0', split m{}mxs, b10_to_bm($real, -4)) || 0; + if ($img) { + $img = join('0', split m{}mxs, b10_to_bm(-2 * $img, -4)); + my $after = substr $img, -1, 1, ''; + $real += $img if $img; + $real .= '.' . $after if $after; + } + return $real; +} + +sub b10_to_bm ($x, $m) { + my @digits; + while ($x) { + my $rem = $x % $m; + $x = (($x - $rem) / $m); + ($rem, $x) = ($rem - $m, $x + 1) if $rem < 0; + unshift @digits, $rem; + } + return join '', @digits; +} + +sub parse_complex ($x) { + $x =~ m{ + \A\s*(?: + (?<real> 0 | -?[1-9]\d*) + | (?<real> 0 | -?[1-9]\d*) \s* (?<img> [-+] (?:[1-9]\d*|))i + | (?<img> [-+]? (?:[1-9]\d*|))i + )\s*\z + }mxs; + my $real = $+{real} // 0; + my $img = $+{img} // 0; + $img = 1 if $img eq '' || $img eq '+'; + $img = -1 if $img eq '-'; + return ($real, $img); +} diff --git a/challenge-178/polettix/perl/ch-2.pl b/challenge-178/polettix/perl/ch-2.pl new file mode 100644 index 0000000000..b4a2b45064 --- /dev/null +++ b/challenge-178/polettix/perl/ch-2.pl @@ -0,0 +1,75 @@ +#!/usr/bin/env perl +use v5.24; +use warnings; +use experimental 'signatures'; +no warnings 'experimental::signatures'; +use Time::Local 'timegm'; + +my $ts = shift // '2022-08-01 10:30'; +my $duration = shift // 4; +say add_bh($ts, $duration); + +sub parse_datetime ($dt) { + my $error = "invalid input timestamp <$dt>\n"; + my ($y, $m, $d, $H, $M) = $dt =~ m{ + \A (\d+) - (\d\d) - (\d\d) \ (\d\d):(\d\d) \z + }mxs or die $error; + die $error unless eval { + timegm(0, $M, $H, $d, $m - 1, $y); + 1; + }; + return [ $y, $m, $d, $H, $M ]; +} + +sub add_bh ($timestamp, $duration) { + state $sod_min = 9 * 60; + state $eod_min = 18 * 60; + my $duration_min = int($duration * 60); # in minutes, rounded down + + my $dt = parse_datetime($timestamp); + + # cope with the possibility that the provided timestamp is + # *outside* the allowed range, move to the beginning of the + # next business day + $dt = next_business_day_start($dt) unless is_in_business($dt); + my $start_min = $dt->[3] * 60 + $dt->[4]; + + while ($duration_min > 0) { + my $available_min = $eod_min - $start_min; + if ($duration_min >= $available_min) { + $dt = next_business_day_start($dt); + $duration_min -= $available_min; + $start_min = $sod_min; + } + else { # we're in the very day! + my $stop_min = $start_min + $duration_min; + $dt->[4] = my $M = $stop_min % 60; + $dt->[3] = ($stop_min - $M) / 60; + $duration_min = 0; + } + } + + return sprintf '%04d-%02d-%02d %02d:%02d', $dt->@*; +} + +sub is_in_business ($dt) { + my ($y, $m, $d, $H, $M) = $dt->@*; + return if $H < 9 || $H > 17; # 18:00 is out ;) + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + gmtime(timegm(0, $M, $H, $d, $m - 1, $y)); + return (0 < $wday && $wday < 6); +} ## end sub is_in_business ($dt) + +sub next_business_day_start ($dt) { + state $day_s = 24 * 60 * 60; + my ($y, $m, $d) = $dt->@*; + while ('necessary') { + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = + gmtime($day_s + timegm(0, 30, 12, $d, $m - 1, $y)); + $year += 1900; + ++$mon; + return [$year, $mon, $mday, 9, 0] + if 0 < $wday && $wday < 6; + ($y, $m, $d) = ($year, $mon, $mday); + } +} diff --git a/challenge-178/polettix/raku/ch-1.raku b/challenge-178/polettix/raku/ch-1.raku new file mode 100644 index 0000000000..a9968c0e0a --- /dev/null +++ b/challenge-178/polettix/raku/ch-1.raku @@ -0,0 +1,63 @@ +#!/usr/bin/env raku +use v6; + +sub MAIN (Str:D() $x) { put to-m4i($x) } + +sub to-m4i (Complex:D() $cx) { + my $real = b10-to-bm($cx.re.Int, -4).comb.join('0') || 0; + my $img = b10-to-bm(-2 * $cx.im.Int, -4).comb.join('0'); + if $img.chars { + my $after = $img.substr(*-1, 1); + $img.substr-rw(*-1, 1) = ''; + $real += $img if $img.elems; + $real ~= '.' ~ $after if $after > 0; + } + return $real; +} + +multi sub b10-to-bm (Int:D $x is copy, Int:D $m where * < 0 --> Str) { + my @digits; + while $x { + my $rem = $x % $m; + $x = (($x - $rem) / $m).Int; + ($rem, $x) = $rem - $m, $x + 1 if $rem < 0; + @digits.unshift: $rem; + } + return @digits.join(''); +} + +=finish + +multi sub b10-to-bm (Int:D $x is copy, Int:D $m where * > 0 --> Str) { + my @digits; + my $sign = ''; + ($sign, $x) = ('-', $x.abs) if $x < 0; + while $x { + @digits.unshift: my $rem = $x % $m; + $x = (($x - $rem) / $m).Int; + } + @digits.unshift: $sign; + return @digits.join(''); +} + +sub bm-to-b10 (Str:D $x is copy, Int:D $m --> Int) { + my $sign = 1; + my @digits = $x.comb; + if ($m > 0 && @digits[0] eq '-') { + @digits.shift; + $sign = -1; + } + + state %weight_for; + if ! %weight_for.elems { + %weight_for{$_} = .Int for 0 .. 9; + %weight_for{$_} = 10 + .ord - 'a'.ord for 'a' .. 'z'; + %weight_for{$_} = 10 + .ord - 'A'.ord for 'A' .. 'Z'; + } + + my $retval = 0; + for @digits -> $digit { + $retval = $retval * $m + %weight_for{$digit}; + } + return $sign * $retval; +} diff --git a/challenge-178/polettix/raku/ch-2.raku b/challenge-178/polettix/raku/ch-2.raku new file mode 100644 index 0000000000..7d21386572 --- /dev/null +++ b/challenge-178/polettix/raku/ch-2.raku @@ -0,0 +1,68 @@ +#!/usr/bin/env raku +use v6; + +sub MAIN ($ts = '2022-08-01 10:30', $duration = 4) { + put add-bh($ts, $duration); +} + +sub parse_datetime ($dt) { + my $error = "invalid input timestamp <$dt>\n"; + my $match = $dt ~~ / + ^ (\d+) '-' (\d\d) '-' (\d\d) ' ' (\d\d) ':' (\d\d) $ + / or die $error; + my ($y, $m, $d, $H, $M) = $match[0..4].map({0 + $_}); + try { + CATCH { + default { die $error } + } + return DateTime.new(year => $y, month => $m, day => $d, + hour => $H, minute => $M); + } +} + +sub is-in-business ($dt) { + return ( + (9 <= $dt.hour < 18) # 18:00 is out ;) + && (1 <= $dt.day-of-week <= 5) + ); +} ## end sub is_in_business ($dt) + +sub next-business-day-start ($dt is copy) { + loop { + $dt = $dt.clone(hour => 9, minute => 0).later(day => 1); + return $dt if (1 <= $dt.day-of-week <= 5); + } +} + +sub add-bh ($timestamp, $duration) { + state $sod_min = 9 * 60; + state $eod_min = 18 * 60; + my $duration_min = ($duration * 60).Int; # in minutes, rounded down + + my $dt = parse_datetime($timestamp); + + # cope with the possibility that the provided timestamp is + # *outside* the allowed range, move to the beginning of the + # next business day + $dt = next-business-day-start($dt) unless is-in-business($dt); + my $start_min = $dt.hour * 60 + $dt.minute; + + while $duration_min > 0 { + my $available_min = $eod_min - $start_min; + if ($duration_min >= $available_min) { + $dt = next-business-day-start($dt); + $duration_min -= $available_min; + $start_min = $sod_min; + } + else { # we're in the very day! + my $stop_min = $start_min + $duration_min; + my $M = $stop_min % 60; + my $H = ($stop_min - $M) / 60; + $dt = $dt.clone(hour => $H, minute => $M); + $duration_min = 0; + } + } + + return '%04d-%02d-%02d %02d:%02d'.sprintf( + $dt.year, $dt.month, $dt.day, $dt.hour, $dt.minute); +} |
