Imaginary base numbers

From Rosetta Code
Imaginary base numbers is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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.

D[edit]

Translation of: Kotlin
import std.algorithm;
import std.array;
import std.complex;
import std.conv;
import std.format;
import std.math;
import std.stdio;
import std.string;
 
Complex!double inv(Complex!double v) {
auto denom = v.re*v.re + v.im*v.im;
return v.conj / denom;
}
 
QuaterImaginary toQuaterImaginary(Complex!double v) {
if (v.re == 0.0 && v.im == 0.0) return QuaterImaginary("0");
auto re = v.re.to!int;
auto im = v.im.to!int;
auto fi = -1;
auto sb = appender!(char[]);
while (re != 0) {
auto rem = re % -4;
re /= -4;
if (rem < 0) {
rem = 4 + rem;
re++;
}
sb.formattedWrite("%d", rem);
sb.put("0");
}
if (im != 0) {
auto f = (complex(0.0, v.im) / complex(0.0, 2.0)).re;
im = f.ceil.to!int;
f = -4.0 * (f - im.to!double);
auto index = 1;
while (im != 0) {
auto rem = im % -4;
im /= -4;
if (rem < 0) {
rem = 4 + rem;
im++;
}
if (index < sb.data.length) {
sb.data[index] = cast(char)(rem + '0');
} else {
sb.put("0");
sb.formattedWrite("%d", rem);
}
index += 2;
}
fi = f.to!int;
}
sb.data.reverse;
if (fi != -1) sb.formattedWrite(".%d", fi);
int i;
while (i < sb.data.length && sb.data[i] == '0') {
i++;
}
auto s = sb.data[i..$].idup;
if (s[0] == '.') s = "0" ~ s;
return QuaterImaginary(s);
}
 
struct QuaterImaginary {
private string b2i;
 
this(string b2i) {
if (b2i == "" || b2i.count('.') > 1) {
throw new Exception("Invalid Base 2i number");
}
foreach (c; b2i) {
if (!canFind("0123.", c)) {
throw new Exception("Invalid Base 2i number");
}
}
this.b2i = b2i;
}
 
T opCast(T : Complex!double)() {
auto pointPos = b2i.indexOf('.');
size_t posLen;
if (pointPos != -1) {
posLen = pointPos;
} else {
posLen = b2i.length;
}
auto sum = complex(0.0, 0.0);
auto prod = complex(1.0, 0.0);
foreach (j; 0..posLen) {
auto k = (b2i[posLen - 1 - j] - '0').to!double;
if (k > 0.0) {
sum += prod * k;
}
prod *= twoI;
}
if (pointPos != -1) {
prod = invTwoI;
foreach (j; posLen+1..b2i.length) {
auto k = (b2i[j] - '0').to!double;
prod *= invTwoI;
}
}
return sum;
}
 
void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const {
if (fmt.spec == 's') {
for (int i=0; i<fmt.width-b2i.length; ++i) {
sink(" ");
}
}
sink(b2i);
}
 
enum twoI = complex(0.0, 2.0);
enum invTwoI = twoI.inv;
}
 
unittest {
import std.exception;
assertThrown!Exception(QuaterImaginary(""));
assertThrown!Exception(QuaterImaginary("1.2.3"));
assertThrown!Exception(QuaterImaginary("a"));
assertThrown!Exception(QuaterImaginary("4"));
assertThrown!Exception(QuaterImaginary(" "));
}
 
