diff options
| author | Mohammad S Anwar <Mohammad.Anwar@yahoo.com> | 2021-08-30 02:23:47 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-30 02:23:47 +0100 |
| commit | 8f2f826dbe6715ab595c4470501f3a8498be15bf (patch) | |
| tree | 2077d001729ddc6ece592edea75498d295e87062 | |
| parent | 7f347f9280b1882236ba6c508dd80779e98dfa8e (diff) | |
| parent | 9a3a45332da9fee3fd57d81eb017277a0cdcdc56 (diff) | |
| download | perlweeklychallenge-club-8f2f826dbe6715ab595c4470501f3a8498be15bf.tar.gz perlweeklychallenge-club-8f2f826dbe6715ab595c4470501f3a8498be15bf.tar.bz2 perlweeklychallenge-club-8f2f826dbe6715ab595c4470501f3a8498be15bf.zip | |
Merge pull request #4815 from iangoodnight/iangoodnight127
Challenge 127 perl and javascript
17 files changed, 1221 insertions, 0 deletions
diff --git a/challenge-127/iangoodnight/README.md b/challenge-127/iangoodnight/README.md new file mode 100644 index 0000000000..2a4a23a28c --- /dev/null +++ b/challenge-127/iangoodnight/README.md @@ -0,0 +1 @@ +Solution by Ian Goodnight diff --git a/challenge-127/iangoodnight/javascript/README.md b/challenge-127/iangoodnight/javascript/README.md new file mode 100644 index 0000000000..1916b6e9d4 --- /dev/null +++ b/challenge-127/iangoodnight/javascript/README.md @@ -0,0 +1,261 @@ + +# Perl Weekly Challenge Club - 127 + +This is my first time submitting! I had a lot of fun and it was a good excuse +to work on my Perl. I spend most of my time working with Javascript, so I +started out by writing a solution Javascript. + +## Task 1 > Disjoint Sets + +You are given two sets with unique integers. +Write a script to figure out if they are disjoint. + +The two sets are disjoint if they don't have any common members + +### EXAMPLE + +**Input:** + +```javascript +const s1 = [1, 2, 5, 3, 4]; +const s2 = [4, 6, 7, 8, 9]; +``` + +**Output:** `0` as the given sets have common member `4`. + +**Input:** + +```javascript +const s1 = [1, 3, 5, 7, 9]; +const s2 = [0, 2, 4, 6, 8]; +``` + +**Output:** `1` as the given two sets do no have a common member. + +### SOLUTION + +```javascript +function isDisjoint(set1 = [], set2 = []) { + const testSet = [...set1]; // shallow copy of the first set + + let disjoint = true; // trust, but verify + while (disjoint && testSet.length) { + const test = testSet.pop(); + if (set2.includes(test)) disjoint = false; + } + return disjoint; +} +``` + +### ch-1.js + +Running `./ch-1.js` tests our solution against the following test cases, +printing `Passed` or `Failed` to the console: + +#### Case 1: + +```javascript +1,2,5,3,4 +4,6,7,8,9 +0 // false, 4 is not unique +``` + +#### Case 2: + +```javascript +1,3,5,7,9 +0,2,4,6,8 +1 +``` + +#### Case 3: + +```javascript +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +2 +1 // Technically, the sets are still disjoint +``` + +#### Case 4: + +```javascript +😺,😸,😹,😻,😼,😽 +😽,🙀,😿,😾 +0 // False +``` + +#### Case 5: + +```javascript +🙂,🙃,😉,😌,😍,🥰,😘,😗,😙,😚,😋 +🤤,😪,😵,🤐,🥴,🤢,🤮,🤧,😷,🤒,🤕 +1 // True +``` + +### Custom Tests + +`./ch-1.js` will optionally accept a path to a test file or directory of test +files (ie: `$ ./ch-1.js ./local_test.txt`). Test files must include no more than +2 lines of comma separated values with the last line being either `0` for tests +that should fail or `1` for tests that should pass. Lines beginning with `#` +will be ignored. + +## Task 2 > Conflict Intervals + +You are given a list of intervals. +Write a script to find out if the current interval conflicts with any of the +previous intervals. + +### EXAMPLE + +**Input:** + +```javascript +const intervals = [[1, 4], [3, 5], [6, 8], [12, 13], [3, 20]]; +``` + +**Output:** + +```javascript +[[3, 5], [3, 20]] +``` + +- The 1st interval `[1, 4]` does not have any previous intervals to compare + with, so skip it. +- The 2nd interval `[3, 5]` does conflict with previous interval `[1, 4]`. +- The 3rd interval `[6, 8]` does not conflict with any of the previous + intervals `[1, 4]` and `[3, 5]`, so skip it. +- The 4th interval `[12, 13]` again does not conflict with any previous + intervals `[1, 4]`, `[3, 5]`, or `[6, 8]` so skip it. +- The 5th interval `[3, 20]` conflicts with the first interval `[1, 4]`. + +**Input:** + +```javascript +const intervals = [[3, 4], [5, 7], [6, 9], [10, 12], [13, 15]]; +``` + +**Output:** + +```javascript +[[6, 9]]; +``` + +### SOLUTION + +```javascript +function findConflictIntervals(intervals = [[]]) { + if (!Array.isArray(intervals)) return 'Input must be an 2-dimensional array'; + const [conflicts, ] = intervals.reduce(([conflicts, passed], [ a1, a2 ]) => { + const start = a1 < a2 ? a1: a2; + + const end = start === a1 ? a2: a1; + + if (passed.length === 0) { + passed.push([start, end]); + return [conflicts, passed]; + } + const conflict = passed.reduce((isConflict, [ b1, b2 ]) => { + if ( + (start >= b1 && start <= b2) || + (end >= b1 && end <= b2) || + (start <= b1 && end >= b2) + ) { + return true; + }; + return isConflict; + }, false); + if (conflict) { + conflicts.push([start, end]); + } else { + passed.push([start, end]); + } + return [conflicts, passed] + }, [[], []]); + return conflicts; +} +``` + +### ch-2.js + +Running `./ch-2.js` tests our solution against the folowing test cases, +printing `Passed` or `Failed` to the console: + + +#### Case 1: + +```javascript +{ + input: [[1, 4], [3, 5], [6, 8], [12, 13], [3, 20]], + output: [[3, 5], [3, 20]] +} +``` + +#### Case 2: + +```javascript +{ + input: [[3, 4], [5, 7], [6, 9], [10, 12], [13, 15]], + output: [[6, 9]] +} +``` + +#### Case 3: + +```javascript +{ + input: [[1.14, 1.56], [2.32, 3], [1.5, 1.72]], + output: [[1.5, 1.72]] +} +``` + +#### Case 4: + +```javascript +{ + input: [[-234, 10], [-1.12, 11], [11, 111111]], + output: [[-1.12, 11]] +} +``` + +#### Case 5: + +```javascript +{ + input: [[-1, -1], [1, 3], [3.1, 4], [4, 5]], + output: [[4, 5]] +} +``` + +### Custom Tests + +`./ch-2.js` will optionally accept a path to a test file or directory of test +files (ie: `$ ./ch-2.js ./local_test.json`). Test files must be properly +formatted JSON with both an `"input"` and an `"output"` key. + +#### Example Test + +```json +{ + "input": [ + [ + 1, + 3 + ], + [ + 2, + 4 + ], + [ + 6, + 7 + ] + ], + "output": [ + [ + 2, + 4 + ] + ] +} +``` diff --git a/challenge-127/iangoodnight/javascript/ch-1.js b/challenge-127/iangoodnight/javascript/ch-1.js new file mode 100755 index 0000000000..8daf2fe6e7 --- /dev/null +++ b/challenge-127/iangoodnight/javascript/ch-1.js @@ -0,0 +1,123 @@ +#!/usr/bin/env node + +/** + * Task #1 > Disjoint Sets + * ======================= + * + * You are given two sets with unique integers. + * Write a script to figure out if they are disjoint. + * + * The two sets are disjoint if they don't have any common members + * + * EXAMPLE + * Input: const s1 = [1, 2, 5, 3, 4]; + * const s2 = [4, 6, 7, 8, 9]; + * Output: false as the given sets have common member 4. + * + * Input: const s1 = [1, 3, 5, 7, 9]; + * const s2 = [0, 2, 4, 6, 8]; + * Output: true as the given two sets do no have a common member + **/ + +'use strict'; + +/** + * Node dependencies + **/ + +const fs = require('fs'); + +const path = require('path'); + +/** + * Here, the function to test our sets (PWC solution) + **/ + +function isDisjoint(set1 = [], set2 = []) { + const testSet = [...set1]; // shallow copy of the first set + + let disjoint = true; // trust, but verify + while (disjoint && testSet.length) { + const test = testSet.pop(); + if (set2.includes(test)) disjoint = false; + } + return disjoint; +} + +/** + * Followed by some utilities to test our solution + **/ + +const isFile = (filePath) => fs.lstatSync(filePath).isFile(); + +const isDirectory = (filePath) => fs.lstatSync(filePath).isDirectory(); + +function parseTestCase(filePath = '') { + try { + const data = fs.readFileSync(filePath, 'utf8'); + + const lines = data.split('\n'); + + if (!lines.length) throw new Error('Test cases improperly formatted'); + + const testData = lines.filter((line) => { + return line.length !== 0 && line.charAt(0) !== '#'; + }); + + const [ firstLine, secondLine, result ] = testData; + + const set1 = listToArray(firstLine); + + const set2 = listToArray(secondLine); + + const test = parseInt(result.trim()) === 1 ? true: false; + + return [set1, set2, test]; + } catch (err) { + console.log('Problems parsing test files: ', err); + } +} + +function listToArray(str) { + return str.split(',').map(el => el.trim()); +} + +function assertDisjoint([set1, set2, test], filePath = '') { + const disjoint = isDisjoint(set1, set2); + + const testPath = filePath !== '' ? `${filePath}: `: ''; + + if (disjoint === test) { + return console.log(`${testPath}\x1b[32m%s\x1b[0m`, 'Passed \u2690'); + } + return console.log(`${testPath}\x1b[31m%s\x1b[0m`, 'Failed \u2715'); +} + +/** + * And, our test runner + **/ + +(function main() { + const testPath = process.argv[2] || '../test_cases/ch1'; + + try { + if (isFile(testPath)) { + const test = parseTestCase(data); + + return assertDisjoint(test, testPath); + } + if (isDirectory(testPath)) { + fs.readdirSync(testPath).map(fileName => { + const filePath = path.join(testPath, fileName); + + const test = parseTestCase(filePath); + + assertDisjoint(test, filePath); + }); + return; + } + return 'No tests found'; + } catch (error) { + console.log('Something went wrong: ', error); + } +})(); diff --git a/challenge-127/iangoodnight/javascript/ch-2.js b/challenge-127/iangoodnight/javascript/ch-2.js new file mode 100755 index 0000000000..0a5012f346 --- /dev/null +++ b/challenge-127/iangoodnight/javascript/ch-2.js @@ -0,0 +1,163 @@ +#!/usr/bin/env node + +/** + * Task #2 > Conflict Intervals + * ============================ + * + * You are given a list of intervals. + * Write a script to find out if the current interval conflicts with any of the + * previous intervals. + * + * EXAMPLE + * Input: const intervals = [[1, 4], [3, 5], [6, 8], [12, 13], [3, 20]]; + * Output: [[3, 5], [3, 20]] + * + * - The 1st interval [1, 4] does not have any previous intervals to compare + * with, so skip it. + * - The 2nd interval [3, 5] does conflict with previous interval [1, 4]. + * - The 3rd interval [6, 8] does not conflict with any of the previous + * intervals [1, 4] and [3, 5], so skip it. + * - The 4th interval [12, 13] again does not conflict with any previous + * intervals [1, 4], [3, 5], or [6, 8] so skip it. + * - The 5th interval [3, 20] conflicts with the first interval [1, 4]. + * + * Input: const intervals = [[3, 4], [5, 7], [6, 9], [10, 12], [13, 15]]; + * Output: [[6, 9]]; + **/ + +'use strict'; + +/** + * Built-in Node dependencies + **/ + +const fs = require('fs'); + +const path = require('path'); + +/** + * Here, the function to test our intervals (PWC solution) + **/ + +function findConflictIntervals(intervals = [[]]) { + if (!Array.isArray(intervals)) return 'Input must be an 2-dimensional array'; + const [conflicts, ] = intervals.reduce(([conflicts, passed], [ a1, a2 ]) => { + const start = a1 < a2 ? a1: a2; + + const end = start === a1 ? a2: a1; + + if (passed.length === 0) { + passed.push([start, end]); + return [conflicts, passed]; + } + const conflict = passed.reduce((isConflict, [ b1, b2 ]) => { + if ( + (start >= b1 && start <= b2) || + (end >= b1 && end <= b2) || + (start <= b1 && end >= b2) + ) { + return true; + }; + return isConflict; + }, false); + if (conflict) { + conflicts.push([start, end]); + } else { + passed.push([start, end]); + } + return [conflicts, passed] + }, [[], []]); + return conflicts; +} + +/** + * Followed by some utilities to test our solution + **/ + + +const isFile = (filePath) => fs.lstatSync(filePath).isFile(); + +const isDirectory = (filePath) => fs.lstatSync(filePath).isDirectory(); + +function parseTestCase(filePath = '') { + try { + const data = fs.readFileSync(filePath, 'utf8'); + + const { input, output } = JSON.parse(data); + + return { input, output }; + } catch (err) { + console.log( + 'Problems parsing text files. Is the JSON properly formatted?', + err + ); + } +} + +function sortIntervals(intervalsArr = [[]]) { + const intervalsSorted = intervalsArr.map(([x, y]) => { + return x > y ? [x, y]: [y, x]; + }); + intervalsSorted.sort(([a1, a2], [b1, b2]) => { + if (a1 === b1) return b1 < b2 ? -1: b1 === b2 ? 0: 1; + if (a1 < b1) return -1; + return 1; + }); + return intervalsSorted; +} + +function compareIntervalSets(set1 = [[]], set2 = [[]]) { + if (!Array.isArray(set1) || !Array.isArray(set2)) return false; + if (set1.length !== set2.length) return false; + const set1Sorted = sortIntervals(set1); + + const set2Sorted = sortIntervals(set2); + + return set1Sorted.reduce((equal, [a1, a2], idx) => { + const [ b1, b2 ] = set2Sorted[idx]; + + return equal && a1 === b1 && a2 === b2; + }, true); +} + +function assertConflictIntervals(input = [[]], output = [[]], filePath = '') { + const conflicts = findConflictIntervals(input); + + const passed = compareIntervalSets(conflicts, output); + + const testPath = filePath === '' ? '' : `${filePath}: `; + + if (passed) { + return console.log(`${testPath}\x1b[32m%s\x1b[0m`, 'Passed \u2690'); + } + return console.log(`${testPath}\x1b[31m%s\x1b[0m`, 'Failed \u2715'); +} + +/** + * And, our test runner + **/ + +(function main() { + const testPath = process.argv[2] || '../test_cases/ch2'; + + try { + if (isFile(testPath)) { + const { input, output } = parseTestCase(testPath); + + return assertConflictIntervals(input, output, testPath); + } + if (isDirectory(testPath)) { + fs.readdirSync(testPath).map(fileName => { + const filePath = path.join(testPath, fileName); + + const { input, output } = parseTestCase(filePath); + + assertConflictIntervals(input, output, filePath); + }); + return; + } + return 'No tests found'; + } catch (error) { + console.log('Something went wrong: ', error); + } +})(); diff --git a/challenge-127/iangoodnight/perl/README.md b/challenge-127/iangoodnight/perl/README.md new file mode 100644 index 0000000000..7e1c234ec3 --- /dev/null +++ b/challenge-127/iangoodnight/perl/README.md @@ -0,0 +1,253 @@ +# Perl Weekly Challenge Club - 127 + +This is my first time submitting! I had a lot of fun and it was a good excuse +to work on my Perl. + +## Task 1 > Disjoint Sets + +You are given two sets with unique integers. +Write a script to figure out if they are disjoint. + +The two sets are disjoint if they don't have any common members + +### EXAMPLE + +**Input:** + +```perl +@s1 = (1, 2, 5, 3, 4); +@s2 = (4, 6, 7, 8, 9); +``` + +**Output:** `0` as the given sets have common member `4`. + +**Input:** + +```perl +@s1 = (1, 3, 5, 7, 9); +@s2 = (0, 2, 4, 6, 8); +``` + +**Output:** `1` as the given two sets do no have a common member. + +### SOLUTION + +```perl +sub is_disjoint { + my ($set1_ref, $set2_ref) = @_; # Our two sets for comparison + my @test_set = @$set1_ref; # Shallow copy + my %haystack = map { $_ => 1 } @$set2_ref; # Copy array values into a hash + my $disjoint = 1; # Trust, but verify + while ($disjoint and scalar @test_set) { # Iterate through copy + my $needle = pop(@test_set); + $disjoint = 0 if exists($haystack{$needle}); # Test if disjoint + } + return $disjoint; +} +``` + +### ch-1.pl + +Running `./ch-1.pl` tests our solution against the following test cases, +printing `Passed` or `Failed` to the console: + +#### Case 1: + +```perl +1,2,5,3,4 +4,6,7,8,9 +0 # false, 4 is not unique +``` + +#### Case 2: + +```perl +1,3,5,7,9 +0,2,4,6,8 +1 +``` + +#### Case 3: + +```perl +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +2 +1 # Technically, the sets are still disjoint +``` + +#### Case 4: + +```perl +😺,😸,😹,😻,😼,😽 +😽,🙀,😿,😾 +0 # False +``` + +#### Case 5: + +```perl +🙂,🙃,😉,😌,😍,🥰,😘,😗,😙,😚,😋 +🤤,😪,😵,🤐,🥴,🤢,🤮,🤧,😷,🤒,🤕 +1 # True +``` + +### Custom Tests + +`./ch-1.pl` will optionally accept a path to a test file or directory of test +files (ie: `$ ./ch-1.pl ./local_test.txt`). Test files must include no more than +2 lines of comma separated values with the last line being either `0` for tests +that should fail or `1` for tests that should pass. Lines beginning with `#` +will be ignored. + +## Task 2 > Conflict Intervals + +You are given a list of intervals. +Write a script to find out if the current interval conflicts with any of the +previous intervals. + +### EXAMPLE + +**Input:** + +```perl +@intervals = ( [1, 4], [3, 5], [6, 8], [12, 13], [3, 20] ); +``` + +**Output:** + +```perl +( [3, 5], [3, 20] ) +``` + +- The 1st interval `[1, 4]` does not have any previous intervals to compare + with, so skip it. +- The 2nd interval `[3, 5]` does conflict with previous interval `[1, 4]`. +- The 3rd interval `[6, 8]` does not conflict with any of the previous + intervals `[1, 4]` and `[3, 5]`, so skip it. +- The 4th interval `[12, 13]` again does not conflict with any previous + intervals `[1, 4]`, `[3, 5]`, or `[6, 8]` so skip it. +- The 5th interval `[3, 20]` conflicts with the first interval `[1, 4]`. + +**Input:** + +```perl +@intervals = ( [3, 4], [5, 7], [6, 9], [10, 12], [13, 15] ); +``` + +**Output:** + +```perl +( [6, 9] ); +``` + +### SOLUTION + +```perl + +sub find_conflict_intervals { + my $set_ref = shift @_; + my @conflicts = (); + my @passed = (); + if (reftype $set_ref ne "ARRAY") { + print "Array reference not found\n"; + return 0; + } + foreach my $set (@$set_ref) { + my ($a1, $a2) = sort { $a <=> $b } @$set; + my $conflict = grep { + my ($b1, $b2) = sort { $a <=> $b } @$_; + ($a1 >= $b1 && $a1 <= $b2) || + ($a2 >= $b1 && $a2 <= $b2) || + ($a1 <= $b1 && $a2 >= $b2); + } @passed; + push @passed, [$a1, $a2] if not $conflict; + push @conflicts, [$a1, $a2] if $conflict; + } + return \@conflicts; +} + +``` + +### ch-2.pl + +Running `./ch-2.pl` tests our solution against the folowing test cases, +printing `Passed` or `Failed` to the console: + + +#### Case 1: + +```perl +{ + "input" => ([1, 4], [3, 5], [6, 8], [12, 13], [3, 20]), + "output" => ([3, 5], [3, 20]) +} +``` + +#### Case 2: + +```perl +{ + "input" => ([3, 4], [5, 7], [6, 9], [10, 12], [13, 15]), + "output" => ([6, 9]) +} +``` + +#### Case 3: + +```perl +{ + "input" => ([1.14, 1.56], [2.32, 3], [1.5, 1.72]), + "output" => ([1.5, 1.72]) +} +``` + +#### Case 4: + +```perl +{ + "input" => ([-234, 10], [-1.12, 11], [11, 111111]), + "output" => ([-1.12, 11]) +} +``` + +#### Case 5: + +```perl +{ + "input" => ([-1, -1], [1, 3], [3.1, 4], [4, 5]), + "output" => ([4, 5]) +} +``` + +### Custom Tests + +`./ch-2.pl` will optionally accept a path to a test file or directory of test +files (ie: `$ ./ch-2.pl ./local_test.json`). Test files must be properly +formatted JSON with both an `"input"` and an `"output"` key. + +#### Example Test + +```json +{ + "input": [ + [ + 1, + 3 + ], + [ + 2, + 4 + ], + [ + 6, + 7 + ] + ], + "output": [ + [ + 2, + 4 + ] + ] +} +``` diff --git a/challenge-127/iangoodnight/perl/ch-1.pl b/challenge-127/iangoodnight/perl/ch-1.pl new file mode 100755 index 0000000000..e5572d8929 --- /dev/null +++ b/challenge-127/iangoodnight/perl/ch-1.pl @@ -0,0 +1,120 @@ +#!/usr/bin/perl + +=begin comment + + Task #1 > Disjoint Sets + ======================= + + You are given two sets with unique integers. + Write a script to figure out if they are disjoint. + + The two sets are disjoint if they don't have any common members + + EXAMPLE + Input: @s1 = (1, 2, 5, 3, 4); + @s2 = (4, 6, 7, 8, 9); + Output: 0 as the given sets have common member 4. + + Input: @s1 = (1, 3, 5, 7, 9); + @s2 = (0, 2, 4, 6, 8); + Output: 1 as the given two sets do no have a common member + +=end comment +=cut + +use strict; +use warnings; +use utf8; +use open ":std", ":encoding(UTF-8)"; +use Term::ANSIColor; + +# Here, our subroutine to test our sets (PWC solution) + +sub is_disjoint { + my ($set1_ref, $set2_ref) = @_; + my @test_set = @$set1_ref; + my %haystack = map { $_ => 1 } @$set2_ref; + my $disjoint = 1; + while ($disjoint and scalar @test_set) { + my $needle = pop(@test_set); + $disjoint = 0 if exists($haystack{$needle}); + } + return $disjoint; +} + +# Followed by some utilities to test our solution + +sub parse_test_case { + my $filename = shift; + my $first_line = ""; + my $second_line = ""; + my $test = ""; + + open my $fh, "<", $filename + or die "Could not open '$filename' - $!\n"; + + while (my $line = <$fh>) { + chomp $line; + next if $line =~ /^#/; + unless (length $first_line) { + $first_line = $line; + next; + } + unless (length $second_line) { + $second_line = $line; + next; + } + if ($line eq "1" || $line eq "0") { + $test = int($line); + last; + } + } + close $fh; + my @first_set = split /\s*,\s*/, $first_line; + my @second_set = split /\s*,\s*/, $second_line; + + return (\@first_set, \@second_set, $test); +} + +sub assert_disjoint { + my ($set1_ref, $set2_ref, $test) = @_; + my $disjoint = is_disjoint($set1_ref, $set2_ref); + if ($test eq $disjoint) { + print color("green"), "Passed \x{2690}\n", color("reset"); + } else { + print color("red"), "Failed \x{2715}\n", color("reset"); + } +} + +# And our test runner + +sub main { + my $target = shift @ARGV // "../test_cases/ch1"; + + if (-e -r -f $target) { # handle file target + my ($set1_ref, $set2_ref, $test) = parse_test_case $target; + + print $target, ": "; + assert_disjoint $set1_ref, $set2_ref, $test; + return; + } elsif (-e -r -d _) { # handle directory target + $target =~ s/^(.*?)\/?$/$1\//; # check for trailing slash + opendir my $dh, $target + or die "Could not open '$target' - $!\n"; + my @entries = readdir $dh; + foreach my $entry (@entries) { + if ($entry eq '.' or $entry eq '..') { + next; + } + my $path = $target . $entry; + my ($set1_ref, $set2_ref, $test) = parse_test_case $path; + print $path, ": "; + assert_disjoint $set1_ref, $set2_ref, $test; + } + closedir $dh + } else { + print "No test files found\n"; + } +} + +main(); diff --git a/challenge-127/iangoodnight/perl/ch-2.pl b/challenge-127/iangoodnight/perl/ch-2.pl new file mode 100755 index 0000000000..b4cd2c14ee --- /dev/null +++ b/challenge-127/iangoodnight/perl/ch-2.pl @@ -0,0 +1,144 @@ +#!/usr/bin/perl + +=begin comment + + Task #2 > Conflict Intervals + ============================ + + You are given a list of intervals. + Write a script to find out if the current interval conflicts with any of the + previous intervals. + + EXAMPLE + Input: @intervals = ( [1, 4], [3, 5], [6, 8], [12, 13], [3, 20] ); + Output: ( [3, 5], [3, 20] ) + + - The 1st interval [1, 4] does not have any previous intervals to compare + with, so skip it. + - The 2nd interval [3, 5] does conflict with previous interval [1, 4]. + - The 3rd interval [6, 8] does not conflict with any of the previous + intervals [1, 4] and [3, 5], so skip it. + - The 4th interval [12, 13] again does not conflict with any previous + intervals [1, 4], [3, 5], or [6, 8] so skip it. + - The 5th interval [3, 20] conflicts with the first interval [1, 4]. + + Input: @intervals = ( [3, 4], [5, 7], [6, 9], [10, 12], [13, 15] ); + Output: ( [6, 9] ); + +=end comment +=cut + +use strict; +use warnings; +use utf8; +use open ":std", ":encoding(UTF-8)"; +use Term::ANSIColor; +use Scalar::Util qw( reftype ); +use JSON qw( decode_json ); + +# Here, our subroutine to test our intervals (PWC solution) +sub find_conflict_intervals { + my $set_ref = shift @_; + my @conflicts = (); + my @passed = (); + if (reftype $set_ref ne "ARRAY") { + print "Array reference not found\n"; + return 0; + } + foreach my $set (@$set_ref) { + my ($a1, $a2) = sort { $a <=> $b } @$set; + my $conflict = grep { + my ($b1, $b2) = sort { $a <=> $b } @$_; + ($a1 >= $b1 && $a1 <= $b2) || + ($a2 >= $b1 && $a2 <= $b2) || + ($a1 <= $b1 && $a2 >= $b2); + } @passed; + push @passed, [$a1, $a2] if not $conflict; + push @conflicts, [$a1, $a2] if $conflict; + } + return \@conflicts; +} + +# Followed by some utilities to test our solution + +sub parse_test_case { + my $filename = shift; + + open my $fh, "<", $filename + or die "Could not open '$filename' - $!\n"; + read $fh, my $json, -s $fh; + close $fh; + my $data = decode_json $json; + return $data; +} + +sub sort_compare { + return ($a->[0] <=> $b->[0]) || ($a->[1] <=> $b->[1]); +} + +sub compare_interval_sets { + my ($output, $compare) = @_; + if (reftype $output ne "ARRAY" || reftype $compare ne "ARRAY") { + print "Output not formatted correctly\n"; + return 0; + } + my $length = scalar @$output; + if ($length != scalar @$compare) { + return 0; + } + my @sorted_output = sort sort_compare @$output; + my @sorted_compare = sort sort_compare @$compare; + for my $i (0 .. $length) { + my $test = $sorted_output[0]; + my $answer = $sorted_compare[0]; + if ($test->[0] != $answer->[0] || $test->[1] != $answer->[1]) { + return 0; + } + } + return 1; +} + +sub assert_conflicts { + my ($input, $output) = @_; + my $compare = find_conflict_intervals $input; + my $equal = compare_interval_sets $output, $compare; + print color("green"), "Passed \x{2690}\n", color("reset") if $equal; + print color("red"), "Failed \x{2715}\n", color("reset") if not $equal; +} + +# And our test runner + +sub main { + my $target = shift @ARGV // "../test_cases/ch2"; + + if (-e -r -f $target) { + my $json = parse_test_case $target; + my $input = %$json{"input"}; + my $output = %$json{"output"}; + print $target |
