Imaginary base numbers are a non-standard positional numeral system which uses an imaginary number as its radix. The most common is quater-imaginary with radix 2i.

The quater-imaginary numeral system was first proposed by Donald Knuth in 1955 as a submission for a high school science talent search. [Ref.]

Other imaginary bases are possible too but are not as widely discussed and aren't specifically named.

Task: Write a set of procedures (functions, subroutines, however they are referred to in your language) to convert base 10 numbers to an imaginary base and back.

At a minimum, support quater-imaginary (base 2i).

For extra kudos, support positive or negative bases 2i through 6i (or higher).

As a stretch goal, support converting non-integer numbers ( E.G. 227.65625+10.859375i ) to an imaginary base.

See Wikipedia: Quater-imaginary_base for more details.

For reference, here are some some decimal and complex numbers converted to quater-imaginary.






Base 10
Base 2i


1
 1


2
 2


3
 3


4
 10300


5
 10301


6
 10302


7
 10303


8
 10200


9
 10201


10
 10202


11
 10203


12
 10100


13
 10101


14
 10102


15
 10103


16
 10000






Base 10
Base 2i


−1
 103


−2
 102


−3
 101


−4
 100


−5
 203


−6
 202


−7
 201


−8
 200


−9
 303


−10
 302


−11
 301


−12
 300


−13
 1030003


−14
 1030002


−15
 1030001


−16
 1030000






Base 10
Base 2i


1i
10.2


2i
10.0


3i
20.2


4i
20.0


5i
30.2


6i
30.0


7i
103000.2


8i
103000.0


9i
103010.2


10i
103010.0


11i
103020.2


12i
103020.0


13i
103030.2


14i
103030.0


15i
102000.2


16i
102000.0






Base 10
Base 2i


−1i
0.2


−2i
1030.0


−3i
1030.2


−4i
1020.0


−5i
1020.2


−6i
1010.0


−7i
1010.2


−8i
1000.0


−9i
1000.2


−10i
2030.0


−11i
2030.2


−12i
2020.0


−13i
2020.2


−14i
2010.0


−15i
2010.2


−16i
2000.0





import algorithm, complex, math, strformat, strutils
 
const
  TwoI = complex(0.0, 2.0)
  InvTwoI = inv(TwoI)
 
type QuaterImaginery = object
  b2i: string
 
# Conversions between digit character and digit value.
template digitChar(n: range[0..9]): range['0'..'9'] = chr(n + ord('0'))
template digitValue(c: range['0'..'9']): range[0..9] = ord(c) - ord('0')
 
 
####################################################################################################
# Quater imaginary functions.
 
func initQuaterImaginary(s: string): QuaterImaginery =
  ## Create a Quater imaginary number.
  if s.len == 0 or not s.allCharsInSet({'0'..'3', '.'}) or s.count('.') > 1:
    raise newException(ValueError, "invalid base 2i number.")
  result = QuaterImaginery(b2i: s)
 
#---------------------------------------------------------------------------------------------------
 
func toComplex(q: QuaterImaginery): Complex[float] =
  ## Convert a Quater imaginary number to a complex.
 
  let pointPos = q.b2i.find('.')
  let posLen = if pointPos != -1: pointPos else: q.b2i.len
  var prod = complex(1.0)
 
  for j in 0..<posLen:
    let k = float(q.b2i[posLen - 1 - j].digitValue)
    if k > 0: result += prod * k
    prod *= TwoI
 
  if pointPos != -1:
    prod = InvTwoI
    for j in (posLen + 1)..q.b2i.high:
      let k = float(q.b2i[j].digitValue)
      if k > 0: result += prod * k
      prod *= InvTwoI
 
#---------------------------------------------------------------------------------------------------
 
func `$`(q: QuaterImaginery): string =
  ## Convert a Quater imaginary number to a string.
  q.b2i
 
 
####################################################################################################
# Supplementary functions for complex numbers.
 
func toQuaterImaginary(c: Complex): QuaterImaginery =
  ## Convert a complex number to a Quater imaginary number.
 
  if c.re == 0 and c.im == 0: return initQuaterImaginary("0")
 
  var re = c.re.toInt
  var im = c.im.toInt
  var fi = -1
 
  while re != 0:
    var rem = re mod -4
    re = re div -4
    if rem < 0:
      inc rem, 4
      inc re
    result.b2i.add rem.digitChar
    result.b2i.add '0'
 
  if im != 0:
    var f = (complex(0.0, c.im) / TwoI).re
    im = f.ceil.toInt
    f = -4 * (f - im.toFloat)
    var index = 1
    while im != 0:
      var rem = im mod -4
      im = im div -4
      if rem < 0:
        inc rem, 4
        inc im
      if index < result.b2i.len:
        result.b2i[index] = rem.digitChar
      else:
        result.b2i.add '0'
        result.b2i.add rem.digitChar
      inc index, 2
    fi = f.toInt
 
  result.b2i.reverse()
  if fi != -1: result.b2i.add "." & $fi
  result.b2i = result.b2i.strip(leading = true, trailing = false, {'0'})
  if result.b2i.startsWith('.'): result.b2i = '0' & result.b2i
 
#---------------------------------------------------------------------------------------------------
 
func toString(c: Complex[float]): string =
  ## Convert a complex number to a string.
  ## This function is used in place of `$`.
 
  let real = if c.re.classify == fcNegZero: 0.0 else: c.re
  let imag = if c.im.classify == fcNegZero: 0.0 else: c.im
  result = if imag >= 0: fmt"{real} + {imag}i" else: fmt"{real} - {-imag}i"
  result = result.replace(".0 ", " ").replace(".0i", "i").replace(" + 0i", "")
  if result.startsWith("0 + "): result = result[4..^1]
  if result.startsWith("0 - "): result = '-' & result[4..^1]
 
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
when isMainModule:
 
  for i in 1..16:
    var c1 = complex(i.toFloat)
    var qi = c1.toQuaterImaginary
    var c2 = qi.toComplex
    stdout.write fmt"{c1.toString:>4s} → {qi:>8s} → {c2.toString:>4s}     "
    c1 = -c1
    qi = c1.toQuaterImaginary
    c2 = qi.toComplex
    echo fmt"{c1.toString:>4s} → {qi:>8s} → {c2.toString:>4s}"
 
  echo ""
 
  for i in 1..16:
    var c1 = complex(0.0, i.toFloat)
    var qi = c1.toQuaterImaginary
    var c2 = qi.toComplex
    stdout.write fmt"{c1.toString:>4s} → {qi:>8s} → {c2.toString:>4s}     "
    c1 = -c1
    qi = c1.toQuaterImaginary
    c2 = qi.toComplex
    echo fmt"{c1.toString:>4s} → {qi:>8s} → {c2.toString:>4s}"