diff options
| -rw-r--r-- | challenge-332/lubos-kolouch/perl/ch-1.pl | 47 | ||||
| -rw-r--r-- | challenge-332/lubos-kolouch/perl/ch-2.pl | 50 | ||||
| -rw-r--r-- | challenge-332/lubos-kolouch/python/ch-1.py | 49 | ||||
| -rw-r--r-- | challenge-332/lubos-kolouch/python/ch-2.py | 44 | ||||
| -rw-r--r-- | challenge-344/lubos-kolouch/README | 48 | ||||
| -rw-r--r-- | challenge-344/lubos-kolouch/perl/ch-1.pl | 52 | ||||
| -rw-r--r-- | challenge-344/lubos-kolouch/perl/ch-2.pl | 68 | ||||
| -rw-r--r-- | challenge-344/lubos-kolouch/python/ch-1.py | 67 | ||||
| -rw-r--r-- | challenge-344/lubos-kolouch/python/ch-2.py | 84 |
9 files changed, 485 insertions, 24 deletions
diff --git a/challenge-332/lubos-kolouch/perl/ch-1.pl b/challenge-332/lubos-kolouch/perl/ch-1.pl new file mode 100644 index 0000000000..f3ffb1fec2 --- /dev/null +++ b/challenge-332/lubos-kolouch/perl/ch-1.pl @@ -0,0 +1,47 @@ +#!/usr/bin/env perl +use v5.38; +use strict; +use warnings; +use feature 'signatures'; +no warnings 'experimental::signatures'; + +use Type::Params qw(compile); +use Types::Standard qw(StrMatch); +use Test::More; + +=pod + +=head1 NAME + +ch-1 - Binary Date converter + +=head1 SYNOPSIS + + perl ch-1.pl + +=head1 DESCRIPTION + +The script implements Task 1 (Binary Date) from The Weekly Challenge #332. +It exposes L</binary_date> that validates an ISO-8601 date string +and returns the corresponding binary representation for the year, +month, and day components joined with hyphens. + +=cut + +my $ISO_DATE = StrMatch [qr/\A\d{4}-\d{2}-\d{2}\z/]; +my $date_check = compile($ISO_DATE); + +## no critic (Subroutines::ProhibitSubroutinePrototypes) +sub binary_date ($date) { + ($date) = $date_check->($date); + my ( $year, $month, $day ) = split /-/xms, $date; + return join q{-}, map { sprintf '%b', $_ + 0 } ( $year, $month, $day ); +} +## use critic + +# Unit tests (Examples provided by the challenge) +is binary_date('2025-07-26'), '11111101001-111-11010', 'Example 1'; +is binary_date('2000-02-02'), '11111010000-10-10', 'Example 2'; +is binary_date('2024-12-31'), '11111101000-1100-11111', 'Example 3'; + +done_testing(); diff --git a/challenge-332/lubos-kolouch/perl/ch-2.pl b/challenge-332/lubos-kolouch/perl/ch-2.pl new file mode 100644 index 0000000000..1db92c3ed6 --- /dev/null +++ b/challenge-332/lubos-kolouch/perl/ch-2.pl @@ -0,0 +1,50 @@ +#!/usr/bin/env perl +use v5.38; +use strict; +use warnings; +use feature 'signatures'; +no warnings 'experimental::signatures'; + +use List::Util qw(all); +use Type::Params qw(compile); +use Types::Standard qw(StrMatch); +use Test::More; + +=pod + +=head1 NAME + +ch-2 - Odd Letters frequency checker + +=head1 SYNOPSIS + + perl ch-2.pl + +=head1 DESCRIPTION + +The script implements Task 2 (Odd Letters) from The Weekly Challenge #332. +It provides L</odd_letters> that accepts an ASCII alphabetic string and +returns a boolean indicating whether every distinct letter occurs an odd +number of times. + +=cut + +my $AlphaWord = StrMatch [qr/\A[a-zA-Z]+\z/]; +my $string_check = compile($AlphaWord); + +## no critic (Subroutines::ProhibitSubroutinePrototypes) +sub odd_letters ($word) { + ($word) = $string_check->($word); + my %tally; + $tally{$_}++ for split //, lc $word; + my $all_odd = all { $_ % 2 == 1 } values %tally; + return $all_odd ? 1 : 0; +} +## use critic + +# Unit tests (Examples provided by the challenge) +ok !odd_letters('weekly'), 'Example 1'; +ok odd_letters('perl'), 'Example 2'; +ok !odd_letters('challenge'), 'Example 3'; + +done_testing(); diff --git a/challenge-332/lubos-kolouch/python/ch-1.py b/challenge-332/lubos-kolouch/python/ch-1.py new file mode 100644 index 0000000000..3d8a96fdae --- /dev/null +++ b/challenge-332/lubos-kolouch/python/ch-1.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Task 1 - Binary Date converter for The Weekly Challenge #332.""" +from __future__ import annotations + +import re +import unittest +from typing import Final + +DATE_PATTERN: Final[re.Pattern[str]] = re.compile(r"^\d{4}-\d{2}-\d{2}$") + + +def binary_date(date: str) -> str: + """ + Return the binary representation of the year, month, and day components. + + Args: + date: An ISO-8601 date string in the form YYYY-MM-DD. + + Returns: + The converted date with each component rendered in base 2. + + Raises: + ValueError: If *date* is not a well-formed ISO-8601 date. + """ + if DATE_PATTERN.fullmatch(date) is None: + msg = f"Invalid ISO date: {date!r}" + raise ValueError(msg) + year_text, month_text, day_text = date.split("-") + year = int(year_text, 10) + month = int(month_text, 10) + day = int(day_text, 10) + return f"{year:b}-{month:b}-{day:b}" + + +class BinaryDateExamples(unittest.TestCase): + """Unit tests using the official examples.""" + + def test_example_1(self) -> None: + self.assertEqual(binary_date("2025-07-26"), "11111101001-111-11010") + + def test_example_2(self) -> None: + self.assertEqual(binary_date("2000-02-02"), "11111010000-10-10") + + def test_example_3(self) -> None: + self.assertEqual(binary_date("2024-12-31"), "11111101000-1100-11111") + + +if __name__ == "__main__": + unittest.main() diff --git a/challenge-332/lubos-kolouch/python/ch-2.py b/challenge-332/lubos-kolouch/python/ch-2.py new file mode 100644 index 0000000000..c37c90eeea --- /dev/null +++ b/challenge-332/lubos-kolouch/python/ch-2.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Task 2 - Odd Letters checker for The Weekly Challenge #332.""" +from __future__ import annotations + +import unittest +from collections import Counter + + +def has_odd_letter_counts(text: str) -> bool: + """ + Return True if every distinct letter in *text* appears an odd number of times. + + Args: + text: A string containing only alphabetic characters. + + Returns: + True when each distinct letter appears an odd number of times, otherwise False. + + Raises: + ValueError: If *text* contains non-alphabetic characters or is empty. + """ + if not text.isalpha(): + msg = f"Input must contain only alphabetic characters: {text!r}" + raise ValueError(msg) + normalized = text.lower() + counts = Counter(normalized) + return all(value % 2 == 1 for value in counts.values()) + + +class OddLettersExamples(unittest.TestCase): + """Unit tests derived from the supplied examples.""" + + def test_example_1(self) -> None: + self.assertFalse(has_odd_letter_counts("weekly")) + + def test_example_2(self) -> None: + self.assertTrue(has_odd_letter_counts("perl")) + + def test_example_3(self) -> None: + self.assertFalse(has_odd_letter_counts("challenge")) + + +if __name__ == "__main__": + unittest.main() diff --git a/challenge-344/lubos-kolouch/README b/challenge-344/lubos-kolouch/README index dcfc506962..972fc9e0d2 100644 --- a/challenge-344/lubos-kolouch/README +++ b/challenge-344/lubos-kolouch/README @@ -1,38 +1,38 @@ # Perl Weekly Challenge Solutions -This repository contains solutions for the Perl Weekly Challenge tasks in both Perl and Python. Below is a summary of the tasks and the implemented solutions. +Solutions for Perl Weekly Challenge 344 presented in both Perl and Python. -## Task 1: Zero Friend +## Task 1: Array Form Compute ### Description -Given a list of numbers, find the number closest to zero and return its distance to zero. If zero is present in the list, it is considered the closest, and the distance is 0. +Given an integer represented as an array of digits (`@ints`) and another integer (`$x`), add them and return the resulting integer in array form. ### Solution -- **Perl (`ch-1.pl`)**: The `zero_friend` function iterates through the list of numbers, computing the absolute value of each to find the minimum distance to zero. If the list is empty, it returns 0. -- **Python (`ch-1.py`)**: The `zero_friend` function uses a list comprehension with `min` and `abs` to find the smallest absolute value in the list. Returns 0 for an empty list. -- **Test Cases**: - - `(4, 2, -1, 3, -2)` → `1` - - `(-5, 5, -3, 3, -1, 1)` → `1` - - `(7, -3, 0, 2, -8)` → `0` - - `(-2, -5, -1, -8)` → `1` - - `(-2, 2, -4, 4, -1, 1)` → `1` +- **Perl (`perl/ch-1.pl`)**: Processes digits from least significant to most significant, maintaining a carry and constructing the resulting digit list. +- **Python (`python/ch-1.py`)**: Mirrors the Perl approach using an iterative carry-based loop to build the new digit array. +- **Examples Covered**: + - `(1, 2, 3, 4) + 12` → `(1, 2, 4, 6)` + - `(2, 7, 4) + 181` → `(4, 5, 5)` + - `(9, 9, 9) + 1` → `(1, 0, 0, 0)` + - `(1, 0, 0, 0, 0) + 9999` → `(1, 9, 9, 9, 9)` + - `(0) + 1000` → `(1, 0, 0, 0)` -## Task 2: Champion Team +## Task 2: Array Formation ### Description -Given a matrix `grid` where `grid[i][j] == 1` means team `i` is stronger than team `j`, and `grid[i][j] == 0` means team `j` is stronger, find the champion team with the most wins. If multiple teams tie for the most wins, return the one that beats the others. +Determine if the target array (`@target`) can be formed by concatenating subarrays from `@source` without altering the order inside each subarray, though subarrays themselves may be reordered. ### Solution -- **Perl (`ch-2.pl`)**: The `champion_team` function counts wins for each team by summing the rows of the grid (excluding self-comparisons). It identifies teams with the maximum wins and, if multiple, checks which one beats the others based on the grid. -- **Python (`ch-2.py`)**: Similar to the Perl solution, it counts wins per team, finds those with the maximum wins, and selects the champion by checking which team beats all other top teams. -- **Test Cases**: - - `[[0, 1, 1], [0, 0, 1], [0, 0, 0]]` → `Team 0` - - `[[0, 1, 0, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0]]` → `Team 3` - - `[[0, 1, 0, 1], [0, 0, 1, 1], [1, 0, 0, 0], [0, 0, 1, 0]]` → `Team 0` - - `[[0, 1, 1], [0, 0, 0], [0, 1, 0]]` → `Team 0` - - `[[0, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 1, 0, 1, 1], [1, 1, 0, 0, 0], [1, 1, 0, 1, 0]]` → `Team 2` +- **Perl (`perl/ch-2.pl`)**: Iterates through the target, greedily matching unused source subarrays based on their leading element and length, marking each subarray as consumed once used. +- **Python (`python/ch-2.py`)**: Utilises the same greedy matching strategy, leveraging Python's iteration constructs for clarity. +- **Examples Covered**: + - `([2,3], [1], [4])` → `(1, 2, 3, 4)` → `true` + - `([1,3], [2,4])` → `(1, 2, 3, 4)` → `false` + - `([9,1], [5,8], [2])` → `(5, 8, 2, 9, 1)` → `true` + - `([1], [3])` → `(1, 2, 3)` → `false` + - `([7,4,6])` → `(7, 4, 6)` → `true` ## Running the Solutions -- **Perl**: Run `perl ch-1.pl` or `perl ch-2.pl` to execute the tests. -- **Python**: Run `python3 ch-1.py` or `python3 ch-2.py` to execute the tests. -- The solutions have been validated with `flake8`, `pyupgrade`, `bandit` (Python), and `Perl::Critic` (Perl) to ensure code quality. +- **Perl**: Execute `perl perl/ch-1.pl` or `perl perl/ch-2.pl`. +- **Python**: Execute `python3 python/ch-1.py` or `python3 python/ch-2.py`. +- Each script includes embedded unit tests covering all examples from the task specification. diff --git a/challenge-344/lubos-kolouch/perl/ch-1.pl b/challenge-344/lubos-kolouch/perl/ch-1.pl new file mode 100644 index 0000000000..9906490ef1 --- /dev/null +++ b/challenge-344/lubos-kolouch/perl/ch-1.pl @@ -0,0 +1,52 @@ +#!/usr/bin/env perl + +# Perl Weekly Challenge: Task 1 - Array Form Compute +# Add an integer to its array-form representation and return the resulting digits. + +use strict; +use warnings; +use v5.30; + +# Enable signatures for clearer intent. +use experimental 'signatures'; + +# Add the integer $x to the array-form integer represented by $digits_ref. +## no critic (Subroutines::ProhibitSubroutinePrototypes) +sub array_form_compute ( $digits_ref, $x ) { + my @digits = $digits_ref->@*; + my @result; + my $index = $#digits; + my $carry = $x; + + while ( $index >= 0 || $carry > 0 ) { + my $sum = $carry; + if ( $index >= 0 ) { + $sum += $digits[$index]; + $index--; + } + + unshift @result, $sum % 10; + $carry = int( $sum / 10 ); + } + + return @result ? @result : (0); +} + +# Unit tests +use Test::More; + +is_deeply( + [ array_form_compute( [ 1, 2, 3, 4 ], 12 ) ], + [ 1, 2, 4, 6 ], + 'Example 1: (1,2,3,4) + 12 -> (1,2,4,6)' +); +is_deeply( [ array_form_compute( [ 2, 7, 4 ], 181 ) ], [ 4, 5, 5 ], 'Example 2: (2,7,4) + 181 -> (4,5,5)' ); +is_deeply( [ array_form_compute( [ 9, 9, 9 ], 1 ) ], [ 1, 0, 0, 0 ], 'Example 3: (9,9,9) + 1 -> (1,0,0,0)' ); +is_deeply( + [ array_form_compute( [ 1, 0, 0, 0, 0 ], 9999 ) ], + [ 1, 9, 9, 9, 9 ], + 'Example 4: (1,0,0,0,0) + 9999 -> (1,9,9,9,9)' +); +is_deeply( [ array_form_compute( [0], 1000 ) ], [ 1, 0, 0, 0 ], 'Example 5: (0) + 1000 -> (1,0,0,0)' ); + +done_testing(); diff --git a/challenge-344/lubos-kolouch/perl/ch-2.pl b/challenge-344/lubos-kolouch/perl/ch-2.pl new file mode 100644 index 0000000000..d80ea09097 --- /dev/null +++ b/challenge-344/lubos-kolouch/perl/ch-2.pl @@ -0,0 +1,68 @@ +#!/usr/bin/env perl + +# Perl Weekly Challenge: Task 2 - Array Formation +# Determine whether the target array can be formed by concatenating source subarrays without reordering their elements. + +use strict; +use warnings; +use v5.30; + +# Enable signatures for clarity. +use experimental 'signatures'; + +# Check if @target can be built by reordering the subarrays in @source and concatenating them. +## no critic (Subroutines::ProhibitSubroutinePrototypes) +sub array_formation ( $source_ref, $target_ref ) { + my @source = $source_ref->@*; + my @target = $target_ref->@*; + my $target_length = @target; + my @used = (0) x @source; + my $index = 0; + + while ( $index < $target_length ) { + my $matched = 0; + + for my $i ( 0 .. $#source ) { + next if $used[$i]; + my @piece = $source[$i]->@*; + next unless @piece; + next unless $piece[0] == $target[$index]; + + my $len = @piece; + next if $index + $len > $target_length; + + my $fits = 1; + for my $offset ( 0 .. $len - 1 ) { + if ( $piece[$offset] != $target[ $index + $offset ] ) { + $fits = 0; + last; + } + } + + next unless $fits; + + $used[$i] = 1; + $index += $len; + $matched = 1; + last; + } + + return 'false' unless $matched; + } + + return 'true'; +} + +# Unit tests +use Test::More; + +is( array_formation( [ [ 2, 3 ], [1], [4] ], [ 1, 2, 3, 4 ] ), + 'true', 'Example 1: ([2,3],[1],[4]) -> (1,2,3,4)' ); +is( array_formation( [ [ 1, 3 ], [ 2, 4 ] ], [ 1, 2, 3, 4 ] ), + 'false', 'Example 2: ([1,3],[2,4]) -> (1,2,3,4)' ); +is( array_formation( [ [ 9, 1 ], [ 5, 8 ], [2] ], [ 5, 8, 2, 9, 1 ] ), + 'true', 'Example 3: ([9,1],[5,8],[2]) -> (5,8,2,9,1)' ); +is( array_formation( [ [1], [3] ], [ 1, 2, 3 ] ), 'false', 'Example 4: ([1],[3]) -> (1,2,3)' ); +is( array_formation( [ [ 7, 4, 6 ] ], [ 7, 4, 6 ] ), 'true', 'Example 5: ([7,4,6]) -> (7,4,6)' ); + +done_testing(); diff --git a/challenge-344/lubos-kolouch/python/ch-1.py b/challenge-344/lubos-kolouch/python/ch-1.py new file mode 100644 index 0000000000..136fd38515 --- /dev/null +++ b/challenge-344/lubos-kolouch/python/ch-1.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Perl Weekly Challenge: Task 1 - Array Form Compute +Add an integer to its array-form representation and return the resulting digits. +""" + +from __future__ import annotations + +import unittest + + +def array_form_compute(ints: list[int], x: int) -> list[int]: + """ + Add the integer ``x`` to the number represented by ``ints`` in array form. + + Args: + ints (list[int]): Digits of the number ordered from most to least significant. + x (int): The value to add. + + Returns: + list[int]: Digits of the resulting sum in array form. + """ + result: list[int] = [] + carry = x + index = len(ints) - 1 + + while index >= 0 or carry > 0: + if index >= 0: + carry += ints[index] + index -= 1 + result.append(carry % 10) + carry //= 10 + + if not result: + return [0] + + result.reverse() + return result + + +class TestArrayFormCompute(unittest.TestCase): + """Unit tests for the array_form_compute function.""" + + def test_example_1(self) -> None: + """Example 1: (1, 2, 3, 4) + 12 -> (1, 2, 4, 6)""" + self.assertEqual(array_form_compute([1, 2, 3, 4], 12), [1, 2, 4, 6]) + + def test_example_2(self) -> None: + """Example 2: (2, 7, 4) + 181 -> (4, 5, 5)""" + self.assertEqual(array_form_compute([2, 7, 4], 181), [4, 5, 5]) + + def test_example_3(self) -> None: + """Example 3: (9, 9, 9) + 1 -> (1, 0, 0, 0)""" + self.assertEqual(array_form_compute([9, 9, 9], 1), [1, 0, 0, 0]) + + def test_example_4(self) -> None: + """Example 4: (1, 0, 0, 0, 0) + 9999 -> (1, 9, 9, 9, 9)""" + self.assertEqual(array_form_compute([1, 0, 0, 0, 0], 9999), + [1, 9, 9, 9, 9]) + + def test_example_5(self) -> None: + """Example 5: (0) + 1000 -> (1, 0, 0, 0)""" + self.assertEqual(array_form_compute([0], 1000), [1, 0, 0, 0]) + + +if __name__ == "__main__": + unittest.main() diff --git a/challenge-344/lubos-kolouch/python/ch-2.py b/challenge-344/lubos-kolouch/python/ch-2.py new file mode 100644 index 0000000000..00ac85b5e2 --- /dev/null +++ b/challenge-344/lubos-kolouch/python/ch-2.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +Perl Weekly Challenge: Task 2 - Array Formation +Check whether the target array can be formed by concatenating the source subarrays without reordering their elements. +""" + +from __future__ import annotations + +import unittest + + +def array_formation(source: list[list[int]], target: list[int]) -> bool: + """ + Determine if ``target`` can be constructed by concatenating subarrays from ``source``. + + Args: + source (list[list[int]]): Available subarrays that may be reordered but not restructured. + target (list[int]): Desired array to build. + + Returns: + bool: ``True`` if the target can be formed, otherwise ``False``. + """ + used = [False] * len(source) + index = 0 + + while index < len(target): + matched = False + + for i, piece in enumerate(source): + if used[i] or not piece or piece[0] != target[index]: + continue + + if index + len(piece) > len(target): + continue + + if all(target[index + offset] == value + for offset, value in enumerate(piece)): + used[i] = True + index += len(piece) + matched = True + break + + if not matched: + return False + + return True + + +class TestArrayFormation(unittest.TestCase): + """Unit tests for the array_formation function.""" + + def test_example_1(self) -> None: + """Example 1: ([2,3], [1], [4]) -> (1, 2, 3, 4)""" + source = [[2, 3], [1], [4]] + target = [1, 2, 3, 4] + self.assertTrue(array_formation(source, target)) + + def test_example_2(self) -> None: + """Example 2: ([1,3], [2,4]) -> (1, 2, 3, 4)""" + source = [[1, 3], [2, 4]] + target = [1, 2, 3, 4] + self.assertFalse(array_formation(source, target)) + + def test_example_3(self) -> None: + """Example 3: ([9,1], [5,8], [2]) -> (5, 8, 2, 9, 1)""" + source = [[9, 1], [5, 8], [2]] + target = [5, 8, 2, 9, 1] + self.assertTrue(array_formation(source, target)) + + def test_example_4(self) -> None: + """Example 4: ([1], [3]) -> (1, 2, 3)""" + source = [[1], [3]] + target = [1, 2, 3] + self.assertFalse(array_formation(source, target)) + + def test_example_5(self) -> None: + """Example 5: ([7,4,6]) -> (7, 4, 6)""" + source = [[7, 4, 6]] + target = [7, 4, 6] + self.assertTrue(array_formation(source, target)) + + +if __name__ == "__main__": + unittest.main() |