void main() {
foreach (i; 1..17) {
auto c1 = complex(i, 0);
auto qi = c1.toQuaterImaginary;
auto c2 = cast(Complex!double) qi;
writef("%4s -> %8s -> %4s ", c1.re, qi, c2.re);
c1 = -c1;
qi = c1.toQuaterImaginary();
c2 = cast(Complex!double) qi;
writefln("%4s -> %8s -> %4s ", c1.re, qi, c2.re);
}
writeln;
foreach (i; 1..17) {
auto c1 = complex(0, i);
auto qi = c1.toQuaterImaginary;
auto c2 = qi.to!(Complex!double);
writef("%4s -> %8s -> %4s ", c1.im, qi, c2.im);
c1 = -c1;
qi = c1.toQuaterImaginary();
c2 = cast(Complex!double) qi;
writefln("%4s -> %8s -> %4s ", c1.im, qi, c2.im);
}
}
Output:
   1 ->        1 ->    1       -1 ->      103 ->   -1
   2 ->        2 ->    2       -2 ->      102 ->   -2
   3 ->        3 ->    3       -3 ->      101 ->   -3
   4 ->    10300 ->    4       -4 ->      100 ->   -4
   5 ->    10301 ->    5       -5 ->      203 ->   -5
   6 ->    10302 ->    6       -6 ->      202 ->   -6
   7 ->    10303 ->    7       -7 ->      201 ->   -7
   8 ->    10200 ->    8       -8 ->      200 ->   -8
   9 ->    10201 ->    9       -9 ->      303 ->   -9
  10 ->    10202 ->   10      -10 ->      302 ->  -10
  11 ->    10203 ->   11      -11 ->      301 ->  -11
  12 ->    10100 ->   12      -12 ->      300 ->  -12
  13 ->    10101 ->   13      -13 ->  1030003 ->  -13
  14 ->    10102 ->   14      -14 ->  1030002 ->  -14
  15 ->    10103 ->   15      -15 ->  1030001 ->  -15
  16 ->    10000 ->   16      -16 ->  1030000 ->  -16

   1 ->     10.2 ->    2       -1 ->      0.2 ->    0
   2 ->     10.0 ->    2       -2 ->   1030.0 ->   -2
   3 ->     20.2 ->    4       -3 ->   1030.2 ->   -2
   4 ->     20.0 ->    4       -4 ->   1020.0 ->   -4
   5 ->     30.2 ->    6       -5 ->   1020.2 ->   -4
   6 ->     30.0 ->    6       -6 ->   1010.0 ->   -6
   7 -> 103000.2 ->    8       -7 ->   1010.2 ->   -6
   8 -> 103000.0 ->    8       -8 ->   1000.0 ->   -8
   9 -> 103010.2 ->   10       -9 ->   1000.2 ->   -8
  10 -> 103010.0 ->   10      -10 ->   2030.0 ->  -10
  11 -> 103020.2 ->   12      -11 ->   2030.2 ->  -10
  12 -> 103020.0 ->   12      -12 ->   2020.0 ->  -12
  13 -> 103030.2 ->   14      -13 ->   2020.2 ->  -12
  14 -> 103030.0 ->   14      -14 ->   2010.0 ->  -14
  15 -> 102000.2 ->   16      -15 ->   2010.2 ->  -14
  16 -> 102000.0 ->   16      -16 ->   2000.0 ->  -16

Go[edit]

Translation of: Kotlin

... though a bit shorter as Go has support for complex numbers built into the language.

package main
 
import (
"fmt"
"math"
"strconv"
"strings"
)
 
const (
twoI = 2.0i
invTwoI = 1.0 / twoI
)
 
type quaterImaginary struct {
b2i string
}
 
func reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
 
func newQuaterImaginary(b2i string) quaterImaginary {
b2i = strings.TrimSpace(b2i)
_, err := strconv.ParseFloat(b2i, 64)
if err != nil {
panic("invalid Base 2i number")
}
return quaterImaginary{b2i}
}
 
func toComplex(q quaterImaginary) complex128 {
pointPos := strings.Index(q.b2i, ".")
var posLen int
if pointPos != -1 {
posLen = pointPos
} else {
posLen = len(q.b2i)
}
sum := 0.0i
prod := complex(1.0, 0.0)
for j := 0; j < posLen; j++ {
k := float64(q.b2i[posLen-1-j] - '0')
if k > 0.0 {
sum += prod * complex(k, 0.0)
}
prod *= twoI
}
if pointPos != -1 {
prod = invTwoI
for j := posLen + 1; j < len(q.b2i); j++ {
k := float64(q.b2i[j] - '0')
if k > 0.0 {
sum += prod * complex(k, 0.0)
}
prod *= invTwoI
}
}
return sum
}
 
func (q quaterImaginary) String() string {
return q.b2i
}
 
// only works properly if 'real' and 'imag' are both integral
func toQuaterImaginary(c complex128) quaterImaginary {
if c == 0i {
return quaterImaginary{"0"}
}
re := int(real(c))
im := int(imag(c))
fi := -1
var sb strings.Builder
for re != 0 {
rem := re % -4
re /= -4
if rem < 0 {
rem += 4
re++
}
sb.WriteString(strconv.Itoa(rem))
sb.WriteString("0")
}
if im != 0 {
f := real(complex(0.0, imag(c)) / 2.0i)
im = int(math.Ceil(f))
f = -4.0 * (f - float64(im))
index := 1
for im != 0 {
rem := im % -4
im /= -4
if rem < 0 {
rem += 4
im++
}
if index < sb.Len() {
bs := []byte(sb.String())
bs[index] = byte(rem + 48)
sb.Reset()
sb.Write(bs)
} else {
sb.WriteString("0")
sb.WriteString(strconv.Itoa(rem))
}
index += 2
}
fi = int(f)
}
s := reverse(sb.String())
if fi != -1 {
s = fmt.Sprintf("%s.%d", s, fi)
}
s = strings.TrimLeft(s, "0")
if s[0] == '.' {
s = "0" + s
}
return newQuaterImaginary(s)
}
 
