This commit is contained in:
Ingy döt Net 2013-04-10 16:57:12 -07:00
parent 518da4a923
commit 764da6cbbb
6144 changed files with 83610 additions and 11 deletions

View file

@ -0,0 +1,93 @@
;;; balanced ternary
;;; represented as a list of 0, 1 or -1s, with least significant digit first
;;; convert ternary to integer
(defun bt-integer (b)
(reduce (lambda (x y) (+ x (* 3 y))) b :from-end t :initial-value 0))
;;; convert integer to ternary
(defun integer-bt (n)
(if (zerop n) nil
(case (mod n 3)
(0 (cons 0 (integer-bt (/ n 3))))
(1 (cons 1 (integer-bt (floor n 3))))
(2 (cons -1 (integer-bt (floor (1+ n) 3)))))))
;;; convert string to ternary
(defun string-bt (s)
(loop with o = nil for c across s do
(setf o (cons (case c (#\+ 1) (#\- -1) (#\0 0)) o))
finally (return o)))
;;; convert ternary to string
(defun bt-string (bt)
(if (not bt) "0"
(let* ((l (length bt))
(s (make-array l :element-type 'character)))
(mapc (lambda (b)
(setf (aref s (decf l))
(case b (-1 #\-) (0 #\0) (1 #\+))))
bt)
s)))
;;; arithmetics
(defun bt-neg (a) (map 'list #'- a))
(defun bt-sub (a b) (bt-add a (bt-neg b)))
(let ((tbl #((0 -1) (1 -1) (-1 0) (0 0) (1 0) (-1 1) (0 1))))
(defun bt-add-digits (a b c)
(values-list (aref tbl (+ 3 a b c)))))
(defun bt-add (a b &optional (c 0))
(if (not (and a b))
(if (zerop c) (or a b)
(bt-add (list c) (or a b)))
(multiple-value-bind (d c)
(bt-add-digits (if a (car a) 0) (if b (car b) 0) c)
(let ((res (bt-add (cdr a) (cdr b) c)))
;; trim leading zeros
(if (or res (not (zerop d)))
(cons d res))))))
(defun bt-mul (a b)
(if (not (and a b))
nil
(bt-add (case (car a)
(-1 (bt-neg b))
( 0 nil)
( 1 b))
(cons 0 (bt-mul (cdr a) b)))))
;;; division with quotient/remainder, for completeness
(defun bt-truncate (a b)
(let ((n (- (length a) (length b)))
(d (car (last b))))
(if (minusp n)
(values nil a)
(labels ((recur (a b x)
(multiple-value-bind (quo rem)
(if (plusp x) (recur a (cons 0 b) (1- x))
(values nil a))
(loop with g = (car (last rem))
with quo = (cons 0 quo)
while (= (length rem) (length b)) do
(cond ((= g d) (setf rem (bt-sub rem b)
quo (bt-add '(1) quo)))
((= g (- d)) (setf rem (bt-add rem b)
quo (bt-add '(-1) quo))))
(setf x (car (last rem)))
finally (return (values quo rem))))))
(recur a b n)))))
;;; test case
(let* ((a (string-bt "+-0++0+"))
(b (integer-bt -436))
(c (string-bt "+-++-"))
(d (bt-mul a (bt-sub b c))))
(format t "a~5d~8t~a~%b~5d~8t~a~%c~5d~8t~a~%a × (b c) = ~d ~a~%"
(bt-integer a) (bt-string a)
(bt-integer b) (bt-string b)
(bt-integer c) (bt-string c)
(bt-integer d) (bt-string d)))

View file

@ -0,0 +1,4 @@
a 523 +-0++0+
b -436 -++-0--
c 65 +-++-
a × (b c) = -262023 ----0+--0++0

View file

@ -0,0 +1,142 @@
import std.stdio, std.bigint, std.range, std.algorithm, std.array,
std.conv, std.exception;
struct BalancedTernary {
enum Dig : byte { N=-1, Z=0, P=+1 } // digits
Dig[] digits;
// Represented as a list of 0, 1 or -1s,
// with least significant digit first.
static string dig2str = "-0+";
const static Dig[dchar] str2dig; // = ['+': Dig.P, ...];
static this() {
str2dig = ['+': Dig.P, '-': Dig.N, '0': Dig.Z];
}
immutable static Dig[2][] table =
[[Dig.Z, Dig.N], [Dig.P, Dig.N], [Dig.N, Dig.Z],
[Dig.Z, Dig.Z], [Dig.P, Dig.Z], [Dig.N, Dig.P],
[Dig.Z, Dig.P]];
this(string inp) {
this.digits = map!(c => cast()str2dig[c])(retro(inp)).array();
}
this(long inp) {
this.digits = _bint2ternary(BigInt(inp));
}
this(BigInt inp) {
this.digits = _bint2ternary(inp);
}
this(BalancedTernary inp) {
// no need to dup, they are virtually immutable
this.digits = inp.digits;
}
private this(Dig[] inp) {
this.digits = inp;
}
static Dig[] _bint2ternary(/*in*/ BigInt n) {
static py_div(T1, T2)(T1 a, T2 b) {
if (a < 0)
if (b < 0)
return -a / -b;
else
return -(-a / b) - (-a % b != 0 ? 1 : 0);
else if (b < 0)
return -(a / -b) - (a % -b != 0 ? 1 : 0);
else
return a / b;
}
if (n == 0) return [];
switch (((n % 3) + 3) % 3) { // (n % 3) is the remainder
case 0: return Dig.Z ~ _bint2ternary(py_div(n, 3));
case 1: return Dig.P ~ _bint2ternary(py_div(n, 3));
case 2: return Dig.N ~ _bint2ternary(py_div(n + 1, 3));
default: assert(0, "Can't happen");
}
}
@property BigInt toBint() const {
return reduce!((y,x) => x + 3 * y)(BigInt(0), retro(digits));
}
string toString() const {
if (digits.empty) return "0";
//return map!(d => dig2str[d + 1])(retro(digits)).array();
auto r = map!(d => cast()dig2str[d+1])(retro(digits)).array();
return assumeUnique(r); ///
}
static Dig[] neg_(Dig[] digs) {
return map!(d => -d)(digs).array();
}
BalancedTernary opUnary(string op:"-")() {
return BalancedTernary(neg_(this.digits));
}
static Dig[] add_(Dig[] a, Dig[] b, Dig c=Dig.Z) {
auto a_or_b = a.length ? a : b;
if (a.empty || b.empty) {
if (c == Dig.Z)
return a_or_b;
else
return BalancedTernary.add_([c], a_or_b);
} else {
// (const d, c) = table[...];
const dc = table[3 + (a.length ? a[0] : 0) +
(b.length ? b[0] : 0) + c];
auto res = add_(a[1 .. $], b[1 .. $], dc[1]);
// trim leading zeros
if (res.length || dc[0] != Dig.Z)
return [dc[0]] ~ res;
else
return res;
}
}
BalancedTernary opBinary(string op:"+")(BalancedTernary b) {
return BalancedTernary(add_(this.digits, b.digits));
}
BalancedTernary opBinary(string op:"-")(BalancedTernary b) {
return this + (-b);
}
static Dig[] mul_(in Dig[] a, /*in*/ Dig[] b) {
if (a.empty || b.empty) {
return [];
} else {
Dig[] y = Dig.Z ~ mul_(a[1 .. $], b);
final switch (a[0]) {
case Dig.N: return add_(neg_(b), y);
case Dig.Z: return add_([], y);
case Dig.P: return add_(b, y);
}
}
}
BalancedTernary opBinary(string op:"*")(BalancedTernary b) {
return BalancedTernary(mul_(this.digits, b.digits));
}
}
void main() {
auto a = BalancedTernary("+-0++0+");
writeln("a: ", a.toBint, " ", a);
auto b = BalancedTernary(-436);
writeln("b: ", b.toBint, " ", b);
auto c = BalancedTernary("+-++-");
writeln("c: ", c.toBint, " ", c);
auto r = a * (b - c);
writeln("a * (b - c): ", r.toBint, " ", r);
}