spitewaste 0.1.007 → 0.1.012

Sign up to get free protection for your applications and to get access to all the features.
@@ -56,3 +56,24 @@ _collatz_done: pop ret
56
56
 
57
57
  ; ruby:
58
58
  ; push "'" :strcat push "ruby -e 'p " swap :strcat shell ret
59
+
60
+ $_to_roman(r, v) {
61
+ push `v` :divmod swap push `r` swap :strrep
62
+ push -2 load swap :strcat push -2 swap store
63
+ }
64
+
65
+ ; converts the number N to a string of roman numerals R
66
+ ; [N] => [R]
67
+ ;
68
+ ; [2020] => ["MMXX"]
69
+ ; [1666] => ["MDCLXVI"]
70
+ ; [1337] => ["MCCCXXXVII"]
71
+ ; [94] => ["XCIV"]
72
+ to_roman: push -2,0 store
73
+ $_to_roman("M", 1000) $_to_roman("CM", 900)
74
+ $_to_roman("D", 500) $_to_roman("CD", 400)
75
+ $_to_roman("C", 100) $_to_roman("XC", 90)
76
+ $_to_roman("L", 50) $_to_roman("XL", 40)
77
+ $_to_roman("X", 10) $_to_roman("IX", 9)
78
+ $_to_roman("V", 5) $_to_roman("IV", 4)
79
+ $_to_roman("I", 1) push 2 sub load ret
@@ -2,10 +2,12 @@ import array ; arycat, arydup
2
2
  import util ; eq, inc, range
3
3
 
4
4
  ; returns B raised to the power E
5
+ ; ! TODO: support negative exponents (return Rational)
5
6
  ; [B E] => [B**E]
6
7
  ;
7
8
  ; [0 0] => [1], [0 9] => [0], [9 0] => [1]
8
9
  ; [3 2] => [9], [2 3] => [8], [7 4] => [2401]
10
+ ; [-2 3] => [-8] [-5 0] => [1] [-4 4] => [256]
9
11
  pow: push 1 swap
10
12
  _pow_loop: ; [b n e]
11
13
  dup jz _pow_done
@@ -18,10 +20,10 @@ _pow_done: swap slide 2 ret
18
20
  ;
19
21
  ; [0] => [1], [1] => [1], [2] => [2]
20
22
  ; [3] => [6], [5] => [120], [10] => [3628800]
21
- factorial: push 0 swap :range
23
+ factorial: dup jz _fac_zero push 0 swap :range
22
24
  _fac_loop: swap dup jz _fac_done mul jump _fac_loop
23
-
24
25
  _fac_done: pop ret
26
+ _fac_zero: $++ ret
25
27
 
26
28
  ; returns the integer square root of N
27
29
  ; [N] => [floor(sqrt(N))]
@@ -153,6 +155,8 @@ _divisors_square: slide 1 $-- ret ; de-duplicate when N is a perfect square
153
155
  ; [16 4] => [1820]
154
156
  ; [50 3] => [19600]
155
157
  nCk:
158
+ copy 1 copy 1 sub jn _nCk_empty
156
159
  copy 1 copy 1 sub :factorial
157
160
  swap :factorial mul
158
161
  swap :factorial swap div ret
162
+ _nCk_empty: push 0 slide 2 ret
@@ -10,3 +10,7 @@ rand:
10
10
 
11
11
  rand_range: ; [a b]
12
12
  copy 1 sub :rand swap mod add ret
13
+
14
+ dice:
15
+ push -2 swap store push 1 :aryfill
16
+ map (push -2 load :rand_range) ret
@@ -151,3 +151,51 @@ ratsign:
151
151
  dup $RAT push 2 mul push 2 add sub jn _ratsign_zero
152
152
  push 2 mod push 2 mul push 1 sub ret
153
153
  _ratsign_zero: pop push 0 ret