func main() {
for i := 1; i <= 16; i++ {
c1 := complex(float64(i), 0.0)
qi := toQuaterImaginary(c1)
c2 := toComplex(qi)
fmt.Printf("%4.0f -> %8s -> %4.0f ", real(c1), qi, real(c2))
c1 = -c1
qi = toQuaterImaginary(c1)
c2 = toComplex(qi)
fmt.Printf("%4.0f -> %8s -> %4.0f\n", real(c1), qi, real(c2))
}
fmt.Println()
for i := 1; i <= 16; i++ {
c1 := complex(0.0, float64(i))
qi := toQuaterImaginary(c1)
c2 := toComplex(qi)
fmt.Printf("%3.0fi -> %8s -> %3.0fi ", imag(c1), qi, imag(c2))
c1 = -c1
qi = toQuaterImaginary(c1)
c2 = toComplex(qi)
fmt.Printf("%3.0fi -> %8s -> %3.0fi\n", imag(c1), qi, imag(c2))
}
}
Output:
   1 ->        1 ->    1       -1 ->      103 ->   -1
   2 ->        2 ->    2       -2 ->      102 ->   -2
   3 ->        3 ->    3       -3 ->      101 ->   -3
   4 ->    10300 ->    4       -4 ->      100 ->   -4
   5 ->    10301 ->    5       -5 ->      203 ->   -5
   6 ->    10302 ->    6       -6 ->      202 ->   -6
   7 ->    10303 ->    7       -7 ->      201 ->   -7
   8 ->    10200 ->    8       -8 ->      200 ->   -8
   9 ->    10201 ->    9       -9 ->      303 ->   -9
  10 ->    10202 ->   10      -10 ->      302 ->  -10
  11 ->    10203 ->   11      -11 ->      301 ->  -11
  12 ->    10100 ->   12      -12 ->      300 ->  -12
  13 ->    10101 ->   13      -13 ->  1030003 ->  -13
  14 ->    10102 ->   14      -14 ->  1030002 ->  -14
  15 ->    10103 ->   15      -15 ->  1030001 ->  -15
  16 ->    10000 ->   16      -16 ->  1030000 ->  -16

  1i ->     10.2 ->   1i      -1i ->      0.2 ->  -1i
  2i ->     10.0 ->   2i      -2i ->   1030.0 ->  -2i
  3i ->     20.2 ->   3i      -3i ->   1030.2 ->  -3i
  4i ->     20.0 ->   4i      -4i ->   1020.0 ->  -4i
  5i ->     30.2 ->   5i      -5i ->   1020.2 ->  -5i
  6i ->     30.0 ->   6i      -6i ->   1010.0 ->  -6i
  7i -> 103000.2 ->   7i      -7i ->   1010.2 ->  -7i
  8i -> 103000.0 ->   8i      -8i ->   1000.0 ->  -8i
  9i -> 103010.2 ->   9i      -9i ->   1000.2 ->  -9i
 10i -> 103010.0 ->  10i     -10i ->   2030.0 -> -10i
 11i -> 103020.2 ->  11i     -11i ->   2030.2 -> -11i
 12i -> 103020.0 ->  12i     -12i ->   2020.0 -> -12i
 13i -> 103030.2 ->  13i     -13i ->   2020.2 -> -13i
 14i -> 103030.0 ->  14i     -14i ->   2010.0 -> -14i
 15i -> 102000.2 ->  15i     -15i ->   2010.2 -> -15i
 16i -> 102000.0 ->  16i     -16i ->   2000.0 -> -16i

Java[edit]

