Imaginaries and the daily grind
Weekly challenge 178 — 15 August 2022
Week 178: 15 Aug 2022
Write a script to convert a given number base 10 to quater-imaginary base and vice-versa. For more information, please see this Wikipedia page.
In summary, quater-imaginary base is base 2i or sqrt(-4).
Example 1:
$base_10 = 4
$quater_imaginary_base = 10300
as that is 1 x (2i)4 + 3 x (2i)2, which is 8 - 4 = 4
Quater-imaginary base numbers - henceforth qibs - are numbers written in the base 2i. For the purposes of the blog I am going to show qibs in red, for example 301.1. Note that the digits of a qib can only be 0, 1, 2 or 3.
So, for example:
123 = 1 x (2i)^2 + 2 x 2i + 3 = - 4 + 4i + 3 = -1 + 4i
I thought at first that I could just translate the Wikipedia explanation into Perl, but that turned out
quite tricky. I also though about using Math::Complex
, but decided - probably wrongly - that it
wasn't worth the effort of relearning its rather unfamiliar syntax.
So I wrote my own functions to_qib
and from_qib
. It wasn't that hard, though perhaps not as much
fun as I might have hoped. I confined myself to complex numbers with integer coefficients, which means the
qibs either end in .0 or .2.
The length of qibs increases quite rapidly and my rather clunky way of adding them means that my code won't handle them over 18 digits before the point - but a little tweaking could probably cure that.
#!/usr/bin/perl # Peter Campbell Smith - 2022-08-15 # PWC 178 task 1 use v5.26; use utf8; use warnings; use POSIX 'floor'; my (@tests, $test, $real_part, $imag_part, $result, $real, $imag, $qib); @tests = ([5, 0], [0, 5], [4, 6], [35, 23], [-7, -10], [52, -77], [-1, 4]); # 5 5i 4+6i 35+23i -7-10i 52-77i -1+4i for $test (@tests) { $qib = to_qib(@$test); $result = from_qib($qib); say ''; } sub to_qib { # converts a complex number with integer coefficients to a quater-imaginary base number my ($real, $imag, $real_part, $imag_part, $result); ($real, $imag) = @_; # convert real part and -2 x imag part to qib numbers $real_part = quater_imag($real) . '.0'; $imag_part = quater_imag(-$imag * 2); # right shift the imag part (so dividing it by 2i) $imag_part =~ m|(.*)(.)|; $imag_part = qq[$1.$2]; # add the real and imag parts $result = quater_add($real_part, $imag_part); say qq[$real + ${imag}i = [$result]2i]; return $result; } sub from_qib { # converts a quater-imaginary base number to a complex number with integer coefficients my ($qib, @digits, $real, $imag, $real_mult, $imag_mult, $hold, $digit); $qib = $_[0]; # put the digits into an array @digits = split(//, $qib); # loop over the digits from least significant upwards $imag = 0; $real = 0; $real_mult = 0; # these are the contributions of the digit after the point $imag_mult = -0.5; for $digit (reverse @digits) { next if $digit eq '.'; # add the (decimal) values of the digits to the answer $real += $real_mult * $digit; $imag += $imag_mult * $digit; # and get the real and imag contributions from the next digit $hold = $real_mult; $real_mult = -2 * $imag_mult; $imag_mult = 2 * $hold; } say qq[[$qib]2i = $real + ${imag}i]; } sub quater_imag { # converts a real decimal number to a qib my ($number, $answer, $quotient, $remainder); # apply the 'Another conversion method' from Wikipedia $number = $_[0]; $answer = ''; # each division step contributes a remainder (0..3) and a following zero while (1) { ($quotient, $remainder) = div_minus4($number); $answer = $remainder . '0' . $answer; $number = $quotient; last unless $number != 0; } # remove the trailing zero return substr($answer, 0, -1); } sub div_minus4 { # divides argument by -4 such that the remainder is always +ve my ($dividend, $quotient, $remainder); $dividend = $_[0]; $quotient = -floor($dividend / 4); $remainder = $dividend + 4 * $quotient; return ($quotient, $remainder); } sub quater_add { # adds 2 qib numbers of which the first is real and the second is imaginary # both have one digit after the point my ($real, $imag, $result, $j); # left pad them with lots of zeroes so that corresponding digits are in the same place in the string ($real, $imag) = @_; $real = substr('00000000000000000000' . $real, -20); $imag = substr('00000000000000000000' . $imag, -20); # add them digit by digit for $j (0 .. 19) { if (substr($real, $j, 1) eq '.') { $result .= '.'; } else { $result .= substr($real, $j, 1) + substr($imag, $j, 1); } } # get rid of the leading '0's $result =~ s|^0*||; return $result; }
Qibs are shown as [qib]2i 5 + 0i = [10301.0]2i [10301.0]2i = 5 + 0i 0 + 5i = [30.2]2i [30.2]2i = 0 + 5i 4 + 6i = [10330.0]2i [10330.0]2i = 4 + 6i 35 + 23i = [121003.2]2i [121003.2]2i = 35 + 23i -7 + -10i = [2231.0]2i [2231.0]2i = -7 + -10i 52 + -77i = [113202320.2]2i [113202320.2]2i = 52 + -77i -1 + 4i = [123.0]2i [123.0]2i = -1 + 4i
Any content of this website which has been created by Peter Campbell Smith is in the public domain