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.

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