Peter’s blog ✴ Week 386 ✴ 10 August 2026

THE WEEKLY CHALLENGE
Hitting the bases with recurring numbers

The Perl Camel

Task 2

Rational 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.

Examples


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

Analysis

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. Check if the bracketed part can be shortened:
    1.234(3434) -> 1.234(34)
  2. Check if the unbracketed part can be shortened, which may involve rotating the bracketed part:
    1.234(34) -> 1.2(34)
    1.232(32) -> 1.(23)
  3. If the bracketed part is (9) remove it and add 1 to the preceding digit:
    1.23(9) -> 1.24
    1.29(9) -> 1.30
    1.99(9) -> 2
    1.(9) -> 2
  4. if the bracketed part is (0) delete it:
    12.(0) -> 12
    12.34(0) -> 12.34
  5. If the number ends with '.' followed by any zeroes, remove them:
    123. -> 123
    456.000 -> 456
  6. If the number starts with '.', precede it with zero:
    .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.

Perl Weekly’s review

from PW issue 786

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.

Try it 

Your input:



eg: 1.23456(45623)



eg: 1.23456(456)

Script


#!/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

Output from script


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