#!/usr/bin/python3 """Print balanced nonary multiplication etc. tables. Balanced ternary (see also bt.py) is an appealing and entertaining base with similar advantages and disadvantages to binary. Both are positional-value systems, and in both systems, the values of the positions are powers of a single base (2⁰, 2¹, 2², etc., in binary, and 3⁰, 3¹, 3², etc., in balanced ternary). In binary, the digits that can appear in a place have the values 0 and 1, while in balanced ternary, they have the values -1, 0, and 1 (commonly written as ‘-’, ‘0’, and ‘+’). Arithmetic is much simpler in both systems than in, for example, decimal, vigesimal, or sexagesimal. And, in both systems, the number of digits needed to write numbers is unreasonably large, with a corresponding increase in calculational effort; 4755 is ‘+-+---0+0’ in balanced ternary or ‘1001010010011’ in binary. In base 36, using the English letters as 26 additional digits, it’s just ‘3o3’, while in base 94, using the printable ASCII characters as 94 digits, it’s just ‘SX’. There’s a tradeoff visible here, where larger bases require shorter strings of symbols to represent the same number, but require handling a larger vocabulary of digits, which may have some kind of compensating disadvantage. For example, if you’re using it to design circuits, you might have a memory element that includes one switching element that turns on for each possible digit, turning the other parts of the memory element off; in binary, this is called a “flip-flop”, and they were originally built from two vacuum tubes. ENIAC was built in a ridiculously inefficient fashion that used ten such vacuum tubes for each decimal digit. In cases like these, ternary (balanced or otherwise) is an optimal base, because the number of switching elements needed to thus represent numbers up to N in base B is B ⌈(ln N)/ln B⌉, which is close to (B/ln B) ln N when N gets large. And it turns out that the minimum of (B/ln B) is when B = e = (e/ln e) ≈ 2.7182818. 2/ln 2 = 4/ln 4 ≈ 2.885, while 3/ln 3 ≈ 2.731, and so you need about 5.4% less switching elements to store numbers in balanced ternary than in binary. And this is the reason the Soviet computer Setun was built with balanced ternary. One of the key advantages of binary is that the addition and multiplication tables are very simple. The multiplication table, for example, looks like this: · 0 1 0 0 0 1 0 1 Balanced ternary is only slightly more complicated: · - 0 + - + 0 - 0 0 0 0 + - 0 + Compare either of these to the Lovecraftian horror of the decimal (or, bizarrely, duodecimal) multiplication tables we memorized in our childhoods. To deal with the unwieldy long strings of binary, we usually write binary numbers in octal or hexadecimal form for human examination; each octal digit corresponds precisely to three bits, and each hexadecimal digit corresponds to four. So 4755, 1001010010011 in binary, can be broken into three-bit groups as 1 001 010 010 011 — octal 11223 — or four-bit groups as 1 0010 1001 0011 — hex 1293. Similarly, we can group balanced ternary into groups of two trits and represent them as balanced nonary, with place values of 9⁰, 9¹, 9², etc., and digit values of -4, -3, -2, -1, 0, 1, 2, 3, and 4. I suggest the following glyphs, which conveniently already exist in Unicode thanks to compatibility with a forgotten 1980s teletext system called Videotex Mosaic: ┌ 2 ┬ 3 ┐ 4 ├ -1 ┼ 0 ┤ 1 └ -4 ┴ -3 ┘ -2 These glyphs have the pleasant property that negation corresponds to rotating the glyph 180°. In this system, if we use the conventional ordering, 4755 is “┤┘└┴┬”, which is perhaps even simpler to write than “4755”, despite being a digit longer. This program generates the following tables for balanced nonary arithmetic, covering multiplication, maximum, addition, and subtraction: · └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ ∧ └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ + └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ - └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ └ ┌┘ ┤┬ ┤├ ┐ ┼ └ ├┤ ├┴ ┘┌ └ └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ └ ├┤ ├┌ ├┬ ├┐ └ ┴ ┘ ├ ┼ └ ┼ ├ ┘ ┴ └ ├┐ ├┬ ├┌ ├┤ ┴ ┤┬ ┤┼ ┤┴ ┬ ┼ ┴ ├┬ ├┼ ├┴ ┴ ┴ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ ┴ ├┌ ├┬ ├┐ └ ┴ ┘ ├ ┼ ┤ ┴ ┤ ┼ ├ ┘ ┴ └ ├┐ ├┬ ├┌ ┘ ┤├ ┤┴ ┐ ┌ ┼ ┘ └ ├┬ ├┤ ┘ ┘ ┘ ┘ ├ ┼ ┤ ┌ ┬ ┐ ┘ ├┬ ├┐ └ ┴ ┘ ├ ┼ ┤ ┌ ┘ ┌ ┤ ┼ ├ ┘ ┴ └ ├┐ ├┬ ├ ┐ ┬ ┌ ┤ ┼ ├ ┘ ┴ └ ├ ├ ├ ├ ├ ┼ ┤ ┌ ┬ ┐ ├ ├┐ └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ├ ┬ ┌ ┤ ┼ ├ ┘ ┴ └ ├┐ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┼ ┤ ┌ ┬ ┐ ┼ └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ ┼ ┐ ┬ ┌ ┤ ┼ ├ ┘ ┴ └ ┤ └ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ ┤ ┤ ┤ ┤ ┤ ┤ ┤ ┌ ┬ ┐ ┤ ┴ ┘ ├ ┼ ┤ ┌ ┬ ┐ ┤└ ┤ ┤└ ┐ ┬ ┌ ┤ ┼ ├ ┘ ┴ ┌ ├┤ ├┬ └ ┘ ┼ ┌ ┐ ┤┴ ┤├ ┌ ┌ ┌ ┌ ┌ ┌ ┌ ┌ ┬ ┐ ┌ ┘ ├ ┼ ┤ ┌ ┬ ┐ ┤└ ┤┴ ┌ ┤┴ ┤└ ┐ ┬ ┌ ┤ ┼ ├ ┘ ┬ ├┴ ├┼ ├┬ ┴ ┼ ┬ ┤┴ ┤┼ ┤┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┐ ┬ ├ ┼ ┤ ┌ ┬ ┐ ┤└ ┤┴ ┤┘ ┬ ┤┘ ┤┴ ┤└ ┐ ┬ ┌ ┤ ┼ ├ ┐ ┘┌ ├┴ ├┤ └ ┼ ┐ ┤├ ┤┬ ┌┘ ┐ ┐ ┐ ┐ ┐ ┐ ┐ ┐ ┐ ┐ ┐ ┼ ┤ ┌ ┬ ┐ ┤└ ┤┴ ┤┘ ┤├ ┐ ┤├ ┤┘ ┤┴ ┤└ ┐ ┬ ┌ ┤ ┼ It also generates a larger multiplication table covering numbers from -13 to 13 (├└ to ┤┐). Another possible thing you might conceivably want from your number base is convenient access to properties of numbers. For example, in base 10, it’s easy to see if a number is divisible by 2, 5, or 10; just look at its last digit; and, similarly, you can determine whether it’s divisible by 20, 25, 50, and other factors of powers of 10, by looking at its last few digits. And you can tell if it’s divisible by 3 or 9 by computing its digit sum, and whether it’s divisible by 11 by adding and subtracting alternate digits. And in each of these cases, you can quickly determine not only divisibility but also the remainder from division if nonzero. This can serve as a checksum for calculations (“casting out nines”). So you might prefer a base B such that B-1, B, and B+1 have a lot of different factors, preferably small ones, so that you can do a lot of different computations here. For example, base 10 gives you prime factors 2, 3, 5, and 11; base 6 gives you 2, 3, 5, and 7; base 7 or base 8 gives you 2, 3, and 7; base 9 gives you 2, 3, and 5; base 12 gives you 2, 3, 11, and 13; base 14 gives you 2, 3, 5, 7, and 13; base 16 gives you 2, 3, 5, and 17. Nonary is rather poor here, and base 14 comes out much better. For calendrical calculations, it’s advantageous that 9³ = 729 is almost exactly the number of days in two years, as I've explored in more detail at . """ def gen_table(op, name=None, values=range(-4, 5)): return [[name if name is not None else op.__name__, *map(nonary, values)], *([nonary(i), *(nonary(op(i, j)) for j in values)] for i in values)] def nonary(n, digits='└┴┘├┼┤┌┬┐'): big = round(n/9) return (nonary(big, digits) if big else '') + digits[n - 9*big + 4] def layout(table): table = list(table) w = [max(widths) for widths in (map(len, col) for col in zip(*table))] return [' '.join(c.rjust(wi) for c, wi in zip(row, w)) for row in table] def paste(t, *ts): return ((a + [' '] + b for a, b in zip(t, paste(*ts))) if ts else t) if __name__ == '__main__': for s in (layout(paste(gen_table(lambda i, j: i*j, '·'), gen_table(max, '∧'), gen_table(lambda i, j: i+j, '+'), gen_table(lambda i, j: i-j, '-'))) + [''] + layout(gen_table(lambda i, j: i*j, '·', range(-13, 14)))): print(s)