Translation of: Kotlin
public class ImaginaryBaseNumber {
private static class Complex {
private Double real, imag;
 
public Complex(double r, double i) {
this.real = r;
this.imag = i;
}
 
public Complex(int r, int i) {
this.real = (double) r;
this.imag = (double) i;
}
 
public Complex add(Complex rhs) {
return new Complex(
real + rhs.real,
imag + rhs.imag
);
}
 
public Complex times(Complex rhs) {
return new Complex(
real * rhs.real - imag * rhs.imag,
real * rhs.imag + imag * rhs.real
);
}
 
public Complex times(double rhs) {
return new Complex(
real * rhs,
imag * rhs
);
}
 
public Complex inv() {
double denom = real * real + imag * imag;
return new Complex(
real / denom,
-imag / denom
);
}
 
public Complex unaryMinus() {
return new Complex(-real, -imag);
}
 
public Complex divide(Complex rhs) {
return this.times(rhs.inv());
}
 
// only works properly if 'real' and 'imag' are both integral
public QuaterImaginary toQuaterImaginary() {
if (real == 0.0 && imag == 0.0) return new QuaterImaginary("0");
int re = real.intValue();
int im = imag.intValue();
int fi = -1;
StringBuilder sb = new StringBuilder();
while (re != 0) {
int rem = re % -4;
re /= -4;
if (rem < 0) {
rem += 4;
re++;
}
sb.append(rem);
sb.append(0);
}
if (im != 0) {
Double f = new Complex(0.0, imag).divide(new Complex(0.0, 2.0)).real;
im = ((Double) Math.ceil(f)).intValue();
f = -4.0 * (f - im);
int index = 1;
while (im != 0) {
int rem = im % -4;
im /= -4;
if (rem < 0) {
rem += 4;
im++;
}
if (index < sb.length()) {
sb.setCharAt(index, (char) (rem + 48));
} else {
sb.append(0);
sb.append(rem);
}
index += 2;
}
fi = f.intValue();
}
sb.reverse();
if (fi != -1) sb.append(".").append(fi);
while (sb.charAt(0) == '0') sb.deleteCharAt(0);
if (sb.charAt(0) == '.') sb.insert(0, '0');
return new QuaterImaginary(sb.toString());
}
 
@Override
public String toString() {
double real2 = real == -0.0 ? 0.0 : real; // get rid of negative zero
double imag2 = imag == -0.0 ? 0.0 : imag; // ditto
String result = imag2 >= 0.0 ? String.format("%.0f + %.0fi", real2, imag2) : String.format("%.0f - %.0fi", real2, -imag2);
result = result.replace(".0 ", " ").replace(".0i", "i").replace(" + 0i", "");
if (result.startsWith("0 + ")) result = result.substring(4);
if (result.startsWith("0 - ")) result = result.substring(4);
return result;
}
}
 
private static class QuaterImaginary {
private static final Complex TWOI = new Complex(0.0, 2.0);
private static final Complex INVTWOI = TWOI.inv();
 
private String b2i;
 
public QuaterImaginary(String b2i) {
if (b2i.equals("") || !b2i.chars().allMatch(c -> "0123.".indexOf(c) > -1) || b2i.chars().filter(c -> c == '.').count() > 1) {
throw new RuntimeException("Invalid Base 2i number");
}
this.b2i = b2i;
}
 
public Complex toComplex() {
int pointPos = b2i.indexOf(".");
int posLen = pointPos != -1 ? pointPos : b2i.length();
Complex sum = new Complex(0, 0);
Complex prod = new Complex(1, 0);
 
for (int j = 0; j < posLen; ++j) {
double k = b2i.charAt(posLen - 1 - j) - '0';
if (k > 0.0) sum = sum.add(prod.times(k));
prod = prod.times(TWOI);
}
if (pointPos != -1) {
prod = INVTWOI;
for (int j = posLen + 1; j < b2i.length(); ++j) {
double k = b2i.charAt(j) - '0';
if (k > 0.0) sum = sum.add(prod.times(k));
prod = prod.times(INVTWOI);
}
}
 
return sum;
}
 
@Override
public String toString() {
return b2i;
}
}
 
public static void main(String[] args) {
String fmt = "%4s -> %8s -> %4s";
for (int i = 1; i <= 16; ++i) {
Complex c1 = new Complex(i, 0);
QuaterImaginary qi = c1.toQuaterImaginary();
Complex c2 = qi.toComplex();
System.out.printf(fmt + " ", c1, qi, c2);
c1 = c2.unaryMinus();
qi = c1.toQuaterImaginary();
c2 = qi.toComplex();
System.out.printf(fmt, c1, qi, c2);
System.out.println();
}
System.out.println();
for (int i = 1; i <= 16; ++i) {
Complex c1 = new Complex(0, i);
QuaterImaginary qi = c1.toQuaterImaginary();
Complex c2 = qi.toComplex();
System.out.printf(fmt + " ", c1, qi, c2);
c1 = c2.unaryMinus();
qi = c1.toQuaterImaginary();
c2 = qi.toComplex();
System.out.printf(fmt, c1, qi, c2);
System.out.println();
}
}
}
Output:
   1 ->        1 ->    1       -1 ->      103 ->   -1
   2 ->        2 ->    2       -2 ->      102 ->   -2
   3 ->        3 ->    3       -3 ->      101 ->   -3
   4 ->    10300 ->    4       -4 ->      100 ->   -4
   5 ->    10301 ->    5       -5 ->      203 ->   -5
   6 ->    10302 ->    6       -6 ->      202 ->   -6
   7 ->    10303 ->    7       -7 ->      201 ->   -7
   8 ->    10200 ->    8       -8 ->      200 ->   -8
   9 ->    10201 ->    9       -9 ->      303 ->   -9
  10 ->    10202 ->   10      -10 ->      302 ->  -10
  11 ->    10203 ->   11      -11 ->      301 ->  -11
  12 ->    10100 ->   12      -12 ->      300 ->  -12
  13 ->    10101 ->   13      -13 ->  1030003 ->  -13
  14 ->    10102 ->   14      -14 ->  1030002 ->  -14
  15 ->    10103 ->   15      -15 ->  1030001 ->  -15
  16 ->    10000 ->   16      -16 ->  1030000 ->  -16

  1i ->     10.2 ->   1i       1i ->      0.2 ->   1i
  2i ->     10.0 ->   2i       2i ->   1030.0 ->   2i
  3i ->     20.2 ->   3i       3i ->   1030.2 ->   3i
  4i ->     20.0 ->   4i       4i ->   1020.0 ->   4i
  5i ->     30.2 ->   5i       5i ->   1020.2 ->   5i
  6i ->     30.0 ->   6i       6i ->   1010.0 ->   6i
  7i -> 103000.2 ->   7i       7i ->   1010.2 ->   7i
  8i -> 103000.0 ->   8i       8i ->   1000.0 ->   8i
  9i -> 103010.2 ->   9i       9i ->   1000.2 ->   9i
 10i -> 103010.0 ->  10i      10i ->   2030.0 ->  10i
 11i -> 103020.2 ->  11i      11i ->   2030.2 ->  11i
 12i -> 103020.0 ->  12i      12i ->   2020.0 ->  12i
 13i -> 103030.2 ->  13i      13i ->   2020.2 ->  13i
 14i -> 103030.0 ->  14i      14i ->   2010.0 ->  14i
 15i -> 102000.2 ->  15i      15i ->   2010.2 ->  15i
 16i -> 102000.0 ->  16i      16i ->   2000.0 ->  16i

