# Ascending and Descending

## Lexicographic Ordering

Lexicographic ordering is what the APL primitives `⍋` and `⍒` provide:

``````   ⎕io←0     ⍝ ⎕io delenda est
⎕rl←7*5   ⍝ to get reproducible random results

a←?11 3⍴3

a          a ⌷⍨⊂ ⍋a
2 1 0      0 1 0
0 2 2      0 2 2
1 1 1      1 0 0
1 0 0      1 0 1
1 1 1      1 0 1
1 2 1      1 1 0
1 0 1      1 1 1
1 0 1      1 1 1
1 1 0      1 2 1
0 1 0      1 2 2
1 2 2      2 1 0
``````

First order by column 0, resulting in groups of rows with the same values in column 0. Then, within each group, order by column 1, getting subgroups with the same values in columns 0 and 1. Then, within each subgroup, order by column 2, getting subsubgroups with the same values in columns 0, 1, and 2. In general, for each subsub…subgroup, order by column `k`, getting groups with identical values in columns `⍳k`.

The preceding discourse is descriptive rather than prescriptive—algorithms for `⍋` can use more efficient and more straightforward approaches. As well, for ease of understanding, the description is for a matrix and speaks of columns and rows. In general, any non-scalar array can be ordered, whence instead of rows, think major cells, instead of column, think item in a major cell. Symbolically and more succinctly, `⍋⍵ ←→ ⍋⍪⍵`.

`⍋` can be used if the orderings in the process are all ascending, or `⍒` if all descending. The problem to be solved here is where the orderings are a mix of ascending and descending.

## Numeric Arrays

Since `⍒⍵ ←→ ⍋-⍵` if `⍵` is numeric, for such `⍵` multiply each descending column by `¯1` and each ascending column by `1`, then apply `⍋`. This induces a “control array” having the same shape as a major cell of the argument, with a `¯1` for descending and a `1` for ascending.

``````   adn←{⍵ ⌷⍨⊂ ⍋ ⍺ ×⍤99 ¯1 ⊢⍵}
``````

For the array `a` in the previous section:

``````   a              1 ¯1 1 adn a        ¯1 1 1 adn a
2 1 0          0 2 2               2 1 0
0 2 2          0 1 0               1 0 0
1 1 1          1 2 1               1 0 1
1 0 0          1 2 2               1 0 1
1 1 1          1 1 0               1 1 0
1 2 1          1 1 1               1 1 1
1 0 1          1 1 1               1 1 1
1 0 1          1 0 0               1 2 1
1 1 0          1 0 1               1 2 2
0 1 0          1 0 1               0 1 0
1 2 2          2 1 0               0 2 2
``````

In `1 ¯1 1 adn a`, column 0 is ascending, and within that, column 1 is descending, and within that, column 2 is ascending. In `¯1 1 1 adn a`, column 0 is descending, and within that, column 1 is ascending, and within that, column 2 is ascending.

## Ordinals

An array to be sorted can be converted to an order-equivalent integer array by assigning to each item an ordinal (an integer) which has the same ordering relationship as the original item relative to other items in the array:

``````   sort    ← {(⊂⍋⍵)⌷⍵}
ordinal ← {⎕ct←0 ⋄ ⍵⍳⍨sort,⍵}
``````

That is, the ordinals obtain as the indices of the original array in the sorted list of the ravelled elements, using exact comparisons. (Exact comparisons are used because sorting necessarily uses exact comparisons.)
For example:

``````   ⊢ d←¯1 'syzygy' (3 ¯5) 1j2 'chthonic' (¯1)
┌──┬──────┬────┬───┬────────┬──┐
│¯1│syzygy│3 ¯5│1J2│chthonic│¯1│
└──┴──────┴────┴───┴────────┴──┘
ordinal d
0 5 3 2 4 0
``````

In the example, the data items are `¯1`, `'syzygy'`, `'chthonic'`, `3 ¯5`, `1j2`, and `¯1` again. With respect to ordering, these data items are perfectly represented by the ordinals (numbers) 0, 5, 3, 2, 4, and 0, respectively. That is, `⍋d ←→ ⍋ordinal d`.

``````   ⍋ d
0 5 3 2 4 1
⍋ ordinal d
0 5 3 2 4 1
``````

As the example illustrates, it is imperative that identical ordinals are assigned to identical items, else the ordering relationships would be changed. For example, if `b←0,⍪2 1` and the 0s are assigned different ordinals,

``````   ⊢ b←0,⍪2 1
0 2
0 1
ordinal b                 ⊢ bo←0 3,⍪1 2  ⍝ faux ordinals
0 3                       0 3
0 2                       1 2
⍋ ordinal b               ⍋ bo
1 0                       0 1
⍋ b
1 0
``````

Computation of ordinals is greatly facilitated by the total array ordering introduced in Dyalog APL version 17.0.

## Non-Numeric Arrays

A general solution for the ordering problem obtains by first converting the array to an order-equivalent integer array through the use of ordinals.

``````   ado ← {⍵ ⌷⍨⊂ ⍋ ⍺ ×⍤99 ¯1 ordinal ⍵}
``````

For example:

``````   ⎕rl←7*5   ⍝ to get reproducible random results

x0← ?19⍴4
x1← (⊂?19⍴2) ⌷ 'alpha' 'beta'
x2← (⊂?19⍴3) ⌷ 'abc'
x3← (⊂?19⍴3) ⌷ 'able' 'baker' 'charlie'

x ← x0,x1,x2,⍪x3

ordinal x
13 49 19 42
10 49 32 68
13 49 63 68
4 49 63 42
0 27 19 23
13 49 32 42
0 49 19 42
10 49 32 68
10 27 32 23
4 49 32 68
4 49 32 68
4 27 32 23
4 49 32 68
0 49 63 68
13 49 63 68
0 49 32 42
13 27 32 23
4 27 63 42
13 49 19 42

(⍋x) ≡ ⍋ ordinal x
1
``````

