spitewaste 0.2.0 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -36,6 +36,12 @@ _isop_resume: sub add swap push 128 div jump _isop_loop
36
36
  _isop_no: dup jump _isop_resume
37
37
  _isop_done: pop ret
38
38
 
39
+ $_do_collatz() {
40
+ dup push 2 mod
41
+ swap copy 1 push 2 mul $++ mul
42
+ push 2 copy 2 sub div add
43
+ }
44
+
39
45
  ; returns the elements of the Collatz sequence for integer N as a pseudo-array
40
46
  ; ! may run forever on some as-yet-unknown input
41
47
  ; [N] => [A]
@@ -48,18 +54,29 @@ _isop_done: pop ret
48
54
  collatz: push 1 ; sequence length
49
55
  _collatz_loop:
50
56
  copy 1 dup push 1 sub jz _collatz_done
51
- dup push 2 mod
52
- swap copy 1 push 2 mul $++ mul
53
- push 2 copy 2 sub div add
54
- swap $++ jump _collatz_loop
57
+ $_do_collatz() swap $++ jump _collatz_loop
55
58
  _collatz_done: pop ret
56
59
 
60
+ ; returns the length L of the Collatz sequence for integer N
61
+ ; ! may run forever on some as-yet-unknown input
62
+ ; [N] => [L]
63
+ ;
64
+ ; [1] => [1]
65
+ ; [4] => [3]
66
+ ; [7] => [17]
67
+ ; [189] => [107]
68
+ collatz_len: push 1 swap
69
+ _collatz_len_loop:
70
+ dup $-- jz _collatz_done
71
+ $_do_collatz() swap $++ swap jump _collatz_len_loop
72
+ copy 1 dup push 1 sub jz _collatz_done
73
+
57
74
  ; ruby:
58
75
  ; push "'" :strcat push "ruby -e 'p " swap :strcat shell ret
59
76
 
60
77
  $_to_roman(r, v) {
61
78
  push `v` :divmod swap push `r` swap :strrep
62
- push -2 load swap :strcat push -2 swap store
79
+ @-2 swap :strcat ^-2
63
80
  }
64
81
 
65
82
  ; converts the number N to a string of roman numerals R
@@ -77,3 +94,17 @@ to_roman: push -2,0 store
77
94
  $_to_roman("X", 10) $_to_roman("IX", 9)
78
95
  $_to_roman("V", 5) $_to_roman("IV", 4)
79
96
  $_to_roman("I", 1) push 2 sub load ret
