carray-jit 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +917 -3
  3. data/README.md +14 -12
  4. data/carray-jit.gemspec +1 -3
  5. data/docs/00_Introduction.md +5 -3
  6. data/docs/01_GettingStarted.md +1 -1
  7. data/docs/02_KernelShapes.md +94 -15
  8. data/docs/03_SupportedFeatures.md +665 -32
  9. data/docs/04_Compiling.md +34 -7
  10. data/docs/05_DesignNotes.md +3 -3
  11. data/docs/06_Cheatsheet.md +200 -7
  12. data/docs/07_StepByStep.ja.md +533 -0
  13. data/docs/07_StepByStep.md +534 -0
  14. data/docs/08_LanguageReference.md +427 -0
  15. data/examples/README.md +12 -0
  16. data/examples/applications/alarm.rb +121 -0
  17. data/examples/applications/collatz.rb +105 -0
  18. data/examples/applications/cubic_spline.rb +331 -0
  19. data/examples/applications/dithering.rb +144 -0
  20. data/examples/applications/group_stats.rb +115 -0
  21. data/examples/applications/lookup.rb +126 -0
  22. data/examples/applications/median_filter.rb +153 -0
  23. data/examples/applications/parcel_ascent.rb +220 -0
  24. data/examples/applications/point_in_polygon.rb +111 -0
  25. data/examples/applications/random_walk.rb +98 -0
  26. data/examples/applications/van_der_pol.rb +186 -0
  27. data/examples/applications/wet_bulb.rb +140 -0
  28. data/examples/features/10_complex.rb +14 -4
  29. data/examples/features/15_loops.rb +7 -1
  30. data/lib/carray/jit/access.rb +14 -0
  31. data/lib/carray/jit/analyzer.rb +2218 -140
  32. data/lib/carray/jit/block_reader.rb +37 -6
  33. data/lib/carray/jit/c_function.rb +961 -86
  34. data/lib/carray/jit/c_generator.rb +1970 -191
  35. data/lib/carray/jit/call.rb +68 -0
  36. data/lib/carray/jit/compiler.rb +102 -14
  37. data/lib/carray/jit/expression.rb +271 -33
  38. data/lib/carray/jit/kernel.rb +382 -32
  39. data/lib/carray/jit/node.rb +374 -9
  40. data/lib/carray/jit/sorting_networks.rb +182 -0
  41. data/lib/carray/jit/type_assignment.rb +424 -35
  42. data/lib/carray/jit/version.rb +1 -1
  43. data/lib/carray/jit.rb +547 -77
  44. metadata +23 -8
  45. data/ext/carray_jit_access/carray_jit_access.c +0 -460
  46. data/ext/carray_jit_access/extconf.rb +0 -8