Suppose `x` is to be sorted ascending in columns 0 and 2 and descending in columns 1 and 3. The control array is `1 ¯1 1 ¯1`, and:

``````   x                       1 ¯1 1 ¯1 ado x
┌─┬─────┬─┬───────┐     ┌─┬─────┬─┬───────┐
│2│beta │b│baker  │     │0│beta │a│able   │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│alpha│a│able   │     │0│beta │b│charlie│
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│beta │b│able   │     │0│beta │b│baker  │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│alpha│b│baker  │     │0│beta │c│charlie│
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│1│beta │b│charlie│     │0│beta │c│able   │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│1│beta │a│baker  │     │0│alpha│c│baker  │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│0│beta │c│charlie│     │1│beta │a│baker  │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│0│beta │b│baker  │     │1│beta │b│charlie│
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│0│beta │c│able   │     │1│alpha│c│able   │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│0│beta │a│able   │     │2│beta │a│baker  │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│alpha│a│baker  │     │2│beta │b│baker  │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│alpha│a│baker  │     │3│beta │b│able   │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│1│alpha│c│able   │     │3│beta │b│able   │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│0│beta │b│charlie│     │3│beta │c│charlie│
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│0│alpha│c│baker  │     │3│alpha│a│baker  │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│beta │b│able   │     │3│alpha│a│baker  │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│2│beta │a│baker  │     │3│alpha│a│able   │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│beta │c│charlie│     │3│alpha│a│able   │
├─┼─────┼─┼───────┤     ├─┼─────┼─┼───────┤
│3│alpha│a│able   │     │3│alpha│b│baker  │
└─┴─────┴─┴───────┘     └─┴─────┴─┴───────┘
``````

## Finally

``````   (ordinal x) ≡ ordinal ordinal x
1
``````

That is, `ordinal` is idempotent. Actually, this is kind of obvious, but I never miss an opportunity to use the word “idempotent”.☺

# Diane’s Lasagne Problem

## Making Lasagne

Participants in the SA2 Performance Tuning workshop at the Dyalog ’18 User Meeting were encouraged to bring their own problems for the group to work on. Diane Hymas of ExxonMobil brought a good one. The one-liner computation is as follows:

``lasagne0 ← {groups {+⌿⍵}⌸ amts ×[⎕io] spices[inds;]}``

where

``````   n ← 8e5
spices ← ?6000 44⍴0
groups ← +\(16↑1 2)[?n⍴16]
inds   ← ?n⍴≢spices
amts   ← ?n⍴0
``````

Applying `lasagne0` to this dataset:

``````   ⍴ lasagne0 ⍬
100015 44
≢ ∪ groups
100015

)copy dfns wsreq cmpx

wsreq 'lasagne0 ⍬'
844799820
cmpx  'lasagne0 ⍬'
2.12E¯1
``````

The problem with `lasagne0` is space rather than time. The 845 MB required for this dataset may be acceptable, but we can be called upon to cook up large batches of lasagne in a smallish workspace, on a machine with limited RAM. (Large `n` and large `≢∪groups`.)

All benchmarks in this document were run in Dyalog APL version 17.0, in a 2 GB workspace, on a machine with generous RAM.

## Solutions

Marshall Lochbaum solved the problem. The alternative solutions are as follows:

``````lasagne0 ← {groups {+⌿⍵}⌸ amts ×[⎕io] spices[inds;]}
lasagne1 ← {↑ (groups{⊂⍵}⌸amts) {+⌿⍺×[⎕io]spices[⍵;]}¨ groups{⊂⍵}⌸inds}
lasagne2 ← {↑ (groups{⊂⍵}⌸amts)      {⍺+.×spices[⍵;]}¨ groups{⊂⍵}⌸inds}
lasagne3 ← {↑ {amts[⍵]+.×spices[inds[⍵];]}¨ {⊂⍵}⌸groups}
``````

`lasagne0` is the original expression; `lasagne1` and `lasagne2` were derived by Marshall during the workshop; `lasagne3` was suggested by a participant in the workshop. The four functions produce matching results. Comparing the space and time:

 space (MB) time `lasagne0` `845` `2.29e¯1 ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕` `lasagne1` `74` `3.60e¯1 ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕` `lasagne2` `74` `2.39e¯1 ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕` `lasagne3` `74` `2.93e¯1 ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕`

## lasagne0 v  lasagne1

Nearly all of the space required to evaluate `lasagne0` is accounted for by the space for computing the right argument to key:

``````   wsreq 'lasagne0 ⍬'
844799820

wsreq 'amts ×[⎕io] spices[inds;]'
844799548
``````

In fact, the array `spices[inds;]`, all by itself, is already very large. It has shape `(⍴inds),1↓⍴spices` `(≡ 8e5 44)`, each item requiring 8 bytes.

``````   wsreq 'spices[inds;]'
281599556

⍴ spices[inds;]
800000 44

8 × ×/ ⍴ spices[inds;]
281600000

qsize←{⎕size '⍵'}    ⍝ # bytes for array ⍵
qsize spices[inds;]
281600040
``````

`lasagne1` avoids creating these large intermediate results, by first partitioning the arguments (`groups{⊂⍵}⌸amts` and `groups{⊂⍵}⌸inds`) and then applying a computation to each partition. In that computation, the operand function `{+⌿⍺×[⎕io]spices[⍵;]}`, `⍺` is a partition of `amts` and `⍵` is the corresponding partition of `inds`.

`lasagne1`, `lasagne2` and `lasagne3` require the same amount of space to run, so the comparison among them is on time.

## lasagne1 v  lasagne2

The only change is from `+⌿⍺×[⎕io]spices[⍵;]` to `⍺+.×spices[⍵;]`, which are equivalent when `⍺` is a vector. The interpreter can compute `+.×` in one go rather than doing `+⌿` separately after doing `×[⎕io]`; in such computation the interpreter can and does exploit the additional information afforded by `+.×` and is faster by a factor of `1.5` `(= 2.39 ÷⍨ 3.60)`.

## lasagne2 v  lasagne3

