# Blindfolded Arithmetic Possibly a fairly easy language to start off with, relatively speaking. (There are limits on how difficult a language I can make because I have to prove it Turing-complete myself!) For this language, the program set is the set of Blindfolded Arithmetic programs as given in the section "program" below, the input set is the set of positive integers, and the output set is the set of integers (the entire set, not just the positive integers). This language is fairly interesting – maybe even practically useful – because it has a more or less total lack of control flow, being entirely based on computations on numbers you can't see. That makes it potentially useful as a language for implementing programs inside [homomorphic encryption](https://en.wikipedia.org/wiki/Homomorphic_encryption) systems. # Specification Blindfolded Arithmetic is an esoteric programming language with the following specifications: ## Data storage A state of a running Blindfolded Arithmetic program consists of six variables, each of which can store an integer. (There are no limits on how large or small these integers can get; in particular, they can go negative.) The variables are called `a`, `b`, `c`, `d`, `e`, and `i`. At the start of the program, `a` to `e` inclusive are each initialised to 0, and `i` is initialised to a positive integer taken from user input. (If no input is available, `i` is initialised to 1.) If the program ceases execution (this can only occur due to division by zero), the value of `i` immediately before the division was attempted is used as the program's output. ## Program A Blindfolded Arithmetic program is a list of commands, each of which has one of the following forms (where *v* can be replaced by any variable name, potentially a different name each time it's used): * *v* `=` *v* `+` *v* * *v* `=` *v* `-` *v* * *v* `=` *v* `*` *v* * *v* `=` *v* `/` *v* Note that each operand of the commands *must* be a variable; this language does not allow the use of literal integers. Execution of the program is performed by running each of the commands in sequence, and then looping back to the start and continuing to run the commands in sequence again, ad infinitum (or until there's an attempt to divide by zero, which ends the program). Each command has the same semantics as you'd expect from the notation, which is no different from that used by most practical programming languages: the values of the second and third variables metioned in the command are taken, an arithmetic operation (add/subtract/multiply/divide respectively for the four forms of command) is applied to them, and the resulting value stored into the first variable. Division here is integer division, rounding towards 0. There is no control flow of any kind, other than program exit; the commands always cycle in sequence, up until a division by zero (if any) occurs and ends the program. In particular, there's no way to directly read variables, only to use them as a source of calculations whose results impact the values assigned to other variables. # Computational class We can prove this language Turing-complete by translating Minsky machines into it. We'll use a formalization of Minsky machines with three counters (of which counter C contains the initial input), and the following set of commands, each of which is formed from a condition and an action: * **Conditions** * run unconditionally * run if *counter* is 1; do nothing if the value is 2 or greater; undefined behaviour if the value is 0 * **Actions** * increment *counter* * decrement *counter*; decrementing 0 or 1 is undefined behaviour * goto location X * halt, outputting the value in counter C minus the value in counter A It's fairly obvious that any Minsky machine can be written in this form (you need to start off by unconditionally incrementing counters A and B, then you simply treat a value of 1 as your "zero point" and never let the counters go lower). The limited conditionals can be made into arbitrary conditionals by conditionally running a goto command. Two counters is enough for Turing-completeness, but for noninteractive-IO-completeness (which is what the question wants), we need either three counters or a more complex I/O encoding. It's simplest to use three counters so that we don't have to pack and unpack the I/O encoding. `e` is used as the instruction pointer, pointing to the active command in the Minsky Machine program. This is initially 0, so we need to zero-index our commands (i.e. the first command is IP 0). We use only even numbers for instructions (with odd-numbered instruction pointers conceptually pointing to implicit "no-op" commands). Additionally, we make it undefined behaviour to run off the end of the program (this is not a problem as you could just put an unconditional goto there). The counter values are stored in `a` (A), `b` (B), and `i` (C). Counter C is never allowed to reach zero (except temporarily for very brief periods). `c` is used as a flag to determine if the command should run. `d` is used as a temporary. The basic idea is to write the implementation of each Minsky command in sequence, where each command is implemented such that if `e` has a particular value, it will mimic the effect of that command, and if `e` does not have that value, the implementation will do nothing. Each command starts off the same way, by setting `c` to 1 if `e` has a given value *N* and 0 otherwise: * `d = i / i` (now `d` is 1) * Do `e = e - d` *N* times (now `e` is 0 iff `e` = *N*) * `c = e + e` (now `c` is 0 iff `e` = *N* and even) * `c = c + c` (now `c` is 0 iff `e` = *N* and a multiple of 4) * `c = c + d` (now `c` is 1 iff `e` = *N*, else ≥5 or ≤-3) * `c = d / c` (now `c` is 1 iff `e` = *N*, else 0) * Do `e = e + d` *N* times (restoring it to its original value) Next, we determine if the condition matches. If the condition is "run unconditionally", we have nothing to do. Otherwise, we want to set `c` to 0 (regardless of its previous value) if the counter in question has a value greater than 1. The below is shown for counter A, but you can consistently replace `a` with `b` or `i` to get the implementation of the other two nontrivial conditions: * `d = d / a` (now `d` is 1 if `a` = 1, otherwise 0) * `c = c * d` (now `c` is 1 if both `e` = *N* and `a` = 1, otherwise 0) Most of the commands now have an obvious implementation. To increment a counter (if the right command is running and the condition if any is met), we add `c` to `a`, `b`, or `i` as appropriate. To decrement a counter, we subtract `c` from the relevant variable. To do a goto command, we repeatedly do `e = e - c` or `e = e + c` to adjust the instruction pointer by the required offset. None of these commands will do anything if `c` is 0, of course. (Note that for goto, we jump to the no-op just before the target command, so that we never end up with two commands running in the same program iteration; this isn't technically necessary but is the simplest implementation to prove correct.) To implement the fairly complex conditional halt, we do this: * `d = i / i` (now `d` is 1) * `d = d - c` (now `d` is 0 if a halt is required, 1 otherwise) * `i = i - a` (now `i` is the value we'd want to output) * `d = d / d` (division-by-0 if a halt is required, otherwise no-op) * `i = i + a` (restores the value of `i`) Note that the complexity is required so that we can output the entire range of integers, not just the positive integers, as required by the problem specification and the choice of the integers as the output set. At the end of the Blindfolded Arithmetic program, we need to move onto the next Minsky Machine command for the next iteration. That looks like this: * `d = i / i` (now `d` is 1) * `e = e + d` Note that we're only adding 1 at a time to `e`, in case we just ran a goto instruction. This means that while we aren't doing gotos, only every second iteration will do anything interesting. That isn't a problem; the next command in the program will still run eventually, and Turing-completeness proofs don't care about performance.