librarydemos/rosetta/Comparison.xtl
Comparison: the Rosetta stone's state machine (docs/rosetta.md), a library beside the demo ("cm:" u_se< "Comparison"). The state is one 4 x 5 matrix of numbers. The first three rows are the axes (the idiom drum, the top ring, the bottom ring), four columns each: how many items the axis has, how many steps it has taken (an integer; the item facing is 1 + steps, wrapped, and this is the truth), its angle in degrees (90 per step; the presentation, easing toward 90 * steps) and whether it plays on its own. The fourth row is the pointer: the axis being dragged (0 none, 0.5 pressed and not yet moved, else the axis), where it last was (x, y), how far it has moved in all, and the last turn it gave in degrees (for a flick). The fifth row is the attract mode's clock: seconds since the last interaction, seconds since its last step, and its two counters (how far the bottom ring is through its sweep of the other languages, how far the drum is through its sweep of the idioms). Nothing is ever inferred from an angle except what to draw. Every function gives a new state; none has an effect. Events (RS1) come in as []E_VENT gives them; cm:u_pdate takes one.
ˡdwell : Float
The attract mode's pace, in seconds: how long a comparison holds
still before the next (long enough to read two faces of code), and
how long after an interaction the stone waits before moving on its
own again (long enough to look round a comparison by hand). The
traversal is the nested loop for top, for idiom, for bottom: the
bottom ring steps at each dwell; when it has swept every other
language the drum rolls to the next idiom in the same instant, the
bottom going on round; and only when every idiom has been shown
does the top ring advance to the next language. So the language
compared from stays put through the whole table, and no comparison
comes round again within minutes (the user, 2026-10-06; before,
the top stepped after each sweep of the bottom and the drum after
each sweep of the top: two minutes per idiom and the same pairs
again and again).
ˡdwell ← 5.0
ˡs̲tart : (Num a, Num b) => a -> b -> Float
The state for i idioms and l languages, i ᶜᵐs̲tart l: nothing
turned, all playing, no pointer, the attract mode ready (idle long
enough already).
ᶜᵐ⁼u̲se< "Comparison"
31 ᶜᵐs̲tart 7 31.0 0.0 0.0 1.0 0.0 7.0 0.0 0.0 1.0 0.0 7.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 20.0 0.0 0.0 0.0 0.0
ˡs̲tart ← { i l → 5 5 r̲eshape (f̲loat i) c̲at 0.0 0.0 1.0 0.0 c̲at (f̲loat l) c̲at 0.0 0.0 1.0 0.0 c̲at (f̲loat l) c̲at 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 c̲at ˡidleBefore c̲at 0.0 0.0 0.0 0.0 }
ˡTOP : Float
ˡTOP ← 2.0
ˡBOTTOM : Float
ˡBOTTOM ← 3.0
ˡmiddle : Float
The picture's middle, in its own pixels (Stone's size is 420): the top half above it, the bottom half below; and how many degrees a pixel of drag turns an axis (a quarter turn across half the picture).
ˡmiddle ← 210.0
ˡa̲t : Num a => a -> b -> b
A cell of the state: (row c̲at col) ᶜᵐa̲t state.
ˡa̲t ← { rc state → (f̲loor 2 s̲elect rc) s̲elect (f̲loor 1 s̲elect rc) s̲elect state }
ˡp̲ut : Float -> Float -> Float
The state with one cell replaced: (row c̲at col c̲at v) ᶜᵐp̲ut state (a
mask over the matrix, so no cell is ever addressed by hand).
ˡp̲ut ← { rcv state → mask ← f̲loat (rows = 1 s̲elect rcv) × cols = 2 s̲elect rcv (state × 1.0 − mask) + mask × 3 s̲elect rcv }
ˡw̲rap : Int -> Int -> Int
An item of an axis of n, wrapped around: item k of n for any integer k.
ˡw̲rap ← { n k → 1 + (k − 1) m̲od n }
ˡc̲urrent : Float -> Float -> Int
The item facing on an axis: 1 + its steps, wrapped.
ᶜᵐ⁼u̲se< "Comparison"
ᶜᵐTOP ᶜᵐc̲urrent (ᶜᵐTOP c̲at 1.0) ᶜᵐs̲tep 31 ᶜᵐs̲tart 7 2
ˡc̲urrent ← { axis state → (f̲loor 0.5 + (axis c̲at ˡCOUNT) ˡa̲t state) ˡw̲rap f̲loor 1.5 + (axis c̲at ˡSTEPS) ˡa̲t state }
ˡs̲tep : Float -> Float -> Float
An axis stepped by d items (1 forward, -1 back): its steps change,
its angle will follow at each tick. A ring never stops on the other
ring's language (the two halves always compare two languages): it
steps once more the same way. (axis c̲at d) ᶜᵐs̲tep state.
ᶜᵐ⁼u̲se< "Comparison"
ᶜᵐIDIOM ᶜᵐc̲urrent (ᶜᵐIDIOM c̲at -1.0) ᶜᵐs̲tep 31 ᶜᵐs̲tart 7 31
ᶜᵐBOTTOM ᶜᵐc̲urrent (ᶜᵐBOTTOM c̲at 1.0) ᶜᵐs̲tep (ᶜᵐTOP c̲at 2.0) ᶜᵐc̲hoose 31 ᶜᵐs̲tart 7 3
ˡs̲tep ← { ad state → axis ← 1 s̲elect ad d ← 2 s̲elect ad moved ← (axis c̲at ˡSTEPS c̲at d + (axis c̲at ˡSTEPS) ˡa̲t state) ˡp̲ut state axis = ˡIDIOM ? moved other ← 5.0 − axis clash ← (axis ˡc̲urrent moved) = other ˡc̲urrent moved "clash" i̲f< "(axis c_at d) l:s_tep moved; moved" }
ˡc̲hoose : Float -> Float -> Float
An axis turned to item k the short way round, and paused: choosing
is navigating, turning is exploring. A ring is not turned to the
other ring's language (the choice is offered disabled); the state is
unchanged. (axis c̲at k) ᶜᵐc̲hoose state.
ᶜᵐ⁼u̲se< "Comparison"
ᶜᵐTOP ᶜᵐc̲urrent (ᶜᵐTOP c̲at 7.0) ᶜᵐc̲hoose 31 ᶜᵐs̲tart 7 7
(ᶜᵐTOP c̲at ᶜᵐSTEPS) ᶜᵐa̲t (ᶜᵐTOP c̲at 7.0) ᶜᵐc̲hoose 31 ᶜᵐs̲tart 7 -1.0
ˡc̲hoose ← { ak state → axis ← 1 s̲elect ak n ← f̲loor 0.5 + (axis c̲at ˡCOUNT) ˡa̲t state d ← ((f̲loor 2 s̲elect ak) − axis ˡc̲urrent state) m̲od n short ← "(2 * d) > n" i̲f< "d - n; d" chosen ← (axis c̲at ˡPLAYING c̲at 0.0) ˡp̲ut (axis c̲at f̲loat short) ˡs̲tep state axis = ˡIDIOM ? chosen (f̲loor 2 s̲elect ak) = (5.0 − axis) ˡc̲urrent state ? state chosen }
ˡe̲ase : Float -> Float -> Float
An axis's angle eased toward 90 times its steps over dt seconds: it
covers the gap in about half a second, and lands exactly when near.
(axis c̲at dt) ᶜᵐe̲ase state.
ᶜᵐ⁼u̲se< "Comparison"
(ᶜᵐTOP c̲at ᶜᵐANGLE) ᶜᵐa̲t (ᶜᵐTOP c̲at 0.25) ᶜᵐe̲ase (ᶜᵐTOP c̲at 1.0) ᶜᵐs̲tep 31 ᶜᵐs̲tart 7 45.0
ˡe̲ase ← { adt state → axis ← 1 s̲elect adt angle ← (axis c̲at ˡANGLE) ˡa̲t state target ← 90.0 × (axis c̲at ˡSTEPS) ˡa̲t state gap ← target − angle moved ← angle + gap × 1.0 m̲in 2.0 × 2 s̲elect adt near ← "0.5 > a_bs target - moved" i̲f< "target; moved" (axis c̲at ˡANGLE c̲at near) ˡp̲ut state }
ˡs̲ettle : Float -> Float
Every axis at rest where its steps put it (angles 90 times steps), for a state built by choosing before the first tick.
ᶜᵐ⁼u̲se< "Comparison"
ᶜᵐs̲ettle (ᶜᵐIDIOM c̲at 3.0) ᶜᵐc̲hoose 31 ᶜᵐs̲tart 7 31.0 2.0 180.0 0.0 0.0 7.0 0.0 0.0 1.0 0.0 7.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 20.0 0.0 0.0 0.0 0.0
ˡs̲ettle ← { state → (ˡBOTTOM c̲at 9.0) ˡe̲ase (ˡTOP c̲at 9.0) ˡe̲ase (ˡIDIOM c̲at 9.0) ˡe̲ase state }
ˡt̲ick : Float -> Float -> Float
Every axis eased over dt seconds (a tick), and the attract mode's clock advanced: when the stone has been left alone long enough and the last comparison has held still for a dwell, the attract mode steps (cm:a_ttract).
ᶜᵐ⁼u̲se< "Comparison"
ᶜᵐBOTTOM ᶜᵐc̲urrent ᶜᵐdwell ᶜᵐt̲ick 31 ᶜᵐs̲tart 7 2
ᶜᵐBOTTOM ᶜᵐc̲urrent (ᶜᵐdwell − 0.1) ᶜᵐt̲ick 31 ᶜᵐs̲tart 7 1
ˡt̲ick ← { dt state → eased ← (ˡBOTTOM c̲at dt) ˡe̲ase (ˡTOP c̲at dt) ˡe̲ase (ˡIDIOM c̲at dt) ˡe̲ase state idle ← dt + (ˡATTRACT c̲at ˡIDLE) ˡa̲t state since ← dt + (ˡATTRACT c̲at ˡSINCE) ˡa̲t state timed ← (ˡATTRACT c̲at ˡSINCE c̲at since) ˡp̲ut (ˡATTRACT c̲at ˡIDLE c̲at idle) ˡp̲ut eased (idle < ˡidleBefore) ∨ since < ˡdwell ? timed ˡa̲ttract (ˡATTRACT c̲at ˡSINCE c̲at 0.0) ˡp̲ut timed }
ˡa̲ttract : Float -> Float
One step of the attract mode: the bottom ring steps (if it plays); when it has swept every other language (its count reaches one less than the languages), the drum rolls to the next idiom too and the bottom's count starts over; when the drum has rolled through every idiom, the top ring steps too and the drum's count starts over. A paused axis is counted but not turned, so the sweeps keep their rhythm. With 3 idioms and 4 languages: the drum rolls at every third step, the top steps at the ninth.
ᶜᵐ⁼u̲se< "Comparison"
s ← 3 ᶜᵐs̲tart 4
(ᶜᵐIDIOM ᶜᵐc̲urrent ᶜᵐa̲ttract² s) c̲at ᶜᵐIDIOM ᶜᵐc̲urrent ᶜᵐa̲ttract³ s 1 2
(ᶜᵐTOP ᶜᵐc̲urrent ᶜᵐa̲ttract⁸ s) c̲at ᶜᵐTOP ᶜᵐc̲urrent ᶜᵐa̲ttract⁹ s 1 2
ᶜᵐIDIOM ᶜᵐc̲urrent ᶜᵐa̲ttract⁹ s 1
ˡa̲ttract ← { state → n ← (f̲loor 0.5 + (ˡTOP c̲at ˡCOUNT) ˡa̲t state) − 1 m ← f̲loor 0.5 + (ˡIDIOM c̲at ˡCOUNT) ˡa̲t state bottoms ← 1.0 + (ˡATTRACT c̲at ˡBOTTOMS) ˡa̲t state idioms ← 1.0 + (ˡATTRACT c̲at ˡIDIOMS) ˡa̲t state moved ← ˡBOTTOM ˡp̲lay state bottoms < f̲loat n ? (ˡATTRACT c̲at ˡBOTTOMS c̲at bottoms) ˡp̲ut moved rolled ← ˡIDIOM ˡp̲lay (ˡATTRACT c̲at ˡBOTTOMS c̲at 0.0) ˡp̲ut moved idioms < f̲loat m ? (ˡATTRACT c̲at ˡIDIOMS c̲at idioms) ˡp̲ut rolled ˡTOP ˡp̲lay (ˡATTRACT c̲at ˡIDIOMS c̲at 0.0) ˡp̲ut rolled }
ˡp̲lay : Float -> Float -> Float
An axis stepped forward if it plays, else left as it is.
ˡp̲lay ← { axis state → "1.0 = (axis c_at l:PLAYING) l:a_t state" i̲f< "(axis c_at 1.0) l:s_tep state; state" }
i̲f< expands to
{ @ → (1.0 = (axis c̲at ˡPLAYING) ˡa̲t state) ? (axis c̲at 1.0) ˡs̲tep state◆ state } @ˡt̲ouched : Float -> Float
The stone touched: the attract mode waits again from now.
ˡt̲ouched ← { state → (ˡATTRACT c̲at ˡSINCE c̲at 0.0) ˡp̲ut (ˡATTRACT c̲at ˡIDLE c̲at 0.0) ˡp̲ut state }
ˡr̲esume : Float -> Float
Every axis playing again (a start chosen with cm:c_hoose is paused).
ˡr̲esume ← { state → (ˡBOTTOM c̲at ˡPLAYING c̲at 1.0) ˡp̲ut (ˡTOP c̲at ˡPLAYING c̲at 1.0) ˡp̲ut (ˡIDIOM c̲at ˡPLAYING c̲at 1.0) ˡp̲ut state }
ˡt̲oggleTour : Float -> Float
The tour paused when any axis plays, else every axis resumed (the space bar: one key stops everything, and starts it again).
ᶜᵐ⁼u̲se< "Comparison"
(ᶜᵐTOP c̲at ᶜᵐPLAYING) ᶜᵐa̲t ᶜᵐt̲oggleTour 31 ᶜᵐs̲tart 7 0.0
(ᶜᵐBOTTOM c̲at ᶜᵐPLAYING) ᶜᵐa̲t ᶜᵐt̲oggleTour ᶜᵐTOP ᶜᵐt̲oggle 31 ᶜᵐs̲tart 7 0.0
(ᶜᵐTOP c̲at ᶜᵐPLAYING) ᶜᵐa̲t ᶜᵐt̲oggleTour ᶜᵐt̲oggleTour 31 ᶜᵐs̲tart 7 1.0
ˡt̲oggleTour ← { state → playing ← ((ˡIDIOM c̲at ˡPLAYING) ˡa̲t state) + ((ˡTOP c̲at ˡPLAYING) ˡa̲t state) + (ˡBOTTOM c̲at ˡPLAYING) ˡa̲t state 0.0 < playing ? (ˡBOTTOM c̲at ˡPLAYING c̲at 0.0) ˡp̲ut (ˡTOP c̲at ˡPLAYING c̲at 0.0) ˡp̲ut (ˡIDIOM c̲at ˡPLAYING c̲at 0.0) ˡp̲ut state ˡr̲esume state }
ˡm̲ode : Float -> Char
The tour's mode, for the host to show: "paused" when every axis is paused, "paused: top, bottom" naming the axes paused when some are, "holding" while the stone waits after a touch, else "touring".
ᶜᵐ⁼u̲se< "Comparison"
ᶜᵐm̲ode 31 ᶜᵐs̲tart 7 touring
ᶜᵐm̲ode ᶜᵐt̲ouched 31 ᶜᵐs̲tart 7 holding
ᶜᵐm̲ode ᶜᵐTOP ᶜᵐt̲oggle 31 ᶜᵐs̲tart 7 paused: top
ᶜᵐm̲ode ᶜᵐt̲oggleTour 31 ᶜᵐs̲tart 7 paused
ˡm̲ode ← { state → p̲aused ← { axis → 0.0 = (axis c̲at ˡPLAYING) ˡa̲t state } paused ← (p̲aused ˡIDIOM) c̲at (p̲aused ˡTOP) c̲at p̲aused ˡBOTTOM 3 = '+ r̲/ paused ? "paused" names ← paused r̲eplicate (e̲nclose "roll") c̲at (e̲nclose "top") c̲at e̲nclose "bottom" 0 < '+ r̲/ paused ? "paused: " c̲at d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at ", " c̲at d̲isclose y } r̲/ names ˡidleBefore > (ˡATTRACT c̲at ˡIDLE) ˡa̲t state ? "holding" "touring" }
ˡt̲oggle : Float -> Float -> Float
An axis paused or resumed.
ˡt̲oggle ← { axis state → (axis c̲at ˡPLAYING c̲at 1.0 − (axis c̲at ˡPLAYING) ˡa̲t state) ˡp̲ut state }
ˡu̲pdate : Float -> Event -> Float
The state after an event: a tick eases every axis; the arrow keys
step the drum (Up, Down) and the top ring (Left, Right), a and d the
bottom ring, space pauses or resumes the whole tour (cm:t_oggleTour),
r resumes it at once, the idle wait over (cm:r_esume; the host
sends it after its opening choices, since a choice pauses its
axis), s the bottom ring and w
the drum on their own; the pointer drags and
clicks (cm:d_own, cm:m_ove, cm:u_p,
cm:c_lick); a choice from the host's controls, choose AXIS ITEM,
turns that axis to the item (cm:c_hoose).
ˡu̲pdate ← { state e → kind ← ⎕E̲KIND e kind m̲atch "tick" ? (f̲irst ⎕E̲AT e) ˡt̲ick state touched ← ˡt̲ouched state kind m̲atch "key" ? (⎕E̲KEY e) ˡk̲ey touched kind m̲atch "down" ? (⎕E̲AT e) ˡd̲own touched kind m̲atch "move" ? (⎕E̲AT e) ˡm̲ove touched kind m̲atch "up" ? (⎕E̲AT e) ˡu̲p touched kind m̲atch "click" ? (⎕E̲AT e) ˡc̲lick touched kind m̲atch "choose" ? (⎕E̲AT e) ˡc̲hoose touched state }
ˡh̲alf : Float -> Float
The half under a point: the top ring above the middle, the bottom ring below.
ˡh̲alf ← { xy → "(2 s_elect xy) < l:middle" i̲f< "l:TOP; l:BOTTOM" }
ˡd̲own : Float -> Float -> Float
The pointer pressed at a point: remembered, nothing turned yet.
xy ᶜᵐd̲own state.
ᶜᵐ⁼u̲se< "Comparison"
4 s̲elect 100.0 50.0 ᶜᵐd̲own 31 ᶜᵐs̲tart 7 0.5 100.0 50.0 0.0 0.0
ˡd̲own ← { xy state → (ˡPOINTER c̲at ˡLAST c̲at 0.0) ˡp̲ut (ˡPOINTER c̲at ˡMOVED c̲at 0.0) ˡp̲ut (ˡPOINTER c̲at ˡY c̲at 2 s̲elect xy) ˡp̲ut (ˡPOINTER c̲at ˡX c̲at 1 s̲elect xy) ˡp̲ut (ˡPOINTER c̲at ˡDRAGGING c̲at 0.5) ˡp̲ut state }
ˡm̲ove : Float -> Float -> Float
The pointer moved to a point: nothing unless it is pressed; the
first move of more than a few pixels decides which axis it drags (a
sideways move the half it is over, an up or down move the drum);
then each move turns that axis by its distance, a drag right or down
turning toward the previous item. A drag does not pause the axis for
good: the stone waits cm:idleBefore seconds after any touch before
moving on its own again. xy ᶜᵐm̲ove state.
ᶜᵐ⁼u̲se< "Comparison"
(ᶜᵐTOP c̲at ᶜᵐANGLE) ᶜᵐa̲t 142.0 50.0 ᶜᵐm̲ove 100.0 50.0 ᶜᵐd̲own 31 ᶜᵐs̲tart 7 -18.0
(ᶜᵐBOTTOM c̲at ᶜᵐANGLE) ᶜᵐa̲t 142.0 50.0 ᶜᵐm̲ove 100.0 50.0 ᶜᵐd̲own 31 ᶜᵐs̲tart 7 0.0
ˡm̲ove ← { xy state → held ← (ˡPOINTER c̲at ˡDRAGGING) ˡa̲t state held = 0.0 ? state dx ← (1 s̲elect xy) − (ˡPOINTER c̲at ˡX) ˡa̲t state dy ← (2 s̲elect xy) − (ˡPOINTER c̲at ˡY) ˡa̲t state moved ← (a̲bs dx) + (a̲bs dy) + (ˡPOINTER c̲at ˡMOVED) ˡa̲t state placed ← (ˡPOINTER c̲at ˡMOVED c̲at moved) ˡp̲ut (ˡPOINTER c̲at ˡY c̲at 2 s̲elect xy) ˡp̲ut (ˡPOINTER c̲at ˡX c̲at 1 s̲elect xy) ˡp̲ut state (held = 0.5) ∧ moved ≤ 4.0 ? placed axis ← "held = 0.5" i̲f< "\"(a_bs dx) >= a_bs dy\" i_f< \"l:h_alf xy; l:IDIOM\"; held" along ← "axis = l:IDIOM" i̲f< "dy; dx" turn ← n̲eg along × ˡperPixel angle ← turn + (axis c̲at ˡANGLE) ˡa̲t placed (ˡPOINTER c̲at ˡLAST c̲at turn) ˡp̲ut (ˡPOINTER c̲at ˡDRAGGING c̲at axis) ˡp̲ut (axis c̲at ˡANGLE c̲at angle) ˡp̲ut placed }
ˡu̲p : Float -> Float -> Float
The pointer released: a drag settles on the nearest item, one
further along when the last move was a flick (more than 6 degrees),
its steps becoming that item so the angle eases the rest of the way;
a press that never moved is a click. xy ᶜᵐu̲p state.
ᶜᵐ⁼u̲se< "Comparison"
(ᶜᵐTOP c̲at ᶜᵐSTEPS) ᶜᵐa̲t 142.0 50.0 ᶜᵐu̲p 142.0 50.0 ᶜᵐm̲ove 100.0 50.0 ᶜᵐd̲own 31 ᶜᵐs̲tart 7 -1.0
(ᶜᵐTOP c̲at ᶜᵐSTEPS) ᶜᵐa̲t 125.0 50.0 ᶜᵐu̲p 125.0 50.0 ᶜᵐm̲ove 120.0 50.0 ᶜᵐm̲ove 100.0 50.0 ᶜᵐd̲own 31 ᶜᵐs̲tart 7 0.0
ˡu̲p ← { xy state → held ← (ˡPOINTER c̲at ˡDRAGGING) ˡa̲t state held = 0.0 ? state released ← (ˡPOINTER c̲at ˡDRAGGING c̲at 0.0) ˡp̲ut state held = 0.5 ? xy ˡc̲lick released last ← (ˡPOINTER c̲at ˡLAST) ˡa̲t state flick ← "6.0 < a_bs last" i̲f< "45.0 * last / a_bs last; 0.0" angle ← (held c̲at ˡANGLE) ˡa̲t state (held c̲at ˡSTEPS c̲at f̲loat f̲loor 0.5 + (angle + flick) ÷ 90.0) ˡp̲ut released }
ˡc̲lick : Float -> Float -> Float
A click on a half pauses or resumes it. xy ᶜᵐc̲lick state.
ᶜᵐ⁼u̲se< "Comparison"
(ᶜᵐBOTTOM c̲at ᶜᵐPLAYING) ᶜᵐa̲t 100.0 300.0 ᶜᵐc̲lick 31 ᶜᵐs̲tart 7 0.0
ˡc̲lick ← { xy state → (ˡh̲alf xy) ˡt̲oggle state }
ˡk̲ey : Key -> Float -> Float
A key's effect: see cm:u_pdate. Named keys compare with []K_NAMED (1 Up, 2 Down, 3 Left, 4 Right); printing keys by their character.
ˡk̲ey ← { k state → k = ⎕K̲NAMED 1 ? (ˡIDIOM c̲at -1.0) ˡs̲tep state k = ⎕K̲NAMED 2 ? (ˡIDIOM c̲at 1.0) ˡs̲tep state k = ⎕K̲NAMED 3 ? (ˡTOP c̲at -1.0) ˡs̲tep state k = ⎕K̲NAMED 4 ? (ˡTOP c̲at 1.0) ˡs̲tep state c ← ⎕K̲CHAR k c m̲atch "a" ? (ˡBOTTOM c̲at -1.0) ˡs̲tep state c m̲atch "d" ? (ˡBOTTOM c̲at 1.0) ˡs̲tep state c m̲atch " " ? ˡt̲oggleTour state c m̲atch "r" ? (ˡATTRACT c̲at ˡIDLE c̲at ˡidleBefore) ˡp̲ut ˡr̲esume state c m̲atch "s" ? ˡBOTTOM ˡt̲oggle state c m̲atch "w" ? ˡIDIOM ˡt̲oggle state state }