The idea in `lasagne3` is doing one key operation rather than the two in `lasagne2`. Therefore, the changes between `lasagne2` v `lasagne3` are:

 `lasagne2` ```groups{⊂⍵}⌸amts groups{⊂⍵}⌸inds``` `⍺` `spices[⍵;]` `lasagne3` `{⊂⍵}⌸groups` `amts[⍵]` `spices[inds[⍵];]`

All three key operations involve `{⊂⍵}⌸` with `groups` as the key, and are roughly equally fast, each taking up no more than 7% of the total time.

``````   cmpx 'groups{⊂⍵}⌸amts' 'groups{⊂⍵}⌸inds' '{⊂⍵}⌸groups'
groups{⊂⍵}⌸amts → 1.69E¯2 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* groups{⊂⍵}⌸inds → 1.39E¯2 | -18% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* {⊂⍵}⌸groups     → 1.36E¯2 | -20% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
``````

`lasagne3` is doing one less key operation than `lasagne2` in exchange for doing, for each of the `≢∪groups` `(= 100015)` executions of the operand function, `amt[⍵]` v `⍺` and `spices[ind[⍵];]` v `spices[⍵;]`. Indexing is by no means slow, but it’s not as fast as references to `⍺` and `⍵`. Therefore, `lasagne2` is faster.

The trade-off may differ for different values of `groups`. In this case `groups` are small-range integers so operations using it as the key value are fast.

# Progressive Index-Of

`⎕io=0 `is assumed throughout.

A recent Forum post motivated investigations into the progressive index-of functions in the FinnAPL Idiom Library:

``````pix  ← {((⍴⍺)⍴⍋⍋⍺⍳⍺,⍵) ⍳ ((⍴⍵)⍴⍋⍋⍺⍳⍵,⍺)}   ⍝ FinnAPL Idiom 1
pixa ← {((⍋⍺⍳⍺,⍵)⍳⍳⍴⍺) ⍳ ((⍋⍺⍳⍵,⍺)⍳⍳⍴⍵)}   ⍝ FinnAPL Idiom 5
``````

In this note, we:

• explain what is progressive index-of
• explain why the two functions work
• investigate the performance of the two functions
• provide a more general solution

## Progressive Index-Of

Progressive index-of is like index-of (`⍳`) except that each find “uses up” the target of that find. There are no duplicates in the result with the possible exception of `≢⍺` (for “not found”). Thus:

``````      x←'mississippi'
y←'dismiss'

x pix y
11 1 2 0 4 3 5
``````

The following chart illustrates a step-by-step derivation of each progressive index:

``````0 1 2 3 4 5 6 7 8 9 10

m i s s i s s i p p  i      d i s m i s s
11
m i s s i s s i p p  i      d i s m i s s
11 1
m i s s i s s i p p  i      d i s m i s s
11 1 2
m i s s i s s i p p  i      d i s m i s s
11 1 2 0
m i s s i s s i p p  i      d i s m i s s
11 1 2 0 4
m i s s i s s i p p  i      d i s m i s s
11 1 2 0 4 3
m i s s i s s i p p  i      d i s m i s s
11 1 2 0 4 3 5
``````

It is possible to compute the progressive index without looping or recursion, as the two FinnAPL functions demonstrate.

## Why It Works

The basic idea of `⍺ pix ⍵` is to substitute for each item of `⍺` and `⍵` an equivalent representative, collectively `c` and `d`, whence the result obtains as `c⍳d`. The equivalent representative used here is ranking, specifically the ranking of the indices in `⍺`.

The ranking of an array `⍵` is a permutation of order `≢⍵`. The smallest major cell is assigned 0; the next smallest is assigned 1; and so on. Ties are resolved by favoring the earlier-occurring cell. The ranking can be computed by `⍋⍋⍵`. For example:

``````      x ⍪ ⍉⍪ ⍋⍋x
m i s s i s  s i p p i
4 0 7 8 1 9 10 2 5 6 3

y ⍪ ⍉⍪ ⍋⍋y
d i s m i s s
0 1 4 3 2 5 6
``````

`⍺ pix ⍵` works on two different rankings of indices in `⍺`:

`⍋⍋⍺⍳⍺,⍵    `rankings of indices in `⍺` of `⍺` and `⍵`, favoring `⍺`
`⍋⍋⍺⍳⍵,⍺    `rankings of indices in `⍺` of `⍵` and `⍺`, favoring `⍵`

The first `⍴⍺` items of the former are those for `⍺` and the first `⍴⍵` of the latter are those for `⍵`, and we get

``pix ← {((⍴⍺)⍴⍋⍋⍺⍳⍺,⍵) ⍳ ((⍴⍵)⍴⍋⍋⍺⍳⍵,⍺)}``

The second version depends on the following properties of permutations. Let `p` be a permutation. Then `p[⍋p] ←→ ⍳≢p`, the identity permutation, and therefore `⍋p` is the inverse of `p`. Furthermore, `p[p⍳⍳≢p] ←→ ⍳≢p` and so `p⍳⍳≢p` is also the inverse of `p`. The inverse is unique (that’s why it’s called the inverse), therefore `⍋p ←→ p⍳⍳≢p`.

``````      p←97?97         ⍝ a random permutation

p[⍋p]    ≡ ⍳≢p
1
p[p⍳⍳≢p] ≡ ⍳≢p
1
(⍋p)     ≡ p⍳⍳≢p
1
``````

The two rankings are permutations (because the leftmost functions are `⍋`) and we just need the first `⍴⍺` items of the former and the first `⍴⍵` items of the latter. Thus:

``````pixa ← {((⍋⍺⍳⍺,⍵)⍳⍳⍴⍺) ⍳ ((⍋⍺⍳⍵,⍺)⍳⍳⍴⍵)}
``````

## Performance

We note that both versions of `pix` contain the expressions `⍺⍳⍺,⍵` and `⍺⍳⍵,⍺`, but the latter is just a rotation of the former. Thus:

``````pixb ← {i←⍺⍳⍺,⍵ ⋄ ((⍴⍺)⍴⍋⍋i) ⍳ ((⍴⍵)⍴⍋⍋(⍴⍺)⌽i)}
pixc ← {i←⍺⍳⍺,⍵ ⋄ ((⍋i)⍳⍳⍴⍺) ⍳ ((⍋(⍴⍺)⌽i)⍳⍳⍴⍵)}
``````

Which is faster? The answer may surprise.

``````      x←?1e6⍴3e5
y←?2e5⍴3e5

cmpx 'x pixb y' 'x pixc y'
x pixb y → 9.15E¯2 |  0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
x pixc y → 9.21E¯2 |  0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
``````

A few factors about the Dyalog APL interpreter are relevant to this performance:

• Computing `⍺⍳⍵,⍺` as a rotation of an already computed `i←⍺⍳⍺,⍵` produces a worthwhile speed-up, although only on a relatively small part of the overall computation.
``````      i←x⍳x,y
cmpx '(⍴x)⌽i' 'x⍳y,x'
(⍴x)⌽i → 5.00E¯4 |     0% ⎕⎕
x⍳y,x  → 7.19E¯3 | +1337% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
``````
• Both `⍳` and `⍋` have special code for small range data.
``````      s←?1e6⍴5e5           ⍝ small range
t←s ⋄ t[t⍳⌈/t]←2e9   ⍝ large range

cmpx 's⍳s' 't⍳t'
s⍳s → 5.87E¯3 |    0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕
t⍳t → 2.00E¯2 | +240% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕

cmpx '⍋s' '⍋t'
⍋s → 3.25E¯2 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
⍋t → 3.84E¯2 | +18% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
``````
• `⍋⍵` has special code when `⍵` is a permutation.
``````      p←1e6?1e6           ⍝ p is a permutation
q←p ⋄ q←⊃q  ⍝ q is not; both are small-range

cmpx '⍋p' '⍋q'
⍋p → 5.81E¯3 |    0% ⎕⎕⎕
* ⍋q → 5.71E¯2 | +882% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
``````
• We saw previously that if `p` is a permutation then `⍋p ←→ p⍳⍳⍴p`. The special code for `⍋p` makes the two expressions run at roughly the same speed. The slight advantage for `⍋⍋x` versus `(⍋x)⍳⍳⍴x` would increase if and when `⍋⍋` is recognized as an idiom.
``````      cmpx '⍋p' 'p⍳⍳⍴p'
⍋p    → 6.02E¯3 |  0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
p⍳⍳⍴p → 6.57E¯3 | +9% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕

cmpx '⍋⍋x' '(⍋x)⍳⍳⍴x'
⍋⍋x      → 3.16E¯2 |  0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
(⍋x)⍳⍳⍴x → 3.25E¯2 | +2% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕

``````

## A General Solution

Index-of works on cells rather than just scalars. Likewise, progressive index-of can also be extended to work on cells. The core algorithm remains the same. The generalization obtains by first reshaping `⍵` to have the same rank as `⍺` (having major cells with the same shape), applying the core algorithm, and then reshaping its result to have the same leading shape as the original `⍵`. Thus:

``````pixd←{
m←≢⍺
r←0⌊1-⍴⍴⍺
n←×/r↓⍴⍵
i←⍺⍳⍺⍪(n,1↓⍴⍺)⍴⍵
(r↓⍴⍵) ⍴ ((⍋i)⍳⍳m) ⍳ ((⍋m⌽i)⍳⍳n)
}

xx              yy
mmmm            dddd
iiii            iiii
ssss            ssss
ssss            mmmm
iiii            iiii
ssss            ssss
ssss            ssss
iiii
pppp
pppp                                  x
iiii                               mississippi

⍴xx             ⍴yy                y
11 4            7 4                dismiss

xx pixd yy                         x pixd y
11 1 2 0 4 3 5                     11 1 2 0 4 3 5

xx pixd 3 5 4⍴yy                   x pixd 3 5⍴y
11  1  2  0  4                     11  1  2  0  4
3  5 11  7  6                      3  5 11  7  6
11 10 11 11 11                     11 10 11 11 11
``````

Postscript
After having written the above, I discovered an alternative exposition on progressive index-of by Bob Smith entitled Anatomy of an Idiom. Adám Brudzewsky has produced a Stack Exchange lesson and a Jupyter Notebook based on Smith’s text.

There is also an exposition in J on the same topic, with a more verbose but easier-to-understand derivation.

# 2018 APL Problem Solving Competition: Phase I Problems Sample Solutions

The following are my attempts at the Phase I problems of the 2018 APL Problem Solving Competition. There are not necessarily “right answers” as personal style and taste come into play. More explanation of the code is provided here than common practice. All solutions pass all the tests specified in the official problem description.

The solutions for problems 1 and 3 are due to Brian Becker, judge and supremo of the competition. They are better than the ones I had originally.

1. Oh Say Can You See?

Given a vector or scalar of skyscraper heights, compute the number of skyscrapers which can be seen from the left. A skyscraper hides one further to the right of equal or lesser height.

``   visible ← {≢∪⌈\⍵}``

Proceeding from left to right, each new maximum obscures subsequent equal or lesser values. The answer is the number of unique maxima. A tacit equivalent is `≢∘∪∘(⌈\)`.

2. Number Splitting

Split a non-negative real number into the integer and fractional parts.

``   split ← 0 1∘⊤``

The function `⍺⊤⍵` encodes the number `⍵` in the number system specified by numeric vector `⍺` (the bases). For example, `24 60 60⊤sec` expresses `sec` in hours, minutes, and seconds. Such expression obtains by repeated application of the process, starting from the right of `⍺`: the next “digit” is the remainder of the number on division by a base, and the quotient of the division feeds into the division by the next base. Therefore, `0 1⊤⍵` divides `⍵` by `1`; the remainder of that division is the requisite fractional part and the quotient the integer part. That integer part is further divided by the next base, `0`. In APL, remaindering by `0` is defined to be the identity function.

