]> gitweb.factorcode.org Git - factor.git/blob - core/sequences/sequences.factor
Fix conflict
[factor.git] / core / sequences / sequences.factor
1 ! Copyright (C) 2005, 2008 Slava Pestov, Daniel Ehrenberg.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors kernel kernel.private slots.private math
4 math.private math.order ;
5 IN: sequences
6
7 MIXIN: sequence
8
9 GENERIC: length ( seq -- n ) flushable
10 GENERIC: set-length ( n seq -- )
11 GENERIC: nth ( n seq -- elt ) flushable
12 GENERIC: set-nth ( elt n seq -- )
13 GENERIC: new-sequence ( len seq -- newseq ) flushable
14 GENERIC: new-resizable ( len seq -- newseq ) flushable
15 GENERIC: like ( seq exemplar -- newseq ) flushable
16 GENERIC: clone-like ( seq exemplar -- newseq ) flushable
17
18 : new-like ( len exemplar quot -- seq )
19     over [ [ new-sequence ] dip call ] dip like ; inline
20
21 M: sequence like drop ;
22
23 GENERIC: lengthen ( n seq -- )
24 GENERIC: shorten ( n seq -- )
25
26 M: sequence lengthen 2dup length > [ set-length ] [ 2drop ] if ;
27
28 M: sequence shorten 2dup length < [ set-length ] [ 2drop ] if ;
29
30 : empty? ( seq -- ? ) length 0 = ; inline
31
32 : if-empty ( seq quot1 quot2 -- )
33     [ dup empty? ] [ [ drop ] prepose ] [ ] tri* if ; inline
34
35 : when-empty ( seq quot -- ) [ ] if-empty ; inline
36
37 : unless-empty ( seq quot -- ) [ ] swap if-empty ; inline
38
39 : delete-all ( seq -- ) 0 swap set-length ;
40
41 : first ( seq -- first ) 0 swap nth ; inline
42 : second ( seq -- second ) 1 swap nth ; inline
43 : third ( seq -- third ) 2 swap nth ; inline
44 : fourth ( seq -- fourth ) 3 swap nth ; inline
45
46 : set-first ( first seq -- ) 0 swap set-nth ; inline
47 : set-second ( second seq -- ) 1 swap set-nth ; inline
48 : set-third ( third seq -- ) 2 swap set-nth ; inline
49 : set-fourth  ( fourth seq -- ) 3 swap set-nth ; inline
50
51 : push ( elt seq -- ) [ length ] [ set-nth ] bi ;
52
53 : bounds-check? ( n seq -- ? )
54     dupd length < [ 0 >= ] [ drop f ] if ; inline
55
56 ERROR: bounds-error index seq ;
57
58 : bounds-check ( n seq -- n seq )
59     2dup bounds-check? [ bounds-error ] unless ; inline
60
61 MIXIN: immutable-sequence
62
63 ERROR: immutable seq ;
64
65 M: immutable-sequence set-nth immutable ;
66
67 INSTANCE: immutable-sequence sequence
68
69 <PRIVATE
70
71 : array-nth ( n array -- elt )
72     swap 2 fixnum+fast slot ; inline
73
74 : set-array-nth ( elt n array -- )
75     swap 2 fixnum+fast set-slot ; inline
76
77 : dispatch ( n array -- ) array-nth call ;
78
79 GENERIC: resize ( n seq -- newseq ) flushable
80
81 ! Unsafe sequence protocol for inner loops
82 GENERIC: nth-unsafe ( n seq -- elt ) flushable
83 GENERIC: set-nth-unsafe ( elt n seq -- )
84 GENERIC: new-sequence-unsafe ( len seq -- newseq ) flushable
85
86 M: sequence nth bounds-check nth-unsafe ;
87 M: sequence set-nth bounds-check set-nth-unsafe ;
88
89 M: sequence nth-unsafe nth ;
90 M: sequence set-nth-unsafe set-nth ;
91
92 M: sequence new-sequence-unsafe new-sequence ;
93
94 ! The f object supports the sequence protocol trivially
95 M: f length drop 0 ;
96 M: f nth-unsafe nip ;
97 M: f like drop [ f ] when-empty ;
98
99 INSTANCE: f immutable-sequence
100
101 ! Integers support the sequence protocol
102 M: integer length ;
103 M: integer nth-unsafe drop ;
104
105 INSTANCE: integer immutable-sequence
106
107 : first-unsafe
108     0 swap nth-unsafe ; inline
109
110 : first2-unsafe
111     [ first-unsafe ] [ 1 swap nth-unsafe ] bi ; inline
112
113 : first3-unsafe
114     [ first2-unsafe ] [ 2 swap nth-unsafe ] bi ; inline
115
116 : first4-unsafe
117     [ first3-unsafe ] [ 3 swap nth-unsafe ] bi ; inline
118
119 : exchange-unsafe ( m n seq -- )
120     [ tuck [ nth-unsafe ] 2bi@ ]
121     [ tuck [ set-nth-unsafe ] 2bi@ ] 3bi ; inline
122
123 : (head) ( seq n -- from to seq ) 0 spin ; inline
124
125 : (tail) ( seq n -- from to seq ) over length rot ; inline
126
127 : from-end [ dup length ] dip - ; inline
128
129 : (2sequence)
130     tuck 1 swap set-nth-unsafe
131     tuck 0 swap set-nth-unsafe ; inline
132
133 : (3sequence)
134     tuck 2 swap set-nth-unsafe
135     (2sequence) ; inline
136
137 : (4sequence)
138     tuck 3 swap set-nth-unsafe
139     (3sequence) ; inline
140
141 PRIVATE>
142
143 : 2sequence ( obj1 obj2 exemplar -- seq )
144     2 swap [ (2sequence) ] new-like ; inline
145
146 : 3sequence ( obj1 obj2 obj3 exemplar -- seq )
147     3 swap [ (3sequence) ] new-like ; inline
148
149 : 4sequence ( obj1 obj2 obj3 obj4 exemplar -- seq )
150     4 swap [ (4sequence) ] new-like ; inline
151
152 : first2 ( seq -- first second )
153     1 swap bounds-check nip first2-unsafe ; flushable
154
155 : first3 ( seq -- first second third )
156     2 swap bounds-check nip first3-unsafe ; flushable
157
158 : first4 ( seq -- first second third fourth )
159     3 swap bounds-check nip first4-unsafe ; flushable
160
161 : ?nth ( n seq -- elt/f )
162     2dup bounds-check? [ nth-unsafe ] [ 2drop f ] if ; flushable
163
164 MIXIN: virtual-sequence
165 GENERIC: virtual-seq ( seq -- seq' )
166 GENERIC: virtual@ ( n seq -- n' seq' )
167
168 M: virtual-sequence nth virtual@ nth ;
169 M: virtual-sequence set-nth virtual@ set-nth ;
170 M: virtual-sequence nth-unsafe virtual@ nth-unsafe ;
171 M: virtual-sequence set-nth-unsafe virtual@ set-nth-unsafe ;
172 M: virtual-sequence like virtual-seq like ;
173 M: virtual-sequence new-sequence virtual-seq new-sequence ;
174
175 INSTANCE: virtual-sequence sequence
176
177 ! A reversal of an underlying sequence.
178 TUPLE: reversed { seq read-only } ;
179
180 C: <reversed> reversed
181
182 M: reversed virtual-seq seq>> ;
183
184 M: reversed virtual@ seq>> [ length swap - 1- ] keep ;
185
186 M: reversed length seq>> length ;
187
188 INSTANCE: reversed virtual-sequence
189
190 ! A slice of another sequence.
191 TUPLE: slice
192 { from read-only }
193 { to read-only }
194 { seq read-only } ;
195
196 : collapse-slice ( m n slice -- m' n' seq )
197     [ from>> ] [ seq>> ] bi [ tuck [ + ] 2bi@ ] dip ; inline
198
199 ERROR: slice-error from to seq reason ;
200
201 : check-slice ( from to seq -- from to seq )
202     pick 0 < [ "start < 0" slice-error ] when
203     dup length pick < [ "end > sequence" slice-error ] when
204     2over > [ "start > end" slice-error ] when ; inline
205
206 : <slice> ( from to seq -- slice )
207     dup slice? [ collapse-slice ] when
208     check-slice
209     slice boa ; inline
210
211 M: slice virtual-seq seq>> ;
212
213 M: slice virtual@ [ from>> + ] [ seq>> ] bi ;
214
215 M: slice length [ to>> ] [ from>> ] bi - ;
216
217 : short ( seq n -- seq n' ) over length min ; inline
218
219 : head-slice ( seq n -- slice ) (head) <slice> ; inline
220
221 : tail-slice ( seq n -- slice ) (tail) <slice> ; inline
222
223 : rest-slice ( seq -- slice ) 1 tail-slice ; inline
224
225 : head-slice* ( seq n -- slice ) from-end head-slice ; inline
226
227 : tail-slice* ( seq n -- slice ) from-end tail-slice ; inline
228
229 : but-last-slice ( seq -- slice ) 1 head-slice* ; inline
230
231 INSTANCE: slice virtual-sequence
232
233 ! One element repeated many times
234 TUPLE: repetition { len read-only } { elt read-only } ;
235
236 C: <repetition> repetition
237
238 M: repetition length len>> ;
239 M: repetition nth-unsafe nip elt>> ;
240
241 INSTANCE: repetition immutable-sequence
242
243 <PRIVATE
244
245 : check-length ( n -- n )
246     #! Ricing.
247     dup integer? [ "length not an integer" throw ] unless ; inline
248
249 : ((copy)) ( dst i src j n -- dst i src j n )
250     dup -roll [
251         + swap nth-unsafe -roll [
252             + swap set-nth-unsafe
253         ] 3keep drop
254     ] 3keep ; inline
255
256 : (copy) ( dst i src j n -- dst )
257     dup 0 <= [ 2drop 2drop ] [ 1- ((copy)) (copy) ] if ;
258     inline recursive
259
260 : prepare-subseq ( from to seq -- dst i src j n )
261     #! The check-length call forces partial dispatch
262     [ [ swap - ] dip new-sequence-unsafe dup 0 ] 3keep
263     -rot drop roll length check-length ; inline
264
265 : check-copy ( src n dst -- )
266     over 0 < [ bounds-error ] when
267     [ swap length + ] dip lengthen ; inline
268
269 PRIVATE>
270
271 : subseq ( from to seq -- subseq )
272     [ check-slice prepare-subseq (copy) ] [ like ] bi ;
273
274 : head ( seq n -- headseq ) (head) subseq ;
275
276 : tail ( seq n -- tailseq ) (tail) subseq ;
277
278 : rest ( seq -- tailseq ) 1 tail ;
279
280 : head* ( seq n -- headseq ) from-end head ;
281
282 : tail* ( seq n -- tailseq ) from-end tail ;
283
284 : but-last ( seq -- headseq ) 1 head* ;
285
286 : copy ( src i dst -- )
287     #! The check-length call forces partial dispatch
288     pick length check-length [ 3dup check-copy spin 0 ] dip
289     (copy) drop ; inline
290
291 M: sequence clone-like
292     [ dup length ] dip new-sequence [ 0 swap copy ] keep ;
293
294 M: immutable-sequence clone-like like ;
295
296 : push-all ( src dest -- ) [ length ] [ copy ] bi ;
297
298 <PRIVATE
299
300 : (append) ( seq1 seq2 accum -- accum )
301     [ [ over length ] dip copy ]
302     [ 0 swap copy ]
303     [ ] tri ; inline
304
305 PRIVATE>
306
307 : append-as ( seq1 seq2 exemplar -- newseq )
308     [ over length over length + ] dip
309     [ (append) ] new-like ; inline
310
311 : 3append-as ( seq1 seq2 seq3 exemplar -- newseq )
312     [ pick length pick length pick length + + ] dip [
313         [ [ pick length pick length + ] dip copy ]
314         [ (append) ] bi
315     ] new-like ; inline
316
317 : append ( seq1 seq2 -- newseq ) over append-as ;
318
319 : prepend ( seq1 seq2 -- newseq ) swap append ; inline
320
321 : 3append ( seq1 seq2 seq3 -- newseq ) pick 3append-as ;
322
323 : surround ( seq1 seq2 seq3 -- newseq ) swapd 3append ; inline
324
325 : glue ( seq1 seq2 seq3 -- newseq ) swap 3append ; inline
326
327 : change-nth ( i seq quot -- )
328     [ [ nth ] dip call ] 3keep drop set-nth ; inline
329
330 : min-length ( seq1 seq2 -- n ) [ length ] bi@ min ; inline
331
332 : max-length ( seq1 seq2 -- n ) [ length ] bi@ max ; inline
333
334 <PRIVATE
335
336 : (each) ( seq quot -- n quot' )
337     [ dup length swap [ nth-unsafe ] curry ] dip compose ; inline
338
339 : (collect) ( quot into -- quot' )
340     [ [ keep ] dip set-nth-unsafe ] 2curry ; inline
341
342 : collect ( n quot into -- )
343     (collect) each-integer ; inline
344
345 : map-into ( seq quot into -- )
346     [ (each) ] dip collect ; inline
347
348 : 2nth-unsafe ( n seq1 seq2 -- elt1 elt2 )
349     [ over ] dip nth-unsafe [ nth-unsafe ] dip ; inline
350
351 : (2each) ( seq1 seq2 quot -- n quot' )
352     [ [ min-length ] 2keep ] dip
353     [ [ 2nth-unsafe ] dip call ] 3curry ; inline
354
355 : 2map-into ( seq1 seq2 quot into -- newseq )
356     [ (2each) ] dip collect ; inline
357
358 : finish-find ( i seq -- i elt )
359     over [ dupd nth-unsafe ] [ drop f ] if ; inline
360
361 : (find) ( seq quot quot' -- i elt )
362     pick [ [ (each) ] dip call ] dip finish-find ; inline
363
364 : (find-from) ( n seq quot quot' -- i elt )
365     [ 2dup bounds-check? ] 2dip
366     [ (find) ] 2curry
367     [ 2drop f f ]
368     if ; inline
369
370 : (monotonic) ( seq quot -- ? )
371     [ 2dup nth-unsafe rot 1+ rot nth-unsafe ]
372     prepose curry ; inline
373
374 : (interleave) ( n elt between quot -- )
375     roll 0 = [ nip ] [ swapd 2slip ] if call ; inline
376
377 PRIVATE>
378
379 : each ( seq quot -- )
380     (each) each-integer ; inline
381
382 : reduce ( seq identity quot -- result )
383     swapd each ; inline
384
385 : map-as ( seq quot exemplar -- newseq )
386     [ over length ] dip [ [ map-into ] keep ] new-like ; inline
387
388 : map ( seq quot -- newseq )
389     over map-as ; inline
390
391 : replicate ( seq quot -- newseq )
392     [ drop ] prepose map ; inline
393
394 : replicate-as ( seq quot exemplar -- newseq )
395     [ [ drop ] prepose ] dip map-as ; inline
396
397 : change-each ( seq quot -- )
398     over map-into ; inline
399
400 : accumulate ( seq identity quot -- final newseq )
401     swapd [ pick slip ] curry map ; inline
402
403 : 2each ( seq1 seq2 quot -- )
404     (2each) each-integer ; inline
405
406 : 2reverse-each ( seq1 seq2 quot -- )
407     [ [ <reversed> ] bi@ ] dip 2each ; inline
408
409 : 2reduce ( seq1 seq2 identity quot -- result )
410     [ -rot ] dip 2each ; inline
411
412 : 2map-as ( seq1 seq2 quot exemplar -- newseq )
413     [ 2over min-length ] dip
414     [ [ 2map-into ] keep ] new-like ; inline
415
416 : 2map ( seq1 seq2 quot -- newseq )
417     pick 2map-as ; inline
418
419 : 2change-each ( seq1 seq2 quot -- )
420     pick 2map-into ; inline
421
422 : 2all? ( seq1 seq2 quot -- ? )
423     (2each) all-integers? ; inline
424
425 : find-from ( n seq quot -- i elt )
426     [ (find-integer) ] (find-from) ; inline
427
428 : find ( seq quot -- i elt )
429     [ find-integer ] (find) ; inline
430
431 : find-last-from ( n seq quot -- i elt )
432     [ nip find-last-integer ] (find-from) ; inline
433
434 : find-last ( seq quot -- i elt )
435     [ [ 1- ] dip find-last-integer ] (find) ; inline
436
437 : all? ( seq quot -- ? )
438     (each) all-integers? ; inline
439
440 : push-if ( elt quot accum -- )
441     [ keep ] dip rot [ push ] [ 2drop ] if ; inline
442
443 : pusher ( quot -- quot accum )
444     V{ } clone [ [ push-if ] 2curry ] keep ; inline
445
446 : filter ( seq quot -- subseq )
447     over [ pusher [ each ] dip ] dip like ; inline
448
449 : push-either ( elt quot accum1 accum2 -- )
450     [ keep swap ] 2dip ? push ; inline
451
452 : 2pusher ( quot -- quot accum1 accum2 )
453     V{ } clone V{ } clone [ [ push-either ] 3curry ] 2keep ; inline
454
455 : partition ( seq quot -- trueseq falseseq )
456     over [ 2pusher [ each ] 2dip ] dip tuck [ like ] 2bi@ ; inline
457
458 : monotonic? ( seq quot -- ? )
459     [ dup length 1- swap ] dip (monotonic) all? ; inline
460
461 : interleave ( seq between quot -- )
462     [ (interleave) ] 2curry [ dup length swap ] dip 2each ; inline
463
464 : accumulator ( quot -- quot' vec )
465     V{ } clone [ [ push ] curry compose ] keep ; inline
466
467 : produce-as ( pred quot tail exemplar -- seq )
468     [ swap accumulator [ swap while ] dip ] dip like ; inline
469
470 : produce ( pred quot tail -- seq )
471     { } produce-as ; inline
472
473 : follow ( obj quot -- seq )
474     [ dup ] swap [ keep ] curry [ ] produce nip ; inline
475
476 : prepare-index ( seq quot -- seq n quot )
477     [ dup length ] dip ; inline
478
479 : each-index ( seq quot -- )
480     prepare-index 2each ; inline
481
482 : map-index ( seq quot -- )
483     prepare-index 2map ; inline
484
485 : reduce-index ( seq identity quot -- )
486     swapd each-index ; inline
487
488 : index ( obj seq -- n )
489     [ = ] with find drop ;
490
491 : index-from ( obj i seq -- n )
492     rot [ = ] curry find-from drop ;
493
494 : last-index ( obj seq -- n )
495     [ = ] with find-last drop ;
496
497 : last-index-from ( obj i seq -- n )
498     rot [ = ] curry find-last-from drop ;
499
500 : indices ( obj seq -- indices )
501     V{ } clone spin
502     [ rot = [ over push ] [ drop ] if ]
503     curry each-index ;
504
505 : nths ( indices seq -- seq' )
506     [ nth ] curry map ;
507
508 : contains? ( seq quot -- ? )
509     find drop >boolean ; inline
510
511 : member? ( elt seq -- ? )
512     [ = ] with contains? ;
513
514 : memq? ( elt seq -- ? )
515     [ eq? ] with contains? ;
516
517 : remove ( elt seq -- newseq )
518     [ = not ] with filter ;
519
520 : remq ( elt seq -- newseq )
521     [ eq? not ] with filter ;
522
523 : sift ( seq -- newseq )
524     [ ] filter ;
525
526 : harvest ( seq -- newseq )
527     [ empty? not ] filter ;
528
529 : mismatch ( seq1 seq2 -- i )
530     [ min-length ] 2keep
531     [ 2nth-unsafe = not ] 2curry
532     find drop ; inline
533
534 M: sequence <=>
535     2dup mismatch
536     [ -rot 2nth-unsafe <=> ] [ [ length ] compare ] if* ;
537
538 : sequence= ( seq1 seq2 -- ? )
539     2dup [ length ] bi@ =
540     [ mismatch not ] [ 2drop f ] if ; inline
541
542 : sequence-hashcode-step ( oldhash newpart -- newhash )
543     >fixnum swap [
544         dup -2 fixnum-shift-fast swap 5 fixnum-shift-fast
545         fixnum+fast fixnum+fast
546     ] keep fixnum-bitxor ; inline
547
548 : sequence-hashcode ( n seq -- x )
549     0 -rot [ hashcode* sequence-hashcode-step ] with each ; inline
550
551 M: reversed equal? over reversed? [ sequence= ] [ 2drop f ] if ;
552
553 M: slice equal? over slice? [ sequence= ] [ 2drop f ] if ;
554
555 : move ( to from seq -- )
556     2over =
557     [ 3drop ] [ [ nth swap ] [ set-nth ] bi ] if ; inline
558
559 <PRIVATE
560
561 : (filter-here) ( quot: ( elt -- ? ) store scan seq -- )
562     2dup length < [
563         [ move ] 3keep
564         [ nth-unsafe pick call [ 1+ ] when ] 2keep
565         [ 1+ ] dip
566         (filter-here)
567     ] [ nip set-length drop ] if ; inline recursive
568
569 PRIVATE>
570
571 : filter-here ( seq quot -- )
572     0 0 roll (filter-here) ; inline
573
574 : delete ( elt seq -- )
575     [ = not ] with filter-here ;
576
577 : delq ( elt seq -- )
578     [ eq? not ] with filter-here ;
579
580 : prefix ( seq elt -- newseq )
581     over [ over length 1+ ] dip [
582         [ 0 swap set-nth-unsafe ] keep
583         [ 1 swap copy ] keep
584     ] new-like ;
585
586 : suffix ( seq elt -- newseq )
587     over [ over length 1+ ] dip [
588         [ [ over length ] dip set-nth-unsafe ] keep
589         [ 0 swap copy ] keep
590     ] new-like ;
591
592 : peek ( seq -- elt ) [ length 1- ] [ nth ] bi ;
593
594 : pop* ( seq -- ) [ length 1- ] [ shorten ] bi ;
595
596 <PRIVATE
597
598 : move-backward ( shift from to seq -- )
599     2over = [
600         2drop 2drop
601     ] [
602         [ [ 2over + pick ] dip move [ 1+ ] dip ] keep
603         move-backward
604     ] if ;
605
606 : move-forward ( shift from to seq -- )
607     2over = [
608         2drop 2drop
609     ] [
610         [ [ pick [ dup dup ] dip + swap ] dip move 1- ] keep
611         move-forward
612     ] if ;
613
614 : (open-slice) ( shift from to seq ? -- )
615     [
616         [ [ 1- ] bi@ ] dip move-forward
617     ] [
618         [ over - ] 2dip move-backward
619     ] if ;
620
621 PRIVATE>
622
623 : open-slice ( shift from seq -- )
624     pick 0 = [
625         3drop
626     ] [
627         pick over length + over
628         [ pick 0 > [ [ length ] keep ] dip (open-slice) ] 2dip
629         set-length
630     ] if ;
631
632 : delete-slice ( from to seq -- )
633     check-slice [ over [ - ] dip ] dip open-slice ;
634
635 : delete-nth ( n seq -- )
636     [ dup 1+ ] dip delete-slice ;
637
638 : replace-slice ( new from to seq -- )
639     [ [ [ dup pick length + ] dip - over ] dip open-slice ] keep
640     copy ;
641
642 : remove-nth ( n seq -- seq' )
643     [ swap head-slice ] [ swap 1+ tail-slice ] 2bi append ;
644
645 : pop ( seq -- elt )
646     [ length 1- ] [ [ nth ] [ shorten ] 2bi ] bi ;
647
648 : all-equal? ( seq -- ? ) [ = ] monotonic? ;
649
650 : all-eq? ( seq -- ? ) [ eq? ] monotonic? ;
651
652 : exchange ( m n seq -- )
653     pick over bounds-check 2drop 2dup bounds-check 2drop
654     exchange-unsafe ;
655
656 : reverse-here ( seq -- )
657     dup length dup 2/ [
658         [ 2dup ] dip
659         tuck - 1- rot exchange-unsafe
660     ] each 2drop ;
661
662 : reverse ( seq -- newseq )
663     [
664         dup [ length ] keep new-sequence
665         [ 0 swap copy ] keep
666         [ reverse-here ] keep
667     ] keep like ;
668
669 : sum-lengths ( seq -- n )
670     0 [ length + ] reduce ;
671
672 : concat ( seq -- newseq )
673     [
674         { }
675     ] [
676         [ sum-lengths ] keep
677         [ first new-resizable ] keep
678         [ [ over push-all ] each ] keep
679         first like
680     ] if-empty ;
681
682 <PRIVATE
683
684 : joined-length ( seq glue -- n )
685     [ dup sum-lengths swap length 1 [-] ] dip length * + ;
686
687 PRIVATE>
688
689 : join ( seq glue -- newseq )
690     [
691         2dup joined-length over new-resizable spin
692         [ dup pick push-all ] [ pick push-all ] interleave drop
693     ] keep like ;
694
695 : padding ( seq n elt quot -- newseq )
696     [
697         [ over length [-] dup 0 = [ drop ] ] dip
698         [ <repetition> ] curry
699     ] dip compose if ; inline
700
701 : pad-left ( seq n elt -- padded )
702     [ swap dup append-as ] padding ;
703
704 : pad-right ( seq n elt -- padded )
705     [ append ] padding ;
706
707 : shorter? ( seq1 seq2 -- ? ) [ length ] bi@ < ;
708
709 : head? ( seq begin -- ? )
710     2dup shorter? [
711         2drop f
712     ] [
713         tuck length head-slice sequence=
714     ] if ;
715
716 : tail? ( seq end -- ? )
717     2dup shorter? [
718         2drop f
719     ] [
720         tuck length tail-slice* sequence=
721     ] if ;
722
723 : cut-slice ( seq n -- before-slice after-slice )
724     [ head-slice ] [ tail-slice ] 2bi ;
725
726 : insert-nth ( elt n seq -- seq' )
727     swap cut-slice [ swap suffix ] dip append ;
728
729 : midpoint@ ( seq -- n ) length 2/ ; inline
730
731 : halves ( seq -- first-slice second-slice )
732     dup midpoint@ cut-slice ;
733
734 : binary-reduce ( seq start quot: ( elt1 elt2 -- newelt ) -- value )
735     #! We can't use case here since combinators depends on
736     #! sequences
737     pick length dup 0 3 between? [
738         >fixnum {
739             [ drop nip ]
740             [ 2drop first ]
741             [ [ drop first2 ] dip call ]
742             [ [ drop first3 ] dip bi@ ]
743         } dispatch
744     ] [
745         drop
746         [ halves ] 2dip
747         [ [ binary-reduce ] 2curry bi@ ] keep
748         call
749     ] if ; inline recursive
750
751 : cut ( seq n -- before after )
752     [ head ] [ tail ] 2bi ;
753
754 : cut* ( seq n -- before after )
755     [ head* ] [ tail* ] 2bi ;
756
757 <PRIVATE
758
759 : (start) ( subseq seq n -- subseq seq ? )
760     pick length [
761         [ 3dup ] dip [ + swap nth-unsafe ] keep rot nth-unsafe =
762     ] all? nip ; inline
763
764 PRIVATE>
765
766 : start* ( subseq seq n -- i )
767     pick length pick length swap - 1+
768     [ (start) ] find-from
769     swap [ 3drop ] dip ;
770
771 : start ( subseq seq -- i ) 0 start* ; inline
772
773 : subseq? ( subseq seq -- ? ) start >boolean ;
774
775 : drop-prefix ( seq1 seq2 -- slice1 slice2 )
776     2dup mismatch [ 2dup min-length ] unless*
777     tuck [ tail-slice ] 2bi@ ;
778
779 : unclip ( seq -- rest first )
780     [ rest ] [ first-unsafe ] bi ;
781
782 : unclip-last ( seq -- butlast last )
783     [ but-last ] [ peek ] bi ;
784
785 : unclip-slice ( seq -- rest-slice first )
786     [ rest-slice ] [ first-unsafe ] bi ; inline
787
788 : 2unclip-slice ( seq1 seq2 -- rest-slice1 rest-slice2 first1 first2 )
789     [ unclip-slice ] bi@ swapd ; inline
790
791 : map-reduce ( seq map-quot reduce-quot -- result )
792     [ [ unclip-slice ] dip [ call ] keep ] dip
793     compose reduce ; inline
794
795 : 2map-reduce ( seq1 seq2 map-quot reduce-quot -- result )
796     [ [ 2unclip-slice ] dip [ call ] keep ] dip
797     compose 2reduce ; inline
798
799 : unclip-last-slice ( seq -- butlast-slice last )
800     [ but-last-slice ] [ peek ] bi ; inline
801
802 : <flat-slice> ( seq -- slice )
803     dup slice? [ { } like ] when 0 over length rot <slice> ;
804     inline
805
806 : trim-left-slice ( seq quot -- slice )
807     over [ [ not ] compose find drop ] dip swap
808     [ tail-slice ] [ dup length tail-slice ] if* ; inline
809     
810 : trim-left ( seq quot -- newseq )
811     over [ trim-left-slice ] dip like ; inline
812
813 : trim-right-slice ( seq quot -- slice )
814     over [ [ not ] compose find-last drop ] dip swap
815     [ 1+ head-slice ] [ 0 head-slice ] if* ; inline
816
817 : trim-right ( seq quot -- newseq )
818     over [ trim-right-slice ] dip like ; inline
819
820 : trim-slice ( seq quot -- slice )
821     [ trim-left-slice ] [ trim-right-slice ] bi ; inline
822
823 : trim ( seq quot -- newseq )
824     over [ trim-slice ] dip like ; inline
825
826 : sum ( seq -- n ) 0 [ + ] binary-reduce ;
827
828 : product ( seq -- n ) 1 [ * ] binary-reduce ;
829
830 : infimum ( seq -- n ) dup first [ min ] reduce ;
831
832 : supremum ( seq -- n ) dup first [ max ] reduce ;
833
834 : sigma ( seq quot -- n ) [ + ] compose 0 swap reduce ; inline
835
836 : count ( seq quot -- n ) [ 1 0 ? ] compose sigma ; inline
837
838 ! We hand-optimize flip to such a degree because type hints
839 ! cannot express that an array is an array of arrays yet, and
840 ! this word happens to be performance-critical since the compiler
841 ! itself uses it. Optimizing it like this reduced compile time.
842 <PRIVATE
843
844 : generic-flip ( matrix -- newmatrix )
845     [ dup first length [ length min ] reduce ] keep
846     [ [ nth-unsafe ] with { } map-as ] curry { } map-as ; inline
847
848 USE: arrays
849
850 : array-length ( array -- len )
851     { array } declare length>> ;
852
853 : array-flip ( matrix -- newmatrix )
854     [ dup first array-length [ array-length min ] reduce ] keep
855     [ [ array-nth ] with { } map-as ] curry { } map-as ;
856
857 PRIVATE>
858
859 : flip ( matrix -- newmatrix )
860     dup empty? [
861         dup array? [
862             dup [ array? ] all?
863             [ array-flip ] [ generic-flip ] if
864         ] [ generic-flip ] if
865     ] unless ;