spitewaste 0.1.006 → 0.1.011

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
@@ -1,45 +1,45 @@
1
1
  import math ; divmod
2
2
  import util ; inc
3
3
 
4
- ; [n] => [0|1]
5
- prime?: ; [n]
4
+ ; returns 1 if the number N is prime, 0 otherwise
5
+ ; [N] => [0 | 1]
6
+ ;
7
+ ; [0] => [0] [1] => [0]
8
+ ; [2] => [1] [4] => [0]
9
+ ; [3] => [1] [9] => [0]
10
+ prime?:
6
11
  dup push 3 sub jn _prime_special ; special-case < 3
7
12
  dup push 2 mod jz _prime_even ; otherwise, even -> false
8
- push 3
9
-
10
- _prime_loop: ; [n d]
11
- copy 1 copy 1 dup mul sub jn _prime_yes ; divisor > isqrt(n), n is prime
13
+ push 3 ; initial divisor to check
14
+ _prime_loop:
15
+ copy 1 copy 1 dup mul sub jn _prime_yes ; divisor > isqrt(n), so n is prime
12
16
  copy 1 copy 1 mod jz _prime_no
13
17
  push 2 add jump _prime_loop
14
-
15
- _prime_yes:
16
- pop dup div ret ; return 1
17
-
18
- _prime_even:
19
- push 1
20
- _prime_no:
21
- pop dup sub ret ; return 0
22
-
23
- _prime_special: ; [n]
24
- push 2 sub jz _prime_two
25
- push 0 ret ; false for n < 2
26
-
27
- _prime_two:
28
- push 1 ret
29
-
30
- ;;;
31
-
18
+ _prime_even: dup sub ret
19
+ _prime_no: pop dup sub ret ; return 0
20
+ _prime_yes: pop dup div ret ; return 1
21
+ _prime_special: push 2 :eq ret
22
+
23
+ ; returns the first prime number P greater than N
24
+ ; [N] => [P]
25
+ ;
26
+ ; [0] => [2] [1] => [2]
27
+ ; [2] => [3] [3] => [5]
28
+ ; [13] => [17]
29
+ ; [100] => [101]
32
30
  next_prime: push 1 add dup :prime? jz next_prime ret
33
31
 