97
+
98
+ ; applies the ROT13 "cipher" to the string S
99
+ ; [S] => [S']
100
+ ;
101
+ ; ["gnat"] => ["tang"]
102
+ ; ["purely"] => ["cheryl"]
103
+ ; ["cat.PNG"] => ["png.CAT"]
104
+ ; ["Hello, world."] => ["Uryyb, jbeyq."]
105
+ ; ["a123z"] => ["n123m"]
106
+ ROT13:
107
+ push "AZ" :strexpand push "az" :strexpand :strcat
108
+ push "NZ" :strexpand push "AM" :strexpand
109
+ push "nz" :strexpand push "am" :strexpand
110
+ :strcat :strcat :strcat :strtrans ret
@@ -1,6 +1,7 @@
1
1
  import string ; strcat, strpack, strrev, strunpack
2
2
 
3
3
  ; prints the character at the top of the stack until terminating zero
4
+ ; [0 ... C] => []
4
5
  print: :strunpack
5
6
  _print_loop:
6
7
  dup jz _print_done
@@ -8,34 +9,35 @@ _print_loop:
8
9
  _print_done: pop ret
9
10
 
10
11
  ; print with newline
12
+ ; [0 ... C] => []
11
13
  println: :print push 10 ochr ret
12
14
 
13
- ;;;
14
-
15
15
  ; reads a line of input onto the top of the stack as a packed string
16
16
  ; ! clobbers heap address -1
17
+ ; [] => [L]
17
18
  getline: push 0 ; terminator for strpack
18
-
19
- ; read characters onto the stack until newline (10) or EOF (-1)
20
19
  _getline_loop:
21
20
  push -1 dup ichr load
22
21
  dup jn _getline_eof
23
22
  dup push 10 sub jz _getline_done
24
23
  jump _getline_loop
25
-
26
24
  _getline_eof: pop
27
25
  _getline_done: :strpack :strrev ret
28
26
 
29
- ;;;
30
-
27
+ ; displays the string S then reads a line of input
28
+ ; [S] => [L]
31
29
  prompt: :print :getline ret
32
30
 
33
- ;;;
34
-
35
- ; consume stdin until EOF
31
+ ; consume stdin until EOF into the string S
32
+ ; [] => [S]
36
33
  readall: push 0 ; accumulated string
37
34
  _readall_loop:
38
35
  :getline dup jz _readall_done
39
36
  :strcat jump _readall_loop
40
-
41
37
  _readall_done: pop ret
38
+
39
+ ; returns the contents C of the file at path P (a string)
40
+ ; NON-STANDARD! This function makes use of the `shell` instruction, which is
41
+ ; only(?) available in the Spiceweight Whitespace interpreter.
42
+ ; [P] => [C]
43
+ readfile: push "cat " swap :strcat shell ret
@@ -51,7 +51,7 @@ ilog: push -1,0 store ; accumulator at -1
51
51
  _ilog_loop: ; [n b]
52
52
  swap copy 1 div dup jz _ilog_done
53
53
  push -1 :inc swap jump _ilog_loop
54
- _ilog_done: push -1 load slide 2 ret
54
+ _ilog_done: @-1 slide 2 ret
55
55
 
56
56
  ; returns the greatest common divisor of A and B
57
57
  ; [A B] => [gcd(A, B)]
@@ -111,10 +111,7 @@ abs: dup :sign mul ret
111
111
  ; [42 6] => [7 0]
112
112
  ; [ 1 5] => [0 1]
113
113
  ; ! [9 0] => [!!] TODO: find a way to expect exceptions
114
- divmod:
115
- push -1 swap store
116
- dup push -1 load div
117
- swap push -1 load mod ret
114
+ divmod: ^-1 dup @-1 div swap @-1 mod ret
118
115
 
119
116
  ; returns whether N is greater than 0
120
117
  ; [N] => [N > 0]
@@ -138,11 +135,11 @@ neg?: :sign push -1 :eq ret
138
135
  ; [25] => [1 5 25 3] ; no duplicate for perfect squares
139
136
  ; [60] => [1 2 3 4 5 6 60 30 20 15 12 10 12]
140
137
  divisors: ; [n]
141
- dup push -1 swap store ; preserve N because array operations
138
+ dup ^-1 ; preserve N because array operations
142
139
  :isqrt push 1 swap :range dup ; 1..isqrt(N)
143
- reject (push -1 load swap mod) :arydup ; get first half of divisors
144
- map (push -1 load swap div) :arycat ; map first half to second half
145
- push -1 load copy 2 dup mul sub jz _divisors_square ret
140
+ reject (@-1 swap mod) :arydup ; get first half of divisors
141
+ map (@-1 swap div) :arycat ; map first half to second half
142
+ @-1 copy 2 dup mul sub jz _divisors_square ret
146
143
  _divisors_square: slide 1 $-- ret ; de-duplicate when N is a perfect square
147
144
 
148
145
  ; returns the number of ways to choose K elements from a set of N
@@ -1,16 +1,39 @@
1
- import math ; pow
1
+ import array ; sorted?
2
+ import math ; pow
3
+ import string ; strtoa
2
4
 
3
- srand: push $seed swap store ret
5
+ ; seeds the random number generator with integer S
6
+ ; [S] => []
7
+ srand: ^$seed ret
4
8
 
9
+ ; returns the next number N in the linear congruential generator (better MINSTD)
10
+ ; [] => [N]
5
11
  rand:
6
12
  push $seed dup dup load
7
- push 3,13,10244807 mul mul mul
8
- push 2,32 :pow mod
13
+ push 48271 mul
14
+ push 2,31 :pow $-- mod
9
15
  store load ret
10
16
 
11
- rand_range: ; [a b]
12
- copy 1 sub :rand swap mod add ret
17
+ ; returns a random integer I between A and B (inclusive)
18
+ ; [A B] => [I]
19
+ rand_range:
20
+ $++ copy 1 sub :rand swap mod add ret
13
21
 
14
- dice:
15
- push -2 swap store push 1 :aryfill
16
- map (push -2 load :rand_range) ret
22
+ ; returns an array A of N random integers between 1 and D (inclusive)
23
+ ; [N D] => [A]
24
+ dice: ^-2 dup ^-1 times (push 1 @-2 :rand_range) @-1 ret
25
+
26
+ ; shuffles the array A in-place using the modern Fisher-Yates algorithm
27
+ ; [A] => [A']
28
+ shuffle: dup $-- ^-3
29
+ _shuffle_loop:
30
+ push 0 @-3 :rand_range @-3 :aryswap
31
+ push -3 :dec @-3 push -1 mul jn _shuffle_loop ret
32
+
33
+ ; shuffles the characters of the string S, producing a random anagram
34
+ ; [S] => [S']
35
+ strfry: :strtoa :shuffle pop :strpack ret
36
+
37
+ ; sorts the array A if you're lucky
38
+ ; [A] => [A']
39
+ bogosort: :shuffle :arydup :sorted? jz bogosort ret
@@ -187,9 +187,9 @@ _ratpow_neg: push -1 mul swap :ratinv swap :ratpow ret
187
187
  ; [R(355,113) 6] => [3 141592]
188
188
  ; [R(8675,309) 10] => [28 744336569] TODO: leading 0 is lost (bug)
189
189
  ; [R(2,4) 3] => [0 500]
190
- to_f: push -2 swap store :from_r :divmod push 0 swap
190
+ to_f: ^-2 :from_r :divmod push 0 swap
191
191
  _to_f_loop:
192
- push -1 load :divmod swap
192
+ @-1 :divmod swap
193
193
  copy 2 push 10 mul add
194
194
  swap push 10 mul
195
195
  push 2 :dig
@@ -1,6 +1,6 @@
1
1
  ;;; Heavy-handed stack manipulation
2
2
 
3
- ; These subrtouines do some pretty intricate stack-based operations, relying
3
+ ; These subroutines do some pretty intricate stack-based operations, relying
4
4
  ; heavily on clobbering the heap in order to maintain their "bookkeeping".
5
5
  ; Many of them use heap addresses -10 and lower, unbounded, so they're only
6
6
  ; meant to be used in a pinch or when there just isn't much of an alternative.
@@ -16,15 +16,13 @@ import util ; dec
16
16
  ; [1 2 3 4 5 3] => [1 3 4 5 2]
17
17
  roll:
18
18
  push -10 dup store ; current heap index kept at -10
19
- _roll_keep: ; [n]
19
+ _roll_keep:
20
20
  dup jz _roll_remove
21
21
  push -10 :dec
22
- swap push -10 load swap store
22
+ swap @-10 swap store
23
23
  push 1 sub jump _roll_keep
24
- _roll_remove:
25
- push 10 sub load
26
- swap push -10 swap store
27
- _roll_restore: ; i
24
+ _roll_remove: push 10 sub load swap ^-10
25
+ _roll_restore:
28
26
  dup load swap push 1 add
29
27
  dup push 10 add jz _roll_done
30
28
  jump _roll_restore
@@ -37,15 +35,15 @@ _roll_done: load ret
37
35
  ; [1 2 3 4 5 8 5] => [8 1 2 3 4 5]
38
36
  bury:
39
37
  push -10 dup store ; current heap index kept at -10
40
- swap push -9 swap store ; preserve element to bury
38
+ swap ^-9 ; preserve element to bury
41
39
  _bury_keep: ; [n]
42
40
  dup jz _bury_restore
43
41
  push -10 :dec
44
- swap push -10 load swap store
42
+ swap @-10 swap store
45
43
  push 1 sub jump _bury_keep
46
44
  _bury_restore:
47
45
  push 9 sub load
48
- push -10 load :_roll_restore pop ret
46
+ @-10 :_roll_restore pop ret
49
47
 
50
48
 
51
49
  ; "digs" out the Ith element of the stack and discards it
@@ -63,14 +61,14 @@ dig: :roll pop ret
63
61
  ;
64
62
  ; [-1 9 8 7 -1] => [7 8 9 3]
65
63
  ; [0 'c' 'b' 'a' 0] => ['a' 'b' 'c' 3]
66
- to_a: push -1 swap store push -10 dup store
64
+ to_a: ^-1 push -10 dup store
67
65
  _to_a_loop:
68
- dup push -1 load sub jz _to_a_sentinel
66
+ dup @-1 sub jz _to_a_sentinel
69
67
  push -10 dup :dec load
70
68
  swap store jump _to_a_loop
71
69
  _to_a_sentinel: pop push -10
72
70
  _to_a_restore:
73
- dup push -10 load sub jz _to_a_done
71
+ dup @-10 sub jz _to_a_done
74
72
  push 1 sub dup load swap
75
73
  jump _to_a_restore
76
74
  _to_a_done: push -10 swap sub ret
@@ -88,7 +86,7 @@ _npop_done: pop ret
88
86
  ;
89
87
  ; [1 2 3 4 5 2] => [1 2 5]
90
88
  ; [1 2 3 4 1] => [1 2 4]
91
- nslide: swap push -1 swap store :npop push -1 load ret
89
+ nslide: swap ^-1 :npop @-1 ret
92
90
 
93
91
  ; copies the Nth element to the top of the stack; this does exactly what
94
92
  ; a `copy N` instruction would do, but we don't always know N in advance
@@ -106,3 +104,14 @@ _ncopy_restore:
106
104
  dup push 9 add jz _ncopy_done
107
105
  dup load swap push 1 add jump _ncopy_restore
108
106
  _ncopy_done: pop ret
107
+
108
+ ; swaps the two arrays at the top of the stack
109
+ ; [A B] => [B A]
110
+ ;
111
+ ; [1 2 3 3 3 2 1 3] => [3 2 1 3 1 2 3 3]
112
+ ; [5 6 2 9 7 5 3 1 5] => [9 7 5 3 1 5 5 6 2]
113
+ ; [0 0 2 4 2 0 3] => [4 2 0 3 0 0 2]
114
+ ; [1 1 2 1] => [2 1 1 1]
115
+ swapary:
116
+ push -10 :aryheap push -11 @-9 sub :aryheap
117
+ push -10 :heapary push -11 @-9 sub :heapary ret
@@ -224,13 +224,13 @@ _strchop_empty: ret
224
224
  ; ["foo,,bar" ','] => ["foo" "" "bar" 3]
225
225
  ; ["/foo/bar/" '/'] => ["" "foo" "bar" "" 4]
226
226
  strsplit:
227
- push -3 push 1 store ; number of found substrings
228
- push -2 swap store ; stash delimiter to allow some stack juggling
227
+ push -3,1 store ; number of found substrings
228
+ ^-2 ; stash delimiter to allow some stack juggling
229
229
  _strsplit_loop:
230
- dup dup push -2 load
230
+ dup dup @-2
231
231
  :strindex dup jn _strsplit_done ; done when index of delimiter is -1
232
232
  push 0 swap :strslice
233
- swap copy 1 push -3 load
233
+ swap copy 1 @-3
234
234
  swap :strlen
235
235
  swap push -3 swap push 1 add store ; update number of found
236
236
  push 1 add push 128 swap :pow div ; shrink haystack
@@ -248,11 +248,10 @@ lines: push 10 :strsplit ret
248
248
  ; ["foo" "bar" "baz" 3 '--'] => ["foo--bar--baz"]
249
249
  ; ["foo" 1 "?!"] => ["foo"]
250
250
  strjoinc:
251
- dup :strlen pop ; get delimiter length into -1
252
- push -2 swap store
253
- map (push -2 load :strcat) ; add delimiter to all elements
251
+ dup :strlen pop ^-2 ; get delimiter length into -1
252
+ map (@-2 :strcat) ; add delimiter to all elements
254
253
  swap push 128 copy 1 :strlen
255
- push -2 load :strlen
254
+ @-2 :strlen
256
255
  sub :pow mod swap ; remove delimiter from last and flow into strjoin
257
256
 
258
257
  ; concatenates the pseudo-array of strings A into string S
@@ -289,9 +288,8 @@ _strcountc_done: swap slide 2 ret
289
288
  ; ["eunoia" "aeiou"] => [5]
290
289
  ; ["why" "aeiou"] => [0]
291
290
  strcount:
292
- swap push -2 swap store
293
- :strunpack push 0 :to_a
294
- map (push -2 load swap :strcountc)
291
+ swap ^-2 :strunpack push 0 :to_a
292
+ map (@-2 swap :strcountc)
295
293
  reduce (add) ret
296
294
 
297
295
  ; translates all characters in A to the corresponding characters in B
@@ -303,16 +301,13 @@ strcount:
303
301
  ; ["abcd" "abc" "xyz"] => ["xyzd"]
304
302
  ; ["foobar" "oba" "ele"] => ["feeler"]
305
303
  ; ["abcdcba" "abcd" "xyz|"] => ["xyz|zyx"]
306
- strtrans:
307
- push -3 swap store
308
- push -2 swap store
309
- dup :strlen push -1 swap store
310
- :strunpack push -1 load
304
+ strtrans: ^-3 ^-2
305
+ dup :strlen ^-1 :strunpack @-1
311
306
  map (:_strtrans) pop :strpack ret
312
307
  _strtrans:
313
- dup push -2 load swap :strindex
308
+ dup @-2 swap :strindex
314
309
  dup jn _strtrans_no
315
- push -3 load swap :charat
310
+ @-3 swap :charat
316
311
  slide 1 ret
317
312
  _strtrans_no: pop ret
318
313
 
@@ -346,8 +341,8 @@ _strsqueeze_skip: pop jump _strsqueeze_loop
346
341
  _strsqueeze_done: pop :strpack :strrev ret
347
342
 
348
343
  $_strdel(cmp) {
349
- :strunpack push -1 load :strlen
350
- select (push -2 load swap :strindex `cmp`)
344
+ :strunpack @-1 :strlen
345
+ select (@-2 swap :strindex `cmp`)
351
346
  pop :strpack ret
352
347
  }
353
348
 
@@ -390,13 +385,39 @@ strrotl: push 128 swap copy 2 :strlen mod :pow :divmod :strcat ret
390
385
  ; ["abcd" 1] => ["dabc"]
391
386
  ; ["abcd" 5] => ["dabc"]
392
387
  ; ["foodbar" 3] => ["barfood"]
393
- strrotr: push 0 swap sub :strrotl ret
388
+ strrotr: push -1 mul :strrotl ret
389
+
390
+ ; gets the characters of the string S onto the stack as a pseudo-array, but
391
+ ; with a leading 0 on the assumption that it'll eventually be repacked
392
+ ;
393
+ ; ["abc"] => [0 99 98 97 3]
394
+ strtoa: dup :strlen pop :strunpack @-1 $++ ret
394
395
 
395
396
  ; frobnicates the string S by XORing all its bytes with 42
396
397
  ; [S] => [S']
397
398
  ;
398
399
  ; ["foobar"] => ["LEEHKX"]
399
400
  ; ["LEEHKX"] => ["foobar"]
400
- memfrob:
401
- dup :strlen pop :strunpack push -1 load $++
402
- map (push 42 :bxor) pop :strpack ret
401
+ memfrob: :strtoa map (push 42 :bxor) pop :strpack ret
402
+
403
+ ; returns 1 if the string S begins with substring T, 0 otherwise
404
+ ; [S T] => [0 | 1]
405
+ ;
406
+ ; ["foobar" "foo"] => [1]
407
+ ; ["foobar" "boo"] => [0]
408
+ ; ["abc123" "123"] => [0]
409
+ ; [" foo" " "] = [1]
410
+ strbegins?:
411
+ dup :strlen copy 2 swap push 0 swap
412
+ :strslice :eq slide 1 ret
413
+
414
+ ; returns 1 if the string S ends with substring T, 0 otherwise
415
+ ; [S T] => [0 | 1]
416
+ ;
417
+ ; ["foobar" "bar"] => [1]
418
+ ; ["foobar" "foo"] => [0]
419
+ ; ["abc123" "abc"] => [0]
420
+ ; ["foo " " "] = [1]
421
+ strends?:
422
+ :strrev dup :strlen copy 2 :strrev swap push 0 swap
423
+ :strslice :eq slide 1 ret
@@ -8,3 +8,6 @@ assert_eq:
8
8
  push ", got " :print onum
9
9
  push " for test " swap :strcat :die!
10
10
  _assert_eq_yes: pop pop pop ret
11
+
12
+ assert_nz: jz _assert_nz_no pop ret
13
+ _assert_nz_no: push "expected nonzero for test " swap :strcat :die!
@@ -26,7 +26,7 @@ _range_down:
26
26
 
27
27
  $range_loop(fn, cmp) {
28
28
  `fn`:
29
- dup copy 2 add push -1 load `cmp` jz _`fn`_done
29
+ dup copy 2 add @-1 `cmp` jz _`fn`_done
30
30
  copy 1 copy 1 add swap jump `fn`
31
31
  _`fn`_done: pop ret
32
32
  }
@@ -50,8 +50,10 @@ steprange: swap push -1 copy 1 store copy 2 sub
50
50
  $range_loop(_steprange_loop, :lte)
51
51
  $range_loop(_steprange_down_loop, :gte)
52
52
 
53
- ; prints the string at the top of the stack and halts execution
54
- die!: :println exit
53
+ ; prints the string at the top of the stack and halts execution after pushing
54
+ ; something onto the stack to signal abnormal termination/unclean exit
55
+ ; [...] => [... 1]
56
+ die!: :println push 1 exit
55
57
 
56
58
  ; for stoi and itos
57
59
  alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
@@ -72,7 +74,7 @@ alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
72
74
  ; ["-123" 10] => [-123]
73
75
  ; ["-ff" 16] => [-255]
74
76
  stoi: swap dup :_stoi_sign swap copy 1 :eq
75
- push 2 mul $-- push -2 swap store push 0
77
+ push 2 mul $-- ^-2 push 0
76
78
  _stoi_loop: ; [b s a]
77
79
  swap dup jz _stoi_done
78
80
  swap copy 2 copy 2
@@ -83,8 +85,8 @@ _stoi_loop: ; [b s a]
83
85
  mul add swap push 128 div swap
84
86
  jump _stoi_loop
85
87
  _stoi_sign: dup push 0 :charat push '-' :eq copy 1 :strlen :strslice ret
86
- _stoi_invalid: pop pop slide 1 swap div push -2 load mul ret
87
- _stoi_done: swap slide 2 push -2 load mul ret
88
+ _stoi_invalid: pop pop slide 1 swap div @-2 mul ret
89
+ _stoi_done: swap slide 2 @-2 mul ret
88
90
 
89
91
  ; creature comforts
90
92
 
@@ -111,7 +113,7 @@ _itos_loop:
111
113
  swap :strcat
112
114
  swap copy 2 div
113
115
  swap jump _itos_loop
114
- _itos_done: swap slide 2 push 45,-2 load mul swap :strcat ret
116
+ _itos_done: swap slide 2 push 45 @-2 mul swap :strcat ret
115
117
 
116
118
  ; creature comforts
117
119
 
@@ -129,12 +131,10 @@ to_hex: push 16 :itos ret
129
131
  ; [256 16] => [1 0 0 3]
130
132
  digits:
131
133
  copy 1 jz _digits_zero ; special case
132
- push -1 swap store
133
- push -1 swap ; sentinel value
134
+ ^-1 push -1 swap ; sentinel value
134
135
  _digits_loop:
135
136
  dup jz _digits_done
136
- push -1 load :divmod
137
- swap jump _digits_loop
137
+ @-1 :divmod swap jump _digits_loop
138
138
  _digits_zero: dup div ret
139
139
  _digits_done: push 1 sub :to_a ret
140
140
 
@@ -215,8 +215,8 @@ between?: copy 2 :gte swap copy 2 :lte mul slide 1 ret
215
215
  ; a value V to search for and a starting index I, and either returns the first
216
216
  ; key associated with that value or loops forever. Probably don't touch.
217
217
  ; [V I]
218
- heap_seeking_missile:
219
- $++ dup load copy 2 :eq jz heap_search
218
+ heap-seeking_missile:
219
+ $++ dup load copy 2 :eq jz heap-seeking_missile
220
220
  slide 1 ret
221
221
 
222
222
  ; converts the #RRGGBB (leading '#' optional) color string S to
@@ -246,3 +246,31 @@ rgb2hex:
246
246
  copy 2 push 256 mul add add
247
247
  slide 2 :to_hex push 6,48 :rjustc ret
248
248
  _rgb2hex_invalid: push "(rgb2hex) invalid RGB" :die!
249
+
250
+ ; stashes the array A in negative heap space starting at index I
251
+ ; [A I] => []
252
+ aryheap:
253
+ $++ dup copy 2 store
254
+ swap times ($-- swap copy 1 swap store) pop ret
255
+
256
+ ; restores the heaped array starting at index I
257
+ ; [I] => [A]
258
+ heapary:
259
+ $++ dup load swap copy 1 sub swap
260
+ times (dup load swap $++) load ret
261
+
262
+ ; swaps the elements in the heap at indices I and J
263
+ ; ! TODO: make heap effects doctest-able
264
+ ; [I J] => []
265
+ heapswap: dup load swap copy 2 load store store ret
266
+
267
+ ; returns the number of nanoseconds N since the Unix epoch
268
+ ; [] => [N]
269
+ time: push "date +%s%N" shell :to_i ret
270
+
271
+ $bench(insns) {
272
+ #insns :println
273
+ :time ^0 `insns` :time @0 sub
274
+ push 10,9 :pow :divmod swap onum
275
+ push '.' ochr :to_s push 9,48 :rjustc :println
276
+ }