You can have a good argue about the philosophy (theology?) of division by `0`, but the APL definition in the context of `⍺⊤⍵` gives practically useful results: A `0` in `⍺` essentially says, whatever is left. For example, `0 24 60 60⊤sec` expresses `sec` as days/hours/minutes/seconds.

3. Rolling Along

Given an integer vector or scalar representing a set of dice, produce a histogram of the possible totals that can be rolled.

``````   roll ← {{⍺('*'⍴⍨≢⍵)}⌸,+/¨⍳⍵}

roll 5 3 4
3  *
4  ***
5  ******
6  *********
7  ***********
8  ***********
9  *********
10 ******
11 ***
12 *``````

`⍳⍵` produces all possible rolls for the set of dice (the cartesian product of `⍳¨⍵`) whence further application of `+/¨` and then `,` produce a vector of all possible totals. With the vector of possible totals in hand, the required unique totals and corresponding histogram of the number of occurrences obtain readily with the key operator `⌸`. (And rather messy without the key operator.) For each unique total, the operand function `{⍺('*'⍴⍨≢⍵)}` produces that total and a vector of `*` with the required number of repetitions.

The problem statement does not require it, but it would be nice if the totals are listed in increasing order. At first, I’d thought that the totals would need to be explicitly sorted to make that happen, but on further reflection realized that the unique elements of `,+/¨⍳⍵` (when produced by taking the first occurrence) are guaranteed to be sorted.

4. What’s Your Sign?

Find the Chinese zodiac sign for a given year.

``   zodiac_zh ← {(1+12|⍵+0>⍵) ⊃ ' '(≠⊆⊢)' Monkey Rooster Dog Pig Rat Ox Tiger Rabbit Dragon Snake Horse Goat'}``

Since the zodiac signs are assigned in cycles of 12, the phrase `12|` plays a key role in the solution. The residue (remainder) function `|` is inherently and necessarily 0-origin; the `1+` accounts for the 1-origin indexing required by the competition. Adding `0>⍵` overcomes the inconvenient fact that there is no year 0.

Essentials of the computation are brought into sharper relief if each zodiac sign is denoted by a single character:

``   zzh ← {(1+12|⍵+0>⍵)⊃'猴雞狗豬鼠牛虎兔龍蛇馬羊'}``

The Chinese Unicode characters were found using https://translate.google.com and then copied and pasted into the Dyalog APL session.

5. What’s Your Sign? Revisited

Find the Western zodiac sign for a given month and day.

``````   zodiac_en←{
d←12 2⍴ 1 20  2 19  3 21  4 20  5 21  6 21  7 23  8 23  9 23  10 23  11 22  12 22
s←13⍴' '(≠⊆⊢)' Capricorn Aquarius Pisces Aries Taurus Gemini Cancer Leo Virgo Libra Scorpio Sagittarius'
(1+d⍸⍵)⊃s
}``````

For working with irregular-sized non-overlapping intervals, the pre-eminent function is `⍸`, interval index. As with the Chinese zodiac, essentials of the computation are brought into sharper relief if each zodiac sign is denoted by a single character:

``````   zen←{
d←12 2⍴ 1 20  2 19  3 21  4 20  5 21  6 21  7 23  8 23  9 23  10 23  11 22  12 22
(1+d⍸⍵)⊃13⍴'♑♒♓♈♉♊♋♌♍♎♏♐'
}``````

The single-character signs, Unicode U+2648 to U+2653, were found by Google search and then confirmed by https://www.visibone.com/htmlref/char/cer09800.htm. It is possible that the single-character signs do not display correctly in your browser; the line of code can be expressed alternatively as `(1+d⍸⍵)⊃13⍴⎕ucs 9800+12|8+⍳12`.

6. What’s Your Angle?

Check that angle brackets are balanced and not nested.

``   balanced ← {(∧/c∊0 1)∧0=⊃⌽c←+\1 ¯1 0['<>'⍳⍵]}``

In APL, functions take array arguments, and so too indexing takes array arguments, including the indices (the “subscripts”). This fact is exploited to transform the argument string into a vector `c` of `1` and `¯1` and `0`, according to whether a character is `<` or `>` or “other”. For the angles to be balanced, the plus scan (the partial sums) of `c` must be a `0-1` vector whose last element is `0`.

The closely related problem where the brackets can be nested (e.g. where the brackets are parentheses), can be solved by checking that `c` is non-negative, that is, `∧/(×c)∊0 1` (and the last element is `0`).

7. Unconditionally Shifty

Shift a boolean vector `⍵` by `⍺`, where positive means a right shift and negative means a left shift.

``   shift ← {(≢⍵)⍴(-⍺)⌽⍵,(|⍺)⍴0}``

The problem solution is facilitated by the rotate function `⍺⌽⍵`, where a negative `⍺` means rotate right and positive means rotate left. Other alternative unguarded code can use `↑` or `↓` (take or drop) where a negative `⍺` means take (drop) from the right and positive means from the left.

8. Making a Good Argument

Check whether a numeric left argument to `⍺⍉⍵` is valid.

``   dta ← {0::0 ⋄ 1⊣⍺⍉⍵}``

This is probably the shortest possible solution: Return `1` if `⍺⍉⍵` executes successfully, otherwise the error is trapped and a `0` is returned. A longer but more insightful solution is as follows:

``   dt ← {((≢⍺)=≢⍴⍵) ∧ ∧/⍺∊⍨⍳(≢⍴⍵)⌊(×≢⍺)⌈⌈⌈/⍺}``

The first part of the conjunction checks that the length of `⍺` is the same as the rank of `⍵`. (Many APL authors would write `⍴⍴⍵`; I prefer `≢⍴⍵` because the result is a scalar.) The second part checks the following properties on `⍺`:

• all elements are positive
• the elements (if any) form a dense set of integers (from `1` to `⌈/⍺`)
• a (necessarily incorrect) large element would not cause the `⍳` to error

The three consecutive `⌈⌈⌈`, each interpreted differently by the system, are quite the masterstroke, don’t you think? ☺

