aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2021-08-30 02:23:47 +0100
committerGitHub <noreply@github.com>2021-08-30 02:23:47 +0100
commit8f2f826dbe6715ab595c4470501f3a8498be15bf (patch)
tree2077d001729ddc6ece592edea75498d295e87062
parent7f347f9280b1882236ba6c508dd80779e98dfa8e (diff)
parent9a3a45332da9fee3fd57d81eb017277a0cdcdc56 (diff)
downloadperlweeklychallenge-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
-rw-r--r--challenge-127/iangoodnight/README.md1
-rw-r--r--challenge-127/iangoodnight/javascript/README.md261
-rwxr-xr-xchallenge-127/iangoodnight/javascript/ch-1.js123
-rwxr-xr-xchallenge-127/iangoodnight/javascript/ch-2.js163
-rw-r--r--challenge-127/iangoodnight/perl/README.md253
-rwxr-xr-xchallenge-127/iangoodnight/perl/ch-1.pl120
-rwxr-xr-xchallenge-127/iangoodnight/perl/ch-2.pl144
-rw-r--r--challenge-127/iangoodnight/test_cases/ch1/case1.txt4
-rw-r--r--challenge-127/iangoodnight/test_cases/ch1/case2.txt4
-rw-r--r--challenge-127/iangoodnight/test_cases/ch1/case3.txt6
-rw-r--r--challenge-127/iangoodnight/test_cases/ch1/case4.txt4
-rw-r--r--challenge-127/iangoodnight/test_cases/ch1/case5.txt4
-rw-r--r--challenge-127/iangoodnight/test_cases/ch2/case1.json34
-rw-r--r--challenge-127/iangoodnight/test_cases/ch2/case2.json30
-rw-r--r--challenge-127/iangoodnight/test_cases/ch2/case3.json22
-rw-r--r--challenge-127/iangoodnight/test_cases/ch2/case4.json22
-rw-r--r--challenge-127/iangoodnight/test_cases/ch2/case5.json26
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