librarylibs/Radix/src/Radix.xtl
Radix: an eight-digit calculator in any radix (hexadecimal, octal, decimal): the specification HEX1000's ROM program must meet Import it with an alias of your choice: "rx:" u_se< "Radix". Put libs/Radix/src on XETAL_PATH ("just path"); the reference is libs/Radix/docs. Names with l: are exported; the others are private to this file.
A number is eight digits in radix r (16, 8 or 10), as a calculator chip keeps it: a whole number from 0 to r^8 - 1. Arithmetic wraps like an odometer, modulo r^8: in hexadecimal FFFFFFFF + 1 is 0 and 5 - 7 is FFFFFFFE (the r's complement a digit-serial subtract leaves). The radix is the first argument of every function, bound once by applying the function to it: u:k_ey := rx:k_ey 16.
Numbers as digits
ˡWidth : Int
ˡWidth ← 8
ˡd̲igits : Int -> Int -> Int
r d_igits n: the eight digits of n, most significant first (the display's order); n is wrapped first.
ʳˣ⁼u̲se< "Radix"
16 ʳˣd̲igits 255 0 0 0 0 0 0 15 15
ˡd̲igits ← { r n → (ˡWidth r̲eshape r) e̲ncode n m̲od ˡm̲odulus r }
ˡv̲alue : Num a => a -> a -> a
r v_alue ds: the number eight digits (or any digits) stand for.
ʳˣ⁼u̲se< "Radix"
16 ʳˣv̲alue 1 0 0 256
ˡv̲alue ← { r ds → r d̲ecode ds }
The keys
Keys : Char
Key codes: the digits 0 to 15 are themselves, then + - * = and X (clear: C is a hexadecimal digit); Keys is the text whose place, less 1, is the code.
Keys ← ⎕D c̲at (6 t̲ake ⎕A) c̲at "+-*=X"
ˡk̲eys : Char -> Int
k_eys text: the key codes of a text such as "12+34=" (spaces are skipped; any other character gives 21, which k_ey ignores).
ʳˣ⁼u̲se< "Radix"
ʳˣk̲eys "12+34=" 1 2 16 3 4 19
ˡk̲eys ← { t → (Keys i̲ndexOf (t ≠ f̲irst " ") r̲eplicate t) − 1 }
The calculator: a step per key
ˡReset : Int
The state, five numbers: the accumulator, the entry, the pending operation (0 none, 1 +, 2 -, 3 *), the phase (0 after an operator, 1 entering digits, 2 after =) and the number displayed.
ʳˣ⁼u̲se< "Radix"
ʳˣReset 0 0 0 0 0
ˡReset ← 0 0 0 0 0
ˡa̲lu : Int -> Int -> Int
r a_lu ops: the operation ops[1] (1 +, 2 -, 3 *) of ops[2] and ops[3], wrapped (the operands as one vector: a function has two sides).
ʳˣ⁼u̲se< "Radix"
16 ʳˣa̲lu 2 5 7 4294967294
ˡa̲lu ← { r ops → op ← f̲irst ops a ← 2 s̲elect ops b ← 3 s̲elect ops r ˡw̲rap '+ r̲/ (op = 1 2 3) × (a + b) c̲at (a − b) c̲at r ʰt̲imes a c̲at b }
ʰt̲imes : Int -> Int -> Int
r t_imes a b: a * b wrapped, without overflow: b split at r^4, so no partial product reaches r^12 (FFFFFFFF * FFFFFFFF itself is past a 64-bit Int).
ʰt̲imes ← { r ab → a ← f̲irst ab b ← f̲irst 1 d̲rop ab half ← r ^ 4 r ˡw̲rap (half × r ˡw̲rap a × b d̲iv half) + a × b m̲od half }
ʰs̲ettle : Int -> Int -> Int
The pending operation settled: the accumulator after it takes the entry (the entry itself when nothing is pending; the accumulator unchanged when no digit was entered since).
ʰs̲ettle ← { r s → 1 ≠ ʰp̲hase s ? ʰa̲cc s 0 = ʰo̲p s ? ʰe̲ntry s r ˡa̲lu (ʰo̲p s) c̲at (ʰa̲cc s) c̲at ʰe̲ntry s }
ˡk̲ey : Int -> Int -> Int -> Int
r k_ey s k: the state after the key k (a step: the machine's ROM program must leave the same display). A digit not below the radix, or one that would make a ninth digit, is ignored.
ʳˣ⁼u̲se< "Radix"
ᵘk̲ey ← ʳˣk̲ey 16
ʳˣReset ᵘk̲ey 10 0 10 0 1 10
ˡk̲ey ← { r s k → k ≥ 21 ? s k = ˡKeyClear ? ˡReset k = ˡKeyEquals ? (r ʰs̲ettle s) c̲at 0 0 2 c̲at r ʰs̲ettle s k ≥ ˡKeyPlus ? (r ʰs̲ettle s) c̲at 0 c̲at (k − 15) c̲at 0 c̲at r ʰs̲ettle s k ≥ r ? s fresh ← 2 = ʰp̲hase s old ← (n̲ot fresh) × (1 = ʰp̲hase s) × ʰe̲ntry s old ≥ r ^ ˡWidth − 1 ? s entry ← k + r × old ((n̲ot fresh) × ʰa̲cc s) c̲at entry c̲at ((n̲ot fresh) × ʰo̲p s) c̲at 1 c̲at entry }
ˡs̲creen : Int -> Int -> Char
r s_creen s: the display as eight characters: the digits, leading zeros blank (zero itself shows one 0).
ʳˣ⁼u̲se< "Radix"
16 ʳˣs̲creen 0 0 0 0 305419896 12345678
ˡs̲creen ← { r s → ds ← r ˡd̲igits ˡd̲isplay s blank ← ('∧ s̲\ 0 = 7 t̲ake ds) c̲at 0 (1 + (1 − blank) × 1 + ds) s̲elect " " c̲at 16 t̲ake Keys }
ˡc̲alc : Int -> Char -> Char
r c_alc text: the screen after the keys of a text, from Reset: 16 rx:c_alc "12+34=" is " 46".
ʳˣ⁼u̲se< "Radix"
16 ʳˣc̲alc "5-7=" FFFFFFFE
ˡc̲alc ← { r t → s ← r ˡr̲un ˡk̲eys t r ˡs̲creen s }
ˡr̲un : Int -> Int -> Int
r r_un keys: the state after every key, from Reset (the harness: a loop over the keys, as Clocked's).
ʳˣ⁼u̲se< "Radix"
16 ʳˣr̲un ʳˣk̲eys "12+34=" 70 0 0 2 70
ˡr̲un ← { r keys → (n, s) ← (t̲ally keys) '{ (i, s) → (i + 1, (r ˡk̲ey s)_ i s̲elect keys) } p̲ower (1, ˡReset) s }