Kotlin[edit]

The following deals with conversions to and from quater-imaginary only.

As the JDK lacks a complex number class, I've included a very basic one in the program.

// version 1.2.10
 
import kotlin.math.ceil
 
class Complex(val real: Double, val imag: Double) {
 
constructor(r: Int, i: Int) : this(r.toDouble(), i.toDouble())
 
operator fun plus(other: Complex) = Complex(real + other.real, imag + other.imag)
 
operator fun times(other: Complex) = Complex(
real * other.real - imag * other.imag,
real * other.imag + imag * other.real
)
 
operator fun times(other: Double) = Complex(real * other, imag * other)
 
fun inv(): Complex {
val denom = real * real + imag * imag
return Complex(real / denom, -imag / denom)
}
 
operator fun unaryMinus() = Complex(-real, -imag)
 
operator fun minus(other: Complex) = this + (-other)
 
operator fun div(other: Complex) = this * other.inv()
 
// only works properly if 'real' and 'imag' are both integral
fun toQuaterImaginary(): QuaterImaginary {
if (real == 0.0 && imag == 0.0) return QuaterImaginary("0")
var re = real.toInt()
var im = imag.toInt()
var fi = -1
val sb = StringBuilder()
while (re != 0) {
var rem = re % -4
re /= -4
if (rem < 0) {
rem = 4 + rem
re++
}
sb.append(rem)
sb.append(0)
}
if (im != 0) {
var f = (Complex(0.0, imag) / Complex(0.0, 2.0)).real
im = ceil(f).toInt()
f = -4.0 * (f - im.toDouble())
var index = 1
while (im != 0) {
var rem = im % -4
im /= -4
if (rem < 0) {
rem = 4 + rem
im++
}
if (index < sb.length) {
sb[index] = (rem + 48).toChar()
}
else {
sb.append(0)
sb.append(rem)
}
index += 2
}
fi = f.toInt()
}
sb.reverse()
if (fi != -1) sb.append(".$fi")
var s = sb.toString().trimStart('0')
if (s.startsWith(".")) s = "0$s"
return QuaterImaginary(s)
}
 
override fun toString(): String {
val real2 = if (real == -0.0) 0.0 else real // get rid of negative zero
val imag2 = if (imag == -0.0) 0.0 else imag // ditto
var result = if (imag2 >= 0.0) "$real2 + ${imag2}i" else "$real2 - ${-imag2}i"
result = result.replace(".0 ", " ").replace(".0i", "i").replace(" + 0i", "")
if (result.startsWith("0 + ")) result = result.drop(4)
if (result.startsWith("0 - ")) result = "-" + result.drop(4)
return result
}
}
 
class QuaterImaginary(val b2i: String) {
 
init {
if (b2i == "" || !b2i.all { it in "0123." } || b2i.count { it == '.'} > 1 )
throw RuntimeException("Invalid Base 2i number")
}
 
fun toComplex(): Complex {
val pointPos = b2i.indexOf(".")
var posLen = if (pointPos != -1) pointPos else b2i.length
var sum = Complex(0.0, 0.0)
var prod = Complex(1.0, 0.0)
for (j in 0 until posLen) {
val k = (b2i[posLen - 1 - j] - '0').toDouble()
if (k > 0.0) sum += prod * k
prod *= twoI
}
if (pointPos != -1) {
prod = invTwoI
for (j in posLen + 1 until b2i.length) {
val k = (b2i[j] - '0').toDouble()
if (k > 0.0) sum += prod * k
prod *= invTwoI
}
}
return sum
}
 
override fun toString() = b2i
 
companion object {
val twoI = Complex(0.0, 2.0)
val invTwoI = twoI.inv()
}
}
 
