1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
//! Sort order: which direction a comparator points.
//!
//! A sorting network is a fixed sequence of compare-exchanges, and a
//! compare-exchange is fully described by "which of the two values goes to the
//! lower index". [`SortOrder`] is that choice, as a compile-time parameter, so
//! one network body serves every direction.
//!
//! # Why this is free
//!
//! Flipping every comparator in an *ascending* network yields a *descending*
//! one - for any network, not just the bitonic ones here. If `f` is an
//! order-reversing bijection then `min(f a, f b) = f(max(a, b))`, so a flipped
//! network `N'` satisfies `N'(x) = f^-1(N(f(x)))`: the same permutation network
//! run on reversed values, which is `x` sorted descending.
//!
//! At the machine level the flip is not even an extra instruction. A network
//! layer is `permute`, `min`, `max`, `blend`; reversing it swaps which of the
//! `min`/`max` results feeds the blend's true arm. Measured on znver3 (llvm-mca,
//! `crates/sortasm` probe), `f32x8`:
//!
//! | | insns | `vminps` | `vmaxps` | shuffles | RThroughput | latency |
//! |---|---|---|---|---|---|---|
//! | [`Ascending`] | 29 | 6 | 6 | 8 | 9.0 | **43 cyc** |
//! | [`Descending`] | 29 | 6 | 6 | 8 | 9.0 | **43 cyc** |
//! | ascending then `reverse` | 31 | 6 | 6 | 11 | 8.5 | 50 cyc |
//!
//! So a descending sort costs exactly nothing, where post-processing an
//! ascending one with [`reverse`](crate::register::Register::reverse) costs 3
//! shuffles and 16% latency. The counts of `min` and `max` are unchanged in both
//! directions because each layer issues one of each regardless; only the blend
//! operands swap.
//!
//! # Why the methods are generic, not the trait
//!
//! The obvious shape parameterizes the trait by the register
//! (`trait SortOrder<R: NumericRegister>`). It serves native registers fine and
//! then hard-blocks on the first *delegating* one: inside
//! `ArrayRegister::<R, N>::sort_by::<O>`, calling `R::sort_by::<O>` needs
//! `O: SortOrder<R>`, which `O: SortOrder<Self>` does not imply - and the bound
//! cannot be added, because the method signature is fixed by the trait
//! declaration, which has no `R` to name. Each nesting level would want another
//! bound.
//!
//! Generic *methods* keep the bound flat: `O: SortOrder` is the whole
//! requirement at every layer, whatever the register, however deeply nested.
use crate;
use crateNumericVector;
/// The direction a comparator points, as a compile-time parameter.
///
/// Implementors are zero-sized markers ([`Ascending`], [`Descending`]) selected
/// with turbofish - `R::sort_by::<Descending>(v)`. Both methods must be a
/// consistent pair: `first` and `last` are the two halves of one
/// compare-exchange, so for any `a`, `b` the multiset `{first(a,b), last(a,b)}`
/// must equal `{a, b}`, and `first(a,b)` must not come after `last(a,b)` under
/// the order. Violating that does not just misorder - it duplicates and drops
/// values, because a network never re-reads what it overwrote.
///
/// Deliberately only these two methods. They are the whole of what a *network*
/// needs; the padding sentinels (a value that sorts after everything, for
/// filling a partial register) and the members a partition-based sort would want
/// (`compare`, `prev_value`) get added when something calls them.
/// Sort smallest-first. The default everywhere a direction is not named.
;
/// Sort largest-first.
///
/// Costs exactly the same as [`Ascending`] - see the module docs.
;
// ---------------------------------------------------------------------------
// Compare-exchange layer descriptions
//
// Pure index math over a lane count - no register, no vector, no layer. Both
// the register-layer networks (`backend::generic::polyfills::sort`) and the
// vector-layer block sort (`thermite_sort::merge`) build their layers from
// these, and there is no reason for two copies of the arithmetic.
// ---------------------------------------------------------------------------
use crateSwizzleIndices;
use ;
/// Lane `i` faces lane `i ^ K`: the partner set of a distance-`K`
/// compare-exchange, and the shuffle behind Highway's `SortPairsDistance{K}` /
/// `SwapAdjacentPairs` / `SwapAdjacentQuads`.
///
/// `K >= N` is clamped to the identity rather than left out of range. Such a
/// stage is dead code that a caller's `if const` ladder never reaches, but its
/// `INDICES` are still const-evaluated, and an out-of-range swizzle index is
/// undefined behavior by contract rather than merely unused.
;
/// Lane `i` faces its mirror within its contiguous group of `K` lanes - the
/// shuffle behind Highway's `ReverseKeys{K}` and `SortPairsReverse{K}`.
///
/// `K > N` is clamped to a full reverse; see [`XorIdx`] for why that matters.
;
/// Lanes whose index has `bit` set - the lanes that receive
/// [`SortOrder::last`] of their pair. `bit == 0` means no lane does.
///
/// Bits at or above the lane count are ignored by
/// [`from_native_bitmask`](crate::mask::GenericMask::from_native_bitmask), so
/// one 64-bit constant serves every width.
pub const
/// One in-register compare-exchange layer: a partner permutation, plus the lane
/// mask saying which side of each pair keeps the later element.
///
/// A network layer is `permute` + `first` + `last` + `blend`, and this is the
/// half of it that varies. Splitting it out as a type rather than two const
/// parameters is what lets [`RevPairs`] derive its mask from `K / 2` - a const
/// *expression*, which is stable, where a const-generic expression is not.
/// Compare-exchange lane `i` with lane `i ^ K` (Highway's
/// `SortPairsDistance{K}`). The high lane of each pair keeps the last.
///
/// The halving strides `LANES/2 .. 1` of this are exactly a bitonic cleanup.
;
/// Compare-exchange each lane with its mirror in its group of `K` (Highway's
/// `SortPairsReverse{K}`).
///
/// The upper *half* of each reversed group keeps the last, so the blend mask is
/// the distance-`K/2` one. That identity is what collapses the two families of
/// odd-even blend (`OddEvenKeys` / `OddEvenPairs` / `OddEvenQuads`) onto one
/// [`keep_bits`].
;
// ---------------------------------------------------------------------------
// Key-driven lane sorts at the vector layer
//
// The register-layer networks move *elements* through `min`/`max`, which is
// only correct when a lane is one value. A composite vector (autodiff dual,
// compensated pair, complex) is several component vectors whose lanes move as
// a unit under a KEY comparison - the primal for `Dual`, the value for
// `Compensated`, lexicographic (re, im) for `Complex`. These run the exact
// same stage sequence (`RevPairs`/`Distance`, the depth-minimal construction
// behind `sort_lanes`), but each compare-exchange derives one *routing mask*
// from the key and applies it to every component with a single whole-vector
// `select` - which the composite's own `Mask::select` already does
// componentwise.
//
// The alternative - computing the sorting permutation of the key lanes and
// `permute`-ing every component once (an argsort) - costs fewer ops when the
// component count is large, since the network then only carries (key, index).
// At the component counts that exist today (2-5) the direct form is at parity
// or better and needs no index machinery; revisit if a many-component
// composite shows up hot.
// ---------------------------------------------------------------------------
use crateGenericMask;
use crateGenericVector;
/// The comparison a key-driven lane sort routes on: `key_lt(a, b)` masks the
/// lanes of `a` strictly before those of `b` under the type's sort key.
///
/// A static trait method rather than an `F: Fn` parameter ON PURPOSE. The
/// closure/fn-item form was measured leaving the comparison out of line - the
/// `Fn::call` shim through `&F` survived to codegen for the ternlog-carrying
/// lexicographic comparisons, one base-ISA `call` per network stage - and an
/// `#[inline(always)]` on a static method cannot be declined that way.
///
/// Requirements: a *strict* order test on the key alone (never the payload
/// components), consistent across lanes. Typically implemented by the
/// composite for itself (`impl SortKey<Self> for Self`) as its `cmp_lt` or a
/// component's `cmp_lt`.
/// One key-driven compare-exchange layer: partner permutation, two strict key
/// comparisons, one routing select applied to the whole vector.
///
/// **Both sides of a pair need their own strict test.** With one mask `m =
/// lt(partner, self)` and the keep-last side taking `!m`, a key *tie* routes
/// the same composite to both lanes - one value duplicated, its partner
/// dropped, invisibly to any test whose payloads tie too. Two strict tests
/// (`lt(partner, self)` for keep-first lanes, `lt(self, partner)` for
/// keep-last) make every tied pair keep itself on both sides, so the layer is
/// a permutation for every input.
/// Sort the lanes of `v` in `O` order of `K`'s key. Whole lanes move
/// together: every component of a composite follows its key.
///
/// Same depth-minimal construction as the register-layer `sort_lanes` (the
/// widening tails at one chunk); covers power-of-two lane counts up to 16.
/// Ties by key keep the input's lane order within each compare-exchange (see
/// `key_stage`) but the sort as a whole is not stable.
/// [`sort_lanes_by_key`] for an already-**bitonic** vector: the halving
/// compare-exchange strides `LANES/2 .. 1`. Garbage in, garbage out on
/// non-bitonic input, exactly like the register-layer `bitonic_clean_lanes`.