Imaginary base numbers
You are encouraged to solve this task according to the task description, using any language you may know.
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.
|
|
|
|
11l
<lang 11l>F inv(c)
V denom = c.real * c.real + c.imag * c.imag R Complex(c.real / denom, -c.imag / denom)
V QuaterImaginary_twoI = Complex(0, 2) V QuaterImaginary_invTwoI = inv(QuaterImaginary_twoI)
T QuaterImaginary
String b2i
F (str) I !re:‘[0123.]+’.match(str) | str.count(‘.’) > 1 assert(0B, ‘Invalid base 2i number’) .b2i = str
F toComplex() V pointPos = .b2i.findi(‘.’) V posLen = I (pointPos < 0) {.b2i.len} E pointPos V sum = Complex(0, 0) V prod = Complex(1, 0) L(j) 0 .< posLen V k = Int(.b2i[posLen - 1 - j]) I k > 0 sum += prod * k prod *= QuaterImaginary_twoI I pointPos != -1 prod = QuaterImaginary_invTwoI L(j) posLen + 1 .< .b2i.len V k = Int(.b2i[j]) I k > 0 sum += prod * k prod *= QuaterImaginary_invTwoI R sum
F String() R String(.b2i)
F toQuaterImaginary(c)
I c.real == 0.0 & c.imag == 0.0 R QuaterImaginary(‘0’)
V re = Int(c.real) V im = Int(c.imag) V fi = -1 V ss = ‘’ L re != 0 (re, V rem) = divmod(re, -4) I rem < 0 rem += 4 re++ ss ‘’= String(rem)‘0’ I im != 0 V f = c.imag / 2 im = Int(ceil(f)) f = -4 * (f - im) V index = 1 L im != 0 (im, V rem) = divmod(im, -4) I rem < 0 rem += 4 im++ I index < ss.len assert(0B) E ss ‘’= ‘0’String(rem) index = index + 2 fi = Int(f) ss = reversed(ss) I fi != -1 ss ‘’= ‘.’String(fi) ss = ss.ltrim(‘0’) I ss[0] == ‘.’ ss = ‘0’ss R QuaterImaginary(ss)
L(i) 1..16
V c1 = Complex(i, 0) V qi = toQuaterImaginary(c1) V c2 = qi.toComplex() print(‘#8 -> #8 -> #8 ’.format(c1, qi, c2), end' ‘ ’)
c1 = -c1 qi = toQuaterImaginary(c1) c2 = qi.toComplex() print(‘#8 -> #8 -> #8’.format(c1, qi, c2))
print()
L(i) 1..16
V c1 = Complex(0, i) V qi = toQuaterImaginary(c1) V c2 = qi.toComplex() print(‘#8 -> #8 -> #8 ’.format(c1, qi, c2), end' ‘ ’)
c1 = -c1 qi = toQuaterImaginary(c1) c2 = qi.toComplex() print(‘#8 -> #8 -> #8’.format(c1, qi, c2))
print(‘done’)</lang>
- 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 done
C
<lang c>#include <math.h>
- include <stdio.h>
- include <string.h>
int find(char *s, char c) {
for (char *i = s; *i != 0; i++) { if (*i == c) { return i - s; } } return -1;
}
void reverse(char *b, char *e) {
for (e--; b < e; b++, e--) { char t = *b; *b = *e; *e = t; }
}
//////////////////////////////////////////////////////
struct Complex {
double rel, img;
};
void printComplex(struct Complex c) {
printf("(%3.0f + %3.0fi)", c.rel, c.img);
}
struct Complex makeComplex(double rel, double img) {
struct Complex c = { rel, img }; return c;
}
struct Complex addComplex(struct Complex a, struct Complex b) {
struct Complex c = { a.rel + b.rel, a.img + b.img }; return c;
}
struct Complex mulComplex(struct Complex a, struct Complex b) {
struct Complex c = { a.rel * b.rel - a.img * b.img, a.rel * b.img - a.img * b.rel }; return c;
}
struct Complex mulComplexD(struct Complex a, double b) {
struct Complex c = { a.rel * b, a.img * b }; return c;
}
struct Complex negComplex(struct Complex a) {
return mulComplexD(a, -1.0);
}
struct Complex divComplex(struct Complex a, struct Complex b) {
double re = a.rel * b.rel + a.img * b.img; double im = a.img * b.rel - a.rel * b.img; double d = b.rel * b.rel + b.img * b.img; struct Complex c = { re / d, im / d }; return c;
}
struct Complex inv(struct Complex c) {
double d = c.rel * c.rel + c.img * c.img; struct Complex i = { c.rel / d, -c.img / d }; return i;
}
const struct Complex TWO_I = { 0.0, 2.0 }; const struct Complex INV_TWO_I = { 0.0, -0.5 };
//////////////////////////////////////////////////////
struct QuaterImaginary {
char *b2i; int valid;
};
struct QuaterImaginary makeQuaterImaginary(char *s) {
struct QuaterImaginary qi = { s, 0 }; // assume invalid until tested size_t i, valid = 1, cnt = 0;
if (*s != 0) { for (i = 0; s[i] != 0; i++) { if (s[i] < '0' || '3' < s[i]) { if (s[i] == '.') { cnt++; } else { valid = 0; break; } } } if (valid && cnt > 1) { valid = 0; } }
qi.valid = valid; return qi;
}
void printQuaterImaginary(struct QuaterImaginary qi) {
if (qi.valid) { printf("%8s", qi.b2i); } else { printf(" ERROR "); }
}
//////////////////////////////////////////////////////
struct Complex qi2c(struct QuaterImaginary qi) {
size_t len = strlen(qi.b2i); int pointPos = find(qi.b2i, '.'); size_t posLen = (pointPos > 0) ? pointPos : len; struct Complex sum = makeComplex(0.0, 0.0); struct Complex prod = makeComplex(1.0, 0.0); size_t j;
for (j = 0; j < posLen; j++) { double k = qi.b2i[posLen - 1 - j] - '0'; if (k > 0.0) { sum = addComplex(sum, mulComplexD(prod, k)); } prod = mulComplex(prod, TWO_I); } if (pointPos != -1) { prod = INV_TWO_I; for (j = posLen + 1; j < len; j++) { double k = qi.b2i[j] - '0'; if (k > 0.0) { sum = addComplex(sum, mulComplexD(prod, k)); } prod = mulComplex(prod, INV_TWO_I); } } return sum;
}
// only works properly if the real and imaginary parts are integral struct QuaterImaginary c2qi(struct Complex c, char *out) {
char *p = out; int re, im, fi;
*p = 0; if (c.rel == 0.0 && c.img == 0.0) { return makeQuaterImaginary("0"); }
re = (int)c.rel; im = (int)c.img; fi = -1; while (re != 0) { int rem = re % -4; re /= -4; if (rem < 0) { rem += 4; re++; } *p++ = rem + '0'; *p++ = '0'; *p = 0; } if (im != 0) { size_t index = 1; struct Complex fc = divComplex((struct Complex) { 0.0, c.img }, (struct Complex) { 0.0, 2.0 }); double f = fc.rel; im = (int)ceil(f); f = -4.0 * (f - im); while (im != 0) { int rem = im % -4; im /= -4; if (rem < 0) { rem += 4; im++; } if (index < (p - out)) { out[index] = rem + '0'; } else { *p++ = '0'; *p++ = rem + '0'; *p = 0; } index += 2; } fi = (int)f; }
reverse(out, p); if (fi != -1) { *p++ = '.'; *p++ = fi + '0'; *p = 0; } while (out[0] == '0' && out[1] != '.') { size_t i; for (i = 0; out[i] != 0; i++) { out[i] = out[i + 1]; } } if (*out == '.') { reverse(out, p); *p++ = '0'; *p = 0; reverse(out, p); } return makeQuaterImaginary(out);
}
//////////////////////////////////////////////////////
int main() {
char buffer[16]; int i;
for (i = 1; i <= 16; i++) { struct Complex c1 = { i, 0.0 }; struct QuaterImaginary qi = c2qi(c1, buffer); struct Complex c2 = qi2c(qi); printComplex(c1); printf(" -> "); printQuaterImaginary(qi); printf(" -> "); printComplex(c2);
printf(" ");
c1 = negComplex(c1); qi = c2qi(c1, buffer); c2 = qi2c(qi); printComplex(c1); printf(" -> "); printQuaterImaginary(qi); printf(" -> "); printComplex(c2);
printf("\n"); }
printf("\n");
for (i = 1; i <= 16; i++) { struct Complex c1 = { 0.0, i }; struct QuaterImaginary qi = c2qi(c1, buffer); struct Complex c2 = qi2c(qi); printComplex(c1); printf(" -> "); printQuaterImaginary(qi); printf(" -> "); printComplex(c2);
printf(" ");
c1 = negComplex(c1); qi = c2qi(c1, buffer); c2 = qi2c(qi); printComplex(c1); printf(" -> "); printQuaterImaginary(qi); printf(" -> "); printComplex(c2);
printf("\n"); }
return 0;
}</lang>
- Output:
( 1 + 0i) -> 1 -> ( 1 + 0i) ( -1 + -0i) -> 103 -> ( -1 + 0i) ( 2 + 0i) -> 2 -> ( 2 + 0i) ( -2 + -0i) -> 102 -> ( -2 + 0i) ( 3 + 0i) -> 3 -> ( 3 + 0i) ( -3 + -0i) -> 101 -> ( -3 + 0i) ( 4 + 0i) -> 10300 -> ( 4 + 0i) ( -4 + -0i) -> 100 -> ( -4 + 0i) ( 5 + 0i) -> 10301 -> ( 5 + 0i) ( -5 + -0i) -> 203 -> ( -5 + 0i) ( 6 + 0i) -> 10302 -> ( 6 + 0i) ( -6 + -0i) -> 202 -> ( -6 + 0i) ( 7 + 0i) -> 10303 -> ( 7 + 0i) ( -7 + -0i) -> 201 -> ( -7 + 0i) ( 8 + 0i) -> 10200 -> ( 8 + 0i) ( -8 + -0i) -> 200 -> ( -8 + 0i) ( 9 + 0i) -> 10201 -> ( 9 + 0i) ( -9 + -0i) -> 303 -> ( -9 + 0i) ( 10 + 0i) -> 10202 -> ( 10 + 0i) (-10 + -0i) -> 302 -> (-10 + 0i) ( 11 + 0i) -> 10203 -> ( 11 + 0i) (-11 + -0i) -> 301 -> (-11 + 0i) ( 12 + 0i) -> 10100 -> ( 12 + 0i) (-12 + -0i) -> 300 -> (-12 + 0i) ( 13 + 0i) -> 10101 -> ( 13 + 0i) (-13 + -0i) -> 1030003 -> (-13 + 0i) ( 14 + 0i) -> 10102 -> ( 14 + 0i) (-14 + -0i) -> 1030002 -> (-14 + 0i) ( 15 + 0i) -> 10103 -> ( 15 + 0i) (-15 + -0i) -> 1030001 -> (-15 + 0i) ( 16 + 0i) -> 10000 -> ( 16 + 0i) (-16 + -0i) -> 1030000 -> (-16 + 0i) ( 0 + 1i) -> 10.2 -> ( 0 + 1i) ( -0 + -1i) -> 0.2 -> ( 0 + -1i) ( 0 + 2i) -> 10.0 -> ( 0 + 2i) ( -0 + -2i) -> 1030.0 -> ( 0 + -2i) ( 0 + 3i) -> 20.2 -> ( 0 + 3i) ( -0 + -3i) -> 1030.2 -> ( 0 + -3i) ( 0 + 4i) -> 20.0 -> ( 0 + 4i) ( -0 + -4i) -> 1020.0 -> ( 0 + -4i) ( 0 + 5i) -> 30.2 -> ( 0 + 5i) ( -0 + -5i) -> 1020.2 -> ( 0 + -5i) ( 0 + 6i) -> 30.0 -> ( 0 + 6i) ( -0 + -6i) -> 1010.0 -> ( 0 + -6i) ( 0 + 7i) -> 103000.2 -> ( 0 + 7i) ( -0 + -7i) -> 1010.2 -> ( 0 + -7i) ( 0 + 8i) -> 103000.0 -> ( 0 + 8i) ( -0 + -8i) -> 1000.0 -> ( 0 + -8i) ( 0 + 9i) -> 103010.2 -> ( 0 + 9i) ( -0 + -9i) -> 1000.2 -> ( 0 + -9i) ( 0 + 10i) -> 103010.0 -> ( 0 + 10i) ( -0 + -10i) -> 2030.0 -> ( 0 + -10i) ( 0 + 11i) -> 103020.2 -> ( 0 + 11i) ( -0 + -11i) -> 2030.2 -> ( 0 + -11i) ( 0 + 12i) -> 103020.0 -> ( 0 + 12i) ( -0 + -12i) -> 2020.0 -> ( 0 + -12i) ( 0 + 13i) -> 103030.2 -> ( 0 + 13i) ( -0 + -13i) -> 2020.2 -> ( 0 + -13i) ( 0 + 14i) -> 103030.0 -> ( 0 + 14i) ( -0 + -14i) -> 2010.0 -> ( 0 + -14i) ( 0 + 15i) -> 102000.2 -> ( 0 + 15i) ( -0 + -15i) -> 2010.2 -> ( 0 + -15i) ( 0 + 16i) -> 102000.0 -> ( 0 + 16i) ( -0 + -16i) -> 2000.0 -> ( 0 + -16i)
C#
<lang csharp>using System; using System.Linq; using System.Text;
namespace ImaginaryBaseNumbers {
class Complex { private double real, imag;
public Complex(int r, int i) { real = r; imag = i; }
public Complex(double r, double i) { real = r; imag = i; }
public static Complex operator -(Complex self) => new Complex(-self.real, -self.imag);
public static Complex operator +(Complex rhs, Complex lhs) => new Complex(rhs.real + lhs.real, rhs.imag + lhs.imag);
public static Complex operator -(Complex rhs, Complex lhs) => new Complex(rhs.real - lhs.real, rhs.imag - lhs.imag);
public static Complex operator *(Complex rhs, Complex lhs) => new Complex( rhs.real * lhs.real - rhs.imag * lhs.imag, rhs.real * lhs.imag + rhs.imag * lhs.real );
public static Complex operator *(Complex rhs, double lhs) => new Complex(rhs.real * lhs, rhs.imag * lhs);
public static Complex operator /(Complex rhs, Complex lhs) => rhs * lhs.Inv();
public Complex Inv() { double denom = real * real + imag * imag; return new Complex(real / denom, -imag / denom); }
public QuaterImaginary ToQuaterImaginary() { if (real == 0.0 && imag == 0.0) return new QuaterImaginary("0"); int re = (int)real; int im = (int)imag; int fi = -1; StringBuilder sb = new StringBuilder(); while (re != 0) { int rem = re % -4; re /= -4; if (rem < 0) { rem = 4 + rem; re++; } sb.Append(rem); sb.Append(0); } if (im != 0) { double f = (new Complex(0.0, imag) / new Complex(0.0, 2.0)).real; im = (int)Math.Ceiling(f); f = -4.0 * (f - im); int index = 1; while (im != 0) { int rem = im % -4; im /= -4; if (rem < 0) { rem = 4 + rem; im++; } if (index < sb.Length) { sb[index] = (char)(rem + 48); } else { sb.Append(0); sb.Append(rem); } index += 2; } fi = (int)f; } string reverse = new string(sb.ToString().Reverse().ToArray()); sb.Length = 0; sb.Append(reverse); if (fi != -1) sb.AppendFormat(".{0}", fi); string s = sb.ToString().TrimStart('0'); if (s[0] == '.') s = "0" + s; return new QuaterImaginary(s); }
public override string ToString() { double real2 = (real == -0.0) ? 0.0 : real; // get rid of negative zero double imag2 = (imag == -0.0) ? 0.0 : imag; // ditto if (imag2 == 0.0) { return string.Format("{0}", real2); } if (real2 == 0.0) { return string.Format("{0}i", imag2); } if (imag2 > 0.0) { return string.Format("{0} + {1}i", real2, imag2); } return string.Format("{0} - {1}i", real2, -imag2); } }
class QuaterImaginary { internal static Complex twoI = new Complex(0.0, 2.0); internal static Complex invTwoI = twoI.Inv();
private string b2i;
public QuaterImaginary(string b2i) { if (b2i == "" || !b2i.All(c => "0123.".IndexOf(c) > -1) || b2i.Count(c => c == '.') > 1) { throw new Exception("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, 0.0); Complex prod = new Complex(1.0, 0.0); for (int j = 0; j < posLen; j++) { double k = (b2i[posLen - 1 - j] - '0'); if (k > 0.0) { sum += prod * k; } prod *= twoI; } if (pointPos != -1) { prod = invTwoI; for (int j = posLen + 1; j < b2i.Length; j++) { double k = (b2i[j] - '0'); if (k > 0.0) { sum += prod * k; } prod *= invTwoI; } }
return sum; }
public override string ToString() { return b2i; } }
class Program { static void Main(string[] args) { for (int i = 1; i <= 16; i++) { Complex c1 = new Complex(i, 0); QuaterImaginary qi = c1.ToQuaterImaginary(); Complex c2 = qi.ToComplex(); Console.Write("{0,4} -> {1,8} -> {2,4} ", c1, qi, c2); c1 = -c1; qi = c1.ToQuaterImaginary(); c2 = qi.ToComplex(); Console.WriteLine("{0,4} -> {1,8} -> {2,4}", c1, qi, c2); } Console.WriteLine(); for (int i = 1; i <= 16; i++) { Complex c1 = new Complex(0, i); QuaterImaginary qi = c1.ToQuaterImaginary(); Complex c2 = qi.ToComplex(); Console.Write("{0,4} -> {1,8} -> {2,4} ", c1, qi, c2); c1 = -c1; qi = c1.ToQuaterImaginary(); c2 = qi.ToComplex(); Console.WriteLine("{0,4} -> {1,8} -> {2,4}", c1, qi, c2); } } }
}</lang>
- 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
C++
<lang cpp>#include <algorithm>
- include <complex>
- include <iomanip>
- include <iostream>
std::complex<double> inv(const std::complex<double>& c) {
double denom = c.real() * c.real() + c.imag() * c.imag(); return std::complex<double>(c.real() / denom, -c.imag() / denom);
}
class QuaterImaginary { public:
QuaterImaginary(const std::string& s) : b2i(s) { static std::string base("0123.");
if (b2i.empty() || std::any_of(s.cbegin(), s.cend(), [](char c) { return base.find(c) == std::string::npos; }) || std::count(s.cbegin(), s.cend(), '.') > 1) { throw std::runtime_error("Invalid base 2i number"); } }
QuaterImaginary& operator=(const QuaterImaginary& q) { b2i = q.b2i; return *this; }
std::complex<double> toComplex() const { int pointPos = b2i.find('.'); int posLen = (pointPos != std::string::npos) ? pointPos : b2i.length(); std::complex<double> sum(0.0, 0.0); std::complex<double> prod(1.0, 0.0); for (int j = 0; j < posLen; j++) { double k = (b2i[posLen - 1 - j] - '0'); if (k > 0.0) { sum += prod * k; } prod *= twoI; } if (pointPos != -1) { prod = invTwoI; for (size_t j = posLen + 1; j < b2i.length(); j++) { double k = (b2i[j] - '0'); if (k > 0.0) { sum += prod * k; } prod *= invTwoI; } }
return sum; }
friend std::ostream& operator<<(std::ostream&, const QuaterImaginary&);
private:
const std::complex<double> twoI{ 0.0, 2.0 }; const std::complex<double> invTwoI = inv(twoI);
std::string b2i;
};
std::ostream& operator<<(std::ostream& os, const QuaterImaginary& q) {
return os << q.b2i;
}
// only works properly if 'real' and 'imag' are both integral QuaterImaginary toQuaterImaginary(const std::complex<double>& c) {
if (c.real() == 0.0 && c.imag() == 0.0) return QuaterImaginary("0");
int re = (int)c.real(); int im = (int)c.imag(); int fi = -1; std::stringstream ss; while (re != 0) { int rem = re % -4; re /= -4; if (rem < 0) { rem = 4 + rem; re++; } ss << rem << 0; } if (im != 0) { double f = (std::complex<double>(0.0, c.imag()) / std::complex<double>(0.0, 2.0)).real(); im = (int)ceil(f); f = -4.0 * (f - im); size_t index = 1; while (im != 0) { int rem = im % -4; im /= -4; if (rem < 0) { rem = 4 + rem; im++; } if (index < ss.str().length()) { ss.str()[index] = (char)(rem + 48); } else { ss << 0 << rem; } index += 2; } fi = (int)f; }
auto r = ss.str(); std::reverse(r.begin(), r.end()); ss.str(""); ss.clear(); ss << r; if (fi != -1) ss << '.' << fi; r = ss.str(); r.erase(r.begin(), std::find_if(r.begin(), r.end(), [](char c) { return c != '0'; })); if (r[0] == '.')r = "0" + r; return QuaterImaginary(r);
}
int main() {
using namespace std;
for (int i = 1; i <= 16; i++) { complex<double> c1(i, 0); QuaterImaginary qi = toQuaterImaginary(c1); complex<double> c2 = qi.toComplex(); cout << setw(8) << c1 << " -> " << setw(8) << qi << " -> " << setw(8) << c2 << " "; c1 = -c1; qi = toQuaterImaginary(c1); c2 = qi.toComplex(); cout << setw(8) << c1 << " -> " << setw(8) << qi << " -> " << setw(8) << c2 << endl; } cout << endl;
for (int i = 1; i <= 16; i++) { complex<double> c1(0, i); QuaterImaginary qi = toQuaterImaginary(c1); complex<double> c2 = qi.toComplex(); cout << setw(8) << c1 << " -> " << setw(8) << qi << " -> " << setw(8) << c2 << " "; c1 = -c1; qi = toQuaterImaginary(c1); c2 = qi.toComplex(); cout << setw(8) << c1 << " -> " << setw(8) << qi << " -> " << setw(8) << c2 << endl; }
return 0;
}</lang>
- Output:
(1,0) -> 1 -> (1,0) (-1,-0) -> 103 -> (-1,0) (2,0) -> 2 -> (2,0) (-2,-0) -> 102 -> (-2,0) (3,0) -> 3 -> (3,0) (-3,-0) -> 101 -> (-3,0) (4,0) -> 10300 -> (4,0) (-4,-0) -> 100 -> (-4,0) (5,0) -> 10301 -> (5,0) (-5,-0) -> 203 -> (-5,0) (6,0) -> 10302 -> (6,0) (-6,-0) -> 202 -> (-6,0) (7,0) -> 10303 -> (7,0) (-7,-0) -> 201 -> (-7,0) (8,0) -> 10200 -> (8,0) (-8,-0) -> 200 -> (-8,0) (9,0) -> 10201 -> (9,0) (-9,-0) -> 303 -> (-9,0) (10,0) -> 10202 -> (10,0) (-10,-0) -> 302 -> (-10,0) (11,0) -> 10203 -> (11,0) (-11,-0) -> 301 -> (-11,0) (12,0) -> 10100 -> (12,0) (-12,-0) -> 300 -> (-12,0) (13,0) -> 10101 -> (13,0) (-13,-0) -> 1030003 -> (-13,0) (14,0) -> 10102 -> (14,0) (-14,-0) -> 1030002 -> (-14,0) (15,0) -> 10103 -> (15,0) (-15,-0) -> 1030001 -> (-15,0) (16,0) -> 10000 -> (16,0) (-16,-0) -> 1030000 -> (-16,0) (0,1) -> 10.2 -> (0,1) (-0,-1) -> 0.2 -> (0,-1) (0,2) -> 10.0 -> (0,2) (-0,-2) -> 1030.0 -> (0,-2) (0,3) -> 20.2 -> (0,3) (-0,-3) -> 1030.2 -> (0,-3) (0,4) -> 20.0 -> (0,4) (-0,-4) -> 1020.0 -> (0,-4) (0,5) -> 30.2 -> (0,5) (-0,-5) -> 1020.2 -> (0,-5) (0,6) -> 30.0 -> (0,6) (-0,-6) -> 1010.0 -> (0,-6) (0,7) -> 103000.2 -> (0,7) (-0,-7) -> 1010.2 -> (0,-7) (0,8) -> 103000.0 -> (0,8) (-0,-8) -> 1000.0 -> (0,-8) (0,9) -> 103010.2 -> (0,9) (-0,-9) -> 1000.2 -> (0,-9) (0,10) -> 103010.0 -> (0,10) (-0,-10) -> 2030.0 -> (0,-10) (0,11) -> 103020.2 -> (0,11) (-0,-11) -> 2030.2 -> (0,-11) (0,12) -> 103020.0 -> (0,12) (-0,-12) -> 2020.0 -> (0,-12) (0,13) -> 103030.2 -> (0,13) (-0,-13) -> 2020.2 -> (0,-13) (0,14) -> 103030.0 -> (0,14) (-0,-14) -> 2010.0 -> (0,-14) (0,15) -> 102000.2 -> (0,15) (-0,-15) -> 2010.2 -> (0,-15) (0,16) -> 102000.0 -> (0,16) (-0,-16) -> 2000.0 -> (0,-16)
D
<lang D>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; if (k > 0.0) { sum += prod * k; } 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("%4si -> %8s -> %4si ", c1.im, qi, c2.im); c1 = -c1; qi = c1.toQuaterImaginary(); c2 = cast(Complex!double) qi; writefln("%4si -> %8s -> %4si", c1.im, qi, c2.im); }
}</lang>
- 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
Go
... though a bit shorter as Go has support for complex numbers built into the language. <lang go>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)) }
}</lang>
- 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
Haskell
<lang Haskell>import Data.Char (chr, digitToInt, intToDigit, isDigit, ord) import Data.Complex (Complex(..), imagPart, realPart) import Data.List (elemIndex, delete) import Data.Maybe (fromMaybe)
base :: Complex Float
base = 0 :+ 2
quotRemPositive :: Int -> Int -> (Int, Int) quotRemPositive a b
| r < 0 = (1 + q, floor (realPart (-base ^^ 2)) + r) | otherwise = (q, r) where (q, r) = quotRem a b
digitToIntQI :: Char -> Int digitToIntQI c
| isDigit c = digitToInt c | otherwise = ord c - ord 'a' + 10
shiftRight :: String -> String shiftRight n
| l == '0' = h | otherwise = h ++ "." ++ [l] where (l, h) = (last n, init n)
intToDigitQI :: Int -> Char intToDigitQI i
| i `elem` [0 .. 9] = intToDigit i | otherwise = chr (i - 10 + ord 'a')
fromQItoComplex :: String -> Complex Float -> Complex Float fromQItoComplex num b =
let dot = fromMaybe (length num) (elemIndex '.' num) in fst $ foldl (\(a, indx) x -> (a + fromIntegral (digitToIntQI x) * (b ^^ (dot - indx)), indx + 1)) (0, 1) (delete '.' num)
euclidEr :: Int -> Int -> [Int] -> [Int] euclidEr a b l
| a == 0 = l | otherwise = let (q, r) = quotRemPositive a b in euclidEr q b (0 : r : l)
fromIntToQI :: Int -> [Int] fromIntToQI 0 = [0] fromIntToQI x = tail (euclidEr x (floor $ realPart (base ^^ 2)) [])
getCuid :: Complex Int -> Int getCuid c = imagPart c * floor (imagPart (-base))
qizip :: Complex Int -> [Int] qizip c =
let (r, i) = (fromIntToQI (realPart c) ++ [0], fromIntToQI (getCuid c)) in let m = min (length r) (length i) in take (length r - m) r ++ take (length i - m) i ++ reverse (zipWith (+) (take m (reverse r)) (take m (reverse i)))
fromComplexToQI :: Complex Int -> String fromComplexToQI = shiftRight . fmap intToDigitQI . qizip
main :: IO () main = print (fromComplexToQI (35 :+ 23)) >> print (fromQItoComplex "10.2" base)</lang>
- Output:
"121003.2" 0.0 :+ 1.0
With base = 8i (you may choose any base):
"3z.8" 0.0 :+ 7.75
J
Implementation:
<lang J> ibdec=: {{
0j2 ibdec y
digits=. 0,".,~&'36b'@> tolower y -.'. ' (x #. digits) % x^#(}.~ 1+i.&'.')y-.' '
}}"1
ibenc=: {{
0j2 ibenc y
if.0=y do.,'0' return.end. sq=.*:x assert. 17 > sq step=. }.,~(1,|sq) +^:(0>{:@]) (0,sq) #: {. seq=. step^:(0~:{.)^:_"0 're im0'=.+.y 'im imf'=.(sign,1)*(0,|x)#:im0*sign=.*im0 frac=. ,hfd (imf*|x)-.0 if.#frac do.frac=.'.',frac end. frac,~(}.~0 i.~_1}.'0'=]) }:,hfd|:0 1|."0 1 seq re,im
}}"0 </lang>
This ibdec can decode numbers from complex bases up to 0j6, but this ibenc can only represent digits in complex bases up to 0j4.
Examples:
<lang J>
(ibenc i:16),.' ',.ibenc j.i:16
1030000 2000 1030001 2010.2 1030002 2010 1030003 2020.2 300 2020 301 2030.2 302 2030 303 1000.2 200 1000 201 1010.2 202 1010 203 1020.2 100 1020 101 1030.2 102 1030 103 0.2 0 0 1 0.2 2 10 3 10.2 10300 20 10301 20.2 10302 30 10303 30.2 10200 103000 10201 103000.2 10202 103010 10203 103010.2 10100 103020 10101 103020.2 10102 103030 10103 103030.2 10000 102000
(ibdec ibenc i:16),:ibdec ibenc j.i:16 _16 _15 _14 _13 _12 _11 _10 _9 _8 _7 _6 _5 _4 _3 _2 _1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0j_16 0j_15 0j_14 0j_13 0j_12 0j_11 0j_10 0j_9 0j_8 0j_7 0j_6 0j_5 0j_4 0j_3 0j_2 0j_1 0 0j_1 0j2 0j1 0j4 0j3 0j6 0j5 0j8 0j7 0j10 0j9 0j12 0j11 0j14 0j13 0j16
0j4 ibenc 42
10e0a
0j4 ibdec 0j4 ibenc 42
42 </lang>
Java
<lang Java>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(); } }
}</lang>
- 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
Julia
<lang julia>import Base.show, Base.parse, Base.+, Base.-, Base.*, Base./, Base.^
function inbase4(charvec::Vector)
if (!all(x -> x in ['-', '0', '1', '2', '3', '.'], charvec)) || ((x = findlast(x -> x == '-', charvec)) != nothing && x > findfirst(x -> x != '-', charvec)) || ((x = findall(x -> x == '.', charvec)) != nothing && length(x) > 1) return false end true
end inbase4(s::String) = inbase4(split(s, ""))
abstract type ImaginaryBaseNumber <: Number end
struct QuaterImaginary <: ImaginaryBaseNumber
cvector::Vector{Char} isnegative::Bool
end
function QuaterImaginary(charvec::Vector{Char})
isneg = false if !inbase4(charvec) throw("Constructor vector for QuaterImaginary ($charvec) is not base 2i") elseif (i = length(findall(x -> x == '-', charvec))) > 0 isneg = (-1) ^ i == -1 end while length(charvec) > 1 && charvec[1] == '0' && charvec[2] != '.' popfirst!(charvec) end if (i = findfirst(x -> x == '.', charvec)) != nothing while length(charvec) > 3 && charvec[end] == '0' && charvec[end-1] != '.' pop!(charvec) end end if charvec[1] == '.' pushfirst!(charvec, '0') end if charvec[end] == '.' pop!(charvec) end QuaterImaginary(filter!(x -> x in ['0', '1', '2', '3', '.'], charvec), isneg)
end
function QuaterImaginary(s::String = "0")
if match(r"^-?[0123\.]+$", s) == nothing throw("String constructor argument <$s> for QuaterImaginary is not base 2i") end QuaterImaginary([s[i] for i in 1:length(s)])
end
show(io::IO, qim::QuaterImaginary) = print(io, qim.isnegative ? "-" : "", join(qim.cvector, ""))
function parse(QuaterImaginary, x::Complex)
sb = Vector{Char}() rea, ima = Int(floor(real(x))), Int(floor(imag(x))) if floor(real(x)) != rea || floor(imag(x)) != ima throw("Non-integer real and complex portions of complex numbers are not supported for QuaterImaginary") elseif rea == 0 == ima return QuaterImaginary(['0']) else fi = -1 while rea != 0 rea, rem = divrem(rea, -4) if rem < 0 rem += 4 rea += 1 end push!(sb, Char(rem + '0'), '0') end if ima != 0 f = real((ima * im)/(2im)) ima = Int(ceil(f)) f = -4.0 * (f - ima) idx = 1 while ima != 0 ima, rem = divrem(ima, -4) if rem < 0 rem += 4 ima += 1 end if idx < length(sb) sb[idx + 1] = Char(rem + '0') else push!(sb, '0', Char(rem + '0')) end idx += 2 end fi = Int(floor(f)) end sb = reverse(sb) if fi != -1 push!(sb, '.') append!(sb, map(x -> x[1], split(string(fi), ""))) end end QuaterImaginary(sb)
end
function parse(Complex, qim::QuaterImaginary)
pointpos = ((x = indexin('.', qim.cvector))[1] == nothing) ? -1 : x[1] poslen = (pointpos != -1) ? pointpos : length(qim.cvector) + 1 qsum = 0.0 + 0.0im prod = 1.0 + 0.0im for j in 1:poslen-1 k = Float64(qim.cvector[poslen - j] - '0') if k > 0.0 qsum += prod * k end prod *= 2im end if pointpos != -1 prod = inv(2im) for j in poslen+1:length(qim.cvector) k = Float64(qim.cvector[j] - '0') if k > 0.0 qsum += prod * k end prod *= inv(2im) end end qsum
end
function testquim()
function printcqc(c) q = parse(QuaterImaginary, Complex(c)) c2 = parse(Complex, q) if imag(c2) == 0 c2 = Int(c2) end print(lpad(c, 10), " -> ", lpad(q, 10), " -> ", lpad(c2, 12)) end for i in 1:16 printcqc(i) print(" ") printcqc(-i) println() end println() for i in 1:16 c1 = Complex(0, i) printcqc(c1) print(" ") printcqc(-c1) println() end
end
QuaterImaginary(c::Complex) = parse(QuaterImaginary, c) Complex(q::QuaterImaginary) = parse(Complex, q)
+(q1::QuaterImaginary, q2::QuaterImaginary) = QuaterImaginary(Complex(q1) + Complex(q2)) +(q1::Complex, q2::QuaterImaginary) = q1 + Complex(q2) +(q1::QuaterImaginary, q2::Complex) = Complex(q1) + q2 -(q1::QuaterImaginary, q2::QuaterImaginary) = QuaterImaginary(Complex(q1) - Complex(q2)) -(q1::Complex, q2::QuaterImaginary) = q1 - Complex(q2) -(q1::QuaterImaginary, q2::Complex) = Complex(q1) - q2
- (q1::QuaterImaginary, q2::QuaterImaginary) = QuaterImaginary(Complex(q1) * Complex(q2))
- (q1::Complex, q2::QuaterImaginary) = q1 * Complex(q2)
- (q1::QuaterImaginary, q2::Complex) = Complex(q1) * q2
/(q1::QuaterImaginary, q2::QuaterImaginary) = QuaterImaginary(Complex(q1) / Complex(q2)) /(q1::Complex, q2::QuaterImaginary) = q1 / Complex(q2) /(q1::QuaterImaginary, q2::Complex) = Complex(q1) / q2 ^(q1::QuaterImaginary, q2::QuaterImaginary) = QuaterImaginary(Complex(q1) ^ Complex(q2)) ^(q1::Complex, q2::QuaterImaginary) = q1 ^ Complex(q2) ^(q1::QuaterImaginary, q2::Complex) = Complex(q1) ^ q2
testquim()
</lang>
- 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 -> -160 + 1im -> 10.2 -> 0.0 + 1.0im 0 - 1im -> 0.2 -> 0.0 - 1.0im 0 + 2im -> 10.0 -> 0.0 + 2.0im 0 - 2im -> 1030.0 -> 0.0 - 2.0im 0 + 3im -> 20.2 -> 0.0 + 3.0im 0 - 3im -> 1030.2 -> 0.0 - 3.0im 0 + 4im -> 20.0 -> 0.0 + 4.0im 0 - 4im -> 1020.0 -> 0.0 - 4.0im 0 + 5im -> 30.2 -> 0.0 + 5.0im 0 - 5im -> 1020.2 -> 0.0 - 5.0im 0 + 6im -> 30.0 -> 0.0 + 6.0im 0 - 6im -> 1010.0 -> 0.0 - 6.0im 0 + 7im -> 103000.2 -> 0.0 + 7.0im 0 - 7im -> 1010.2 -> 0.0 - 7.0im 0 + 8im -> 103000.0 -> 0.0 + 8.0im 0 - 8im -> 1000.0 -> 0.0 - 8.0im 0 + 9im -> 103010.2 -> 0.0 + 9.0im 0 - 9im -> 1000.2 -> 0.0 - 9.0im 0 + 10im -> 103010.0 -> 0.0 + 10.0im 0 - 10im -> 2030.0 -> 0.0 - 10.0im 0 + 11im -> 103020.2 -> 0.0 + 11.0im 0 - 11im -> 2030.2 -> 0.0 - 11.0im 0 + 12im -> 103020.0 -> 0.0 + 12.0im 0 - 12im -> 2020.0 -> 0.0 - 12.0im 0 + 13im -> 103030.2 -> 0.0 + 13.0im 0 - 13im -> 2020.2 -> 0.0 - 13.0im 0 + 14im -> 103030.0 -> 0.0 + 14.0im 0 - 14im -> 2010.0 -> 0.0 - 14.0im 0 + 15im -> 102000.2 -> 0.0 + 15.0im 0 - 15im -> 2010.2 -> 0.0 - 15.0im 0 + 16im -> 102000.0 -> 0.0 + 16.0im 0 - 16im -> 2000.0 -> 0.0 - 16.0im
Kotlin
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. <lang scala>// 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)) }
}</lang>
- 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
Modula-2
<lang modula2>MODULE ImaginaryBase; FROM FormatString IMPORT FormatString; FROM RealMath IMPORT round; FROM Terminal IMPORT WriteString,WriteLn,ReadChar;
(* Helper *) TYPE
String = ARRAY[0..10] OF CHAR; StringBuilder = RECORD buf : String; ptr : CARDINAL; END;
PROCEDURE ToChar(n : INTEGER) : CHAR; BEGIN
CASE n OF 0 : RETURN '0' | 1 : RETURN '1' | 2 : RETURN '2' | 3 : RETURN '3' | 4 : RETURN '4' | 5 : RETURN '5' | 6 : RETURN '6' | 7 : RETURN '7' | 8 : RETURN '8' | 9 : RETURN '9' ELSE RETURN '-' END
END ToChar;
PROCEDURE AppendChar(VAR sb : StringBuilder; c : CHAR); BEGIN
sb.buf[sb.ptr] := c; INC(sb.ptr); sb.buf[sb.ptr] := 0C
END AppendChar;
PROCEDURE AppendInt(VAR sb : StringBuilder; n : INTEGER); BEGIN
sb.buf[sb.ptr] := ToChar(n); INC(sb.ptr); sb.buf[sb.ptr] := 0C
END AppendInt;
PROCEDURE Ceil(r : REAL) : REAL; VAR t : REAL; BEGIN
t := FLOAT(INT(r)); IF r - t > 0.0 THEN t := t + 1.0 END; RETURN t
END Ceil;
PROCEDURE Modulus(q,d : INTEGER) : INTEGER; VAR t : INTEGER; BEGIN
t := q / d; RETURN q - d * t
END Modulus;
PROCEDURE PrependInt(VAR sb : StringBuilder; n : INTEGER); VAR i : CARDINAL; BEGIN
i := sb.ptr; INC(sb.ptr); sb.buf[sb.ptr] := 0C; WHILE i > 0 DO sb.buf[i] := sb.buf[i-1]; DEC(i) END; sb.buf[0] := ToChar(n)
END PrependInt;
PROCEDURE Reverse(VAR str : String); VAR
i,j : CARDINAL; c : CHAR;
BEGIN
IF str[0] = 0C THEN RETURN END; i := 0; WHILE str[i] # 0C DO INC(i) END; DEC(i); j := 0; WHILE i > j DO c := str[i]; str[i] := str[j]; str[j] := c; DEC(i); INC(j) END
END Reverse;
PROCEDURE TrimStart(VAR str : String; c : CHAR); VAR i : CARDINAL; BEGIN
WHILE str[0] = c DO i := 0; WHILE str[i] # 0C DO str[i] := str[i+1]; INC(i) END END
END TrimStart;
PROCEDURE WriteInteger(n : INTEGER); VAR buf : ARRAY[0..15] OF CHAR; BEGIN
FormatString("%i", buf, n); WriteString(buf)
END WriteInteger;
(* Imaginary *) TYPE
Complex = RECORD real,imag : REAL; END; QuaterImaginary = RECORD b2i : String; END;
PROCEDURE ComplexMul(lhs,rhs : Complex) : Complex; BEGIN
RETURN Complex{ rhs.real * lhs.real - rhs.imag * lhs.imag, rhs.real * lhs.imag + rhs.imag * lhs.real }
END ComplexMul;
PROCEDURE ComplexMulR(lhs : Complex; rhs : REAL) : Complex; BEGIN
RETURN Complex{lhs.real * rhs, lhs.imag * rhs}
END ComplexMulR;
PROCEDURE ComplexInv(c : Complex) : Complex; VAR denom : REAL; BEGIN
denom := c.real * c.real + c.imag * c.imag; RETURN Complex{c.real / denom, -c.imag / denom}
END ComplexInv;
PROCEDURE ComplexDiv(lhs,rhs : Complex) : Complex; BEGIN
RETURN ComplexMul(lhs, ComplexInv(rhs))
END ComplexDiv;
PROCEDURE ComplexNeg(c : Complex) : Complex; BEGIN
RETURN Complex{-c.real, -c.imag}
END ComplexNeg;
PROCEDURE ComplexSum(lhs,rhs : Complex) : Complex; BEGIN
RETURN Complex{lhs.real + rhs.real, lhs.imag + rhs.imag}
END ComplexSum;
PROCEDURE WriteComplex(c : Complex); VAR buf : ARRAY[0..15] OF CHAR; BEGIN
IF c.imag = 0.0 THEN WriteInteger(INT(c.real)) ELSIF c.real = 0.0 THEN WriteInteger(INT(c.imag)); WriteString("i") ELSIF c.imag > 0.0 THEN WriteInteger(INT(c.real)); WriteString(" + "); WriteInteger(INT(c.imag)); WriteString("i") ELSE WriteInteger(INT(c.real)); WriteString(" - "); WriteInteger(INT(-c.imag)); WriteString("i") END
END WriteComplex;
PROCEDURE ToQuaterImaginary(c : Complex) : QuaterImaginary; VAR
re,im,fi,rem,index : INTEGER; f : REAL; t : Complex; sb : StringBuilder;
BEGIN
IF (c.real = 0.0) AND (c.imag = 0.0) THEN RETURN QuaterImaginary{"0"} END; re := INT(c.real); im := INT(c.imag); fi := -1; sb := StringBuilder{"", 0}; WHILE re # 0 DO rem := Modulus(re, -4); re := re / (-4); IF rem < 0 THEN rem := 4 + rem; INC(re) END; AppendInt(sb, rem); AppendInt(sb, 0) END; IF im # 0 THEN t := ComplexDiv(Complex{0.0, c.imag}, Complex{0.0, 2.0}); f := t.real; im := INT(Ceil(f)); f := -4.0 * (f - FLOAT(im)); index := 1; WHILE im # 0 DO rem := Modulus(im, -4); im := im / (-4); IF rem < 0 THEN rem := 4 + rem; INC(im) END; IF index < INT(sb.ptr) THEN sb.buf[index] := ToChar(rem) ELSE AppendInt(sb, 0); AppendInt(sb, rem) END; index := index + 2; END; fi := INT(f) END; Reverse(sb.buf); IF fi # -1 THEN AppendChar(sb, '.'); AppendInt(sb, fi) END; TrimStart(sb.buf, '0'); IF sb.buf[0] = '.' THEN PrependInt(sb, 0) END; RETURN QuaterImaginary{sb.buf}
END ToQuaterImaginary;
PROCEDURE ToComplex(qi : QuaterImaginary) : Complex; VAR
j,pointPos,posLen,b2iLen : INTEGER; k : REAL; sum,prod : Complex;
BEGIN
pointPos := 0; WHILE (qi.b2i[pointPos] # 0C) AND (qi.b2i[pointPos] # '.') DO INC(pointPos) END; IF qi.b2i[pointPos] # '.' THEN pointPos := -1; posLen := 0; WHILE qi.b2i[posLen] # 0C DO INC(posLen) END ELSE posLen := pointPos END;
sum := Complex{0.0, 0.0}; prod := Complex{1.0, 0.0}; FOR j:=0 TO posLen - 1 DO k := FLOAT(ORD(qi.b2i[posLen - 1 - j]) - ORD('0')); IF k > 0.0 THEN sum := ComplexSum(sum, ComplexMulR(prod, k)) END; prod := ComplexMul(prod, Complex{0.0, 2.0}) END; IF pointPos # -1 THEN prod := ComplexInv(Complex{0.0, 2.0}); b2iLen := 0; WHILE qi.b2i[b2iLen] # 0C DO INC(b2iLen) END; FOR j:=posLen + 1 TO b2iLen - 1 DO k := FLOAT(ORD(qi.b2i[j]) - ORD('0')); IF k > 0.0 THEN sum := ComplexSum(sum, ComplexMulR(prod, k)) END; prod := ComplexMul(prod, ComplexInv(Complex{0.0, 2.0})) END END; RETURN sum
END ToComplex;
(* Main *) VAR
c1,c2 : Complex; qi : QuaterImaginary; i : INTEGER;
BEGIN
FOR i:=1 TO 16 DO c1 := Complex{FLOAT(i), 0.0}; WriteComplex(c1); WriteString(" -> "); qi := ToQuaterImaginary(c1); WriteString(qi.b2i); WriteString(" -> "); c2 := ToComplex(qi); WriteComplex(c2); WriteString(" "); c1 := ComplexNeg(c1); WriteComplex(c1); WriteString(" -> "); qi := ToQuaterImaginary(c1); WriteString(qi.b2i); WriteString(" -> "); c2 := ToComplex(qi); WriteComplex(c2); WriteLn END; WriteLn;
FOR i:=1 TO 16 DO c1 := Complex{0.0, FLOAT(i)}; WriteComplex(c1); WriteString(" -> "); qi := ToQuaterImaginary(c1); WriteString(qi.b2i); WriteString(" -> "); c2 := ToComplex(qi); WriteComplex(c2); WriteString(" ");
c1 := ComplexNeg(c1); WriteComplex(c1); WriteString(" -> "); qi := ToQuaterImaginary(c1); WriteString(qi.b2i); WriteString(" -> "); c2 := ToComplex(qi); WriteComplex(c2); WriteLn END;
ReadChar
END ImaginaryBase.</lang>
- 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
Nim
This is a fairly faithful translation of the Kotlin program except that we had not to define a Complex type as Nim provides the module “complex” in its standard library. We had only to define a function “toString” for the “Complex[float]” type, function to use in place of “$” in order to get a more pleasant output. <lang Nim>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}"</lang>
- 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
<lang perl>use strict; use warnings; use feature 'say';
use Math::Complex; use List::AllUtils qw(sum mesh); use ntheory qw<todigitstring fromdigits>;
sub zip {
my($a,$b) = @_; my($la, $lb) = (length $a, length $b); my $l = '0' x abs $la - $lb; $a .= $l if $la < $lb; $b .= $l if $lb < $la; (join(, mesh(@{[split(,$a),]}, @{[split(,$b),]})) =~ s/0+$//r) or 0;
}
sub base_i {
my($num,$radix,$precision) = @_; die unless $radix > -37 and $radix < -1; 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 and $value < $upper_bound;
while (($value or $place > 0) and $place > $precision) { my $digit = int $radix * $value - $lower_bound; $value = $radix * $value - $digit; $result .= '.' unless $place or not index($result, '.'); $result .= $digit == -$radix ? todigitstring($digit-1, -$radix) . '0' : (todigitstring($digit, -$radix) or '0'); $place--; } $result
}
sub base_c {
my($num, $radix, $precision) = @_; die "Base $radix out of range" unless (-6 <= $radix->Im or $radix->Im <= -2) or (2 <= $radix->Im or $radix->Im <= 6); my ($re,$im); defined $num->Im ? ($re, $im) = ($num->Re, $num->Im) : $re = $num; my ($re_wh, $re_fr) = split /\./, base_i( $re, -1 * int($radix->Im**2), $precision); my ($im_wh, $im_fr) = split /\./, base_i( ($im/($radix->Im)), -1 * int($radix->Im**2), $precision); $_ //= for $re_fr, $im_fr;
my $whole = reverse zip scalar reverse($re_wh), scalar reverse($im_wh); my $fraction = zip $im_fr, $re_fr; $fraction eq 0 ? "$whole" : "$whole.$fraction"
}
sub parse_base {
my($str, $radix) = @_; return -1 * parse_base( substr($str,1), $radix) if substr($str,0,1) eq '-'; my($whole, $frac) = split /\./, $str; my $fraction = 0; my $k = 0; $fraction = sum map { (fromdigits($_, int $radix->Im**2) || 0) * $radix ** -($k++ +1) } split , $frac if $frac; $k = 0; $fraction + sum map { (fromdigits($_, int $radix->Im**2) || 0) * $radix ** $k++ } reverse split , $whole;
}
for (
[ 0*i, 2*i], [1+0*i, 2*i], [5+0*i, 2*i], [ -13+0*i, 2*i], [ 9*i, 2*i], [ -3*i, 2*i], [7.75-7.5*i, 2*i], [0.25+0*i, 2*i], [5+5*i, 2*i], [5+5*i, 3*i], [5+5*i, 4*i], [5+5*i, 5*i], [5+5*i, 6*i], [5+5*i, -2*i], [5+5*i, -3*i], [5+5*i, -4*i], [5+5*i, -5*i], [5+5*i, -6*i]
) {
my($v,$r) = @$_; my $ibase = base_c($v, $r, -6); my $rt = cplx parse_base($ibase, $r); $rt->display_format('format' => '%.2f'); printf "base(%3s): %10s => %9s => %13s\n", $r, $v, $ibase, $rt;
}
say ; say 'base( 6i): 31432.6219135802-2898.5266203704*i => ' .
base_c(31432.6219135802-2898.5266203704*i, 0+6*i, -3);</lang>
- Output:
base( 2i): 0 => 0 => 0 base( 2i): 1 => 1 => 1.00 base( 2i): 5 => 10301 => 5.00-0.00i base( 2i): -13 => 1030003 => -13.00+0.00i base( 2i): 9i => 103010.2 => 0.00+9.00i base( 2i): -3i => 1030.2 => -0.00-3.00i base( 2i): 7.75-7.5i => 11210.31 => 7.75-7.50i base( 2i): 0.25 => 1.03 => 0.25-0.00i base( 2i): 5+5i => 10331.2 => 5.00+5.00i base( 3i): 5+5i => 25.3 => 5.00+5.00i base( 4i): 5+5i => 25.c => 5.00+5.00i base( 5i): 5+5i => 15 => 5.00+5.00i base( 6i): 5+5i => 15.6 => 5.00+5.00i base(-2i): 5+5i => 11321.2 => 5.00+5.00i base(-3i): 5+5i => 1085.6 => 5.00+5.00i base(-4i): 5+5i => 10f5.4 => 5.00+5.00i base(-5i): 5+5i => 10o5 => 5.00+5.00i base(-6i): 5+5i => 5.u => 5.00+5.00i base( 6i): 31432.6219135802-2898.5266203704*i => perl5.4ever
Phix
with javascript_semantics include complex.e function base2(atom num, integer radix, precision = -8) if radix<-36 or radix>-2 then throw("radix out of range (-2..-36)") end if sequence result if num=0 then result = {"0",""} else integer place = 0 result = "" atom v = num atom upper_bound = 1/(1-radix), lower_bound = radix*upper_bound while not(lower_bound <= v) or not(v < upper_bound) do place += 1 v = num/power(radix,place) end while while (v or place > 0) and (place > precision) do integer digit = floor(radix*v - lower_bound) v = (radix*v - digit) if place=0 and not find('.',result) then result &= '.' end if result &= digit+iff(digit>9?'a'-10:'0') place -= 1 end while integer dot = find('.',result) if dot then result = trim_tail(result,'0') result = {result[1..dot-1],result[dot+1..$]} else result = {result,""} end if end if return result end function function zip(string a, string b) integer ld = length(a)-length(b) if ld!=0 then if ld>0 then b &= repeat('0',ld) else a &= repeat('0',abs(ld)) end if end if string res = "" for i=1 to length(a) do res &= a[i]&b[i] end for res = trim_tail(res,'0') if res="" then res = "0" end if return res end function function base(complexn num, integer radix, precision = -8) integer absrad = abs(radix), radix2 = -power(radix,2) if absrad<2 or absrad>6 then throw("base radix out of range") end if atom {re, im} = {complex_real(num), complex_imag(num)} string {re_wh, re_fr} = base2(re, radix2, precision), {im_wh, im_fr} = base2(im/radix, radix2, precision) string whole = reverse(zip(reverse(re_wh), reverse(im_wh))), fraction = zip(im_fr, re_fr) if fraction!="0" then whole &= '.'&fraction end if return whole end function function parse_base(string str, integer radix) complexn fraction = 0 integer dot = find('.',str) if dot then string fr = str[dot+1..$] for i=1 to length(fr) do integer c = fr[i] c -= iff(c>='a'?'a'-10:'0') fraction = complex_add(fraction,complex_mul(c,complex_power({0,radix},-i))) end for str = str[1..dot-1] end if str = reverse(str) for i=1 to length(str) do integer c = str[i] c -= iff(c>='a'?'a'-10:'0') fraction = complex_add(fraction,complex_mul(c,complex_power({0,radix},(i-1)))) end for return fraction end function constant tests = {{0,2},{1,2},{5,2},{-13,2},{{0,9},2},{{0,-3},2},{{7.75,-7.5}, 2},{.25, 2}, -- base 2i tests {{5,5}, 2},{{5,5}, 3},{{5,5}, 4},{{5,5}, 5},{{5,5}, 6}, -- same value, positive imaginary bases {{5,5},-2},{{5,5},-3},{{5,5},-4},{{5,5},-5},{{5,5},-6}, -- same value, negative imaginary bases {{227.65625,10.859375},4}, -- larger test value {{-579.8225308641975744,-5296.406378600824},6}} -- phix.rules -- matches output of Sidef and Raku: for t=1 to length(tests) do {complexn v, integer r} = tests[t] string ibase = base(v,r), strv = complex_sprint(v), strb = complex_sprint(parse_base(ibase, r)) printf(1,"base(%20s, %2di) = %-10s : parse_base(%12s, %2di) = %s\n", {strv, r, ibase, '"'&ibase&'"', r, strb}) end for -- matches output of Kotlin, Java, Go, D, and C#: for ri=1 to 2 do -- real then imag for i=1 to 16 do complexn c = iff(ri=1?i:{0,i}), nc = complex_neg(c) string sc = complex_sprint(c), snc = complex_sprint(nc), ib = base(c,2), inb = base(nc,2), rc = complex_sprint(parse_base(ib,2)), rnc = complex_sprint(parse_base(inb,2)) printf(1,"%4s -> %8s -> %4s %4s -> %8s -> %4s\n", {sc, ib, rc, snc, inb, rnc }) end for puts(1,"\n") end for
- Output:
Matches the output of Sidef and Raku, except for the final line:
base( -579.823-5296.41i, 6i) = phix.rules : parse_base("phix.rules", 6i) = -579.823-5296.41i
Also matches the output of Kotlin, Java, Go, D, and C#, except the even entries in the second half, eg:
2i -> 10 -> 2i -2i -> 1030 -> -2i
instead of
2i -> 10.0 -> 2i -2i -> 1030.0 -> -2i
ie the unnecessary trailing ".0" are trimmed. (see talk page)
Python
<lang python>import math import re
def inv(c):
denom = c.real * c.real + c.imag * c.imag return complex(c.real / denom, -c.imag / denom)
class QuaterImaginary:
twoI = complex(0, 2) invTwoI = inv(twoI)
def __init__(self, str): if not re.match("^[0123.]+$", str) or str.count('.') > 1: raise Exception('Invalid base 2i number') self.b2i = str
def toComplex(self): pointPos = self.b2i.find('.') posLen = len(self.b2i) if (pointPos < 0) else pointPos sum = complex(0, 0) prod = complex(1, 0) for j in xrange(0, posLen): k = int(self.b2i[posLen - 1 - j]) if k > 0: sum += prod * k prod *= QuaterImaginary.twoI if pointPos != -1: prod = QuaterImaginary.invTwoI for j in xrange(posLen + 1, len(self.b2i)): k = int(self.b2i[j]) if k > 0: sum += prod * k prod *= QuaterImaginary.invTwoI return sum
def __str__(self): return str(self.b2i)
def toQuaterImaginary(c):
if c.real == 0.0 and c.imag == 0.0: return QuaterImaginary("0")
re = int(c.real) im = int(c.imag) fi = -1 ss = "" while re != 0: re, rem = divmod(re, -4) if rem < 0: rem += 4 re += 1 ss += str(rem) + '0' if im != 0: f = c.imag / 2 im = int(math.ceil(f)) f = -4 * (f - im) index = 1 while im != 0: im, rem = divmod(im, -4) if rem < 0: rem += 4 im += 1 if index < len(ss): ss[index] = str(rem) else: ss += '0' + str(rem) index = index + 2 fi = int(f) ss = ss[::-1] if fi != -1: ss += '.' + str(fi) ss = ss.lstrip('0') if ss[0] == '.': ss = '0' + ss return QuaterImaginary(ss)
for i in xrange(1,17):
c1 = complex(i, 0) qi = toQuaterImaginary(c1) c2 = qi.toComplex() print "{0:8} -> {1:>8} -> {2:8} ".format(c1, qi, c2),
c1 = -c1 qi = toQuaterImaginary(c1) c2 = qi.toComplex() print "{0:8} -> {1:>8} -> {2:8}".format(c1, qi, c2)
for i in xrange(1,17):
c1 = complex(0, i) qi = toQuaterImaginary(c1) c2 = qi.toComplex() print "{0:8} -> {1:>8} -> {2:8} ".format(c1, qi, c2),
c1 = -c1 qi = toQuaterImaginary(c1) c2 = qi.toComplex() print "{0:8} -> {1:>8} -> {2:8}".format(c1, qi, c2)
print "done" </lang>
- Output:
(1+0j) -> 1 -> (1+0j) (-1-0j) -> 103 -> (-1+0j) (2+0j) -> 2 -> (2+0j) (-2-0j) -> 102 -> (-2+0j) (3+0j) -> 3 -> (3+0j) (-3-0j) -> 101 -> (-3+0j) (4+0j) -> 10300 -> (4+0j) (-4-0j) -> 100 -> (-4+0j) (5+0j) -> 10301 -> (5+0j) (-5-0j) -> 203 -> (-5+0j) (6+0j) -> 10302 -> (6+0j) (-6-0j) -> 202 -> (-6+0j) (7+0j) -> 10303 -> (7+0j) (-7-0j) -> 201 -> (-7+0j) (8+0j) -> 10200 -> (8+0j) (-8-0j) -> 200 -> (-8+0j) (9+0j) -> 10201 -> (9+0j) (-9-0j) -> 303 -> (-9+0j) (10+0j) -> 10202 -> (10+0j) (-10-0j) -> 302 -> (-10+0j) (11+0j) -> 10203 -> (11+0j) (-11-0j) -> 301 -> (-11+0j) (12+0j) -> 10100 -> (12+0j) (-12-0j) -> 300 -> (-12+0j) (13+0j) -> 10101 -> (13+0j) (-13-0j) -> 1030003 -> (-13+0j) (14+0j) -> 10102 -> (14+0j) (-14-0j) -> 1030002 -> (-14+0j) (15+0j) -> 10103 -> (15+0j) (-15-0j) -> 1030001 -> (-15+0j) (16+0j) -> 10000 -> (16+0j) (-16-0j) -> 1030000 -> (-16+0j) 1j -> 10.2 -> 1j (-0-1j) -> 0.2 -> -1j 2j -> 10.0 -> 2j (-0-2j) -> 1030.0 -> -2j 3j -> 20.2 -> 3j (-0-3j) -> 1030.2 -> -3j 4j -> 20.0 -> 4j (-0-4j) -> 1020.0 -> -4j 5j -> 30.2 -> 5j (-0-5j) -> 1020.2 -> -5j 6j -> 30.0 -> 6j (-0-6j) -> 1010.0 -> -6j 7j -> 103000.2 -> 7j (-0-7j) -> 1010.2 -> -7j 8j -> 103000.0 -> 8j (-0-8j) -> 1000.0 -> -8j 9j -> 103010.2 -> 9j (-0-9j) -> 1000.2 -> -9j 10j -> 103010.0 -> 10j (-0-10j) -> 2030.0 -> -10j 11j -> 103020.2 -> 11j (-0-11j) -> 2030.2 -> -11j 12j -> 103020.0 -> 12j (-0-12j) -> 2020.0 -> -12j 13j -> 103030.2 -> 13j (-0-13j) -> 2020.2 -> -13j 14j -> 103030.0 -> 14j (-0-14j) -> 2010.0 -> -14j 15j -> 102000.2 -> 15j (-0-15j) -> 2010.2 -> -15j 16j -> 102000.0 -> 16j (-0-16j) -> 2000.0 -> -16j done
Raku
(formerly Perl 6)
Explicit
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 Raku 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.
<lang perl6>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;
}</lang>
- 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
Module
Using the module Base::Any from the Raku ecosystem.
Does everything the explicit version does but also handles a much larger range of imaginary bases.
Doing pretty much the same tests as the explicit version.
<lang perl6>use Base::Any;
- 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 -3544.29+26541.468i, -10i -> $v, $r { my $ibase = $v.&to-base($r, :precision(-6)); printf "%33s.&to-base\(%3si\) = %-11s : %13s.&from-base\(%3si\) = %s\n", $v, $r.im, $ibase, "'$ibase'", $r.im, $ibase.&from-base($r).round(1e-10).narrow;
}</lang>
- Output:
0.&to-base( 2i) = 0 : '0'.&from-base( 2i) = 0 1.&to-base( 2i) = 1 : '1'.&from-base( 2i) = 1 5.&to-base( 2i) = 10301 : '10301'.&from-base( 2i) = 5 -13.&to-base( 2i) = 1030003 : '1030003'.&from-base( 2i) = -13 0+9i.&to-base( 2i) = 103010.2 : '103010.2'.&from-base( 2i) = 0+9i -0-3i.&to-base( 2i) = 1030.2 : '1030.2'.&from-base( 2i) = 0-3i 7.75-7.5i.&to-base( 2i) = 11210.31 : '11210.31'.&from-base( 2i) = 7.75-7.5i 0.25.&to-base( 2i) = 1.03 : '1.03'.&from-base( 2i) = 0.25 5+5i.&to-base( 2i) = 10331.2 : '10331.2'.&from-base( 2i) = 5+5i 5+5i.&to-base( 3i) = 25.3 : '25.3'.&from-base( 3i) = 5+5i 5+5i.&to-base( 4i) = 25.C : '25.C'.&from-base( 4i) = 5+5i 5+5i.&to-base( 5i) = 15 : '15'.&from-base( 5i) = 5+5i 5+5i.&to-base( 6i) = 15.6 : '15.6'.&from-base( 6i) = 5+5i 5+5i.&to-base( -2i) = 11321.2 : '11321.2'.&from-base( -2i) = 5+5i 5+5i.&to-base( -3i) = 1085.6 : '1085.6'.&from-base( -3i) = 5+5i 5+5i.&to-base( -4i) = 10F5.4 : '10F5.4'.&from-base( -4i) = 5+5i 5+5i.&to-base( -5i) = 10O5 : '10O5'.&from-base( -5i) = 5+5i 5+5i.&to-base( -6i) = 5.U : '5.U'.&from-base( -6i) = 5+5i 227.65625+10.859375i.&to-base( 4i) = 10234.5678 : '10234.5678'.&from-base( 4i) = 227.65625+10.859375i 31433.3487654321-2902.4480452675i.&to-base( 6i) = PERL6.ROCKS : 'PERL6.ROCKS'.&from-base( 6i) = 31433.3487654321-2902.4480452675i -3544.29+26541.468i.&to-base(-10i) = Raku.FTW : 'Raku.FTW'.&from-base(-10i) = -3544.29+26541.468i
Ruby
The Functions
<lang ruby># Convert a quarter-imaginary base value (as a string) into a base 10 Gaussian integer.
def base2i_decode(qi)
return 0 if qi == '0' md = qi.match(/^(?<int>[0-3]+)(?:\.(?<frc>[0-3]+))?$/) raise 'ill-formed quarter-imaginary base value' if !md ls_pow = md[:frc] ? -(md[:frc].length) : 0 value = 0 (md[:int] + (md[:frc] ? md[:frc] : )).reverse.each_char.with_index do |dig, inx| value += dig.to_i * (2i)**(inx + ls_pow) end return value
end
- Convert a base 10 Gaussian integer into a quarter-imaginary base value (as a string).
def base2i_encode(gi)
odd = gi.imag.to_i.odd? frac = (gi.imag.to_i != 0) real = gi.real.to_i imag = (gi.imag.to_i + 1) / 2 value = phase_real = true while (real != 0) || (imag != 0) if phase_real real, rem = real.divmod(4) real = -real else imag, rem = imag.divmod(4) imag = -imag end value.prepend(rem.to_s) phase_real = !phase_real end value = '0' if value == value.concat(odd ? '.2' : '.0') if frac return value
end</lang>
The Task
<lang ruby># Extend class Integer with a string conveter.
class Integer
def as_str() return to_s() end
end
- Extend class Complex with a string conveter (works only with Gaussian integers).
class Complex
def as_str() return '0' if self == 0 return real.to_i.to_s if imag == 0 return imag.to_i.to_s + 'i' if real == 0 return real.to_i.to_s + '+' + imag.to_i.to_s + 'i' if imag >= 0 return real.to_i.to_s + '-' + (-(imag.to_i)).to_s + 'i' end
end
- Emit various tables of conversions.
1.step(16) do |gi|
puts(" %4s -> %8s -> %4s %4s -> %8s -> %4s" % [gi.as_str, base2i_encode(gi), base2i_decode(base2i_encode(gi)).as_str, (-gi).as_str, base2i_encode(-gi), base2i_decode(base2i_encode(-gi)).as_str])
end puts 1.step(16) do |gi|
gi *= 0+1i puts(" %4s -> %8s -> %4s %4s -> %8s -> %4s" % [gi.as_str, base2i_encode(gi), base2i_decode(base2i_encode(gi)).as_str, (-gi).as_str, base2i_encode(-gi), base2i_decode(base2i_encode(-gi)).as_str])
end puts 0.step(3) do |m|
0.step(3) do |l| 0.step(3) do |h| qi = (100 * h + 10 * m + l).to_s gi = base2i_decode(qi) md = base2i_encode(gi).match(/^(?<num>[0-3]+)(?:\.0)?$/) print(" %4s -> %6s -> %4s" % [qi, gi.as_str, md[:num]]) end puts end
end</lang>
- Output:
Conversions given in the task.
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
- Output:
From the OEIS Wiki [1].
0 -> 0 -> 0 100 -> -4 -> 100 200 -> -8 -> 200 300 -> -12 -> 300 1 -> 1 -> 1 101 -> -3 -> 101 201 -> -7 -> 201 301 -> -11 -> 301 2 -> 2 -> 2 102 -> -2 -> 102 202 -> -6 -> 202 302 -> -10 -> 302 3 -> 3 -> 3 103 -> -1 -> 103 203 -> -5 -> 203 303 -> -9 -> 303 10 -> 2i -> 10 110 -> -4+2i -> 110 210 -> -8+2i -> 210 310 -> -12+2i -> 310 11 -> 1+2i -> 11 111 -> -3+2i -> 111 211 -> -7+2i -> 211 311 -> -11+2i -> 311 12 -> 2+2i -> 12 112 -> -2+2i -> 112 212 -> -6+2i -> 212 312 -> -10+2i -> 312 13 -> 3+2i -> 13 113 -> -1+2i -> 113 213 -> -5+2i -> 213 313 -> -9+2i -> 313 20 -> 4i -> 20 120 -> -4+4i -> 120 220 -> -8+4i -> 220 320 -> -12+4i -> 320 21 -> 1+4i -> 21 121 -> -3+4i -> 121 221 -> -7+4i -> 221 321 -> -11+4i -> 321 22 -> 2+4i -> 22 122 -> -2+4i -> 122 222 -> -6+4i -> 222 322 -> -10+4i -> 322 23 -> 3+4i -> 23 123 -> -1+4i -> 123 223 -> -5+4i -> 223 323 -> -9+4i -> 323 30 -> 6i -> 30 130 -> -4+6i -> 130 230 -> -8+6i -> 230 330 -> -12+6i -> 330 31 -> 1+6i -> 31 131 -> -3+6i -> 131 231 -> -7+6i -> 231 331 -> -11+6i -> 331 32 -> 2+6i -> 32 132 -> -2+6i -> 132 232 -> -6+6i -> 232 332 -> -10+6i -> 332 33 -> 3+6i -> 33 133 -> -1+6i -> 133 233 -> -5+6i -> 233 333 -> -9+6i -> 333
Sidef
<lang ruby>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))
})</lang>
- 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
Visual Basic .NET
<lang vbnet>Imports System.Text
Module Module1
Class Complex : Implements IFormattable Private ReadOnly real As Double Private ReadOnly imag As Double
Public Sub New(r As Double, i As Double) real = r imag = i End Sub
Public Sub New(r As Integer, i As Integer) real = r imag = i End Sub
Public Function Inv() As Complex Dim denom = real * real + imag * imag Return New Complex(real / denom, -imag / denom) End Function
Public Shared Operator -(self As Complex) As Complex Return New Complex(-self.real, -self.imag) End Operator
Public Shared Operator +(lhs As Complex, rhs As Complex) As Complex Return New Complex(lhs.real + rhs.real, lhs.imag + rhs.imag) End Operator
Public Shared Operator -(lhs As Complex, rhs As Complex) As Complex Return New Complex(lhs.real - rhs.real, lhs.imag - rhs.imag) End Operator
Public Shared Operator *(lhs As Complex, rhs As Complex) As Complex Return New Complex(lhs.real * rhs.real - lhs.imag * rhs.imag, lhs.real * rhs.imag + lhs.imag * rhs.real) End Operator
Public Shared Operator /(lhs As Complex, rhs As Complex) As Complex Return lhs * rhs.Inv End Operator
Public Shared Operator *(lhs As Complex, rhs As Double) As Complex Return New Complex(lhs.real * rhs, lhs.imag * rhs) End Operator
Public Function ToQuaterImaginary() As QuaterImaginary If real = 0.0 AndAlso imag = 0.0 Then Return New QuaterImaginary("0") End If Dim re = CType(real, Integer) Dim im = CType(imag, Integer) Dim fi = -1 Dim sb As New StringBuilder While re <> 0 Dim rm = re Mod -4 re \= -4 If rm < 0 Then rm += 4 re += 1 End If sb.Append(rm) sb.Append(0) End While If im <> 0 Then Dim f = (New Complex(0.0, imag) / New Complex(0.0, 2.0)).real im = Math.Ceiling(f) f = -4.0 * (f - im) Dim index = 1 While im <> 0 Dim rm = im Mod -4 im \= -4 If rm < 0 Then rm += 4 im += 1 End If If index < sb.Length Then sb(index) = Chr(rm + 48) Else sb.Append(0) sb.Append(rm) End If index += 2 End While fi = f End If Dim reverse As New String(sb.ToString().Reverse().ToArray()) sb.Length = 0 sb.Append(reverse) If fi <> -1 Then sb.AppendFormat(".{0}", fi) End If Dim s = sb.ToString().TrimStart("0") If s(0) = "." Then s = "0" + s End If Return New QuaterImaginary(s) End Function
Public Overloads Function ToString() As String Dim r2 = If(real = -0.0, 0.0, real) 'get rid of negative zero Dim i2 = If(imag = -0.0, 0.0, imag) 'ditto If i2 = 0.0 Then Return String.Format("{0}", r2) End If If r2 = 0.0 Then Return String.Format("{0}i", i2) End If If i2 > 0.0 Then Return String.Format("{0} + {1}i", r2, i2) End If Return String.Format("{0} - {1}i", r2, -i2) End Function
Public Overloads Function ToString(format As String, formatProvider As IFormatProvider) As String Implements IFormattable.ToString Return ToString() End Function End Class
Class QuaterImaginary Private Shared ReadOnly twoI = New Complex(0.0, 2.0) Private Shared ReadOnly invTwoI = twoI.Inv()
Private ReadOnly b2i As String
Public Sub New(b2i As String) If b2i = "" OrElse Not b2i.All(Function(c) "0123.".IndexOf(c) > -1) OrElse b2i.Count(Function(c) c = ".") > 1 Then Throw New Exception("Invalid Base 2i number") End If Me.b2i = b2i End Sub
Public Function ToComplex() As Complex Dim pointPos = b2i.IndexOf(".") Dim posLen = If(pointPos <> -1, pointPos, b2i.Length) Dim sum = New Complex(0.0, 0.0) Dim prod = New Complex(1.0, 0.0) For j = 0 To posLen - 1 Dim k = Asc(b2i(posLen - 1 - j)) - Asc("0") If k > 0.0 Then sum += prod * k End If prod *= twoI Next If pointPos <> -1 Then prod = invTwoI For j = posLen + 1 To b2i.Length - 1 Dim k = Asc(b2i(j)) - Asc("0") If k > 0.0 Then sum += prod * k End If prod *= invTwoI Next End If Return sum End Function
Public Overrides Function ToString() As String Return b2i End Function End Class
Sub Main() For i = 1 To 16 Dim c1 As New Complex(i, 0) Dim qi = c1.ToQuaterImaginary() Dim c2 = qi.ToComplex() Console.Write("{0,4} -> {1,8} -> {2,4} ", c1, qi, c2) c1 = -c1 qi = c1.ToQuaterImaginary() c2 = qi.ToComplex() Console.WriteLine("{0,4} -> {1,8} -> {2,4}", c1, qi, c2) Next Console.WriteLine() For i = 1 To 16 Dim c1 As New Complex(0, i) Dim qi = c1.ToQuaterImaginary() Dim c2 = qi.ToComplex() Console.Write("{0,4} -> {1,8} -> {2,4} ", c1, qi, c2) c1 = -c1 qi = c1.ToQuaterImaginary() c2 = qi.ToComplex() Console.WriteLine("{0,4} -> {1,8} -> {2,4}", c1, qi, c2) Next End Sub
End Module</lang>
- 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
Wren
<lang ecmascript>import "/complex" for Complex import "/fmt" for Fmt
class QuaterImaginary {
construct new(b2i) { if (b2i.type != String || b2i == "" || !b2i.all { |d| "0123.".contains(d) } || b2i.count { |d| d == "." } > 1) Fiber.abort("Invalid Base 2i number.") _b2i = b2i }
// only works properly if 'c.real' and 'c.imag' are both integral static fromComplex(c) { if (c.real == 0 && c.imag == 0) return QuaterImaginary.new("0") var re = c.real.truncate var im = c.imag.truncate var fi = -1 var sb = "" while (re != 0) { var rem = re % (-4) re = (re/(-4)).truncate if (rem < 0) { rem = 4 + rem re = re + 1 } if (rem == -0) rem = 0 // get rid of minus zero sb = sb + rem.toString + "0" } if (im != 0) { var f = (Complex.new(0, c.imag) / Complex.imagTwo).real im = f.ceil f = -4 * (f - im) var index = 1 while (im != 0) { var rem = im % (-4) im = (im/(-4)).truncate if (rem < 0) { rem = 4 + rem im = im + 1 } if (index < sb.count) { var sbl = sb.toList sbl[index] = String.fromByte(rem + 48) sb = sbl.join() } else { if (rem == -0) rem = 0 // get rid of minus zero sb = sb + "0" + rem.toString } index = index + 2 } fi = f.truncate } if (sb.count > 0) sb = sb[-1..0] if (fi != -1) { if (fi == -0) fi = 0 // get rid of minus zero sb = sb + ".%(fi)" } sb = sb.trimStart("0") if (sb.startsWith(".")) sb = "0" + sb return QuaterImaginary.new(sb) }
toComplex { var pointPos = _b2i.indexOf(".") var posLen = (pointPos != -1) ? pointPos : _b2i.count var sum = Complex.zero var prod = Complex.one for (j in 0...posLen) { var k = _b2i.bytes[posLen-1-j] - 48 if (k > 0) sum = sum + prod * k prod = prod * Complex.imagTwo } if (pointPos != -1) { prod = Complex.imagTwo.inverse var j = posLen + 1 while (j < _b2i.count) { var k = _b2i.bytes[j] - 48 if (k > 0) sum = sum + prod * k prod = prod / Complex.imagTwo j = j + 1 } } return sum }
toString { _b2i }
}
var imagOnly = Fn.new { |c| c.imag.toString + "i" }
var fmt = "$4s -> $8s -> $4s" Complex.showAsReal = true for (i in 1..16) {
var c1 = Complex.new(i, 0) var qi = QuaterImaginary.fromComplex(c1) var c2 = qi.toComplex Fmt.write("%(fmt) ", c1, qi, c2) c1 = -c1 qi = QuaterImaginary.fromComplex(c1) c2 = qi.toComplex Fmt.print(fmt, c1, qi, c2)
} System.print() for (i in 1..16) {
var c1 = Complex.new(0, i) var qi = QuaterImaginary.fromComplex(c1) var c2 = qi.toComplex Fmt.write("%(fmt) ", imagOnly.call(c1), qi, imagOnly.call(c2)) c1 = -c1 qi = QuaterImaginary.fromComplex(c1) c2 = qi.toComplex Fmt.print(fmt, imagOnly.call(c1), qi, imagOnly.call(c2))
}</lang>
- 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