fun main(args: Array<String>) {
val fmt = "%4s -> %8s -> %4s"
for (i in 1..16) {
var c1 = Complex(i, 0)
var qi = c1.toQuaterImaginary()
var c2 = qi.toComplex()
print("$fmt ".format(c1, qi, c2))
c1 = -c1
qi = c1.toQuaterImaginary()
c2 = qi.toComplex()
println(fmt.format(c1, qi, c2))
}
println()
for (i in 1..16) {
var c1 = Complex(0, i)
var qi = c1.toQuaterImaginary()
var c2 = qi.toComplex()
print("$fmt ".format(c1, qi, c2))
c1 = -c1
qi = c1.toQuaterImaginary()
c2 = qi.toComplex()
println(fmt.format(c1, qi, c2))
}
}
Output:
   1 ->        1 ->    1       -1 ->      103 ->   -1
   2 ->        2 ->    2       -2 ->      102 ->   -2
   3 ->        3 ->    3       -3 ->      101 ->   -3
   4 ->    10300 ->    4       -4 ->      100 ->   -4
   5 ->    10301 ->    5       -5 ->      203 ->   -5
   6 ->    10302 ->    6       -6 ->      202 ->   -6
   7 ->    10303 ->    7       -7 ->      201 ->   -7
   8 ->    10200 ->    8       -8 ->      200 ->   -8
   9 ->    10201 ->    9       -9 ->      303 ->   -9
  10 ->    10202 ->   10      -10 ->      302 ->  -10
  11 ->    10203 ->   11      -11 ->      301 ->  -11
  12 ->    10100 ->   12      -12 ->      300 ->  -12
  13 ->    10101 ->   13      -13 ->  1030003 ->  -13
  14 ->    10102 ->   14      -14 ->  1030002 ->  -14
  15 ->    10103 ->   15      -15 ->  1030001 ->  -15
  16 ->    10000 ->   16      -16 ->  1030000 ->  -16

  1i ->     10.2 ->   1i      -1i ->      0.2 ->  -1i
  2i ->     10.0 ->   2i      -2i ->   1030.0 ->  -2i
  3i ->     20.2 ->   3i      -3i ->   1030.2 ->  -3i
  4i ->     20.0 ->   4i      -4i ->   1020.0 ->  -4i
  5i ->     30.2 ->   5i      -5i ->   1020.2 ->  -5i
  6i ->     30.0 ->   6i      -6i ->   1010.0 ->  -6i
  7i -> 103000.2 ->   7i      -7i ->   1010.2 ->  -7i
  8i -> 103000.0 ->   8i      -8i ->   1000.0 ->  -8i
  9i -> 103010.2 ->   9i      -9i ->   1000.2 ->  -9i
 10i -> 103010.0 ->  10i     -10i ->   2030.0 -> -10i
 11i -> 103020.2 ->  11i     -11i ->   2030.2 -> -11i
 12i -> 103020.0 ->  12i     -12i ->   2020.0 -> -12i
 13i -> 103030.2 ->  13i     -13i ->   2020.2 -> -13i
 14i -> 103030.0 ->  14i     -14i ->   2010.0 -> -14i
 15i -> 102000.2 ->  15i     -15i ->   2010.2 -> -15i
 16i -> 102000.0 ->  16i     -16i ->   2000.0 -> -16i

Perl 6[edit]

Works with: Rakudo version 2017.01

These are generalized imaginary-base conversion routines. They only work for imaginary bases, not complex. (Any real portion of the radix must be zero.) Theoretically they could be made to work for any imaginary base; in practice, they are limited to integer bases from -6i to -2i and 2i to 6i. Bases -1i and 1i exist but require special handling and are not supported. Bases larger than 6i (or -6i) require digits outside of base 36 to express them, so aren't as standardized, are implementation dependent and are not supported. Note that imaginary number coefficients are stored as floating point numbers in Perl 6 so some rounding error may creep in during calculations. The precision these conversion routines use is configurable; we are using 6 decimal, um... radicimal(?) places of precision here.

Implements minimum, extra kudos and stretch goals.

multi sub base ( Real $num, Int $radix where -37 < * < -1, :$precision = -15 ) {
return '0' unless $num;
my $value = $num;
my $result = '';
my $place = 0;
my $upper-bound = 1 / (-$radix + 1);
my $lower-bound = $radix * $upper-bound;
 
$value = $num / $radix ** ++$place until $lower-bound <= $value < $upper-bound;
 
while ($value or $place > 0) and $place > $precision {
my $digit = ($radix * $value - $lower-bound).Int;
$value = $radix * $value - $digit;
$result ~= '.' unless $place or $result.contains: '.';
$result ~= $digit == -$radix ?? ($digit-1).base(-$radix)~'0' !! $digit.base(-$radix);
$place--
}
$result
}
 
