sourcelibs/Radix/src/Radix.xtl

1⍝# Radix: an eight-digit calculator in any radix (hexadecimal, octal, decimal): the specification HEX1000's ROM program must meet 2⍝# Import it with an alias of your choice: "rx:" u_se< "Radix". 3⍝# Put libs/Radix/src on XETAL_PATH ("just path"); the reference is libs/Radix/docs. 4⍝# Names with l: are exported; the others are private to this file. 5⍝# 6⍝# A number is eight digits in radix r (16, 8 or 10), as a calculator 7⍝# chip keeps it: a whole number from 0 to r^8 - 1. Arithmetic wraps 8⍝# like an odometer, modulo r^8: in hexadecimal FFFFFFFF + 1 is 0 and 9⍝# 5 - 7 is FFFFFFFE (the r's complement a digit-serial subtract 10⍝# leaves). The radix is the first argument of every function, bound 11⍝# once by applying the function to it: u:k_ey := rx:k_ey 16. 12 13⍝## Numbers as digits 14 15⍝# Width: the digits a number has (the display's). 16⍝# >> "rx:" u_se< "Radix" 17⍝# >> rx:Width 18⍝# 8 19ˡWidth ← 8 20 21⍝# m_odulus r: r^8, one more than the largest number. 22⍝# >> "rx:" u_se< "Radix" 23⍝# >> rx:m_odulus 16 24⍝# 4294967296 25ˡm̲odulus ← { r → r ^ ˡWidth } 26 27⍝# r d_igits n: the eight digits of n, most significant first (the 28⍝# display's order); n is wrapped first. 29⍝# >> "rx:" u_se< "Radix" 30⍝# >> 16 rx:d_igits 255 31⍝# 0 0 0 0 0 0 15 15 32ˡd̲igits ← { r n → (ˡWidth r̲eshape r) e̲ncode n m̲od ˡm̲odulus r } 33 34⍝# r v_alue ds: the number eight digits (or any digits) stand for. 35⍝# >> "rx:" u_se< "Radix" 36⍝# >> 16 rx:v_alue 1 0 0 37⍝# 256 38ˡv̲alue ← { r ds → r d̲ecode ds } 39 40⍝# r w_rap n: n modulo r^8 (an odometer). 41⍝# >> "rx:" u_se< "Radix" 42⍝# >> 16 rx:w_rap -1 43⍝# 4294967295 44ˡw̲rap ← { r n → n m̲od ˡm̲odulus r } 45 46⍝## The keys 47 48⍝# Key codes: the digits 0 to 15 are themselves, then + - * = and X 49⍝# (clear: C is a hexadecimal digit); Keys is the text whose place, 50⍝# less 1, is the code. 51Keys ← ⎕D c̲at (6 t̲ake ⎕A) c̲at "+-*=X" 52 53⍝# KeyPlus: the code of +. 54⍝# >> "rx:" u_se< "Radix" 55⍝# >> rx:KeyPlus 56⍝# 16 57ˡKeyPlus ← 16 58 59⍝# KeyMinus: the code of -. 60⍝# >> "rx:" u_se< "Radix" 61⍝# >> rx:KeyMinus 62⍝# 17 63ˡKeyMinus ← 17 64 65⍝# KeyTimes: the code of *. 66⍝# >> "rx:" u_se< "Radix" 67⍝# >> rx:KeyTimes 68⍝# 18 69ˡKeyTimes ← 18 70 71⍝# KeyEquals: the code of =. 72⍝# >> "rx:" u_se< "Radix" 73⍝# >> rx:KeyEquals 74⍝# 19 75ˡKeyEquals ← 19 76 77⍝# KeyClear: the code of X, clear. 78⍝# >> "rx:" u_se< "Radix" 79⍝# >> rx:KeyClear 80⍝# 20 81ˡKeyClear ← 20 82 83⍝# k_eys text: the key codes of a text such as "12+34=" (spaces are 84⍝# skipped; any other character gives 21, which k_ey ignores). 85⍝# >> "rx:" u_se< "Radix" 86⍝# >> rx:k_eys "12+34=" 87⍝# 1 2 16 3 4 19 88ˡk̲eys ← { t → (Keys i̲ndexOf (t ≠ f̲irst " ") r̲eplicate t) − 1 } 89 90⍝## The calculator: a step per key 91 92⍝# The state, five numbers: the accumulator, the entry, the pending 93⍝# operation (0 none, 1 +, 2 -, 3 *), the phase (0 after an operator, 94⍝# 1 entering digits, 2 after =) and the number displayed. 95⍝# >> "rx:" u_se< "Radix" 96⍝# >> rx:Reset 97⍝# 0 0 0 0 0 98ˡReset ← 0 0 0 0 0 99 100ʰa̲cc ← { s → 1 s̲elect s } 101ʰe̲ntry ← { s → 2 s̲elect s } 102ʰo̲p ← { s → 3 s̲elect s } 103ʰp̲hase ← { s → 4 s̲elect s } 104 105⍝# r a_lu ops: the operation ops[1] (1 +, 2 -, 3 *) of ops[2] and 106⍝# ops[3], wrapped (the operands as one vector: a function has two 107⍝# sides). 108⍝# >> "rx:" u_se< "Radix" 109⍝# >> 16 rx:a_lu 2 5 7 110⍝# 4294967294 111ˡa̲lu ← { r ops → 112 op ← f̲irst ops 113 a ← 2 s̲elect ops 114 b ← 3 s̲elect ops 115 r ˡw̲rap '+ r̲/ (op = 1 2 3) × (a + b) c̲at (a − b) c̲at r ʰt̲imes a c̲at b 116} 117 118⍝# r t_imes a b: a * b wrapped, without overflow: b split at r^4, so no 119⍝# partial product reaches r^12 (FFFFFFFF * FFFFFFFF itself is past a 120⍝# 64-bit Int). 121ʰt̲imes ← { r ab → 122 a ← f̲irst ab 123 b ← f̲irst 1 d̲rop ab 124 half ← r ^ 4 125 r ˡw̲rap (half × r ˡw̲rap a × b d̲iv half) + a × b m̲od half 126} 127 128⍝# The pending operation settled: the accumulator after it takes the 129⍝# entry (the entry itself when nothing is pending; the accumulator 130⍝# unchanged when no digit was entered since). 131ʰs̲ettle ← { r s → 132 1 ≠ ʰp̲hase s ? ʰa̲cc s 133 0 = ʰo̲p s ? ʰe̲ntry s 134 r ˡa̲lu (ʰo̲p s) c̲at (ʰa̲cc s) c̲at ʰe̲ntry s 135} 136 137⍝# r k_ey s k: the state after the key k (a step: the machine's ROM 138⍝# program must leave the same display). A digit not below the radix, 139⍝# or one that would make a ninth digit, is ignored. 140⍝# >> "rx:" u_se< "Radix" 141⍝# >> u:k_ey := rx:k_ey 16 142⍝# >> rx:Reset u:k_ey 10 143⍝# 0 10 0 1 10 144ˡk̲ey ← { r s k → 145 k ≥ 21 ? s 146 k = ˡKeyClear ? ˡReset 147 k = ˡKeyEquals ? (r ʰs̲ettle s) c̲at 0 0 2 c̲at r ʰs̲ettle s 148 k ≥ ˡKeyPlus ? (r ʰs̲ettle s) c̲at 0 c̲at (k − 15) c̲at 0 c̲at r ʰs̲ettle s 149 k ≥ r ? s 150 fresh ← 2 = ʰp̲hase s 151 old ← (n̲ot fresh) × (1 = ʰp̲hase s) × ʰe̲ntry s 152 old ≥ r ^ ˡWidth − 1 ? s 153 entry ← k + r × old 154 ((n̲ot fresh) × ʰa̲cc s) c̲at entry c̲at ((n̲ot fresh) × ʰo̲p s) c̲at 1 c̲at entry 155} 156 157⍝# d_isplay s: the number displayed. 158⍝# >> "rx:" u_se< "Radix" 159⍝# >> rx:d_isplay 70 0 0 2 70 160⍝# 70 161ˡd̲isplay ← { s → 5 s̲elect s } 162 163⍝# r s_creen s: the display as eight characters: the digits, leading 164⍝# zeros blank (zero itself shows one 0). 165⍝# >> "rx:" u_se< "Radix" 166⍝# >> 16 rx:s_creen 0 0 0 0 305419896 167⍝# 12345678 168ˡs̲creen ← { r s → 169 ds ← r ˡd̲igits ˡd̲isplay s 170 blank ← ('∧ s̲\ 0 = 7 t̲ake ds) c̲at 0 171 (1 + (1 − blank) × 1 + ds) s̲elect " " c̲at 16 t̲ake Keys 172} 173 174⍝# r c_alc text: the screen after the keys of a text, from Reset: 175⍝# 16 rx:c_alc "12+34=" is " 46". 176⍝# >> "rx:" u_se< "Radix" 177⍝# >> 16 rx:c_alc "5-7=" 178⍝# FFFFFFFE 179ˡc̲alc ← { r t → 180 s ← r ˡr̲un ˡk̲eys t 181 r ˡs̲creen s 182} 183 184⍝# r r_un keys: the state after every key, from Reset (the harness: a 185⍝# loop over the keys, as Clocked's). 186⍝# >> "rx:" u_se< "Radix" 187⍝# >> 16 rx:r_un rx:k_eys "12+34=" 188⍝# 70 0 0 2 70 189ˡr̲un ← { r keys → 190 (n, s) ← (t̲ally keys) '{ (i, s) → (i + 1, (r ˡk̲ey s)_ i s̲elect keys) } p̲ower (1, ˡReset) 191 s 192}