9. Earlier, Later, or the Same

Return `¯1`, `1`, or `0` according to whether a timestamp is earlier than, later than, or simultaneous with another.

``   ts ← {⊃0~⍨×⍺-⍵}``

The functions works by returning the first non-zero value in the signum of the difference between the arguments, or `0` if all values are zero. A tacit solution of same: `ts1 ← ⊃∘(~∘0)∘× -` .

10. Anagrammatically Correct

Determine whether two character vectors or scalars are anagrams of each other. Spaces are not significant. The problem statement is silent on this, but I am assuming that upper/lower case is significant.

``   anagram ← {g←{{⍵[⍋⍵]}⍵~' '} ⋄ (g ⍺)≡(g ⍵)}``

The function works by first converting each argument to a standard form, sorted and without spaces, using the local function `g`, and then comparing the standard forms. In `g`, the idiom `{⍵[⍋⍵]}` sorts a vector and the phrase `⍵~' '` removes spaces and finesses the problem of scalars.

A reasonable tacit solution depends on the over operator `⍥`, contemplated but not implemented:

``````     f⍥g ⍵  ←→  f g ⍵
⍺ f⍥g ⍵  ←→  (g ⍺) f (g ⍵)``````

A tacit solution written with over: `≡⍥((⊂∘⍋ ⌷ ⊢)∘(~∘' '))`. I myself would prefer the semi-tacit `≡⍥{{⍵[⍋⍵]}⍵~' '}`.

# Is it Sorted?

## Motivation

I have been working on the Dyalog APL quicksort implementation. The following programming puzzle arose in the process of doing the QA for this work.

`⍵` is a simple array. Write a function `sorted`, without using `⍋` or `⍒`, such that `sorted ⍵` is 1 if `⍵` is sorted in ascending order and 0 otherwise.

The point about not using grade is that this is supposed to be an independent check that grade is correct (remains correct) after the new work.

## Real Vectors

The simplest case is when `⍵` is a numeric vector. If furthermore `⍵` are not complex numbers (a case addressed later), then

``````   ∧/ 2≤/⍵
``````

each item being less than or equal to the next one, checks that `⍵` is sorted. Since `⍋` uses exact comparisons, here we must set `⎕ct←⎕dct←0`. Morever, in order that decimal floating-point numbers (DECFs) be compared correctly, here `⎕fr←1287`.

## Real Arrays

More generally, when `⍵` is a non-complex numeric matrix, we must check that each row precedes or is equal to the next row. If `c` and `d` are consecutive rows, then corresponding items are compared and at the first item where they differ, `c[i]` must be less than `d[i]`.

``````   ~ 0 ∊ (2>⌿⍪⍵) ⍲ <\ 2≠⌿⍪⍵
``````

The expression incorporates two refinements:

• If `⍵` is not a matrix, first apply `⍪⍵`.
• Instead of checking `c[i]` is less than `d[i]`, check that `c[i]` is not greater than `d[i]`. This finesses the case where `c≡d` and there is no first item where they differ; that is, the case where `<\2≠⌿⍪⍵` is all 0s for that row.