154
+
155
+ ; returns the simplified inverse of the rational number R
156
+ ; [R] => [1/R]
157
+ ;
158
+ ; [R(4,8)] => [R(2,1)]
159
+ ; [R(3,-5)] => [R(-5,3)]
160
+ ; [R(-22,-7)] => [R(7,22)]
161
+ ratinv:
162
+ push 2 :divmod push 2 mul $--
163
+ swap $RAT :divmod copy 2 mul
164
+ swap :to_r :ratsimp slide 1 ret
165
+
166
+ ; returns the rational R raised to the power E
167
+ ; [R E] => [R']
168
+ ;
169
+ ; [R(4,2) 5] => [R(32,1)]
170
+ ; [R(-3,7) 0] => [R(1,1)]
171
+ ; [R(-3,14) 2] => [R(9,196)]
172
+ ratpow:
173
+ swap push 2 :divmod push 2 mul $--
174
+ swap $RAT :divmod copy 2 mul
175
+ copy 3 :pow swap copy 3 :pow
176
+ swap :to_r :ratsimp slide 2 ret
177
+
178
+ ; converts the rational number R to a "pseudo-float" with a whole (W) part
179
+ ; and a fractional (F) part composed of P digits after the decimal point
180
+ ; [R P] => [W F]
181
+ ;
182
+ ; [R(22,7) 2] => [3 14]
183
+ ; [R(355,113) 6] => [3 141592]
184
+ ; [R(8675,309) 10] => [28 744336569] TODO: leading 0 is lost (bug)
185
+ ; [R(2,4) 3] => [0 500]
186
+ to_f: push -2 swap store :from_r :divmod push 0 swap
187
+ _to_f_loop:
188
+ push -1 load :divmod swap
189
+ copy 2 push 10 mul add
190
+ swap push 10 mul
191
+ push 2 :dig
192
+ push -2 dup :dec load :neg? jz _to_f_loop pop ret
193
+
194
+ ; converts the rational number R to a string S of the form
195
+ ; "<simplified numerator>/<simplified denominator>"
196
+ ; [R] => [S]
197
+ ;
198
+ ; [R(22,7)] => ["22/7"]
199
+ ; [R(2,4)] => ["1/2"]
200
+ ; [R(99,9)] => ["11/1"]
201
+ ratstr: :ratsimp :from_r push 2 map (:to_s) push '/' :strjoinc ret
@@ -207,8 +207,10 @@ center: push ' ' :centerc ret
207
207
  ; ["foobar"] => ["fooba"]
208
208
  ; ["abc"] => ["ab"]
209
209
  ; ["a"] => [""]
210
- ; ! [""] => ERROR TODO: Should just be a no-op.
211
- strchop: dup :strlen push 1 sub push 0 swap :strslice ret
210
+ ; [""] => [""]
211
+ strchop: dup jz _strchop_empty
212
+ dup :strlen push 1 sub push 0 swap :strslice ret
213
+ _strchop_empty: ret
212
214
 
213
215
  ; splits string S on delimiting character C, leaving the resultant substrings
214
216
  ; on the stack as a pseudo-array (length at top of stack)
@@ -293,7 +295,7 @@ strcount:
293
295
  reduce (add) ret
294
296
 
295
297
  ; translates all characters in A to the corresponding characters in B
296
- ; in stirng S, ; similar to the `tr` utility in Unix. A and B must be
298
+ ; in string S, ; similar to the `tr` utility in Unix. A and B must be
297
299
  ; of the same length. TODO: make this smarter (ranges, length mismatch)
298
300
  ; ! clobbers heap addresses -1, -2, and -3
299
301
  ; [S A B] => [S']
@@ -319,9 +321,10 @@ _strtrans_no: pop ret
319
321
  ; [S] => [S']
320
322
  ;
321
323
  ; ["CJ"] => ["CDEFGHIJ"]
322
- ; ["DA"] => ["DE"] TODO: bug
324
+ ; ["DA"] => ["DCBA"]
323
325
  ; ["af"] => ["abcdef"]
324
326
  ; ["09"] => ["0123456789"]
327
+ ; ["90"] => ["9876543210"]
325
328
  ; ["(1"] => ["()*+,-./01"]
326
329
  strexpand:
327
330
  push 0 swap push 128 :divmod
@@ -342,17 +345,38 @@ _strsqueeze_loop: ; [s]
342
345
  _strsqueeze_skip: pop jump _strsqueeze_loop
343
346
  _strsqueeze_done: pop :strpack :strrev ret
344
347
 
348
+ $_strdel(cmp) {
349
+ :strunpack push -1 load :strlen
350
+ select (push -2 load swap :strindex `cmp`)
351
+ pop :strpack ret
352
+ }
353
+
354
+ ; returns the string S with all characters in string T removed, like `tr -d`.
355
+ ; If the first character of T is '^', instead only those characters are kept.
356
+ ; [S T] => [S']
357
+ ;
358
+ ; ["abc123" "abc"] => ["123"]
359
+ ; ["abc123" "123"] => ["abc"]
360
+ ; ["abcba12321" "abc"] => ["12321"]
361
+ ; ["abc12321cba" "^2ac"] => ["ac22ca"]
362
+ ; ["facetious" "^aeiou"] => ["aeiou"]
363
+ strdel: push -1 copy 2 store push -2 copy 1 store
364
+ push 0 :charat push '^' :neq jz _strdel_comp $_strdel(:neg?)
365
+ _strdel_comp: $_strdel(:pos?)
366
+
345
367
  ; returns the sum of the ordinal values of the characters in string S
346
368
  ; [S] => [N]
347
369
  ;
348
370
  ; ["ABC"] => [198]
349
371
  ; ["012"] => [147]
350
372
  ; ["a"] => [97]
351
- ; [""] => [] TODO: bug, should be 0
352
- strsum: :strunpack push 0 :to_a reduce (add) ret
373
+ ; [""] => [0]
374
+ strsum: dup jz _strsum_empty
375
+ :strunpack push 0 :to_a reduce (add) ret
376
+ _strsum_empty: ret
353
377
 
354
378
  ; rotates the string S to the left N times, wrapping
355
- ; [S N]
379
+ ; [S N] => [S']
356
380
  ;
357
381
  ; ["abc" 0] => ["abc"]
358
382
  ; ["abcd" 1] => ["bcda"]
@@ -361,9 +385,18 @@ strsum: :strunpack push 0 :to_a reduce (add) ret
361
385
  strrotl: push 128 swap copy 2 :strlen mod :pow :divmod :strcat ret
362
386
 
363
387
  ; rotates the string S to the right N times, wrapping
364
- ; [S N]
388
+ ; [S N] => [S']
365
389
  ;
366
390
  ; ["abcd" 1] => ["dabc"]
367
391
  ; ["abcd" 5] => ["dabc"]
368
392
  ; ["foodbar" 3] => ["barfood"]
369
393
  strrotr: push 0 swap sub :strrotl ret
394
+
395
+ ; frobnicates the string S by XORing all its bytes with 42
396
+ ; [S] => [S']
397
+ ;
398
+ ; ["foobar"] => ["LEEHKX"]
399
+ ; ["LEEHKX"] => ["foobar"]
400
+ memfrob:
401
+ dup :strlen pop :strunpack push -1 load $++
402
+ map (push 42 :bxor) pop :strpack ret
@@ -0,0 +1,14 @@
1
+ ;;; Miscellaneous terminal-based functionality
2
+
3
+ import util ; hex2rgb
4
+
5
+ $_setg(code) {
6
+ push 91,27 ochr ochr
7
+ push `code` onum push 50,59 ochr ochr
8
+ :hex2rgb each (push 59 ochr onum)
9
+ push 109 ochr ret
10
+ }
11
+
12
+ setfg: $_setg(38)
13
+ setbg: $_setg(48)
14
+ reset: push 109,91,27 ochr ochr ochr ret
@@ -1,11 +1,10 @@
1
1
  import io ; print, println
2
2
  import string ; strcat
3
+ import util ; die!
3
4
 
4
5
  assert_eq:
5
6
  dup copy 2 sub jz _assert_eq_yes
6
7
  push "expected " :print onum
7
8
  push ", got " :print onum
8
- push " for test " :strcat :print :println
9
- exit
10
-
9
+ push " for test " swap :strcat :die!
11
10
  _assert_eq_yes: pop pop pop ret
@@ -4,18 +4,51 @@ import stack ; to_a
4
4
  import string ; charat, strcat, strindex, strlen
5
5
 
6
6
  ; inserts between the top two stack values the intervening consecutive elements
7
- ; ! TODO: Support step, and negative as well.
8
- ; [I J] => [I I+1 ... J]
7
+ ; supports counting up or down
8
+ ; [I J] => [I I±1 ... J]
9
9
  ;
10
10
  ; [2 5] => [2 3 4 5]
11
+ ; [5 2] => [5 4 3 2]
11
12
  ; [0 4] => [0 1 2 3 4]
13
+ ; [4 0] => [4 3 2 1 0]
12
14
  ; [-2 0] => [-2 -1 0]
15
+ ; [0 -2] => [0 -1 -2]
13
16
  ; [-3 3] => [-3 -2 -1 0 1 2 3]
14
- ; [4 4] => [4 5] TODO: bug
15
- range:
17
+ ; [3 -3] => [3 2 1 0 -1 -2 -3]
18
+ ; [4 4] => [4]
19
+ range: dup copy 2 sub jz _range_one dup copy 2 sub jn _range_down
16
20
  copy 1 push 1 add swap
17
- copy 1 copy 1 sub jn range
18
- pop ret
21
+ copy 1 copy 1 sub jn range pop ret
22
+ _range_one: pop ret
23
+ _range_down:
24
+ copy 1 push 1 sub swap
25
+ dup copy 2 sub jn _range_down pop ret
26
+
27
+ $range_loop(fn, cmp) {
28
+ `fn`:
29
+ dup copy 2 add push -1 load `cmp` jz _`fn`_done
30
+ copy 1 copy 1 add swap jump `fn`
31
+ _`fn`_done: pop ret
32
+ }
33
+
34
+ ; inserts between I and J the intervening consecutive elements, counting by
35
+ ; step S up/down to (but never beyond) J
36
+ ; [I J S] => [I I±S ... ~J]
37
+ ;
38
+ ; [1 4 1] => [1 2 3 4]
39
+ ; [4 1 -1] => [4 3 2 1]
40
+ ; [2 10 3] => [2 5 8]
41
+ ; [10 2 -3] => [10 7 4]
42
+ ; [-5 25 10] => [-5 5 15 25]
43
+ ; [25 -5 -10] => [25 15 5 -5]
44
+ ; [4 20 3] => [4 7 10 13 16 19]
45
+ ; [20 4 -3] => [20 17 14 11 8 5]
46
+ ; [3 9 7] => [3]
47
+ ; [9 3 -7] => [9]
48
+ steprange: swap push -1 copy 1 store copy 2 sub
49
+ jn _steprange_down_loop jump _steprange_loop ; prevent DCE
50
+ $range_loop(_steprange_loop, :lte)
51
+ $range_loop(_steprange_down_loop, :gte)
19
52
 
20
53
  ; prints the string at the top of the stack and halts execution
21
54
  die!: :println exit
@@ -34,7 +67,12 @@ alpha: push "0123456789abcdefghijklmnopqrstuvwxyz" ret
34
67
  ; ["123____" 10] => [123000] ; TODO: bug
35
68
  ; ["dead" 16] => [57005]
36
69
  ; ["gabe" 17] => [81699]
37
- stoi: swap push 0 ; accumulator
70
+ ; ["0" 10] => [0] ["-0" 10] => [0]
71
+ ; ["-10001" 2] => [-17]
72
+ ; ["-123" 10] => [-123]
73
+ ; ["-ff" 16] => [-255]
74
+ stoi: swap dup :_stoi_sign swap copy 1 :eq
75
+ push 2 mul $-- push -2 swap store push 0
38
76
  _stoi_loop: ; [b s a]
39
77
  swap dup jz _stoi_done
40
78
  swap copy 2 copy 2
@@ -44,10 +82,12 @@ _stoi_loop: ; [b s a]
44
82
  dup jn _stoi_invalid ; found something non-alphanumeric
45
83
  mul add swap push 128 div swap
46
84
  jump _stoi_loop
47
- _stoi_invalid: pop pop slide 1 swap div ret ; return what we were able to parse
48
- _stoi_done: swap slide 2 ret
85
+ _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
49
88
 
50
89
  ; creature comforts
90
+
51
91
  bin: push 2 :stoi ret
52
92
  oct: push 8 :stoi ret
53
93
  to_i: push 10 :stoi ret
@@ -57,11 +97,13 @@ hex: push 16 :stoi ret
57
97
  ; [N B]
58
98
  ;
59
99
  ; [42 2] => ["101010"]
100
+ ; [-42 2] => ["-101010"]
60
101
  ; [511 8] => ["777"]
61
102
  ; [12345 10] => ["12345"]
103
+ ; [-54321 10] => ["-54321"]
62
104
  ; [57005 16] => ["dead"]
63
105
  ; [81699 17] => ["gabe"]
64
- itos: swap push 0 ; accumulator
106
+ itos: swap push -2 copy 1 :neg? store :abs push 0
65
107
  _itos_loop:
66
108
  swap dup jz _itos_done
67
109
  swap copy 1 copy 3 mod
@@ -69,9 +111,10 @@ _itos_loop:
69
111
  swap :strcat
70
112
  swap copy 2 div
71
113
  swap jump _itos_loop
72
- _itos_done: swap slide 2 ret
114
+ _itos_done: swap slide 2 push 45,-2 load mul swap :strcat ret
73
115
 
74
116
  ; creature comforts
117
+
75
118
  to_bin: push 2 :itos ret
76
119
  to_oct: push 8 :itos ret
77
120
  to_s: push 10 :itos ret
@@ -128,22 +171,45 @@ _neq_no: push 0 ret
128
171
  ; pops A and B and pushes 1 if A is greater than B, 0 otherwise
129
172
  ; [A B] => [A > B]
130
173
  ;
131
- ; [4 3] => [1]
132
- ; [3 4] => [0]
133
- ; [2 2] => [0]
134
- ; [2 1] => [1]
174
+ ; [4 3] => [1] [3 4] => [0]
175
+ ; [2 2] => [0] [2 1] => [1]
135
176
  gt: swap ; intentionally flow into lt
136
177
 
137
178
  ; pops A and B and pushes 1 if A is less than than B, 0 otherwise
138
179
  ; [A B] => [A < B]
139
180
  ;
140
- ; [3 4] => [1]
141
- ; [4 3] => [0]
142
- ; [2 2] => [0]
143
- ; [1 2] => [1]
181
+ ; [3 4] => [1] [4 3] => [0]
182
+ ; [2 2] => [0] [1 2] => [1]
144
183
  lt: sub jn _lt_yes push 0 ret
145
184
  _lt_yes: push 1 ret
146
185
 
186
+ ; pops A and B and pushes 1 if A is less than or equal to B, 0 otherwise
187
+ ; [A B] => [A > B]
188
+ ;
189
+ ; [2 2] => [1] [2 1] => [0]
190
+ ; [4 3] => [0] [3 4] => [1]
191
+ lte: swap ; intentionally flow into gte
192
+
193
+ ; pops A and B and pushes 1 if A is greater than or equal to B, 0 otherwise
194
+ ; [A B] => [A > B]
195
+ ;
196
+ ; [2 2] => [1] [1 2] => [0]
197
+ ; [3 4] => [0] [4 3] => [1]
198
+ gte: sub jn _gte_no push 1 ret
199
+ _gte_no: push 0 ret
200
+
201
+ ; returns 1 if the number N is between A and B (inclusive), 0 otherwise
202
+ ; ! A must be <= B for sensible results TODO: bug?
203
+ ; [N A B]
204
+ ;
205
+ ; [5 0 10] => [1]
206
+ ; [11 0 10] => [0]
207
+ ; [4 0 4] => [1]
208
+ ; [-1 0 4] => [0]
209
+ ; [-5 -10 0] => [1]
210
+ ; [3 4 2] => [0]
211
+ between?: copy 2 :gte swap copy 2 :lte mul slide 1 ret
212
+
147
213
  ; Though extremely rare, it's possible that we know a particular value is
148
214
  ; stored in the heap at some key, just not which one. This subroutine takes
149
215
  ; a value V to search for and a starting index I, and either returns the first
@@ -152,3 +218,31 @@ _lt_yes: push 1 ret
152
218
  heap_seeking_missile:
153
219
  $++ dup load copy 2 :eq jz heap_search
154
220
  slide 1 ret
221
+
222
+ ; converts the #RRGGBB (leading '#' optional) color string S to
223
+ ; its individual RGB components as integers in the range 0-255
224
+ ; [S] => [R G B]
225
+ ;
226
+ ; ["#000000"] => [0 0 0]
227
+ ; ["ffffff"] => [255 255 255]
228
+ ; ["#102030"] => [16 32 48]
229
+ ; ["c0ffee"] => [192 255 238]
230
+ hex2rgb:
231
+ dup push 0 :charat push '#' :eq push 127 mul $++ div
232
+ push 128,2 :pow :divmod :hex swap
233
+ push 128,2 :pow :divmod :hex swap :hex ret
234
+
235
+ ; converts R, G, and B components to length-6 hexadecimal string S
236
+ ; ! dies if any of the values to convert aren't between 0 and 255
237
+ ; [R G B] => [S]
238
+ ;
239
+ ; [0 0 0] => ["000000"]
240
+ ; [255 255 255] => ["ffffff"]
241
+ ; [16 32 48] => ["102030"]
242
+ ; [192 255 238] => ["c0ffee"]
243
+ rgb2hex:
244
+ push 3 :arydup all (push 0,255 :between?) jz _rgb2hex_invalid
245
+ pop copy 2 push 256,2 :pow mul
246
+ copy 2 push 256 mul add add
247
+ slide 2 :to_hex push 6,48 :rjustc ret
248
+ _rgb2hex_invalid: push "(rgb2hex) invalid RGB" :die!
@@ -64,9 +64,13 @@ maxby_lesser_%1$s: swap jump maxby_resume_%1$s
64
64
  maxby_done_%1$s:
65
65
  SPW
66
66
 
67
- 'minby' => 'maxby (%2$s push -1 mul)',
68
- 'each' => 'dup times (dup call roll %2$s push 1 sub) pop',
69
- 'count' => 'select (%2$s) dup call nslide',
67
+ 'minby' => 'maxby (%2$s push -1 mul)',
68
+ 'each' => 'dup times (dup call roll %2$s push 1 sub) pop',
69
+ 'all' => 'map (%2$s) reduce (add) push -11 load call eq',
70
+ # TODO: optimize any to stop early if possible
71
+ 'any' => 'map (%2$s) reduce (add) push 0 call gt',
72
+ 'none' => 'map (%2$s) reduce (add) push 0 call eq',
73
+ 'count' => 'select (%2$s) dup call nslide',
70
74
  'select' => generate_filter_spw('select', 0, 1),
71
75
  'reject' => generate_filter_spw('reject', 1, 0),
72
76
  }