Peter’s blog ✴ Week 386 ✴ 10 August 2026
THE WEEKLY CHALLENGE
Hitting the bases with recurring numbers
You are given two strings representing non-negative rational numbers. Write a script to return true if the two given rational numbers are same, otherwise false.
Example 1 Input: $rat1 = '0.(12)' $rat2 = '0.(121)' Output: false Expansion of '0.(12)' = 0.12 12 12 12 Expansion of '0.(121)' = 0.121 121 121 Example 2 Input: $rat1 = '0.1(23)' $rat2 = '0.12(32)' Output: true Expansion of '0.1(23)' = 0.1 23 23 23 Expansion of '0.12(32)' = 0.12 32 32 32 Example 3 Input: $rat1 = '0.1(234)' $rat2 = '0.12(342)' Output: true Expansion of '0.1(234)' = 0.1 234 234 234 Expansion of '0.12(342)' = 0.12 342 342 342 Example 4 Input: $rat1 = '12.99(99)' $rat2 = '13.' Output: true Example 5 Input: $rat1 = '0.(123)' $rat2 = '0.1(231)' Output: true
Well, this one wasn't as easy as I thought!
After some experiments, I decided the best way was to
convert each supplied number to a standard format such as:
1
1.2
1.2(3)
1.2(34)
0.12(345)
These formats have the unbracketed and bracketed components reduced to their minimum length, and comply with normal practices such as not starting or finishing with a decimal point. The process for doing that is as follows:
1.234(3434) -> 1.234(34)
1.234(34) -> 1.2(34)
1.232(32) -> 1.(23)
1.23(9) -> 1.24
1.29(9) -> 1.30
1.99(9) -> 2
1.(9) -> 2
12.(0) -> 12
12.34(0) -> 12.34
123. -> 123
456.000 -> 456
.123 -> 0.123
.123(45) -> 0.123(45)
Then, the numbers are equal if their normalised forms are textually identical.
I have purposely coded this using more lines than strictly neccesary, so that the conversion process is transparent.
In week 386, Peter presents a neat, efficient, and very strong Perl design for the base conversion process. The blog post is remarkable because there is an insightful mention of the special cases, and all the edge cases are taken into account, for example, the digit limits and base limits, which makes it different from the others.
This review may cover either or both challenges for this week.
I am sorry that the 'Try it' feature is currently working very slowly or not at all owing to some issue with my web hosting provider.
#!/usr/bin/perl # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge/386/2 use v5.26; # The Weekly Challenge - 2026-08-10 use utf8; # Week 386 - task 2 - Rational numbers use warnings; # Peter Campbell Smith binmode STDOUT, ':utf8'; use Encode; rational_numbers('0.(12)', '0.(121)'); rational_numbers('0.12(32)', '0.1(23)'); rational_numbers('0.1(234)', '0.12(342)'); rational_numbers('12.99(99)', '13'); rational_numbers('0.(123)', '.1(231)'); rational_numbers('0.000', '0'); rational_numbers('42', '41.9999999(9)'); rational_numbers('0.456(456)', '0.45645(645)'); sub rational_numbers { my (@r, $j, $num, $brac, $unbrac, $len, $half, $k, $mult, $substr, $last); # initialise @r = @_; say qq[\nInput: \$rat1 = $r[0], \$rat2 = $r[1]]; # canonicalise the numbers (see blog) for $j (0, 1) { $num = $r[$j]; # split into bracketed and unbracketed parts if ($num =~ m|(.*) \( (.*) \)|x) { ($unbrac, $brac) = ($1, $2); # shorten the bracketed part if possible $len = length($brac); $half = int($len / 2); $mult = 1; for $k (1 .. $half) { $mult = $len / $k; if ($mult == int($mult)) { $substr = substr($brac, 0, $k); if ($brac eq $substr x $mult) { $brac = $substr; last; } } } # shorten the unbracketed part if possible while ($unbrac =~ m|^(.*)$brac$|g) { $unbrac = $1; } while (1) { $last = substr($unbrac, -1); last unless $last =~ m|\d|; if ($brac =~ m|^(.*)$last(.*)$|) { $brac = $last . $2 . $1; $unbrac = substr($unbrac, 0, -1); } else { last; } } # if the bracketed part is (9) add 1 to the digit before if ($brac eq '9') { $unbrac =~ m|.*\.(\d*)|; $mult = 10 ** (length($1)); $unbrac = (qq[$unbrac] * $mult + 1) / $mult; $brac = ''; } # if the bracketed part is (0), delete it $num = $unbrac . ($brac ? qq[($brac)] : ''); } # if the number ends with '.' and any zeroes, delete them $num = $1 if $num =~ m|^(\d*)\.0*$|; # if the number starts with '.', precede it with '0' $num = qq[0$num] if $num =~ m|^\.|; $r[$j] = $num; } say qq[Output: ] . ($r[0] eq $r[1] ? qq[true - both are $r[0]] : qq[false - $r[0] and $r[1]]); }
40 lines of code
Input: $rat1 = 0.(12), $rat2 = 0.(121) Output: false - 0.(12) and 0.(121) Input: $rat1 = 0.12(32), $rat2 = 0.1(23) Output: true - both are 0.1(23) Input: $rat1 = 0.1(234), $rat2 = 0.12(342) Output: true - both are 0.1(234) Input: $rat1 = 12.99(99), $rat2 = 13 Output: true - both are 13 Input: $rat1 = 0.(123), $rat2 = .1(231) Output: true - both are 0.(123) Input: $rat1 = 0.000, $rat2 = 0 Output: true - both are 0 Input: $rat1 = 42, $rat2 = 41.9999999(9) Output: true - both are 42 Input: $rat1 = 0.456(456), $rat2 = 0.45645(645) Output: true - both are 0.(456)
Any content of this website which has been created by Peter Campbell Smith is in the public domain