multi sub base (Numeric $num, Complex $radix where *.re == 0, :$precision = -8 ) {
die "Base $radix out of range" unless -6 <= $radix.im <= -2 or 2 <= $radix.im <= 6;
my ($re, $im) = $num.Complex.reals;
my ($re-wh, $re-fr) = $re.&base( -$radix.im².Int, :precision($precision) ).split: '.';
my ($im-wh, $im-fr) = ($im/$radix.im).&base( -$radix.im².Int, :precision($precision) ).split: '.';
$_ //= '' for $re-fr, $im-fr;
 
sub zip (Str $a, Str $b) {
my $l = '0' x ($a.chars - $b.chars).abs;
([~] flat ($a~$l).comb Z flat ($b~$l).comb).subst(/ '0'+ $ /, '') || '0'
}
 
my $whole = flip zip $re-wh.flip, $im-wh.flip;
my $fraction = zip $im-fr, $re-fr;
$fraction eq 0 ?? "$whole" !! "$whole.$fraction"
}
 
multi sub parse-base (Str $str, Complex $radix where *.re == 0) {
return -1 * $str.substr(1).&parse-base($radix) if $str.substr(0,1) eq '-';
my ($whole, $frac) = $str.split: '.';
my $fraction = 0;
$fraction = [+] $frac.comb.kv.map: { $^v.parse-base($radix.im².Int) * $radix ** -($^k+1) } if $frac;
$fraction + [+] $whole.flip.comb.kv.map: { $^v.parse-base($radix.im².Int) * $radix ** $^k }
}
 
# TESTING
for 0, 2i, 1, 2i, 5, 2i, -13, 2i, 9i, 2i, -3i, 2i, 7.75-7.5i, 2i, .25, 2i, # base 2i tests
5+5i, 2i, 5+5i, 3i, 5+5i, 4i, 5+5i, 5i, 5+5i, 6i, # same value, positive imaginary bases
5+5i, -2i, 5+5i, -3i, 5+5i, -4i, 5+5i, -5i, 5+5i, -6i, # same value, negative imaginary bases
227.65625+10.859375i, 4i, # larger test value
31433.3487654321-2902.4480452675i, 6i # heh
-> $v, $r {
my $ibase = $v.&base($r, :precision(-6));
printf "%33s.&base\(%2si\) = %-11s : %13s.&parse-base\(%2si\) = %s\n",
$v, $r.im, $ibase, "'$ibase'", $r.im, $ibase.&parse-base($r).round(1e-10).narrow;
}
Output:
                                0.&base( 2i) = 0           :           '0'.&parse-base( 2i) = 0
                                1.&base( 2i) = 1           :           '1'.&parse-base( 2i) = 1
                                5.&base( 2i) = 10301       :       '10301'.&parse-base( 2i) = 5
                              -13.&base( 2i) = 1030003     :     '1030003'.&parse-base( 2i) = -13
                             0+9i.&base( 2i) = 103010.2    :    '103010.2'.&parse-base( 2i) = 0+9i
                            -0-3i.&base( 2i) = 1030.2      :      '1030.2'.&parse-base( 2i) = 0-3i
                        7.75-7.5i.&base( 2i) = 11210.31    :    '11210.31'.&parse-base( 2i) = 7.75-7.5i
                             0.25.&base( 2i) = 1.03        :        '1.03'.&parse-base( 2i) = 0.25
                             5+5i.&base( 2i) = 10331.2     :     '10331.2'.&parse-base( 2i) = 5+5i
                             5+5i.&base( 3i) = 25.3        :        '25.3'.&parse-base( 3i) = 5+5i
                             5+5i.&base( 4i) = 25.C        :        '25.C'.&parse-base( 4i) = 5+5i
                             5+5i.&base( 5i) = 15          :          '15'.&parse-base( 5i) = 5+5i
                             5+5i.&base( 6i) = 15.6        :        '15.6'.&parse-base( 6i) = 5+5i
                             5+5i.&base(-2i) = 11321.2     :     '11321.2'.&parse-base(-2i) = 5+5i
                             5+5i.&base(-3i) = 1085.6      :      '1085.6'.&parse-base(-3i) = 5+5i
                             5+5i.&base(-4i) = 10F5.4      :      '10F5.4'.&parse-base(-4i) = 5+5i
                             5+5i.&base(-5i) = 10O5        :        '10O5'.&parse-base(-5i) = 5+5i
                             5+5i.&base(-6i) = 5.U         :         '5.U'.&parse-base(-6i) = 5+5i
             227.65625+10.859375i.&base( 4i) = 10234.5678  :  '10234.5678'.&parse-base( 4i) = 227.65625+10.859375i
31433.3487654321-2902.4480452675i.&base( 6i) = PERL6.ROCKS : 'PERL6.ROCKS'.&parse-base( 6i) = 31433.3487654321-2902.4480452675i

