(*
** This one is
** translated into ATS from the Ocaml entry
*)

(* ****** ****** *)
//
// How to compile:
// patscc -DATS_MEMALLOC_LIBC -o bternary bternary.dats
//
(* ****** ****** *)

#include
"share/atspre_staload.hats"

(* ****** ****** *)

datatype btd = P | Z | N; typedef btern = List0(btd)

(* ****** ****** *)

fun
btd2int (d: btd): int =
  (case+ d of P() => 1 | Z() => 0 | N() => ~1)

(* ****** ****** *)

fun
btd2string (d:btd): string =
(
case+ d of P() => "+" | Z() => "0" | N() => "-"
)

(* ****** ****** *)

fun
btern2string
(
  ds: btern
) : string =
  strptr2string(res) where
{
  val xs = list_map_cloref (ds, lam d => btd2string(d))
  val xs = list_vt_reverse (xs)
  val res = stringlst_concat($UNSAFE.castvwtp1{List(string)}(xs))
  val () = list_vt_free<string> (xs)
}

(* ****** ****** *)

fun
from_string
  (inp: string): btern = let
//
fun
loop{n:nat}
(
  inp: string(n), ds: btern
) : btern =
(
//
  if isneqz(inp)
    then let
      val c = inp.head()
      val d =
        (case- c of '+' => P | '0' => Z | '-' => N): btd
      // end of [val]
    in
      loop (inp.tail(), list_cons(d, ds))
    end // end of [then]
    else ds // end of [else]
//
) (* end of [loop] *)
//
in
  loop (g1ofg0(inp), list_nil)
end // end of [from_string]

(* ****** ****** *)

fun
to_int (ds: btern): int =
(
case+ ds of
| list_nil () => 0
| list_cons (d, ds) => 3*to_int(ds) + btd2int(d)
) (* end of [to_int] *)

fun
from_int (n: int): btern =
(
if
n = 0
then list_nil
else let
  val r = n mod 3
in
  if r = 0
    then list_cons (Z, from_int (n/3))
    else if (r = 1 || r = ~2)
           then list_cons (P, from_int ((n-1)/3))
           else list_cons (N, from_int ((n+1)/3))
end // end of [else]
) (* end of [from_int] *)

(* ****** ****** *)

fun
neg_btern
  (ds: btern): btern =
list_vt2t
(
list_map_cloref<btd><btd>
  (ds, lam d => case+ d of P() => N() | Z() => Z() | N() => P())
) (* end of [neg_btern] *)

overload ~ with neg_btern

(* ****** ****** *)
//
extern
fun
add_btern_btern: (btern, btern) -> btern
and
sub_btern_btern: (btern, btern) -> btern
overload + with add_btern_btern of 100
overload - with sub_btern_btern of 100
//
extern
fun
mul_btern_btern: (btern, btern) -> btern
overload * with mul_btern_btern of 110
//
(* ****** ****** *)

#define :: list_cons

(* ****** ****** *)

local

fun aux0 (ds: btern): btern =
(
  case+ ds of nil() => ds | _ => Z()::ds
)

fun succ(ds:btern) = ds+list_sing(P())
fun pred(ds:btern) = ds+list_sing(N())

in (* in-of-local *)

implement
add_btern_btern
  (ds1, ds2) =
(
case+ (ds1, ds2) of
| (nil(), _) => ds2
| (_, nil()) => ds1
| (P()::ds1, N()::ds2) => aux0 (ds1+ds2)
| (Z()::ds1, Z()::ds2) => aux0 (ds1+ds2)
| (N()::ds1, P()::ds2) => aux0 (ds1+ds2)
| (P()::ds1, P()::ds2) => N() :: succ(ds1 + ds2)
| (N()::ds1, N()::ds2) => P() :: pred(ds1 + ds2)
| (Z()::ds1, btd::ds2) => btd :: (ds1 + ds2)
| (btd::ds1, Z()::ds2) => btd :: (ds1 + ds2)
)

implement
sub_btern_btern (ds1, ds2) = ds1 + (~ds2)

implement
mul_btern_btern (ds1, ds2) =
(
case+ ds2 of
| nil() => nil()
| Z()::ds2 => aux0 (ds1 * ds2)
| P()::ds2 => aux0 (ds1 * ds2) + ds1
| N()::ds2 => aux0 (ds1 * ds2) - ds1
)

end // end of [local]

(* ****** ****** *)

typedef charptr = $extype"char*"

(* ****** ****** *)

implement main0 () =
{
//
val a =
from_string "+-0++0+"
//
val b = from_int (~436)
val c = from_string "+-++-"
//
val d = a * (b - c)
//
val () =
$extfcall
(
  void
, "printf"
, "a = %d\nb = %d\nc = %d\na * (b - c) = %s = %d\n"
, to_int(a)
, to_int(b)
, to_int(c)
, $UNSAFE.cast{charptr}(btern2string(d))
, to_int(d)
) (* end of [val] *)
//
} (* end of [main0] *)


