aboutsummaryrefslogtreecommitdiff
path: root/challenge-146
diff options
context:
space:
mode:
authorIan Goodnight <goodnight.ian@gmail.com>2022-01-09 16:34:53 -0500
committerIan Goodnight <goodnight.ian@gmail.com>2022-01-09 16:34:53 -0500
commit329ea0e601d67c6eb37e8677aba54922a4680603 (patch)
treec6b1c34463dc123496b231f15f19a35a323754c6 /challenge-146
parentbf0e37317122de0322d69b1c68bd07ccfc5b36a8 (diff)
downloadperlweeklychallenge-club-329ea0e601d67c6eb37e8677aba54922a4680603.tar.gz
perlweeklychallenge-club-329ea0e601d67c6eb37e8677aba54922a4680603.tar.bz2
perlweeklychallenge-club-329ea0e601d67c6eb37e8677aba54922a4680603.zip
challenge 146
Diffstat (limited to 'challenge-146')
-rwxr-xr-xchallenge-146/iangoodnight/javascript/ch-1.js73
-rwxr-xr-xchallenge-146/iangoodnight/javascript/ch-2.js257
-rwxr-xr-xchallenge-146/iangoodnight/perl/ch-1.pl96
-rwxr-xr-xchallenge-146/iangoodnight/perl/ch-2.pl242
-rwxr-xr-xchallenge-146/iangoodnight/python/ch-1.py77
-rwxr-xr-xchallenge-146/iangoodnight/ruby/ch-1.rb65
-rwxr-xr-xchallenge-146/iangoodnight/ruby/ch-2.rb158
7 files changed, 968 insertions, 0 deletions
diff --git a/challenge-146/iangoodnight/javascript/ch-1.js b/challenge-146/iangoodnight/javascript/ch-1.js
new file mode 100755
index 0000000000..dd9b97965f
--- /dev/null
+++ b/challenge-146/iangoodnight/javascript/ch-1.js
@@ -0,0 +1,73 @@
+#!/usr/bin/env node
+// ch-1.js
+
+/*******************************************************************************
+ * https://theweeklychallenge.org/blog/perl-weekly-challenge-146/
+ *
+ * ## Task 1 > 10001st Prime Number
+ * ================================
+ *
+ * Write a script to generate the 10001st prime number.
+ ******************************************************************************/
+
+'use strict';
+
+/*******************************************************************************
+ * PWC Solution ****************************************************************
+ ******************************************************************************/
+
+function getPrime(numPrime = 10001) {
+ const primes = [2, 3];
+
+ const limit = parseInt(numPrime, 10);
+
+ const isPrime = (n) =>
+ !primes.some(
+ (prime) => prime <= Math.floor(Math.sqrt(n)) && n % prime === 0,
+ );
+
+ let num = 5;
+
+ while (primes.length < limit) {
+ if (isPrime(num)) primes.push(num);
+ num += 2;
+ }
+
+ return primes.slice(-1)[0];
+}
+
+/*******************************************************************************
+ * Utilities *******************************************************************
+ ******************************************************************************/
+
+function getSuffix(num) {
+ const lastDigit = parseInt([...num.toString()].pop(), 10);
+
+ if (lastDigit === 0 || lastDigit >= 4) return 'th';
+ if (lastDigit === 1) return 'st';
+ if (lastDigit === 2) return 'nd';
+ if (lastDigit === 3) return 'rd';
+ return '';
+}
+
+const colors = {
+ yellow: '\x1b[33m',
+ green: '\x1b[32m',
+ reset: '\x1b[0m',
+};
+
+/*******************************************************************************
+ * Main ************************************************************************
+ ******************************************************************************/
+
+(function main() {
+ const nth = process.argv[2] || 10001;
+
+ const prime = getPrime(nth);
+
+ const suffix = getSuffix(nth);
+
+ const { yellow: y, green: g, reset: r } = colors;
+
+ console.log(`The ${y + nth + suffix + r} prime number is: ${g + prime + r}`);
+})();
diff --git a/challenge-146/iangoodnight/javascript/ch-2.js b/challenge-146/iangoodnight/javascript/ch-2.js
new file mode 100755
index 0000000000..a71c81fa85
--- /dev/null
+++ b/challenge-146/iangoodnight/javascript/ch-2.js
@@ -0,0 +1,257 @@
+#!/usr/bin/env node
+// ch-2.js
+
+/*******************************************************************************
+ * https://theweeklychallenge.org/blog/perl-weekly-challenge-146/
+ *
+ * ## Task 2 > Curious Fraction Tree
+ * =================================
+ *
+ * Consider the following `Curious Fraction Tree`:
+ *
+ * ```
+ * __________1/1__________
+ * / \
+ * ___1/2___ ___2/1___
+ * / \ / \
+ * 1/3 3/2 2/3 3/1
+ * / \ / \ / \ / \
+ * 1/4 4/3 3/5 5/2 2/5 5/3 3/4 4/1
+ * ```
+ *
+ * You are given a fraction, member of the tree created similar to the above
+ * sample.
+ *
+ * Write a script to find out the parent and grandparent of the given member.
+ *
+ * **Example 1:**
+ *
+ * ```
+ * Input: $member = '3/5';
+ * Output: parent = '3/2' and grandparent = '1/2'
+ * ```
+ *
+ * **Example 2:**
+ *
+ * ```
+ * Input: $member = '4/3';
+ * Output: parent = '1/3' and grandparent = '1/2'
+ * ```
+ *
+ ******************************************************************************/
+
+'use strict';
+
+/*******************************************************************************
+ * Dependencies ****************************************************************
+ ******************************************************************************/
+
+const readline = require('readline');
+
+/*******************************************************************************
+ * PWC Solution ****************************************************************
+ ******************************************************************************/
+
+// Input guard
+function isFractionString(string) {
+ const re = /^\d+\/\d+$/;
+
+ return typeof string === 'string' && re.test(string.trim());
+}
+
+// Find immediate parent
+function getCuriousParent(member = '1/1') {
+ if (!isFractionString(member)) {
+ const badArg = '`getCuriousParent` expects a string (i.e.: "3/4")';
+
+ throw new Error(badArg);
+ }
+
+ const [num, den] = member.split('/').map((elem) => parseInt(elem, 10));
+
+ const value = num / den;
+
+ if (value > 1) return `${num - den}/${den}`;
+ if (value < 1) return `${num}/${den - num}`;
+ return null;
+}
+
+// Returns whole chain from member to root
+function getCuriousTree(member = '1/1') {
+ if (!isFractionString(member)) {
+ throw new Error('`getCuriousTree` expects a string (i.e.: "3/2")');
+ }
+
+ const parents = [];
+
+ let child = member;
+
+ while (child !== null) {
+ parents.push(child);
+ child = getCuriousParent(child);
+ }
+
+ return parents;
+}
+
+// Not really necessarily to abstract this step, but it makes it easier to do
+// other things with the whole chain, like figure out if it is rooted at 1/1 or
+// not
+function getCuriousGenerations(tree = [], generations = 2) {
+ return tree.slice(1, generations + 1);
+}
+
+/*******************************************************************************
+ * Utilities *******************************************************************
+ ******************************************************************************/
+
+const colors = {
+ green: '\x1b[32m',
+ red: '\x1b[31m',
+ reset: '\x1b[0m',
+ yellow: '\x1b[33m',
+};
+
+function formatOutput(tree = [], termColors = {}) {
+ const { yellow: y = '', green: g = '', reset: rst = '' } = termColors;
+
+ return tree
+ .map((member, idx, array) => {
+ const { length } = array;
+
+ const conjunction = idx === length - 1 && length !== 1 ? 'and ' : '';
+
+ const last = idx === length - 1;
+
+ let ordinal = idx === 0 ? 'parent' : 'grandparent';
+
+ if (idx > 1) ordinal = 'great-'.repeat(idx - 1) + ordinal;
+
+ let separator = last ? '' : ', ';
+
+ if (length === 2) separator = ' ';
+
+ return `${conjunction}${y + ordinal + rst} = ${
+ g + member + rst
+ }${separator}`;
+ })
+ .join('');
+}
+
+function assertRoot(tree = [], termColors = {}) {
+ const last = [...tree].pop();
+
+ const {
+ green: g = '',
+ red: r = '',
+ reset: rst = '',
+ yellow: y = '',
+ } = termColors;
+
+ if (last !== '1/1') {
+ return `${y}Curious fraction tree rooted at ${
+ r + last + y
+ } and not at ${`${g}1/1${rst}`}`;
+ }
+ return null;
+}
+
+function repl(termColors = {}) {
+ const { green: g = '', yellow: y = '', reset: rst = '' } = termColors;
+
+ return new Promise((resolve) => {
+ const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ });
+
+ /* eslint-disable prefer-template */
+ const mainPrompt =
+ 'Enter a fraction (i.e.: "' +
+ g +
+ '3/5' +
+ rst +
+ '") or type "' +
+ y +
+ 'exit' +
+ rst +
+ '" to quit /> ';
+
+ const morePrompt =
+ 'Enter "' +
+ y +
+ 'more' +
+ rst +
+ '" to see more details, or enter a fraction (i.e.: "' +
+ g +
+ '3/5' +
+ rst +
+ '") to continue /> ';
+ /* eslint-enable prefer-template */
+
+ let currentTree;
+ let generations;
+ let assertedRoot;
+
+ rl.setPrompt(mainPrompt);
+
+ rl.prompt();
+
+ rl.on('line', (line) => {
+ const input = line.trim().toLowerCase();
+
+ if (input === 'exit' || input === 'quit' || input === 'q') {
+ return rl.close();
+ }
+
+ if (isFractionString(input)) {
+ currentTree = getCuriousTree(input);
+ generations = getCuriousGenerations(currentTree, 2);
+ assertedRoot = assertRoot(currentTree, termColors);
+ console.log(formatOutput(generations, termColors));
+ rl.setPrompt(morePrompt);
+ return rl.prompt();
+ }
+
+ if (input === 'more') {
+ console.log(formatOutput(currentTree.slice(1), termColors));
+ if (assertedRoot) console.log(assertedRoot);
+ currentTree = null;
+ generations = null;
+ assertedRoot = null;
+ rl.setPrompt(mainPrompt);
+ return rl.prompt();
+ }
+ console.log(`Sorry, I don't understand ${input}.`);
+ return rl.prompt();
+ }).on('close', () => {
+ console.log(`${y}Goodbye.${rst}`);
+ resolve();
+ });
+ });
+}
+
+function printBanner(termColors = {}) {
+ const { yellow: y = '', reset: rst = '' } = termColors;
+
+ const message = 'A Curious Fraction Tree';
+
+ const underline = '='.repeat(message.length);
+
+ console.log(`${y}${message}`);
+ console.log(`${underline}${rst}`);
+}
+
+/*******************************************************************************
+ * Main ************************************************************************
+ ******************************************************************************/
+
+(async function main() {
+ try {
+ printBanner(colors);
+ await repl(colors);
+ } catch (error) {
+ console.log(error);
+ process.exit(1);
+ }
+})();
diff --git a/challenge-146/iangoodnight/perl/ch-1.pl b/challenge-146/iangoodnight/perl/ch-1.pl
new file mode 100755
index 0000000000..34d5b75eff
--- /dev/null
+++ b/challenge-146/iangoodnight/perl/ch-1.pl
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+# ch-1.pl
+
+=begin comment
+
+ https://theweeklychallenge.org/blog/perl-weekly-challenge-146/
+
+ ## Task 1 > 10001st Prime Number
+ ================================
+
+ Write a script to generate the 10001st prime number.
+
+=end comment
+=cut
+
+use strict;
+use warnings;
+
+################################################################################
+# PWC Solution #################################################################
+################################################################################
+
+sub get_prime {
+ my $nth = shift;
+ my @primes = ( 2, 3 );
+ my $num = 5;
+
+ if ( $nth <= 2 ) {
+ return $primes[ $nth - 1 ];
+ }
+
+ while ( scalar @primes < $nth ) {
+ my $is_prime = 1;
+ for my $prime (@primes) {
+ if ( int( sqrt($num) ) < $prime ) {
+ last;
+ }
+ if ( $num % $prime == 0 ) {
+ $is_prime = 0;
+ last;
+ }
+ }
+ if ($is_prime) {
+ push @primes, $num;
+ }
+
+ $num += 2;
+ }
+ return $primes[-1];
+}
+
+################################################################################
+# Utilities ####################################################################
+################################################################################
+
+sub get_suffix {
+ my $last_digit = substr shift, -1;
+
+ if ( $last_digit == 0 || $last_digit >= 4 ) {
+ return 'th';
+ }
+ if ( $last_digit == 1 ) {
+ return 'st';
+ }
+ if ( $last_digit == 2 ) {
+ return 'nd';
+ }
+ if ( $last_digit == 3 ) {
+ return 'rd';
+ }
+ return q{ };
+}
+
+sub color_string {
+ my $str = shift;
+ my $color = shift;
+ my %colors = (
+ yellow => "\e[33m",
+ green => "\e[32m",
+ );
+ my $reset = "\e[0m";
+
+ return $colors{$color} . $str . $reset;
+}
+
+################################################################################
+# Main #########################################################################
+################################################################################
+
+my $nth = shift @ARGV // '10001';
+my $prime = get_prime $nth;
+my $suffix = get_suffix $nth;
+my $num_string = color_string( $nth . $suffix, 'yellow' );
+my $prime_string = color_string( $prime, 'green' );
+
+print "The $num_string prime number is $prime_string\n";
diff --git a/challenge-146/iangoodnight/perl/ch-2.pl b/challenge-146/iangoodnight/perl/ch-2.pl
new file mode 100755
index 0000000000..a8bc926997
--- /dev/null
+++ b/challenge-146/iangoodnight/perl/ch-2.pl
@@ -0,0 +1,242 @@
+#!/usr/bin/perl
+# ch-2.pl
+
+=begin comment
+
+ https://theweeklychallenge.org/blog/perl-weekly-challenge-146/
+
+ ## Task 2 > Curious Fraction Tree
+ =================================
+
+ Consider the following `Curious Fraction Tree`:
+
+ ```
+ __________1/1__________
+ / \
+ ___1/2___ ___2/1___
+ / \ / \
+ 1/3 3/2 2/3 3/1
+ / \ / \ / \ / \
+ 1/4 4/3 3/5 5/2 2/5 5/3 3/4 4/1
+ ```
+
+ You are given a fraction, member of the tree created similar to the above
+ sample.
+
+ Write a script to find out the parent and grandparent of the given member.
+
+ **Example 1:**
+
+ ```
+ Input: $member = '3/5';
+ Output: parent = '3/2' and grandparent = '1/2'
+ ```
+
+ **Example 2:**
+
+ ```
+ Input: $member = '4/3';
+ Output: parent = '1/3' and grandparent = '1/2'
+ ```
+
+=end comment
+=cut
+
+use strict;
+use warnings;
+
+################################################################################
+# PWC Solution #################################################################
+################################################################################
+
+sub is_fraction_string {
+ my $string = shift;
+
+ if ( $string =~ m{ \A \s* \d+ / \d+ \s* \z }x ) {
+ $string =~ s/ \A \s+ | \s+ \z//gx;
+ return $string;
+ }
+ return 0;
+}
+
+sub get_curious_parent {
+ my $member = shift;
+ my $fraction = is_fraction_string $member;
+
+ die qq{`get_curious_parent` expects a string (i.e.: "3/5"), got $member\n}
+ unless $fraction;
+
+ my ( $num, $den ) = split qr{ / }x, $fraction;
+ my $quotient = $num / $den;
+
+ if ( $quotient > 1 ) {
+ return ( $num - $den ) . q{/} . $den;
+ }
+ if ( $quotient < 1 ) {
+ return $num . q{/} . ( $den - $num );
+ }
+ return 0;
+}
+
+sub get_curious_tree {
+ my $member = shift;
+ my $fraction = is_fraction_string $member;
+
+ die qq{`get_curious_tree` expects a string (i.e.: "3/5"), got $member\n}
+ unless $fraction;
+
+ my @parents;
+ my $child = $fraction;
+
+ while ($child) {
+ push @parents, $child;
+ $child = get_curious_parent $child;
+ }
+
+ return \@parents;
+}
+
+sub get_curious_generations {
+ my @tree = @{ +shift };
+ my $generations = shift // 2;
+
+ if ( scalar @tree == 2 ) {
+ return [ $tree[1] ];
+ }
+
+ my @slice = @tree[ 1 .. $generations ];
+
+ return \@slice;
+}
+
+################################################################################
+# Utilities ####################################################################
+################################################################################
+
+sub color {
+ my $color = shift;
+ my $str = shift // q{};
+ my $reset = "\e[0m";
+ my %colors = (
+ green => "\e[32m",
+ none => q{},
+ red => "\e[31m",
+ yellow => "\e[33m",
+ );
+ return $colors{$color} . $str . $reset;
+}
+
+sub format_results {
+ my @tree = @{ +shift };
+ my $length = @tree;
+ my $results = q{};
+
+ for my $idx ( 0 .. $#tree ) {
+ my $relation = $idx == 0 ? 'parent' : 'grandparent';
+ my $great = $idx > 1 ? 'great-' x ( $idx - 1 ) : q{};
+ my $ordinal = color 'yellow', qq{$great$relation};
+ my $member = color 'green', $tree[$idx];
+ my $separator =
+ $idx == 0 ? q{} : $length <= 2 || $idx == $length - 2 ? ' and ' : ', ';
+ $results = $results . $separator . "$ordinal = $member";
+ }
+ return $results;
+}
+
+sub assert_root {
+ my @tree = @{ +shift };
+ my $last_elem = $tree[-1];
+
+ if ( $last_elem ne '1/1' ) {
+ my $beginning = color 'yellow', 'Curious fraction tree rooted at ';
+ my $root = color 'red', $last_elem;
+ my $middle = color 'yellow', ' and not at ';
+ my $end = color 'green', '1/1';
+ return $beginning . $root . $middle . $end;
+ }
+ return 0;
+}
+
+sub repl {
+ my $prompt =
+ 'Enter a fraction (i.e.: "'
+ . color( 'green', '3/5' )
+ . '") or type "'
+ . color( 'yellow', 'exit' )
+ . '" to quit /> ';
+ my $continue_prompt =
+ 'Enter "'
+ . color( 'yellow', 'more' )
+ . '" to see more details, or enter a fraction (i.e.: "'
+ . color( 'green', '3/5' )
+ . '") to continue /> ';
+ my $current_tree;
+ my $generations;
+ my $assert_root;
+
+ print $prompt;
+
+ while ( my $line = <> ) {
+ chomp $line;
+
+ $line =~ s/ \A \s+ | \s+ \z //gx;
+
+ if ( $line eq 'exit' || $line eq 'quit' || $line eq 'q' ) {
+ print color( 'yellow', 'Goodbye.' ), "\n";
+ return 0;
+ }
+
+ if ( $current_tree && $line eq 'more' ) {
+ my @tree = @{$current_tree};
+ my $slice = [ @tree[ 1 .. $#tree ] ];
+ print format_results($slice), "\n";
+
+ if ($assert_root) {
+ print $assert_root, "\n";
+ }
+
+ $current_tree = undef;
+ $generations = undef;
+ $assert_root = undef;
+
+ print $prompt;
+ next;
+ }
+
+ my $fraction = is_fraction_string $line;
+
+ if ($fraction) {
+ if ( $fraction eq '1/1' ) {
+ my $message = color( 'green', '1/1' ) . " is root.\n";
+ print $message;
+ print $prompt;
+ next;
+ }
+
+ $current_tree = get_curious_tree $fraction;
+ $generations = get_curious_generations $current_tree;
+ $assert_root = assert_root $current_tree;
+ print format_results($generations), "\n";
+ print $continue_prompt;
+ next;
+ }
+
+ print "Sorry, I don't understand $line\n";
+ print $prompt;
+ }
+ return 0;
+}
+
+sub print_banner {
+ my $message = 'A Curious Fracion Tree';
+ my $underline = q{=} x length $message;
+
+ return print color( 'yellow', $message . "\n" . $underline . "\n" );
+}
+
+################################################################################
+# Main #########################################################################
+################################################################################
+
+print_banner();
+repl();
diff --git a/challenge-146/iangoodnight/python/ch-1.py b/challenge-146/iangoodnight/python/ch-1.py
new file mode 100755
index 0000000000..840d58770f
--- /dev/null
+++ b/challenge-146/iangoodnight/python/ch-1.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python3
+# ch-1.py
+
+# > https://theweeklychallenge.org/blog/perl-weekly-challenge-146/
+#
+# ## Task 1 > 10001st Prime Number
+# ================================
+#
+# Write a script to generate the 10001st prime number.
+
+import math
+import sys
+
+###############################################################################
+# PWC Solution ################################################################
+###############################################################################
+
+
+def get_prime(nth):
+ primes = [2, 3]
+ cursor = 5
+ while (len(primes) < nth):
+ is_prime = True
+ for prime in primes:
+ if prime > math.sqrt(cursor):
+ break
+ if cursor % prime == 0:
+ is_prime = False
+ break
+ if is_prime:
+ primes.append(cursor)
+ cursor += 2
+ return primes.pop()
+
+
+###############################################################################
+# Utilities ###################################################################
+###############################################################################
+
+
+def get_suffix(num):
+ last_d = num % 10
+ if last_d == 0 or last_d >= 4:
+ return 'th'
+ if last_d == 1:
+ return 'st'
+ if last_d == 2:
+ return 'nd'
+ if last_d == 3:
+ return 'rd'
+ return ''
+
+
+def print_colors(string, color):
+ colordict = {
+ 'yellow': '\u001b[33m',
+ 'green': '\u001b[32m',
+ 'reset': '\u001b[0m'
+ }
+ return f"{colordict[color]}{string}{colordict['reset']}"
+
+
+###############################################################################
+# Main ########################################################################
+###############################################################################
+
+try:
+ nth = int(sys.argv[1])
+except IndexError:
+ nth = 10001
+
+prime = get_prime(nth)
+suffix = get_suffix(nth)
+num_str = print_colors(str(nth) + suffix, 'yellow')
+prime_str = print_colors(prime, 'green')
+
+print(f"The {num_str} prime number is {prime_str}")
diff --git a/challenge-146/iangoodnight/ruby/ch-1.rb b/challenge-146/iangoodnight/ruby/ch-1.rb
new file mode 100755
index 0000000000..4e621a31a4
--- /dev/null
+++ b/challenge-146/iangoodnight/ruby/ch-1.rb
@@ -0,0 +1,65 @@
+#!/usr/bin/ruby -w
+# ch-1.rb
+
+# > https://theweeklychallenge.org/blog/perl-weekly-challenge-146/
+#
+# ## Task 1 > 10001st Prime Number
+# ================================
+#
+# Write a script to generate the 10001st prime number.
+
+################################################################################
+# PWC Solution #################################################################
+################################################################################
+
+def get_prime(nth)
+ primes = [2, 3]
+ cursor = 5
+ until primes.length >= nth
+ is_prime = true
+ primes.each do |prime|
+ (prime > Math.sqrt(cursor) || !is_prime) && break
+ (cursor % prime).zero? && is_prime = false
+ end
+ is_prime && primes.push(cursor)
+ cursor += 2
+ end
+ nth <= 2 ? primes[nth - 1] : primes.pop
+end
+
+################################################################################
+# Utilities ####################################################################
+################################################################################
+
+def get_suffix(num)
+ last = num.to_s[-1].to_i
+ return 'th' if last.zero? || last >= 4
+ return 'st' if last == 1
+ return 'nd' if last == 2
+ return 'rd' if last == 3
+ ''
+end
+
+def print_colors(str, color)
+ reset = "\e[0m"
+ case color
+ when 'yellow'
+ "\e[33m#{str}#{reset}"
+ when 'green'
+ "\e[32m#{str}#{reset}"
+ else
+ str
+ end
+end
+
+################################################################################
+# Main #########################################################################
+################################################################################
+
+nth = ARGV[0] ? ARGV[0].to_i : 10_001
+prime = get_prime(nth)
+suffix = get_suffix(nth)
+num_str = print_colors(nth.to_s + suffix, 'yellow')
+prime_str = print_colors(prime, 'green')
+
+puts "The #{num_str} prime number is #{prime_str}"
diff --git a/challenge-146/iangoodnight/ruby/ch-2.rb b/challenge-146/iangoodnight/ruby/ch-2.rb
new file mode 100755
index 0000000000..72fdb9bab7
--- /dev/null
+++ b/challenge-146/iangoodnight/ruby/ch-2.rb
@@ -0,0 +1,158 @@
+#!/usr/bin/ruby -w
+# ch-2.rb
+
+# https://theweeklychallenge.org/blog/perl-weekly-challenge-146/
+#
+# ## Task 2 > Curious Fraction Tree
+# =================================
+#
+# Consider the following `Curious Fraction Tree`:
+#
+# ```
+# __________1/1__________
+# / \
+# ___1/2___ ___2/1___
+# / \ / \
+# 1/3 3/2 2/3 3/1
+# / \ / \ / \ / \
+# 1/4 4/3 3/5 5/2 2/5 5/3 3/4 4/1
+# ```
+#
+# You are given a fraction, member of the tree created similar to the above
+# sample.
+#
+# Write a script to find out the parent and grandparent of the given member.
+#
+# **Example 1:**
+#
+# ```
+# Input: $member = '3/5';
+# Output: parent = '3/2' and grandparent = '1/2'
+# ```
+#
+# **Example 2:**
+#
+# ```
+# Input: $member = '4/3';
+# Output: parent = '1/3' and grandparent = '1/2'
+# ```
+
+################################################################################
+# PWC Solution #################################################################
+################################################################################
+
+def get_curious_parent(member)
+ num, den = member.split(%r{/}, 2).map(&:to_i)
+ val = num.to_f / den.to_f
+ return nil unless val != 1.0
+ return "#{num - den}/#{den}" unless val < 1
+ return "#{num}/#{den - num}" unless val > 1
+end
+
+def get_curious_tree(member)
+ parents = []
+ child = member
+ until child.nil?
+ parents.push child
+ child = get_curious_parent(child)
+ end
+ parents
+end
+
+def get_curious_generations(tree, generations = 2)
+ return [tree[1]] unless tree.length > 2
+ tree.slice(1, generations)
+end
+
+################################################################################
+# Utilities ####################################################################
+################################################################################
+
+def color_str(color, str)
+ colors = {
+ green: "\e[32m",
+ red: "\e[31m",
+ reset: "\e[0m",
+ yellow: "\e[33m"
+ }
+ colors[color] + str + colors[:reset]
+end
+
+def format_results(tree, results = '')
+ tree.each_with_index do |member, idx|
+ length = tree.length
+ results += color_str(:yellow, 'parent') unless idx > 0
+ results += color_str(:yellow, 'great-' * (idx - 1)) unless idx < 2
+ results += color_str(:yellow, 'grandparent') unless idx < 1
+ results += ' = ' + color_str(:green, member)
+ results = length == 2 && idx.zero? ? results + ' and ' : results
+ results = idx == length - 2 && length != 2 ? results + ', and ' : results
+ results = length > 2 && idx < length - 2 ? results + ', ' : results
+ end
+ results
+end
+
+def assert_root(tree)
+ last = tree[-1]
+ last != '1/1' && color_str(:yellow, 'Curious fraction tree rooted at ') +
+ color_str(:red, last) +
+ color_str(:yellow, ' and not at ') +
+ color_str(:green, '1/1')
+end
+
+################################################################################
+# Main #########################################################################
+################################################################################
+
+prompt =
+ 'Enter a fraction (i.e.: "' +
+ color_str(:green, '3/5') +
+ '") or type "' +
+ color_str(:yellow, 'exit') +
+ '" to quit /> '
+
+more_prompt =
+ 'Enter "' +
+ color_str(:yellow, 'more') +
+ '" to see more details, or enter a fraction (i.e.: "' +
+ color_str(:green, '3/5') +
+ '") to continue /> '
+
+banner = 'A Curious Fraction Tree'
+
+puts color_str(:yellow, banner)
+puts color_str(:yellow, '=' * banner.length)
+
+current_tree = nil
+generations = nil
+asserted_root = nil
+
+print prompt
+
+loop do
+ input = gets.chomp
+ input.strip!
+ case input
+ when %r{\A1\/1\z}
+ puts color_str(:green, input) + ' is root.'
+ print prompt
+ when %r{\A\d+\/\d+\z}
+ current_tree = get_curious_tree(input)
+ generations = get_curious_generations(current_tree)
+ asserted_root = assert_root(current_tree)
+ puts format_results(generations)
+ print more_prompt
+ when /\Amore\z/i
+ tree = current_tree.slice(1, current_tree.size - 1)
+ puts format_results(tree)
+ puts asserted_root if asserted_root
+ current_tree = nil
+ generations = nil
+ asserted_root = nil
+ print prompt
+ when /exit|quit|q/i
+ puts color_str(:yellow, 'Goodbye.')
+ exit
+ else puts "I don't understand #{color_str(:yellow, input)}"
+ end
+end