@@ -0,0 +1,534 @@
1
+ # 31 carray-jit exercises, with solutions
2
+
3
+ Thirty-one small tasks, in the order the documentation introduces things, with a
4
+ solution under each. Roughly in order of difficulty -- the stars say how much
5
+ of the subset a task leans on, not how much code it takes.
6
+
7
+ They run as written. Start with
8
+
9
+ ```ruby
10
+ require "carray"
11
+ require "carray/jit"
12
+ ```
13
+
14
+ and every `#=>` is what that line actually answers.
15
+
16
+ Read the task, try it, then look. Where the answer is that the compiler
17
+ refuses something, the refusal is the answer and is printed under the code.
18
+
19
+ ---
20
+
21
+ #### 1. Compute `a + b * c` into `out` in a single pass, with no array built in between (★☆☆)
22
+
23
+ `hint: the method for work that reaches no neighbour`
24
+
25
+ ```ruby
26
+ a = CA_DOUBLE([1, 2, 3])
27
+ b = CA_DOUBLE([2, 3, 4])
28
+ c = CA_DOUBLE([3, 4, 5])
29
+ out = CArray.double(3)
30
+
31
+ CArray.jit_each { out = a + b * c }
32
+ out.to_a #=> [7.0, 14.0, 23.0]
33
+ ```
34
+
35
+ #### 2. Compute `(a + b * c) / 2` over three int32 arrays, taking the result back as a new array. What is its data type? (★☆☆)
36
+
37
+ `hint: the other element-wise method`
38
+
39
+ ```ruby
40
+ a = CA_INT32([1, 2, 3])
41
+ b = CA_INT32([2, 3, 4])
42
+ c = CA_INT32([3, 4, 5])
43
+
44
+ half = CArray.jit_map { (a + b * c) / 2.0 }
45
+ half.to_a #=> [3.5, 7.0, 11.5]
46
+ half.data_type_name #=> "float64"
47
+ # typed from the block's last value, not from the arrays: `2.0` has no width
48
+ # of its own, so it takes the one it meets. Without it, int64.
49
+ ```
50
+
51
+ #### 3. Add a row of three to every row of a 2x3 table into `out`, without copying the row (★☆☆)
52
+
53
+ `hint: CArray will broadcast an axis, but not invent a rank`
54
+
55
+ ```ruby
56
+ table = CArray.double(2, 3).seq!(1.0)
57
+ row = CA_DOUBLE([10, 20, 30]).reshape(1, 3) # 1x3, not 3: else `ndim mismatch`
58
+ out = CArray.double(2, 3)
59
+
60
+ CArray.jit_each { out = table + row }
61
+ out.to_a #=> [[11.0, 22.0, 33.0], [14.0, 25.0, 36.0]]
62
+ ```
63
+
64
+ #### 4. Scale an array by a Float the block closes over, twice with different values and compiling only once. Collect both results (★☆☆)
65
+
66
+ `hint: nothing special is needed`
67
+
68
+ ```ruby
69
+ a = CA_DOUBLE([1, 2])
70
+ out = CArray.double(2)
71
+
72
+ [2.0, 5.0].map { |gain|
73
+ CArray.jit_each { out = a * gain } # a captured scalar is an argument,
74
+ out.to_a # not a constant compiled in
75
+ } #=> [[2.0, 4.0], [5.0, 10.0]]
76
+ ```
77
+
78
+ #### 5. Scale an array into `out` by a factor that carries a data type of its own (★☆☆)
79
+
80
+ `hint: CScalar`
81
+
82
+ ```ruby
83
+ a = CA_DOUBLE([1, 2])
84
+ gain = CScalar.double() { 2.5 }
85
+ out = CArray.double(2)
86
+
87
+ CArray.jit_each { out = a * gain }
88
+ out.to_a #=> [2.5, 5.0]
89
+ ```
90
+
91
+ #### 6. Write each value into `out`, with the negatives clipped to zero (★☆☆)
92
+
93
+ `hint: the branch is an expression`
94
+
95
+ ```ruby
96
+ a = CA_DOUBLE([-2, 0, 1.5, 3])
97
+ out = CArray.double(4)
98
+
99
+ CArray.jit_each { out = a < 0.0 ? 0.0 : a }
100
+ out.to_a #=> [0.0, 0.0, 1.5, 3.0]
101
+ ```
102
+
103
+ #### 7. Write a vector into `out` reversed (★☆☆)
104
+
105
+ `hint: the cell has to say which cell it reads`
106
+
107
+ ```ruby
108
+ a = CArray.double(5).seq!(1.0)
109
+ out = CArray.double(5)
110
+
111
+ CArray.jit_for(5) { |i| out[i] = a[4 - i] }
112
+ out.to_a #=> [5.0, 4.0, 3.0, 2.0, 1.0]
113
+ ```
114
+
115
+ #### 8. First differences: `out[i] = a[i] - a[i-1]`, leaving `out[0]` at zero (★☆☆)
116
+
117
+ `hint: where can the range start?`
118
+
119
+ ```ruby
120
+ a = CA_DOUBLE([1, 3, 6, 10])
121
+ out = CArray.double(4)
122
+
123
+ CArray.jit_for(1...4) { |i| out[i] = a[i] - a[i - 1] }
124
+ out.to_a #=> [0.0, 2.0, 3.0, 4.0]
125
+ # 1... and not 0...: bounds are checked against the subscript as written,
126
+ # before the loop runs, so a guard inside the body would not help.
127
+ ```
128
+
129
+ #### 9. `out[i] = 0.5 * a[i] + 0.5 * out[i-1]`, with `out[0] = a[0]` (★★☆)
130
+
131
+ `hint: read what this loop just wrote`
132
+
133
+ ```ruby
134
+ a = CA_DOUBLE([4, 8, 8, 8])
135
+ out = CArray.double(4)
136
+ out[0] = a[0]
137
+
138
+ CArray.jit_for(1...4) { |i| out[i] = 0.5 * a[i] + 0.5 * out[i - 1] }
139
+ out.to_a #=> [4.0, 6.0, 7.0, 7.5]
140
+ ```
141
+
142
+ #### 10. Write a 2x3 into `out` transposed, as a 3x2 (★☆☆)
143
+
144
+ `hint: two parameters, two extents`
145
+
146
+ ```ruby
147
+ a = CArray.double(2, 3).seq!(1.0)
148
+ out = CArray.double(3, 2)
149
+
150
+ CArray.jit_for(2, 3) { |i, j| out[j, i] = a[i, j] }
151
+ out.to_a #=> [[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]]
152
+ ```
153
+
154
+ #### 11. Write the largest value of each row into `out` (★☆☆)
155
+
156
+ `hint: a loop inside the cell`
157
+
158
+ ```ruby
159
+ a = CA_DOUBLE([[1, 3, 2], [9, 4, 5]])
160
+ out = CArray.double(2)
161
+
162
+ CArray.jit_for(2) { |i|
163
+ best = a[i, 0]
164
+ (1...3).each { |j| best = a[i, j] if a[i, j] > best }
165
+ out[i] = best
166
+ }
167
+ out.to_a #=> [3.0, 9.0]
168
+ ```
169
+
170
+ #### 12. Total an int32 array into a single value the kernel writes (★☆☆)
171
+
172
+ `hint: a local does not survive the iteration`
173
+
174
+ ```ruby
175
+ a = CArray.int32(10).seq!(1)
176
+ total = CScalar.int32() { 0 }
177
+
178
+ CArray.jit_for(10) { |i| total[] += a[i] }
179
+ total[0] #=> 55
180
+ ```
181
+
182
+ #### 13. Given the bin number of each reading, count the readings of every bin into `counts` (★★☆)
183
+
184
+ `hint: the subscript is a value, not the index`
185
+
186
+ ```ruby
187
+ bin = CA_INT32([0, 2, 1, 1, 0, 1])
188
+ counts = CArray.int32(3)
189
+
190
+ CArray.jit_for(6) { |i| counts[bin[i]] += 1 }
191
+ counts.to_a #=> [2, 3, 1]
192
+ ```
193
+
194
+ #### 14. Write the distance from the origin of each (x, y) into `out` (★☆☆)
195
+
196
+ `hint: Math`
197
+
198
+ ```ruby
199
+ x = CA_DOUBLE([3, 5])
200
+ y = CA_DOUBLE([4, 12])
201
+ out = CArray.double(2)
202
+
203
+ CArray.jit_each { out = Math.sqrt(x * x + y * y) }
204
+ out.to_a #=> [5.0, 13.0]
205
+ ```
206
+
207
+ #### 15. Give `jit_each` a block that names an index, and `jit_for` one that names none. What comes back? (★★☆)
208
+
209
+ `hint: neither guesses`
210
+
211
+ ```ruby
212
+ a = CA_DOUBLE([1, 2, 3])
213
+ out = CArray.double(3)
214
+
215
+ CArray.jit_each { |i| out[i] = a[i] + 1.0 }
216
+ #=> CArray::JIT::Unsupported: this block names the arrays it reaches, so it
217
+ # takes no parameters; for a loop that names its indices, see `jit_for`
218
+
219
+ CArray.jit_for(3) { out = a + 1.0 }
220
+ #=> CArray::JIT::Unsupported: jit_for's block names the cells it is on, so it
221
+ # takes the loop indices as its parameters; a block that names none is
222
+ # element-wise and belongs to jit_each
223
+ ```
224
+
225
+ #### 16. Write how many decimal digits each number has into `digits` (★★☆)
226
+
227
+ `hint: divide by ten until there is nothing left -- but how many times is that?`
228
+
229
+ ```ruby
230
+ value = CA_INT32([7, 42, 1000, 65536])
231
+ digits = CArray.int32(4)
232
+
233
+ CArray.jit_for(4) { |i|
234
+ n = value[i]
235
+ count = 0
236
+ while n > 0 # one cell goes round once, another five
237
+ n = n / 10 # integer division, and Ruby's: it floors
238
+ count = count + 1
239
+ end
240
+ digits[i] = count
241
+ }
242
+ digits.to_a #=> [1, 2, 4, 5]
243
+ ```
244
+
245
+ #### 17. Write the first column past 4.0 in each row into `first`, or -1 where there is none (★★☆)
246
+
247
+ `hint: stop looking`
248
+
249
+ ```ruby
250
+ rows = CA_DOUBLE([[1, 2, 9, 3], [5, 1, 1, 8]])
251
+ first = CArray.int32(2)
252
+
253
+ CArray.jit_for(2) { |i|
254
+ found = -1
255
+ (0...4).each { |j|
256
+ if rows[i, j] > 4.0
257
+ found = j
258
+ break
259
+ end
260
+ }
261
+ first[i] = found
262
+ }
263
+ first.to_a #=> [2, 0]
264
+ ```
265
+
266
+ #### 18. Multiply a 2x3 by a 3x2, taking the result back (★★☆)
267
+
268
+ `hint: the index that repeats`
269
+
270
+ ```ruby
271
+ a = CArray.double(2, 3).seq!(1.0)
272
+ b = CArray.double(3, 2).seq!(1.0)
273
+
274
+ CArray.jit_contract { |i, j, k| a[i, k] * b[k, j] }.to_a
275
+ #=> [[22.0, 28.0], [49.0, 64.0]]
276
+ ```
277
+
278
+ #### 19. For each row `i` of two 2x3 arrays, take back the sum over `k` of `x[i, k] * y[i, k]` (★★★)
279
+
280
+ `hint: the row index repeats too, and must not be summed`
281
+
282
+ ```ruby
283
+ x = CA_DOUBLE([[1, 2, 3], [4, 5, 6]])
284
+ y = CA_DOUBLE([[1, 1, 1], [2, 2, 2]])
285
+
286
+ CArray.jit_contract(:b) { |k| x[b, k] * y[b, k] }.to_a
287
+ #=> [6.0, 30.0]
288
+ # `b` is named in the call, not taken as a parameter; both at once is refused
289
+ ```
290
+
291
+ #### 20. Take back each cell plus its two neighbours, with a cell off the end reading as zero (★★☆)
292
+
293
+ `hint: no index, no extent, no edge`
294
+
295
+ ```ruby
296
+ a = CA_DOUBLE([1, 2, 3, 4])
297
+
298
+ CArray.jit_stencil(a, border: :zero) { |w| w[-1] + w[0] + w[1] }.to_a
299
+ #=> [3.0, 6.0, 9.0, 7.0]
300
+ # border: :zero, :clamp, :wrap compute the frame; :mask and :skip answer
301
+ # about the frame cells themselves
302
+ ```
303
+
304
+ #### 21. Write every cell into `out`, with `UNDEF` replaced by zero (★★☆)
305
+
306
+ `hint: ask about one cell`
307
+
308
+ ```ruby
309
+ a = CA_DOUBLE([1, 2, 3])
310
+ a[1] = UNDEF
311
+ out = CArray.double(3)
312
+
313
+ CArray.jit_for(3) { |i| out[i] = a[i] == UNDEF ? 0.0 : a[i] }
314
+ out.to_a #=> [1.0, 0.0, 3.0]
315
+ # where "propagate" is the answer wanted, write nothing: masks are ORed
316
+ # across the inputs and carried to the outputs on their own
317
+ ```
318
+
319
+ #### 22. Write 1, 2, 3 down the middle column of a 3x3, leaving the rest (★★☆)
320
+
321
+ `hint: hand the kernel the column`
322
+
323
+ ```ruby
324
+ table = CArray.double(3, 3)
325
+ column = table[nil, 1]
326
+
327
+ CArray.jit_for(3) { |i| column[i] = i + 1.0 }
328
+ table.to_a #=> [[0.0, 1.0, 0.0], [0.0, 2.0, 0.0], [0.0, 3.0, 0.0]]
329
+ ```
330
+
331
+ #### 23. Total a uint64 array through a local accumulator. What stops it, and what gets past it? (★★★)
332
+
333
+ `hint: a literal has no width of its own`
334
+
335
+ ```ruby
336
+ source = CArray.uint64(2).seq!(1)
337
+ out = CArray.uint64(1)
338
+
339
+ CArray.jit_for(1) { |i|
340
+ total = 0
341
+ (0...2).each { |j| total = total + source[j] }
342
+ out[i] = total
343
+ }
344
+ #=> CArray::JIT::Unsupported: `total` enters this loop as an Integer and comes
345
+ # back round as a uint64; the value carried to the next pass would change
346
+ # type, and one C variable is one type -- give it one type before the loop
347
+ ```
348
+
349
+ ```ruby
350
+ seed = CScalar.uint64() { 0 } # a value with a data type
351
+
352
+ CArray.jit_for(1) { |i|
353
+ total = seed
354
+ (0...2).each { |j| total = total + source[j] }
355
+ out[i] = total
356
+ }
357
+ out.to_a #=> [3]
358
+ ```
359
+
360
+ #### 24. Suffix sums: `out[i] = a[i] + out[i+1]`, with `out[4]` already set (★★★)
361
+
362
+ `hint: which way does the loop have to run, and where do you say so?`
363
+
364
+ ```ruby
365
+ a = CA_DOUBLE([1, 2, 3, 4, 5])
366
+ out = CArray.double(5)
367
+ out[4] = a[4]
368
+
369
+ CArray.jit_for(3.step(0, -1)) { |i| out[i] = a[i] + out[i + 1] }
370
+ out.to_a #=> [15.0, 14.0, 12.0, 9.0, 5.0]
371
+ ```
372
+
373
+ ```ruby
374
+ out = CArray.double(5)
375
+ out[4] = a[4]
376
+
377
+ CArray.jit_for(0...4) { |i| out[i] = a[i] + out[i + 1] }
378
+ out.to_a #=> [1.0, 2.0, 3.0, 9.0, 5.0]
379
+ # the extent is the only place the direction is stated, and stating it
380
+ # wrongly is not refused: every cell read the zero that was still there
381
+ ```
382
+
383
+ #### 25. Write the `i`-th Fibonacci number into `out[i]`, advancing two locals rather than reading the array back (★★★)
384
+
385
+ `hint: both names change at once`
386
+
387
+ ```ruby
388
+ out = CArray.int64(12)
389
+
390
+ CArray.jit_for(12) { |i|
391
+ a = 0
392
+ b = 1
393
+ i.times { |k| a, b = b, a + b }
394
+ out[i] = a
395
+ }
396
+ out.to_a #=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
397
+ # every value on the right is settled before anything on the left is
398
+ # written, so `a + b` reads the `a` this pass came in with. Written as two
399
+ # lines -- `a = b` and then `b = a + b` -- the second reads the `a` the
400
+ # first just wrote, and `a` merely doubles: [0, 1, 2, 4, 8, ...]. Not a
401
+ # refusal, and not this compiler's doing: those two lines mean the same in
402
+ # Ruby
403
+ ```
404
+
405
+ #### 26. The longest run of 1s in a series of 0s and 1s (★★★)
406
+
407
+ `hint: two things have to survive the iteration, and neither is a sum`
408
+
409
+ ```ruby
410
+ flag = CA_INT32([0, 1, 1, 1, 0, 1, 1, 0])
411
+ run = CScalar.int32() { 0 }
412
+ best = CScalar.int32() { 0 }
413
+
414
+ CArray.jit_for(8) { |i|
415
+ run[] = flag[i] == 1 ? run[] + 1 : 0
416
+ best[] = run[] if run[] > best[]
417
+ }
418
+ best[0] #=> 3
419
+ ```
420
+
421
+ #### 27. For each query, write the index of the last grid point not above it into `found` (★★★)
422
+
423
+ `hint: the grid is sorted`
424
+
425
+ ```ruby
426
+ grid = CA_DOUBLE([0, 1, 4, 9, 16])
427
+ query = CA_DOUBLE([0.5, 5.0, 9.0, 12.0])
428
+ found = CArray.int32(4)
429
+
430
+ CArray.jit_for(4) { |i|
431
+ t = query[i]
432
+ low = 0
433
+ high = 4
434
+ while high - low > 1
435
+ middle = (low + high) / 2
436
+ if grid[middle] > t
437
+ high = middle
438
+ else
439
+ low = middle
440
+ end
441
+ end
442
+ found[i] = low
443
+ }
444
+ found.to_a #=> [0, 2, 3, 3]
445
+ ```
446
+
447
+ #### 28. Sum the first `bound[i]` cells of row `i`, where `bound` is read from an array. What is checked, and when? (★★★)
448
+
449
+ `hint: a range the data decides is known only when the loop is reached`
450
+
451
+ ```ruby
452
+ bound = CA_INT32([2, 3])
453
+ m = CA_INT32([[1, 2, 3], [4, 5, 6]])
454
+ out = CArray.int32(2)
455
+
456
+ CArray.jit_for(2) { |i|
457
+ s = 0
458
+ (0...bound[i]).each { |j| s = s + m[i, j] }
459
+ out[i] = s
460
+ }
461
+ out.to_a #=> [3, 15]
462
+ ```
463
+
464
+ A range written in literals and captured integers is known before the kernel runs, so `m[i, j]` is proved inside `m` before the first cell. This one is not known until the loop is reached, so `m[i, j]` is checked at each access instead, as `m[i, b[i]]` would be. Past the end of a row, the kernel finishes its pass and raises:
465
+
466
+ ```ruby
467
+ bound = CA_INT32([2, 4])
468
+ CArray.jit_for(2) { |i|
469
+ s = 0
470
+ (0...bound[i]).each { |j| s = s + m[i, j] }
471
+ out[i] = s
472
+ }
473
+ #=> IndexError: index out of range
474
+ ```
475
+
476
+ #### 29. Write the square of a complex array into `squared`, and the magnitude of the original into `size` (★★☆)
477
+
478
+ `hint: a data type like the others`
479
+
480
+ ```ruby
481
+ z = CA_CMPLX128([Complex(0, 1), Complex(1, 1)])
482
+ squared = CArray.cmplx128(2)
483
+ size = CArray.double(2)
484
+
485
+ CArray.jit_each { squared = z * z }
486
+ CArray.jit_each { size = z.abs }
487
+
488
+ squared.to_a #=> [(-1.0+0.0i), (0.0+2.0i)]
489
+ size.to_a #=> [1.0, 1.4142135623730951]
490
+ ```
491
+
492
+ #### 30. Fill an array with a hundred thousand random numbers in [0, 1), drawn inside the kernel (★★☆)
493
+
494
+ `hint: not Ruby's rand -- the loop runs without the GVL`
495
+
496
+ ```ruby
497
+ random = CArray::Rng.new(seed: 3)
498
+ draws = CArray.double(100_000)
499
+
500
+ CArray.jit_for(100_000) { |i| draws[i] = random.random }
501
+ draws.mean.round(4) #=> 0.5006
502
+ # which draw lands in which cell is the loop's order; where that matters,
503
+ # fill an array with CArray#random! first and read a cell of it
504
+ ```
505
+
506
+ #### 31. A local holds an int32 cell and is squared into an int64 array. What C type did it get? (★★★)
507
+
508
+ `hint: read what was compiled`
509
+
510
+ ```ruby
511
+ source = CArray.int32(4).seq!(1)
512
+ out = CArray.int64(4)
513
+
514
+ kernel = CArray.jit_for(4) { |i|
515
+ v = source[i]
516
+ out[i] = v * v
517
+ }
518
+
519
+ kernel.c_source.lines.grep(/int64_t v;/).first.strip
520
+ #=> "int64_t v;"
521
+ # and the assignment below it:
522
+ # v = (int64_t)*(int32_t *)(p_source + (i) * source_s0);
523
+ # nothing in `v = source[i]` asked for 64 bits: a local's type is settled
524
+ # from the whole body, and `v * v` lands in an int64 array
525
+ ```
526
+
527
+ ---
528
+
529
+ Where to go next: [06_Cheatsheet.md](06_Cheatsheet.md) to look things up,
530
+ [examples/features/](../examples/features) for a tour of one feature per file,
531
+ and [examples/applications/](../examples/applications) for programs that use
532
+ all of this to do something -- `mandelbrot.rb` is a loop like 16 whose
533
+ length no cell shares, `alarm.rb` is 25 with hysteresis, `lookup.rb` is 26
534
+ against half a million queries, `sieve.rb` is 27 from the other side.