`<\`on a boolean vector has 0s after the first 1, (and is all 0 if there are no 1s). Therefore, `<\2≠⌿⍪⍵` finds the first item (if any) where one cell differs from the next cell, and that item must not be greater than the corresponding item in the next cell.

For example:

``````   x←?97 3⍴10

{~ 0 ∊ (2>⌿⍪⍵) ⍲ <\ 2≠⌿⍪⍵} x
0
{~ 0 ∊ (2>⌿⍪⍵) ⍲ <\ 2≠⌿⍪⍵} x[⍋x;]
1
``````

(Muse: since `x` above are random numbers, there is a possibility that it is sorted and the first test above can be 1. But: if each elementary particle in the visible universe were a computer and every nanosecond each of them creates a random matrix and tests it for sortedness as above, running from the beginning of the time to the end of our lives, it is still a very safe bet that no 1 would result.)

For integer arrays, there is an alternative of using the signs of the arithmetic difference between successive cells:

``````   {~ 0 ∊ 1≠t×<\0≠t← × 2-⌿⍪⍵} x[⍋x;]
1
``````

(The sign where consecutive cells first differ must not be 1.) However, computing the difference on floating point numbers can founder on overflow:

``````   ⊢ x←¯1 1×⌊/⍬
¯1.79769E308 1.79769E308

{~ 0 ∊ 1≠t×<\0≠t← × 2-⌿⍪⍵} x
DOMAIN ERROR
{~0∊1≠t×<\0≠t←×2-⌿⍪⍵}x
∧
``````

## Complex Numbers

Two complex numbers are ordered first by the real parts and then by the imaginary parts. (This is part of the TAO extension implemented in Dyalog APL version 17.0.) Therefore, a complex array can be tested for sortedness by testing an equivalent real array with each number replaced by their real and imaginary parts, thus:

``````   (¯1⌽⍳1+⍴⍴⍵) ⍉ 9 11∘.○⍵
↑9 11∘○¨⍵
9 11○⍤1 0⊢⍵
``````

Although the second expression is the shortest, it is less efficient in time, space, and number of `getspace` calls. The last expression is favored for its brevity and performance.

The number of `getspace` is a worthwhile measure. Part of the QA process is a rather stringent procedure called the “Shuffle QA”. The entire Shuffle QA takes several weeks to run and its running time is directly related to the number of `getspace`.

## Character Arrays

None of the functions `< ≤ ≥ > - ×` are permitted on characters. This is solved by application of `⎕ucs`, converting characters to integers while preserving the ordering.

## Putting It All Together

``````sorted←{
⎕ct←⎕dct←0
⎕fr←1287
d←10|⎕dr ⍵
d∊0 2: ∇ ⎕ucs ⍵
d=9:   ∇ 9 11○⍤1 0⊢⍵
~ 0 ∊ (2>⌿⍪⍵) ⍲ <\ 2≠⌿⍪⍵
}
``````

## Other Considerations

That `⍵⌷⍨⊂⍋⍵` is sorted is a necessary but not sufficient condition that `⍋⍵` is correct. For example, an “adversary” can supply the following results for `⍋⍵` so that `⍵⌷⍨⊂⍋⍵` is sorted:

``````?≢⍵
(≢⍵)⍴?≢⍵
¯1↓⍋⍵
∊ i {⊂⍵[?⍨≢⍵]}⌸⍨ ⍵⌷⍨⊂i←⍋⍵
``````

The last expression randomly permutes the grade indices of equal cells, a result which violates the requirement that grade indices of equal cells are in ascending order. That is, grade must be stable.

In Dyalog APL version 17.0, grade has been extended to work on non-simple arrays, the much discussed TAO, total array ordering. Checking that a non-simple array is sorted without using grade requires facilities discussed in the paper TAO Axioms and is beyond the scope of this note.

`⎕io=0` is assumed throughout. The essay talks only about `⍋` but the same ideas apply to `⍒`.

## Background

`⍋` has the distinction of being the first (in 1980) APL primitive function defined on major cells: the result orders items of a vector, rows of a matrix, planes of a 3-d array, etc. In the ordering major cells are compared in ravelled order, with leading items being more significant than trailing (lexicographic ordering). Moreover, in dyadic grade `⍺⍋⍵`, `⍺` specifies “alphabets” to be used in comparing the items of character array `⍵`.

Dyadic grade has always been an APL primitive which is hard for me to understand, in that way kind of like dyadic transpose ☺. I sat down to really understand it, starting from the simplest cases to the general case. The following is a record of my explorations.

## Vector Left Argument

``````   gv← {⍋⍺⍳⍵}

a0← 'abcdefghij'
x0← 'chthonic'

a0 gv x0
0 7 1 3 6 2 4 5
a0 ⍋ x0
0 7 1 3 6 2 4 5

x0 ⌷⍨ ⊂ a0 gv x0
cchhiton
``````

That is, grade the indices of `⍵` in `⍺`. If an item of `⍵` is not in `⍺` then its index is `≢⍺`.

## Higher-Rank Left Argument with Unique Items

The coordinates of `A[i;j;k;…]` or `A[⊂i,j,k,…]` is the vector `i,j,k,…`. The phrase `⍳⍴A` produces the array of coordinates. For example, if `⍺` is the `(2 26)`-matrix of the upper and lower case English letters,

``````   ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
``````

the corresponding coordinates are

``````   ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬   ┬────┬────┐
│0 0│0 1│0 2│0 3│0 4│0 5│0 6│0 7│0 8│   │0 24│0 25│
├───┼───┼───┼───┼───┼───┼───┼───┼───┼ … ├────┼────┤
│1 0│1 1│1 2│1 3│1 4│1 5│1 6│1 7│1 8│   │1 24│1 25│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴   ┴────┴────┘
``````

If the items of `⍺` are unique,

``````   gu← {⍋ 0 2 1 ⍉ (⊂(,⍺)⍳⍪⍵) ⌷ ⌽ (⍴⍺) ⍪⍨ ⍉(⍴⍺)⊤⍳×/⍴⍺}
``````

That is, `⍺⍋⍵` obtains as the grade of the reversed coordinates of `⍵` in `⍺`. (If an item does not occur in `⍺`, its coordinates are `⍴⍺`.) The `⌽` implements that in `⍺`, the first axis is least significant and the last axis is most significant. For the `(2 26)`-matrix above, case (the first axis) is less significant than `A-Z` and `a-z` (the last axis).

``````   ⊢ a1←' ',⎕av[(⎕av⍳'Aa')∘.+⍳26]
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz

⊢ x1←↑' '(≠⊆⊢)' Jay roger Roger adam Adam jay'
Jay
roger
Roger
jay

a1 gu x1
4 3 0 5 2 1
a1 ⍋ x1
4 3 0 5 2 1

x1 ⌷⍨ ⊂ a1 gu x1
Jay
jay
Roger
roger
``````

## Higher-Rank Left Arguments

Suppose `⍺` does have duplicates? For purposes of `⍋`, the coordinates of an item `c` are

``   ⌊⌿(c=,⍺)⌿↑,⍳⍴⍺``

That is, the minimum of coordinates of all items equal to `c`. Note that the expression also works if `c` is a unique item. Therefore, for a general `⍺`, with or without duplicates, `⍺⍋⍵` obtains as

``````   gr← {⍋ 0 2 1 ⍉ (⊂(∪,⍺)⍳⍪⍵) ⌷ ⌽ (⍴⍺) ⍪⍨ (,⍺) {⌊⌿⍵}⌸ ⍉(⍴⍺)⊤⍳×/⍴⍺}
``````

The “minimum of coordinates” computation is exploited to effect equal coodinates for disparate characters. For example, an ordering where upper and lower case are significant but diacritical marks are not, can be implemented as follows:

``````   A    ⍝ A has a leading blank column
AÀÁÂÃÄÅBCÇDEÈÉÊËFGHIÌÍÎÏJKLMNÑOÒÓÔÕÖØPQRSTUÙÚÛÜVWXYÝZ
aàáâãäåbcçdeèéêëfghiìíîïjklmnñoòóôõöøpqrstuùúûüvwxyýz
À       Ç  È       Ì        Ñ Ò                   Ý
Á       ç  É       Í        ñ Ó                   ý
Â          Ê       Î          Ô
Ã          Ë       Ï          Ö
Ä          è       ì          Õ
Å          é       í          Ø
à          ê       î          ò
á          ë       ï          ó
â                             ô
ã                             õ
ä                             ö
å                             ø
⍴A
14 54

('È'=,A)⌿↑,⍳⍴A                ('è'=,A)⌿↑,⍳⍴A
0 13                          1 13
2 12                          6 12
⌊⌿('È'=,A)⌿↑,⍳⍴A              ⌊⌿('è'=,A)⌿↑,⍳⍴A
0 12                          1 12

('E'=,A)⌿↑,⍳⍴A                ('e'=,A)⌿↑,⍳⍴A
0 12                          1 12
⌊⌿('E'=,A)⌿↑,⍳⍴A              ⌊⌿('e'=,A)⌿↑,⍳⍴A
0 12                          1 12
``````

`'È'` occurs twice in `A` with coordinates `0 13` and `2 12`. The coordinates assigned to `'È'` are the minimum of these, `0 12`. In contrast, `'E'` occurs once and its coordinates are `0 12`, the same as those for `'È'`. Therefore, `'E'` and `'È'` are considered equal for purposes of dyadic grade. Similarly, `'e'` and `'è'` have coordinates `1 12` and are considered equal by `⍋`, but they follow `'E'` and `'È'` (because their coordinates are `0 12`).

For example:

``````   ⊢ x← ↑' '(≠⊆⊢)' roger adàm Röger rÖger Adåm JÃY JAY JÃY adåm adàm'
roger
Röger
rÖger
JÃY
JAY
JÃY

A gr x
4 1 8 9 5 6 7 2 3 0
A ⍋ x
4 1 8 9 5 6 7 2 3 0

x ⌷⍨⊂ A gr x
JÃY
JAY
JÃY
Röger
rÖger
roger
``````

Lest you think that dyadic grade in its full generality suffices to implement any ordering: in “telephone book” ordering, “1600 Pennsylvania Avenue” and “Unter den Linden 23” are ordered as if 1600 were spelled out as “Sixteen Hundred” and 23 as “Dreiundzwanzig”. A program to do that ought to be très amusant.

## Code Archeology

The above code are improved versions of what appeared in Peter Wooster, Extended Upgrade and Downgrade, SHARP APL Technical Notes 35, I.P. Sharp Associates, 1980-09-15. It is interesting to study the code from the two eras. (The code from 1980 is lightly edited for executability and clarity.)

2018

``````gv← {⍋⍺⍳⍵}
gu← {⍋ 0 2 1 ⍉ (⊂(,⍺)⍳⍪⍵) ⌷ ⌽ (⍴⍺) ⍪⍨ ⍉(⍴⍺)⊤⍳×/⍴⍺}
gr← {⍋ 0 2 1 ⍉ (⊂(∪,⍺)⍳⍪⍵) ⌷ ⌽ (⍴⍺) ⍪⍨ (,⍺) {⌊⌿⍵}⌸ ⍉(⍴⍺)⊤⍳×/⍴⍺}
``````

1980

``````eu← {d⊤⍳×/d←⍴⍵}
er← {¯1+÷(÷1+d⊤⍳×/d←⍴⍵)⌈.×a∘.=a←,⍵}

fv← {⍋⍺⍳⍵}
fu← {⍋(⍒0 1,1↓0×⍳⍴⍴⍵)⍉(⊖(eu ⍺),⍴⍺)[;(,⍺)⍳⍵]}
fr← {⍋(⍒0 1,1↓0×⍳⍴⍴⍵)⍉(⊖(er ⍺),⍴⍺)[;(,⍺)⍳⍵]}
``````
 `gv, fv` vector left argument `gu, fu` higher-ranked left argument with unique items `gr, fr` higher-ranked left argument

In the sequence `gv gu gr`, a function is more general than the preceding one and subsumes it. Likewise `fv fu fr`.

Comparison of the code illustrates advances in APL between 1980 and 2018:

• `{⌊⌿⍵}⌸ `minimum of major cells corresponding to identical keys
• `∪      `unique items
• `⍪⍵     `ravel major cells
• `⍺⍪⍵    `catenate on first axis
• `⍨      `commute operator
• dfns

## Alternatives

If a left argument is large and complicated and is used repeatedly, it may be worthwhile for the APL interpreter to perform precomputations on it. Thus:

``````   U← ∪,A
C← ⌽ (⍴A) ⍪⍨ (,A) {⌊⌿⍵}⌸ ⍉(⍴A)⊤⍳×/⍴A

⍴U        ⍴C
107       108 2

⍪U        C
0  0
A          1  0
À          1  0
Á          1  0
Â          1  0
Ã          1  0
Ä          1  0
Å          1  0
B          8  0
C          9  0
Ç          9  0
…           …
x         50  1
y         51  1
ý         51  1
z         53  1
14 54

gp← (U C)∘{U C←⍺ ⋄ ⍋0 2 1⍉C[U⍳⍪⍵;]}

gp x
4 1 8 9 5 6 7 2 3 0
A ⍋ x
4 1 8 9 5 6 7 2 3 0
``````

It makes sense that the number of columns in the coordinate matrix `C` is equal to the rank of the alphabet array `A`: The rank is the number of dimensions, a-z, upper/lower case, color, etc.; each row of `C` is a vector of the numeric value for each dimension.

With 20/20 hindsight, the above code can be seen as an argument against defining dyadic grade to do ordering with specified alphabets. After all,

``````   ⍺⍋⍵  ←→  ⍋0 2 1⍉C[U⍳⍪⍵;]
``````

and specifying `U` and `C` directly makes the computation easier to understand, easier to use, and as it happens is faster than the primitive in the current implementation. The inverse calculation, from `U C` to the alphabet array `A`, is an amusing bit of code left as an exercise for the reader☺.

One can further argue that the current definition of dyadic grade precludes an alternative attractive but incompatible definition:

``````   ⍺⍋⍵  ←→  ⍺⌷⍨⊂⍋⍵
``````

That is, order `⍺` by the grade of `⍵`, whence `⍋⍨` sorts. In Dyalog APL version 17.0, monadic grade is extended to work with a TAO (total array ordering). With a TAO and this alternative definition, `⍋⍨` sorts any array.

The present exposition exposes a difficulty with extending the current dyadic grade to work with TAO: It is axiomatic that monadic grade compares cells itemwise, stopping at the first pair of unequal items. Dyadic grade does not do that in general. For example, with an upper and lower case alphabet, you don’t stop comparing `'Rogerz'` and `'rogers'` on encountering `'R'` and `'r'`.