34
- ;;;
35
-
36
- ; prime factorization
37
- factor: ; [n]
38
- push 2 swap ; initial divisor
39
- push -2,0 store ; number of prime factors
40
- _factor_loop: ; [d n]
41
- dup push 2 sub jn _factor_done
42
- jump _divisor_loop ; prevent dead code elimination :(
32
+ ; returns the prime factorization of N as a pseudo-array
33
+ ; ! N must be greater than 1
34
+ ; [N] => [A]
35
+ ;
36
+ ; [2] => [2 1]
37
+ ; [8] => [2 2 2 3]
38
+ ; [15] => [3 5 2]
39
+ ; [17] => [17 1]
40
+ ; [100] => [2 2 5 5 4]
41
+ factor: push 2 swap push -2,0 store
42
+ _factor_loop: dup push 2 sub jn _factor_done jump _divisor_loop
43
43
  _divisor_loop:
44
44
  dup copy 2 :divmod jz _divisor_keep
45
45
  pop swap :next_prime swap jump _factor_loop
@@ -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,26 @@ 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
@@ -293,7 +293,7 @@ strcount:
293
293
  reduce (add) ret
294
294
 
295
295
  ; 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
296
+ ; in string S, ; similar to the `tr` utility in Unix. A and B must be
297
297
  ; of the same length. TODO: make this smarter (ranges, length mismatch)
298
298
  ; ! clobbers heap addresses -1, -2, and -3
299
299
  ; [S A B] => [S']
@@ -319,9 +319,10 @@ _strtrans_no: pop ret
319
319
  ; [S] => [S']
320
320
  ;
321
321
  ; ["CJ"] => ["CDEFGHIJ"]
322
- ; ["DA"] => ["DE"] TODO: bug
322
+ ; ["DA"] => ["DCBA"]
323
323
  ; ["af"] => ["abcdef"]
324
324
  ; ["09"] => ["0123456789"]
325
+ ; ["90"] => ["9876543210"]
325
326
  ; ["(1"] => ["()*+,-./01"]
326
327
  strexpand:
327
328
  push 0 swap push 128 :divmod
@@ -342,6 +343,25 @@ _strsqueeze_loop: ; [s]
342
343
  _strsqueeze_skip: pop jump _strsqueeze_loop
343
344
  _strsqueeze_done: pop :strpack :strrev ret
344
345
 
346
+ $_strdel(cmp) {
347
+ :strunpack push -1 load :strlen
348
+ select (push -2 load swap :strindex `cmp`)
349
+ pop :strpack ret
350
+ }
351
+
352
+ ; returns the string S with all characters in string T removed, like `tr -d`.
353
+ ; If the first character of T is '^', instead only those characters are kept.
354
+ ; [S T] => [S']
355
+ ;
356
+ ; ["abc123" "abc"] => ["123"]
357
+ ; ["abc123" "123"] => ["abc"]
358
+ ; ["abcba12321" "abc"] => ["12321"]
359
+ ; ["abc12321cba" "^2ac"] => ["ac22ca"]
360
+ ; ["facetious" "^aeiou"] => ["aeiou"]
361
+ strdel: push -1 copy 2 store push -2 copy 1 store
362
+ push 0 :charat push '^' :neq jz _strdel_comp $_strdel(:neg?)
363
+ _strdel_comp: $_strdel(:pos?)
364
+
345
365
  ; returns the sum of the ordinal values of the characters in string S
346
366
  ; [S] => [N]
347
367
  ;
@@ -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,8 +82,9 @@ _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
51
90
  bin: push 2 :stoi ret
@@ -57,11 +96,13 @@ hex: push 16 :stoi ret
57
96
  ; [N B]
58
97
  ;
59
98
  ; [42 2] => ["101010"]
99
+ ; [-42 2] => ["-101010"]
60
100
  ; [511 8] => ["777"]
61
101
  ; [12345 10] => ["12345"]
102
+ ; [-54321 10] => ["-54321"]
62
103
  ; [57005 16] => ["dead"]
63
104
  ; [81699 17] => ["gabe"]
64
- itos: swap push 0 ; accumulator
105
+ itos: swap push -2 copy 1 :neg? store :abs push 0
65
106
  _itos_loop:
66
107
  swap dup jz _itos_done
67
108
  swap copy 1 copy 3 mod
@@ -69,7 +110,7 @@ _itos_loop:
69
110
  swap :strcat
70
111
  swap copy 2 div
71
112
  swap jump _itos_loop
72
- _itos_done: swap slide 2 ret
113
+ _itos_done: swap slide 2 push 45,-2 load mul swap :strcat ret
73
114
 
74
115
  ; creature comforts
75
116
  to_bin: push 2 :itos ret
@@ -128,22 +169,45 @@ _neq_no: push 0 ret
128
169
  ; pops A and B and pushes 1 if A is greater than B, 0 otherwise
129
170
  ; [A B] => [A > B]
130
171
  ;
131
- ; [4 3] => [1]
132
- ; [3 4] => [0]
133
- ; [2 2] => [0]
134
- ; [2 1] => [1]
172
+ ; [4 3] => [1] [3 4] => [0]
173
+ ; [2 2] => [0] [2 1] => [1]
135
174
  gt: swap ; intentionally flow into lt
136
175
 
137
176
  ; pops A and B and pushes 1 if A is less than than B, 0 otherwise
138
177
  ; [A B] => [A < B]
139
178
  ;
140
- ; [3 4] => [1]
141
- ; [4 3] => [0]
142
- ; [2 2] => [0]
143
- ; [1 2] => [1]
179
+ ; [3 4] => [1] [4 3] => [0]
180
+ ; [2 2] => [0] [1 2] => [1]
144
181
  lt: sub jn _lt_yes push 0 ret
145
182
  _lt_yes: push 1 ret
146
183
 
184
+ ; pops A and B and pushes 1 if A is less than or equal to B, 0 otherwise
185
+ ; [A B] => [A > B]
186
+ ;
187
+ ; [2 2] => [1] [2 1] => [0]
188
+ ; [4 3] => [0] [3 4] => [1]
189
+ lte: swap ; intentionally flow into gte
190
+
191
+ ; pops A and B and pushes 1 if A is greater than or equal to B, 0 otherwise
192
+ ; [A B] => [A > B]
193
+ ;
194
+ ; [2 2] => [1] [1 2] => [0]
195
+ ; [3 4] => [0] [4 3] => [1]
196
+ gte: sub jn _gte_no push 1 ret
197
+ _gte_no: push 0 ret
198
+
199
+ ; returns 1 if the number N is between A and B (inclusive), 0 otherwise
200
+ ; ! A must be <= B for sensible results TODO: bug?
201
+ ; [N A B]
202
+ ;
203
+ ; [5 0 10] => [1]
204
+ ; [11 0 10] => [0]
205
+ ; [4 0 4] => [1]
206
+ ; [-1 0 4] => [0]
207
+ ; [-5 -10 0] => [1]
208
+ ; [3 4 2] => [0]
209
+ between?: copy 2 :gte swap copy 2 :lte mul slide 1 ret
210
+
147
211
  ; Though extremely rare, it's possible that we know a particular value is
148
212
  ; stored in the heap at some key, just not which one. This subroutine takes
149
213
  ; a value V to search for and a starting index I, and either returns the first
@@ -152,3 +216,31 @@ _lt_yes: push 1 ret
152
216
  heap_seeking_missile:
153
217
  $++ dup load copy 2 :eq jz heap_search
154
218
  slide 1 ret
219
+
220
+ ; converts the #RRGGBB (leading '#' optional) color string S to
221
+ ; its individual RGB components as integers in the range 0-255
222
+ ; [S] => [R G B]
223
+ ;
224
+ ; ["#000000"] => [0 0 0]
225
+ ; ["ffffff"] => [255 255 255]
226
+ ; ["#102030"] => [16 32 48]
227
+ ; ["c0ffee"] => [192 255 238]
228
+ hex2rgb:
229
+ dup push 0 :charat push '#' :eq push 127 mul $++ div
230
+ push 128,2 :pow :divmod :hex swap
231
+ push 128,2 :pow :divmod :hex swap :hex ret
232
+
233
+ ; converts R, G, and B components to length-6 hexadecimal string S
234
+ ; ! dies if any of the values to convert aren't between 0 and 255
235
+ ; [R G B] => [S]
236
+ ;
237
+ ; [0 0 0] => ["000000"]
238
+ ; [255 255 255] => ["ffffff"]
239
+ ; [16 32 48] => ["102030"]
240
+ ; [192 255 238] => ["c0ffee"]
241
+ rgb2hex:
242
+ push 3 :arydup all (push 0,255 :between?) jz _rgb2hex_invalid
243
+ pop copy 2 push 256,2 :pow mul
244
+ copy 2 push 256 mul add add
245
+ slide 2 :to_hex push 6,48 :rjustc ret
246
+ _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
  }