BalancedTernary(n){
	k = 0
	if abs(n)<2
		return n=1?"+":n=0?"0":"-"
	if n<1
		negative := true, n:= -1*n
	while !break {
		d := Mod(n, 3**(k+1)) / 3**k
		d := d=2?-1:d
		n := n - (d * 3**k)
		r := (d=-1?"-":d=1?"+":0) . r
		k++
		if (n = 3**k)
			r := "+" . r	, break := true
	}
	if negative {
		StringReplace, r, r, -,n, all
		StringReplace, r, r, `+,-, all
		StringReplace, r, r, n,+, all
	}
	return r
}


using System;
using System.Text;
using System.Collections.Generic;

public class BalancedTernary
{
	public static void Main()
	{
		BalancedTernary a = new BalancedTernary("+-0++0+");
		System.Console.WriteLine("a: " + a + " = " + a.ToLong());
		BalancedTernary b = new BalancedTernary(-436);
		System.Console.WriteLine("b: " + b + " = " + b.ToLong());
		BalancedTernary c = new BalancedTernary("+-++-");
		System.Console.WriteLine("c: " + c + " = " + c.ToLong());
		BalancedTernary d = a * (b - c);
		System.Console.WriteLine("a * (b - c): " + d + " = " + d.ToLong());
	}

	private enum BalancedTernaryDigit
	{
		MINUS = -1,
		ZERO = 0,
		PLUS = 1
	}

	private BalancedTernaryDigit[] value;

	// empty = 0
	public BalancedTernary()
	{
		this.value = new BalancedTernaryDigit[0];
	}

	// create from String
	public BalancedTernary(String str)
	{
		this.value = new BalancedTernaryDigit[str.Length];
		for (int i = 0; i < str.Length; ++i)
		{
			switch (str[i])
			{
				case '-':
					this.value[i] = BalancedTernaryDigit.MINUS;
					break;
				case '0':
					this.value[i] = BalancedTernaryDigit.ZERO;
					break;
				case '+':
					this.value[i] = BalancedTernaryDigit.PLUS;
					break;
				default:
					throw new ArgumentException("Unknown Digit: " + str[i]);
			}
		}
		Array.Reverse(this.value);
	}

	// convert long integer
	public BalancedTernary(long l)
	{
		List<BalancedTernaryDigit> value = new List<BalancedTernaryDigit>();
		int sign = Math.Sign(l);
		l = Math.Abs(l);

		while (l != 0)
		{
			byte rem = (byte)(l % 3);
			switch (rem)
			{
				case 0:
				case 1:
					value.Add((BalancedTernaryDigit)rem);
					l /= 3;
					break;
				case 2:
					value.Add(BalancedTernaryDigit.MINUS);
					l = (l + 1) / 3;
					break;
			}
		}

		this.value = value.ToArray();
		if (sign < 0)
		{
			this.Invert();
		}
	}

	// copy constructor
	public BalancedTernary(BalancedTernary origin)
	{
		this.value = new BalancedTernaryDigit[origin.value.Length];
		Array.Copy(origin.value, this.value, origin.value.Length);
	}

	// only for internal use
	private BalancedTernary(BalancedTernaryDigit[] value)
	{
		int end = value.Length - 1;
		while (value[end] == BalancedTernaryDigit.ZERO)
			--end;
		this.value = new BalancedTernaryDigit[end + 1];
		Array.Copy(value, this.value, end + 1);
	}

	// invert the values
	private void Invert()
	{
		for (int i=0; i < this.value.Length; ++i)
		{
			this.value[i] = (BalancedTernaryDigit)(-(int)this.value[i]);
		}
	}

	// convert to string
	override public String ToString()
	{
		StringBuilder result = new StringBuilder();
		for (int i = this.value.Length - 1; i >= 0; --i)
		{
			switch (this.value[i])
			{
				case BalancedTernaryDigit.MINUS:
					result.Append('-');
					break;
				case BalancedTernaryDigit.ZERO:
					result.Append('0');
					break;
				case BalancedTernaryDigit.PLUS:
					result.Append('+');
					break;
			}
		}
		return result.ToString();
	}

	// convert to long
	public long ToLong()
	{
		long result = 0;
		int digit;
		for (int i = 0; i < this.value.Length; ++i)
		{
			result += (long)this.value[i] * (long)Math.Pow(3.0, (double)i);
		}
		return result;
	}

	// unary minus
	public static BalancedTernary operator -(BalancedTernary origin)
	{
		BalancedTernary result = new BalancedTernary(origin);
		result.Invert();
		return result;
	}

	// addition of digits
	private static BalancedTernaryDigit carry = BalancedTernaryDigit.ZERO;
	private static BalancedTernaryDigit Add(BalancedTernaryDigit a, BalancedTernaryDigit b)
	{
		if (a != b)
		{
			carry = BalancedTernaryDigit.ZERO;
			return (BalancedTernaryDigit)((int)a + (int)b);
		}
		else
		{
			carry = a;
			return (BalancedTernaryDigit)(-(int)b);
		}
	}

	// addition of balanced ternary numbers
	public static BalancedTernary operator +(BalancedTernary a, BalancedTernary b)
	{
		int maxLength = Math.Max(a.value.Length, b.value.Length);
		BalancedTernaryDigit[] resultValue = new BalancedTernaryDigit[maxLength + 1];
		for (int i=0; i < maxLength; ++i)
		{
			if (i < a.value.Length)
			{
				resultValue[i] = Add(resultValue[i], a.value[i]);
				resultValue[i+1] = carry;
			}
			else
			{
				carry = BalancedTernaryDigit.ZERO;
			}

			if (i < b.value.Length)
			{
				resultValue[i] = Add(resultValue[i], b.value[i]);
				resultValue[i+1] = Add(resultValue[i+1], carry);
			}
		}
		return new BalancedTernary(resultValue);
	}

	// subtraction of balanced ternary numbers
	public static BalancedTernary operator -(BalancedTernary a, BalancedTernary b)
	{
		return a + (-b);
	}

	// multiplication of balanced ternary numbers
	public static BalancedTernary operator *(BalancedTernary a, BalancedTernary b)
	{
		BalancedTernaryDigit[] longValue = a.value;
		BalancedTernaryDigit[] shortValue = b.value;
		BalancedTernary result = new BalancedTernary();
		if (a.value.Length < b.value.Length)
		{
			longValue = b.value;
			shortValue = a.value;
		}

		for (int i = 0; i < shortValue.Length; ++i)
		{
			if (shortValue[i] != BalancedTernaryDigit.ZERO)
			{
				BalancedTernaryDigit[] temp = new BalancedTernaryDigit[i + longValue.Length];
				for (int j = 0; j < longValue.Length; ++j)
				{
					temp[i+j] = (BalancedTernaryDigit)((int)shortValue[i] * (int)longValue[j]);
				}
				result = result + new BalancedTernary(temp);
			}
		}
		return result;
	}
}


#include <iostream>
#include <string>
#include <climits>
using namespace std;

class BalancedTernary {
protected:
	// Store the value as a reversed string of +, 0 and - characters
	string value;

	// Helper function to change a balanced ternary character to an integer
	int charToInt(char c) const {
		if (c == '0')
			return 0;
		return 44 - c;
	}

	// Helper function to negate a string of ternary characters
	string negate(string s) const {
		for (int i = 0; i < s.length(); ++i) {
			if (s[i] == '+')
				s[i] = '-';
			else if (s[i] == '-')
				s[i] = '+';
		}
		return s;
	}

public:
	// Default constructor
	BalancedTernary() {
		value = "0";
	}

	// Construct from a string
	BalancedTernary(string s) {
		value = string(s.rbegin(), s.rend());
	}

	// Construct from an integer
	BalancedTernary(long long n) {
		if (n == 0) {
			value = "0";
			return;
		}

		bool neg = n < 0;
		if (neg)
			n = -n;

		value = "";
		while (n != 0) {
			int r = n % 3;
			if (r == 0)
				value += "0";
			else if (r == 1)
				value += "+";
			else {
				value += "-";
				++n;
			}

			n /= 3;
		}

		if (neg)
			value = negate(value);
	}

	// Copy constructor
	BalancedTernary(const BalancedTernary &n) {
		value = n.value;
	}

	// Addition operators
	BalancedTernary operator+(BalancedTernary n) const {
		n += *this;
		return n;
	}

	BalancedTernary& operator+=(const BalancedTernary &n) {
		static char *add = "0+-0+-0";
		static char *carry = "--000++";

		int lastNonZero = 0;
		char c = '0';
		for (int i = 0; i < value.length() || i < n.value.length(); ++i) {
			char a = i < value.length() ? value[i] : '0';
			char b = i < n.value.length() ? n.value[i] : '0';

			int sum = charToInt(a) + charToInt(b) + charToInt(c) + 3;
			c = carry[sum];

			if (i < value.length())
				value[i] = add[sum];
			else
				value += add[sum];

			if (add[sum] != '0')
				lastNonZero = i;
		}

		if (c != '0')
			value += c;
		else
			value = value.substr(0, lastNonZero + 1); // Chop off leading zeroes

		return *this;
	}

	// Negation operator
	BalancedTernary operator-() const {
		BalancedTernary result;
		result.value = negate(value);
		return result;
	}

	// Subtraction operators
	BalancedTernary operator-(const BalancedTernary &n) const {
		return operator+(-n);
	}

	BalancedTernary& operator-=(const BalancedTernary &n) {
		return operator+=(-n);
	}

	// Multiplication operators
	BalancedTernary operator*(BalancedTernary n) const {
		n *= *this;
		return n;
	}

	BalancedTernary& operator*=(const BalancedTernary &n) {
		BalancedTernary pos = *this;
		BalancedTernary neg = -pos; // Storing an extra copy to avoid negating repeatedly
		value = "0";

		for (int i = 0; i < n.value.length(); ++i) {
			if (n.value[i] == '+')
				operator+=(pos);
			else if (n.value[i] == '-')
				operator+=(neg);
			pos.value = '0' + pos.value;
			neg.value = '0' + neg.value;
		}

		return *this;
	}

	// Stream output operator
	friend ostream& operator<<(ostream &out, const BalancedTernary &n) {
		out << n.toString();
		return out;
	}

	// Convert to string
	string toString() const {
		return string(value.rbegin(), value.rend());
	}

	// Convert to integer
	long long toInt() const {
		long long result = 0;
		for (long long i = 0, pow = 1; i < value.length(); ++i, pow *= 3)
			result += pow * charToInt(value[i]);
		return result;
	}

	// Convert to integer if possible
	bool tryInt(long long &out) const {
		long long result = 0;
		bool ok = true;

		for (long long i = 0, pow = 1; i < value.length() && ok; ++i, pow *= 3) {
			if (value[i] == '+') {
				ok &= LLONG_MAX - pow >= result; // Clear ok if the result overflows
				result += pow;
			} else if (value[i] == '-') {
				ok &= LLONG_MIN + pow <= result; // Clear ok if the result overflows
				result -= pow;
			}
		}

		if (ok)
			out = result;
		return ok;
	}
};

int main() {
	BalancedTernary a("+-0++0+");
	BalancedTernary b(-436);
	BalancedTernary c("+-++-");

	cout << "a = " << a << " = " << a.toInt() << endl;
	cout << "b = " << b << " = " << b.toInt() << endl;
	cout << "c = " << c << " = " << c.toInt() << endl;

	BalancedTernary d = a * (b - c);

	cout << "a * (b - c) = " << d << " = " << d.toInt() << endl;

	BalancedTernary e("+++++++++++++++++++++++++++++++++++++++++");

	long long n;
	if (e.tryInt(n))
		cout << "e = " << e << " = " << n << endl;
	else
		cout << "e = " << e << " is too big to fit in a long long" << endl;

	return 0;
}


;;; 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)))


import std.stdio, std.bigint, std.range, std.algorithm;

struct BalancedTernary {
    // Represented as a list of 0, 1 or -1s,
    // with least significant digit first.
    enum Dig : byte { N=-1, Z=0, P=+1 } // Digit.
    const Dig[] digits;

    // This could also be a BalancedTernary template argument.
    static immutable string dig2str = "-0+";

    immutable static Dig[dchar] str2dig; // = ['+': Dig.P, ...];
    nothrow static this() {
        str2dig = ['+': Dig.P, '-':  Dig.N, '0': Dig.Z];
    }

    immutable pure nothrow 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(in string inp) const pure {
        this.digits = inp.retro.map!(c => str2dig[c]).array;
    }

    this(in long inp) const pure nothrow {
        this.digits = _bint2ternary(inp.BigInt);
    }

    this(in BigInt inp) const pure nothrow {
        this.digits = _bint2ternary(inp);
    }

    this(in BalancedTernary inp) const pure nothrow {
        // No need to dup, they are virtually immutable.
        this.digits = inp.digits;
    }

    private this(in Dig[] inp) pure nothrow {
        this.digits = inp;
    }

    static Dig[] _bint2ternary(in BigInt n) pure nothrow {
        static py_div(T1, T2)(in T1 a, in T2 b) pure nothrow {
            if (a < 0) {
                return (b < 0) ?
                       -a / -b :
                       -(-a / b) - (-a % b != 0 ? 1 : 0);
            } else {
                return (b < 0) ?
                       -(a / -b) - (a % -b != 0 ? 1 : 0) :
                       a / b;
            }
        }

        if (n == 0) return [];
        // This final switch in D v.2.064 is fake, not enforced.
        final 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));
        }
    }

    @property BigInt toBint() const pure nothrow {
        return reduce!((y, x) => x + 3 * y)(0.BigInt, digits.retro);
    }

    string toString() const pure nothrow {
        if (digits.empty) return "0";
        return digits.retro.map!(d => dig2str[d + 1]).array;
    }

    static const(Dig)[] neg_(in Dig[] digs) pure nothrow {
        return digs.map!(a => -a).array;
    }

    BalancedTernary opUnary(string op:"-")() const pure nothrow {
        return BalancedTernary(neg_(this.digits));
    }

    static const(Dig)[] add_(in Dig[] a, in Dig[] b, in Dig c=Dig.Z)
    pure nothrow {
        const 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];
            const 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:"+")(in BalancedTernary b)
    const pure nothrow {
        return BalancedTernary(add_(this.digits, b.digits));
    }

    BalancedTernary opBinary(string op:"-")(in BalancedTernary b)
    const pure nothrow {
        return this + (-b);
    }

    static const(Dig)[] mul_(in Dig[] a, in Dig[] b) pure nothrow {
        if (a.empty || b.empty) {
            return [];
        } else {
            const 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:"*")(in BalancedTernary b)
    const pure nothrow {
        return BalancedTernary(mul_(this.digits, b.digits));
    }
}

void main() {
    immutable a = BalancedTernary("+-0++0+");
    writeln("a: ", a.toBint, ' ', a);

    immutable b = BalancedTernary(-436);
    writeln("b: ", b.toBint, ' ', b);

    immutable c = BalancedTernary("+-++-");
    writeln("c: ", c.toBint, ' ', c);

    const /*immutable*/ r = a * (b - c);
    writeln("a * (b - c): ", r.toBint, ' ', r);
}


defmodule Ternary do
  def to_string(t), do: ( for x <- t, do: to_char(x) ) |> List.to_string

  def from_string(s), do: ( for x <- to_char_list(s), do: from_char(x) )

  defp to_char(-1), do: ?-
  defp to_char(0), do: ?0
  defp to_char(1), do: ?+

  defp from_char(?-), do: -1
  defp from_char(?0), do: 0
  defp from_char(?+), do: 1

  def to_ternary(n) when n > 0, do: to_ternary(n,[])
  def to_ternary(n), do: neg(to_ternary(-n))

  defp to_ternary(0,acc), do: acc
  defp to_ternary(n,acc) when rem(n, 3) == 0, do: to_ternary(div(n, 3), [0|acc])
  defp to_ternary(n,acc) when rem(n, 3) == 1, do: to_ternary(div(n, 3), [1|acc])
  defp to_ternary(n,acc), do: to_ternary(div((n+1), 3), [-1|acc])

  def from_ternary(t), do: from_ternary(t,0)

  defp from_ternary([],acc), do: acc
  defp from_ternary([h|t],acc), do: from_ternary(t, acc*3 + h)

  def mul(a,b), do: mul(b,a,[])

  defp mul(_,[],acc), do: acc
  defp mul(b,[a|as],acc) do
    bp = case a do
           -1 -> neg(b)
            0 -> [0]
            1 -> b
         end
    a = add(bp, acc ++ [0])
    mul(b,as,a)
  end

  defp neg(t), do: ( for h <- t, do: -h )

  def sub(a,b), do: add(a,neg(b))

  def add(a,b) when length(a) < length(b),
    do: add(List.duplicate(0, length(b)-length(a)) ++ a, b)
  def add(a,b) when length(a) > length(b), do: add(b,a)
  def add(a,b), do: add(Enum.reverse(a), Enum.reverse(b), 0, [])

  defp add([],[],0,acc), do: acc
  defp add([],[],c,acc), do: [c|acc]
  defp add([a|as],[b|bs],c,acc) do
    [c1,d] = add_util(a+b+c)
    add(as,bs,c1,[d|acc])
  end

  defp add_util(-3), do: [-1,0]
  defp add_util(-2), do: [-1,1]
  defp add_util(-1), do: [0,-1]
  defp add_util(3), do: [1,0]
  defp add_util(2), do: [1,-1]
  defp add_util(1), do: [0,1]
  defp add_util(0), do: [0,0]
end

as = "+-0++0+"; at = Ternary.from_string(as); a = Ternary.from_ternary(at)
b = -436; bt = Ternary.to_ternary(b); bs = Ternary.to_string(bt)
cs = "+-++-"; ct = Ternary.from_string(cs); c = Ternary.from_ternary(ct)
rt = Ternary.mul(at,Ternary.sub(bt,ct))
r = Ternary.from_ternary(rt)
rs = Ternary.to_string(rt)
IO.puts "a = #{as} -> #{a}"
IO.puts "b = #{bs} -> #{b}"
IO.puts "c = #{cs} -> #{c}"
IO.puts "a x (b - c) = #{rs} -> #{r}"


-module(ternary).
-compile(export_all).

test() ->
    AS = "+-0++0+", AT = from_string(AS), A = from_ternary(AT),
    B = -436, BT = to_ternary(B), BS = to_string(BT),
    CS = "+-++-", CT = from_string(CS), C = from_ternary(CT),
    RT = mul(AT,sub(BT,CT)),
    R = from_ternary(RT),
    RS = to_string(RT),
    io:fwrite("A = ~s -> ~b~n",[AS, A]),
    io:fwrite("B = ~s -> ~b~n",[BS, B]),
    io:fwrite("C = ~s -> ~b~n",[CS, C]),
    io:fwrite("A x (B - C) = ~s -> ~b~n", [RS, R]).

to_string(T) -> [to_char(X) || X <- T].

from_string(S) -> [from_char(X) || X <- S].

to_char(-1) -> $-;
to_char(0) -> $0;
to_char(1) -> $+.

from_char($-) -> -1;
from_char($0) -> 0;
from_char($+) -> 1.

to_ternary(N) when N > 0 ->
    to_ternary(N,[]);
to_ternary(N) ->
    neg(to_ternary(-N)).

to_ternary(0,Acc) ->
    Acc;
to_ternary(N,Acc) when N rem 3 == 0 ->
    to_ternary(N div 3, [0|Acc]);
to_ternary(N,Acc) when N rem 3 == 1 ->
    to_ternary(N div 3, [1|Acc]);
to_ternary(N,Acc) ->
    to_ternary((N+1) div 3, [-1|Acc]).

from_ternary(T) -> from_ternary(T,0).

from_ternary([],Acc) ->
    Acc;
from_ternary([H|T],Acc) ->
    from_ternary(T,Acc*3 + H).

mul(A,B) -> mul(B,A,[]).

mul(_,[],Acc) ->
    Acc;
mul(B,[A|As],Acc) ->
    BP = case A of
             -1 -> neg(B);
             0 ->  [0];
             1 ->  B
         end,
    A1 = Acc++[0],
    A2=add(BP,A1),
    mul(B,As,A2).

neg(T) -> [ -H || H <- T].

sub(A,B) -> add(A,neg(B)).

add(A,B) when length(A) < length(B) ->
    add(lists:duplicate(length(B)-length(A),0)++A,B);
add(A,B) when length(A) > length(B) ->
   add(B,A);
add(A,B) ->
    add(lists:reverse(A),lists:reverse(B),0,[]).

add([],[],0,Acc) ->
    Acc;
add([],[],C,Acc) ->
    [C|Acc];
add([A|As],[B|Bs],C,Acc) ->
    [C1,D] = add_util(A+B+C),
    add(As,Bs,C1,[D|Acc]).

add_util(-3) -> [-1,0];
add_util(-2) -> [-1,1];
add_util(-1) -> [0,-1];
add_util(3) -> [1,0];
add_util(2) -> [1,-1];
add_util(1) -> [0,1];
add_util(0) -> [0,0].


USING: kernel combinators locals formatting lint literals
       sequences assocs strings arrays
       math math.functions math.order ;
IN: rosetta-code.bt
CONSTANT: addlookup {
  { 0 CHAR: 0 }
  { 1 CHAR: + }
  { -1 CHAR: - }
}

<PRIVATE

: bt-add-digits ( a b c -- d e )
  + + 3 +
  { { 0 -1 } { 1 -1 } { -1 0 } { 0 0 } { 1 0 } { -1 1 } { 0 1 } }
  nth first2
;

PRIVATE>

! Conversion
: bt>integer ( seq -- x ) 0 [ swap 3 * + ] reduce ;
: integer>bt ( x -- x ) [ dup zero? not ] [
    dup 3 rem {
      { 0 [ 3 / 0 ] }
      { 1 [ 3 / round 1 ] }
      { 2 [ 1 + 3 / round -1 ] }
    } case
  ] produce nip reverse
;
: bt>string ( seq -- str ) [ addlookup at ] map >string ;
: string>bt ( str -- seq ) [ addlookup value-at ] { } map-as ;

! Arithmetic
: bt-neg ( a -- -a ) [ neg ] map ;
:: bt-add ( u v -- w )
  u v max-length :> maxl
  u v [ maxl 0 pad-head reverse ] [email protected] :> ( u v )
  0 :> carry!
  u v { } [ carry bt-add-digits carry! prefix ] 2reduce
  carry prefix [ zero? ] trim-head
;
: bt-sub ( u v -- w ) bt-neg bt-add ;
:: bt-mul ( u v -- w ) u { } [
    {
      { -1 [ v bt-neg ] }
      { 0  [ { } ] }
      { 1  [ v ] }
    } case bt-add 0 suffix
  ] reduce
  1 head*
;

[let
  "+-0++0+" string>bt :> a
  -436 integer>bt     :> b
  "+-++-" string>bt   :> c
  b c bt-sub a bt-mul :> d
  "a" a bt>integer a bt>string "%s: %d, %s\n" printf
  "b" b bt>integer b bt>string "%s: %d, %s\n" printf
  "c" c bt>integer c bt>string "%s: %d, %s\n" printf
  "a*(b-c)" d bt>integer d bt>string "%s: %d, %s\n" printf
]


#define MAX(a, b) iif((a) > (b), (a), (b))
Dim Shared As Integer pow, signo
Dim Shared As String t
t = "-0+"

Function deci(cadena As String) As Integer
    Dim As Integer i, deci1
    Dim As String c1S
    pow = 1
    For i = Len(cadena) To 1 Step -1
        c1S = Mid(cadena,i,1)
        signo = Instr(t, c1S)-2
        deci1 = deci1 + pow * signo
        pow *= 3
    Next i
    Return deci1
End Function

Function ternary(n As Integer) As String
    Dim As String ternario
    Dim As Integer i, k
    While Abs(n) > 3^k/2
        k += 1
    Wend
    k -= 1

    pow = 3^k
    For i = k To 0 Step -1
        signo = (n>0) - (n<0)
        signo *= (Abs(n) > pow/2)
        ternario += Mid(t,signo+2,1)
        n -= signo*pow
        pow /= 3
    Next
    If ternario = "" Then ternario = "0"
    Return ternario
End Function

Function negate(cadena As String) As String
    Dim As String negar = ""
    For i As Integer = 1 To Len(cadena)
        negar += Mid(t, 4-Instr(t, Mid(cadena,i,1)), 1)
    Next i
    Return negar
End Function

Function pad(cadenaA As String, n As Integer) As String
    Dim As String relleno = cadenaA
    While Len(relleno) < n
        relleno = "0" + relleno
    Wend
    Return relleno
End Function

Function addTernary(cadenaA As String, cadenaB As String) As String
    Dim As Integer l = max(Len(cadenaA), Len(cadenaB))
    Dim As Integer i, x, y, z
    cadenaA = pad(cadenaA, l)
    cadenaB = pad(cadenaB, l)
    Dim As String resultado = ""
    Dim As Byte llevar = 0
    For i = l To 1 Step -1
        x = Instr(t, Mid(cadenaA,i,1))-2
        y = Instr(t, Mid(cadenaB,i,1))-2
        z = x + y + llevar

        If Abs(z) < 2 Then
            llevar = 0
        Elseif z > 0 Then
            llevar = 1: z -= 3
        Elseif z < 0 Then
            llevar = -1: z += 3
        End If

        resultado = Mid(t,z+2,1) + resultado
    Next i
    If llevar <> 0 Then resultado = Mid(t,llevar+2,1) + resultado

    i = 0
    While Mid(resultado,i+1,1) = "0"
        i += 1
    Wend
    resultado = Mid(resultado,i+1)
    If resultado = "" Then resultado = "0"
    Return resultado
End Function

Function subTernary(cadenaA As String, cadenaB As String) As String
    Return addTernary(cadenaA, negate(cadenaB))
End Function

Function multTernary(cadenaA As String, cadenaB As String) As String
    Dim As String resultado = ""
    Dim As String tS = "", cambio = ""
    For i As Integer = Len(cadenaA) To 1 Step -1
        Select Case Mid(cadenaA,i,1)
        Case "+": tS = cadenaB
        Case "0": tS = "0"
        Case "-": tS = negate(cadenaB)
        End Select

        resultado = addTernary(resultado, tS + cambio)
        cambio += "0"
    Next i
    Return resultado
End Function

Dim As String cadenaA = "+-0++0+"
Dim As Integer a = deci(cadenaA)
Print "      a:", a, cadenaA

Dim As Integer b = -436
Dim As String cadenaB = ternary(b)
Print "      b:", b, cadenaB

Dim As String cadenaC = "+-++-"
Dim As Integer c = deci(cadenaC)
Print "      c:", c, cadenaC

'calcular en ternario
Dim As String resS = multTernary(cadenaA, subTernary(cadenaB, cadenaC))
Print "a*(b-c):", deci(resS), resS

Print !"\nComprobamos:"
Print "a*(b-c): ", a * (b - c)
Sleep


ОТДЕЛ Сетунь+;
ИСПОЛЬЗУЕТ
  Параметр ИЗ "...\Отделы\Обмен\",
  Текст ИЗ "...\Отделы\Числа\",
  Вывод ИЗ "...\Отделы\Обмен\";

ПЕР
  зч: РЯД 10 ИЗ ЗНАК;
  счпоз: ЦЕЛ;
  число: ЦЕЛ;
  память: ДОСТУП К НАБОР
    ячейки: РЯД 20 ИЗ ЦЕЛ;
    размер: УЗКЦЕЛ;
    отрицательное: КЛЮЧ
  КОН;

ЗАДАЧА СоздатьПамять;
УКАЗ
  СОЗДАТЬ(память);
  память.размер := 0;
  память.отрицательное := ОТКЛ
КОН СоздатьПамять;

ЗАДАЧА ДобавитьВПамять(что: ЦЕЛ);
УКАЗ
  память.ячейки[память.размер] := что;
  УВЕЛИЧИТЬ(память.размер)
КОН ДобавитьВПамять;

ЗАДАЧА ОбратитьПамять;
ПЕР
  зчсл: ЦЕЛ;
  сч: ЦЕЛ;
УКАЗ
  ОТ сч := 0 ДО память.размер ДЕЛИТЬ 2 - 1 ВЫП
    зчсл := память.ячейки[сч];
    память.ячейки[сч] := память.ячейки[память.размер-сч-1];
    память.ячейки[память.размер-сч-1] := зчсл
  КОН
КОН ОбратитьПамять;

ЗАДАЧА ВывестиПамять;
ПЕР
  сч: ЦЕЛ;
УКАЗ
  ОТ сч := 0 ДО память.размер-1 ВЫП
    ЕСЛИ память.ячейки[сч] < 0 ТО
      Вывод.Цепь("-")
    АЕСЛИ память.ячейки[сч] > 0 ТО
      Вывод.Цепь("+")
    ИНАЧЕ Вывод.Цепь("0") КОН
  КОН
КОН ВывестиПамять;

ЗАДАЧА УдалитьПамять;
УКАЗ
  память := ПУСТО
КОН УдалитьПамять;

ЗАДАЧА Перевести(число: ЦЕЛ);
ПЕР
  о: ЦЕЛ;
  з: КЛЮЧ;
  ЗАДАЧА ВПамять(что: ЦЕЛ);
  УКАЗ
    ЕСЛИ память.отрицательное ТО
      ЕСЛИ что < 0 ТО ДобавитьВПамять(1)
      АЕСЛИ что > 0 ТО ДобавитьВПамять(-1)
      ИНАЧЕ ДобавитьВПамять(0) КОН
    ИНАЧЕ
      ДобавитьВПамять(что)
    КОН
  КОН ВПамять;
УКАЗ
  ЕСЛИ число < 0 ТО память.отрицательное := ВКЛ КОН;
  число := МОДУЛЬ(число);
  з := ОТКЛ;
  ПОКА число > 0 ВЫП
    о := число ОСТАТОК 3;
    число := число ДЕЛИТЬ 3;
    ЕСЛИ з ТО
      ЕСЛИ о = 2 ТО ВПамять(0) АЕСЛИ о = 1 ТО ВПамять(-1) ИНАЧЕ ВПамять(1); з := ОТКЛ КОН
    ИНАЧЕ
      ЕСЛИ о = 2 ТО ВПамять(-1); з := ВКЛ ИНАЧЕ ВПамять(о) КОН
    КОН
  КОН;
  ЕСЛИ з ТО ВПамять(1) КОН;
  ОбратитьПамять;
  ВывестиПамять(ВКЛ);
КОН Перевести;

ЗАДАЧА ВЧисло(): ЦЕЛ;
ПЕР
  сч, мн: ЦЕЛ;
  результат: ЦЕЛ;
УКАЗ
  результат := 0;
  мн := 1;
  ОТ сч := 0 ДО память.размер-1 ВЫП
    УВЕЛИЧИТЬ(результат, память.ячейки[память.размер-сч-1]*мн);
    мн := мн * 3
  КОН;
  ВОЗВРАТ результат
КОН ВЧисло;

УКАЗ
  Параметр.Текст(1, зч); счпоз := 0;
  число := Текст.ВЦел(зч, счпоз);
  СоздатьПамять;
  Перевести(число);
  Вывод.ЧЦел(" = %d.", ВЧисло(), 0, 0, 0);
  УдалитьПамять

КОН Сетунь.


package main

import (
    "fmt"
    "strings"
)

// R1: representation is a slice of int8 digits of -1, 0, or 1.
// digit at index 0 is least significant.  zero value of type is
// representation of the number 0.
type bt []int8

// R2: string conversion:

// btString is a constructor.  valid input is a string of any length
// consisting of only '+', '-', and '0' characters.
// leading zeros are allowed but are trimmed and not represented.
// false return means input was invalid.
func btString(s string) (*bt, bool) {
    s = strings.TrimLeft(s, "0")
    b := make(bt, len(s))
    for i, last := 0, len(s)-1; i < len(s); i++ {
        switch s[i] {
        case '-':
            b[last-i] = -1
        case '0':
            b[last-i] = 0
        case '+':
            b[last-i] = 1
        default:
            return nil, false
        }
    }
    return &b, true
}

// String method converts the other direction, returning a string of
// '+', '-', and '0' characters representing the number.
func (b bt) String() string {
    if len(b) == 0 {
        return "0"
    }
    last := len(b) - 1
    r := make([]byte, len(b))
    for i, d := range b {
        r[last-i] = "-0+"[d+1]
    }
    return string(r)
}

// R3: integer conversion
// int chosen as "native integer"

// btInt is a constructor like btString.
func btInt(i int) *bt {
    if i == 0 {
        return new(bt)
    }
    var b bt
    var btDigit func(int)
    btDigit = func(digit int) {
        m := int8(i % 3)
        i /= 3
        switch m {
        case 2:
            m = -1
            i++
        case -2:
            m = 1
            i--
        }
        if i == 0 {
            b = make(bt, digit+1)
        } else {
            btDigit(digit + 1)
        }
        b[digit] = m
    }
    btDigit(0)
    return &b
}

// Int method converts the other way, returning the value as an int type.
// !ok means overflow occurred during conversion, not necessarily that the
// value is not representable as an int.  (Of course there are other ways
// of doing it but this was chosen as "reasonable.")
func (b bt) Int() (r int, ok bool) {
    pt := 1
    for _, d := range b {
        dp := int(d) * pt
        neg := r < 0
        r += dp
        if neg {
            if r > dp {
                return 0, false
            }
        } else {
            if r < dp {
                return 0, false
            }
        }
        pt *= 3
    }
    return r, true
}

// R4: negation, addition, and multiplication

func (z *bt) Neg(b *bt) *bt {
    if z != b {
        if cap(*z) < len(*b) {
            *z = make(bt, len(*b))
        } else {
            *z = (*z)[:len(*b)]
        }
    }
    for i, d := range *b {
        (*z)[i] = -d
    }
    return z
}

func (z *bt) Add(a, b *bt) *bt {
    if len(*a) < len(*b) {
        a, b = b, a
    }
    r := *z
    r = r[:cap(r)]
    var carry int8
    for i, da := range *a {
        if i == len(r) {
            n := make(bt, len(*a)+4)
            copy(n, r)
            r = n
        }
        sum := da + carry
        if i < len(*b) {
            sum += (*b)[i]
        }
        carry = sum / 3
        sum %= 3
        switch {
        case sum > 1:
            sum -= 3
            carry++
        case sum < -1:
            sum += 3
            carry--
        }
        r[i] = sum
    }
    last := len(*a)
    if carry != 0 {
        if len(r) == last {
            n := make(bt, last+4)
            copy(n, r)
            r = n
        }
        r[last] = carry
        *z = r[:last+1]
        return z
    }
    for {
        if last == 0 {
            *z = nil
            break
        }
        last--
        if r[last] != 0 {
            *z = r[:last+1]
            break
        }
    }
    return z
}

func (z *bt) Mul(a, b *bt) *bt {
    if len(*a) < len(*b) {
        a, b = b, a
    }
    var na bt
    for _, d := range *b {
        if d == -1 {
            na.Neg(a)
            break
        }
    }
    r := make(bt, len(*a)+len(*b))
    for i := len(*b) - 1; i >= 0; i-- {
        switch (*b)[i] {
        case 1:
            p := r[i:]
            p.Add(&p, a)
        case -1:
            p := r[i:]
            p.Add(&p, &na)
        }
    }
    i := len(r)
    for i > 0 && r[i-1] == 0 {
        i--
    }
    *z = r[:i]
    return z
}

func main() {
    a, _ := btString("+-0++0+")
    b := btInt(-436)
    c, _ := btString("+-++-")
    show("a:", a)
    show("b:", b)
    show("c:", c)
    show("a(b-c):", a.Mul(a, b.Add(b, c.Neg(c))))
}

func show(label string, b *bt) {
    fmt.Printf("%7s %12v ", label, b)
    if i, ok := b.Int(); ok {
        fmt.Printf("%7d\n", i)
    } else {
        fmt.Println("int overflow")
    }
}


enum T {
    m('-', -1), z('0', 0), p('+', 1)

    final String symbol
    final int value

    private T(String symbol, int value) {
        this.symbol = symbol
        this.value = value
    }

    static T get(Object key) {
        switch (key) {
            case [m.value, m.symbol] : return m
            case [z.value, z.symbol] : return z
            case [p.value, p.symbol] : return p
            default:                   return null
        }
    }

    T negative() {
        T.get(-this.value)
    }

    String toString() { this.symbol }
}

class BalancedTernaryInteger {

    static final MINUS = new BalancedTernaryInteger(T.m)
    static final ZERO  = new BalancedTernaryInteger(T.z)
    static final PLUS  = new BalancedTernaryInteger(T.p)
    private static final LEADING_ZEROES = /^0+/

    final String value

    BalancedTernaryInteger(String bt) {
        assert bt && bt.toSet().every { T.get(it) }
        value = bt ==~ LEADING_ZEROES ? T.z : bt.replaceAll(LEADING_ZEROES, '');
    }

    BalancedTernaryInteger(BigInteger i) {
        this(i == 0 ? T.z.symbol : valueFromInt(i));
    }

    BalancedTernaryInteger(T...tArray) {
        this(tArray.sum{ it.symbol });
    }

    BalancedTernaryInteger(List<T> tList) {
        this(tList.sum{ it.symbol });
    }

    private static String valueFromInt(BigInteger i) {
        assert i != null
        if (i < 0) return negate(valueFromInt(-i))
        if (i == 0) return ''
        int bRem = (((i % 3) - 2) ?: -3) + 2
        valueFromInt((i - bRem).intdiv(3)) + T.get(bRem)
    }

    private static String negate(String bt) {
        bt.collect{ T.get(it) }.inject('') { str, t ->
            str + (-t)
        }
    }

    private static final Map INITIAL_SUM_PARTS = [carry:T.z, sum:[]]
    private static final prepValueLen = { int len, String s ->
        s.padLeft(len + 1, T.z.symbol).collect{ T.get(it) }
    }
    private static final partCarrySum = { partialSum, carry, trit ->
        [carry: carry, sum: [trit] + partialSum]
    }
    private static final partSum = { parts, trits ->
        def carrySum = partCarrySum.curry(parts.sum)
        switch ((trits + parts.carry).sort()) {
            case [[T.m, T.m, T.m]]:                  return carrySum(T.m, T.z) //-3
            case [[T.m, T.m, T.z]]:                  return carrySum(T.m, T.p) //-2
            case [[T.m, T.z, T.z], [T.m, T.m, T.p]]: return carrySum(T.z, T.m) //-1
            case [[T.z, T.z, T.z], [T.m, T.z, T.p]]: return carrySum(T.z, T.z) //+0
            case [[T.z, T.z, T.p], [T.m, T.p, T.p]]: return carrySum(T.z, T.p) //+1
            case [[T.z, T.p, T.p]]:                  return carrySum(T.p, T.m) //+2
            case [[T.p, T.p, T.p]]: default:         return carrySum(T.p, T.z) //+3
        }
    }

    BalancedTernaryInteger plus(BalancedTernaryInteger that) {
        assert that != null
        if (this == ZERO) return that
        if (that == ZERO) return this
        def prep = prepValueLen.curry([value.size(), that.value.size()].max())
        List values = [prep(value), prep(that.value)].transpose()
        new BalancedTernaryInteger(values[-1..(-values.size())].inject(INITIAL_SUM_PARTS, partSum).sum)
    }

    BalancedTernaryInteger negative() {
        !this ? this : new BalancedTernaryInteger(negate(value))
    }

    BalancedTernaryInteger minus(BalancedTernaryInteger that) {
        assert that != null
        this + -that
    }

    private static final INITIAL_PRODUCT_PARTS = [sum:ZERO, pad:'']
    private static final sigTritCount = { it.value.replaceAll(T.z.symbol,'').size() }

    private BalancedTernaryInteger paddedValue(String pad) {
        new BalancedTernaryInteger(value + pad)
    }

    private BalancedTernaryInteger partialProduct(T multiplier, String pad){
        switch (multiplier) {
            case T.z:          return ZERO
            case T.m:          return -paddedValue(pad)
            case T.p: default: return paddedValue(pad)
        }
    }

    BalancedTernaryInteger multiply(BalancedTernaryInteger that) {
        assert that != null
        if (that == ZERO)  return ZERO
        if (that == PLUS)  return this
        if (that == MINUS) return -this
        if (this.value.size() == 1 || sigTritCount(this) < sigTritCount(that)) {
            return that.multiply(this)
        }
        that.value.collect{ T.get(it) }[-1..(-value.size())].inject(INITIAL_PRODUCT_PARTS) { parts, multiplier ->
            [sum: parts.sum + partialProduct(multiplier, parts.pad), pad: parts.pad + T.z]
        }.sum
    }

    BigInteger asBigInteger() {
        value.collect{ T.get(it) }.inject(0) { i, trit -> i * 3 + trit.value }
    }

    def asType(Class c) {
        switch (c) {
            case Integer:              return asBigInteger() as Integer
            case Long:                 return asBigInteger() as Long
            case [BigInteger, Number]: return asBigInteger()
            case Boolean:              return this != ZERO
            case String:               return toString()
            default:                   return super.asType(c)
        }
    }

    boolean equals(Object that) {
        switch (that) {
            case BalancedTernaryInteger: return this.value == that?.value
            default:                     return super.equals(that)
        }
    }

    int hashCode() { this.value.hashCode() }

    String toString() { value }
}


data BalancedTernary = Bt [Int]

zeroTrim a = if null s then [0] else s where
	s = fst $ foldl f ([],[]) a
	f (x,y) 0 = (x, y++[0])
	f (x,y) z = (x++y++[z], [])

btList (Bt a) = a

instance Eq BalancedTernary where
	(==) a b = btList a == btList b

btNormalize = listBt . _carry 0 where
	_carry c [] = if c == 0 then [] else [c]
	_carry c (a:as) = r:_carry cc as where
		(cc, r) = f $ (a+c) `quotRem` 3 where
			f (x,  2) = (x + 1, -1)
			f (x, -2) = (x - 1,  1)
			f x = x

listBt = Bt . zeroTrim

instance Show BalancedTernary where
	show = reverse . map (\d->case d of -1->'-'; 0->'0'; 1->'+') . btList

strBt = Bt . zeroTrim.reverse.map (\c -> case c of '-' -> -1; '0' -> 0; '+' -> 1)

intBt :: Integral a => a -> BalancedTernary
intBt = fromIntegral . toInteger

btInt = foldr (\a z -> a + 3 * z) 0 . btList

listAdd a b = take (max (length a) (length b)) $ zipWith (+) (a++[0,0..]) (b++[0,0..])

-- mostly for operators, also small stuff to make GHC happy
instance Num BalancedTernary where
	negate = Bt . map negate . btList
	(+) x y = btNormalize $ listAdd (btList x) (btList y)
	(*) x y = btNormalize $ mul_ (btList x) (btList y) where
		mul_ _ [] = []
                mul_ as b = foldr (\a z -> listAdd (map (a*) b) (0:z)) [] as

	-- we don't need to define binary "-" by hand

	signum (Bt a) = if a == [0] then 0 else Bt [last a]
	abs x = if signum x == Bt [-1] then negate x else x

	fromInteger = btNormalize . f where
		f 0 = []
		f x = fromInteger (rem x 3) : f (quot x 3)

main = let	(a,b,c) = (strBt "+-0++0+", intBt (-436), strBt "+-++-")
		r = a * (b - c)
	in do
		print $ map btInt [a,b,c]
		print $ r
		print $ btInt r


procedure main()
    a := "+-0++0+"
    write("a = +-0++0+"," = ",cvtFromBT("+-0++0+"))
    write("b = -436 = ",b := cvtToBT(-436))
    c := "+-++-"
    write("c = +-++- = ",cvtFromBT("+-++-"))
    d := mul(a,sub(b,c))
    write("a(b-c) = ",d," = ",cvtFromBT(d))
end

procedure bTrim(s)
    return s[upto('+-',s):0] | "0"
end

procedure cvtToBT(n)
    if n=0 then return "0"
    if n<0 then return map(cvtToBT(-n),"+-","-+")
    return bTrim(case n%3 of {
        0: cvtToBT(n/3)||"0"
        1: cvtToBT(n/3)||"+"
        2: cvtToBT((n+1)/3)||"-"
        })
end

procedure cvtFromBT(n)
    sum := 0
    i := -1
    every c := !reverse(n) do {
        sum +:= case c of {
            "+" : 1
            "-" : -1
            "0" : 0
            }*(3^(i+:=1))
        }
    return sum
end

procedure neg(n)
    return map(n,"+-","-+")
end

procedure add(a,b)
    if *b > *a then a :=: b
    b := repl("0",*a-*b)||b
    c := "0"
    sum := ""
    every place := 1 to *a do {
        ds := addDigits(a[-place],b[-place],c)
        c := if *ds > 1 then c := ds[1] else "0"
        sum := ds[-1]||sum
        }
    return bTrim(c||sum)
end

procedure addDigits(a,b,c)
    sum1 := addDigit(a,b)
    sum2 := addDigit(sum1[-1],c)
    if *sum1 = 1 then return sum2
    if *sum2 = 1 then return sum1[1]||sum2
    return sum1[1]
end

procedure addDigit(a,b)
    return case(a||b) of {
                "00"|"0+"|"0-": b
                "+0"|"-0"     : a
                "++"          : "+-"
                "+-"|"-+"     : "0"
                "--"          : "-+"
                }
end

procedure sub(a,b)
    return add(a,neg(b))
end

procedure mul(a,b)
    if b[1] == "-" then {
        b := neg(b)
        negate := "yes"
        }
    b := cvtFromBT(b)
    i := "+"
    mul := "0"
    while cvtFromBT(i) <= b do {
        mul := add(mul,a)
        i := add(i,"+")
        }
    return (\negate,map(mul,"+-","-+")) | mul
end


trigits=: 1+3 <[email protected]^. 2 * 1&>[email protected]|
trinOfN=: |[email protected]((_1 + ] #: #.&1@] + [) #&3@trigits) :. nOfTrin
nOfTrin=: p.&3 :. trinOfN
trinOfStr=: 0 1 _1 {~ '0+-'&[email protected]|. :. strOfTrin
strOfTrin=: {&'0+-'@|. :. trinOfStr

carry=: +//[email protected]:(trinOfN"0)^:_
trimLead0=: (}.~ i.&1@:~:&0)&.|.

add=: [email protected](+/@,:)
neg=: -
mul=: [email protected]@(+//[email protected](*/))


/*
 * Test case
 * With balanced ternaries a from string "+-0++0+", b from native integer -436, c "+-++-":
 * Write out a, b and c in decimal notation;
 * Calculate a × (b − c), write out the result in both ternary and decimal notations.
 */
public class BalancedTernary
{
	public static void main(String[] args)
	{
 		BTernary a=new BTernary("+-0++0+");
		BTernary b=new BTernary(-436);
		BTernary c=new BTernary("+-++-");

		System.out.println("a="+a.intValue());
		System.out.println("b="+b.intValue());
		System.out.println("c="+c.intValue());
		System.out.println();

		//result=a*(b-c)
		BTernary result=a.mul(b.sub(c));

		System.out.println("result= "+result+" "+result.intValue());
	}

	public static class BTernary
	{
		String value;
		public BTernary(String s)
		{
			int i=0;
			while(s.charAt(i)=='0')
				i++;
			this.value=s.substring(i);
		}
		public BTernary(int v)
		{
			this.value="";
			this.value=convertToBT(v);
		}

		private String convertToBT(int v)
		{
			if(v<0)
				return flip(convertToBT(-v));
			if(v==0)
				return "";
			int rem=mod3(v);
			if(rem==0)
				return convertToBT(v/3)+"0";
			if(rem==1)
				return convertToBT(v/3)+"+";
			if(rem==2)
				return convertToBT((v+1)/3)+"-";
			return "You can't see me";
		}
		private String flip(String s)
		{
			String flip="";
			for(int i=0;i<s.length();i++)
			{
				if(s.charAt(i)=='+')
					flip+='-';
				else if(s.charAt(i)=='-')
					flip+='+';
				else
					flip+='0';
			}
			return flip;
		}
		private int mod3(int v)
		{
			if(v>0)
				return v%3;
			v=v%3;
			return (v+3)%3;
		}

		public int intValue()
		{
			int sum=0;
			String s=this.value;
			for(int i=0;i<s.length();i++)
			{
				char c=s.charAt(s.length()-i-1);
				int dig=0;
				if(c=='+')
					dig=1;
				else if(c=='-')
					dig=-1;
				sum+=dig*Math.pow(3, i);
			}
			return sum;
		}

		public BTernary add(BTernary that)
		{
			String a=this.value;
			String b=that.value;

			String longer=a.length()>b.length()?a:b;
			String shorter=a.length()>b.length()?b:a;

			while(shorter.length()<longer.length())
				shorter=0+shorter;

			a=longer;
			b=shorter;

			char carry='0';
			String sum="";
			for(int i=0;i<a.length();i++)
			{
				int place=a.length()-i-1;
				String digisum=addDigits(a.charAt(place),b.charAt(place),carry);
				if(digisum.length()!=1)
					carry=digisum.charAt(0);
				else
					carry='0';
				sum=digisum.charAt(digisum.length()-1)+sum;
			}
			sum=carry+sum;

			return new BTernary(sum);
		}
		private String addDigits(char a,char b,char carry)
		{
			String sum1=addDigits(a,b);
			String sum2=addDigits(sum1.charAt(sum1.length()-1),carry);
			//System.out.println(carry+" "+sum1+" "+sum2);
			if(sum1.length()==1)
				return sum2;
			if(sum2.length()==1)
				return sum1.charAt(0)+sum2;
			return sum1.charAt(0)+"";
		}
		private String addDigits(char a,char b)
		{
			String sum="";
			if(a=='0')
				sum=b+"";
			else if (b=='0')
				sum=a+"";
			else if(a=='+')
			{
				if(b=='+')
					sum="+-";
				else
					sum="0";
			}
			else
			{
				if(b=='+')
					sum="0";
				else
					sum="-+";
			}
			return sum;
		}

		public BTernary neg()
		{
			return new BTernary(flip(this.value));
		}

		public BTernary sub(BTernary that)
		{
			return this.add(that.neg());
		}

		public BTernary mul(BTernary that)
		{
			BTernary one=new BTernary(1);
			BTernary zero=new BTernary(0);
			BTernary mul=new BTernary(0);

			int flipflag=0;
			if(that.compareTo(zero)==-1)
			{
				that=that.neg();
				flipflag=1;
			}
			for(BTernary i=new BTernary(1);i.compareTo(that)<1;i=i.add(one))
				mul=mul.add(this);

			if(flipflag==1)
				mul=mul.neg();
			return mul;
		}

		public boolean equals(BTernary that)
		{
			return this.value.equals(that.value);
		}
		public int compareTo(BTernary that)
		{
			if(this.intValue()>that.intValue())
				return 1;
			else if(this.equals(that))
				return 0;
			 return -1;
		}

		public String toString()
		{
			return value;
		}
	}
}


struct BalancedTernary <: Signed
    digits::Vector{Int8}
end
BalancedTernary() = zero(BalancedTernary)
BalancedTernary(n) = convert(BalancedTernary, n)

const sgn2chr = Dict{Int8,Char}(-1 => '-', 0 => '0', +1 => '+')
Base.show(io::IO, bt::BalancedTernary) = print(io, join(sgn2chr[x] for x in reverse(bt.digits)))
Base.copy(bt::BalancedTernary) = BalancedTernary(copy(bt.digits))
Base.zero(::Type{BalancedTernary}) = BalancedTernary(Int8[0])
Base.iszero(bt::BalancedTernary) = bt.digits == Int8[0]
Base.convert(::Type{T}, bt::BalancedTernary) where T<:Number = sum(3 ^ T(ex - 1) * s for (ex, s) in enumerate(bt.digits))
function Base.convert(::Type{BalancedTernary}, n::Signed)
    r = BalancedTernary(Int8[])
    if iszero(n) push!(r.digits, 0) end
    while n != 0
        if mod(n, 3) == 0
            push!(r.digits, 0)
            n = fld(n, 3)
        elseif mod(n, 3) == 1
            push!(r.digits, 1)
            n = fld(n, 3)
        else
            push!(r.digits, -1)
            n = fld(n + 1, 3)
        end
    end
    return r
end
const chr2sgn = Dict{Char,Int8}('-' => -1, '0' => 0, '+' => 1)
function Base.convert(::Type{BalancedTernary}, s::AbstractString)
    return BalancedTernary(getindex.(chr2sgn, collect(reverse(s))))
end

macro bt_str(s)
    convert(BalancedTernary, s)
end

const table = NTuple{2,Int8}[(0, -1), (1, -1), (-1, 0), (0, 0), (1, 0), (-1, 1), (0, 1)]
function _add(a::Vector{Int8}, b::Vector{Int8}, c::Int8=Int8(0))
    if isempty(a) || isempty(b)
        if c == 0 return isempty(a) ? b : a end
        return _add([c], isempty(a) ? b : a)
    else
        d, c = table[4 + (isempty(a) ? 0 : a[1]) + (isempty(b) ? 0 : b[1]) + c]
        r = _add(a[2:end], b[2:end], c)
        if !isempty(r) || d != 0
            return unshift!(r, d)
        else
            return r
        end
    end
end
function Base.:+(a::BalancedTernary, b::BalancedTernary)
    v = _add(a.digits, b.digits)
    return isempty(v) ? BalancedTernary(0) : BalancedTernary(v)
end
Base.:-(bt::BalancedTernary) = BalancedTernary(-bt.digits)
Base.:-(a::BalancedTernary, b::BalancedTernary) = a + (-b)
function _mul(a::Vector{Int8}, b::Vector{Int8})
    if isempty(a) || isempty(b)
        return Int8[]
    else
        if a[1] == -1 x = (-BalancedTernary(b)).digits
        elseif a[1] == 0 x = Int8[]
        elseif a[1] == 1 x = b end
        y = append!(Int8[0], _mul(a[2:end], b))
        return _add(x, y)
    end
end
function Base.:*(a::BalancedTernary, b::BalancedTernary)
    v = _mul(a.digits, b.digits)
    return isempty(v) ? BalancedTernary(0) : BalancedTernary(v)
end

a = bt"+-0++0+"
println("a: $(Int(a)), $a")
b = BalancedTernary(-436)
println("b: $(Int(b)), $b")
c = BalancedTernary("+-++-")
println("c: $(Int(c)), $c")
r = a * (b - c)
println("a * (b - c): $(Int(r)), $r")

@assert Int(r) == Int(a) * (Int(b) - Int(c))


// version 1.1.3

import java.math.BigInteger

val bigZero = BigInteger.ZERO
val bigOne = BigInteger.ONE
val bigThree = BigInteger.valueOf(3L)

data class BTernary(private var value: String) : Comparable<BTernary> {

    init {
        require(value.all { it in "0+-" })
        value = value.trimStart('0')
    }

    constructor(v: Int) : this(BigInteger.valueOf(v.toLong()))

    constructor(v: BigInteger) : this("") {
        value = toBT(v)
    }

    private fun toBT(v: BigInteger): String {
        if (v < bigZero) return flip(toBT(-v))
        if (v == bigZero) return ""
        val rem = mod3(v)
        return when (rem) {
            bigZero -> toBT(v / bigThree) + "0"
            bigOne  -> toBT(v / bigThree) + "+"
            else    -> toBT((v + bigOne) / bigThree) + "-"
        }
    }

    private fun flip(s: String): String {
        val sb = StringBuilder()
        for (c in s) {
            sb.append(when (c) {
                '+'  -> "-"
                '-'  -> "+"
                else -> "0"
            })
        }
        return sb.toString()
    }

    private fun mod3(v: BigInteger): BigInteger {
        if (v > bigZero) return v % bigThree
        return ((v % bigThree) + bigThree) % bigThree
    }

    fun toBigInteger(): BigInteger {
        val len = value.length
        var sum = bigZero
        var pow = bigOne
        for (i in 0 until len) {
            val c = value[len - i - 1]
            val dig = when (c) {
                '+'  -> bigOne
                '-'  -> -bigOne
                else -> bigZero
            }
            if (dig != bigZero) sum += dig * pow
            pow *= bigThree
        }
        return sum
    }

    private fun addDigits(a: Char, b: Char, carry: Char): String {
        val sum1 = addDigits(a, b)
        val sum2 = addDigits(sum1.last(), carry)
        return when {
            sum1.length == 1 -> sum2
            sum2.length == 1 -> sum1.take(1) + sum2
            else             -> sum1.take(1)
        }
    }

    private fun addDigits(a: Char, b: Char): String =
        when {
            a == '0' -> b.toString()
            b == '0' -> a.toString()
            a == '+' -> if (b == '+') "+-" else "0"
            else     -> if (b == '+') "0" else "-+"
        }

    operator fun plus(other: BTernary): BTernary {
        var a = this.value
        var b = other.value
        val longer = if (a.length > b.length) a else b
        var shorter = if (a.length > b.length) b else a
        while (shorter.length < longer.length) shorter = "0" + shorter
        a = longer
        b = shorter
        var carry = '0'
        var sum = ""
        for (i in 0 until a.length) {
            val place = a.length - i - 1
            val digisum = addDigits(a[place], b[place], carry)
            carry = if (digisum.length != 1) digisum[0] else '0'
            sum = digisum.takeLast(1) + sum
        }
        sum = carry.toString() + sum
        return BTernary(sum)
    }

    operator fun unaryMinus() = BTernary(flip(this.value))

    operator fun minus(other: BTernary) = this + (-other)

    operator fun times(other: BTernary): BTernary {
        var that = other
        val one = BTernary(1)
        val zero = BTernary(0)
        var mul = zero
        var flipFlag = false
        if (that < zero) {
            that = -that
            flipFlag = true
        }
        var i = one
        while (i <= that) {
            mul += this
            i += one
        }
        if (flipFlag) mul = -mul
        return mul
    }

    override operator fun compareTo(other: BTernary) =
        this.toBigInteger().compareTo(other.toBigInteger())

    override fun toString() = value
}

fun main(args: Array<String>) {
    val a = BTernary("+-0++0+")
    val b = BTernary(-436)
    val c = BTernary("+-++-")
    println("a = ${a.toBigInteger()}")
    println("b = ${b.toBigInteger()}")
    println("c = ${c.toBigInteger()}")
    val bResult = a * (b - c)
    val iResult = bResult.toBigInteger()
    println("a * (b - c) = $bResult = $iResult")
}


global tt$
tt$="-0+"   '-1 0 1; +2 -> 1 2 3, instr

'Test case:
'With balanced ternaries a from string "+-0++0+", b from native integer -436, c "+-++-":
'* write out a, b and c in decimal notation;
'* calculate a * (b - c), write out the result in both ternary and decimal notations.

a$="+-0++0+"
a=deci(a$)
print "a",a, a$

b=-436
b$=ternary$(b)
print "b",b, b$

c$="+-++-"
c=deci(c$)
print "c",c, c$

'calculate in ternary

res$=multTernary$(a$, subTernary$(b$, c$))
print "a * (b - c)", res$
print "In decimal:",deci(res$)

print "Check:"
print "a * (b - c)", a * (b - c)
end

function deci(s$)
    pow = 1
    for i = len(s$) to 1 step -1
        c$ = mid$(s$,i,1)
        'select case c$
        '    case "+":sign= 1
        '    case "-":sign=-1
        '    case "0":sign= 0
        'end select
        sign = instr(tt$,c$)-2
        deci = deci+pow*sign
        pow = pow*3
    next
end function

function ternary$(n)
    while abs(n)>3^k/2
        k=k+1
    wend
    k=k-1

    pow = 3^k
    for i = k to 0 step -1
        sign = (n>0) - (n<0)
        sign = sign * (abs(n)>pow/2)
        ternary$ = ternary$+mid$(tt$,sign+2,1)
        n = n - sign*pow
        pow = pow/3
    next
    if  ternary$ = "" then  ternary$ ="0"
end function

function multTernary$(a$, b$)

    c$ = ""
    t$ = ""
    shift$ = ""
    for i = len(a$) to 1 step -1

        select case mid$(a$,i,1)
        case "+": t$ = b$
        case "0": t$ = "0"
        case "-": t$ = negate$(b$)
        end select

        c$ = addTernary$(c$, t$+shift$)

        shift$ = shift$ +"0"
    'print d, t$, c$
    next
    multTernary$ = c$
end function

function subTernary$(a$, b$)
     subTernary$ = addTernary$(a$, negate$(b$))
end function

function negate$(s$)
    negate$=""
    for i = 1 to len(s$)
        'print mid$(s$,i,1), instr(tt$, mid$(s$,i,1)), 4-instr(tt$, mid$(s$,i,1))
        negate$=negate$+mid$(tt$, 4-instr(tt$, mid$(s$,i,1)), 1)
    next
end function

function addTernary$(a$, b$)
'add a$ + b$, for now only positive
    l = max(len(a$), len(b$))
    a$=pad$(a$,l)
    b$=pad$(b$,l)
    c$ = "" 'result
    carry = 0
    for i = l to 1 step -1
        a = instr(tt$,mid$(a$,i,1))-2
        b = instr(tt$,mid$(b$,i,1))-2     '-1 0 1
        c = a+b+carry

        select case
        case abs(c)<2
            carry = 0
        case c>0
            carry =1: c=c-3
        case c<0
            carry =-1: c=c+3
        end select

        'print a, b, c
        c$ = mid$(tt$,c+2,1)+c$
    next
    if carry<>0 then c$ = mid$(tt$,carry+2,1) +c$
    'print c$
    'have to trim leading 0's
    i=0
    while mid$(c$,i+1,1)="0"
        i=i+1
    wend
    c$=mid$(c$,i+1)
    if c$="" then c$="0"
    addTernary$ = c$
end function

function pad$(a$,n)  'pad from right with 0 to length n
     pad$ = a$
     while len(pad$)<n
        pad$ = "0"+pad$
     wend
end function


function to_bt(n)
    local d = { '0', '+', '-' }
    local v = { 0, 1, -1 }

    local b = ""

    while n ~= 0 do
        local r = n % 3
        if r < 0 then
            r = r + 3
        end

        b = b .. d[r + 1]

        n = n - v[r + 1]
        n = math.floor(n / 3)
    end

    return b:reverse()
end

function from_bt(s)
    local n = 0

    for i=1,s:len() do
        local c = s:sub(i,i)
        n = n * 3
        if c == '+' then
            n = n + 1
        elseif c == '-' then
            n = n - 1
        end
    end

    return n
end

function last_char(s)
    return s:sub(-1,-1)
end

function add(b1,b2)
    local out = "oops"
    if b1 ~= "" and b2 ~= "" then
        local d = ""

        local L1 = last_char(b1)
        local c1 = b1:sub(1,-2)
        local L2 = last_char(b2)
        local c2 = b2:sub(1,-2)
        if L2 < L1 then
            L2, L1 = L1, L2
        end

        if L1 == '-' then
            if L2 == '0' then
                d = "-"
            end
            if L2 == '-' then
                d = "+-"
            end
        elseif L1 == '+' then
            if L2 == '0' then
                d = "+"
            elseif L2 == '-' then
                d = "0"
            elseif L2 == '+' then
                d = "-+"
            end
        elseif L1 == '0' then
            if L2 == '0' then
                d = "0"
            end
        end

        local ob1 = add(c1,d:sub(2,2))
        local ob2 = add(ob1,c2)

        out = ob2 .. d:sub(1,1)
    elseif b1 ~= "" then
        out = b1
    elseif b2 ~= "" then
        out = b2
    else
        out = ""
    end

    return out
end

function unary_minus(b)
    local out = ""

    for i=1, b:len() do
        local c = b:sub(i,i)
        if c == '-' then
            out = out .. '+'
        elseif c == '+' then
            out = out .. '-'
        else
            out = out .. c
        end
    end

    return out
end

function subtract(b1,b2)
    return add(b1, unary_minus(b2))
end

function mult(b1,b2)
    local r = "0"
    local c1 = b1
    local c2 = b2:reverse()

    for i=1,c2:len() do
        local c = c2:sub(i,i)
        if c == '+' then
            r = add(r, c1)
        elseif c == '-' then
            r = subtract(r, c1)
        end
        c1 = c1 .. '0'
    end

    while r:sub(1,1) == '0' do
        r = r:sub(2)
    end

    return r
end

function main()
    local a = "+-0++0+"
    local b = to_bt(-436)
    local c = "+-++-"
    local d = mult(a, subtract(b, c))

    print(string.format("      a: %14s %10d", a, from_bt(a)))
    print(string.format("      b: %14s %10d", b, from_bt(b)))
    print(string.format("      c: %14s %10d", c, from_bt(c)))
    print(string.format("a*(b-c): %14s %10d", d, from_bt(d)))
end

main()


import strformat
import tables

type

  # Trit definition.
  Trit = range[-1'i8..1'i8]

  # Balanced ternary number as a sequence of trits stored in little endian way.
  BTernary = seq[Trit]

const

  # Textual representation of trits.
  Trits: array[Trit, char] = ['-', '0', '+']

  # Symbolic names used for trits.
  TN = Trit(-1)
  TZ = Trit(0)
  TP = Trit(1)

  # Table to convert the result of classic addition to balanced ternary numbers.
  AddTable = {-2: @[TP, TN], -1: @[TN], 0: @[TZ], 1: @[TP], 2: @[TN, TP]}.toTable()

  # Mapping from modulo to trits (used for conversion from int to balanced ternary).
  ModTrits: array[-2..2, Trit] = [TP, TN, TZ, TP, TN]

#---------------------------------------------------------------------------------------------------

func normalize(bt: var BTernary) =
  ## Remove the extra zero trits at head of a BTernary number.
  var i = bt.high
  while i >= 0 and bt[i] == 0:
    dec i
  bt.setlen(if i < 0: 1 else: i + 1)

#---------------------------------------------------------------------------------------------------

func `+`*(a, b: BTernary): BTernary =
  ## Add two BTernary numbers.

  # Prepare operands.
  var (a, b) = (a, b)
  if a.len < b.len:
    a.setLen(b.len)
  else:
    b.setLen(a.len)

  # Perform addition trit per trit.
  var carry = TZ
  for i in 0..<a.len:
    var s = AddTable[a[i] + b[i]]
    if carry != TZ:
      s = s + @[carry]
    carry = if s.len > 1: s[1] else: TZ
    result.add(s[0])

  # Append the carry to the result if it is not null.
  if carry != TZ:
    result.add(carry)

#---------------------------------------------------------------------------------------------------

func `+=`*(a: var BTernary; b: BTernary) {.inline.} =
  ## Increment a BTernary number.
  a = a + b

#---------------------------------------------------------------------------------------------------

func `-`(a: BTernary): BTernary =
  ## Negate a BTernary number.
  result.setLen(a.len)
  for i, t in a:
    result[i] = -t

#---------------------------------------------------------------------------------------------------

func `-`*(a, b: BTernary): BTernary {.inline.} =
  ## Subtract a BTernary number to another.
  a + -b

#---------------------------------------------------------------------------------------------------

func `-=`*(a: var BTernary; b: BTernary) {.inline.} =
  ## Decrement a BTernary number.
  a = a + -b

#---------------------------------------------------------------------------------------------------

func `*`*(a, b: BTernary): BTernary =
  ## Multiply tow BTernary numbers.

  var start: BTernary
  let na = -a

  # Loop on each trit of "b" and add directly a whole row.
  for t in b:
    case t
    of TP: result += start & a
    of TZ: discard
    of TN: result += start & na
    start.add(TZ)   # Shift next row.
  result.normalize()

#---------------------------------------------------------------------------------------------------

func toTrit*(c: char): Trit =
  ## Convert a char to a trit.
  case c
  of '-': -1
  of '0': 0
  of '+': 1
  else:
    raise newException(ValueError, fmt"Invalid trit: '{c}'")

#---------------------------------------------------------------------------------------------------

func `$`*(bt: BTernary): string =
  ## Return the string representation of a BTernary number.
  result.setLen(bt.len)
  for i, t in bt:
    result[^(i + 1)] = Trits[t]

#---------------------------------------------------------------------------------------------------

func toBTernary*(s: string): BTernary =
  ## Build a BTernary number from its string representation.
  result.setLen(s.len)
  for i, c in s:
    result[^(i + 1)] = c.toTrit()

#---------------------------------------------------------------------------------------------------

func toInt*(bt: BTernary): int =
  ## Convert a BTernary number to an integer.
  ## An overflow error is raised if the result cannot fit in an integer.
  var m = 1
  for t in bt:
    result += m * t
    m *= 3

#---------------------------------------------------------------------------------------------------

func toBTernary(val: int): BTernary =
  ## Convert an integer to a BTernary number.
  var val = val
  while true:
    let trit = ModTrits[val mod 3]
    result.add(trit)
    val = (val - trit) div 3
    if val == 0:
      break

#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:

  let a = "+-0++0+".toBTernary
  let b = -436.toBTernary
  let c = "+-++-".toBTernary

  echo "Balanced ternary numbers:"
  echo fmt"a = {a}"
  echo fmt"b = {b}"
  echo fmt"c = {c}"
  echo ""

  echo "Their decimal representation:"
  echo fmt"a = {a.toInt: 4d}"
  echo fmt"b = {b.toInt: 4d}"
  echo fmt"c = {c.toInt: 4d}"
  echo ""

  let x = a * (b - c)
  echo "a × (b - c):"
  echo fmt"– in ternary: {x}"
  echo fmt"– in decimal: {x.toInt}"


type btdigit = Pos | Zero | Neg
type btern = btdigit list

let to_string n =
   String.concat ""
      (List.rev_map (function Pos -> "+" | Zero -> "0" | Neg -> "-") n)

let from_string s =
   let sl = ref [] in
   let digit = function '+' -> Pos | '-' -> Neg | '0' -> Zero
     | _ -> failwith "invalid digit" in
    String.iter (fun c -> sl := (digit c) :: !sl) s; !sl

let rec to_int = function
   | [Zero] | [] -> 0
   | Pos :: t -> 1 + 3 * to_int t
   | Neg :: t -> -1 + 3 * to_int t
   | Zero :: t -> 3 * to_int t

let rec from_int n =
   if n = 0 then [] else
   match n mod 3 with
      | 0 -> Zero :: from_int (n/3)
      | 1 | -2 -> Pos :: from_int ((n-1)/3)
      | 2 | -1 -> Neg :: from_int ((n+1)/3)

let rec (+~) n1 n2 = match (n1,n2) with
   | ([], a) | (a,[]) -> a
   | (Pos::t1, Neg::t2) | (Neg::t1, Pos::t2) | (Zero::t1, Zero::t2) ->
      let sum = t1 +~ t2 in if sum = [] then [] else Zero :: sum
   | (Pos::t1, Pos::t2) -> Neg :: t1 +~ t2 +~ [Pos]
   | (Neg::t1, Neg::t2) -> Pos :: t1 +~ t2 +~ [Neg]
   | (Zero::t1, h::t2) | (h::t1, Zero::t2) -> h :: t1 +~ t2

let neg = List.map (function Pos -> Neg | Neg -> Pos | Zero -> Zero)
let (-~) a b = a +~ (neg b)

let rec ( *~) n1 = function
   | [] -> []
   | [Pos] -> n1
   | [Neg] -> neg n1
   | Pos::t -> (Zero :: t *~ n1) +~ n1
   | Neg::t -> (Zero :: t *~ n1) -~ n1
   | Zero::t -> Zero :: t *~ n1

let a = from_string "+-0++0+"
let b = from_int (-436)
let c = from_string "+-++-"
let d = a *~ (b -~ c)
let _ =
  Printf.printf "a = %d\nb = %d\nc = %d\na * (b - c) = %s = %d\n"
   (to_int a) (to_int b) (to_int c) (to_string d) (to_int d);


with javascript_semantics
function bt2dec(string bt)
    integer res = 0
    for i=1 to length(bt) do
        res = 3*res+(bt[i]='+')-(bt[i]='-')
    end for
    return res
end function

function negate(string bt)
    for i=1 to length(bt) do
        if bt[i]!='0' then
            bt[i] = '+'+'-'-bt[i]
        end if
    end for
    return bt
end function

function dec2bt(integer n)
    string res = "0"
    if n!=0 then
        integer neg = n<0
        if neg then n = -n end if
        res = ""
        while n!=0 do
            integer r = mod(n,3)
            res = "0+-"[r+1]&res
            n = floor((n+(r=2))/3)
        end while
        if neg then res = negate(res) end if
    end if
    return res
end function

-- res,carry for a+b+carry lookup tables (not the fastest way to do it, I'm sure):
constant {tadd,addres} = columnize({{"---","0-"},{"--0","+-"},{"--+","-0"},
                                    {"-0-","+-"},{"-00","-0"},{"-0+","00"},
                                    {"-+-","-0"},{"-+0","00"},{"-++","+0"},
                                    {"0--","+-"},{"0-0","-0"},{"0-+","00"},
                                    {"00-","-0"},{"000","00"},{"00+","+0"},
                                    {"0+-","00"},{"0+0","+0"},{"0++","-+"},
                                    {"+--","-0"},{"+-0","00"},{"+-+","+0"},
                                    {"+0-","00"},{"+00","+0"},{"+0+","-+"},
                                    {"++-","+0"},{"++0","-+"},{"+++","0+"}})

function bt_add(string a, b)
    integer padding = length(a)-length(b),
            carry = '0', ch
    if padding!=0 then
        if padding<0 then
            a = repeat('0',-padding)&a
        else
            b = repeat('0',padding)&b
        end if
    end if
    for i=length(a) to 1 by -1 do
        string cc = addres[find(a[i]&b[i]&carry,tadd)]
        a[i] = cc[1]
        carry = cc[2]
    end for
    if carry!='0' then
        a = carry&a
    else
        while length(a)>1 and a[1]='0' do
            a = a[2..$]
        end while
    end if
    return a
end function

function bt_mul(string a, string b)
    string pos = a, neg = negate(a), res = "0"
    for i=length(b) to 1 by -1 do
        integer ch = b[i]
        if ch='+' then
            res = bt_add(res,pos)
        elsif ch='-' then
            res = bt_add(res,neg)
        end if
        pos = pos&'0'
        neg = neg&'0'
    end for
    return res
end function

string a = "+-0++0+", b = dec2bt(-436), c = "+-++-",
       res = bt_mul(a,bt_add(b,negate(c)))
printf(1,"%7s:  %12s  %9d\n",{"a",a,bt2dec(a)})
printf(1,"%7s:  %12s  %9d\n",{"b",b,bt2dec(b)})
printf(1,"%7s:  %12s  %9d\n",{"c",c,bt2dec(c)})
printf(1,"%7s:  %12s  %9d\n",{"a*(b-c)",res,bt2dec(res)})


(seed (in "/dev/urandom" (rd 8)))

(setq *G '((0 -1) (1 -1) (-1 0) (0 0) (1 0) (-1 1) (0 1)))

# For humans
(de negh (L)
   (mapcar
      '((I)
         (case I
            (- '+)
            (+ '-)
            (T 0) ) )
      L ) )

(de trih (X)
   (if (num? X)
      (let (S (lt0 X)  X (abs X)  R NIL)
         (if (=0 X)
            (push 'R 0)
            (until (=0 X)
               (push 'R
                  (case (% X 3)
                     (0 0)
                     (1 '+)
                     (2 (inc 'X) '-) ) )
               (setq X (/ X 3)) ) )
         (if S (pack (negh R)) (pack R)) )
      (let M 1
         (sum
            '((C)
               (prog1
                  (unless (= C "0") ((intern C) M))
                  (setq M (* 3 M)) ) )
            (flip (chop X)) ) ) ) )

# For robots
(de neg (L)
   (mapcar
      '((I)
         (case I (-1 1) (1 -1) (T 0)) )
      L ) )

(de tri (X)
   (if (num? X)
      (let (S (lt0 X)  X (abs X)  R NIL)
         (if (=0 X)
            (push 'R 0)
            (until (=0 X)
               (push 'R
                  (case (% X 3)
                     (0 0)
                     (1 1)
                     (2 (inc 'X) (- 1)) ) )
               (setq X (/ X 3)) ) )
         (flip (if S (neg R) R)) )
      (let M 1
         (sum
            '((C)
               (prog1 (* C M) (setq M (* 3 M))) )
            X ) ) ) )

(de add (D1 D2)
   (let
      (L (max (length D1) (length D2))
         D1 (need (- L) D1 0)
         D2 (need (- L) D2 0)
         C 0 )
      (mapcon
         '((L1 L2)
            (let R
               (get
                  *G
                  (+ 4 (+ (car L1) (car L2) C)) )
               (ifn (cdr L1)
                  R
                  (setq C (cadr R))
                  (cons (car R)) ) ) )
         D1
         D2 ) ) )

(de mul (D1 D2)
   (ifn (and D1 D2)
      0
      (add
         (case (car D1)
            (0 0)
            (1 D2)
            (-1 (neg D2)) )
         (cons 0 (mul (cdr D1) D2) ) ) ) )

(de sub (D1 D2)
   (add D1 (neg D2)) )

# Random testing
(let (X 0  Y 0  C 2048)
   (do C
      (setq
         X (rand (- C) C)
         Y (rand (- C) C) )
      (test X (trih (trih X)))
      (test X (tri (tri X)))
      (test
         (+ X Y)
         (tri (add (tri X) (tri Y))) )
      (test
         (- X Y)
         (tri (sub (tri X) (tri Y))) )
      (test
         (* X Y)
         (tri (mul (tri X) (tri Y))) ) ) )

(println 'A (trih 523) (trih "+-0++0+"))
(println 'B (trih -436) (trih "-++-0--"))
(println 'C (trih 65) (trih "+-++-"))
(let R
   (tri
      (mul
         (tri (trih "+-0++0+"))
         (sub (tri -436) (tri (trih "+-++-"))) ) )
   (println 'R (trih R) R) )

(bye)


:- module('bt_convert.pl', [bt_convert/2,
			    op(950, xfx, btconv),
			    btconv/2]).

:- use_module(library(clpfd)).

:- op(950, xfx, btconv).

X btconv Y :-
	bt_convert(X, Y).

% bt_convert(?X, ?L)
bt_convert(X, L) :-
	(   (nonvar(L), \+is_list(L)) ->string_to_list(L, L1);  L1 = L),
	convert(X, L1),
	(   var(L) -> string_to_list(L, L1); true).

% map numbers toward digits +, - 0
plus_moins( 1, 43).
plus_moins(-1, 45).
plus_moins( 0, 48).

convert(X, [48| L]) :-
	var(X),
	(   L \= [] -> convert(X, L); X = 0, !).

convert(0, L) :-
	var(L), !, string_to_list(L, [48]).

convert(X, L) :-
	(   (nonvar(X), X > 0)
	;   (var(X), X #> 0,
	    L = [43|_],
	     maplist(plus_moins, L1, L))),
	!,
	convert(X, 0, [], L1),
	(   nonvar(X) -> maplist(plus_moins, L1, LL),  string_to_list(L, LL)
	;   true).

convert(X, L) :-
	(  nonvar(X) -> Y is -X
	;  X #< 0,
	   maplist(plus_moins, L2, L),
	   maplist(mult(-1), L2, L1)),
	convert(Y, 0, [], L1),
	(   nonvar(X) ->
	    maplist(mult(-1), L1, L2),
	    maplist(plus_moins, L2, LL),
            string_to_list(L, LL)
	;   X #= -Y).

mult(X, Y, Z) :-
	Z #= X * Y.

convert(0, 0, L, L) :-  !.

convert(0, 1, L, [1 | L]) :- !.

convert(N, C, LC, LF) :-
	R #= N mod 3 + C,
	R #> 1 #<==> C1,
	N1 #= N / 3,
	R1 #= R - 3 * C1, % C1 #= 1,
	convert(N1, C1, [R1 | LC], LF).


class BalancedTernary:
    # Represented as a list of 0, 1 or -1s, with least significant digit first.

    str2dig = {'+': 1, '-': -1, '0': 0} # immutable
    dig2str = {1: '+', -1: '-', 0: '0'} # immutable
    table = ((0, -1), (1, -1), (-1, 0), (0, 0), (1, 0), (-1, 1), (0, 1)) # immutable

    def __init__(self, inp):
        if isinstance(inp, str):
            self.digits = [BalancedTernary.str2dig[c] for c in reversed(inp)]
        elif isinstance(inp, int):
            self.digits = self._int2ternary(inp)
        elif isinstance(inp, BalancedTernary):
            self.digits = list(inp.digits)
        elif isinstance(inp, list):
            if all(d in (0, 1, -1) for d in inp):
                self.digits = list(inp)
            else:
                raise ValueError("BalancedTernary: Wrong input digits.")
        else:
            raise TypeError("BalancedTernary: Wrong constructor input.")

    @staticmethod
    def _int2ternary(n):
        if n == 0: return []
        if (n % 3) == 0: return [0] + BalancedTernary._int2ternary(n // 3)
        if (n % 3) == 1: return [1] + BalancedTernary._int2ternary(n // 3)
        if (n % 3) == 2: return [-1] + BalancedTernary._int2ternary((n + 1) // 3)

    def to_int(self):
        return reduce(lambda y,x: x + 3 * y, reversed(self.digits), 0)

    def __repr__(self):
        if not self.digits: return "0"
        return "".join(BalancedTernary.dig2str[d] for d in reversed(self.digits))

    @staticmethod
    def _neg(digs):
        return [-d for d in digs]

    def __neg__(self):
        return BalancedTernary(BalancedTernary._neg(self.digits))

    @staticmethod
    def _add(a, b, c=0):
        if not (a and b):
            if c == 0:
                return a or b
            else:
                return BalancedTernary._add([c], a or b)
        else:
            (d, c) = BalancedTernary.table[3 + (a[0] if a else 0) + (b[0] if b else 0) + c]
            res = BalancedTernary._add(a[1:], b[1:], c)
            # trim leading zeros
            if res or d != 0:
                return [d] + res
            else:
                return res

    def __add__(self, b):
        return BalancedTernary(BalancedTernary._add(self.digits, b.digits))

    def __sub__(self, b):
        return self + (-b)

    @staticmethod
    def _mul(a, b):
        if not (a and b):
            return []
        else:
            if   a[0] == -1: x = BalancedTernary._neg(b)
            elif a[0] ==  0: x = []
            elif a[0] ==  1: x = b
            else: assert False
            y = [0] + BalancedTernary._mul(a[1:], b)
            return BalancedTernary._add(x, y)

    def __mul__(self, b):
        return BalancedTernary(BalancedTernary._mul(self.digits, b.digits))

def main():
    a = BalancedTernary("+-0++0+")
    print "a:", a.to_int(), a

    b = BalancedTernary(-436)
    print "b:", b.to_int(), b

    c = BalancedTernary("+-++-")
    print "c:", c.to_int(), c

    r = a * (b - c)
    print "a * (b - c):", r.to_int(), r

main()


#lang racket

;; Represent a balanced-ternary number as a list of 0's, 1's and -1's.
;;
;; e.g. 11 = 3^2 + 3^1 - 3^0 ~ "++-" ~ '(-1 1 1)
;;       6 = 3^2 - 3^1       ~ "+-0" ~ '(0 -1 1)
;;
;; Note: the list-rep starts with the least signifcant tert, while
;;       the string-rep starts with the most significsnt tert.

(define (bt->integer t)
  (if (null? t)
      0
      (+ (first t) (* 3 (bt->integer (rest t))))))

(define (integer->bt n)
  (letrec ([recur (λ (b r) (cons b (convert (floor (/ r 3)))))]
           [convert (λ (n) (if (zero? n) null
                               (case (modulo n 3)
                                 [(0) (recur 0 n)]
                                 [(1) (recur 1 n)]
                                 [(2) (recur -1 (add1 n))])))])
    (convert n)))

(define (bt->string t)
  (define (strip-leading-zeroes a)
    (if (or (null? a) (not (= (first a) 0))) a (strip-leading-zeroes (rest a))))
  (string-join (map (λ (u)
                      (case u
                        [(1) "+"]
                        [(-1) "-"]
                        [(0) "0"]))
                    (strip-leading-zeroes (reverse t))) ""))

(define (string->bt s)
  (reverse
   (map (λ (c)
          (case c
            [(#\+) 1]
            [(#\-) -1]
            [(#\0) 0]))
        (string->list s))))

(define (bt-negate t)
  (map (λ (u) (- u)) t))

(define (bt-add a b [c 0])
  (cond [(and (null? a) (null? b)) (if (zero? c) null (list c))]
        [(null? b) (if (zero? c) a (bt-add a (list c)))]
        [(null? a) (bt-add b a c)]
        [else (let* ([t (+ (first a) (first b) c)]
                     [carry (if (> (abs t) 1) (sgn t) 0)]
                     [v (case (abs t)
                          [(3) 0]
                          [(2) (- (sgn t))]
                          [else t])])
                (cons v (bt-add (rest a) (rest b) carry)))]))

(define (bt-multiply a b)
  (cond [(null? a) null]
        [(null? b) null]
        [else (bt-add (case (first a)
                        [(-1) (bt-negate b)]
                        [(0) null]
                        [(1) b])
                      (cons 0 (bt-multiply (rest a) b)))]))

; test case
(let* ([a (string->bt "+-0++0+")]
       [b (integer->bt -436)]
       [c (string->bt "+-++-")]
       [d (bt-multiply a (bt-add b (bt-negate c)))])
  (for ([bt (list a b c d)]
        [description (list 'a 'b 'c "a×(b−c)")])
    (printf "~a = ~a or ~a\n" description (bt->integer bt) (bt->string bt))))


class BT {
    has @.coeff;

    my %co2bt = '-1' => '-', '0' => '0', '1' => '+';
    my %bt2co = %co2bt.invert;

    multi method new (Str $s) {
	self.bless(coeff => %bt2co{$s.flip.comb});
    }
    multi method new (Int $i where $i >= 0) {
	self.bless(coeff => carry $i.base(3).comb.reverse);
    }
    multi method new (Int $i where $i < 0) {
	self.new(-$i).neg;
    }

    method Str () { %co2bt{@!coeff}.join.flip }
    method Int () { [+] @!coeff Z* (1,3,9...*) }

    multi method neg () {
	self.new: coeff => carry self.coeff X* -1;
    }
}

sub carry (*@digits is copy) {
    loop (my $i = 0; $i < @digits; $i++) {
	while @digits[$i] < -1 { @digits[$i] += 3; @digits[$i+1]--; }
	while @digits[$i] > 1  { @digits[$i] -= 3; @digits[$i+1]++; }
    }
    pop @digits while @digits and not @digits[*-1];
    @digits;
}

multi prefix:<-> (BT $x) { $x.neg }

multi infix:<+> (BT $x, BT $y) {
    my ($b,$a) = sort +*.coeff, ($x, $y);
    BT.new: coeff => carry ($a.coeff Z+ |$b.coeff, |(0 xx $a.coeff - $b.coeff));
}

multi infix:<-> (BT $x, BT $y) { $x + $y.neg }

multi infix:<*> (BT $x, BT $y) {
    my @x = $x.coeff;
    my @y = $y.coeff;
    my @z = 0 xx @x+@y-1;
    my @safe;
    for @x -> $xd {
	@z = @z Z+ |(@y X* $xd), |(0 xx @z-@y);
	@safe.push: @z.shift;
    }
    BT.new: coeff => carry @safe, @z;
}

my $a = BT.new: "+-0++0+";
my $b = BT.new: -436;
my $c = BT.new: "+-++-";
my $x = $a * ( $b - $c );

say 'a == ', $a.Int;
say 'b == ', $b.Int;
say 'c == ', $c.Int;
say "a × (b − c) == ", ~$x, ' == ', $x.Int;


/*REXX program converts decimal  ◄───►  balanced ternary;  it also performs arithmetic. */
numeric digits 10000                             /*be able to handle  gihugic  numbers. */
Ao = '+-0++0+' ;    Abt =      Ao                /*   [↓]  2 literals used by subroutine*/
Bo =    '-436' ;    Bbt = d2bt(Bo);                    @ = "(decimal)"
Co =   '+-++-' ;    Cbt =      Co ;                   @@ = "balanced ternary ="
                  call btShow  '[a]',       Abt
                  call btShow  '[b]',       Bbt
                  call btShow  '[c]',       Cbt
                  say;                      $bt = btMul(Abt, btSub(Bbt, Cbt) )
                  call btShow '[a*(b-c)]',  $bt
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
d2bt: procedure; parse arg x 1;  x= x / 1;    p= 0;  $.= '-';   $.1= "+";   $.0= 0;     #=
                    do  until x==0;           _= (x // (3** (p+1) ) )  %  3**p
                    if _== 2  then _= -1
                              else if _== -2  then _= 1
                    x= x  -  _ * (3**p);      p= p + 1;                     #= $._  ||  #
                    end   /*until*/;          return #
/*──────────────────────────────────────────────────────────────────────────────────────*/
bt2d: procedure; parse arg x;  r= reverse(x);  $.= -1;  $.0= 0;  #= 0;    _= '+';   $._= 1
                    do j=1  for length(x);  _= substr(r, j, 1);  #= #  +  $._ * 3 ** (j-1)
                    end   /*j*/;                         return #
/*──────────────────────────────────────────────────────────────────────────────────────*/
btAdd: procedure; parse arg x,y;    rx= reverse(x);      ry= reverse(y);          carry= 0
       @.= 0;   _= '-';   @._= -1;   _= "+";  @._= 1;  $.= '-';   $.0= 0;   $.1= "+";   #=
                                           do j=1  for max( length(x), length(y) )
                                           x_= substr(rx, j, 1);            xn= @.x_
                                           y_= substr(ry, j, 1);            yn= @.y_
                                           s= xn + yn + carry;           carry= 0
                                           if s== 2  then do;   s=-1;    carry= 1;    end
                                           if s== 3  then do;   s= 0;    carry= 1;    end
                                           if s==-2  then do;   s= 1;    carry=-1;    end
                                           #= $.s || #
                                           end   /*j*/
       if carry\==0  then #= $.carry || #;                      return btNorm(#)
/*──────────────────────────────────────────────────────────────────────────────────────*/
btMul: procedure; parse arg x 1 x1 2, y 1 y1 2; if x==0 | y==0  then return 0;  S= 1;  P=0
       x= btNorm(x); y= btNorm(y); Lx= length(x); Ly= length(y)  /*handle: 0-xxx values.*/
       if x1=='-'  then do;   x= btNeg(x);   S= -S;   end        /*positate the number. */
       if y1=='-'  then do;   y= btNeg(y);   S= -S;   end        /*    "     "    "     */
       if Ly>Lx    then parse value  x y  with  y x              /*optimize  "    "     */
                                             do   until  y==0    /*keep adding 'til done*/
                                             P= btAdd(P,  x )    /*multiple the hard way*/
                                             y= btSub(y, '+')    /*subtract  1  from  Y.*/
                                             end   /*until*/
       if S==-1  then P= btNeg(P);  return P       /*adjust the product's sign;  return.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
btNeg:  return translate( arg(1), '-+', "+-")                    /*negate bal_ternary #.*/
btNorm: _= strip(arg(1), 'L', 0);  if _==''  then _=0;  return _ /*normalize the number.*/
btSub:  return btAdd( arg(1), btNeg( arg(2) ) )                  /*subtract two BT args.*/
btShow: say center( arg(1), 9)  right( arg(2), 20)  @@  right( bt2d(arg(2)), 9) @;  return


use std::{
    cmp::min,
    convert::{TryFrom, TryInto},
    fmt,
    ops::{Add, Mul, Neg},
    str::FromStr,
};

fn main() -> Result<(), &'static str> {
    let a = BalancedTernary::from_str("+-0++0+")?;
    let b = BalancedTernary::from(-436);
    let c = BalancedTernary::from_str("+-++-")?;
    println!("a = {} = {}", a, i128::try_from(a.clone())?);
    println!("b = {} = {}", b, i128::try_from(b.clone())?);
    println!("c = {} = {}", c, i128::try_from(c.clone())?);

    let d = a * (b + -c);
    println!("a * (b - c) = {} = {}", d, i128::try_from(d.clone())?);

    let e = BalancedTernary::from_str(
        "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
    )?;
    assert_eq!(i128::try_from(e).is_err(), true);

    Ok(())
}

#[derive(Clone, Copy, PartialEq)]
enum Trit {
    Zero,
    Pos,
    Neg,
}

impl TryFrom<char> for Trit {
    type Error = &'static str;

    fn try_from(value: char) -> Result<Self, Self::Error> {
        match value {
            '0' => Ok(Self::Zero),
            '+' => Ok(Self::Pos),
            '-' => Ok(Self::Neg),
            _ => Err("Invalid character for balanced ternary"),
        }
    }
}

impl From<Trit> for char {
    fn from(x: Trit) -> Self {
        match x {
            Trit::Zero => '0',
            Trit::Pos => '+',
            Trit::Neg => '-',
        }
    }
}

impl Add for Trit {
    // (Carry, Current)
    type Output = (Self, Self);

    fn add(self, rhs: Self) -> Self::Output {
        use Trit::{Neg, Pos, Zero};
        match (self, rhs) {
            (Zero, x) | (x, Zero) => (Zero, x),
            (Pos, Neg) | (Neg, Pos) => (Zero, Zero),
            (Pos, Pos) => (Pos, Neg),
            (Neg, Neg) => (Neg, Pos),
        }
    }
}

impl Mul for Trit {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        use Trit::{Neg, Pos, Zero};
        match (self, rhs) {
            (Zero, _) | (_, Zero) => Zero,
            (Pos, Pos) | (Neg, Neg) => Pos,
            (Pos, Neg) | (Neg, Pos) => Neg,
        }
    }
}

impl Neg for Trit {
    type Output = Self;

    fn neg(self) -> Self::Output {
        match self {
            Trit::Zero => Trit::Zero,
            Trit::Pos => Trit::Neg,
            Trit::Neg => Trit::Pos,
        }
    }
}

// The vector is stored in reverse from how it would be viewed, as
// operations tend to work backwards
#[derive(Clone)]
struct BalancedTernary(Vec<Trit>);

impl fmt::Display for BalancedTernary {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            self.0
                .iter()
                .rev()
                .map(|&d| char::from(d))
                .collect::<String>()
        )
    }
}

impl Add for BalancedTernary {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        use Trit::Zero;

        // Trim leading zeroes
        fn trim(v: &mut Vec<Trit>) {
            while let Some(last_elem) = v.pop() {
                if last_elem != Zero {
                    v.push(last_elem);
                    break;
                }
            }
        }

        if rhs.0.is_empty() {
            // A balanced ternary shouldn't be empty
            if self.0.is_empty() {
                return BalancedTernary(vec![Zero]);
            }
            return self;
        }

        let length = min(self.0.len(), rhs.0.len());
        let mut sum = Vec::new();
        let mut carry = vec![Zero];

        for i in 0..length {
            let (carry_dig, digit) = self.0[i] + rhs.0[i];
            sum.push(digit);
            carry.push(carry_dig);
        }
        // At least one of these two loops will be ignored
        for i in length..self.0.len() {
            sum.push(self.0[i]);
        }
        for i in length..rhs.0.len() {
            sum.push(rhs.0[i]);
        }

        trim(&mut sum);
        trim(&mut carry);

        BalancedTernary(sum) + BalancedTernary(carry)
    }
}

// This version of `Mul` requires an implementation of the `Add` trait
impl Mul for BalancedTernary {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        let mut results = Vec::with_capacity(rhs.0.len());
        for i in 0..rhs.0.len() {
            let mut digits = vec![Trit::Zero; i];
            for j in 0..self.0.len() {
                digits.push(self.0[j] * rhs.0[i]);
            }
            results.push(BalancedTernary(digits));
        }
        #[allow(clippy::suspicious_arithmetic_impl)]
        results
            .into_iter()
            .fold(BalancedTernary(vec![Trit::Zero]), |acc, x| acc + x)
    }
}

impl Neg for BalancedTernary {
    type Output = Self;

    fn neg(self) -> Self::Output {
        BalancedTernary(self.0.iter().map(|&x| -x).collect())
    }
}

impl FromStr for BalancedTernary {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        s.chars()
            .rev()
            .map(|c| c.try_into())
            .collect::<Result<_, _>>()
            .map(BalancedTernary)
    }
}

impl From<i128> for BalancedTernary {
    fn from(x: i128) -> Self {
        let mut v = Vec::new();
        let mut curr = x;

        loop {
            let rem = curr % 3;

            match rem {
                0 => v.push(Trit::Zero),
                1 | -2 => v.push(Trit::Pos),
                2 | -1 => v.push(Trit::Neg),
                _ => unreachable!(),
            }

            let offset = (rem as f64 / 3.0).round() as i128;
            curr = curr / 3 + offset;

            if curr == 0 {
                break;
            }
        }

        BalancedTernary(v)
    }
}

impl TryFrom<BalancedTernary> for i128 {
    type Error = &'static str;

    fn try_from(value: BalancedTernary) -> Result<Self, Self::Error> {
        value
            .0
            .iter()
            .enumerate()
            .try_fold(0_i128, |acc, (i, character)| {
                let size_err = "Balanced ternary string is too large to fit into 16 bytes";
                let index: u32 = i.try_into().map_err(|_| size_err)?;

                match character {
                    Trit::Zero => Ok(acc),
                    Trit::Pos => 3_i128
                        .checked_pow(index)
                        .and_then(|x| acc.checked_add(x))
                        .ok_or(size_err),
                    Trit::Neg => 3_i128
                        .checked_pow(index)
                        .and_then(|x| acc.checked_sub(x))
                        .ok_or(size_err),
                }
            })
    }
}


object TernaryBit {
    val P = TernaryBit(+1)
    val M = TernaryBit(-1)
    val Z = TernaryBit( 0)

  implicit def asChar(t: TernaryBit): Char = t.charValue
  implicit def valueOf(c: Char): TernaryBit = {
    c match {
      case '0' => 0
      case '+' => 1
      case '-' => -1
      case nc => throw new IllegalArgumentException("Illegal ternary symbol " + nc)
    }
  }
  implicit def asInt(t: TernaryBit): Int = t.intValue
  implicit def valueOf(i: Int): TernaryBit = TernaryBit(i)
}

case class TernaryBit(val intValue: Int) {

    def inverse: TernaryBit = TernaryBit(-intValue)

    def charValue = intValue match {
      case  0 => '0'
      case  1 => '+'
      case -1 => '-'
    }
}

class Ternary(val bits: List[TernaryBit]) {

  def + (b: Ternary) = {
    val sumBits: List[Int] = bits.map(_.intValue).zipAll(b.bits.map(_.intValue), 0, 0).map(p => p._1 + p._2)

    // normalize
    val iv: Tuple2[List[Int], Int] = (List(), 0)
    val (revBits, carry) = sumBits.foldLeft(iv)((accu: Tuple2[List[Int], Int], e: Int) => {
      val s = e + accu._2
      (((s + 1 + 3 * 100) % 3 - 1) :: accu._1 , (s + 1 + 3 * 100) / 3 - 100)
    })

    new Ternary(( TernaryBit(carry) :: revBits.map(TernaryBit(_))).reverse )
  }

  def - (b: Ternary) = {this + (-b)}
  def <<<(a: Int): Ternary = { List.fill(a)(TernaryBit.Z) ++ bits}
  def >>>(a: Int): Ternary = { bits.drop(a) }
  def unary_- = { bits.map(_.inverse) }

  def ** (b: TernaryBit): Ternary = {
    b match {
      case TernaryBit.P => this
      case TernaryBit.M => - this
      case TernaryBit.Z => 0
    }
  }

  def * (mul: Ternary): Ternary = {
    // might be done more efficiently - perform normalize only once
    mul.bits.reverse.foldLeft(new Ternary(Nil))((a: Ternary, b: TernaryBit) => (a <<< 1) + (this ** b))
  }

  def intValue = bits.foldRight(0)((c, a) => a*3 + c.intValue)

  override def toString = new String(bits.reverse.map(_.charValue).toArray)
}

object Ternary {

  implicit def asString(t: Ternary): String = t.toString()
  implicit def valueOf(s: String): Ternary = new Ternary(s.toList.reverse.map(TernaryBit.valueOf(_)))

  implicit def asBits(t: Ternary): List[TernaryBit] = t.bits
  implicit def valueOf(l: List[TernaryBit]): Ternary = new Ternary(l)

  implicit def asInt(t: Ternary): BigInt = t.intValue
  // XXX not tail recursive
  implicit def valueOf(i: BigInt): Ternary = {
    if (i < 0) -valueOf(-i)
    else if (i == 0) new Ternary(List())
    else if (i % 3 == 0) TernaryBit.Z :: valueOf(i / 3)
    else if (i % 3 == 1) TernaryBit.P :: valueOf(i / 3)
    else /*(i % 3 == 2)*/ TernaryBit.M :: valueOf((i + 1)  / 3)
  }
  implicit def intToTernary(i: Int): Ternary = valueOf(i)
}


package require Tcl 8.5

proc bt-int b {
    set n 0
    foreach c [split $b ""] {
	set n [expr {$n * 3}]
	switch -- $c {
	    + { incr n 1  }
	    - { incr n -1 }
	}
    }
    return $n
}
proc int-bt n {
    if {$n == 0} {
	return "0"
    }
    while {$n != 0} {
	lappend result [lindex {0 + -} [expr {$n % 3}]]
	set n [expr {$n / 3 + ($n%3 == 2)}]
    }
    return [join [lreverse $result] ""]
}

proc bt-neg b {
    string map {+ - - +} $b
}
proc bt-sub {a b} {
    bt-add $a [bt-neg $b]
}
proc bt-add-digits {a b c} {
    if {$a eq ""} {set a 0}
    if {$b eq ""} {set b 0}
    if {$a ne 0} {append a 1}
    if {$b ne 0} {append b 1}
    lindex {{0 -1} {+ -1} {- 0} {0 0} {+ 0} {- 1} {0 1}} [expr {$a+$b+$c+3}]
}
proc bt-add {a b} {
    set c 0
    set result {}
    foreach ca [lreverse [split $a ""]] cb [lreverse [split $b ""]] {
	lassign [bt-add-digits $ca $cb $c] d c
	lappend result $d
    }
    if {$c ne "0"} {lappend result [lindex {0 + -} $c]}
    if {![llength $result]} {return "0"}
    string trimleft [join [lreverse $result] ""] 0
}
proc bt-mul {a b} {
    if {$a eq "0" || $a eq "" || $b eq "0"} {return "0"}
    set sub [bt-mul [string range $a 0 end-1] $b]0
    switch -- [string index $a end] {
	0 { return $sub }
	+ { return [bt-add $sub $b] }
	- { return [bt-sub $sub $b] }
    }
}