Sidef[edit]

Translation of: Perl 6
func base (Number num, Number radix { _ ~~ (-36 .. -2) }, precision = -15) -> String {
num || return '0'
 
var place = 0
var result = ''
var value = num
var upper_bound = 1/(-radix + 1)
var lower_bound = radix*upper_bound
 
while (!(lower_bound <= value) || !(value < upper_bound)) {
value = num/(radix**++place)
}
 
while ((value || (place > 0)) && (place > precision)) {
var digit = (radix*value - lower_bound -> int)
value = (radix*value - digit)
result += '.' if (!place && !result.contains('.'))
result += ((digit == -radix) ? (digit-1 -> base(-radix) + '0') : digit.base(-radix))
place--
}
 
return result
}
 
func base (Number num, Number radix { .re == 0 }, precision = -8) -> String {
 
(radix.im.abs ~~ 2..6) || die "Base #{radix} out of range"
 
var (re, im) = (num.re, num.im)
var (re_wh, re_fr='') = base(re, -radix.im**2, precision).split('.')...
var (im_wh, im_fr='') = base(im/radix.im, -radix.im**2, precision).split('.')...
 
func zip (String a, String b) {
var l = ('0' * abs(a.len - b.len))
chars(a+l) ~Z chars(b+l) -> flat.join.sub(/0+\z/, '') || '0'
}
 
var whole = zip(re_wh.flip, im_wh.flip).flip
var fraction = zip(im_fr, re_fr)
fraction == '0' ? whole : "#{whole}.#{fraction}"
}
 
func parse_base (String str, Number radix { .re == 0 }) -> Number {
 
if (str.char(0) == '-') {
return (-1 * parse_base(str.substr(1), radix))
}
 
var (whole, frac='') = str.split('.')...
 
var fraction = frac.chars.map_kv {|k,v|
Number(v, radix.im**2) * radix**-(k+1)
}.sum
 
fraction += whole.flip.chars.map_kv {|k,v|
Number(v, radix.im**2) * radix**k
}.sum
 
return fraction
}
 
var tests = [0, 2i, 1, 2i, 5, 2i, -13, 2i, 9i, 2i, -3i, 2i, 7.75-7.5i, 2i, .25, 2i, # base 2i tests
5+5i, 2i, 5+5i, 3i, 5+5i, 4i, 5+5i, 5i, 5+5i, 6i, # same value, positive imaginary bases
5+5i, -2i, 5+5i, -3i, 5+5i, -4i, 5+5i, -5i, 5+5i, -6i, # same value, negative imaginary bases
227.65625+10.859375i, 4i] # larger test value
 
tests.each_slice(2, {|v,r|
var ibase = base(v, r)
printf("base(%20s, %2si) = %-10s : parse_base(%12s, %2si) = %s\n",
v, r.im, ibase, "'#{ibase}'", r.im, parse_base(ibase, r).round(-8))
})
Output:
base(                   0,  2i) = 0          : parse_base(         '0',  2i) = 0
base(                   1,  2i) = 1          : parse_base(         '1',  2i) = 1
base(                   5,  2i) = 10301      : parse_base(     '10301',  2i) = 5
base(                 -13,  2i) = 1030003    : parse_base(   '1030003',  2i) = -13
base(                  9i,  2i) = 103010.2   : parse_base(  '103010.2',  2i) = 9i
base(                 -3i,  2i) = 1030.2     : parse_base(    '1030.2',  2i) = -3i
base(           7.75-7.5i,  2i) = 11210.31   : parse_base(  '11210.31',  2i) = 7.75-7.5i
base(                0.25,  2i) = 1.03       : parse_base(      '1.03',  2i) = 0.25
base(                5+5i,  2i) = 10331.2    : parse_base(   '10331.2',  2i) = 5+5i
base(                5+5i,  3i) = 25.3       : parse_base(      '25.3',  3i) = 5+5i
base(                5+5i,  4i) = 25.c       : parse_base(      '25.c',  4i) = 5+5i
base(                5+5i,  5i) = 15         : parse_base(        '15',  5i) = 5+5i
base(                5+5i,  6i) = 15.6       : parse_base(      '15.6',  6i) = 5+5i
base(                5+5i, -2i) = 11321.2    : parse_base(   '11321.2', -2i) = 5+5i
base(                5+5i, -3i) = 1085.6     : parse_base(    '1085.6', -3i) = 5+5i
base(                5+5i, -4i) = 10f5.4     : parse_base(    '10f5.4', -4i) = 5+5i
base(                5+5i, -5i) = 10o5       : parse_base(      '10o5', -5i) = 5+5i
base(                5+5i, -6i) = 5.u        : parse_base(       '5.u', -6i) = 5+5i
base(227.65625+10.859375i,  4i) = 10234.5678 : parse_base('10234.5678',  4i) = 227.65625+10.859375i