Every processor has comparison instructions. Binary architectures give you XOR to detect differences and a subtractor to determine direction. That's two operations for two pieces of information. Balanced ternary can do better, because each trit carries three states: negative, zero, positive. The Setnex ISA exploits this with three trit-parallel primitives—CONS, ACONS, and TCMP—plus a configurable implication operator, TIMPL, that has no equivalent in binary at all.
This article explains what they do, why they form a complete toolkit, and where they could matter in practice.
Three ways to compare two trits
Consider two 27-trit words, A and B. At each position i, the trits A[i] and B[i] are drawn from {N, Z, P}. Three natural questions arise:
What do they agree on? That's CONS (consensus). If A[i] and B[i] are the same, return that value. If they differ, return Z—"no agreement."
What's missing? That's ACONS (anti-consensus). If A[i] and B[i] are the same, return Z—"nothing is missing." If they differ, return the third trit, the one that neither A nor B contributed.
Who dominates? That's TCMP (ternary compare). Return the sign of A[i] − B[i]: N if A[i] is less, Z if equal, P if A[i] is greater.
Example — five trits for readability
Notice how they partition the information differently. CONS tells you what is shared. ACONS tells you what's absent. TCMP tells you who's on top. Together, they extract every possible relation between two trit-parallel vectors in three instructions.
The Z ambiguity and why TCMP matters
CONS has a blind spot: CONS(Z, Z) = Z, but CONS(N, P) = Z too. The output Z means either "both agree on zero" or "they disagree." You can't tell which. TCMP resolves this: if TCMP gives Z, it's genuine agreement. If TCMP gives N or P, the Z from CONS was a disagreement. The two operations are not redundant—they're complementary.
The binary comparison
In binary, XOR detects differences (0 = same, 1 = different). That's a single bit of information per position. To get the direction of the difference, you need a subtractor or additional logic. There is no "absent value" concept at all—with only two states, if you know one and it's different, the other is determined.
| Question | Binary | Ternary (Setnex) |
|---|---|---|
| Same or different? | XOR (1 op) | CONS (1 op) |
| Direction of difference? | SUB + flags (2 ops) | TCMP (1 op) |
| What's missing from the pair? | N/A | ACONS (1 op) |
| Information per position | 1 bit | 1 trit (3 states) |
TIMPL: the instruction with five personalities
CONS, ACONS, and TCMP are pure arithmetic—they behave the same regardless of configuration. TIMPL is different. It computes the ternary implication A ⇒ B, and its truth table changes based on the LMODE register and STATUS.lx trit. Same opcode, same operands, five different semantics.
| A | B | Kleene | Łukasiewicz | Heyting | RM3 | B3 |
|---|---|---|---|---|---|---|
| N | N | P | P | P | P | P |
| N | Z | P | P | P | P | Z |
| N | P | P | P | P | P | P |
| Z | N | Z | Z | N | N | Z |
| Z | Z | Z | P | P | Z | Z |
| Z | P | P | P | P | P | Z |
| P | N | N | N | N | N | N |
| P | Z | Z | Z | Z | N | Z |
| P | P | P | P | P | P | P |
The highlighted cells show where the modes diverge. Five philosophies about what the indeterminate value Z means.
In Kleene mode, IMPL(Z, Z) = Z: indeterminate implies indeterminate is itself indeterminate. In Łukasiewicz, IMPL(Z, Z) = P: if the premise is "unknown" and the conclusion is "unknown," the implication holds—you can't violate a rule you can't evaluate. In Heyting, IMPL(Z, N) = N: unknown combined with false is false—the most conservative stance. In RM3, IMPL(P, Z) = N: contradictions don't propagate as truth. In B3, any contact with Z produces Z—meaninglessness is infectious.
Łukasiewicz TIMPL as an order test
In Łukasiewicz mode, TIMPL has a clean algebraic interpretation: IMPL(a, b) = min(P, −a + b + 1). The result is P if and only if a ≤ b in the natural trit ordering N < Z < P. This makes TIMPL a trit-parallel subsumption test: if every trit of the result is P, then A ≤ B at every position.
Potential applications
Triple Modular Redundancy
Three redundant systems A, B, C produce results. You need the majority vote and the identity of the faulty replica.
CONS tmp, A, B # agreement between A and B, or Z
CONS vote, tmp, C # if A=B, they win; else C decides
TCMP faultA, A, vote # nonzero trits = where A diverges
TCMP faultB, B, vote # nonzero trits = where B diverges
ACONS repair, A, B # the missing trit repairs the faulty replica
Five instructions. In binary, the majority vote alone costs five operations (3 AND + 2 OR), and identifying + repairing the fault requires additional logic. The ternary version produces the vote, the fault map, and the repair value.
Oriented diff
Two snapshots of a state vector, old and new. You want a complete change map with direction.
TCMP diff, old, new # N = decreased, Z = unchanged, P = increased
One instruction. The result is a 27-trit map where each trit tells you not just that something changed, but which way. In binary, XOR tells you where changes happened; determining direction requires a separate subtraction pass.
For monotonicity checking (e.g., "did any signal decrease?"), combine with TIMPL:
TIMPL mono, old, new # Łukasiewicz: P iff old[i] ≤ new[i]
# if all trits are P → monotone; non-P trits = violations
Capability-based access control
Each trit represents a permission: N = denied, Z = unspecified, P = granted. A user has capabilities; an operation requires capabilities.
TIMPL check, required, user_caps # Łukasiewicz mode
# check[i] = P iff required[i] ≤ user_caps[i]
# all-P → access granted
One instruction checks 27 permission dimensions simultaneously. And by switching LMODE, the same code enforces different policies:
| LMODE | Policy | IMPL(Z, Z) | Meaning |
|---|---|---|---|
| Łukasiewicz | Permissive | P | Unspecified satisfies unspecified |
| Kleene | Standard | Z | Unspecified × unspecified = uncertain |
| Heyting | Conservative | P | Same, but IMPL(Z,N) = N: gaps + denial = denied |
| RM3 | Paraconsistent | Z | Contradictory data does not grant access |
| B3 (Bochvar) | Strict | Z | Any missing data → no conclusion at all |
The policy is a register write, not a code change.
Native three-valued data
SQL's NULL handling uses Kleene three-valued logic with AND, OR, and NOT—but not implication. Every database engine simulates 3VL on binary hardware with extra NULL-check branches. A ternary processor runs it natively: Z is the NULL state, TAND and TOR in Kleene mode are the SQL truth tables, and TIMPL can express rules that SQL currently cannot.
Pattern: "for each row, does every known constraint hold?"
TIMPL valid, constraints, data # Kleene mode
# valid[i] = P if constraint holds
# = Z if either side is NULL → inconclusive
# = N if constraint is violated
Three outcomes in one operation, without branches.
The bigger picture
None of these applications are impossible in binary. Everything that runs on a ternary processor can be compiled for x86. The question is density: how many instructions does a given pattern take?
| Pattern | Binary | Ternary | Ratio |
|---|---|---|---|
| Majority vote (3 sources) | 5 ops | 2 ops | 2.5× |
| Oriented diff | 2+ passes | 1 op | 2×+ |
| TMR: vote + fault + repair | ~8 ops | 5 ops | 1.6× |
| 27-dim permission check | loops or SIMD | 1 op | structural |
| Policy change | code rewrite | 1 CSR write | structural |
The gains are modest in raw instruction count—2× to 3×, not 10×. But they're structural: patterns that require multi-pass logic in binary reduce to single-pass primitives in ternary. And TIMPL's configurable semantics via LMODE has no binary equivalent at all. You'd need different code paths, not different register values.
The honest assessment: these advantages exist on paper. No ternary processor has been built at scale since the Soviet Setun in 1958. The Setnex ISA is a specification, not silicon. Whether the instruction density gains translate to real-world performance depends on workloads, compiler quality, and the eventual hardware implementation. But the mathematical foundation is clean and the gate-level cost is low.