Check-in [80c8c83034]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:initial import
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 80c8c830348aff44fde2153299f41c6323154dbf
User & Date: ovenpasta@pizzahack.eu 2016-07-07 18:11:39
Context
2016-07-07
18:15
Update README.md check-in: f849a85634 user: noreply@github.com tags: trunk
18:11
initial import check-in: 80c8c83034 user: ovenpasta@pizzahack.eu tags: trunk
18:07
Initial commit check-in: 3012e9c8d9 user: ovenpasta@users.noreply.github.com tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added fmt/README.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

                                 fmt
                                 ---

                    Combinator Formatting Library

                   http://synthcode.com/scheme/fmt/


This directory contains a portable combinator-based formatting library
for Scheme.  It has been tested on Chicken, Gauche, MzScheme 3.x and
Scheme48.

Documentation is in the file fmt.html.

Added fmt/VERSION.



>
1
0.8.4

Added fmt/c.sls.



















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
;;;; fmt-c.scm -- fmt module for emitting/pretty-printing C code
;;
;; Copyright (c) 2007 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

#!r6rs
(library
 (fmt c)
 (export
  fmt-in-macro? fmt-expression? fmt-return? fmt-default-type
  fmt-newline-before-brace? fmt-braceless-bodies?
  fmt-indent-space fmt-switch-indent-space fmt-op fmt-gen
  c-in-expr c-in-stmt c-in-test
  c-paren c-maybe-paren c-type c-literal? c-literal char->c-char
  c-struct c-union c-class c-enum c-typedef c-cast
  c-expr c-expr/sexp c-apply c-op c-indent c-current-indent-string
  c-wrap-stmt c-open-brace c-close-brace
  c-block c-braced-block c-begin
  c-fun c-var c-prototype c-param c-param-list
  c-while c-for c-if c-switch
  c-case c-case/fallthrough c-default
  c-break c-continue c-return c-goto c-label
  c-static c-const c-extern c-volatile c-auto c-restrict c-inline
  c++ c-- c+ c- c* c/ c% c& c^ c~ c! c&& c<< c>> c== c!= ;  |c\||  |c\|\||
  c< c> c<= c>= c= c+= c-= c*= c/= c%= c&= c^= c<<= c>>= ;++c --c ;  |c\|=|
  c++/post c--/post c. c->
  c-bit-or c-or c-bit-or=
  cpp-if cpp-ifdef cpp-ifndef cpp-elif cpp-endif cpp-else cpp-undef
  cpp-include cpp-define cpp-wrap-header cpp-pragma cpp-line
  cpp-error cpp-warning cpp-stringify cpp-sym-cat
  c-comment c-block-comment c-attribute
  )

 (import (chezscheme) 
	 (fmt fmt)
	 (only (srfi s1 lists) every)
	 (only (srfi s13 strings) substring/shared string-index string-index-right))

 (include "fmt-c.scm")

 )

Added fmt/fmt-c.scm.





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
;;;; fmt-c.scm -- fmt module for emitting/pretty-printing C code
;;
;; Copyright (c) 2007 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; additional state information

(define (fmt-in-macro? st) (fmt-ref st 'in-macro?))
(define (fmt-expression? st) (fmt-ref st 'expression?))
(define (fmt-return? st) (fmt-ref st 'return?))
(define (fmt-default-type st) (fmt-ref st 'default-type 'int))
(define (fmt-newline-before-brace? st) (fmt-ref st 'newline-before-brace?))
(define (fmt-braceless-bodies? st) (fmt-ref st 'braceless-bodies?))
(define (fmt-non-spaced-ops? st) (fmt-ref st 'non-spaced-ops?))
(define (fmt-no-wrap? st) (fmt-ref st 'no-wrap?))
(define (fmt-indent-space st) (fmt-ref st 'indent-space))
(define (fmt-switch-indent-space st) (fmt-ref st 'switch-indent-space))
(define (fmt-op st) (fmt-ref st 'op 'stmt))
(define (fmt-gen st) (fmt-ref st 'gen))

(define (c-in-expr proc) (fmt-let 'expression? #t proc))
(define (c-in-stmt proc) (fmt-let 'expression? #f proc))
(define (c-in-test proc) (fmt-let 'in-cond? #t (c-in-expr proc)))
(define (c-with-op op proc) (fmt-let 'op op proc))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; be smart about operator precedence

(define (c-op-precedence x)
  (if (string? x)
      (cond
        ((or (string=? x ".") (string=? x "->")) 10)
        ((or (string=? x "++") (string=? x "--")) 20)
        ((string=? x "|") 65)
        ((string=? x "||") 75)
        ((string=? x "|=") 85)
        ((or (string=? x "+=") (string=? x "-=")) 85)
        (else 95))
      (case x
        ;;((|::|) 5) ; C++
        ((paren bracket) 5)
        ((dot arrow post-decrement post-increment) 10)
        ((**) 15)                       ; Perl
        ((unary+ unary- ! ~ cast unary-* unary-& sizeof) 20) ; ++ --
        ((=~ !~) 25)                    ; Perl
        ((* / %) 30)
        ((+ -) 35)
        ((<< >>) 40)
        ((< > <= >=) 45)
        ((lt gt le ge) 45)              ; Perl
        ((== !=) 50)
        ((eq ne cmp) 50)                ; Perl
        ((&) 55)
        ((^) 60)
        ;;((|\||) 65)
        ((&&) 70)
        ;;((|\|\||) 75)
        ;;((.. ...) 77)                   ; Perl
        ((?) 80)
        ((= *= /= %= &= ^= <<= >>=) 85) ; |\|=| ;  += -=
        ((comma) 90)
        ((=>) 90)                       ; Perl
        ((not) 92)                      ; Perl
        ((and) 93)                      ; Perl
        ((or xor) 94)                   ; Perl
        (else 95))))

(define (c-op< x y) (< (c-op-precedence x) (c-op-precedence y)))

(define (c-paren x) (cat "(" x ")"))

(define (c-maybe-paren op x)
  (lambda (st)
    ((fmt-let 'op op
              (if (or (fmt-in-macro? st) (c-op< (fmt-op st) op))
                  (c-paren x)
                  x))
     st)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; default literals writer

(define (c-control-operator? x)
  (memq x '(if while switch repeat do for fun begin)))

(define (c-literal? x)
  (or (number? x) (string? x) (char? x) (boolean? x)))

(define (char->c-char c)
  (string-append "'" (c-escape-char c #\') "'"))

(define (c-escape-char c quote-char)
  (let ((n (char->integer c)))
    (if (<= 32 n 126)
        (if (or (eqv? c quote-char) (eqv? c #\\))
            (string #\\ c)
            (string c))
        (case n
          ((7) "\\a") ((8) "\\b") ((9) "\\t") ((10) "\\n")
          ((11) "\\v") ((12) "\\f") ((13) "\\r")
          (else (string-append "\\x" (number->string (char->integer c) 16)))))))

(define (c-format-number x)
  (if (and (integer? x) (exact? x))
      (lambda (st)
        ((case (fmt-radix st)
           ((16) (cat "0x" (string-upcase (number->string x 16))))
           ((8) (cat "0" (number->string x 8)))
           (else (dsp (number->string x))))
         st))
      (dsp (number->string x))))

(define (c-format-string x)
  (lambda (st) ((cat #\" (apply-cat (c-string-escaped x)) #\") st)))

(define (c-string-escaped x)
  (let loop ((parts '()) (idx (string-length x)))
    (cond ((string-index-right x c-needs-string-escape? 0 idx)
           => (lambda (special-idx)
                (loop (cons (c-escape-char (string-ref x special-idx) #\")
                            (cons (substring/shared x (+ special-idx 1) idx)
                                  parts))
                      special-idx)))
          (else
           (cons (substring/shared x 0 idx) parts)))))

(define (c-needs-string-escape? c)
  (if (<= 32 (char->integer c) 127) (memv c '(#\" #\\)) #t))

(define (c-simple-literal x)
  (c-wrap-stmt
   (cond ((char? x) (dsp (char->c-char x)))
         ((boolean? x) (dsp (if x "1" "0")))
         ((number? x) (c-format-number x))
         ((string? x) (c-format-string x))
         ((null? x) (dsp "NULL"))
         ((eof-object? x) (dsp "EOF"))
         (else (dsp (write-to-string x))))))

(define (c-literal x)
  (lambda (st)
    ((if (and (fmt-in-macro? st) (c-op< 'paren (fmt-op st))
              (not (c-literal? x)))
         (c-paren (c-simple-literal x))
         (c-simple-literal x))
     st)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; default expression generator

(define (c-expr/sexp x)
  (if (procedure? x)
      x
      (lambda (st)
        (cond
         ((pair? x)
          (case (car x)
            ((if) ((apply c-if (cdr x)) st))
            ((for) ((apply c-for (cdr x)) st))
            ((while) ((apply c-while (cdr x)) st))
            ((switch) ((apply c-switch (cdr x)) st))
            ((case) ((apply c-case (cdr x)) st))
            ((case/fallthrough) ((apply c-case/fallthrough (cdr x)) st))
            ((default) ((apply c-default (cdr x)) st))
            ((break) (c-break st))
            ((continue) (c-continue st))
            ((return) ((apply c-return (cdr x)) st))
            ((goto) ((apply c-goto (cdr x)) st))
            ((typedef) ((apply c-typedef (cdr x)) st))
            ((struct union class) ((apply c-struct/aux x) st))
            ((enum) ((apply c-enum (cdr x)) st))
            ((inline auto restrict register volatile extern static)
             ((cat (car x) " " (apply-cat (cdr x))) st))
            ;; non C-keywords must have some character invalid in a C
            ;; identifier to avoid conflicts - by default we prefix %
            ((vector-ref)
             ((c-wrap-stmt
               (cat (c-expr (cadr x)) "[" (c-expr (caddr x)) "]"))
              st))
            ((vector-set!)
             ((c= (c-in-expr
                   (cat (c-expr (cadr x)) "[" (c-expr (caddr x)) "]"))
                  (c-expr (cadddr x)))
              st))
            ((extern/C) ((apply c-extern/C (cdr x)) st))
            ((%apply) ((apply c-apply (cdr x)) st))
            ((%define) ((apply cpp-define (cdr x)) st))
            ((%include) ((apply cpp-include (cdr x)) st))
            ((%fun) ((apply c-fun (cdr x)) st))
            ((%cond)
             (let lp ((ls (cdr x)) (res '()))
               (if (null? ls)
                   ((apply c-if (reverse res)) st)
                   (lp (cdr ls)
                       (cons (if (pair? (cddar ls))
                                 (apply c-begin (cdar ls))
                                 (cadar ls))
                             (cons (caar ls) res))))))
            ((%prototype) ((apply c-prototype (cdr x)) st))
            ((%var) ((apply c-var (cdr x)) st))
            ((%begin) ((apply c-begin (cdr x)) st))
            ((%attribute) ((apply c-attribute (cdr x)) st))
            ((%line) ((apply cpp-line (cdr x)) st))
            ((%pragma %error %warning)
             ((apply cpp-generic (substring/shared (symbol->string (car x)) 1)
                     (cdr x)) st))
            ((%if %ifdef %ifndef %elif)
             ((apply cpp-if/aux (substring/shared (symbol->string (car x)) 1)
                     (cdr x)) st))
            ((%endif) ((apply cpp-endif (cdr x)) st))
            ((%block) ((apply c-braced-block (cdr x)) st))
            ((%comment) ((apply c-comment (cdr x)) st))
            ((:) ((apply c-label (cdr x)) st))
            ((%cast) ((apply c-cast (cdr x)) st))
            ((+ - & * / % ! ~ ^ && < > <= >= == != << >>
                = *= /= %= &= ^= >>= <<=) ; |\|| |\|\|| |\|=|
             ((apply c-op x) st))
            ((bitwise-and bit-and) ((apply c-op '& (cdr x)) st))
            ((bitwise-ior bit-or) ((apply c-op "|" (cdr x)) st))
            ((bitwise-xor bit-xor) ((apply c-op '^ (cdr x)) st))
            ((bitwise-not bit-not) ((apply c-op '~ (cdr x)) st))
            ((arithmetic-shift) ((apply c-op '<< (cdr x)) st))
            ((bitwise-ior= bit-or=) ((apply c-op "|=" (cdr x)) st))
            ((%or) ((apply c-op "||" (cdr x)) st))
            ((%. %field) ((apply c-op "." (cdr x)) st))
            ((%->) ((apply c-op "->" (cdr x)) st))
            (else
             (cond
              ((eq? (car x) (string->symbol "."))
               ((apply c-op "." (cdr x)) st))
              ((eq? (car x) (string->symbol "->"))
               ((apply c-op "->" (cdr x)) st))
              ((eq? (car x) (string->symbol "++"))
               ((apply c-op "++" (cdr x)) st))
              ((eq? (car x) (string->symbol "--"))
               ((apply c-op "--" (cdr x)) st))
              ((eq? (car x) (string->symbol "+="))
               ((apply c-op "+=" (cdr x)) st))
              ((eq? (car x) (string->symbol "-="))
               ((apply c-op "-=" (cdr x)) st))
              (else ((c-apply x) st))))))
         ((vector? x)
          ((c-wrap-stmt
            (fmt-try-fit
             (fmt-let 'no-wrap? #t
                      (cat "{" (fmt-join c-expr (vector->list x) ", ") "}"))
             (lambda (st)
               (let* ((col (fmt-col st))
                      (sep (string-append "," (make-nl-space col))))
                 ((cat "{" (fmt-join c-expr (vector->list x) sep)
                       "}" nl)
                  st)))))
           st))
         (else
          ((c-literal x) st))))))

(define (c-apply ls)
  (c-wrap-stmt
   (c-with-op
    'paren
    (cat (c-expr (car ls))
         (let ((flat (fmt-let 'no-wrap? #t (fmt-join c-expr (cdr ls) ", "))))
           (fmt-if
            fmt-no-wrap?
            (c-paren flat)
            (c-paren
             (fmt-try-fit
              flat
              (lambda (st)
                (let* ((col (fmt-col st))
                       (sep (string-append "," (make-nl-space col))))
                  ((fmt-join c-expr (cdr ls) sep) st)))))))))))

(define (c-expr x)
  (lambda (st) (((or (fmt-gen st) c-expr/sexp) x) st)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; comments, with Emacs-friendly escaping of nested comments

(define (make-comment-writer st)
  (let ((output (fmt-ref st 'writer)))
    (lambda (str st)
      (let ((lim (- (string-length str) 1)))
        (let lp ((i 0) (st st))
          (let ((j (string-index str #\/ i)))
            (if j
                (let ((st (if (and (> j 0)
                                   (eqv? #\* (string-ref str (- j 1))))
                              (output
                               "\\/"
                               (output (substring/shared str i j) st))
                              (output (substring/shared str i (+ j 1)) st))))
                  (lp (+ j 1)
                      (if (and (< j lim) (eqv? #\* (string-ref str (+ j 1))))
                          (output "\\" st)
                          st)))
                (output (substring/shared str i) st))))))))

(define (c-comment . args)
  (lambda (st)
    ((cat "/*" (fmt-let 'writer (make-comment-writer st)
                        (apply-cat args))
          "*/")
     st)))

(define (make-block-comment-writer st)
  (let ((output (make-comment-writer st))
        (indent (string-append (make-nl-space (+ (fmt-col st) 1)) "* ")))
    (lambda (str st)
      (let ((lim (string-length str)))
        (let lp ((i 0) (st st))
          (let ((j (string-index str #\newline i)))
            (if j
                (lp (+ j 1)
                    (output indent (output (substring/shared str i j) st)))
                (output (substring/shared str i) st))))))))

(define (c-block-comment . args)
  (lambda (st)
    (let ((col (fmt-col st))
          (row (fmt-row st))
          (indent (c-current-indent-string st)))
      ((cat "/* "
            (fmt-let 'writer (make-block-comment-writer st) (apply-cat args))
            (lambda (st)
              (cond
                ((= row (fmt-row st)) ((dsp " */") st))
                ;;((= (+ 3 col) (fmt-col st)) ((dsp "*/") st))
                (else ((cat fl indent " */") st)))))
       st))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; preprocessor

(define (make-cpp-writer st)
  (let ((output (fmt-ref st 'writer)))
    (lambda (str st)
      (let lp ((i 0) (st st))
        (let ((j (string-index str #\newline i)))
          (if j
              (lp (+ j 1)
                  (output
                   nl-str
                   (output " \\" (output (substring/shared str i j) st))))
              (output (substring/shared str i) st)))))))

(define (cpp-include file)
  (if (string? file)
      (cat fl "#include " (wrt file) fl)
      (cat fl "#include <" file ">" fl)))

(define (list-dot x)
  (cond ((pair? x) (list-dot (cdr x)))
        ((null? x) #f)
        (else x)))

(define (replace-tree from to x)
  (let replace ((x x))
    (cond ((eq? x from) to)
          ((pair? x) (cons (replace (car x)) (replace (cdr x))))
          (else x))))

(define (cpp-define x . body)
  (define (name-of x) (c-expr (if (pair? x) (cadr x) x)))
  (lambda (st)
    (let* ((body (cond
                   ((and (pair? x) (list-dot x))
                    => (lambda (dot)
                         (if (eq? dot '...)
                             body
                             (replace-tree dot '__VA_ARGS__ body))))
                   (else body)))
           (tail
            (if (pair? body)
                (cat " "
                     (fmt-let 'writer (make-cpp-writer st)
                              (fmt-let 'in-macro? (pair? x)
                                       ((if (or (not (pair? x))
                                                (and (null? (cdr body))
                                                     (c-literal? (car body))))
                                            (lambda (x) x)
                                            c-paren)
                                        (c-in-expr (apply c-begin body))))))
                (lambda (x) x))))
      ((c-in-expr
        (if (pair? x)
            (cat fl "#define " (name-of (car x))
                 (c-paren
                  (fmt-join/dot name-of
                                (lambda (dot) (dsp "..."))
                                (cdr x)
                                ", "))
                 tail fl)
            (cat fl "#define " (c-expr x) tail fl)))
       st))))

(define (cpp-expr x)
  (if (or (symbol? x) (string? x)) (dsp x) (c-expr x)))

(define (cpp-if/aux name check . o)
  (let* ((pass (and (pair? o) (car o)))
         (comment (if (member name '("ifdef" "ifndef"))
                      (cat "  "
                           (c-comment
                            " " (if (equal? name "ifndef") "! " "")
                            check " "))
                      ""))
         (endif (if pass (cat fl "#endif" comment) ""))
         (tail (cond
                ((and (pair? o) (pair? (cdr o)))
                 (if (pair? (cddr o))
                     (apply cpp-elif (cdr o))
                     (cat (cpp-else) (cadr o) endif)))
                (else endif))))
    (lambda (st)
      (let ((indent (c-current-indent-string st)))
        ((cat fl "#" name " " (cpp-expr check) fl
              (if pass (cat indent pass) "") fl
              tail fl)
         st)))))

(define (cpp-if check . o)
  (apply cpp-if/aux "if" check o))
(define (cpp-ifdef check . o)
  (apply cpp-if/aux "ifdef" check o))
(define (cpp-ifndef check . o)
  (apply cpp-if/aux "ifndef" check o))
(define (cpp-elif check . o)
  (apply cpp-if/aux "elif" check o))
(define (cpp-else . o)
  (cat fl "#else " (if (pair? o) (c-comment (car o)) "") fl))
(define (cpp-endif . o)
  (cat fl "#endif " (if (pair? o) (c-comment (car o)) "") fl))

(define (cpp-wrap-header name . body)
  (let ((name name)) ; consider auto-mangling
    (cpp-ifndef name (c-begin (cpp-define name) nl (apply c-begin body) nl))))

(define (cpp-line num . o)
  (cat fl "#line " num (if (pair? o) (cat " " (car o)) "") fl))

(define (cpp-generic name . ls)
  (cat fl "#" name (apply-cat ls) fl))

(define (cpp-undef . args) (apply cpp-generic "undef" args))
(define (cpp-pragma . args) (apply cpp-generic "pragma" args))
(define (cpp-error . args) (apply cpp-generic "error" args))
(define (cpp-warning . args) (apply cpp-generic "warning" args))

(define (cpp-stringify x)
  (cat "#" x))

(define (cpp-sym-cat . args)
  (fmt-join dsp args " ## "))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; general indentation and brace rules

(define (c-current-indent-string st . o)
  (make-space (max 0 (+ (fmt-col st) (if (pair? o) (car o) 0)))))

(define (c-indent st . o)
  (dsp (make-space (max 0 (+ (fmt-col st) (or (fmt-indent-space st) 4)
                             (if (pair? o) (car o) 0))))))

(define (c-indent/switch st)
  (dsp (make-space (+ (fmt-col st) (or (fmt-switch-indent-space st) 4)))))

(define (c-open-brace st)
  (if (fmt-newline-before-brace? st)
      (cat nl (c-current-indent-string st) "{" nl)
      (cat " {" nl)))

(define (c-close-brace st)
  (dsp "}"))

(define (c-wrap-stmt x)
  (fmt-if fmt-expression?
          (c-expr x)
          (cat (fmt-if fmt-return? "return " "")
               (c-in-expr (c-expr x)) ";" nl)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; code blocks

(define (c-block . args)
  (apply c-block/aux 0 args))

(define (c-block/aux offset header body0 . body)
   (let ((inner (apply c-begin body0 body)))
     (if (or (pair? body)
             (not (or (c-literal? body0)
                      (and (pair? body0)
                           (not (c-control-operator? (car body0)))))))
         (c-braced-block/aux offset header inner)
         (lambda (st)
           (if (fmt-braceless-bodies? st)
               ((cat header fl (c-indent st offset) inner fl) st)
               ((c-braced-block/aux offset header inner) st))))))

(define (c-braced-block . args)
  (apply c-braced-block/aux 0 args))

(define (c-braced-block/aux offset header . body)
   (lambda (st)
     ((cat header (c-open-brace st) (c-indent st offset)
           (apply c-begin body) fl
           (c-current-indent-string st offset) (c-close-brace st))
      st)))

(define (c-begin . args)
  (apply c-begin/aux #f args))

(define (c-begin/aux ret? body0 . body)
   (if (null? body)
       (c-expr body0)
       (lambda (st)
         (if (fmt-expression? st)
             ((fmt-try-fit
               (fmt-let 'no-wrap? #t (fmt-join c-expr (cons body0 body) ", "))
               (lambda (st)
                 (let ((indent (c-current-indent-string st)))
                   ((fmt-join c-expr (cons body0 body) (cat "," nl indent)) st))))
              st)
             (let ((orig-ret? (fmt-return? st)))
               ((fmt-join/last c-expr
                               (lambda (x) (fmt-let 'return? orig-ret? (c-expr x)))
                               (cons body0 body)
                               (cat fl (c-current-indent-string st)))
                (fmt-set! st 'return? (and ret? orig-ret?))))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; data structures

(define (c-struct/aux type x . o)
  (let* ((name (if (null? o) (if (or (symbol? x) (string? x)) x #f) x))
         (body (if name (car o) x))
         (o (if (null? o) o (cdr o))))
    (c-wrap-stmt
     (cat
      (c-braced-block
       (cat type (if (and name (not (equal? name ""))) (cat " " name) ""))
       (cat
        (c-in-stmt
         (if (list? body)
             (apply c-begin (map c-wrap-stmt (map c-param body)))
             (c-wrap-stmt (c-expr body))))))
      (if (pair? o) (cat " " (apply c-begin o)) (dsp ""))))))

(define (c-struct . args) (apply c-struct/aux "struct" args))
(define (c-union . args) (apply c-struct/aux "union" args))
(define (c-class . args) (apply c-struct/aux "class" args))

(define (c-enum x . o)
  (define (c-enum-one x)
    (if (pair? x) (cat (car x) " = " (c-expr (cadr x))) (dsp x)))
  (let* ((name (if (null? o) (if (or (symbol? x) (string? x)) x #f) x))
         (vals (if name (car o) x)))
    (c-wrap-stmt
     (cat
      (c-braced-block
       (if name (cat "enum " name) (dsp "enum"))
       (c-in-expr (apply c-begin (map c-enum-one vals))))))))

(define (c-attribute . args)
  (cat "__attribute__ ((" (fmt-join c-expr args ", ") "))"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; basic control structures

(define (c-while check . body)
  (cat (c-block (cat "while (" (c-in-test (c-expr check)) ")")
                (c-in-stmt (apply c-begin body)))
       fl))

(define (c-for init check update . body)
  (cat
   (c-block
    (c-in-expr
     (cat "for (" (c-expr init) "; " (c-in-test (c-expr check)) "; "
          (c-expr update ) ")"))
    (c-in-stmt (apply c-begin body)))
   fl))

(define (c-param x)
  (cond
    ((procedure? x) x)
    ((pair? x) (c-type (car x) (cadr x)))
    (else (cat (lambda (st) ((c-type (fmt-default-type st)) st)) " " x))))

(define (c-param-list ls)
  (c-in-expr (fmt-join/dot c-param (lambda (dot) (dsp "...")) ls ", ")))

(define (c-fun type name params . body)
  (cat (c-block (c-in-expr (c-prototype type name params))
                (fmt-let 'return? (not (eq? 'void type))
                         (c-in-stmt (apply c-begin body))))
       fl))

(define (c-prototype type name params . o)
  (c-wrap-stmt
   (cat (c-type type) " " (c-expr name) " (" (c-param-list params) ")"
        (fmt-join/prefix c-expr o " "))))

(define (c-static x) (cat "static " (c-expr x)))
(define (c-const x) (cat "const " (c-expr x)))
(define (c-restrict x) (cat "restrict " (c-expr x)))
(define (c-volatile x) (cat "volatile " (c-expr x)))
(define (c-auto x) (cat "auto " (c-expr x)))
(define (c-inline x) (cat "inline " (c-expr x)))
(define (c-extern x) (cat "extern " (c-expr x)))
(define (c-extern/C . body)
  (cat "extern \"C\" {" nl (apply c-begin body) nl "}" nl))

(define (c-type type . o)
  (let ((name (and (pair? o) (car o))))
    (cond
     ((pair? type)
      (case (car type)
        ((%fun)
         (cat (c-type (cadr type) #f)
              " (*" (or name "") ")("
              (fmt-join (lambda (x) (c-type x #f)) (caddr type) ", ") ")"))
        ((%array)
         (let ((name (cat name "[" (if (pair? (cddr type))
                                       (c-expr (caddr type))
                                       "")
                          "]")))
           (c-type (cadr type) name)))
        ((%pointer *)
         (let ((name (cat "*" (if name (c-expr name) ""))))
           (c-type (cadr type)
                   (if (and (pair? (cadr type)) (eq? '%array (caadr type)))
                       (c-paren name)
                       name))))
        ((enum) (apply c-enum name (cdr type)))
        ((struct union class)
         (cat (apply c-struct/aux (car type) (cdr type)) " " name))
        (else (fmt-join/last c-expr (lambda (x) (c-type x name)) type " "))))
     ((not type)
      (lambda (st) ((c-type (or (fmt-default-type st) 'int) name) st)))
     (else
      (cat (if (eq? '%pointer type) '* type) (if name (cat " " name) ""))))))

(define (c-var type name . init)
  (c-wrap-stmt
   (if (pair? init)
       (cat (c-type type name) " = " (c-expr (car init)))
       (c-type type (if (pair? name)
                        (fmt-join c-expr name ", ")
                        (c-expr name))))))

(define (c-cast type expr)
  (cat "(" (c-type type) ")" (c-expr expr)))

(define (c-typedef type alias . o)
  (c-wrap-stmt
   (cat "typedef " (c-type type alias) (fmt-join/prefix c-expr o " "))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Generalized IF: allows multiple tail forms for if/else if/.../else
;; blocks.  A final ELSE can be signified with a test of #t or 'else,
;; or by simply using an odd number of expressions (by which the
;; normal 2 or 3 clause IF forms are special cases).

(define (c-if/stmt c p . rest)
  (lambda (st)
    (let ((indent (c-current-indent-string st)))
      ((let lp ((c c) (p p) (ls rest))
         (if (or (eq? c 'else) (eq? c #t))
             (if (not (null? ls))
                 (error "forms after else clause in IF" c p ls)
                 (cat (c-block/aux -1 " else" p) fl))
             (let ((tail (if (pair? ls)
                             (if (pair? (cdr ls))
                                 (lp (car ls) (cadr ls) (cddr ls))
                                 (lp 'else (car ls) '()))
                             fl)))
               (cat (c-block/aux
                     (if (eq? ls rest) 0 -1)
                     (cat (if (eq? ls rest) (lambda (x) x) " else ")
                          "if (" (c-in-test (c-expr c)) ")") p)
                    tail))))
       st))))

(define (c-if/expr c p . rest)
  (let lp ((c c) (p p) (ls rest))
    (cond
      ((or (eq? c 'else) (eq? c #t))
       (if (not (null? ls))
           (error "forms after else clause in IF" c p ls)
           (c-expr p)))
      ((pair? ls)
       (cat (c-in-test (c-expr c)) " ? " (c-expr p) " : "
            (if (pair? (cdr ls))
                (lp (car ls) (cadr ls) (cddr ls))
                (lp 'else (car ls) '()))))
      (else
       (c-or (c-in-test (c-expr c)) (c-expr p))))))

(define (c-if . args)
  (fmt-if fmt-expression?
          (apply c-if/expr args)
          (apply c-if/stmt args)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; switch statements, automatic break handling

(define (c-label name)
  (lambda (st)
    (let ((indent (make-space (max 0 (- (fmt-col st) 2)))))
      ((cat fl indent name ":" fl) st))))

(define c-break
  (c-wrap-stmt (dsp "break")))
(define c-continue
  (c-wrap-stmt (dsp "continue")))
(define (c-return . result)
  (if (pair? result)
      (c-wrap-stmt (cat "return " (c-expr (car result))))
      (c-wrap-stmt (dsp "return"))))
(define (c-goto label)
  (c-wrap-stmt (cat "goto " (c-expr label))))

(define (c-switch val . clauses)
  (lambda (st)
    ((cat "switch (" (c-in-expr val) ")" (c-open-brace st)
          (c-indent/switch st)
          (c-in-stmt (apply c-begin/aux #t (map c-switch-clause clauses))) fl
          (c-current-indent-string st) (c-close-brace st) fl)
     st)))

(define (c-switch-clause/breaks x)
  (lambda (st)
    (let* ((break?
            (and (car x)
                 (not (member (cadr x) '(case/fallthrough
                                         default/fallthrough
                                         else/fallthrough)))))
           (explicit-case? (member (cadr x) '(case case/fallthrough)))
           (indent (c-current-indent-string st))
           (indent-body (c-indent st))
           (sep (string-append ":" nl-str indent)))
      ((cat (c-in-expr
             (fmt-join/suffix
              dsp
              (cond
               ((pair? (cadr x))
                (map (lambda (y) (cat (dsp "case ") (c-expr y)))
                     (cadr x)))
               (explicit-case?
                (map (lambda (y) (cat (dsp "case ") (c-expr y)))
                     (if (list? (caddr x))
                         (caddr x)
                         (list (caddr x)))))
               ((member (cadr x)
                        '(default else default/fallthrough else/fallthrough))
                (list (dsp "default")))
               (else
                (error
                 "unknown switch clause, expected a list or default but got"
                 (cadr x))))
              sep))
            (make-space (or (fmt-indent-space st) 4))
            (fmt-join c-expr
                      (if explicit-case? (cdddr x) (cddr x))
                      indent-body)
            (if (and break? (not (fmt-return? st)))
                (cat fl indent-body c-break)
                ""))
       st))))

(define (c-switch-clause x)
  (if (procedure? x) x (c-switch-clause/breaks (cons #t x))))
(define (c-switch-clause/no-break x)
  (if (procedure? x) x (c-switch-clause/breaks (cons #f x))))

(define (c-case x . body)
  (c-switch-clause (cons (if (pair? x) x (list x)) body)))
(define (c-case/fallthrough x . body)
  (c-switch-clause/no-break (cons (if (pair? x) x (list x)) body)))
(define (c-default . body)
  (c-switch-clause/breaks (cons #t (cons 'else body))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; operators

(define (c-op op first . rest)
  (if (null? rest)
      (c-unary-op op first)
      (apply c-binary-op op first rest)))

(define (c-binary-op op . ls)
  (define (lit-op? x) (or (c-literal? x) (symbol? x)))
  (let ((str (display-to-string op)))
    (c-wrap-stmt
     (c-maybe-paren
      op
      (if (or (equal? str ".") (equal? str "->"))
          (fmt-join c-expr ls str)
          (let ((flat
                 (fmt-let 'no-wrap? #t
                          (lambda (st)
                            ((fmt-join c-expr
                                       ls
                                       (if (and (fmt-non-spaced-ops? st)
                                                (every lit-op? ls))
                                           str
                                           (string-append " " str " ")))
                             st)))))
            (fmt-if
             fmt-no-wrap?
             flat
             (fmt-try-fit
              flat
              (lambda (st)
                   ((fmt-join c-expr
                              ls
                              (cat nl (make-space (+ 2 (fmt-col st))) str " "))
                    st))))))))))

(define (c-unary-op op x)
  (c-wrap-stmt
   (cat (display-to-string op) (c-maybe-paren op (c-expr x)))))

;; some convenience definitions

(define (c++ . args) (apply c-op "++" args))
(define (c-- . args) (apply c-op "--" args))
(define (c+ . args) (apply c-op '+ args))
(define (c- . args) (apply c-op '- args))
(define (c* . args) (apply c-op '* args))
(define (c/ . args) (apply c-op '/ args))
(define (c% . args) (apply c-op '% args))
(define (c& . args) (apply c-op '& args))
;; (define (|c\|| . args) (apply c-op '|\|| args))
(define (c^ . args) (apply c-op '^ args))
(define (c~ . args) (apply c-op '~ args))
(define (c! . args) (apply c-op '! args))
(define (c&& . args) (apply c-op '&& args))
;; (define (|c\|\|| . args) (apply c-op '|\|\|| args))
(define (c<< . args) (apply c-op '<< args))
(define (c>> . args) (apply c-op '>> args))
(define (c== . args) (apply c-op '== args))
(define (c!= . args) (apply c-op '!= args))
(define (c< . args) (apply c-op '< args))
(define (c> . args) (apply c-op '> args))
(define (c<= . args) (apply c-op '<= args))
(define (c>= . args) (apply c-op '>= args))
(define (c= . args) (apply c-op '= args))
(define (c+= . args) (apply c-op "+=" args))
(define (c-= . args) (apply c-op "-=" args))
(define (c*= . args) (apply c-op '*= args))
(define (c/= . args) (apply c-op '/= args))
(define (c%= . args) (apply c-op '%= args))
(define (c&= . args) (apply c-op '&= args))
;; (define (|c\|=| . args) (apply c-op '|\|=| args))
(define (c^= . args) (apply c-op '^= args))
(define (c<<= . args) (apply c-op '<<= args))
(define (c>>= . args) (apply c-op '>>= args))

(define (c. . args) (apply c-op "." args))
(define (c-> . args) (apply c-op "->" args))

(define (c-bit-or . args) (apply c-op "|" args))
(define (c-or . args) (apply c-op "||" args))
(define (c-bit-or= . args) (apply c-op "|=" args))

(define (c++/post x)
  (cat (c-maybe-paren 'post-increment (c-expr x)) "++"))
(define (c--/post x)
  (cat (c-maybe-paren 'post-decrement (c-expr x)) "--"))

Added fmt/fmt-color.scm.



























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
;;;; fmt-color.scm -- colored output
;;
;; Copyright (c) 2006-2007 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

(define (fmt-color st) (fmt-ref st 'color))
(define (fmt-in-html? st) (fmt-ref st 'in-html?))
(define (fmt-use-html-font? st) (fmt-ref st 'use-html-font?))

(define (color->ansi x)
  (if (number? x)
      (let ((r (arithmetic-shift x -16))
            (g (bitwise-and (arithmetic-shift x -8) #xFF))
            (b (bitwise-and x #xFF)))
        ;; just picks the highest color value - need to detect blends
        (color->ansi
         (cond
           ((> r g) (if (> r b) 'red 'blue))
           ((> g b) 'green)
           (else 'blue))))
      (case x
        ((bold) "1")
        ((dark) "2")
        ((underline) "4")
        ((black) "30")
        ((red) "31")
        ((green) "32")
        ((yellow) "33")
        ((blue) "34")
        ((magenta) "35")
        ((cyan) "36")
        ((white) "37")
        (else "0"))))

(define (ansi-escape color)
  (cat (integer->char 27) "[" (color->ansi color) "m"))

(define (fmt-in-html . args)
  (fmt-let 'in-html? #t (apply-cat args)))

(define (fmt-colored color . args)
  (fmt-if fmt-in-html?
          (cond
            ((eq? color 'bold)
             (cat "<b>" (apply-cat args) "</b>"))
            ((eq? color 'underline)
             (cat "<u>" (apply-cat args) "</u>"))
            (else
             (let ((cname (if (number? color) (cat "#" color) color)))
               (fmt-if fmt-use-html-font?
                       (cat "<font color=\"" cname "\">" (apply-cat args)
                            "</font>")
                       (cat "<span style=color:\"" cname "\">"
                            (apply-cat args) "</span>")))))
          (lambda (st)
            (let ((old-color (fmt-color st)))
              ((fmt-let 'color color
                        (cat (ansi-escape color)
                             (apply-cat args)
                             (if (or (memv color '(bold underline))
                                     (memv old-color '(bold underline)))
                                 (ansi-escape 'reset)
                                 (lambda (st) st))
                             (ansi-escape old-color)))
               st)))))

(define (fmt-red . args) (fmt-colored 'red (apply-cat args)))
(define (fmt-blue . args) (fmt-colored 'blue (apply-cat args)))
(define (fmt-green . args) (fmt-colored 'green (apply-cat args)))
(define (fmt-cyan . args) (fmt-colored 'cyan (apply-cat args)))
(define (fmt-yellow . args) (fmt-colored 'yellow (apply-cat args)))
(define (fmt-magenta . args) (fmt-colored 'magenta (apply-cat args)))
(define (fmt-white . args) (fmt-colored 'white (apply-cat args)))
(define (fmt-black . args) (fmt-colored 'black (apply-cat args)))
(define (fmt-bold . args) (fmt-colored 'bold (apply-cat args)))
(define (fmt-underline . args) (fmt-colored 'underline (apply-cat args)))

Added fmt/fmt-column.scm.



































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
;;;; fmt-block.scm -- columnar formatting
;;
;; Copyright (c) 2006-2011 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Columnar formatting
;;
;; A line-oriented formatter.  Takes a list of
;;   (line-fmt1 gen-fmt1 line-fmt2 gen-fmt2 ...)
;; and formats each of the gen-fmt1 formats as columns, printed
;; side-by-side, each line allowing post-processing done by line-fmt1
;; (just use dsp if you want to display the lines verbatim).

;; Continuations come to the rescue to make this work properly,
;; letting us weave the output between different columns without
;; needing to build up intermediate strings.

(define (fmt-columns . ls)
  (lambda (orig-st)
    (call-with-current-continuation
     (lambda (return)
       (define (infinite? x)
         (and (pair? x) (pair? (cdr x)) (pair? (cddr x)) (caddr x)))
       (let ((q1 '())
             (q2 '())
             (remaining (length (remove infinite? ls))))
         (define (enq! proc) (set! q2 (cons proc q2)))
         (define (deq!) (let ((proc (car q1))) (set! q1 (cdr q1)) proc))
         (define (line-init!) (set! q1 (reverse q2)) (set! q2 '()))
         (define (line-done?) (null? q1))
         (define line-buf '())
         (define line-non-empty? #f)
         (define (write-column fmt str finite?)
           (set! line-buf (cons (cons fmt str) line-buf))
           (if finite? (set! line-non-empty? #t)))
         (define (write-line)
           (cond
            (line-non-empty?
             (for-each
              (lambda (x) (set! orig-st (((car x) (cdr x)) orig-st)))
              (reverse line-buf))
             (set! orig-st (nl orig-st))))
           (set! line-buf '())
           (set! line-non-empty? #f)
           (line-init!))
         (define (next cont)
           (enq! cont)
           (cond
            ((line-done?) 
             (write-line)
             (if (not (positive? remaining)) (finish) ((deq!) #f)))
            (else ((deq!) #f))))
         (define (finish)
           (write-line)
           (return orig-st))
         (define (make-empty-col fmt)
           (define (blank *ignored*)
             (write-column fmt "" #f)
             (next blank))    ; infinite loop, next terminates for us
           blank)
         (define (make-col st fmt gen finite?)
           (let ((acc '()))            ; buffer incomplete lines
             (lambda (*ignored*)
               (define (output* str st)
                 (let lp ((i 0))
                   (let ((nli (string-index str #\newline i)))
                     (cond
                      (nli
                       (let ((line
                              (string-concatenate-reverse
                               (cons (substring/shared str i nli) acc))))
                         (set! acc '())
                         (write-column fmt line finite?)
                         (call-with-current-continuation next) 
                         (lp (+ nli 1))))
                      (else
                       (set! acc (cons (substring/shared str i) acc))))))
                 ;; update - don't output or the string port will fill up
                 (fmt-update str st))
               ;; gen threads through it's own state, ignore result
               (gen (fmt-set-writer! (copy-fmt-state st) output*))
               ;; reduce # of remaining finite columns
               (set! remaining (- remaining 1))
               ;; write any remaining accumulated output
               (if (pair? acc)
                   (let ((s (string-concatenate-reverse acc)))
                     (write-column fmt s (and finite? (not (equal? s ""))))))
               ;; (maybe) loop with an empty column in place
               (if (not (positive? remaining))
                   (finish)
                   (next (make-empty-col fmt))))))
         ;; queue up the initial formatters
         (for-each
          (lambda (col)
            (let ((st (fmt-set-port! (copy-fmt-state orig-st)
                                     (open-output-string))))
              (enq! (make-col st (car col) (dsp (cadr col))
                              (not (infinite? col))))))
          ls)
         (line-init!)
         ;; start
         ((deq!) #f))))))

(define (columnar . ls)
  (define (proportional-width? w)
    (and (number? w)
         (or (< 0 w 1)
             (and (inexact? w) (= w 1.0)))))
  (define (whitespace-pad? st)
    (char-whitespace? (or (fmt-pad-char st) #\space)))
  (define (build-column ls)
    (let-optionals* ls ((fixed-width #f)
                        (width #f)
                        (last? #t)
                        (tail '())
                        (gen #f)
                        (prefix '())
                        (align 'left)
                        (infinite? #f))
      (define (scale-width st)
        (max 1 (inexact->exact
                (truncate (* width (- (fmt-width st) fixed-width))))))
      (define (padder)
        (if (proportional-width? width)
            (case align
              ((right)
               (lambda (str) (lambda (st) ((pad/left (scale-width st) str) st))))
              ((center)
               (lambda (str) (lambda (st) ((pad/both (scale-width st) str) st))))
              (else
               (lambda (str) (lambda (st) ((pad/right (scale-width st) str) st)))))
            (case align
              ((right) (lambda (str) (pad/left width str)))
              ((center) (lambda (str) (pad/both width str)))
              (else (lambda (str) (pad/right width str))))))
      (define (affix x)
        (cond
         ((pair? tail)
          (lambda (str)
            (cat (string-concatenate prefix)
                 (x str)
                 (string-concatenate tail))))
         ((pair? prefix)
          (lambda (str) (cat (string-concatenate prefix) (x str))))
         (else x)))
      (list
       ;; line formatter
       (affix
        (let ((pad (padder)))
          (if (and last? (not (pair? tail)) (eq? align 'left))
              (lambda (str)
                (lambda (st)
                  (((if (whitespace-pad? st) dsp pad) str) st)))
              pad)))
       ;; generator
       (if (proportional-width? width)
           (lambda (st) ((with-width (scale-width st) gen) st))
           (with-width width gen))
       infinite?
       )))
  (define (adjust-widths ls border-width)
    (let* ((fixed-ls
            (filter (lambda (x) (and (number? (car x)) (>= (car x) 1))) ls))
           (fixed-total (fold + border-width (map car fixed-ls)))
           (scaled-ls (filter (lambda (x) (proportional-width? (car x))) ls))
           (denom (- (length ls) (+ (length fixed-ls) (length scaled-ls))))
           (rest (if (zero? denom)
                     0
                     (exact->inexact
                      (/ (- 1 (fold + 0 (map car scaled-ls))) denom)))))
      (if (negative? rest)
          (error 'columnar "fractional widths must sum to less than 1"
                 (map car scaled-ls)))
      (map
       (lambda (col)
         (cons fixed-total
               (if (not (number? (car col))) (cons rest (cdr col)) col)))
       ls)))
  (define (finish ls border-width)
    (apply fmt-columns
           (map build-column (adjust-widths (reverse ls) border-width))))
  (let lp ((ls ls) (strs '()) (align 'left) (infinite? #f)
           (width #t) (border-width 0) (res '()))
    (cond
     ((null? ls)
      (if (pair? strs)
          (finish (cons (cons (caar res)
                              (cons #t (cons (append (reverse strs)
                                                     (caddar res))
                                             (cdddar res))))
                        (cdr res))
                  border-width)
          (finish (cons (cons (caar res) (cons #t (cddar res))) (cdr res))
                  border-width)))
     ((string? (car ls))
      (if (string-index (car ls) #\newline)
          (error 'columnar "column string literals can't contain newlines")
          (lp (cdr ls) (cons (car ls) strs) align infinite?
              width (+ border-width (string-length (car ls))) res)))
     ((number? (car ls))
      (lp (cdr ls) strs align infinite? (car ls) border-width res))
     ((eq? (car ls) 'infinite)
      (lp (cdr ls) strs align #t width border-width res))
     ((symbol? (car ls))
      (lp (cdr ls) strs (car ls) infinite? width border-width res))
     ((procedure? (car ls))
      (lp (cdr ls) '() 'left #f #t border-width
          (cons (list width #f '() (car ls) (reverse strs) align infinite?)
                res)))
     (else
      (error 'columnar "invalid column" (car ls))))))

(define (max-line-width string-width str)
  (let lp ((i 0) (hi 0))
    (let ((j (string-index str #\newline i)))
      (if j
          (lp (+ j 1) (max hi (string-width (substring str i j))))
          (max hi (string-width (substring str i (string-length str))))))))

(define (pad-finite st proc width)
  (let* ((str ((fmt-to-string proc) (copy-fmt-state st)))
         (w (max-line-width (or (fmt-string-width st) string-length) str)))
    (list (cat str)
          (if (and (integer? width) (exact? width))
              (max width w)
              w))))

(define (tabular . ls)
  (lambda (st)
    (let lp ((ls ls) (infinite? #f) (width #t) (res '()))
      (cond
       ((null? ls)
        ((apply columnar (reverse res)) st))
       ((number? (car ls))
        (lp (cdr ls) infinite? (car ls) res))
       ((eq? 'infinite (car ls))
        (lp (cdr ls) #t width (cons (car ls) res)))
       ((procedure? (car ls))
        (if infinite?
            (if width
                (lp (cdr ls) #f #t (cons (car ls) (cons width res)))
                (lp (cdr ls) #f #t (cons (car ls) res)))
            (let ((gen+width (pad-finite st (car ls) width)))
              (lp (cdr ls) #f #t (append gen+width res)))))
       (else
        (lp (cdr ls) infinite? width (cons (car ls) res)))))))

;; break lines only, don't fmt-join short lines or justify
(define (fold-lines . ls)
  (lambda (st)
    (define output (fmt-writer st))
    (define (kons-in-line str st)
      (let ((len ((or (fmt-string-width st) string-length) str))
            (space (- (fmt-width st) (fmt-col st))))
        (cond
          ((or (<= len space) (not (positive? space)))
           (output str st))
          (else
           (kons-in-line
            (substring/shared str space len)
            (output nl-str
                    (output (substring/shared str 0 space) st)))))))
    ((fmt-let
      'writer
      (lambda (str st)
        (let lp ((str str) (st st))
          (let ((nli (string-index str #\newline)))
            (cond
              ((not nli)
               (kons-in-line str st))
              (else
               (lp (substring/shared str (+ nli 1))
                   (output nl-str
                           (kons-in-line
                            (substring/shared str 0 nli)
                            st))))))))
      (apply-cat ls))
     st)))

(define (wrap-fold-words seq knil max-width get-width line . o)
  (let* ((last-line (if (pair? o) (car o) line))
         (vec (if (list? seq) (list->vector seq) seq))
         (len (vector-length vec))
         (len-1 (- len 1))
         (breaks (make-vector len #f))
         (penalties (make-vector len #f))
         (widths
          (list->vector
           (map get-width (if (list? seq) seq (vector->list vec))))))
    (define (largest-fit i)
      (let lp ((j (+ i 1)) (width (vector-ref widths i)))
        (let ((width (+ width 1 (vector-ref widths j))))
          (cond
            ((>= width max-width) (- j 1))
            ((>= j len-1) len-1)
            (else (lp (+ j 1) width))))))
    (define (min-penalty! i)
      (cond
        ((>= i len-1) 0)
        ((vector-ref penalties i))
        (else
         (vector-set! penalties i (expt (+ max-width 1) 3))
         (vector-set! breaks i i)
         (let ((k (largest-fit i)))
           (let lp ((j i) (width 0))
             (if (<= j k)
                 (let* ((width (+ width (vector-ref widths j)))
                        (break-penalty
                         (+ (max 0 (expt (- max-width (+ width (- j i))) 3))
                            (min-penalty! (+ j 1)))))
                   (cond
                     ((< break-penalty (vector-ref penalties i))
                      (vector-set! breaks i j)
                      (vector-set! penalties i break-penalty)))
                   (lp (+ j 1) width)))))
         (if (>= (vector-ref breaks i) len-1)
             (vector-set! penalties i 0))
         (vector-ref penalties i))))
    (define (sub-list i j)
      (let lp ((i i) (res '()))
        (if (> i j)
            (reverse res)
            (lp (+ i 1) (cons (vector-ref vec i) res)))))
    (cond
     ((zero? len)
      ;; degenerate case
      (last-line '() knil))
     (else
      ;; compute optimum breaks
      (vector-set! breaks len-1 len-1)
      (vector-set! penalties len-1 0)
      (min-penalty! 0)
      ;; fold
      (let lp ((i 0) (acc knil))
        (let ((break (vector-ref breaks i)))
          (if (>= break len-1)
              (last-line (sub-list i len-1) acc)
              (lp (+ break 1) (line (sub-list i break) acc)))))))))

;; XXXX don't split, traverse the string manually and keep track of
;; sentence endings so we can insert two spaces
(define (wrap-fold str . o)
  (apply wrap-fold-words (string-tokenize str) o))

(define (wrap-lines . ls)
  (define (print-line ls st)
    (nl ((fmt-join dsp ls " ") st)))
  (define buffer '())
  (lambda (st)
    ((fmt-let
      'writer
      (lambda (str st) (set! buffer (cons str buffer)) st)
      (apply-cat ls))
     st)
    (wrap-fold (string-concatenate-reverse buffer)
               st (fmt-width st)
               (or (fmt-string-width st) string-length)
               print-line)))

(define (justify . ls)
  (lambda (st)
    (let ((width (fmt-width st))
          (string-width (or (fmt-string-width st) string-length))
          (output (fmt-writer st))
          (buffer '()))
      (define (justify-line ls st)
        (if (null? ls)
            (nl st)
            (let* ((sum (fold (lambda (s n) (+ n (string-width s))) 0 ls))
                   (len (length ls))
                   (diff (max 0 (- width sum)))
                   (sep (make-string (if (= len 1)
                                         0
                                         (quotient diff (- len 1)))
                                     #\space))
                   (rem (if (= len 1)
                            diff
                            (remainder diff (- len 1)))))
              (output
               (call-with-output-string
                 (lambda (p)
                   (display (car ls) p)
                   (let lp ((ls (cdr ls)) (i 1))
                     (cond
                       ((pair? ls)
                        (display sep p)
                        (if (<= i rem) (write-char #\space p))
                        (display (car ls) p)
                        (lp (cdr ls) (+ i 1)))))
                   (newline p)))
               st))))
      (define (justify-last ls st)
        (nl ((fmt-join dsp ls " ") st)))
      ((fmt-let
        'writer
        (lambda (str st) (set! buffer (cons str buffer)) st)
        (apply-cat ls))
       st)
      (wrap-fold (string-concatenate-reverse buffer)
                 st width string-width justify-line justify-last))))

(define (fmt-file path)
  (lambda (st)
    (call-with-input-file path
      (lambda (p)
        (let lp ((st st))
          (let ((line (read-line p)))
            (if (eof-object? line)
                st
                (lp (nl ((dsp line) st))))))))))

(define (line-numbers . o)
  (let ((start (if (pair? o) (car o) 1)))
    (fmt-join/range dsp start #f nl-str)))

Added fmt/fmt-gauche.scm.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
;;;; fmt-gauche.scm -- Gauche fmt extension
;;
;; Copyright (c) 2006-2011 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

(define-module text.fmt
  (use srfi-1)
  (use srfi-6)
  (use srfi-13)
  (export
   new-fmt-state
   fmt fmt-start fmt-if fmt-capture fmt-let fmt-bind fmt-null
   fmt-ref fmt-set! fmt-add-properties! fmt-set-property!
   fmt-col fmt-set-col! fmt-row fmt-set-row!
   fmt-radix fmt-set-radix! fmt-precision fmt-set-precision!
   fmt-properties fmt-set-properties! fmt-width fmt-set-width!
   fmt-writer fmt-set-writer! fmt-port fmt-set-port!
   fmt-decimal-sep fmt-set-decimal-sep!
   fmt-file fmt-try-fit cat apply-cat nl fl nl-str
   fmt-join fmt-join/last fmt-join/dot
   fmt-join/prefix fmt-join/suffix fmt-join/range
   pad pad/right pad/left pad/both trim trim/left trim/both trim/length
   fit fit/left fit/both tab-to space-to wrt wrt/unshared dsp
   pretty pretty/unshared slashified maybe-slashified
   num num/si num/fit num/comma radix fix decimal-align ellipses
   upcase downcase titlecase pad-char comma-char decimal-char
   with-width wrap-lines fold-lines justify
   make-string-fmt-transformer
   make-space make-nl-space display-to-string write-to-string
   fmt-columns columnar tabular line-numbers
   ))
(select-module text.fmt)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SRFI-69 compatible hashtables

(define (make-eq?-table)
  (make-hash-table 'eq?))
(define hash-table-ref/default hash-table-get)
(define hash-table-set! hash-table-put!)
(define (hash-table-walk tab proc) (hash-table-for-each tab proc))

(define (mantissa+exponent num)
  (let ((vec (decode-float num)))
    (list (vector-ref vec 0) (vector-ref vec 1))))

Added fmt/fmt-js.scm.





















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
;;;; fmt-js.scm -- javascript formatting utilities
;;
;; Copyright (c) 2011-2012 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (js-expr x)
  (fmt-let 'gen js-expr/sexp
           (lambda (st) (((or (fmt-gen st) js-expr/sexp) x) st))))

(define (js-expr/sexp x)
  (cond
   ((procedure? x)
    x)
   ((pair? x)
    (case (car x)
      ((%fun function) (apply js-function (cdr x)))
      ((%var var) (apply js-var (cdr x)))
      ((eq? ===) (apply js=== (cdr x)))
      ((>>>) (apply js>>> (cdr x)))
      ((%array) (js-array x))
      ((%object) (js-object (cdr x)))
      ((%comment) (js-comment x))
      (else (c-expr/sexp x))))
   ((vector? x) (js-array x))
   ((boolean? x) (cat (if x "true" "false")))
   ((char? x) (js-expr/sexp (string x)))
   (else (c-expr/sexp x))))

(define (js-function . x)
  (let* ((name (and (symbol? (car x)) (car x)))
         (params (if name (cadr x) (car x)))
         (body (if name (cddr x) (cdr x))))
    (c-block
     (cat "function " (dsp (or name ""))  "("
          (fmt-join dsp params ", ") ")")
     (fmt-let 'return? #t (c-in-stmt (apply c-begin body))))))

(define (js-var . args)
  (apply c-var 'var args))

(define (js=== . args)
  (apply c-op "===" args))

(define (js>>> . args)
  (apply c-op ">>>" args))

(define (js-comment . args)
  (columnar "// " (apply-cat args)))

(define (js-array x)
  (let ((ls (vector->list x)))
    (c-wrap-stmt
     (fmt-try-fit
      (fmt-let 'no-wrap? #t (cat "[" (fmt-join js-expr ls ", ") "]"))
      (lambda (st)
        (let* ((col (fmt-col st))
               (sep (string-append "," (make-nl-space col))))
          ((cat "[" (fmt-join js-expr ls sep) "]" nl) st)))))))

(define (js-pair x)
  (cat (js-expr (car x)) ": " (js-expr (cdr x))))

(define (js-object ls)
  (c-in-expr
   (fmt-try-fit
    (fmt-let 'no-wrap? #t (cat "{" (fmt-join js-pair ls ", ") "}"))
    (lambda (st)
      (let* ((col (fmt-col st))
             (sep (string-append "," (make-nl-space col))))
        ((cat "{" (fmt-join js-pair ls sep) "}" nl) st))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Added fmt/fmt-pretty.scm.















































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
;;;; fmt-pretty.scm -- pretty printing format combinator
;;
;; Copyright (c) 2006-2007 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; additional settings

(define (fmt-shares st) (fmt-ref st 'shares))
(define (fmt-set-shares! st x) (fmt-set! st 'shares x))
(define (fmt-copy-shares st)
  (fmt-set-shares! (copy-fmt-state st) (copy-shares (fmt-shares st))))

(define (copy-shares shares)
  (let ((tab (make-eq?-table)))
    (hash-table-walk
     (car shares)
     (lambda (obj x) (eq?-table-set! tab obj (cons (car x) (cdr x)))))
    (cons tab (cdr shares))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; utilities

(define (fmt-shared-write obj proc)
  (lambda (st)
    (let* ((shares (fmt-shares st))
           (cell (and shares (eq?-table-ref (car shares) obj))))
      (if (pair? cell)
          (cond
            ((cdr cell)
             ((fmt-writer st) (gen-shared-ref (car cell) "#") st))
            (else
             (set-car! cell (cdr shares))
             (set-cdr! cell #t)
             (set-cdr! shares (+ (cdr shares) 1))
             (proc ((fmt-writer st) (gen-shared-ref (car cell) "=") st))))
          (proc st)))))

(define (fmt-join/shares fmt ls . o)
  (let ((sep (dsp (if (pair? o) (car o) " "))))
    (lambda (st)
      (if (null? ls)
          st
          (let* ((shares (fmt-shares st))
                 (tab (car shares))
                 (output (fmt-writer st)))
            (let lp ((ls ls) (st st))
              (let ((st ((fmt (car ls)) st))
                    (rest (cdr ls)))
                (cond
                 ((null? rest) st)
                 ((pair? rest)
                  (call-with-shared-ref/cdr rest st shares
                      (lambda (st) (lp rest st))
                    sep))
                 (else ((fmt rest) (output ". " (sep st))))))))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; pretty printing

(define (non-app? x)
  (if (pair? x)
      (or (not (or (null? (cdr x)) (pair? (cdr x))))
          (non-app? (car x)))
      (not (symbol? x))))

(define syntax-abbrevs
  '((quote . "'") (quasiquote . "`") (unquote . ",") (unquote-splicing . ",@")
    ))

(define (pp-let ls)
  (if (and (pair? (cdr ls)) (symbol? (cadr ls)))
      (pp-with-indent 2 ls)
      (pp-with-indent 1 ls)))

(define indent-rules
  `((lambda . 1) (define . 1)
    (let . ,pp-let) (loop . ,pp-let)
    (let* . 1) (letrec . 1) (letrec* . 1) (and-let* . 1) (let1 . 2)
    (let-values . 1) (let*-values . 1) (receive . 2) (parameterize . 1)
    (let-syntax . 1) (letrec-syntax . 1) (syntax-rules . 1) (syntax-case . 2)
    (match . 1) (match-let . 1) (match-let* . 1)
    (if . 3) (when . 1) (unless . 1) (case . 1) (while . 1) (until . 1)
    (do . 2) (dotimes . 1) (dolist . 1) (test . 1)
    (condition-case . 1) (guard . 1) (rec . 1)
    (call-with-current-continuation . 0)
    ))

(define indent-prefix-rules
  `(("with-" . -1) ("call-with-" . -1) ("define-" . 1))
  )

(define indent-suffix-rules
  `(("-case" . 1))
  )

(define (pp-indentation form)
  (let ((indent
         (cond
          ((assq (car form) indent-rules) => cdr)
          ((and (symbol? (car form))
                (let ((str (symbol->string (car form))))
                  (or (find (lambda (rx) (string-prefix? (car rx) str))
                            indent-prefix-rules)
                      (find (lambda (rx) (string-suffix? (car rx) str))
                            indent-suffix-rules))))
           => cdr)
          (else #f))))
    (if (and (number? indent) (negative? indent))
        (max 0 (- (+ (length+ form) indent) 1))
        indent)))

(define (pp-with-indent indent-rule ls)
  (lambda (st)
    (let* ((col1 (fmt-col st))
           (st ((cat "(" (pp-object (car ls))) st))
           (col2 (fmt-col st))
           (fixed (take* (cdr ls) (or indent-rule 1)))
           (tail (drop* (cdr ls) (or indent-rule 1)))
           (st2 (fmt-copy-shares st))
           (first-line
            ((fmt-to-string (cat " " (fmt-join/shares pp-flat fixed " "))) st2))
           (default
             (let ((sep (make-nl-space (+ col1 1))))
               (cat sep (fmt-join/shares pp-object (cdr ls) sep) ")"))))
      (cond
       ((< (+ col2 (string-length first-line)) (fmt-width st2))
        ;; fixed values on first line
        (let ((sep (make-nl-space
                    (if indent-rule (+ col1 2) (+ col2 1)))))
          ((cat first-line
                (cond
                 ((not (or (null? tail) (pair? tail)))
                  (cat ". " (pp-object tail)))
                 ((> (length+ (cdr ls)) (or indent-rule 1))
                  (cat sep (fmt-join/shares pp-object tail sep)))
                 (else
                  fmt-null))
                ")")
           st2)))
       (indent-rule ;;(and indent-rule (not (pair? (car ls))))
        ;; fixed values lined up, body indented two spaces
        ((fmt-try-fit
          (lambda (st)
            ((cat
              " "
              (fmt-join/shares pp-object fixed (make-nl-space (+ col2 1)))
              (if (pair? tail)
                  (let ((sep (make-nl-space (+ col1 2))))
                    (cat sep (fmt-join/shares pp-object tail sep)))
                  "")
              ")")
             (fmt-copy-shares st)))
          default)
         st))
       (else
        ;; all on separate lines
        (default st))))))

(define (pp-app ls)
  (let ((indent-rule (pp-indentation ls)))
    (if (procedure? indent-rule)
        (indent-rule ls)
        (pp-with-indent indent-rule ls))))

;; the elements may be shared, just checking the top level list
;; structure
(define (proper-non-shared-list? ls shares)
  (let ((tab (car shares)))
    (let lp ((ls ls))
      (or (null? ls)
          (and (pair? ls)
               (not (eq?-table-ref tab ls))
               (lp (cdr ls)))))))

(define (pp-flat x)
  (cond
    ((pair? x)
     (fmt-shared-write
      x
      (cond
        ((and (pair? (cdr x)) (null? (cddr x))
              (assq (car x) syntax-abbrevs))
         => (lambda (abbrev)
              (cat (cdr abbrev) (pp-flat (cadr x)))))
        (else
         (cat "(" (fmt-join/shares pp-flat x " ") ")")))))
    ((vector? x)
     (fmt-shared-write
      x
      (cat "#(" (fmt-join/shares pp-flat (vector->list x) " ") ")")))
    (else
     (lambda (st) ((write-with-shares x (fmt-shares st)) st)))))

(define (pp-pair ls)
  (fmt-shared-write
   ls
   (cond
    ;; one element list, no lines to break
    ((null? (cdr ls))
     (cat "(" (pp-object (car ls)) ")"))
    ;; quote or other abbrev
    ((and (pair? (cdr ls)) (null? (cddr ls))
          (assq (car ls) syntax-abbrevs))
     => (lambda (abbrev)
          (cat (cdr abbrev) (pp-object (cadr ls)))))
    (else
     (fmt-try-fit
      (lambda (st) ((pp-flat ls) (fmt-copy-shares st)))
      (lambda (st)
        (if (and (non-app? ls)
                 (proper-non-shared-list? ls (fmt-shares st)))
            ((pp-data-list ls) st)
            ((pp-app ls) st))))))))

(define (pp-data-list ls)
  (lambda (st)
    (let* ((output (fmt-writer st))
           (st (output "(" st))
           (col (fmt-col st))
           (width (- (fmt-width st) col))
           (st2 (fmt-copy-shares st)))
      (cond
        ((and (pair? (cdr ls)) (pair? (cddr ls)) (pair? (cdddr ls))
              ((fits-in-columns ls pp-flat width) st2))
         => (lambda (ls)
              ;; at least four elements which can be broken into columns
              (let* ((prefix (make-nl-space (+ col 1)))
                     (widest (+ 1 (car ls)))
                     (columns (quotient width widest))) ; always >= 2
                (let lp ((ls (cdr ls)) (st st2) (i 1))
                  (cond
                    ((null? ls)
                     (output ")" st))
                    ((null? (cdr ls))
                     (output ")" (output (car ls) st)))
                    (else
                     (let ((st (output (car ls) st)))
                       (if (>= i columns)
                           (lp (cdr ls) (output prefix st) 1)
                           (let* ((pad (- widest (string-length (car ls))))
                                  (st (output (make-space pad) st)))
                             (lp (cdr ls) st (+ i 1)))))))))))
        (else
         ;; no room, print one per line
         ((cat (fmt-join pp-object ls (make-nl-space col)) ")") st))))))

(define (pp-vector vec)
  (fmt-shared-write vec (cat "#" (pp-data-list (vector->list vec)))))

(define (pp-object obj)
  (cond
    ((pair? obj) (pp-pair obj))
    ((vector? obj) (pp-vector obj))
    (else (lambda (st) ((write-with-shares obj (fmt-shares st)) st)))))

(define (pretty obj)
  (fmt-bind 'shares (cons (make-shared-ref-table obj) 0)
            (cat (pp-object obj) fl)))

(define (pretty/unshared obj)
  (fmt-bind 'shares (cons (make-eq?-table) 0) (cat (pp-object obj) fl)))

Added fmt/fmt-unicode.scm.















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
;;;; fmt-unicode.scm -- Unicode character width and ANSI escape support
;;
;; Copyright (c) 2006-2007 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

;; a condensed non-spacing mark range from UnicodeData.txt (chars with
;; the Mn property) - generated partially by hand, should automate
;; this better

(define low-non-spacing-chars
  (u8vector
#xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff #xff    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
#x78    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0 #xfe #xff #xff #xff #xff #xff #x1f    0    0    0    0    0    0    0
   0    0 #x3f    0    0    0    0    0    0 #xf8 #xff #x01    0    0 #x01    0
   0    0    0    0    0    0    0    0    0    0 #xc0 #xff #xff #x3f    0    0
   0    0 #x02    0    0    0 #xff #xff #xff #x07    0    0    0    0    0    0
   0    0    0    0 #xc0 #xff #x01    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
#x06    0    0    0    0    0    0 #x10 #xfe #x21 #x1e    0 #x0c    0    0    0
#x02    0    0    0    0    0    0 #x10 #x1e #x20    0    0 #x0c    0    0    0
#x06    0    0    0    0    0    0 #x10 #xfe #x3f    0    0    0    0 #x03    0
#x06    0    0    0    0    0    0 #x30 #xfe #x21    0    0 #x0c    0    0    0
#x02    0    0    0    0    0    0 #x90 #x0e #x20 #x40    0    0    0    0    0
#x04    0    0    0    0    0    0    0    0 #x20    0    0    0    0    0    0
   0    0    0    0    0    0    0 #xc0 #xc1 #xff #x7f    0    0    0    0    0
   0    0    0    0    0    0    0 #x10 #x40 #x30    0    0    0    0    0    0
   0    0    0    0    0    0    0    0 #x0e #x20    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0 #x04 #x7c    0    0    0    0    0
   0    0    0    0    0    0 #xf2 #x07 #x80 #x7f    0    0    0    0    0    0
   0    0    0    0    0    0 #xf2 #x1f    0 #x3f    0    0    0    0    0    0
   0    0    0 #x03    0    0 #xa0 #x02    0    0    0    0    0    0 #xfe #x7f
#xdf    0 #xff #xff #xff #xff #xff #x1f #x40    0    0    0    0    0    0    0
   0    0    0    0    0 #xe0 #xfd #x02    0    0    0 #x03    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0 #x1c    0    0    0 #x1c    0    0    0 #x0c    0    0    0 #x0c    0
   0    0    0    0    0    0 #x80 #x3f #x40 #xfe #x0f #x20    0    0    0    0
   0 #x38    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0 #x02    0    0    0    0    0    0    0    0    0    0
   0    0    0    0 #x87 #x01 #x04 #x0e    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
   0    0    0    0    0    0    0    0    0    0 #xff #x1f #xe2 #x07
       ))

(define (unicode-char-width c)
  (let ((ci (char->integer c)))
    (cond
      ;; hand-checked ranges from EastAsianWidth.txt
      ((<= #x1100 ci #x115F) 2) ; Hangul
      ((<= #x2E80 ci #x4DB5) 2) ; CJK
      ((<= #x4E00 ci #xA4C6) 2)
      ((<= #xAC00 ci #xD7A3) 2) ; Hangul
      ((<= #xF900 ci #xFAD9) 2) ; CJK compat
      ((<= #xFE10 ci #xFE6B) 2)
      ((<= #xFF01 ci #xFF60) 2)
      ((<= #xFFE0 ci #xFFE6) 2)
      ((<= #x20000 ci #x30000) 2)
      ;; non-spacing mark (Mn) ranges from UnicodeData.txt
      ((<= #x0300 ci #x3029)
       ;; inlined bit-vector-ref for portability
       (let* ((i (- ci #x0300))
              (byte (quotient i 8))
              (off (remainder i 8)))
         (if (zero? (bitwise-and (u8vector-ref low-non-spacing-chars byte)
                                 (arithmetic-shift 1 off)))
             1
             0)))
      ((<= #x302A ci #x302F) 0)
      ((<= #x3099 ci #x309A) 0)
      ((= #xFB1E ci) 0)
      ((<= #xFE00 ci #xFE23) 0)
      ((<= #x1D167 ci #x1D169) 0)
      ((<= #x1D17B ci #x1D182) 0)
      ((<= #x1D185 ci #x1D18B) 0)
      ((<= #x1D1AA ci #x1D1AD) 0)
      ((<= #xE0100 ci #xE01EF) 0)
      (else 1))))

(define (unicode-string-width str . o)
  (let ((start (if (pair? o) (car o) 0))
        (end (if (and (pair? o) (pair? (cdr o)))
                 (cadr o)
                 (string-length str))))
    (let lp1 ((i start) (width 0))
      (if (>= i end)
          width
          (let ((c (string-ref str i)))
            (cond
              ;; ANSI escapes
              ((and (= 27 (char->integer c)) ; esc
                    (< (+ i 1) end)
                    (eqv? #\[ (string-ref str (+ i 1))))
               (let lp2 ((i (+ i 2)))
                 (cond ((>= i end) width)
                       ((memv (string-ref str i) '(#\m #\newline))
                        (lp1 (+ i 1) width))
                       (else (lp2 (+ i 1))))))
              ;; unicode characters
              ((>= (char->integer c) #x80)
               (lp1 (+ i 1) (+ width (unicode-char-width c))))
              ;; normal ASCII
              (else (lp1 (+ i 1) (+ width 1)))))))))

(define (fmt-unicode . args)
  (fmt-let 'string-width unicode-string-width (apply-cat args)))

Added fmt/fmt.css.











































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
body {
color: black;
background-color: white;
margin-top: 2em;
margin-left: 10%;
width: 400pt;
}

pre {
  background-color: beige;
}

pre.scheme {
  background-color: white;
}

.subject {
}

h1 {
margin-left: -5%;
margin-top: 2em;
font-size: large;
}

h2 {
margin-left: -4%;
margin-top: 1em;
font-size: large;
}

h3,h4,h5,h6 {
margin-left: -3%;
margin-top: .5em;
font-size: small;
}

.navigation {
color: red;
background-color: beige;
text-align: right;
font-style: italic;
}


.scheme {
color: brown;
}

.scheme .keyword {
color: #cc0000;
font-weight: bold;
}

.scheme .variable {
color: navy;
}

.scheme .global {
color: purple;
}

.scheme .constant,.number,.char,.string,.boolean {
color: green;
}

.scheme .comment {
color: teal;
}

Added fmt/fmt.doc.











































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349

\title{Combinator Formatting}

\eval
(begin
  (display "<style>\n")
  (display (with-input-from-file "fmt.css" read-string))
  (display "</style>\n"))

\flushright{\urlh{http://synthcode.com/}{Alex Shinn}}
\flushright{\urlh{http://synthcode.com/scheme/fmt/fmt-0.8.4.tar.gz}{Download Version 0.8.4}}

\eval(display "<br /><br />\n\n")

A library of procedures for formatting Scheme objects to text in
various ways, and for easily concatenating, composing and extending
these formatters efficiently without resorting to capturing and
manipulating intermediate strings.

\eval(display "<br /><br />\n\n")

\section{Table of Contents}

\eval(display "\n\n<!-- TOC -->\n\n")

\eval(display "<br /><br />\n\n")

\section{Installation}

Available for Chicken as the \p{fmt} egg, providing the \q{fmt},
\q{fmt-c}, \q{fmt-color} and \q{fmt-unicode} extensions.  To install
manually for Chicken just run \p{"chicken-setup"} in the fmt
directory.

For Gauche run \p{"make gauche && make install-gauche"}.  The modules
are installed as \q{text.fmt}, \q{text.fmt.c}, \q{text.fmt.color} and
\q{text.fmt.unicode}.

For MzScheme you can download and install the latest \p{fmt.plt} yourself
from:

  \urlh{http://synthcode.com/scheme/fmt/fmt.plt}{http://synthcode.com/scheme/fmt/fmt.plt}

To build the \p{fmt.plt} for yourself you can run \p{"make mzscheme"}.

For Scheme48 the package descriptions are in \p{fmt-scheme48.scm}:

\q{
> ,config ,load fmt-scheme48.scm
> ,open fmt
}

For other implementations you'll need to load SRFI's 1, 6, 13, 33
(sample provided) and 69 (also provided), and then load the following
files:

\q{
  (load "let-optionals.scm")  ; if you don't have LET-OPTIONALS*
  (load "read-line.scm")      ; if you don't have READ-LINE
  (load "string-ports.scm")   ; if you don't have CALL-WITH-OUTPUT-STRING
  (load "make-eq-table.scm")
  (load "mantissa.scm")
  (load "fmt.scm")
  (load "fmt-pretty.scm")     ; optional pretty printing
  (load "fmt-column.scm")     ; optional columnar output
  (load "fmt-c.scm")          ; optional C formatting utilities
  (load "fmt-color.scm")      ; optional color utilities
  (load "fmt-unicode.scm")    ; optional Unicode-aware formatting,
                              ;   also requires SRFI-4 or SRFI-66
}

\section{Background}

There are several approaches to text formatting.  Building strings to
\q{display} is not acceptable, since it doesn't scale to very large
output.  The simplest realistic idea, and what people resort to in
typical portable Scheme, is to interleave \q{display} and \q{write}
and manual loops, but this is both extremely verbose and doesn't
compose well.  A simple concept such as padding space can't be
achieved directly without somehow capturing intermediate output.

The traditional approach is to use templates - typically strings,
though in theory any object could be used and indeed Emacs' mode-line
format templates allow arbitrary sexps.  Templates can use either
escape sequences (as in C's \q{printf} and \urlh{#BIBITEM_2}{CL's}
\q{format}) or pattern matching (as in Visual Basic's \q{Format},
\urlh{#BIBITEM_6}{Perl6's} \q{form}, and SQL date formats).  The
primary disadvantage of templates is the relative difficulty (usually
impossibility) of extending them, their opaqueness, and the
unreadability that arises with complex formats.  Templates are not
without their advantages, but they are already addressed by other
libraries such as \urlh{#BIBITEM_3}{SRFI-28} and
\urlh{#BIBITEM_4}{SRFI-48}.

This library takes a combinator approach.  Formats are nested chains
of closures, which are called to produce their output as needed.
The primary goal of this library is to have, first and foremost, a
maximally expressive and extensible formatting library.  The next
most important goal is scalability - to be able to handle
arbitrarily large output and not build intermediate results except
where necessary.  The third goal is brevity and ease of use.

\section{Usage}

The primary interface is the \q{fmt} procedure:

  \q{(fmt <output-dest> <format> ...)}

where \q{<output-dest>} has the same semantics as with \q{format} -
specifically it can be an output-port, \q{#t} to indicate the current
output port, or \q{#f} to accumulate output into a string.

Each \q{<format>} should be a format closure as discussed below.  As a
convenience, non-procedure arguments are also allowed and are
formatted similar to \q{display}, so that

  \q{(fmt #f "Result: " res nl)}

would return the string \q{"Result: 42\n"}, assuming \q{RES} is bound
to \q{42}.

\q{nl} is the newline format combinator.

\section{Specification}

The procedure names have gone through several variations, and I'm
still open to new suggestions.  The current approach is to use
abbreviated forms of standard output procedures when defining an
equivalent format combinator (thus \q{display} becomes \q{dsp} and
\q{write} becomes \q{wrt}), and to use an \q{fmt-} prefix for
utilities and less common combinators.  Variants of the same formatter
get a \q{/<variant>} suffix.

\subsection{Formatting Objects}

\subsubsection*{(dsp <obj>)}

Outputs \q{<obj>} using \q{display} semantics.  Specifically, strings
are output without surrounding quotes or escaping and characters are
written as if by \q{write-char}.  Other objects are written as with
\q{write} (including nested strings and chars inside \q{<obj>}).  This
is the default behavior for top-level formats in \q{fmt}, \q{cat} and
most other higher-order combinators.

\subsubsection*{(wrt <obj>)}

Outputs \q{<obj>} using \q{write} semantics.  Handles shared
structures as in \urlh{#BIBITEM_5}{SRFI-38}.

\subsubsection*{(wrt/unshared <obj>)}

As above, but doesn't handle shared structures.  Infinite loops can
still be avoided if used inside a combinator that truncates data (see
\q{trim} and \q{fit} below).

\subsubsection*{(pretty <obj>)}

Pretty-prints \q{<obj>}.  Also handles shared structures.  Unlike many
other pretty printers, vectors and data lists (lists that don't begin
with a (nested) symbol), are printed in tabular format when there's
room, greatly saving vertical space.

\subsubsection*{(pretty/unshared <obj>)}

As above but without sharing.

\subsubsection*{(slashified <str> [<quote-ch> <esc-ch> <renamer>])}

Outputs the string \q{<str>}, escaping any quote or escape characters.
If \q{<esc-ch>} is \q{#f} escapes only the \q{<quote-ch>} by
doubling it, as in SQL strings and CSV values.  If \q{<renamer>} is
provided, it should be a procedure of one character which maps that
character to its escape value, e.g. \q{#\newline => #\n}, or \q{#f} if
there is no escape value.

  \q{(fmt #f (slashified "hi, \"bob!\""))}

  \q{=> "hi, \"bob!\""}

\subsubsection*{(maybe-slashified <str> <pred> [<quote-ch> <esc-ch> <renamer>])}

Like \q{slashified}, but first checks if any quoting is required (by
the existence of either any quote or escape characters, or any
character matching \q{<pred>}), and if so outputs the string in quotes
and with escapes.  Otherwise outputs the string as is.

  \q{(fmt #f (maybe-slashified "foo" char-whitespace? #\"))}

  \q{=> "foo"}

  \q{(fmt #f (maybe-slashified "foo bar" char-whitespace? #\"))}

  \q{=> "\"foo bar\""}

  \q{(fmt #f (maybe-slashified "foo\"bar\"baz" char-whitespace? #\"))}

  \q{=> "\"foo\"bar\"baz\""}

\subsection{Formatting Numbers}

\subsubsection*{(num <n> [<radix> <precision> <sign> <comma> <comma-sep> <decimal-sep>])}

Formats a single number \q{<n>}.  You can optionally specify any
\q{<radix>} from 2 to 36 (even if \q{<n>} isn't an integer).
\q{<precision>} forces a fixed-point format.

A \q{<sign>} of \q{#t} indicates to output a plus sign (+) for positive
integers.  However, if \q{<sign>} is a character, it means to wrap the
number with that character and its mirror opposite if the number is
negative.  For example, \q{#\(} prints negative numbers in parenthesis,
financial style: \q{-3.14 => (3.14)}

\q{<comma>} is an integer specifying the number of digits between
commas.  Variable length, as in subcontinental-style, is not yet
supported.

\q{<comma-sep>} is the character to use for commas, defaulting to \q{#\,}.

\q{<decimal-sep>} is the character to use for decimals, defaulting to
\q{#\.}, or to \q{#\,} (European style) if \q{<comma-sep>} is already
\q{#\.}.

These parameters may seem unwieldy, but they can also take their
defaults from state variables, described below.

\subsubsection*{(num/comma <n> [<base> <precision> <sign>])}

Shortcut for \q{num} to print with commas.

  \q{(fmt #f (num/comma 1234567))}

  \q{=> "1,234,567"}

\subsubsection*{(num/si <n> [<base> <suffix>])}

Abbreviates \q{<n>} with an SI suffix as in the -h or --si option to
many GNU commands.  The base defaults to 1024, using suffix names
like Ki, Mi, Gi, etc.  Other bases (e.g. the standard 1000) have the
suffixes k, M, G, etc.

The \q{<suffix>} argument is appended only if an abbreviation is used.

  \q{(fmt #f (num/si 608))}

  \q{=> "608"}

  \q{(fmt #f (num/si 3986))}

  \q{=> "3.9Ki"}

  \q{(fmt #f (num/si 3986 1000 "B"))}

  \q{=> "4kB"}

See \urlh{http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html}{http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html}.

\subsubsection*{(num/fit <width> <n> . <ARGS>)}

Like \q{num}, but if the result doesn't fit in \q{<width>}, output
instead a string of hashes (with the current \q{<precision>}) rather
than showing an incorrectly truncated number.  For example

  \q{(fmt #f (fix 2 (num/fit 4 12.345)))}
  \q{=> "#.##"}

\subsubsection*{(num/roman <n>)}

Formats the number as a Roman numeral:

  \q{(fmt #f (num/roman 1989))}
  \q{=> "MCMLXXXIX"}

\subsubsection*{(num/old-roman <n>)}

Formats the number as an old-style Roman numeral, without the
subtraction abbreviation rule:

  \q{(fmt #f (num/old-roman 1989))}
  \q{=> "MDCCCCLXXXVIIII"}


\subsection{Formatting Space}

\subsubsection*{nl}

Outputs a newline.

\subsubsection*{fl}

Short for "fresh line," outputs a newline only if we're not already
at the start of a line.

\subsubsection*{(space-to <column>)}

Outputs spaces up to the given \q{<column>}.  If the current column is
already >= \q{<column>}, does nothing.

\subsubsection*{(tab-to [<tab-width>])}

Outputs spaces up to the next tab stop, using tab stops of width
\q{<tab-width>}, which defaults to 8.  If already on a tab stop, does
nothing.  If you want to ensure you always tab at least one space, you
can use \q{(cat " " (tab-to width))}.

\subsubsection*{fmt-null}

Outputs nothing (useful in combinators and as a default noop in
conditionals).


\subsection{Concatenation}

\subsubsection*{(cat <format> ...)}

Concatenates the output of each \q{<format>}.

\subsubsection*{(apply-cat <list>)}

Equivalent to \q{(apply cat <list>)} but may be more efficient.

\subsubsection*{(fmt-join <formatter> <list> [<sep>])}

Formats each element \q{<elt>} of \q{<list>} with \q{(<formatter>
<elt>)}, inserting \q{<sep>} in between.  \q{<sep>} defaults to the
empty string, but can be any format.

  \q{(fmt #f (fmt-join dsp '(a b c) ", "))}

  \q{=> "a, b, c"}

\subsubsection*{(fmt-join/prefix <formatter> <list> [<sep>])}
\subsubsection*{(fmt-join/suffix <formatter> <list> [<sep>])}

  \q{(fmt #f (fmt-join/prefix dsp '(usr local bin) "/"))}

  \q{=> "/usr/local/bin"}

As \q{fmt-join}, but inserts \q{<sep>} before/after every element.

\subsubsection*{(fmt-join/last <formatter> <last-formatter> <list> [<sep>])}

As \q{fmt-join}, but the last element of the list is formatted with
\q{<last-formatter>} instead.

\subsubsection*{(fmt-join/dot <formatter> <dot-formatter> <list> [<sep>])}

As \q{fmt-join}, but if the list is a dotted list, then formats the dotted
value with \q{<dot-formatter>} instead.


\subsection{Padding and Trimming}

\subsubsection*{(pad <width> <format> ...)}
\subsubsection*{(pad/left <width> <format> ...)}
\subsubsection*{(pad/both <width> <format> ...)}

Analogs of SRFI-13 \q{string-pad}, these add extra space to the left,
right or both sides of the output generated by the \q{<format>}s to
pad it to \q{<width>}.  If \q{<width>} is exceeded has no effect.
\q{pad/both} will include an extra space on the right side of the
output if the difference is odd.

\q{pad} does not accumulate any intermediate data.

Note these are column-oriented padders, so won't necessarily work
with multi-line output (padding doesn't seem a likely operation for
multi-line output).

\subsubsection*{(trim <width> <format> ...)}
\subsubsection*{(trim/left <width> <format> ...)}
\subsubsection*{(trim/both <width> <format> ...)}

Analogs of SRFI-13 \q{string-trim}, truncates the output of the
\q{<format>}s to force it in under \q{<width>} columns.  As soon as
any of the \q{<format>}s exceed \q{<width>}, stop formatting and
truncate the result, returning control to whoever called \q{trim}.  If
\q{<width>} is not exceeded has no effect.

If a truncation ellipse is set (e.g. with the \q{ellipses} procedure
below), then when any truncation occurs \q{trim} and \q{trim/left}
will append and prepend the ellipse, respectively.  \q{trim/both} will
both prepend and append.  The length of the ellipse will be considered
when truncating the original string, so that the total width will
never be longer than \q{<width>}.

 \q{(fmt #f (ellipses "..." (trim 5 "abcde")))}

 \q{=>  "abcde"}

 \q{(fmt #f (ellipses "..." (trim 5 "abcdef")))}

 \q{=>  "ab..."}

\subsubsection*{(trim/length <width> <format> ...)}

A variant of \q{trim} which acts on the actual character count rather
than columns, useful for truncating potentially cyclic data.

\subsubsection*{(fit <width> <format> ...)}
\subsubsection*{(fit/left <width> <format> ...)}
\subsubsection*{(fit/both <width> <format> ...)}

A combination of \q{pad} and \q{trunc}, ensures the output width is
exactly \q{<width>}, truncating if it goes over and padding if it goes
under.


\subsection{Format Variables}

You may have noticed many of the formatters are aware of the current
column.  This is because each combinator is actually a procedure of
one argument, the current format state, which holds basic
information such as the row, column, and any other information that
a format combinator may want to keep track of.  The basic interface
is:

\subsubsection*{(fmt-let <name> <value> <format> ...)}
\subsubsection*{(fmt-bind <name> <value> <format> ...)}

\q{fmt-let} sets the name for the duration of the \q{<format>}s, and
restores it on return.  \q{fmt-bind} sets it without restoring it.

A convenience control structure can be useful in combination with
these states:

\subsubsection*{(fmt-if <pred> <pass> [<fail>])}

\q{<pred>} takes one argument (the format state) and returns a boolean
result.  If true, the \q{<pass>} format is applied to the state,
otherwise \q{<fail>} (defaulting to the identity) is applied.

Many of the previously mentioned combinators have behavior which can
be altered with state variables.  Although \q{fmt-let} and \q{fmt-bind}
could be used, these common variables have shortcuts:

\subsubsection*{(radix <k> <format> ...)}
\subsubsection*{(fix <k> <format> ...)}

These alter the radix and fixed point precision of numbers output with
\q{dsp}, \q{wrt}, \q{pretty} or \q{num}.  These settings apply
recursively to all output data structures, so that

  \q{(fmt #f (radix 16 '(70 80 90)))}

will return the string \q{"(#x46 #x50 #x5a)"}.  Note that read/write
invariance is essential, so for \q{dsp}, \q{wrt} and \q{pretty} the
radix prefix is always included when not decimal.  Use \q{num} if you
want to format numbers in alternate bases without this prefix.  For
example,

  \q{(fmt #f (radix 16 "(" (fmt-join num '(70 80 90) " ") ")"))}

would return \q{"(46 50 5a)"}, the same output as above without the
"#x" radix prefix.

Note that fixed point formatting supports arbitrary precision in
implementations with exact non-integral rationals.  When trying to
print inexact numbers more than the machine precision you will
typically get results like

  \q{(fmt #f (fix 30 #i2/3))}

  \q{=> "0.666666666666666600000000000000"}

but with an exact rational it will give you as many digits as you
request:

  \q{(fmt #f (fix 30 2/3))}

  \q{=> "0.666666666666666666666666666667"}

\subsubsection*{(decimal-align <k> <format> ...)}

Specifies an alignment for the decimal place when formatting numbers,
useful for outputting tables of numbers.

\q{
  (define (print-angles x)
     (fmt-join num (list x (sin x) (cos x) (tan x)) " "))

  (fmt #t (decimal-align 5 (fix 3 (fmt-join/suffix print-angles (iota 5) nl))))
}

would output

\p{
   0.000    0.000    1.000    0.000
   1.000    0.842    0.540    1.557
   2.000    0.909   -0.416   -2.185
   3.000    0.141   -0.990   -0.142
   4.000   -0.757   -0.654    1.158
}

\subsubsection*{(comma-char <k> <format> ...)}
\subsubsection*{(decimal-char <k> <format> ...)}

\q{comma-char} and \q{decimal-char} set the defaults for number
formatting.

\subsubsection*{(pad-char <k> <format> ...)}

The \q{pad-char} sets the character used by \q{space-to}, \q{tab-to},
\q{pad/*}, and \q{fit/*}, and defaults to \q{#\space}.

\q{
  (define (print-table-of-contents alist)
    (define (print-line x)
      (cat (car x) (space-to 72) (pad/left 3 (cdr x))))
    (fmt #t (pad-char #\. (fmt-join/suffix print-line alist nl))))

  (print-table-of-contents
   '(("An Unexpected Party" . 29)
     ("Roast Mutton" . 60)
     ("A Short Rest" . 87)
     ("Over Hill and Under Hill" . 100)
     ("Riddles in the Dark" . 115)))
}

would output

\p{
  An Unexpected Party.....................................................29
  Roast Mutton............................................................60
  A Short Rest............................................................87
  Over Hill and Under Hill...............................................100
  Riddles in the Dark....................................................115
}

\subsubsection*{(ellipse <ell> <format> ...)}

Sets the truncation ellipse to \q{<ell>}, would should be a string or
character.

\subsubsection*{(with-width <width> <format> ...)}

Sets the maximum column width used by some formatters.  The default
is 78.


\subsection{Columnar Formatting}

Although \q{tab-to}, \q{space-to} and padding can be used to manually
align columns to produce table-like output, these can be awkward to
use.  The optional extensions in this section make this easier.

\subsubsection*{(columnar <column> ...)}

Formats each \q{<column>} side-by-side, i.e. as though each were
formatted separately and then the individual lines concatenated
together.  The current column width is divided evenly among the
columns, and all but the last column are right-padded.  For example

  \q{(fmt #t (columnar (dsp "abc\\ndef\\n") (dsp "123\\n456\\n")))}

outputs

\p{
     abc     123
     def     456
}

assuming a 16-char width (the left side gets half the width, or 8
spaces, and is left aligned).  Note that we explicitly use DSP instead
of the strings directly.  This is because \q{columnar} treats raw
strings as literals inserted into the given location on every line, to
be used as borders, for example:

\q{
  (fmt #t (columnar "/* " (dsp "abc\\ndef\\n")
                    " | " (dsp "123\\n456\\n")
                    " */"))
}

would output

\p{
  /* abc | 123 */
  /* def | 456 */
}

You may also prefix any column with any of the symbols \q{'left},
\q{'right} or \q{'center} to control the justification.  The symbol
\q{'infinite} can be used to indicate the column generates an infinite
stream of output.

You can further prefix any column with a width modifier.  Any
positive integer is treated as a fixed width, ignoring the available
width.  Any real number between 0 and 1 indicates a fraction of the
available width (after subtracting out any fixed widths).  Columns
with unspecified width divide up the remaining width evenly.

Note that \q{columnar} builds its output incrementally, interleaving
calls to the generators until each has produced a line, then
concatenating that line together and outputting it.  This is important
because as noted above, some columns may produce an infinite stream of
output, and in general you may want to format data larger than can fit
into memory.  Thus columnar would be suitable for line numbering a
file of arbitrary size, or implementing the Unix \q{yes(1)} command,
etc.

As an implementation detail, \q{columnar} uses first-class
continuations to interleave the column output.  The core \q{fmt}
itself has no knowledge of or special support for \q{columnar}, which
could complicate and potentially slow down simpler \q{fmt} operations.
This is a testament to the power of \q{call/cc} - it can be used to
implement coroutines or arbitrary control structures even where they
were not planned for.

\subsubsection*{(tabular <column> ...)}

Equivalent to \q{columnar} except that each column is padded at least
to the minimum width required on any of its lines.  Thus

  \q{(fmt #t (tabular "|" (dsp "a\\nbc\\ndef\\n") "|" (dsp "123\\n45\\n6\\n") "|"))}

outputs

\p{
|a  |123|
|bc |45 |
|def|6  |
}

This makes it easier to generate tables without knowing widths in
advance.  However, because it requires generating the entire output in
advance to determine the correct column widths, \q{tabular} cannot
format a table larger than would fit in memory.

\subsubsection*{(fmt-columns <column> ...)}

The low-level formatter on which \q{columnar} is based.  Each \q{<column>}
must be a list of 2-3 elements:

  \q{(<line-formatter> <line-generator> [<infinite?>])}

where \q{<line-generator>} is the column generator as above, and the
\q{<line-formatter>} is how each line is formatted.  Raw concatenation
of each line is performed, without any spacing or width adjustment.
\q{<infinite?>}, if true, indicates this generator produces an
infinite number of lines and termination should be determined without
it.

\subsubsection*{(wrap-lines <format> ...)}

Behaves like \q{cat}, except text is accumulated and lines are optimally
wrapped to fit in the current width as in the Unix \p{fmt(1)} command.

\subsubsection*{(justify <format> ...)}

Like \q{wrap-lines} except the lines are full-justified.

\q{
  (define func
    '(define (fold kons knil ls)
       (let lp ((ls ls) (acc knil))
         (if (null? ls) acc (lp (cdr ls) (kons (car ls) acc))))))

  (define doc
    (string-append
      "The fundamental list iterator.  Applies KONS to each element "
      "of LS and the result of the previous application, beginning "
      "with KNIL.  With KONS as CONS and KNIL as '(), equivalent to REVERSE."))

  (fmt #t (columnar (pretty func) " ; " (justify doc)))
}

outputs

\p{
  (define (fold kons knil ls)          ; The   fundamental   list   iterator.
    (let lp ((ls ls) (acc knil))       ; Applies  KONS  to  each  element  of
      (if (null? ls)                   ; LS  and  the  result of the previous
          acc                          ; application,  beginning  with  KNIL.
          (lp (cdr ls)                 ; With  KONS  as CONS and KNIL as '(),
              (kons (car ls) acc)))))  ; equivalent to REVERSE.
}

\subsubsection*{(fmt-file <pathname>)}

Simply displayes the contents of the file \q{<pathname>} a line at a
time, so that in typical formatters such as \q{columnar} only constant
memory is consumed, making this suitable for formatting files of
arbitrary size.

\subsubsection*{(line-numbers [<start>])}

A convenience utility, just formats an infinite stream of numbers (in
the current radix) beginning with \q{<start>}, which defaults to \q{1}.

The Unix \q{nl(1)} utility could be implemented as:

\q{
  (fmt #t (columnar 6 'right 'infinite (line-numbers)
                    " " (fmt-file "read-line.scm")))
}

\p{
     1 
     2 (define (read-line . o)
     3   (let ((port (if (pair? o) (car o) (current-input-port))))
     4     (let lp ((res '()))
     5       (let ((c (read-char port)))
     6         (if (or (eof-object? c) (eqv? c #\newline))
     7             (list->string (reverse res))
     8             (lp (cons c res)))))))
}

\section{C Formatting}

\subsection{C Formatting Basics}

For purposes such as writing wrappers, code-generators, compilers or
other language tools, people often need to generate or emit C code.
Without a decent library framework it's difficult to maintain proper
indentation.  In addition, for the Scheme programmer it's tedious to
work with all the context sensitivities of C, such as the expression
vs. statement distinction, special rules for writing preprocessor
macros, and when precedence rules require parenthesis.  Fortunately,
context is one thing this formatting library is good at keeping
track of.  The C formatting interface tries to make it as easy as
possible to generate C code without getting in your way.

There are two approaches to using the C formatting extensions -
procedural and sexp-oriented (described in \ref{csexprs}).  In the
procedural interface, C operators are made available as formatters
with a "c-" prefix, literals are converted to their C equivalents and
symbols are output as-is (you're responsible for making sure they are
valid C identifiers).  Indentation is handled automatically.

  \q{(fmt #t (c-if 1 2 3))}

outputs

\p{
  if (1) {
      2;
  } else {
      3;
  }
}

In addition, the formatter knows when you're in an expression and
when you're in a statement, and behaves accordingly, so that

  \q{(fmt #t (c-if (c-if 1 2 3) 4 5))}

outputs

\p{
  if (1 ? 2 : 3) {
      4;
  } else {
      5;
  }
}

Similary, \q{c-begin}, used for sequencing, will separate with
semi-colons in a statement and commas in an expression.

Moreover, we also keep track of the final expression in a function
and insert returns for you:

  \q{(fmt #t (c-fun 'int 'foo '() (c-if (c-if 1 2 3) 4 5)))}

outputs

\p{
  int foo () {
      if (1 ? 2 : 3) {
          return 4;
      } else {
          return 5;
      }
  }
}

although it knows that void functions don't return.

Switch statements insert breaks by default if they don't return:

\q{
  (fmt #t (c-switch 'y
            (c-case 1 (c+= 'x 1))
            (c-default (c+= 'x 2))))
}

\p{
  switch (y) {
      case 1:
          x += 1;
          break;
      default:
          x += 2;
          break;
  }
}

though you can explicitly fallthrough if you want:

\q{
  (fmt #t (c-switch 'y
            (c-case/fallthrough 1 (c+= 'x 1))
            (c-default (c+= 'x 2))))
}

\p{
  switch (y) {
      case 1:
          x += 1;
      default:
          x += 2;
          break;
  }
}

Operators are available with just a "c" prefix, e.g. c+, c-, c*, c/,
etc.  \q{c++} is a prefix operator, \q{c++/post} is postfix.  ||, | and
|= are written as \q{c-or}, \q{c-bit-or} and \q{c-bit-or=} respectively.

Function applications are written with \q{c-apply}.  Other control
structures such as \q{c-for} and \q{c-while} work as expected.  The full
list is in the procedure index below.

When a C formatter encounters an object it doesn't know how to write
(including lists and records), it outputs them according to the
format state's current \q{'gen} variable.  This allows you to specify
generators for your own types, e.g. if you are using your own AST
records in a compiler.

If the \q{'gen} variable isn't set it defaults to the \q{c-expr/sexp}
procedure, which formats an s-expression as if it were C code.  Thus
instead of \q{c-apply} you can just use a list.  The full API is
available via normal s-expressions - formatters that aren't keywords
in C are prefixed with a % or otherwise made invalid C identifiers so
that they can't be confused with function application.


\subsection{C Preprocessor Formatting}

C preprocessor formatters also properly handle their surrounding
context, so you can safely intermix them in the normal flow of C
code.

\q{
  (fmt #t (c-switch 'y
            (c-case 1 (c= 'x 1))
            (cpp-ifdef 'H_TWO (c-case 2 (c= 'x 4)))
            (c-default (c= 'x 5))))
}

\p{
  switch (y) {
      case 1:
          x = 1;
          break;

  #ifdef H_TWO
      case 2:
          x = 4;
          break;
  #endif /* H_TWO */
      default:
          x = 5;
          break;
  }
}

Macros can be handled with \q{cpp-define}, which knows to wrap
individual variable references in parenthesis:

  \q{(fmt #t (cpp-define '(min x y) (c-if (c< 'x 'y) 'x 'y)))}

\p{
  #define min(x, y) (((x) < (y)) ? (x) : (y))
}

As with all C formatters, the CPP output is pretty printed as
needed, and if it wraps over several lines the lines are terminated
with a backslash.

To write a C header file that is included at most once, you can wrap
the entire body in \q{cpp-wrap-header}:

\q{
  (fmt #t (cpp-wrap-header "FOO_H"
            (c-extern (c-prototype 'int 'foo '()))))
}

\p{
  #ifndef FOO_H
  #define FOO_H

  extern int foo ();

  #endif /* ! FOO_H */
}


\subsection{Customizing C Style}

The output uses a simplified K&R style with 4 spaces for indentation
by default.  The following state variables let you override the
style:

\subsubsection*{'indent-space}

how many spaces to indent bodies, default \q{4}

\subsubsection*{'switch-indent-space}

how many spaces to indent switch clauses, also defaults to \q{4}

\subsubsection*{'newline-before-brace?}

insert a newline before an open brace (non-K&R), defaults  to \q{#f}

\subsubsection*{'braceless-bodies?}

omit braces when we can prove they aren't needed

\subsubsection*{'non-spaced-ops?}

omit spaces between operators and operands for groups of variables and
literals (e.g. "a+b+3" instead of "a + b + 3"}

\subsubsection*{'no-wrap?}

Don't wrap function calls and long operator groups over mulitple
lines.  Functions and control structures will still use multiple
lines.

The C formatters also respect the \q{'radix} and \q{'precision} settings.


\subsection{C Formatter Index}

\subsubsection*{(c-if <condition> <pass> [<fail> [<condition2> <pass2> ...]])}

Print a chain of if/else conditions.  Use a final condition of \q{'else}
for a final else clause.

\subsubsection*{(c-for <init> <condition> <update> <body> ...)}
\subsubsection*{(c-while <condition> <body> ...)}

Basic loop constructs.

\subsubsection*{(c-fun <type> <name> <params> <body> ...)}
\subsubsection*{(c-prototype <type> <name> <params>)}

Output a function or function prototype.  The parameters should be a
list 2-element lists of the form \q{(<param-type> <param-name>)},
which are output with DSP.  A parameter can be abbreviated as just the
symbol name, or \q{#f} can be passed as the type, in which case the
\q{'default-type} state variable is used.  The parameters may be a
dotted list, in which case ellipses for a C variadic are inserted -
the actual name of the dotted value is ignored.

Types are just typically just symbols, or lists of symbols such as
\q{'(const char)}.  A complete description is given below in section
\ref{ctypes}.

These can also accessed as %fun and %prototype at the head of a list.

\subsubsection*{(c-var <type> <name> [<init-value>])}

Declares and optionally initializes a variable.  Also accessed as %var
at the head of a list.

\subsubsection*{(c-begin <expr> ...)}

Outputs each of the <expr>s, separated by semi-colons if in a
statement or commas if in an expression.

\subsubsection*{(c-switch <clause> ...)}
\subsubsection*{(c-case <values> <body> ...)}
\subsubsection*{(c-case/fallthrough <values> <body> ...)}
\subsubsection*{(c-default <body> ...)}

Switch statements.  In addition to using the clause formatters,
clauses inside a switch may be handled with a Scheme CASE-like list,
with the car a list of case values and the cdr the body.

\subsubsection*{(c-label <name>)}
\subsubsection*{(c-goto <name>)}
\subsubsection*{(c-return [<result>])}
\subsubsection*{c-break}
\subsubsection*{c-continue}

Manual labels and jumps.  Labels can also be accessed as a list
beginning with a colon, e.g. \q{'(: label1)}.

\subsubsection*{(c-const <expr>)}
\subsubsection*{(c-static <expr>)}
\subsubsection*{(c-volatile <expr>)}
\subsubsection*{(c-restrict <expr>)}
\subsubsection*{(c-register <expr>)}
\subsubsection*{(c-auto <expr>)}
\subsubsection*{(c-inline <expr>)}
\subsubsection*{(c-extern <expr>)}

Declaration modifiers.  May be nested.

\subsubsection*{(c-extern/C <body> ...)}

Wraps body in an extern "C" { ... } for use with C++.

\subsubsection*{(c-cast <type> <expr>)}

Casts an expression to a type.  Also %cast at the head of a list.

\subsubsection*{(c-typedef <type> <new-name> ...)}

Creates a new type definition with one or more names.

\subsubsection*{(c-struct [<name>] <field-list> [<attributes>])}
\subsubsection*{(c-union [<name>] <field-list> [<attributes>])}
\subsubsection*{(c-class [<name>] <field-list> [<attributes>])}
\subsubsection*{(c-attribute <values> ...)}

Composite type constructors.  Attributes may be accessed as
%attribute at the head of a list.

\q{
  (fmt #f (c-struct 'employee
                      '((short age)
                        ((char *) name)
                        ((struct (year month day)) dob))
                      (c-attribute 'packed)))
}

\p{
  struct employee {
      short age;
      char* name;
      struct {
          int year;
          int month;
          int day;
      } dob;
  } __attribute__ ((packed));
}

\subsubsection*{(c-enum [<name>] <enum-list>)}

Enumerated types.  \q{<enum-list>} may be strings, symbols, or lists of
string or symbol followed by the enum's value.

\subsubsection*{(c-comment <formatter> ...)}

Outputs the \q{<formatter>}s wrapped in C's /* ... */ comment.  Properly
escapes nested comments inside in an Emacs-friendly style.

\subsection{C Preprocessor Formatter Index}

\subsubsection*{(cpp-include <file>)}

If file is a string, outputs in it "quotes", otherwise (as a symbol
or arbitrary formatter) it outputs it in brackets.

  \q{(fmt #f (cpp-include 'stdio.h))}

  \q{=> "#include <stdio.h>\n"}

  \q{(fmt #f (cpp-include "config.h"))}

  \q{=> "#include \"config.h\"\n"}

\subsubsection*{(cpp-define <macro> [<value>])}

Defines a preprocessor macro, which may be just a name or a list of
name and parameters.  Properly wraps the value in parenthesis and
escapes newlines.  A dotted parameter list will use the C99 variadic
macro syntax, and will also substitute any references to the dotted
name with \p{__VA_ARGS__}:

  \q{(fmt #t (cpp-define '(eprintf . args) '(fprintf stderr args)))}

\p{
  #define eprintf(...) (fprintf(stderr, __VA_ARGS__))
}

\subsubsection*{(cpp-if <condition> <pass> [<fail> ...])}
\subsubsection*{(cpp-ifdef <condition> <pass> [<fail> ...])}
\subsubsection*{(cpp-ifndef <condition> <pass> [<fail> ...])}
\subsubsection*{(cpp-elif <condition> <pass> [<fail> ...])}
\subsubsection*{(cpp-else <body> ...)}

Conditional compilation.

\subsubsection*{(cpp-line <num> [<file>])}

Line number information.

\subsubsection*{(cpp-pragma <args> ...)}
\subsubsection*{(cpp-error <args> ...)}
\subsubsection*{(cpp-warning <args> ...)}

Additional preprocessor directives.

\subsubsection*{(cpp-stringify <expr>)}

Stringifies \q{<expr>} by prefixing the # operator.

\subsubsection*{(cpp-sym-cat <args> ...)}

Joins the \q{<args>} into a single preprocessor token with the ##
operator.

\subsubsection*{(cpp-wrap-header <name> <body> ...)}

Wrap an entire header to only be included once.

\subsubsection*{Operators:}

\q{
c++ c-- c+ c- c* c/ c% c& c^ c~ c! c&& c<< c>> c== c!=
c< c> c<= c>= c= c+= c-= c*= c/= c%= c&= c^= c<<= c>>=
c++/post c--/post c-or c-bit-or c-bit-or=
}

\subsection{C Types}
\label{ctypes}

Typically a type is just a symbol such as \q{'char} or \q{'int}.  You
can wrap types with modifiers such as \q{c-const}, but as a
convenience you can just use a list such as in \q{'(const unsignedchar *)}.
You can also nest these lists, so the previous example is
equivalent to \q{'(* (const (unsigned char)))}.

Pointers may be written as \q{'(%pointer <type>)} for readability -
\q{%pointer} is exactly equivalent to \q{*} in types.

Unamed structs, classes, unions and enums may be used directly as
types, using their respective keywords at the head of a list.

Two special types are the %array type and function pointer type.  An
array is written:

  \q{(%array <type> [<size>])}

where \q{<type>} is any other type (including another array or
function pointer), and \q{<size>}, if given, will print the array
size.  For example:

  \q{(c-var '(%array (unsigned long) SIZE) 'table '#(1 2 3 4))}

\p{
unsigned long table[SIZE] = {1, 2, 3, 4};
}

A function pointer is written:

  \q{(%fun <return-type> (<param-types> ...))}

For example:

  \q{(c-typedef '(%fun double (double double int)) 'f)}

\p{
typedef double (*f)(double, double, int);
}

Wherever a type is expected but not given, the value of the
\q{'default-type} formatting state variable is used.  By default this
is just \q{'int}.

Type declarations work uniformly for variables and parameters, as well
for casts and typedefs.

\subsection{C as S-Expressions}
\label{csexprs}

Rather than building formatting closures by hand, it can be more
convenient to just build a normal s-expression and ask for it to be
formatted as C code.  This can be thought of as a simple Scheme->C
compiler without any runtime support.

In a s-expression, strings and characters are printed as C strings and
characters, booleans are printed as 0 or 1, symbols are displayed
as-is, and numbers are printed as C numbers (using the current
formatting radix if specified).  Vectors are printed as
comma-separated lists wrapped in braces, which can be used for
initializing arrays or structs.

A list indicates a C expression or statement.  Any of the existing C
keywords can be used to pretty-print the expression as described with
the c-keyword formatters above.  Thus, the example above

  \q{(fmt #t (c-if (c-if 1 2 3) 4 5))}

could also be written

  \q{(fmt #t (c-expr '(if (if 1 2 3) 4 5)))}

C constructs that are dependent on the underlying syntax and have no
keyword are written with a % prefix (\q{%fun}, \q{%var}, \q{%pointer},
\q{%array}, \q{%cast}), including C preprocessor constructs
(\q{%include}, \q{%define}, \q{%pragma}, \q{%error}, \q{%warning},
\q{%if}, \q{%ifdef}, \q{%ifndef}, \q{%elif}).  Labels are written as
\q{(: <label-name>)}.  You can write a sequence as \q{(%begin <expr>
...)}.

For example, the following definition of the fibonacci sequence, which
apart from the return type of \q{#f} looks like a Lisp definition:

  \q{(fmt #t (c-expr '(%fun #f fib (n)
                         (if (<= n 1)
                             1
                             (+ (fib (- n 1)) (fib (- n 2)))))))}

prints the working C definition:

\p{
int fib (int n) {
    if (n <= 1) {
        return 1;
    } else {
        return fib((n - 1)) + fib((n - 2));
    }
}
}

\section{JavaScript Formatting}

The experimental fmt-js library extends the fmt-c library with
functionality for formatting JavaScript code.

\subsubsection*{(js-expr x)}

Formats a JavaScript expression similarly to \q{c-expr}.  Inside a
\q{js-expr} formatter, you can use the normal \q{c-} prefixed
formatters described in the previous section, and they will format
appropriately for JavaScript.

Currently expressions will all be terminated with a semi-colon, but
that will be made optional in a later release.

\subsubsection*{(js-function [<name>] (<params>) <body> ...)}

Defines a function (anonymously if no name is provided).

\subsubsection*{(js-var <name> [<init-value>])}

Declares a JavaScript variable, optionally with an initial value.

\subsubsection*{(js-comment <formatter> ...)}

Formats a comment prefixing lines with \q{"// "}.

\section{Formatting with Color}

The fmt-color library provides the following utilities:

\q{
  (fmt-red <formatter> ...)
  (fmt-blue <formatter> ...)
  (fmt-green <formatter> ...)
  (fmt-cyan <formatter> ...)
  (fmt-yellow <formatter> ...)
  (fmt-magenta <formatter> ...)
  (fmt-white <formatter> ...)
  (fmt-black <formatter> ...)
  (fmt-bold <formatter> ...)
  (fmt-underline <formatter> ...)
}

and more generally

  \q{(fmt-color <color> <formatter> ...)}

where color can be a symbol name or \q{#xRRGGBB} numeric value.
Outputs the formatters colored with ANSI escapes.  In addition

  \q{(fmt-in-html <formatter> ...)}

can be used to mark the format state as being inside HTML, which the
above color formats will understand and output HTML \q{<span>} tags with
the appropriate style colors, instead of ANSI escapes.


\section{Unicode}

The fmt-unicode library provides the \q{fmt-unicode} formatter, which
just takes a list of formatters and overrides the string-length for
padding and trimming, such that Unicode double or full width
characters are considered 2 characters wide (as they typically are in
fixed-width terminals), while treating combining and non-spacing
characters as 0 characters wide.

It also recognizes and ignores ANSI escapes, in particular useful if
you want to combine this with the fmt-color utilities.


\section{Optimizing}

The library is designed for scalability and flexibility, not speed,
and I'm not going to think about any fine tuning until it's more
stabilised.  One aspect of the design, however, was influenced for the
sake of future optimizations, which is that none of the default format
variables are initialized by global parameters, which leaves room for
inlining and subsequent simplification of format calls.

If you don't have an aggressively optimizing compiler, you can easily
achieve large speedups on common cases with CL-style compiler macros.

\section{Common Lisp Format Cheat Sheet}

A quick reference for those of you switching over from Common Lisp's
format.

\table{
\b{format} | \b{fmt}
~a | \q{dsp}
~c | \q{dsp}
~s | \q{wrt/unshared}
~w | \q{wrt}
~y | \q{pretty}
~x | \q{(radix 16 ...)} or \q{(num <n> 16)}
~o | \q{(radix 8 ...)} or \q{(num <n> 8)}
~b | \q{(radix 2 ...)} or \q{(num <n> 2)}
~f | \q{(fix <digits> ...)} or \q{(num <n> <radix> <digits>)}
~% | \q{nl}
~& | \q{fl}
~[...~] | normal \q{if} or \q{fmt-if} (delayed test)
~{...~} | \q{(fmt-join ... <list> [<sep>])}
}

\section{References}

\bibitem{R5RS} R. Kelsey, W. Clinger, J. Rees (eds.)
\urlh{http://www.schemers.org/Documents/Standards/R5RS/}{Revised^5 Report on the Algorithmic Language Scheme}

\bibitem{CommonLisp} Guy L. Steele Jr. (editor)
\urlh{http://www.harlequin.com/education/books/HyperSpec/}{Common Lisp Hyperspec}

\bibitem{SRFI-28} Scott G. Miller
\urlh{http://srfi.schemers.org/srfi-28/}{SRFI-28 Basic Format Strings}

\bibitem{SRFI-48} Ken Dickey
\urlh{http://srfi.schemers.org/srfi-48/}{SRFI-48 Intermediate Format Strings}

\bibitem{SRFI-38} Ray Dillinger
\urlh{http://srfi.schemers.org/srfi-38/}{SRFI-38 External Representation for Data With Shared Structure}

\bibitem{Perl6} Damian Conway
\urlh{http://www.perl.com/lpt/a/819}{Perl6 Exegesis 7 - formatting}

\eval(display "<br /><br /><br /><br />\n")

Added fmt/fmt.html.















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
<html><head><title>Combinator Formatting</title>
</head><body bgcolor=white>

<h1><span class=subject>Combinator Formatting</span></h1>

<style>
body {
color: black;
background-color: white;
margin-top: 2em;
margin-left: 10%;
width: 400pt;
}

pre {
  background-color: beige;
}

pre.scheme {
  background-color: white;
}

.subject {
}

h1 {
margin-left: -5%;
margin-top: 2em;
font-size: large;
}

h2 {
margin-left: -4%;
margin-top: 1em;
font-size: large;
}

h3,h4,h5,h6 {
margin-left: -3%;
margin-top: .5em;
font-size: small;
}

.navigation {
color: red;
background-color: beige;
text-align: right;
font-style: italic;
}


.scheme {
color: brown;
}

.scheme .keyword {
color: #cc0000;
font-weight: bold;
}

.scheme .variable {
color: navy;
}

.scheme .global {
color: purple;
}

.scheme .constant,.number,.char,.string,.boolean {
color: green;
}

.scheme .comment {
color: teal;
}
</style>
<div align=right><a href="http://synthcode.com/">Alex Shinn</a></div>
<div align=right><a href="http://synthcode.com/scheme/fmt/fmt-0.8.4.tar.gz">Download Version 0.8.4</a></div>
<p>

<br /><br />

A library of procedures for formatting Scheme objects to text in
various ways, and for easily concatenating, composing and extending
these formatters efficiently without resorting to capturing and
manipulating intermediate strings.
<p>

<br /><br />

<a name="SECTION_1"><h1>1&nbsp;&nbsp;Table of Contents</h1>



<ol>
<li><a href="#SECTION_1">Table of Contents</a>
<li><a href="#SECTION_2">Installation</a>
<li><a href="#SECTION_3">Background</a>
<li><a href="#SECTION_4">Usage</a>
<li><a href="#SECTION_5">Specification</a>
<ol>
<li><a href="#SECTION_5.1">Formatting Objects</a>
<li><a href="#SECTION_5.2">Formatting Numbers</a>
<li><a href="#SECTION_5.3">Formatting Space</a>
<li><a href="#SECTION_5.4">Concatenation</a>
<li><a href="#SECTION_5.5">Padding and Trimming</a>
<li><a href="#SECTION_5.6">Format Variables</a>
<li><a href="#SECTION_5.7">Columnar Formatting</a>
</ol>
<li><a href="#SECTION_6">C Formatting</a>
<ol>
<li><a href="#SECTION_6.1">C Formatting Basics</a>
<li><a href="#SECTION_6.2">C Preprocessor Formatting</a>
<li><a href="#SECTION_6.3">Customizing C Style</a>
<li><a href="#SECTION_6.4">C Formatter Index</a>
<li><a href="#SECTION_6.5">C Preprocessor Formatter Index</a>
<li><a href="#SECTION_6.6">C Types</a>
<li><a href="#SECTION_6.7">C as S-Expressions</a>
</ol>
<li><a href="#SECTION_7">JavaScript Formatting</a>
<li><a href="#SECTION_8">Formatting with Color</a>
<li><a href="#SECTION_9">Unicode</a>
<li><a href="#SECTION_10">Optimizing</a>
<li><a href="#SECTION_11">Common Lisp Format Cheat Sheet</a>
<li><a href="#SECTION_12">References</a>
</ol>

<br /><br />

<a name="SECTION_2"><h1>2&nbsp;&nbsp;Installation</h1>

Available for Chicken as the <code>fmt</code> egg, providing the <code class=scheme><span class=variable>fmt</span></code>,
<code class=scheme><span class=variable>fmt-c</span></code>, <code class=scheme><span class=variable>fmt-color</span></code> and <code class=scheme><span class=variable>fmt-unicode</span></code> extensions.  To install
manually for Chicken just run <code>&quot;chicken-setup&quot;</code> in the fmt
directory.
<p>

For Gauche run <code>&quot;make gauche &amp;&amp; make install-gauche&quot;</code>.  The modules
are installed as <code class=scheme><span class=variable>text.fmt</span></code>, <code class=scheme><span class=variable>text.fmt.c</span></code>, <code class=scheme><span class=variable>text.fmt.color</span></code> and
<code class=scheme><span class=variable>text.fmt.unicode</span></code>.
<p>

For MzScheme you can download and install the latest <code>fmt.plt</code> yourself
from:
<p>

<a href="http://synthcode.com/scheme/fmt/fmt.plt">http://synthcode.com/scheme/fmt/fmt.plt</a>
<p>

To build the <code>fmt.plt</code> for yourself you can run <code>&quot;make mzscheme&quot;</code>.
<p>

For Scheme48 the package descriptions are in <code>fmt-scheme48.scm</code>:
<p>

<pre class=scheme>
<span class=variable>&gt;</span> ,<span class=variable>config</span> ,<span class=variable>load</span> <span class=variable>fmt-scheme48.scm</span>
<span class=variable>&gt;</span> ,<span class=variable>open</span> <span class=variable>fmt</span>
</pre>
<p>

For other implementations you'll need to load SRFI's 1, 6, 13, 33
(sample provided) and 69 (also provided), and then load the following
files:
<p>

<pre class=scheme>
  (<span class=variable>load</span> <span class=string>&quot;let-optionals.scm&quot;</span>)  <span class=comment>; if you don't have LET-OPTIONALS*</span>
  (<span class=variable>load</span> <span class=string>&quot;read-line.scm&quot;</span>)      <span class=comment>; if you don't have READ-LINE</span>
  (<span class=variable>load</span> <span class=string>&quot;string-ports.scm&quot;</span>)   <span class=comment>; if you don't have CALL-WITH-OUTPUT-STRING</span>
  (<span class=variable>load</span> <span class=string>&quot;make-eq-table.scm&quot;</span>)
  (<span class=variable>load</span> <span class=string>&quot;mantissa.scm&quot;</span>)
  (<span class=variable>load</span> <span class=string>&quot;fmt.scm&quot;</span>)
  (<span class=variable>load</span> <span class=string>&quot;fmt-pretty.scm&quot;</span>)     <span class=comment>; optional pretty printing</span>
  (<span class=variable>load</span> <span class=string>&quot;fmt-column.scm&quot;</span>)     <span class=comment>; optional columnar output</span>
  (<span class=variable>load</span> <span class=string>&quot;fmt-c.scm&quot;</span>)          <span class=comment>; optional C formatting utilities</span>
  (<span class=variable>load</span> <span class=string>&quot;fmt-color.scm&quot;</span>)      <span class=comment>; optional color utilities</span>
  (<span class=variable>load</span> <span class=string>&quot;fmt-unicode.scm&quot;</span>)    <span class=comment>; optional Unicode-aware formatting,</span>
                              <span class=comment>;   also requires SRFI-4 or SRFI-66</span>
</pre>
<p>

<a name="SECTION_3"><h1>3&nbsp;&nbsp;Background</h1>

There are several approaches to text formatting.  Building strings to
<code class=scheme><span class=variable>display</span></code> is not acceptable, since it doesn't scale to very large
output.  The simplest realistic idea, and what people resort to in
typical portable Scheme, is to interleave <code class=scheme><span class=variable>display</span></code> and <code class=scheme><span class=variable>write</span></code>
and manual loops, but this is both extremely verbose and doesn't
compose well.  A simple concept such as padding space can't be
achieved directly without somehow capturing intermediate output.
<p>

The traditional approach is to use templates - typically strings,
though in theory any object could be used and indeed Emacs' mode-line
format templates allow arbitrary sexps.  Templates can use either
escape sequences (as in C's <code class=scheme><span class=variable>printf</span></code> and <a href="#BIBITEM_2">CL's</a>
<code class=scheme><span class=variable>format</span></code>) or pattern matching (as in Visual Basic's <code class=scheme><span class=variable>Format</span></code>,
<a href="#BIBITEM_6">Perl6's</a> <code class=scheme><span class=variable>form</span></code>, and SQL date formats).  The
primary disadvantage of templates is the relative difficulty (usually
impossibility) of extending them, their opaqueness, and the
unreadability that arises with complex formats.  Templates are not
without their advantages, but they are already addressed by other
libraries such as <a href="#BIBITEM_3">SRFI-28</a> and
<a href="#BIBITEM_4">SRFI-48</a>.
<p>

This library takes a combinator approach.  Formats are nested chains
of closures, which are called to produce their output as needed.
The primary goal of this library is to have, first and foremost, a
maximally expressive and extensible formatting library.  The next
most important goal is scalability - to be able to handle
arbitrarily large output and not build intermediate results except
where necessary.  The third goal is brevity and ease of use.
<p>

<a name="SECTION_4"><h1>4&nbsp;&nbsp;Usage</h1>

The primary interface is the <code class=scheme><span class=variable>fmt</span></code> procedure:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=variable>&lt;output-dest&gt;</span> <span class=variable>&lt;format&gt;</span> ...)</code>
<p>

where <code class=scheme><span class=variable>&lt;output-dest&gt;</span></code> has the same semantics as with <code class=scheme><span class=variable>format</span></code> -
specifically it can be an output-port, <code class=scheme><span class=boolean>#t</span></code> to indicate the current
output port, or <code class=scheme><span class=boolean>#f</span></code> to accumulate output into a string.
<p>

Each <code class=scheme><span class=variable>&lt;format&gt;</span></code> should be a format closure as discussed below.  As a
convenience, non-procedure arguments are also allowed and are
formatted similar to <code class=scheme><span class=variable>display</span></code>, so that
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> <span class=string>&quot;Result: &quot;</span> <span class=variable>res</span> <span class=variable>nl</span>)</code>
<p>

would return the string <code class=scheme><span class=string>&quot;Result: 42n&quot;</span></code>, assuming <code class=scheme><span class=variable>RES</span></code> is bound
to <code class=scheme><span class=number>42</span></code>.
<p>

<code class=scheme><span class=variable>nl</span></code> is the newline format combinator.
<p>

<a name="SECTION_5"><h1>5&nbsp;&nbsp;Specification</h1>

The procedure names have gone through several variations, and I'm
still open to new suggestions.  The current approach is to use
abbreviated forms of standard output procedures when defining an
equivalent format combinator (thus <code class=scheme><span class=variable>display</span></code> becomes <code class=scheme><span class=variable>dsp</span></code> and
<code class=scheme><span class=variable>write</span></code> becomes <code class=scheme><span class=variable>wrt</span></code>), and to use an <code class=scheme><span class=variable>fmt-</span></code> prefix for
utilities and less common combinators.  Variants of the same formatter
get a <code class=scheme><span class=variable>/&lt;variant&gt;</span></code> suffix.
<p>

<a name="SECTION_5.1"><h2>5.1&nbsp;&nbsp;Formatting Objects</h2>

<h3>(dsp &lt;obj&gt;)</h3>

Outputs <code class=scheme><span class=variable>&lt;obj&gt;</span></code> using <code class=scheme><span class=variable>display</span></code> semantics.  Specifically, strings
are output without surrounding quotes or escaping and characters are
written as if by <code class=scheme><span class=variable>write-char</span></code>.  Other objects are written as with
<code class=scheme><span class=variable>write</span></code> (including nested strings and chars inside <code class=scheme><span class=variable>&lt;obj&gt;</span></code>).  This
is the default behavior for top-level formats in <code class=scheme><span class=variable>fmt</span></code>, <code class=scheme><span class=variable>cat</span></code> and
most other higher-order combinators.
<p>

<h3>(wrt &lt;obj&gt;)</h3>

Outputs <code class=scheme><span class=variable>&lt;obj&gt;</span></code> using <code class=scheme><span class=variable>write</span></code> semantics.  Handles shared
structures as in <a href="#BIBITEM_5">SRFI-38</a>.
<p>

<h3>(wrt/unshared &lt;obj&gt;)</h3>

As above, but doesn't handle shared structures.  Infinite loops can
still be avoided if used inside a combinator that truncates data (see
<code class=scheme><span class=variable>trim</span></code> and <code class=scheme><span class=variable>fit</span></code> below).
<p>

<h3>(pretty &lt;obj&gt;)</h3>

Pretty-prints <code class=scheme><span class=variable>&lt;obj&gt;</span></code>.  Also handles shared structures.  Unlike many
other pretty printers, vectors and data lists (lists that don't begin
with a (nested) symbol), are printed in tabular format when there's
room, greatly saving vertical space.
<p>

<h3>(pretty/unshared &lt;obj&gt;)</h3>

As above but without sharing.
<p>

<h3>(slashified &lt;str&gt; [&lt;quote-ch&gt; &lt;esc-ch&gt; &lt;renamer&gt;])</h3>

Outputs the string <code class=scheme><span class=variable>&lt;str&gt;</span></code>, escaping any quote or escape characters.
If <code class=scheme><span class=variable>&lt;esc-ch&gt;</span></code> is <code class=scheme><span class=boolean>#f</span></code> escapes only the <code class=scheme><span class=variable>&lt;quote-ch&gt;</span></code> by
doubling it, as in SQL strings and CSV values.  If <code class=scheme><span class=variable>&lt;renamer&gt;</span></code> is
provided, it should be a procedure of one character which maps that
character to its escape value, e.g. <code class=scheme><span class=char>#\newline</span> <span class=keyword>=&gt;</span> <span class=char>#\n</span></code>, or <code class=scheme><span class=boolean>#f</span></code> if
there is no escape value.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>slashified</span> <span class=string>&quot;hi, &quot;bob!&quot;&quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;hi, &quot;bob!&quot;&quot;</span></code>
<p>

<h3>(maybe-slashified &lt;str&gt; &lt;pred&gt; [&lt;quote-ch&gt; &lt;esc-ch&gt; &lt;renamer&gt;])</h3>

Like <code class=scheme><span class=variable>slashified</span></code>, but first checks if any quoting is required (by
the existence of either any quote or escape characters, or any
character matching <code class=scheme><span class=variable>&lt;pred&gt;</span></code>), and if so outputs the string in quotes
and with escapes.  Otherwise outputs the string as is.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>maybe-slashified</span> <span class=string>&quot;foo&quot;</span> <span class=variable>char-whitespace?</span> <span class=char>#\&quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;foo&quot;</span></code>
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>maybe-slashified</span> <span class=string>&quot;foo bar&quot;</span> <span class=variable>char-whitespace?</span> <span class=char>#\&quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;&quot;foo bar&quot;&quot;</span></code>
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>maybe-slashified</span> <span class=string>&quot;foo&quot;bar&quot;baz&quot;</span> <span class=variable>char-whitespace?</span> <span class=char>#\&quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;&quot;foo&quot;bar&quot;baz&quot;&quot;</span></code>
<p>

<a name="SECTION_5.2"><h2>5.2&nbsp;&nbsp;Formatting Numbers</h2>

<h3>(num &lt;n&gt; [&lt;radix&gt; &lt;precision&gt; &lt;sign&gt; &lt;comma&gt; &lt;comma-sep&gt; &lt;decimal-sep&gt;])</h3>

Formats a single number <code class=scheme><span class=variable>&lt;n&gt;</span></code>.  You can optionally specify any
<code class=scheme><span class=variable>&lt;radix&gt;</span></code> from 2 to 36 (even if <code class=scheme><span class=variable>&lt;n&gt;</span></code> isn't an integer).
<code class=scheme><span class=variable>&lt;precision&gt;</span></code> forces a fixed-point format.
<p>

A <code class=scheme><span class=variable>&lt;sign&gt;</span></code> of <code class=scheme><span class=boolean>#t</span></code> indicates to output a plus sign (+) for positive
integers.  However, if <code class=scheme><span class=variable>&lt;sign&gt;</span></code> is a character, it means to wrap the
number with that character and its mirror opposite if the number is
negative.  For example, <code class=scheme><span class=char>#\(</span></code> prints negative numbers in parenthesis,
financial style: <code class=scheme><span class=number>-3.14</span> <span class=keyword>=&gt;</span> (<span class=number>3.14</span>)</code>
<p>

<code class=scheme><span class=variable>&lt;comma&gt;</span></code> is an integer specifying the number of digits between
commas.  Variable length, as in subcontinental-style, is not yet
supported.
<p>

<code class=scheme><span class=variable>&lt;comma-sep&gt;</span></code> is the character to use for commas, defaulting to <code class=scheme><span class=char>#\,</span></code>.
<p>

<code class=scheme><span class=variable>&lt;decimal-sep&gt;</span></code> is the character to use for decimals, defaulting to
<code class=scheme><span class=char>#\.</span></code>, or to <code class=scheme><span class=char>#\,</span></code> (European style) if <code class=scheme><span class=variable>&lt;comma-sep&gt;</span></code> is already
<code class=scheme><span class=char>#\.</span></code>.
<p>

These parameters may seem unwieldy, but they can also take their
defaults from state variables, described below.
<p>

<h3>(num/comma &lt;n&gt; [&lt;base&gt; &lt;precision&gt; &lt;sign&gt;])</h3>

Shortcut for <code class=scheme><span class=variable>num</span></code> to print with commas.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/comma</span> <span class=number>1234567</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;1,234,567&quot;</span></code>
<p>

<h3>(num/si &lt;n&gt; [&lt;base&gt; &lt;suffix&gt;])</h3>

Abbreviates <code class=scheme><span class=variable>&lt;n&gt;</span></code> with an SI suffix as in the -h or --si option to
many GNU commands.  The base defaults to 1024, using suffix names
like Ki, Mi, Gi, etc.  Other bases (e.g. the standard 1000) have the
suffixes k, M, G, etc.
<p>

The <code class=scheme><span class=variable>&lt;suffix&gt;</span></code> argument is appended only if an abbreviation is used.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/si</span> <span class=number>608</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;608&quot;</span></code>
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/si</span> <span class=number>3986</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;3.9Ki&quot;</span></code>
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/si</span> <span class=number>3986</span> <span class=number>1000</span> <span class=string>&quot;B&quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;4kB&quot;</span></code>
<p>

See <a href="http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html">http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html</a>.
<p>

<h3>(num/fit &lt;width&gt; &lt;n&gt; . &lt;ARGS&gt;)</h3>

Like <code class=scheme><span class=variable>num</span></code>, but if the result doesn't fit in <code class=scheme><span class=variable>&lt;width&gt;</span></code>, output
instead a string of hashes (with the current <code class=scheme><span class=variable>&lt;precision&gt;</span></code>) rather
than showing an incorrectly truncated number.  For example
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fix</span> <span class=number>2</span> (<span class=variable>num/fit</span> <span class=number>4</span> <span class=number>12.345</span>)))</code>
  <code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;#.##&quot;</span></code>
<p>

<h3>(num/roman &lt;n&gt;)</h3>

Formats the number as a Roman numeral:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/roman</span> <span class=number>1989</span>))</code>
  <code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;MCMLXXXIX&quot;</span></code>
<p>

<h3>(num/old-roman &lt;n&gt;)</h3>

Formats the number as an old-style Roman numeral, without the
subtraction abbreviation rule:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>num/old-roman</span> <span class=number>1989</span>))</code>
  <code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;MDCCCCLXXXVIIII&quot;</span></code>
<p>

<a name="SECTION_5.3"><h2>5.3&nbsp;&nbsp;Formatting Space</h2>

<h3>nl</h3>

Outputs a newline.
<p>

<h3>fl</h3>

Short for &quot;fresh line,&quot; outputs a newline only if we're not already
at the start of a line.
<p>

<h3>(space-to &lt;column&gt;)</h3>

Outputs spaces up to the given <code class=scheme><span class=variable>&lt;column&gt;</span></code>.  If the current column is
already &gt;= <code class=scheme><span class=variable>&lt;column&gt;</span></code>, does nothing.
<p>

<h3>(tab-to [&lt;tab-width&gt;])</h3>

Outputs spaces up to the next tab stop, using tab stops of width
<code class=scheme><span class=variable>&lt;tab-width&gt;</span></code>, which defaults to 8.  If already on a tab stop, does
nothing.  If you want to ensure you always tab at least one space, you
can use <code class=scheme>(<span class=variable>cat</span> <span class=string>&quot; &quot;</span> (<span class=variable>tab-to</span> <span class=variable>width</span>))</code>.
<p>

<h3>fmt-null</h3>

Outputs nothing (useful in combinators and as a default noop in
conditionals).
<p>

<a name="SECTION_5.4"><h2>5.4&nbsp;&nbsp;Concatenation</h2>

<h3>(cat &lt;format&gt; ...)</h3>

Concatenates the output of each <code class=scheme><span class=variable>&lt;format&gt;</span></code>.
<p>

<h3>(apply-cat &lt;list&gt;)</h3>

Equivalent to <code class=scheme>(<span class=variable>apply</span> <span class=variable>cat</span> <span class=variable>&lt;list&gt;</span>)</code> but may be more efficient.
<p>

<h3>(fmt-join &lt;formatter&gt; &lt;list&gt; [&lt;sep&gt;])</h3>

Formats each element <code class=scheme><span class=variable>&lt;elt&gt;</span></code> of <code class=scheme><span class=variable>&lt;list&gt;</span></code> with <code class=scheme>(<span class=variable>&lt;formatter&gt;</span>
<span class=variable>&lt;elt&gt;</span>)</code>, inserting <code class=scheme><span class=variable>&lt;sep&gt;</span></code> in between.  <code class=scheme><span class=variable>&lt;sep&gt;</span></code> defaults to the
empty string, but can be any format.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fmt-join</span> <span class=variable>dsp</span> '(<span class=variable>a</span> <span class=variable>b</span> <span class=variable>c</span>) <span class=string>&quot;, &quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;a, b, c&quot;</span></code>
<p>

<h3>(fmt-join/prefix &lt;formatter&gt; &lt;list&gt; [&lt;sep&gt;])</h3>

<h3>(fmt-join/suffix &lt;formatter&gt; &lt;list&gt; [&lt;sep&gt;])</h3>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fmt-join/prefix</span> <span class=variable>dsp</span> '(<span class=variable>usr</span> <span class=variable>local</span> <span class=variable>bin</span>) <span class=string>&quot;/&quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;/usr/local/bin&quot;</span></code>
<p>

As <code class=scheme><span class=variable>fmt-join</span></code>, but inserts <code class=scheme><span class=variable>&lt;sep&gt;</span></code> before/after every element.
<p>

<h3>(fmt-join/last &lt;formatter&gt; &lt;last-formatter&gt; &lt;list&gt; [&lt;sep&gt;])</h3>

As <code class=scheme><span class=variable>fmt-join</span></code>, but the last element of the list is formatted with
<code class=scheme><span class=variable>&lt;last-formatter&gt;</span></code> instead.
<p>

<h3>(fmt-join/dot &lt;formatter&gt; &lt;dot-formatter&gt; &lt;list&gt; [&lt;sep&gt;])</h3>

As <code class=scheme><span class=variable>fmt-join</span></code>, but if the list is a dotted list, then formats the dotted
value with <code class=scheme><span class=variable>&lt;dot-formatter&gt;</span></code> instead.
<p>

<a name="SECTION_5.5"><h2>5.5&nbsp;&nbsp;Padding and Trimming</h2>

<h3>(pad &lt;width&gt; &lt;format&gt; ...)</h3>

<h3>(pad/left &lt;width&gt; &lt;format&gt; ...)</h3>

<h3>(pad/both &lt;width&gt; &lt;format&gt; ...)</h3>

Analogs of SRFI-13 <code class=scheme><span class=variable>string-pad</span></code>, these add extra space to the left,
right or both sides of the output generated by the <code class=scheme><span class=variable>&lt;format&gt;</span></code>s to
pad it to <code class=scheme><span class=variable>&lt;width&gt;</span></code>.  If <code class=scheme><span class=variable>&lt;width&gt;</span></code> is exceeded has no effect.
<code class=scheme><span class=variable>pad/both</span></code> will include an extra space on the right side of the
output if the difference is odd.
<p>

<code class=scheme><span class=variable>pad</span></code> does not accumulate any intermediate data.
<p>

Note these are column-oriented padders, so won't necessarily work
with multi-line output (padding doesn't seem a likely operation for
multi-line output).
<p>

<h3>(trim &lt;width&gt; &lt;format&gt; ...)</h3>

<h3>(trim/left &lt;width&gt; &lt;format&gt; ...)</h3>

<h3>(trim/both &lt;width&gt; &lt;format&gt; ...)</h3>

Analogs of SRFI-13 <code class=scheme><span class=variable>string-trim</span></code>, truncates the output of the
<code class=scheme><span class=variable>&lt;format&gt;</span></code>s to force it in under <code class=scheme><span class=variable>&lt;width&gt;</span></code> columns.  As soon as
any of the <code class=scheme><span class=variable>&lt;format&gt;</span></code>s exceed <code class=scheme><span class=variable>&lt;width&gt;</span></code>, stop formatting and
truncate the result, returning control to whoever called <code class=scheme><span class=variable>trim</span></code>.  If
<code class=scheme><span class=variable>&lt;width&gt;</span></code> is not exceeded has no effect.
<p>

If a truncation ellipse is set (e.g. with the <code class=scheme><span class=variable>ellipses</span></code> procedure
below), then when any truncation occurs <code class=scheme><span class=variable>trim</span></code> and <code class=scheme><span class=variable>trim/left</span></code>
will append and prepend the ellipse, respectively.  <code class=scheme><span class=variable>trim/both</span></code> will
both prepend and append.  The length of the ellipse will be considered
when truncating the original string, so that the total width will
never be longer than <code class=scheme><span class=variable>&lt;width&gt;</span></code>.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>ellipses</span> <span class=string>&quot;...&quot;</span> (<span class=variable>trim</span> <span class=number>5</span> <span class=string>&quot;abcde&quot;</span>)))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span>  <span class=string>&quot;abcde&quot;</span></code>
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>ellipses</span> <span class=string>&quot;...&quot;</span> (<span class=variable>trim</span> <span class=number>5</span> <span class=string>&quot;abcdef&quot;</span>)))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span>  <span class=string>&quot;ab...&quot;</span></code>
<p>

<h3>(trim/length &lt;width&gt; &lt;format&gt; ...)</h3>

A variant of <code class=scheme><span class=variable>trim</span></code> which acts on the actual character count rather
than columns, useful for truncating potentially cyclic data.
<p>

<h3>(fit &lt;width&gt; &lt;format&gt; ...)</h3>

<h3>(fit/left &lt;width&gt; &lt;format&gt; ...)</h3>

<h3>(fit/both &lt;width&gt; &lt;format&gt; ...)</h3>

A combination of <code class=scheme><span class=variable>pad</span></code> and <code class=scheme><span class=variable>trunc</span></code>, ensures the output width is
exactly <code class=scheme><span class=variable>&lt;width&gt;</span></code>, truncating if it goes over and padding if it goes
under.
<p>

<a name="SECTION_5.6"><h2>5.6&nbsp;&nbsp;Format Variables</h2>

You may have noticed many of the formatters are aware of the current
column.  This is because each combinator is actually a procedure of
one argument, the current format state, which holds basic
information such as the row, column, and any other information that
a format combinator may want to keep track of.  The basic interface
is:
<p>

<h3>(fmt-let &lt;name&gt; &lt;value&gt; &lt;format&gt; ...)</h3>

<h3>(fmt-bind &lt;name&gt; &lt;value&gt; &lt;format&gt; ...)</h3>

<code class=scheme><span class=variable>fmt-let</span></code> sets the name for the duration of the <code class=scheme><span class=variable>&lt;format&gt;</span></code>s, and
restores it on return.  <code class=scheme><span class=variable>fmt-bind</span></code> sets it without restoring it.
<p>

A convenience control structure can be useful in combination with
these states:
<p>

<h3>(fmt-if &lt;pred&gt; &lt;pass&gt; [&lt;fail&gt;])</h3>

<code class=scheme><span class=variable>&lt;pred&gt;</span></code> takes one argument (the format state) and returns a boolean
result.  If true, the <code class=scheme><span class=variable>&lt;pass&gt;</span></code> format is applied to the state,
otherwise <code class=scheme><span class=variable>&lt;fail&gt;</span></code> (defaulting to the identity) is applied.
<p>

Many of the previously mentioned combinators have behavior which can
be altered with state variables.  Although <code class=scheme><span class=variable>fmt-let</span></code> and <code class=scheme><span class=variable>fmt-bind</span></code>
could be used, these common variables have shortcuts:
<p>

<h3>(radix &lt;k&gt; &lt;format&gt; ...)</h3>

<h3>(fix &lt;k&gt; &lt;format&gt; ...)</h3>

These alter the radix and fixed point precision of numbers output with
<code class=scheme><span class=variable>dsp</span></code>, <code class=scheme><span class=variable>wrt</span></code>, <code class=scheme><span class=variable>pretty</span></code> or <code class=scheme><span class=variable>num</span></code>.  These settings apply
recursively to all output data structures, so that
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>radix</span> <span class=number>16</span> '(<span class=number>70</span> <span class=number>80</span> <span class=number>90</span>)))</code>
<p>

will return the string <code class=scheme><span class=string>&quot;(#x46 #x50 #x5a)&quot;</span></code>.  Note that read/write
invariance is essential, so for <code class=scheme><span class=variable>dsp</span></code>, <code class=scheme><span class=variable>wrt</span></code> and <code class=scheme><span class=variable>pretty</span></code> the
radix prefix is always included when not decimal.  Use <code class=scheme><span class=variable>num</span></code> if you
want to format numbers in alternate bases without this prefix.  For
example,
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>radix</span> <span class=number>16</span> <span class=string>&quot;(&quot;</span> (<span class=variable>fmt-join</span> <span class=variable>num</span> '(<span class=number>70</span> <span class=number>80</span> <span class=number>90</span>) <span class=string>&quot; &quot;</span>) <span class=string>&quot;)&quot;</span>))</code>
<p>

would return <code class=scheme><span class=string>&quot;(46 50 5a)&quot;</span></code>, the same output as above without the
&quot;#x&quot; radix prefix.
<p>

Note that fixed point formatting supports arbitrary precision in
implementations with exact non-integral rationals.  When trying to
print inexact numbers more than the machine precision you will
typically get results like
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fix</span> <span class=number>30</span> <span class=constant>#i2/3</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;0.666666666666666600000000000000&quot;</span></code>
<p>

but with an exact rational it will give you as many digits as you
request:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>fix</span> <span class=number>30</span> <span class=number>2/3</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;0.666666666666666666666666666667&quot;</span></code>
<p>

<h3>(decimal-align &lt;k&gt; &lt;format&gt; ...)</h3>

Specifies an alignment for the decimal place when formatting numbers,
useful for outputting tables of numbers.
<p>

<pre class=scheme>
  (<span class=keyword>define</span> (<span class=variable>print-angles</span> <span class=variable>x</span>)
     (<span class=variable>fmt-join</span> <span class=variable>num</span> (<span class=variable>list</span> <span class=variable>x</span> (<span class=variable>sin</span> <span class=variable>x</span>) (<span class=variable>cos</span> <span class=variable>x</span>) (<span class=variable>tan</span> <span class=variable>x</span>)) <span class=string>&quot; &quot;</span>))

  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>decimal-align</span> <span class=number>5</span> (<span class=variable>fix</span> <span class=number>3</span> (<span class=variable>fmt-join/suffix</span> <span class=variable>print-angles</span> (<span class=variable>iota</span> <span class=number>5</span>) <span class=variable>nl</span>))))
</pre>
<p>

would output
<p>

<pre>
   0.000    0.000    1.000    0.000
   1.000    0.842    0.540    1.557
   2.000    0.909   -0.416   -2.185
   3.000    0.141   -0.990   -0.142
   4.000   -0.757   -0.654    1.158
</pre>
<p>

<h3>(comma-char &lt;k&gt; &lt;format&gt; ...)</h3>

<h3>(decimal-char &lt;k&gt; &lt;format&gt; ...)</h3>

<code class=scheme><span class=variable>comma-char</span></code> and <code class=scheme><span class=variable>decimal-char</span></code> set the defaults for number
formatting.
<p>

<h3>(pad-char &lt;k&gt; &lt;format&gt; ...)</h3>

The <code class=scheme><span class=variable>pad-char</span></code> sets the character used by <code class=scheme><span class=variable>space-to</span></code>, <code class=scheme><span class=variable>tab-to</span></code>,
<code class=scheme><span class=variable>pad/*</span></code>, and <code class=scheme><span class=variable>fit/*</span></code>, and defaults to <code class=scheme><span class=char>#\space</span></code>.
<p>

<pre class=scheme>
  (<span class=keyword>define</span> (<span class=variable>print-table-of-contents</span> <span class=variable>alist</span>)
    (<span class=keyword>define</span> (<span class=variable>print-line</span> <span class=variable>x</span>)
      (<span class=variable>cat</span> (<span class=variable>car</span> <span class=variable>x</span>) (<span class=variable>space-to</span> <span class=number>72</span>) (<span class=variable>pad/left</span> <span class=number>3</span> (<span class=variable>cdr</span> <span class=variable>x</span>))))
    (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>pad-char</span> <span class=char>#\.</span> (<span class=variable>fmt-join/suffix</span> <span class=variable>print-line</span> <span class=variable>alist</span> <span class=variable>nl</span>))))

  (<span class=variable>print-table-of-contents</span>
   '((<span class=string>&quot;An Unexpected Party&quot;</span> . <span class=number>29</span>)
     (<span class=string>&quot;Roast Mutton&quot;</span> . <span class=number>60</span>)
     (<span class=string>&quot;A Short Rest&quot;</span> . <span class=number>87</span>)
     (<span class=string>&quot;Over Hill and Under Hill&quot;</span> . <span class=number>100</span>)
     (<span class=string>&quot;Riddles in the Dark&quot;</span> . <span class=number>115</span>)))
</pre>
<p>

would output
<p>

<pre>
  An Unexpected Party.....................................................29
  Roast Mutton............................................................60
  A Short Rest............................................................87
  Over Hill and Under Hill...............................................100
  Riddles in the Dark....................................................115
</pre>
<p>

<h3>(ellipse &lt;ell&gt; &lt;format&gt; ...)</h3>

Sets the truncation ellipse to <code class=scheme><span class=variable>&lt;ell&gt;</span></code>, would should be a string or
character.
<p>

<h3>(with-width &lt;width&gt; &lt;format&gt; ...)</h3>

Sets the maximum column width used by some formatters.  The default
is 78.
<p>

<a name="SECTION_5.7"><h2>5.7&nbsp;&nbsp;Columnar Formatting</h2>

Although <code class=scheme><span class=variable>tab-to</span></code>, <code class=scheme><span class=variable>space-to</span></code> and padding can be used to manually
align columns to produce table-like output, these can be awkward to
use.  The optional extensions in this section make this easier.
<p>

<h3>(columnar &lt;column&gt; ...)</h3>

Formats each <code class=scheme><span class=variable>&lt;column&gt;</span></code> side-by-side, i.e. as though each were
formatted separately and then the individual lines concatenated
together.  The current column width is divided evenly among the
columns, and all but the last column are right-padded.  For example
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> (<span class=variable>dsp</span> <span class=string>&quot;abcndefn&quot;</span>) (<span class=variable>dsp</span> <span class=string>&quot;123n456n&quot;</span>)))</code>
<p>

outputs
<p>

<pre>
     abc     123
     def     456
</pre>
<p>

assuming a 16-char width (the left side gets half the width, or 8
spaces, and is left aligned).  Note that we explicitly use DSP instead
of the strings directly.  This is because <code class=scheme><span class=variable>columnar</span></code> treats raw
strings as literals inserted into the given location on every line, to
be used as borders, for example:
<p>

<pre class=scheme>
  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> <span class=string>&quot;/* &quot;</span> (<span class=variable>dsp</span> <span class=string>&quot;abcndefn&quot;</span>)
                    <span class=string>&quot; | &quot;</span> (<span class=variable>dsp</span> <span class=string>&quot;123n456n&quot;</span>)
                    <span class=string>&quot; */&quot;</span>))
</pre>
<p>

would output
<p>

<pre>
  /* abc | 123 */
  /* def | 456 */
</pre>
<p>

You may also prefix any column with any of the symbols <code class=scheme>'<span class=variable>left</span></code>,
<code class=scheme>'<span class=variable>right</span></code> or <code class=scheme>'<span class=variable>center</span></code> to control the justification.  The symbol
<code class=scheme>'<span class=variable>infinite</span></code> can be used to indicate the column generates an infinite
stream of output.
<p>

You can further prefix any column with a width modifier.  Any
positive integer is treated as a fixed width, ignoring the available
width.  Any real number between 0 and 1 indicates a fraction of the
available width (after subtracting out any fixed widths).  Columns
with unspecified width divide up the remaining width evenly.
<p>

Note that <code class=scheme><span class=variable>columnar</span></code> builds its output incrementally, interleaving
calls to the generators until each has produced a line, then
concatenating that line together and outputting it.  This is important
because as noted above, some columns may produce an infinite stream of
output, and in general you may want to format data larger than can fit
into memory.  Thus columnar would be suitable for line numbering a
file of arbitrary size, or implementing the Unix <code class=scheme><span class=variable>yes</span>(<span class=number>1</span>)</code> command,
etc.
<p>

As an implementation detail, <code class=scheme><span class=variable>columnar</span></code> uses first-class
continuations to interleave the column output.  The core <code class=scheme><span class=variable>fmt</span></code>
itself has no knowledge of or special support for <code class=scheme><span class=variable>columnar</span></code>, which
could complicate and potentially slow down simpler <code class=scheme><span class=variable>fmt</span></code> operations.
This is a testament to the power of <code class=scheme><span class=variable>call/cc</span></code> - it can be used to
implement coroutines or arbitrary control structures even where they
were not planned for.
<p>

<h3>(tabular &lt;column&gt; ...)</h3>

Equivalent to <code class=scheme><span class=variable>columnar</span></code> except that each column is padded at least
to the minimum width required on any of its lines.  Thus
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>tabular</span> <span class=string>&quot;|" (dsp &quot;</span><span class=variable>a\\nbc\\ndef\\n</span><span class=string>&quot;) &quot;</span>|" (<span class=variable>dsp</span> <span class=string>&quot;123n45n6n&quot;</span>) <span class=string>&quot;|"))</span></code>
<p>

outputs
<p>

<pre>
|a  |123|
|bc |45 |
|def|6  |
</pre>
<p>

This makes it easier to generate tables without knowing widths in
advance.  However, because it requires generating the entire output in
advance to determine the correct column widths, <code class=scheme><span class=variable>tabular</span></code> cannot
format a table larger than would fit in memory.
<p>

<h3>(fmt-columns &lt;column&gt; ...)</h3>

The low-level formatter on which <code class=scheme><span class=variable>columnar</span></code> is based.  Each <code class=scheme><span class=variable>&lt;column&gt;</span></code>
must be a list of 2-3 elements:
<p>

<code class=scheme>(<span class=variable>&lt;line-formatter&gt;</span> <span class=variable>&lt;line-generator&gt;</span> [<span class=variable>&lt;infinite?&gt;</span>])</code>
<p>

where <code class=scheme><span class=variable>&lt;line-generator&gt;</span></code> is the column generator as above, and the
<code class=scheme><span class=variable>&lt;line-formatter&gt;</span></code> is how each line is formatted.  Raw concatenation
of each line is performed, without any spacing or width adjustment.
<code class=scheme><span class=variable>&lt;infinite?&gt;</span></code>, if true, indicates this generator produces an
infinite number of lines and termination should be determined without
it.
<p>

<h3>(wrap-lines &lt;format&gt; ...)</h3>

Behaves like <code class=scheme><span class=variable>cat</span></code>, except text is accumulated and lines are optimally
wrapped to fit in the current width as in the Unix <code>fmt(1)</code> command.
<p>

<h3>(justify &lt;format&gt; ...)</h3>

Like <code class=scheme><span class=variable>wrap-lines</span></code> except the lines are full-justified.
<p>

<pre class=scheme>
  (<span class=keyword>define</span> <span class=variable>func</span>
    '(<span class=keyword>define</span> (<span class=variable>fold</span> <span class=variable>kons</span> <span class=variable>knil</span> <span class=variable>ls</span>)
       (<span class=keyword>let</span> <span class=variable>lp</span> ((<span class=variable>ls</span> <span class=variable>ls</span>) (<span class=variable>acc</span> <span class=variable>knil</span>))
         (<span class=keyword>if</span> (<span class=variable>null?</span> <span class=variable>ls</span>) <span class=variable>acc</span> (<span class=variable>lp</span> (<span class=variable>cdr</span> <span class=variable>ls</span>) (<span class=variable>kons</span> (<span class=variable>car</span> <span class=variable>ls</span>) <span class=variable>acc</span>))))))

  (<span class=keyword>define</span> <span class=variable>doc</span>
    (<span class=variable>string-append</span>
      <span class=string>&quot;The fundamental list iterator.  Applies KONS to each element &quot;</span>
      <span class=string>&quot;of LS and the result of the previous application, beginning &quot;</span>
      <span class=string>&quot;with KNIL.  With KONS as CONS and KNIL as '(), equivalent to REVERSE.&quot;</span>))

  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> (<span class=variable>pretty</span> <span class=variable>func</span>) <span class=string>&quot; ; &quot;</span> (<span class=variable>justify</span> <span class=variable>doc</span>)))
</pre>
<p>

outputs
<p>

<pre>
  (define (fold kons knil ls)          ; The   fundamental   list   iterator.
    (let lp ((ls ls) (acc knil))       ; Applies  KONS  to  each  element  of
      (if (null? ls)                   ; LS  and  the  result of the previous
          acc                          ; application,  beginning  with  KNIL.
          (lp (cdr ls)                 ; With  KONS  as CONS and KNIL as '(),
              (kons (car ls) acc)))))  ; equivalent to REVERSE.
</pre>
<p>

<h3>(fmt-file &lt;pathname&gt;)</h3>

Simply displayes the contents of the file <code class=scheme><span class=variable>&lt;pathname&gt;</span></code> a line at a
time, so that in typical formatters such as <code class=scheme><span class=variable>columnar</span></code> only constant
memory is consumed, making this suitable for formatting files of
arbitrary size.
<p>

<h3>(line-numbers [&lt;start&gt;])</h3>

A convenience utility, just formats an infinite stream of numbers (in
the current radix) beginning with <code class=scheme><span class=variable>&lt;start&gt;</span></code>, which defaults to <code class=scheme><span class=number>1</span></code>.
<p>

The Unix <code class=scheme><span class=variable>nl</span>(<span class=number>1</span>)</code> utility could be implemented as:
<p>

<pre class=scheme>
  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>columnar</span> <span class=number>6</span> '<span class=variable>right</span> '<span class=variable>infinite</span> (<span class=variable>line-numbers</span>)
                    <span class=string>&quot; &quot;</span> (<span class=variable>fmt-file</span> <span class=string>&quot;read-line.scm&quot;</span>)))
</pre>
<p>

<pre>
     1 
     2 (define (read-line . o)
     3   (let ((port (if (pair? o) (car o) (current-input-port))))
     4     (let lp ((res '()))
     5       (let ((c (read-char port)))
     6         (if (or (eof-object? c) (eqv? c #\newline))
     7             (list-&gt;string (reverse res))
     8             (lp (cons c res)))))))
</pre>
<p>

<a name="SECTION_6"><h1>6&nbsp;&nbsp;C Formatting</h1>

<a name="SECTION_6.1"><h2>6.1&nbsp;&nbsp;C Formatting Basics</h2>

For purposes such as writing wrappers, code-generators, compilers or
other language tools, people often need to generate or emit C code.
Without a decent library framework it's difficult to maintain proper
indentation.  In addition, for the Scheme programmer it's tedious to
work with all the context sensitivities of C, such as the expression
vs. statement distinction, special rules for writing preprocessor
macros, and when precedence rules require parenthesis.  Fortunately,
context is one thing this formatting library is good at keeping
track of.  The C formatting interface tries to make it as easy as
possible to generate C code without getting in your way.
<p>

There are two approaches to using the C formatting extensions -
procedural and sexp-oriented (described in <a href="#SECTION_6.7">6.7</a>).  In the
procedural interface, C operators are made available as formatters
with a &quot;c-&quot; prefix, literals are converted to their C equivalents and
symbols are output as-is (you're responsible for making sure they are
valid C identifiers).  Indentation is handled automatically.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>))</code>
<p>

outputs
<p>

<pre>
  if (1) {
      2;
  } else {
      3;
  }
</pre>
<p>

In addition, the formatter knows when you're in an expression and
when you're in a statement, and behaves accordingly, so that
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-if</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>))</code>
<p>

outputs
<p>

<pre>
  if (1 ? 2 : 3) {
      4;
  } else {
      5;
  }
</pre>
<p>

Similary, <code class=scheme><span class=variable>c-begin</span></code>, used for sequencing, will separate with
semi-colons in a statement and commas in an expression.
<p>

Moreover, we also keep track of the final expression in a function
and insert returns for you:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-fun</span> '<span class=variable>int</span> '<span class=variable>foo</span> '() (<span class=variable>c-if</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>)))</code>
<p>

outputs
<p>

<pre>
  int foo () {
      if (1 ? 2 : 3) {
          return 4;
      } else {
          return 5;
      }
  }
</pre>
<p>

although it knows that void functions don't return.
<p>

Switch statements insert breaks by default if they don't return:
<p>

<pre class=scheme>
  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-switch</span> '<span class=variable>y</span>
            (<span class=variable>c-case</span> <span class=number>1</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>1</span>))
            (<span class=variable>c-default</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>2</span>))))
</pre>
<p>

<pre>
  switch (y) {
      case 1:
          x += 1;
          break;
      default:
          x += 2;
          break;
  }
</pre>
<p>

though you can explicitly fallthrough if you want:
<p>

<pre class=scheme>
  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-switch</span> '<span class=variable>y</span>
            (<span class=variable>c-case/fallthrough</span> <span class=number>1</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>1</span>))
            (<span class=variable>c-default</span> (<span class=variable>c+=</span> '<span class=variable>x</span> <span class=number>2</span>))))
</pre>
<p>

<pre>
  switch (y) {
      case 1:
          x += 1;
      default:
          x += 2;
          break;
  }
</pre>
<p>

Operators are available with just a &quot;c&quot; prefix, e.g. c+, c-, c*, c/,
etc.  <code class=scheme><span class=variable>c++</span></code> is a prefix operator, <code class=scheme><span class=variable>c++/post</span></code> is postfix.  ||, | and
|= are written as <code class=scheme><span class=variable>c-or</span></code>, <code class=scheme><span class=variable>c-bit-or</span></code> and <code class=scheme><span class=variable>c-bit-or=</span></code> respectively.
<p>

Function applications are written with <code class=scheme><span class=variable>c-apply</span></code>.  Other control
structures such as <code class=scheme><span class=variable>c-for</span></code> and <code class=scheme><span class=variable>c-while</span></code> work as expected.  The full
list is in the procedure index below.
<p>

When a C formatter encounters an object it doesn't know how to write
(including lists and records), it outputs them according to the
format state's current <code class=scheme>'<span class=variable>gen</span></code> variable.  This allows you to specify
generators for your own types, e.g. if you are using your own AST
records in a compiler.
<p>

If the <code class=scheme>'<span class=variable>gen</span></code> variable isn't set it defaults to the <code class=scheme><span class=variable>c-expr/sexp</span></code>
procedure, which formats an s-expression as if it were C code.  Thus
instead of <code class=scheme><span class=variable>c-apply</span></code> you can just use a list.  The full API is
available via normal s-expressions - formatters that aren't keywords
in C are prefixed with a % or otherwise made invalid C identifiers so
that they can't be confused with function application.
<p>

<a name="SECTION_6.2"><h2>6.2&nbsp;&nbsp;C Preprocessor Formatting</h2>

C preprocessor formatters also properly handle their surrounding
context, so you can safely intermix them in the normal flow of C
code.
<p>

<pre class=scheme>
  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-switch</span> '<span class=variable>y</span>
            (<span class=variable>c-case</span> <span class=number>1</span> (<span class=variable>c=</span> '<span class=variable>x</span> <span class=number>1</span>))
            (<span class=variable>cpp-ifdef</span> '<span class=variable>H_TWO</span> (<span class=variable>c-case</span> <span class=number>2</span> (<span class=variable>c=</span> '<span class=variable>x</span> <span class=number>4</span>)))
            (<span class=variable>c-default</span> (<span class=variable>c=</span> '<span class=variable>x</span> <span class=number>5</span>))))
</pre>
<p>

<pre>
  switch (y) {
      case 1:
          x = 1;
          break;

  #ifdef H_TWO
      case 2:
          x = 4;
          break;
  #endif /* H_TWO */
      default:
          x = 5;
          break;
  }
</pre>
<p>

Macros can be handled with <code class=scheme><span class=variable>cpp-define</span></code>, which knows to wrap
individual variable references in parenthesis:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>cpp-define</span> '(<span class=variable>min</span> <span class=variable>x</span> <span class=variable>y</span>) (<span class=variable>c-if</span> (<span class=variable>c&lt;</span> '<span class=variable>x</span> '<span class=variable>y</span>) '<span class=variable>x</span> '<span class=variable>y</span>)))</code>
<p>

<pre>
  #define min(x, y) (((x) &lt; (y)) ? (x) : (y))
</pre>
<p>

As with all C formatters, the CPP output is pretty printed as
needed, and if it wraps over several lines the lines are terminated
with a backslash.
<p>

To write a C header file that is included at most once, you can wrap
the entire body in <code class=scheme><span class=variable>cpp-wrap-header</span></code>:
<p>

<pre class=scheme>
  (<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>cpp-wrap-header</span> <span class=string>&quot;FOO_H&quot;</span>
            (<span class=variable>c-extern</span> (<span class=variable>c-prototype</span> '<span class=variable>int</span> '<span class=variable>foo</span> '()))))
</pre>
<p>

<pre>
  #ifndef FOO_H
  #define FOO_H

  extern int foo ();

  #endif /* ! FOO_H */
</pre>
<p>

<a name="SECTION_6.3"><h2>6.3&nbsp;&nbsp;Customizing C Style</h2>

The output uses a simplified K&amp;R style with 4 spaces for indentation
by default.  The following state variables let you override the
style:
<p>

<h3>'indent-space</h3>

how many spaces to indent bodies, default <code class=scheme><span class=number>4</span></code>
<p>

<h3>'switch-indent-space</h3>

how many spaces to indent switch clauses, also defaults to <code class=scheme><span class=number>4</span></code>
<p>

<h3>'newline-before-brace?</h3>

insert a newline before an open brace (non-K&amp;R), defaults  to <code class=scheme><span class=boolean>#f</span></code>
<p>

<h3>'braceless-bodies?</h3>

omit braces when we can prove they aren't needed
<p>

<h3>'non-spaced-ops?</h3>

omit spaces between operators and operands for groups of variables and
literals (e.g. &quot;a+b+3&quot; instead of &quot;a + b + 3&quot;}
<p>

<h3>'no-wrap?</h3>

Don't wrap function calls and long operator groups over mulitple
lines.  Functions and control structures will still use multiple
lines.
<p>

The C formatters also respect the <code class=scheme>'<span class=variable>radix</span></code> and <code class=scheme>'<span class=variable>precision</span></code> settings.
<p>

<a name="SECTION_6.4"><h2>6.4&nbsp;&nbsp;C Formatter Index</h2>

<h3>(c-if &lt;condition&gt; &lt;pass&gt; [&lt;fail&gt; [&lt;condition2&gt; &lt;pass2&gt; ...]])</h3>

Print a chain of if/else conditions.  Use a final condition of <code class=scheme>'<span class=keyword>else</span></code>
for a final else clause.
<p>

<h3>(c-for &lt;init&gt; &lt;condition&gt; &lt;update&gt; &lt;body&gt; ...)</h3>

<h3>(c-while &lt;condition&gt; &lt;body&gt; ...)</h3>

Basic loop constructs.
<p>

<h3>(c-fun &lt;type&gt; &lt;name&gt; &lt;params&gt; &lt;body&gt; ...)</h3>

<h3>(c-prototype &lt;type&gt; &lt;name&gt; &lt;params&gt;)</h3>

Output a function or function prototype.  The parameters should be a
list 2-element lists of the form <code class=scheme>(<span class=variable>&lt;param-type&gt;</span> <span class=variable>&lt;param-name&gt;</span>)</code>,
which are output with DSP.  A parameter can be abbreviated as just the
symbol name, or <code class=scheme><span class=boolean>#f</span></code> can be passed as the type, in which case the
<code class=scheme>'<span class=variable>default-type</span></code> state variable is used.  The parameters may be a
dotted list, in which case ellipses for a C variadic are inserted -
the actual name of the dotted value is ignored.
<p>

Types are just typically just symbols, or lists of symbols such as
<code class=scheme>'(<span class=variable>const</span> <span class=variable>char</span>)</code>.  A complete description is given below in section
<a href="#SECTION_6.6">6.6</a>.
<p>

These can also accessed as %fun and %prototype at the head of a list.
<p>

<h3>(c-var &lt;type&gt; &lt;name&gt; [&lt;init-value&gt;])</h3>

Declares and optionally initializes a variable.  Also accessed as %var
at the head of a list.
<p>

<h3>(c-begin &lt;expr&gt; ...)</h3>

Outputs each of the &lt;expr&gt;s, separated by semi-colons if in a
statement or commas if in an expression.
<p>

<h3>(c-switch &lt;clause&gt; ...)</h3>

<h3>(c-case &lt;values&gt; &lt;body&gt; ...)</h3>

<h3>(c-case/fallthrough &lt;values&gt; &lt;body&gt; ...)</h3>

<h3>(c-default &lt;body&gt; ...)</h3>

Switch statements.  In addition to using the clause formatters,
clauses inside a switch may be handled with a Scheme CASE-like list,
with the car a list of case values and the cdr the body.
<p>

<h3>(c-label &lt;name&gt;)</h3>

<h3>(c-goto &lt;name&gt;)</h3>

<h3>(c-return [&lt;result&gt;])</h3>

<h3>c-break</h3>

<h3>c-continue</h3>

Manual labels and jumps.  Labels can also be accessed as a list
beginning with a colon, e.g. <code class=scheme>'(<span class=constant>:</span> <span class=variable>label1</span>)</code>.
<p>

<h3>(c-const &lt;expr&gt;)</h3>

<h3>(c-static &lt;expr&gt;)</h3>

<h3>(c-volatile &lt;expr&gt;)</h3>

<h3>(c-restrict &lt;expr&gt;)</h3>

<h3>(c-register &lt;expr&gt;)</h3>

<h3>(c-auto &lt;expr&gt;)</h3>

<h3>(c-inline &lt;expr&gt;)</h3>

<h3>(c-extern &lt;expr&gt;)</h3>

Declaration modifiers.  May be nested.
<p>

<h3>(c-extern/C &lt;body&gt; ...)</h3>

Wraps body in an extern &quot;C&quot; { ... } for use with C++.
<p>

<h3>(c-cast &lt;type&gt; &lt;expr&gt;)</h3>

Casts an expression to a type.  Also %cast at the head of a list.
<p>

<h3>(c-typedef &lt;type&gt; &lt;new-name&gt; ...)</h3>

Creates a new type definition with one or more names.
<p>

<h3>(c-struct [&lt;name&gt;] &lt;field-list&gt; [&lt;attributes&gt;])</h3>

<h3>(c-union [&lt;name&gt;] &lt;field-list&gt; [&lt;attributes&gt;])</h3>

<h3>(c-class [&lt;name&gt;] &lt;field-list&gt; [&lt;attributes&gt;])</h3>

<h3>(c-attribute &lt;values&gt; ...)</h3>

Composite type constructors.  Attributes may be accessed as
%attribute at the head of a list.
<p>

<pre class=scheme>
  (<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>c-struct</span> '<span class=variable>employee</span>
                      '((<span class=variable>short</span> <span class=variable>age</span>)
                        ((<span class=variable>char</span> <span class=global>*</span>) <span class=variable>name</span>)
                        ((<span class=variable>struct</span> (<span class=variable>year</span> <span class=variable>month</span> <span class=variable>day</span>)) <span class=variable>dob</span>))
                      (<span class=variable>c-attribute</span> '<span class=variable>packed</span>)))
</pre>
<p>

<pre>
  struct employee {
      short age;
      char* name;
      struct {
          int year;
          int month;
          int day;
      } dob;
  } __attribute__ ((packed));
</pre>
<p>

<h3>(c-enum [&lt;name&gt;] &lt;enum-list&gt;)</h3>

Enumerated types.  <code class=scheme><span class=variable>&lt;enum-list&gt;</span></code> may be strings, symbols, or lists of
string or symbol followed by the enum's value.
<p>

<h3>(c-comment &lt;formatter&gt; ...)</h3>

Outputs the <code class=scheme><span class=variable>&lt;formatter&gt;</span></code>s wrapped in C's /* ... */ comment.  Properly
escapes nested comments inside in an Emacs-friendly style.
<p>

<a name="SECTION_6.5"><h2>6.5&nbsp;&nbsp;C Preprocessor Formatter Index</h2>

<h3>(cpp-include &lt;file&gt;)</h3>

If file is a string, outputs in it &quot;quotes&quot;, otherwise (as a symbol
or arbitrary formatter) it outputs it in brackets.
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>cpp-include</span> '<span class=variable>stdio.h</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;#include &lt;stdio.h&gt;n&quot;</span></code>
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#f</span> (<span class=variable>cpp-include</span> <span class=string>&quot;config.h&quot;</span>))</code>
<p>

<code class=scheme><span class=keyword>=&gt;</span> <span class=string>&quot;#include &quot;config.h&quot;n&quot;</span></code>
<p>

<h3>(cpp-define &lt;macro&gt; [&lt;value&gt;])</h3>

Defines a preprocessor macro, which may be just a name or a list of
name and parameters.  Properly wraps the value in parenthesis and
escapes newlines.  A dotted parameter list will use the C99 variadic
macro syntax, and will also substitute any references to the dotted
name with <code>__VA_ARGS__</code>:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>cpp-define</span> '(<span class=variable>eprintf</span> . <span class=variable>args</span>) '(<span class=variable>fprintf</span> <span class=variable>stderr</span> <span class=variable>args</span>)))</code>
<p>

<pre>
  #define eprintf(...) (fprintf(stderr, __VA_ARGS__))
</pre>
<p>

<h3>(cpp-if &lt;condition&gt; &lt;pass&gt; [&lt;fail&gt; ...])</h3>

<h3>(cpp-ifdef &lt;condition&gt; &lt;pass&gt; [&lt;fail&gt; ...])</h3>

<h3>(cpp-ifndef &lt;condition&gt; &lt;pass&gt; [&lt;fail&gt; ...])</h3>

<h3>(cpp-elif &lt;condition&gt; &lt;pass&gt; [&lt;fail&gt; ...])</h3>

<h3>(cpp-else &lt;body&gt; ...)</h3>

Conditional compilation.
<p>

<h3>(cpp-line &lt;num&gt; [&lt;file&gt;])</h3>

Line number information.
<p>

<h3>(cpp-pragma &lt;args&gt; ...)</h3>

<h3>(cpp-error &lt;args&gt; ...)</h3>

<h3>(cpp-warning &lt;args&gt; ...)</h3>

Additional preprocessor directives.
<p>

<h3>(cpp-stringify &lt;expr&gt;)</h3>

Stringifies <code class=scheme><span class=variable>&lt;expr&gt;</span></code> by prefixing the # operator.
<p>

<h3>(cpp-sym-cat &lt;args&gt; ...)</h3>

Joins the <code class=scheme><span class=variable>&lt;args&gt;</span></code> into a single preprocessor token with the ##
operator.
<p>

<h3>(cpp-wrap-header &lt;name&gt; &lt;body&gt; ...)</h3>

Wrap an entire header to only be included once.
<p>

<h3>Operators:</h3>

<pre class=scheme>
<span class=variable>c++</span> <span class=variable>c--</span> <span class=variable>c+</span> <span class=variable>c-</span> <span class=variable>c*</span> <span class=variable>c/</span> <span class=variable>c%</span> <span class=variable>c&amp;</span> <span class=variable>c^</span> <span class=variable>c~</span> <span class=variable>c!</span> <span class=variable>c&amp;&amp;</span> <span class=variable>c&lt;&lt;</span> <span class=variable>c&gt;&gt;</span> <span class=variable>c==</span> <span class=variable>c!=</span>
<span class=variable>c&lt;</span> <span class=variable>c&gt;</span> <span class=variable>c&lt;=</span> <span class=variable>c&gt;=</span> <span class=variable>c=</span> <span class=variable>c+=</span> <span class=variable>c-=</span> <span class=variable>c*=</span> <span class=variable>c/=</span> <span class=variable>c%=</span> <span class=variable>c&amp;=</span> <span class=variable>c^=</span> <span class=variable>c&lt;&lt;=</span> <span class=variable>c&gt;&gt;=</span>
<span class=variable>c++/post</span> <span class=variable>c--/post</span> <span class=variable>c-or</span> <span class=variable>c-bit-or</span> <span class=variable>c-bit-or=</span>
</pre>
<p>

<a name="SECTION_6.6"><h2>6.6&nbsp;&nbsp;C Types</h2>


<p>

Typically a type is just a symbol such as <code class=scheme>'<span class=variable>char</span></code> or <code class=scheme>'<span class=variable>int</span></code>.  You
can wrap types with modifiers such as <code class=scheme><span class=variable>c-const</span></code>, but as a
convenience you can just use a list such as in <code class=scheme>'(<span class=variable>const</span> <span class=variable>unsignedchar</span> <span class=global>*</span>)</code>.
You can also nest these lists, so the previous example is
equivalent to <code class=scheme>'(<span class=global>*</span> (<span class=variable>const</span> (<span class=variable>unsigned</span> <span class=variable>char</span>)))</code>.
<p>

Pointers may be written as <code class=scheme>'(<span class=variable>%pointer</span> <span class=variable>&lt;type&gt;</span>)</code> for readability -
<code class=scheme><span class=variable>%pointer</span></code> is exactly equivalent to <code class=scheme><span class=global>*</span></code> in types.
<p>

Unamed structs, classes, unions and enums may be used directly as
types, using their respective keywords at the head of a list.
<p>

Two special types are the %array type and function pointer type.  An
array is written:
<p>

<code class=scheme>(<span class=variable>%array</span> <span class=variable>&lt;type&gt;</span> [<span class=variable>&lt;size&gt;</span>])</code>
<p>

where <code class=scheme><span class=variable>&lt;type&gt;</span></code> is any other type (including another array or
function pointer), and <code class=scheme><span class=variable>&lt;size&gt;</span></code>, if given, will print the array
size.  For example:
<p>

<code class=scheme>(<span class=variable>c-var</span> '(<span class=variable>%array</span> (<span class=variable>unsigned</span> <span class=variable>long</span>) <span class=variable>SIZE</span>) '<span class=variable>table</span> '<span class=constant>#</span>(<span class=number>1</span> <span class=number>2</span> <span class=number>3</span> <span class=number>4</span>))</code>
<p>

<pre>
unsigned long table[SIZE] = {1, 2, 3, 4};
</pre>
<p>

A function pointer is written:
<p>

<code class=scheme>(<span class=variable>%fun</span> <span class=variable>&lt;return-type&gt;</span> (<span class=variable>&lt;param-types&gt;</span> ...))</code>
<p>

For example:
<p>

<code class=scheme>(<span class=variable>c-typedef</span> '(<span class=variable>%fun</span> <span class=variable>double</span> (<span class=variable>double</span> <span class=variable>double</span> <span class=variable>int</span>)) '<span class=variable>f</span>)</code>
<p>

<pre>
typedef double (*f)(double, double, int);
</pre>
<p>

Wherever a type is expected but not given, the value of the
<code class=scheme>'<span class=variable>default-type</span></code> formatting state variable is used.  By default this
is just <code class=scheme>'<span class=variable>int</span></code>.
<p>

Type declarations work uniformly for variables and parameters, as well
for casts and typedefs.
<p>

<a name="SECTION_6.7"><h2>6.7&nbsp;&nbsp;C as S-Expressions</h2>


<p>

Rather than building formatting closures by hand, it can be more
convenient to just build a normal s-expression and ask for it to be
formatted as C code.  This can be thought of as a simple Scheme-&gt;C
compiler without any runtime support.
<p>

In a s-expression, strings and characters are printed as C strings and
characters, booleans are printed as 0 or 1, symbols are displayed
as-is, and numbers are printed as C numbers (using the current
formatting radix if specified).  Vectors are printed as
comma-separated lists wrapped in braces, which can be used for
initializing arrays or structs.
<p>

A list indicates a C expression or statement.  Any of the existing C
keywords can be used to pretty-print the expression as described with
the c-keyword formatters above.  Thus, the example above
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-if</span> (<span class=variable>c-if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>))</code>
<p>

could also be written
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-expr</span> '(<span class=keyword>if</span> (<span class=keyword>if</span> <span class=number>1</span> <span class=number>2</span> <span class=number>3</span>) <span class=number>4</span> <span class=number>5</span>)))</code>
<p>

C constructs that are dependent on the underlying syntax and have no
keyword are written with a % prefix (<code class=scheme><span class=variable>%fun</span></code>, <code class=scheme><span class=variable>%var</span></code>, <code class=scheme><span class=variable>%pointer</span></code>,
<code class=scheme><span class=variable>%array</span></code>, <code class=scheme><span class=variable>%cast</span></code>), including C preprocessor constructs
(<code class=scheme><span class=variable>%include</span></code>, <code class=scheme><span class=variable>%define</span></code>, <code class=scheme><span class=variable>%pragma</span></code>, <code class=scheme><span class=variable>%error</span></code>, <code class=scheme><span class=variable>%warning</span></code>,
<code class=scheme><span class=variable>%if</span></code>, <code class=scheme><span class=variable>%ifdef</span></code>, <code class=scheme><span class=variable>%ifndef</span></code>, <code class=scheme><span class=variable>%elif</span></code>).  Labels are written as
<code class=scheme>(<span class=constant>:</span> <span class=variable>&lt;label-name&gt;</span>)</code>.  You can write a sequence as <code class=scheme>(<span class=variable>%begin</span> <span class=variable>&lt;expr&gt;</span>
...)</code>.
<p>

For example, the following definition of the fibonacci sequence, which
apart from the return type of <code class=scheme><span class=boolean>#f</span></code> looks like a Lisp definition:
<p>

<code class=scheme>(<span class=variable>fmt</span> <span class=boolean>#t</span> (<span class=variable>c-expr</span> '(<span class=variable>%fun</span> <span class=boolean>#f</span> <span class=variable>fib</span> (<span class=variable>n</span>)
                         (<span class=keyword>if</span> (<span class=variable>&lt;=</span> <span class=variable>n</span> <span class=number>1</span>)
                             <span class=number>1</span>
                             (<span class=variable>+</span> (<span class=variable>fib</span> (<span class=variable>-</span> <span class=variable>n</span> <span class=number>1</span>)) (<span class=variable>fib</span> (<span class=variable>-</span> <span class=variable>n</span> <span class=number>2</span>)))))))</code>
<p>

prints the working C definition:
<p>

<pre>
int fib (int n) {
    if (n &lt;= 1) {
        return 1;
    } else {
        return fib((n - 1)) + fib((n - 2));
    }
}
</pre>
<p>

<a name="SECTION_7"><h1>7&nbsp;&nbsp;JavaScript Formatting</h1>

The experimental fmt-js library extends the fmt-c library with
functionality for formatting JavaScript code.
<p>

<h3>(js-expr x)</h3>

Formats a JavaScript expression similarly to <code class=scheme><span class=variable>c-expr</span></code>.  Inside a
<code class=scheme><span class=variable>js-expr</span></code> formatter, you can use the normal <code class=scheme><span class=variable>c-</span></code> prefixed
formatters described in the previous section, and they will format
appropriately for JavaScript.
<p>

Currently expressions will all be terminated with a semi-colon, but
that will be made optional in a later release.
<p>

<h3>(js-function [&lt;name&gt;] (&lt;params&gt;) &lt;body&gt; ...)</h3>

Defines a function (anonymously if no name is provided).
<p>

<h3>(js-var &lt;name&gt; [&lt;init-value&gt;])</h3>

Declares a JavaScript variable, optionally with an initial value.
<p>

<h3>(js-comment &lt;formatter&gt; ...)</h3>

Formats a comment prefixing lines with <code class=scheme><span class=string>&quot;// &quot;</span></code>.
<p>

<a name="SECTION_8"><h1>8&nbsp;&nbsp;Formatting with Color</h1>

The fmt-color library provides the following utilities:
<p>

<pre class=scheme>
  (<span class=variable>fmt-red</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-blue</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-green</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-cyan</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-yellow</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-magenta</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-white</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-black</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-bold</span> <span class=variable>&lt;formatter&gt;</span> ...)
  (<span class=variable>fmt-underline</span> <span class=variable>&lt;formatter&gt;</span> ...)
</pre>
<p>

and more generally
<p>

<code class=scheme>(<span class=variable>fmt-color</span> <span class=variable>&lt;color&gt;</span> <span class=variable>&lt;formatter&gt;</span> ...)</code>
<p>

where color can be a symbol name or <code class=scheme><span class=constant>#xRRGGBB</span></code> numeric value.
Outputs the formatters colored with ANSI escapes.  In addition
<p>

<code class=scheme>(<span class=variable>fmt-in-html</span> <span class=variable>&lt;formatter&gt;</span> ...)</code>
<p>

can be used to mark the format state as being inside HTML, which the
above color formats will understand and output HTML <code class=scheme><span class=variable>&lt;span&gt;</span></code> tags with
the appropriate style colors, instead of ANSI escapes.
<p>

<a name="SECTION_9"><h1>9&nbsp;&nbsp;Unicode</h1>

The fmt-unicode library provides the <code class=scheme><span class=variable>fmt-unicode</span></code> formatter, which
just takes a list of formatters and overrides the string-length for
padding and trimming, such that Unicode double or full width
characters are considered 2 characters wide (as they typically are in
fixed-width terminals), while treating combining and non-spacing
characters as 0 characters wide.
<p>

It also recognizes and ignores ANSI escapes, in particular useful if
you want to combine this with the fmt-color utilities.
<p>

<a name="SECTION_10"><h1>10&nbsp;&nbsp;Optimizing</h1>

The library is designed for scalability and flexibility, not speed,
and I'm not going to think about any fine tuning until it's more
stabilised.  One aspect of the design, however, was influenced for the
sake of future optimizations, which is that none of the default format
variables are initialized by global parameters, which leaves room for
inlining and subsequent simplification of format calls.
<p>

If you don't have an aggressively optimizing compiler, you can easily
achieve large speedups on common cases with CL-style compiler macros.
<p>

<a name="SECTION_11"><h1>11&nbsp;&nbsp;Common Lisp Format Cheat Sheet</h1>

A quick reference for those of you switching over from Common Lisp's
format.
<p>

<table>
<tr><td><strong>format</strong> </td><td> <strong>fmt</strong></td></tr>
<tr><td>~a </td><td> <code class=scheme><span class=variable>dsp</span></code></td></tr>
<tr><td>~c </td><td> <code class=scheme><span class=variable>dsp</span></code></td></tr>
<tr><td>~s </td><td> <code class=scheme><span class=variable>wrt/unshared</span></code></td></tr>
<tr><td>~w </td><td> <code class=scheme><span class=variable>wrt</span></code></td></tr>
<tr><td>~y </td><td> <code class=scheme><span class=variable>pretty</span></code></td></tr>
<tr><td>~x </td><td> <code class=scheme>(<span class=variable>radix</span> <span class=number>16</span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable>&lt;n&gt;</span> <span class=number>16</span>)</code></td></tr>
<tr><td>~o </td><td> <code class=scheme>(<span class=variable>radix</span> <span class=number>8</span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable>&lt;n&gt;</span> <span class=number>8</span>)</code></td></tr>
<tr><td>~b </td><td> <code class=scheme>(<span class=variable>radix</span> <span class=number>2</span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable>&lt;n&gt;</span> <span class=number>2</span>)</code></td></tr>
<tr><td>~f </td><td> <code class=scheme>(<span class=variable>fix</span> <span class=variable>&lt;digits&gt;</span> ...)</code> or <code class=scheme>(<span class=variable>num</span> <span class=variable>&lt;n&gt;</span> <span class=variable>&lt;radix&gt;</span> <span class=variable>&lt;digits&gt;</span>)</code></td></tr>
<tr><td>~% </td><td> <code class=scheme><span class=variable>nl</span></code></td></tr>
<tr><td>~&amp; </td><td> <code class=scheme><span class=variable>fl</span></code></td></tr>
<tr><td>~[...~] </td><td> normal <code class=scheme><span class=keyword>if</span></code> or <code class=scheme><span class=variable>fmt-if</span></code> (delayed test)</td></tr>
<tr><td>~{...~} </td><td> <code class=scheme>(<span class=variable>fmt-join</span> ... <span class=variable>&lt;list&gt;</span> [<span class=variable>&lt;sep&gt;</span>])</code></td></tr>
</table>
<p>

<a name="SECTION_12"><h1>12&nbsp;&nbsp;References</h1>

<a name="BIBITEM_1">[1]&nbsp; R. Kelsey, W. Clinger, J. Rees (eds.)
<a href="http://www.schemers.org/Documents/Standards/R5RS/">Revised^5 Report on the Algorithmic Language Scheme</a>
<p>

<a name="BIBITEM_2">[2]&nbsp; Guy L. Steele Jr. (editor)
<a href="http://www.harlequin.com/education/books/HyperSpec/">Common Lisp Hyperspec</a>
<p>

<a name="BIBITEM_3">[3]&nbsp; Scott G. Miller
<a href="http://srfi.schemers.org/srfi-28/">SRFI-28 Basic Format Strings</a>
<p>

<a name="BIBITEM_4">[4]&nbsp; Ken Dickey
<a href="http://srfi.schemers.org/srfi-48/">SRFI-48 Intermediate Format Strings</a>
<p>

<a name="BIBITEM_5">[5]&nbsp; Ray Dillinger
<a href="http://srfi.schemers.org/srfi-38/">SRFI-38 External Representation for Data With Shared Structure</a>
<p>

<a name="BIBITEM_6">[6]&nbsp; Damian Conway
<a href="http://www.perl.com/lpt/a/819">Perl6 Exegesis 7 - formatting</a>
<p>

<br /><br /><br /><br />
<!-- page created by Mistie, http://www.cs.rice.edu/~dorai/mistie/ -->
</body></html>

Added fmt/fmt.scm.























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
;;;; fmt.scm -- extensible formatting library
;;
;; Copyright (c) 2006-2009 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

;; (require-extension (srfi 1 6 13 23 69))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string utilities

(define (write-to-string x)
  (call-with-output-string (lambda (p) (write x p))))

(define (display-to-string x)
  (if (string? x)
      x
      (call-with-output-string (lambda (p) (display x p)))))

(define nl-str
  (call-with-output-string newline))

(define (make-space n) (make-string n #\space))
(define (make-nl-space n) (string-append nl-str (make-string n #\space)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; list utilities

(define (take* ls n)   ; handles dotted lists and n > length
  (cond ((zero? n) '())
        ((pair? ls) (cons (car ls) (take* (cdr ls) (- n 1))))
        (else '())))

(define (drop* ls n)   ; may return the dot
  (cond ((zero? n) ls)
        ((pair? ls) (drop* (cdr ls) (- n 1)))
        (else ls)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; format state representation

;; Use a flexible representation optimized for common cases -
;; frequently accessed values are in fixed vector slots, with a
;; `properties' slot holding an alist for all other values.

(define *default-fmt-state*
  (vector 0 0 10 '() #\space #f 78 #f #f #f #f #f #f))

(define fmt-state? vector?)

(define (new-fmt-state . o)
  (let ((st (if (pair? o) (car o) (current-output-port))))
    (if (vector? st)
        st
        (fmt-set-writer!
         (fmt-set-port! (copy-fmt-state *default-fmt-state*) st)
         fmt-write))))

(define (copy-fmt-state st)
  (let* ((len (vector-length st))
         (res (make-vector len)))
    (do ((i 0 (+ i 1)))
        ((= i len))
      (vector-set! res i (vector-ref st i)))
    (fmt-set-properties! res (map (lambda (x) (cons (car x) (cdr x)))
                                  (fmt-properties res)))
    res))

(define (fmt-row st) (vector-ref st 0))
(define (fmt-col st) (vector-ref st 1))
(define (fmt-radix st) (vector-ref st 2))
(define (fmt-properties st) (vector-ref st 3))
(define (fmt-pad-char st) (vector-ref st 4))
(define (fmt-precision st) (vector-ref st 5))
(define (fmt-width st) (vector-ref st 6))
(define (fmt-writer st) (vector-ref st 7))
(define (fmt-port st) (vector-ref st 8))
(define (fmt-decimal-sep st) (vector-ref st 9))
(define (fmt-decimal-align st) (vector-ref st 10))
(define (fmt-string-width st) (vector-ref st 11))
(define (fmt-ellipses st) (vector-ref st 12))

(define (fmt-set-row! st x) (vector-set! st 0 x) st)
(define (fmt-set-col! st x) (vector-set! st 1 x) st)
(define (fmt-set-radix! st x) (vector-set! st 2 x) st)
(define (fmt-set-properties! st x) (vector-set! st 3 x) st)
(define (fmt-set-pad-char! st x) (vector-set! st 4 x) st)
(define (fmt-set-precision! st x) (vector-set! st 5 x) st)
(define (fmt-set-width! st x) (vector-set! st 6 x) st)
(define (fmt-set-writer! st x) (vector-set! st 7 x) st)
(define (fmt-set-port! st x) (vector-set! st 8 x) st)
(define (fmt-set-decimal-sep! st x) (vector-set! st 9 x) st)
(define (fmt-set-decimal-align! st x) (vector-set! st 10 x) st)
(define (fmt-set-string-width! st x) (vector-set! st 11 x) st)
(define (fmt-set-ellipses! st x) (vector-set! st 12 x) st)

(define (fmt-ref st key . o)
  (case key
    ((row) (fmt-row st))
    ((col) (fmt-col st))
    ((radix) (fmt-radix st))
    ((properties) (fmt-properties st))
    ((writer) (fmt-writer st))
    ((port) (fmt-port st))
    ((precision) (fmt-precision st))
    ((pad-char) (fmt-pad-char st))
    ((width) (fmt-width st))
    ((decimal-sep) (fmt-decimal-sep st))
    ((decimal-align) (fmt-decimal-align st))
    ((string-width) (fmt-string-width st))
    ((ellipses) (fmt-ellipses st))
    (else (cond ((assq key (fmt-properties st)) => cdr)
                ((pair? o) (car o))
                (else #f)))))

(define (fmt-set-property! st key val)
  (cond ((assq key (fmt-properties st))
         => (lambda (cell) (set-cdr! cell val) st))
        (else (fmt-set-properties!
               st
               (cons (cons key val) (fmt-properties st))))))

(define (fmt-set! st key val)
  (case key
    ((row) (fmt-set-row! st val))
    ((col) (fmt-set-col! st val))
    ((radix) (fmt-set-radix! st val))
    ((properties) (fmt-set-properties! st val))
    ((pad-char) (fmt-set-pad-char! st val))
    ((precision) (fmt-set-precision! st val))
    ((writer) (fmt-set-writer! st val))
    ((port) (fmt-set-port! st val))
    ((width) (fmt-set-width! st val))
    ((decimal-sep) (fmt-set-decimal-sep! st val))
    ((decimal-align) (fmt-set-decimal-align! st val))
    ((string-width) (fmt-set-string-width! st val))
    ((ellipses) (fmt-set-ellipses! st val))
    (else (fmt-set-property! st key val))))

(define (fmt-add-properties! st alist)
  (for-each (lambda (x) (fmt-set! st (car x) (cdr x))) alist)
  st)

(define (fmt-let key val . ls)
  (lambda (st)
    (let ((orig-val (fmt-ref st key)))
      (fmt-set! ((apply-cat ls) (fmt-set! st key val)) key orig-val))))

(define (fmt-bind key val . ls)
  (lambda (st) ((apply-cat ls) (fmt-set! st key val))))

(define (fix prec . ls) (fmt-let 'precision prec (apply-cat ls)))
(define (radix rad . ls) (fmt-let 'radix rad (apply-cat ls)))
(define (pad-char ch . ls) (fmt-let 'pad-char ch (apply-cat ls)))
(define (comma-char ch . ls) (fmt-let 'comma-char ch (apply-cat ls)))
(define (decimal-char ch . ls) (fmt-let 'decimal-sep ch (apply-cat ls)))
(define (decimal-align n . ls) (fmt-let 'decimal-align n (apply-cat ls)))
(define (with-width w . ls) (fmt-let 'width w (apply-cat ls)))
(define (ellipses ell . ls) (fmt-let 'ellipses ell (apply-cat ls)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; the basic interface

(define (fmt-start st initializer proc)
  (cond
    ((or (output-port? st) (fmt-state? st))
     (proc (initializer st))
     (if #f #f))
    ((eq? #t st)
     (proc (initializer (current-output-port)))
     (if #f #f))
    ((eq? #f st)
     (get-output-string
      (fmt-port (proc (initializer (open-output-string))))))
    (else (error "unknown format output" st))))

(define (fmt st . args)
  (fmt-start st new-fmt-state (apply-cat args)))

(define (fmt-update str st)
  (let ((len (string-length str))
        (nli (string-index-right str #\newline))
        (str-width (fmt-string-width st)))
    (if nli
        (let ((row (+ (fmt-row st) 1 (string-count str #\newline 0 nli))))
          (fmt-set-row!
           (fmt-set-col! st (if str-width
                                (str-width str (+ nli 1) len)
                                (- len (+ nli 1))))
           row))
        (fmt-set-col! st (+ (fmt-col st)
                            (if str-width
                                (str-width str 0 len)
                                len))))))

(define (fmt-write str st)
  (display str (fmt-port st))
  (fmt-update str st))

(define (apply-cat procs)
  (lambda (st)
    (let loop ((ls procs) (st st))
      (if (null? ls)
          st
          (loop (cdr ls) ((dsp (car ls)) st))))))

(define (cat . ls) (apply-cat ls))

(define (fmt-null st) st)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; control structures

(define (fmt-if check pass . o)
  (let ((fail (if (pair? o) (car o) (lambda (x) x))))
    (lambda (st) (if (check st) ((dsp pass) st) ((dsp fail) st)))))

(define (fmt-try-fit proc . fail)
  (if (null? fail)
      proc
      (lambda (orig-st)
        (let ((width (fmt-width orig-st))
              (buffer '()))
          (call-with-current-continuation
            (lambda (return)
              (define (output* str st)
                (let lp ((i 0) (col (fmt-col st)))
                  (let ((nli (string-index str #\newline i)))
                    (if nli
                        (if (> (+ (- nli i) col) width)
                            (return ((apply fmt-try-fit fail) orig-st))
                            (lp (+ nli 1) 0))
                        (let* ((len ((or (fmt-string-width st) string-length)
                                     str))
                               (col (+ (- len i) col)))
                          (if (> col width)
                              (return ((apply fmt-try-fit fail) orig-st))
                              (begin
                                (set! buffer (cons str buffer))
                                (fmt-update str st))))))))
              (proc (fmt-set-port! (fmt-set-writer! (copy-fmt-state orig-st)
                                                    output*)
                                   (open-output-string)))
              ((fmt-writer orig-st)
               (string-concatenate-reverse buffer)
               orig-st)))))))

(define (fits-in-width gen width)
  (lambda (st)
    (let ((output (fmt-writer st))
          (port (open-output-string)))
      (call-with-current-continuation
        (lambda (return)
          (define (output* str st)
            (let ((st (fmt-update str st)))
              (if (> (fmt-col st) width)
                  (return #f)
                  (begin
                    (display str port)
                    st))))
          (gen (fmt-set-port! (fmt-set-writer! (copy-fmt-state st) output*)
                              port))
          (get-output-string port))))))

(define (fits-in-columns ls write width)
  (lambda (st)
    (let ((max-w (quotient width 2)))
      (let lp ((ls ls) (res '()) (widest 0))
        (cond
          ((pair? ls)
           (let ((str ((fits-in-width (write (car ls)) max-w) st)))
             (and str
                  (lp (cdr ls)
                      (cons str res)
                      (max ((or (fmt-string-width st) string-length) str)
                           widest)))))
          ((null? ls) (cons widest (reverse res)))
          (else #f))))))

(define (fmt-capture producer consumer)
  (lambda (st)
    (let ((port (open-output-string)))
      (producer (fmt-set-writer! (fmt-set-port! (copy-fmt-state st) port)
                                 fmt-write))
      ((consumer (get-output-string port)) st))))

(define (fmt-to-string producer)
  (fmt-capture producer (lambda (str) (lambda (st) str))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; standard formatters

(define (nl st)
  ((fmt-writer st) nl-str st))

;; output a newline iff we're not at the start of a fresh line
(define (fl st)
  (if (zero? (fmt-col st)) st (nl st)))

;; tab to a given tab-stop
(define (tab-to . o)
  (lambda (st)
    (let* ((tab-width (if (pair? o) (car o) 8))
           (rem (modulo (fmt-col st) tab-width)))
      (if (positive? rem)
          ((fmt-writer st)
           (make-string (- tab-width rem) (fmt-pad-char st))
           st)
          st))))

;; move to an explicit column
(define (space-to col)
  (lambda (st)
    (let ((width (- col (fmt-col st))))
      (if (positive? width)
          ((fmt-writer st) (make-string width (fmt-pad-char st)) st)
          st))))

(define (fmt-join fmt ls . o)
  (let ((sep (dsp (if (pair? o) (car o) ""))))
    (lambda (st)
      (if (null? ls)
          st
          (let lp ((ls (cdr ls))
                   (st ((fmt (car ls)) st)))
            (if (null? ls)
                st
                (lp (cdr ls) ((fmt (car ls)) (sep st)))))))))

(define (fmt-join/prefix fmt ls . o)
  (if (null? ls)
      fmt-null
      (let ((sep (dsp (if (pair? o) (car o) ""))))
        (cat sep (fmt-join fmt ls sep)))))
(define (fmt-join/suffix fmt ls . o)
  (if (null? ls)
      fmt-null
      (let ((sep (dsp (if (pair? o) (car o) ""))))
        (cat (fmt-join fmt ls sep) sep))))

(define (fmt-join/last fmt fmt/last ls . o)
  (let ((sep (dsp (if (pair? o) (car o) ""))))
    (lambda (st)
      (cond
        ((null? ls)
         st)
        ((null? (cdr ls))
         ((fmt/last (car ls)) (sep st)))
        (else
         (let lp ((ls (cdr ls))
                  (st ((fmt (car ls)) st)))
           (if (null? (cdr ls))
               ((fmt/last (car ls)) (sep st))
               (lp (cdr ls) ((fmt (car ls)) (sep st))))))))))

(define (fmt-join/dot fmt fmt/dot ls . o)
  (let ((sep (dsp (if (pair? o) (car o) ""))))
    (lambda (st)
      (cond
        ((pair? ls)
         (let lp ((ls (cdr ls))
                  (st ((fmt (car ls)) st)))
           (cond
             ((null? ls) st)
             ((pair? ls) (lp (cdr ls) ((fmt (car ls)) (sep st))))
             (else ((fmt/dot ls) (sep st))))))
        ((null? ls) st)
        (else ((fmt/dot ls) st))))))

(define (fmt-join/range fmt start . o)
  (let-optionals* o ((end #f) (sep ""))
    (lambda (st)
      (let lp ((i (+ start 1)) (st ((fmt start) st)))
        (if (and end (>= i end))
            st
            (lp (+ i 1) ((fmt i) ((dsp sep) st))))))))

(define (pad/both width . ls)
  (fmt-capture
   (apply-cat ls)
   (lambda (str)
     (lambda (st)
       (let ((diff (- width ((or (fmt-string-width st) string-length) str)))
             (output (fmt-writer st)))
         (if (positive? diff)
             (let* ((diff/2 (quotient diff 2))
                    (left (make-string diff/2 (fmt-pad-char st)))
                    (right (if (even? diff)
                               left
                               (make-string (+ 1 diff/2) (fmt-pad-char st)))))
               (output right (output str (output left st))))
             (output str st)))))))

(define (pad width . ls)
  (lambda (st)
    (let* ((col (fmt-col st))
           (padder
            (lambda (st)
              (let ((diff (- width (- (fmt-col st) col))))
                (if (positive? diff)
                    ((fmt-writer st) (make-string diff (fmt-pad-char st)) st)
                    st)))))
      ((cat (apply-cat ls) padder) st))))

(define pad/right pad)

(define (pad/left width . ls)
  (fmt-capture
   (apply-cat ls)
   (lambda (str)
     (lambda (st)
       (let* ((str-width ((or (fmt-string-width st) string-length) str))
              (diff (- width str-width)))
         ((fmt-writer st)
          str
          (if (positive? diff)
              ((fmt-writer st) (make-string diff (fmt-pad-char st)) st)
              st)))))))

(define (trim/buffered width fmt proc)
  (fmt-capture
   fmt
   (lambda (str)
     (lambda (st)
       (let* ((str-width ((or (fmt-string-width st) string-length) str))
              (diff (- str-width width)))
         ((fmt-writer st)
          (if (positive? diff)
              (proc str str-width diff st)
              str)
          st))))))

(define (trim width . ls)
  (lambda (st)
    (let ((ell (fmt-ellipses st)))
      (if ell
          ((trim/buffered
            width
            (apply-cat ls)
            (lambda (str str-width diff st)
              (let* ((ell (if (char? ell) (string ell) ell))
                     (ell-len (string-length ell))
                     (diff (- (+ str-width ell-len) width)))
                (if (negative? diff)
                    ell
                    (string-append
                     (substring/shared str 0 (- (string-length str) diff))
                     ell)))))
           st)
          (let ((output (fmt-writer st))
                (start-col (fmt-col st)))
            (call-with-current-continuation
              (lambda (return)
                (define (output* str st)
                  (let* ((len ((or (fmt-string-width st) string-length) str))
                         (diff (- (+ (- (fmt-col st) start-col) len) width)))
                    (if (positive? diff)
                        (return
                         (fmt-set-writer!
                          (output (substring/shared str 0 (- len diff)) st)
                          output))
                        (output str st))))
                ((fmt-let 'writer output* (apply-cat ls)) st))))))))

(define (trim/length width . ls)
  (lambda (st)
    (call-with-current-continuation
      (lambda (return)
        (let ((output (fmt-writer st))
              (sum 0))
          (define (output* str st)
            (let ((len (string-length str)))
              (set! sum (+ sum len))
              (if (> sum width)
                  (return
                   (fmt-set-writer!
                    (output (substring/shared str 0 (- len (- sum width))) st)
                    output))
                  (output str st))))
          ((fmt-let 'writer output* (apply-cat ls)) st))))))

(define (trim/left width . ls)
  (trim/buffered
   width
   (apply-cat ls)
   (lambda (str str-width diff st)
     (let ((ell (fmt-ellipses st)))
       (if ell
           (let* ((ell (if (char? ell) (string ell) ell))
                  (ell-len (string-length ell))
                  (diff (- (+ str-width ell-len) width)))
             (if (negative? diff)
                 ell
                 (string-append ell (substring/shared str diff))))
           (substring/shared str diff))))))

(define (trim/both width . ls)
  (trim/buffered
   width
   (apply-cat ls)
   (lambda (str str-width diff st)
     (let ((ell (fmt-ellipses st)))
       (if ell
           (let* ((ell (if (char? ell) (string ell) ell))
                  (ell-len (string-length ell))
                  (diff (- (+ str-width ell-len ell-len) width))
                  (left (quotient diff 2))
                  (right (- (string-length str) (quotient (+ diff 1) 2))))
             (if (negative? diff)
                 ell
                 (string-append ell (substring/shared str left right) ell)))
           (substring/shared str
                             (quotient (+ diff 1) 2)
                             (- (string-length str) (quotient diff 2))))))))

(define (fit width . ls)
  (pad width (trim width (apply-cat ls))))
(define (fit/left width . ls)
  (pad/left width (trim/left width (apply-cat ls))))
(define (fit/both width . ls)
  (pad/both width (trim/both width (apply-cat ls))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; String-map formatters

(define (make-string-fmt-transformer proc)
  (lambda ls
    (lambda (st)
      (let ((base-writer (fmt-writer st)))
        ((fmt-let
          'writer (lambda (str st) (base-writer (proc str) st))
          (apply-cat ls))
         st)))))

(define upcase (make-string-fmt-transformer string-upcase))
(define downcase (make-string-fmt-transformer string-downcase))
(define titlecase (make-string-fmt-transformer string-titlecase))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Numeric formatting

(define *min-e* -1024)
(define *bot-f* (expt 2 52))
;;(define *top-f* (* 2 *bot-f*))

(define (integer-log a base)
  (if (zero? a)
      0
      (inexact->exact (ceiling (/ (log (+ a 1)) (log base))))))
(define (integer-length* a)
  (if (negative? a)
      (integer-log (- 1 a) 2)
      (integer-log a 2)))

(define invlog2of
  (let ((table (make-vector 37))
        (log2 (log 2)))
    (do ((b 2 (+ b 1)))
        ((= b 37))
      (vector-set! table b (/ log2 (log b))))
    (lambda (b)
      (if (<= 2 b 36)
          (vector-ref table b)
          (/ log2 (log b))))))

(define fast-expt
  (let ((table (make-vector 326)))
    (do ((k 0 (+ k 1)) (v 1 (* v 10)))
        ((= k 326))
      (vector-set! table k v))
    (lambda (b k)
      (if (and (= b 10) (<= 0 k 326))
          (vector-ref table (inexact->exact (truncate k)))
          (expt b k)))))

(define (mirror-of c)
  (case c ((#\() #\)) ((#\[) #\]) ((#\{) #\}) ((#\<) #\>) (else c)))

(define default-digits
  (list->vector (string->list "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")))

;; kanji (10 included for base 11 ;)
;; (vector "ï¼" "一" "二" "三" "å››" "五" "å…­" "七" "å…«" "ä¹" "å")

;; old style kanji:
;; (vector "零" "壱" "å¼" "å‚" "肆" "ä¼" "陸" "柒" "æŒ" "玖" "拾")

;; General algorithm based on "Printing Floating-Point Numbers Quickly
;; and Accurately" by Burger and Dybvig (FP-Printing-PLDI96.pdf).  The
;; code below will be hard to read out of that context until it's
;; cleaned up.

(define (num->string n st . opt)
  (call-with-output-string
    (lambda (port)
      (let-optionals* opt
          ((base (fmt-radix st))
           (digits (fmt-precision st))
           (sign? #f)
           (commify? #f)
           (comma-sep (and commify? (fmt-ref st 'comma-char #\,)))
           (decimal-sep (or (fmt-decimal-sep st)
                            (if (eqv? comma-sep #\.) #\, #\.)))
           (comma-rule (if (eq? commify? #t) 3 commify?))
           (align (fmt-decimal-align st))
           (digit-vec default-digits)
           (stack '()))

        (define (write-digit d)
          (display (vector-ref digit-vec (inexact->exact (truncate d))) port))

        ;; This is ugly because we need to keep a list of all output
        ;; of the form x9999... in case we get to the end of the
        ;; precision and need to round up.  Alas, if it weren't for
        ;; decimals and commas, we could just keep track of the last
        ;; non-9 digit and the number of nines seen, without any need
        ;; for a heap-allocated stack.
        (define (write-digit-list ls)
          (for-each
           (lambda (x) (if (number? x) (write-digit x) (display x port)))
           ls))

        (define (flush)
          (write-digit-list (reverse stack))
          (set! stack '()))

        (define (flush/rounded)
          (let lp ((ls stack) (res '()))
            (cond
             ((null? ls)
              (write-digit-list (cons #\1 res)))
             ((not (number? (car ls)))
              (lp (cdr ls) (cons (car ls) res)))
             ((= (car ls) (- base 1))
              (lp (cdr ls) (cons #\0 res)))
             (else
              (write-digit-list
               (append (reverse (cdr ls)) (cons (+ 1 (car ls)) res))))))
          (set! stack '()))

        (define (output digit)
          (if (and (number? digit) (< digit (- base 1)))
              (flush))
          (set! stack (cons digit stack)))

        (define (write-prefix prefix align k)
          (if align
              (let* ((prefix (cond ((string? prefix) prefix)
                                   ((char? prefix) (string prefix))
                                   (else "")))
                     (diff (- align
                              (+ (if (<= k 0) 1 k) (string-length prefix))
                              1)))
                (if (positive? diff)
                    (display (make-string diff (fmt-pad-char st)) port))
                (display prefix port))
              (if prefix (display prefix port)))) 

        (define (write-real n prefix align)

          (let* ((m+e (mantissa+exponent (exact->inexact n)))
                 (f (car m+e))
                 (e (cadr m+e))
                 (inv-base (invlog2of base))
                 (round? (even? f))
                 (smaller (if round? <= <))
                 (bigger (if round? >= >)))

            (define (pad d i) ;; just pad 0's, not #'s
              (write-digit d)
              (let lp ((i (- i 1)))
                (cond
                 ((>= i 0)
                  (if (and commify?
                           (if digits
                               (and (> i digits)
                                    (zero? (modulo (- i (- digits 1))
                                                   comma-rule)))
                               (and (positive? i)
                                    (zero? (modulo i comma-rule)))))
                      (display comma-sep port))
                  (if (= i (- digits 1))
                      (display decimal-sep port))
                  (write-digit 0)
                  (lp (- i 1))))))

            (define (pad-all d i)
              (cond
               ((>= d base)
                (flush/rounded))
               (else
                (flush)
                (write-digit d)))
              (let lp ((i (- i 1)))
                (cond
                 ((> i 0)
                  (if (and commify? (zero? (modulo i comma-rule)))
                      (display comma-sep port))
                  (write-digit 0)
                  (lp (- i 1)))
                 ((and (= i 0) (inexact? n))
                  (display decimal-sep port)
                  (write-digit 0)))))

            ;;(define (pad-sci d i k)
            ;;  (cond
            ;;   ((>= d base)
            ;;    (flush/rounded))
            ;;   (else
            ;;    (flush)
            ;;    (write-digit d)))
            ;;  (write-char #\e port)
            ;;  (cond
            ;;   ((positive? k)
            ;;    (write-char #\+ port)
            ;;    (write (- k 1) port))
            ;;   (else
            ;;    (write k port))))

            (define (scale r s m+ m- k f e)
              (let ((est (inexact->exact
                          (ceiling (- (* (+ e (integer-length* f) -1)
                                         (invlog2of base))
                                      1.0e-10)))))
                (if (not (negative? est))
                    (fixup r (* s (fast-expt base est)) m+ m- est)
                    (let ((skale (fast-expt base (- est))))
                      (fixup (* r skale) s (* m+ skale) (* m- skale) est)))))

            (define (fixup r s m+ m- k)
              (if (and (bigger (+ r m+) s)) ;; (or digits (>= k -4))
                  (lead r s m+ m- (+ k 1))
                  (lead (* r base) s (* m+ base) (* m- base) k)))

            (define (lead r s m+ m- k)
              (write-prefix prefix align k)
              (cond
               ((and (not digits) (or (> k 14) (< k -4)))
                (write n port))      ; XXXX native write for sci
               ;;((and (not digits) (> k 14))
               ;; (generate-sci r s m+ m- k))
               ;;((and (not digits) (< k -4))
               ;; (if (>= (/ r s) base)
               ;;     (generate-sci (/ r base) s (/ m+ base) (/ m- base) k)
               ;;     (generate-sci r s m+ m- k)))
               (else
                (cond
                 ((and (not digits)
                       (or (negative? k)
                           (and (zero? k) (not (integer? n)))))
                  (write-digit 0)
                  (display decimal-sep port)
                  (let lp ((i 0))
                    (cond ((> i k)
                           (write-digit 0)
                           (lp (- i 1)))))))
                (if digits
                    (generate-fixed r s m+ m- k)
                    (generate-all r s m+ m- k)))))

            (define (generate-all r s m+ m- k)
              (let gen ((r r) (m+ m+) (m- m-) (i k))
                (cond ((= i k))
                      ((zero? i)
                       (output decimal-sep))
                      ((and commify?
                            (positive? i)
                            (zero? (modulo i comma-rule)))
                       (output comma-sep)))
                (let ((d (quotient r s))
                      (r (remainder r s)))
                  (if (not (smaller r m-))
                      (cond
                       ((not (bigger (+ r m+) s))
                        (output d)
                        (gen (* r base) (* m+ base) (* m- base) (- i 1)))
                       (else
                        (pad-all (+ d 1) i)))
                      (if (not (bigger (+ r m+) s))
                          (pad-all d i)
                          (pad-all (if (< (* r 2) s) d (+ d 1)) i))))))

            (define (generate-fixed r s m+ m- k)
              (if (<= k 0) 
                  (set! stack (append (make-list (min (- k) digits) 0)
                                      (list decimal-sep 0))))
              (let ((i0 (- (+ k digits) 1)))
                (let gen ((r r) (m+ m+) (m- m-) (i i0))
                  (cond ((= i i0))
                        ((= i (- digits 1))
                         (output decimal-sep))
                        ((and commify?
                              (> i digits)
                              (zero? (modulo (- i (- digits 1))
                                             comma-rule)))
                         (output comma-sep)))
                  (let ((d (quotient r s))
                        (r (remainder r s)))
                    (cond
                     ((< i 0)
                      (let ((d2 (* 2 (if (>= (* r 2) s) (+ d 1) d))))
                        (if (and (not (> (- k) digits))
                                 (or (> d2 base)
                                     (and (= d2 base)
                                          (pair? stack)
                                          (number? (car stack))
                                          (odd? (car stack)))))
                            (flush/rounded)
                            (flush))))
                     ((smaller r m-)
                      (cond
                       ((>= d base)
                        (flush/rounded)
                        (pad 0 i))
                       (else
                        (flush)
                        (if (bigger (+ r m+) s)
                            (pad (if (< (* r 2) s) d (+ d 1)) i)
                            (pad d i)))))
                     ((bigger (+ r m+) s)
                      (cond
                       ((>= d (- base 1))
                        (flush/rounded)
                        (pad 0 i))
                       (else
                        (flush)
                        (pad (+ d 1) i))))
                     (else
                      (output d)
                      (gen (* r base) (* m+ base) (* m- base) (- i 1))))))))

            ;;(define (generate-sci r s m+ m- k)
            ;;  (let gen ((r r) (m+ m+) (m- m-) (i k))
            ;;    (cond ((= i (- k 1)) (display decimal-sep port)))
            ;;    (let ((d (quotient r s))
            ;;          (r (remainder r s)))
            ;;      (if (not (smaller r m-))
            ;;          (cond
            ;;           ((not (bigger (+ r m+) s))
            ;;            (output d)
            ;;            (gen (* r base) (* m+ base) (* m- base) (- i 1)))
            ;;           (else (pad-sci (+ d 1) i k)))
            ;;          (if (not (bigger (+ r m+) s))
            ;;              (pad-sci d i k)
            ;;              (pad-sci (if (< (* r 2) s) d (+ d 1)) i k))))))

            (cond
             ((negative? e)
              (if (or (= e *min-e*) (not (= f *bot-f*)))
                  (scale (* f 2) (* (expt 2.0 (- e)) 2) 1 1 0 f e)
                  (scale (* f 2 2) (* (expt 2.0 (- 1 e)) 2) 2 1 0 f e)))
             (else
              (if (= f *bot-f*)
                  (let ((be (expt 2 e)))
                    (scale (* f be 2) 2.0 be be 0 f e))
                  (let* ((be (expt 2 e)) (be1 (* be 2)))
                    (scale (* f be1 2) (* 2.0 2) be1 be 0 f e)))))))

        (define (write-fixed-rational p prefix align)
          (define (get-scale q) (expt base (- (integer-log q base) 1)))
          (let ((n (numerator p))
                (d (denominator p))
                (k (integer-log p base)))
            (write-prefix prefix align k)
            (let lp ((n n)
                     (i (- k)))
              (cond
               ((< i digits)
                (if (zero? i) (output decimal-sep))
                (let ((q (quotient n d)))
                  (cond
                   ((>= q base)
                    (let* ((scale (get-scale q))
                           (digit (quotient q scale))
                           (n2 (- n (* d digit scale))))
                      (output digit)
                      (lp n2 (+ i 1))))
                   (else
                    (output q)
                    (lp (* (remainder n d) base) (+ i 1))))))
               (else
                (let* ((q (quotient n d))
                       (digit
                        (* 2 (if (>= q base) (quotient q (get-scale q)) q))))
                  (if (or (> digit base)
                          (and (= digit base)
                               (let ((prev (find integer? stack)))
                                 (and prev (odd? prev)))))
                      (flush/rounded)
                      (flush))))))))

        (define (wrap-sign n sign? align writer)
          (cond
           ((negative? n)
            (cond
             ((char? sign?)
              (writer (abs n) sign? align)
              (display (mirror-of sign?) port))
             (else
              (writer (abs n) #\- align))))
           (else
            (cond
             ((and sign? (not (char? sign?)))
              (writer n #\+ align))
             (else
              (writer n #f align))))))

        (let ((imag (imag-part n)))
          (cond
           ((and base (not (and (integer? base) (<= 2 base 36))))
            (error "invalid base for numeric formatting" base))
           ((zero? imag)
            (cond
             ((and (exact? n) (not (integer? n)))
              (cond
               (digits
                (wrap-sign n sign? align write-fixed-rational))
               (else
                (wrap-sign (numerator n) sign? #f write-real)
                (write-char #\/ port)
                (wrap-sign (denominator n) #f #f write-real))))
             (else
              (wrap-sign n sign? align write-real))))
           (else (wrap-sign (real-part n) sign? #f write-real)
                 (wrap-sign imag #t #f write-real)
                 (write-char #\i port))))))))

(define (num n . opt)
  (lambda (st) ((fmt-writer st) (apply num->string n st opt) st)))

(define (num/comma n . o)
  (lambda (st)
    (let-optionals* o
        ((base (fmt-radix st))
         (digits (fmt-precision st))
         (sign? #f)
         (comma-rule 3)
         (comma-sep (fmt-ref st 'comma-char #\,))
         (decimal-sep (or (fmt-decimal-sep st)
                          (if (eqv? comma-sep #\.) #\, #\.))))
      ((num n base digits sign? comma-rule comma-sep decimal-sep) st))))

;; SI suffix formatting, as used in --human-readable options to some
;; GNU commands (such as ls).  See
;;
;;   http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html
;;   http://physics.nist.gov/cuu/Units/binary.html
;;
;; Note: lowercase "k" for base 10, uppercase "K" for base 2

(define num/si
  (let* ((names10 '#("" "k" "M" "G" "T" "E" "P" "Z" "Y"))
         (names2 (list->vector
                  (cons ""
                        (cons "Ki" (map (lambda (s) (string-append s "i"))
                                        (cddr (vector->list names10))))))))
    (lambda (n . o)
      (let-optionals* o ((base 1024)
                         (suffix "")
                         (names (if (= base 1024) names2 names10)))
        (let* ((k (min (inexact->exact (floor (/ (log n) (log base))))
                       (vector-length names)))
               (n2 (/ (round (* (/ n (expt base k)) 10)) 10)))
          (cat (if (integer? n2)
                   (number->string (inexact->exact n2))
                   (exact->inexact n2))
               (vector-ref names k)
               (if (zero? k) "" suffix)))))))

(define roman-numerals
  '((1000 . #\M) (500 . #\D) (100 . #\C)
    (50 . #\L) (10 . #\X) (5 . #\V) (1 . #\I)))

(define (num/old-roman num)
  (lambda (st)
    (let lp ((num num) (res '()))
      (if (positive? num)
          (let ((ch (find (lambda (x) (>= num (car x))) roman-numerals)))
            (lp (- num (car ch)) (cons (cdr ch) res)))
          (fmt-write (reverse-list->string res) st)))))

(define (num/roman num)
  (lambda (st)
    (let lp1 ((num num) (res '()))
      (if (positive? num)
          (let lp2 ((ls roman-numerals))
               (let* ((big (car ls))
                      (big-n (car big)))
                 (cond
                  ((>= num big-n)
                   (lp1 (- num big-n) (cons (cdr big) res)))
                  ((and (> (* 2 num) big-n)
                        (find (lambda (c)
                                (let ((x (car c)))
                                  (<= (+ x 1) (- big-n x) num)))
                              ls))
                   => (lambda (c)
                        (lp1 (- num (- big-n (car c)))
                             (cons (cdr big) (cons (cdr c) res)))))
                  (else
                   (lp2 (cdr ls))))))
          (fmt-write (reverse-list->string res) st)))))

;; Force a number into a fixed width, print as #'s if doesn't fit.
;; Needs to be wrapped in a PAD if you want to expand to the width.

(define (num/fit width n . args)
  (fmt-capture
   (apply num n args)
   (lambda (str)
     (lambda (st)
       (if (> (string-length str) width)
           (let ((prec (if (and (pair? args) (pair? (cdr args)))
                           (cadr args)
                           (fmt-precision st))))
             (if prec
                 (let* ((decimal-sep
                         (or (fmt-ref st 'decimal-sep)
                             (if (eqv? #\. (fmt-ref st 'comma-sep)) #\, #\.)))
                        (diff (- width (+ prec
                                          (if (char? decimal-sep)
                                              1
                                              (string-length decimal-sep))))))
                   ((cat (if (positive? diff) (make-string diff #\#) "")
                         decimal-sep (make-string prec #\#))
                    st))
                 ((fmt-writer st) (make-string width #\#) st)))
           ((fmt-writer st) str st))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; shared structure utilities

(define (eq?-table-ref tab x) (hash-table-ref/default tab x #f))
(define (eq?-table-set! tab x v) (hash-table-set! tab x v))

;; XXXX extend for records and other container data types
(define (make-shared-ref-table obj)
  (let ((tab (make-eq?-table))
        (res (make-eq?-table))
        (index 0))
    (let walk ((obj obj))
      (cond
        ((eq?-table-ref tab obj)
         => (lambda (i) (eq?-table-set! tab obj (+ i 1))))
        ((not (or (symbol? obj) (number? obj) (char? obj)
                  (boolean? obj) (null? obj) (eof-object? obj)))
         (eq?-table-set! tab obj 1)
         (cond
           ((pair? obj)
            (walk (car obj))
            (walk (cdr obj)))
           ((vector? obj)
            (let ((len (vector-length obj)))
              (do ((i 0 (+ i 1))) ((>= i len))
                (walk (vector-ref obj i)))))))))
    (hash-table-walk
     tab
     (lambda (obj count)
       (if (> count 1)
           (begin
             (eq?-table-set! res obj (cons index #f))
             (set! index (+ index 1))))))
    res))

(define (gen-shared-ref i suffix)
  (string-append "#" (number->string i) suffix))

(define (maybe-gen-shared-ref st cell shares)
  (cond
    ((pair? cell)
     (set-car! cell (cdr shares))
     (set-cdr! cell #t)
     (set-cdr! shares (+ (cdr shares) 1))
     ((fmt-writer st) (gen-shared-ref (car cell) "=") st))
    (else st)))

(define (call-with-shared-ref obj st shares proc)
  (let ((cell (eq?-table-ref (car shares) obj)))
    (if (and (pair? cell) (cdr cell))
        ((fmt-writer st) (gen-shared-ref (car cell) "#") st)
        (proc (maybe-gen-shared-ref st cell shares)))))

(define (call-with-shared-ref/cdr obj st shares proc sep)
  (let ((cell (eq?-table-ref (car shares) obj))
        (output (fmt-writer st)))
    (cond
      ((and (pair? cell) (cdr cell))
       (output (gen-shared-ref (car cell) "#") (output ". " (sep st))))
      ((pair? cell)
       (let ((st (maybe-gen-shared-ref (output ". " (sep st)) cell shares)))
         (output ")" (proc (output "(" st)))))
      (else
       (proc (sep st))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; sexp formatters

(define (slashified str . o)
  (let-optionals* o ((quot #\") (esc #\\) (rename (lambda (x) #f)))
    (lambda (st)
      (let* ((len (string-length str))
             (output (fmt-writer st))
             (quot-str (string quot))
             (esc-str (if (char? esc) (string esc) (or esc quot-str))))
        (let lp ((i 0) (j 0) (st st))
          (define (collect)
            (if (= i j) st (output (substring/shared str i j) st)))
          (if (>= j len)
              (collect)
              (let ((c (string-ref str j)))
                (cond
                  ((or (eqv? c quot) (eqv? c esc))
                   (lp j (+ j 1) (output esc-str (collect))))
                  ((rename c)
                   => (lambda (c2)
                        (lp (+ j 1)
                            (+ j 1)
                            (output c2 (output esc-str (collect))))))
                  (else
                   (lp i (+ j 1) st))))))))))

;; Only slashify if there are special characters, in which case also
;; wrap in quotes.  For writing symbols in |...| escapes, or CSV
;; fields, etc.  The predicate indicates which characters cause
;; slashification - this is in addition to automatic slashifying when
;; either the quote or escape char is present.

(define (maybe-slashified str pred . o)
  (let-optionals* o ((quot #\") (esc #\\) (rename (lambda (x) #f)))
    (define (esc? c) (or (eqv? c quot) (eqv? c esc) (rename c) (pred c)))
    (if (string-index str esc?)
        (cat quot (slashified str quot esc rename) quot)
        (dsp str))))

(define (fmt-write-string str)
  (define (rename c)
    (case c
      ((#\newline) "n")
      (else #f)))
  (slashified str #\" #\\ rename))

(define (dsp obj)
  (cond
    ((procedure? obj) obj)
    ((string? obj) (lambda (st) ((fmt-writer st) obj st)))
    ((char? obj) (dsp (string obj)))
    (else (wrt obj))))

(define (write-with-shares obj shares)
  (lambda (st)
    (let* ((output (fmt-writer st))
           (wr-num
            (cond ((and (= 10 (fmt-radix st))
                        (not (fmt-precision st))
                        (not (fmt-decimal-align st)))
                   (lambda (n st) (output (number->string n) st)))
                  ((assv (fmt-radix st)
                         '((16 . "#x") (10 . "") (8 . "#o") (2 . "#b")))
                   => (lambda (cell)
                        (let ((prefix (cdr cell)))
                          (lambda (n st) ((num n) (output prefix st))))))
                  (else (lambda (n st) (output (number->string n) st))))))
      (let wr ((obj obj) (st st))
        (call-with-shared-ref obj st shares
          (lambda (st)
            (cond
              ((pair? obj)
               (output
                ")"
                (let lp ((ls obj)
                         (st (output "(" st)))
                  (let ((st (wr (car ls) st))
                        (rest (cdr ls)))
                    (cond
                      ((null? rest) st)
                      ((pair? rest)
                       (call-with-shared-ref/cdr rest st shares
                         (lambda (st) (lp rest st))
                         (dsp " ")))
                      (else (wr rest (output " . " st))))))))
              ((vector? obj)
               (let ((len (vector-length obj)))
                 (if (zero? len)
                     (output "#()" st)
                     (let lp ((i 1)
                              (st
                               (wr (vector-ref obj 0)
                                   (output "#(" st))))
                       (if (>= i len)
                           (output ")" st)
                           (lp (+ i 1)
                               (wr (vector-ref obj i)
                                   (output " " st))))))))
              ((string? obj)
               (output "\"" ((fmt-write-string obj) (output "\"" st))))
              ((number? obj)
               (wr-num obj st))
              ((boolean? obj)
               (output (if obj "#t" "#f") st))
              (else
               (output (write-to-string obj) st)))))))))

(define (wrt obj)
  (write-with-shares obj (cons (make-shared-ref-table obj) 0)))

;; the only expensive part, in both time and memory, of handling
;; shared structures when writing is building the initial table, so
;; for the efficient version we just skip that

(define (wrt/unshared obj)
  (write-with-shares obj (cons (make-eq?-table) 0)))

Added fmt/fmt.sls.



























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
;;;; fmt.scm -- extensible formatting library
;;
;; Copyright (c) 2006-2009 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

#!r6rs
(library (fmt fmt)
	 (export
	  new-fmt-state
	  fmt fmt-start fmt-if fmt-capture fmt-let fmt-bind fmt-null
	  fmt-ref fmt-set! fmt-add-properties! fmt-set-property!
	  fmt-col fmt-set-col! fmt-row fmt-set-row!
	  fmt-radix fmt-set-radix! fmt-precision fmt-set-precision!
	  fmt-properties fmt-set-properties! fmt-width fmt-set-width!
	  fmt-writer fmt-set-writer! fmt-port fmt-set-port!
	  fmt-decimal-sep fmt-set-decimal-sep!
	  fmt-file fmt-try-fit cat apply-cat nl fl nl-str
	  fmt-join fmt-join/last fmt-join/dot
	  fmt-join/prefix fmt-join/suffix fmt-join/range
	  pad pad/right pad/left pad/both trim trim/left trim/both trim/length
	  fit fit/left fit/both tab-to space-to wrt wrt/unshared dsp
	  pretty pretty/unshared slashified maybe-slashified
	  num num/si num/fit num/comma radix fix decimal-align ellipses
	  upcase downcase titlecase pad-char comma-char decimal-char
	  with-width wrap-lines fold-lines justify
	  make-string-fmt-transformer
	  make-space make-nl-space display-to-string write-to-string
	  fmt-columns columnar tabular line-numbers)
	 (import (chezscheme) 
		 (only (srfi s13 strings) string-count string-index
		       string-index-right 
		       string-concatenate string-concatenate-reverse  
		       substring/shared reverse-list->string string-tokenize
		       string-suffix? string-prefix?)
		 (srfi private let-opt)
		 (only (srfi s1 lists) fold length+))

	 (include "hash-compat.scm")
	 (include "mantissa.scm")
	 (include "read-line.scm")
	 (include "string-ports.scm")
	 (include "fmt.scm")
	 (include "fmt-column.scm")
	 (include "fmt-pretty.scm")
	 )

Added fmt/hash-compat.scm.











>
>
>
>
>
1
2
3
4
5

(define (make-eq?-table) (make-eq-hashtable))
(define hash-table-ref/default hashtable-ref)
(define hash-table-set! hashtable-set!)
(define hash-table-walk hash-table-for-each)

Added fmt/js.sls.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;;;; fmt-js.scm -- javascript formatting utilities
;;
;; Copyright (c) 2011-2012 Alex Shinn.  All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt

#!r6rs
(library 
 (fmt js)
 (export
  js-expr js-function js-var js-comment js-array js-object js=== js>>>)
 
 (import (chezscheme)
	 (fmt fmt) (fmt c))
 
 (include "fmt-js.scm")
 
 )

Added fmt/mantissa.scm.









































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

;; Break a positive real number down to a normalized mantissa and
;; exponent. Default base=2, mant-size=52, exp-size=11 for IEEE doubles.


(define mantissa+exponent
  (case-lambda 
    [(num) (mantissa+exponent num 2)]
    [(num base) (mantissa+exponent num base 52)]
    [(num base mant-size) (mantissa+exponent num base mant-size 11)]
    [(num base mant-size exp-size)
     (if (zero? num)
	 (list 0 0)
	 (let* ((bot (expt base mant-size))
		(top (* base bot)))
	   (let lp ((n num) (e 0))
	     (cond
	      ((>= n top) (lp (quotient n base) (+ e 1)))
	      ((< n bot) (lp (* n base) (- e 1)))
	      (else (list n e))))))]))

Added fmt/read-line.scm.

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8

(define (read-line . o)
  (let ((port (if (pair? o) (car o) (current-input-port))))
    (let lp ((res '()))
      (let ((c (read-char port)))
        (if (or (eof-object? c) (eqv? c #\newline))
            (list->string (reverse res))
            (lp (cons c res)))))))

Added fmt/string-ports.scm.











>
>
>
>
>
1
2
3
4
5

(define (call-with-output-string f)
  (let ((port (open-output-string)))
    (let () (f port))
    (get-output-string port)))

Added fmt/test-fmt-c.scm.

































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

(cond-expand
 (chicken (use test) (load "fmt-c-chicken.scm"))
 (gauche
  (use gauche.test)
  (use text.fmt)
  (use text.fmt.c)
  (define test-begin test-start)
  (define orig-test (with-module gauche.test test))
  (define-syntax test
    (syntax-rules ()
      ((test name expected expr)
       (orig-test name expected (lambda () expr)))
      ((test expected expr)
       (orig-test (let ((s (with-output-to-string (lambda () (write 'expr)))))
                    (substring s 0 (min 60 (string-length s))))
                  expected
                  (lambda () expr)))
      )))
 (else))

(cond-expand
 (chicken
  (import fmt fmt-c))
 (else))

(test-begin "fmt-c")

(test "if (1) {
    2;
} else {
    3;
}
"
    (fmt #f (c-if 1 2 3)))

(test "if (x ? y : z) {
    2;
} else {
    3;
}
"
    (fmt #f (c-if (c-if 'x 'y 'z) 2 3)))

(test "if (x ? y : z) {
    2;
} else {
    3;
}
"
    (fmt #f (c-expr '(if (if x y z) 2 3))))

(test "if (x ? y : z) {
    2;
} else {
    3;
}
"
    (fmt #f (c-expr '(%begin (if (if x y z) 2 3)))))

(test "int square (int x) {
    return x * x;
}
"
    (fmt #f (c-fun 'int 'square '((int x)) (c* 'x 'x))))

(test "int foo (int x, int y, int z) {
    if (x ? y : z) {
        return 2;
    } else {
        return 3;
    }
}
"
    (fmt #f (c-fun 'int 'foo '((int x) (int y) (int z))
                   (c-if (c-if 'x 'y 'z) 2 3))))

(test "void bar (int mode, const char *msg, unsigned int arg) {
    if (mode == 1) {
        printf(msg);
    } else {
        printf(msg, arg);
    }
}
"
    (fmt #f (c-fun 'void 'bar
                   '((int mode)
                     ((%pointer (const char)) msg)
                     ((unsigned int) arg))
                   (c-if (c== 'mode 1) '(printf msg) '(printf msg arg)))))

(test "while ((line = readline()) != EOF) {
    printf(\"%s\", line);
}
"
    (fmt #f (c-while (c!= (c= 'line '(readline)) 'EOF)
                     '(printf "%s" line))))

(test "switch (y) {
    case 1:
        x = 1;
        break;
    case 2:
        x = 4;
        break;
    default:
        x = 5;
        break;
}
"
    (fmt #f (c-switch 'y
                      (c-case 1 (c= 'x 1))
                      (c-case 2 (c= 'x 4))
                      (c-default (c= 'x 5)))))

(test "switch (y) {
    case 1:
        x = 1;
        break;
    case 2:
        x = 4;
    default:
        x = 5;
        break;
}
"
    (fmt #f (c-switch 'y
                      (c-case 1 (c= 'x 1))
                      (c-case/fallthrough 2 (c= 'x 4))
                      (c-default (c= 'x 5)))))

(test "switch (y) {
    case 1:
        x = 1;
        break;
    case 2:
        x = 4;
        break;
    default:
        x = 5;
        break;
}
"
    (fmt #f (c-switch 'y '((1) (= x 1)) '((2) (= x 4)) '(else (= x 5)))))

(test "switch (y) {
    case 1:
        x = 1;
        break;
    case 2:
        x = 4;
        break;
    default:
        x = 5;
        break;
}
"
    (fmt #f (c-expr '(switch y ((1) (= x 1)) ((2) (= x 4)) (else (= x 5))))))

(test "int q (int x) {
    switch (x) {
        case 1:
            return 1;
        case 2:
            return 4;
        default:
            return 5;
    }
}
"
    (fmt #f (c-fun 'int 'q '(x) (c-switch 'x '((1) 1) '((2) 4) '(else 5)))))

(test "switch (x) {
    case 1:
    case 2:
        foo();
        break;
    default:
        bar();
        break;
}
"
    (fmt #f (c-expr '(switch x ((1 2) (foo)) (else (bar))))))

(test "switch (x) {
    case 1:
        foo();
        break;
    case 2:
    case 3:
        bar();
        break;
    default:
        baz();
        break;
}
"
    (fmt #f (c-expr
             '(switch x (case 1 (foo)) (case (2 3) (bar)) (else (baz))))))

(test "switch (x) {
    case 1:
    case 2:
        foo();
    default:
        bar();
        break;
}
"
    (fmt #f (c-expr '(switch x (case/fallthrough (1 2) (foo)) (else (bar))))))

(test "switch (x) {
    case 1:
    case 2:
        foo();
        break;
    default:
        bar();
        break;
}
"
    (fmt #f (c-expr '(switch x ((1 2) (foo)) (default (bar))))))

(test "switch (x) {
    default:
        bar();
    case 1:
    case 2:
        foo();
        break;
}
"
    (fmt #f (c-expr '(switch x (else/fallthrough (bar)) ((1 2) (foo))))))

(test "for (i = 0; i < n; i++) {
    printf(\"i: %d\");
}
"
    (fmt #f (c-for (c= 'i 0) (c< 'i 'n) (c++/post 'i) '(printf "i: %d"))))

(test "a * x + b * y == c;\n"
    (fmt #f (c== (c+ (c* 'a 'x) (c* 'b 'y)) 'c)))
(test "a * x + b * y == c;\n"
    (fmt #f (c-expr '(== (+ (* a x) (* b y)) c))))

(test "(a + x) * (b + y) == c;\n"
    (fmt #f (c-expr '(== (* (+ a x) (+ b y)) c))))

(test
"(abracadabra!!!! + xylophone????)
  * (bananarama____ + yellowstonepark~~~~)
  * (cryptoanalysis + zebramania);\n"
    (fmt #f (c-expr '(* (+ abracadabra!!!! xylophone????)
                        (+ bananarama____ yellowstonepark~~~~)
                        (+ cryptoanalysis zebramania)))))

(test
"abracadabra(xylophone,
            bananarama,
            yellowstonepark,
            cryptoanalysis,
            zebramania,
            delightful,
            wubbleflubbery);\n"
    (fmt #f (c-expr '(abracadabra xylophone
                                  bananarama
                                  yellowstonepark
                                  cryptoanalysis
                                  zebramania
                                  delightful
                                  wubbleflubbery))))

(test "#define foo(x, y) (((x) + (y)))\n"
    (fmt #f (cpp-define '(foo (int x) (int y)) (c+ 'x 'y))))

(test "#define min(x, y) (((x) < (y)) ? (x) : (y))\n"
    (fmt #f (cpp-define '(min x y) (c-if (c< 'x 'y) 'x 'y))))

(test
"#define foo(x, y) (abracadabra(((x) + (y)), \\
                               xylophone, \\
                               bananarama, \\
                               yellowstonepark, \\
                               cryptoanalysis, \\
                               zebramania, \\
                               delightful, \\
                               wubbleflubbery))\n"
    (fmt #f (cpp-define '(foo x y)
                        '(abracadabra (+ x y)
                                      xylophone
                                      bananarama
                                      yellowstonepark
                                      cryptoanalysis
                                      zebramania
                                      delightful
                                      wubbleflubbery))))

(test "#ifndef FOO_H
#define FOO_H

extern int foo ();

#endif  /* ! FOO_H */
"
    (fmt #f (cpp-wrap-header
             'FOO_H
             (c-extern (c-prototype 'int 'foo '())))))

(test "#if foo
1
#elif bar
2
#elif baz
3
#else 
4
#endif
"
    (fmt #f (cpp-if 'foo 1 'bar 2 'baz 3 4)))

(test "/* this is a /\\* nested *\\/ comment */"
    (fmt #f (c-comment " this is a " (c-comment " nested ") " comment ")))

;; the initial leading space is annoying but hard to remove at the
;; moment - the important thing is we preserve indentation in the body
(test "switch (y) {
    case 1:
        x = 1;
        break;
    
#ifdef H_TWO
    case 2:
        x = 4;
        break;
#endif  /* H_TWO */
    default:
        x = 5;
        break;
}
"
    (fmt #f (c-expr
             `(switch y
                      ((1) (= x 1))
                      ,(cpp-ifdef 'H_TWO (c-case '(2) '(= x 4)))
                      (else (= x 5))))))

(test "#define eprintf(...) (fprintf(stderr, __VA_ARGS__))\n"
    (fmt #f (c-expr '(%define (eprintf . args) (fprintf stderr args)))))

(test "struct point {
    int x;
    int y;
};
"
    (fmt #f (c-expr `(struct point (x y)))))

(test "struct employee {
    short age;
    char *name;
    struct {
        int year;
        int month;
        int day;
    } dob;
} __attribute__ ((packed));
"
    (fmt #f (c-expr `(struct employee
                             ((short age)
                              ((%pointer char) name)
                              ((struct (year month day)) dob))
                             (%attribute packed)
                             ))))

(test "class employee {
    short age;
    char *name;
    struct {
        int year;
        int month;
        int day;
    } dob;
} __attribute__ ((packed));
"
    (fmt #f (c-class 'employee
                      '((short age)
                        ((%pointer char) name)
                        ((struct (year month day)) dob))
                      (c-attribute 'packed)
                      )))

(test "union object {
    char tag;
    struct {
        char tag;
        char *data;
    } string;
    struct {
        char tag;
        void *car;
        void *cdr;
    } pair;
    struct {
        char tag;
        unsigned int length;
        void *data;
    } vector;
};
"
    (fmt #f (c-expr
             '(union object
                     ((char tag)
                      ((struct ((char tag) ((* char) data))) string)
                      ((struct ((char tag)
                                ((* void) car)
                                ((* void) cdr)))
                       pair)
                      ((struct ((char tag)
                                ((unsigned int) length)
                                ((* void) data)))
                       vector)
                      )))))

(test "enum type_tags {
    TYPE_CHAR = 1,
    TYPE_FIXNUM,
    TYPE_BOOLEAN,
    TYPE_NULL,
    TYPE_EOF,
    TYPE_STRING,
    TYPE_PAIR,
    TYPE_VECTOR
};
"
    (fmt #f (c-expr '(enum type_tags ((TYPE_CHAR 1) TYPE_FIXNUM TYPE_BOOLEAN TYPE_NULL TYPE_EOF TYPE_STRING TYPE_PAIR TYPE_VECTOR)))))

(test "#define OP_EVAL 0xFE\n" (fmt #f (radix 16 (cpp-define 'OP_EVAL 254))))

(test "unsigned long table[SIZE] = {1, 2, 3, 4};\n"
    (fmt #f (c-var '(%array (unsigned long) SIZE) 'table '#(1 2 3 4))))

(test "int *array_of_ptr[];\n"
    (fmt #f (c-var '(%array (* int)) 'array_of_ptr)))

(test "int (*ptr_to_array)[];\n"
    (fmt #f (c-var '(* (%array int)) 'ptr_to_array)))

(test "foo **table = {{1, \"foo\"}, {2, \"bar\"}, {3, \"baz\"}, {4, \"qux\"}};\n"
    (fmt #f (c-var '(* (* foo)) 'table
                   '#(#(1 "foo") #(2 "bar") #(3 "baz") #(4 "qux")))))

(test "sexp (*f)(sexp, sexp) = NULL;\n"
    (fmt #f (c-var '(%fun sexp (sexp sexp)) 'f 'NULL)))

(test "sexp (*)(sexp) (*f)(sexp, sexp) = NULL;\n"
    (fmt #f (c-var '(%fun (%fun sexp (sexp)) (sexp sexp)) 'f 'NULL)))

(test "typedef double (*f)(double *, double, int);\n"
    (fmt #f (c-typedef '(%fun double ((* double) double int)) 'f)))

(test "\"foo\\tbar\";\n"
    (fmt #f (c-expr "foo\tbar")))

(test-end)

Added fmt/test-fmt-js.scm.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

(cond-expand
 (chicken
  (load "fmt-js-chicken.scm"))
 (else))

(cond-expand
 (chicken
  (use test)
  (import fmt)
  (import fmt-js))
 (gauche
  (use gauche.test)
  (use text.fmt)
  (use text.fmt.js)
  (define test-begin test-start)
  (define orig-test (with-module gauche.test test))
  (define-syntax test
    (syntax-rules ()
      ((test name expected expr)
       (orig-test name expected (lambda () expr)))
      ((test expected expr)
       (orig-test (let ((s (with-output-to-string (lambda () (write 'expr)))))
                    (substring s 0 (min 60 (string-length s))))
                  expected
                  (lambda () expr)))
      )))
 (else))

(test-begin "fmt-js")

(test "var foo = 1 + 2;\n"
    (fmt #f (js-expr '(%var foo (+ 1 2)))))

(test "var foo = 1 + 2;\n"
    (fmt #f (js-expr '(%begin (%var foo (+ 1 2))))))

(test "function square(x) {
    return x * x;
}"
    (fmt #f (js-function 'square '(x) '(* x x))))

(test "{\"foo\": [1, 2, 3], \"bar\": \"baz\"}"
    (fmt #f (js-expr '(%object ("foo" . #(1 2 3)) ("bar" . "baz")))))

(test-end)

Added fmt/test-fmt.scm.













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486

(cond-expand
 (chicken
  (load "fmt-chicken.scm"))
 (else))

(cond-expand
 (chicken
  (use test)
  (import fmt))
 (gauche
  (use gauche.test)
  (use text.fmt)
  (define test-begin test-start)
  (define orig-test (with-module gauche.test test))
  (define-syntax test
    (syntax-rules ()
      ((test name expected expr)
       (guard (e (else #f))
              (orig-test name expected (lambda () expr))))
      ((test expected expr)
       (test (let ((s (with-output-to-string (lambda () (write 'expr)))))
               (substring s 0 (min 60 (string-length s))))
             expected expr)))))
 (else))

(test-begin "fmt")

;; basic data types

(test "hi" (fmt #f "hi"))
(test "\"hi\"" (fmt #f (wrt "hi")))
(test "\"hi \\\"bob\\\"\"" (fmt #f (wrt "hi \"bob\"")))
(test "\"hello\\nworld\"" (fmt #f (wrt "hello\nworld")))
(test "ABC" (fmt #f (upcase "abc")))
(test "abc" (fmt #f (downcase "ABC")))
(test "Abc" (fmt #f (titlecase "abc")))

(test "abc     def" (fmt #f "abc" (tab-to) "def"))
(test "abc  def" (fmt #f "abc" (tab-to 5) "def"))
(test "abcdef" (fmt #f "abc" (tab-to 3) "def"))

(test "-1" (fmt #f -1))
(test "0" (fmt #f 0))
(test "1" (fmt #f 1))
(test "10" (fmt #f 10))
(test "100" (fmt #f 100))
(test "-1" (fmt #f (num -1)))
(test "0" (fmt #f (num 0)))
(test "1" (fmt #f (num 1)))
(test "10" (fmt #f (num 10)))
(test "100" (fmt #f (num 100)))
;; (test "1e+15" (fmt #f (num 1e+15)))
;; (test "1e+23" (fmt #f (num 1e+23)))
;; (test "1.2e+23" (fmt #f (num 1.2e+23)))
;; (test "1e-5" (fmt #f (num 1e-5)))
;; (test "1e-6" (fmt #f (num 1e-6)))
;; (test "1e-7" (fmt #f (num 1e-7)))
;; (test "2e-6" (fmt #f (num 2e-6)))
(test "57005" (fmt #f #xDEAD))
(test "#xDEAD" (fmt #f (radix 16 #xDEAD)))
(test "#xDEAD1234" (fmt #f (radix 16 #xDEAD) 1234))
(test "#xDE.AD" (fmt #f (radix 16 (exact->inexact (/ #xDEAD #x100)))))
(test "#xD.EAD" (fmt #f (radix 16 (exact->inexact (/ #xDEAD #x1000)))))
(test "#x0.DEAD" (fmt #f (radix 16 (exact->inexact (/ #xDEAD #x10000)))))
(test "1G" (fmt #f (radix 17 (num 33))))
(test "1G" (fmt #f (num 33 17)))

(test "3.14159" (fmt #f 3.14159))
(test "3.14" (fmt #f (fix 2 3.14159)))
(test "3.14" (fmt #f (fix 2 3.14)))
(test "3.00" (fmt #f (fix 2 3.)))
(test "1.10" (fmt #f (num 1.099 10 2)))
(test "0.00" (fmt #f (fix 2 1e-17)))
(test "0.0000000000" (fmt #f (fix 10 1e-17)))
(test "0.00000000000000001000" (fmt #f (fix 20 1e-17)))
;; (test-error (fmt #f (num 1e-17 0)))
(test "0.000004" (fmt #f (num 0.000004 10 6)))
(test "0.0000040" (fmt #f (num 0.000004 10 7)))
(test "0.00000400" (fmt #f (num 0.000004 10 8)))
;; (test "0.000004" (fmt #f (num 0.000004)))

(test "   3.14159" (fmt #f (decimal-align 5 (num 3.14159))))
(test "  31.4159" (fmt #f (decimal-align 5 (num 31.4159))))
(test " 314.159" (fmt #f (decimal-align 5 (num 314.159))))
(test "3141.59" (fmt #f (decimal-align 5 (num 3141.59))))
(test "31415.9" (fmt #f (decimal-align 5 (num 31415.9))))
(test "  -3.14159" (fmt #f (decimal-align 5 (num -3.14159))))
(test " -31.4159" (fmt #f (decimal-align 5 (num -31.4159))))
(test "-314.159" (fmt #f (decimal-align 5 (num -314.159))))
(test "-3141.59" (fmt #f (decimal-align 5 (num -3141.59))))
(test "-31415.9" (fmt #f (decimal-align 5 (num -31415.9))))

(cond
 ((exact? (/ 1 3)) ;; exact rationals
  (test "333.333333333333333333333333333333" (fmt #f (fix 30 1000/3)))
  (test  "33.333333333333333333333333333333" (fmt #f (fix 30 100/3)))
  (test   "3.333333333333333333333333333333" (fmt #f (fix 30 10/3)))
  (test   "0.333333333333333333333333333333" (fmt #f (fix 30 1/3)))
  (test   "0.033333333333333333333333333333" (fmt #f (fix 30 1/30)))
  (test   "0.003333333333333333333333333333" (fmt #f (fix 30 1/300)))
  (test   "0.000333333333333333333333333333" (fmt #f (fix 30 1/3000)))
  (test   "0.666666666666666666666666666667" (fmt #f (fix 30 2/3)))
  (test   "0.090909090909090909090909090909" (fmt #f (fix 30 1/11)))
  (test   "1.428571428571428571428571428571" (fmt #f (fix 30 10/7)))
  (test "0.123456789012345678901234567890"
      (fmt #f (fix 30 (/  123456789012345678901234567890
                         1000000000000000000000000000000))))
  (test  " 333.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 1000/3))))
  (test  "  33.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 100/3))))
  (test  "   3.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 10/3))))
  (test  "   0.333333333333333333333333333333" (fmt #f (decimal-align 5 (fix 30 1/3))))
  ))

(test "11.75" (fmt #f (num (/ 47 4) 10 2)))
(test "-11.75" (fmt #f (num (/ -47 4) 10 2)))

(test "(#x11 #x22 #x33)" (fmt #f (radix 16 '(#x11 #x22 #x33))))

(test "299,792,458" (fmt #f (num 299792458 10 #f #f #t)))
(test "299,792,458" (fmt #f (num/comma 299792458)))
(test "299.792.458" (fmt #f (comma-char #\. (num/comma 299792458))))
(test "299.792.458,0" (fmt #f (comma-char #\. (num/comma 299792458.0))))

(test "100,000" (fmt #f (num 100000 10 0 #f 3)))
(test "100,000.0" (fmt #f (num 100000 10 1 #f 3)))
(test "100,000.00" (fmt #f (num 100000 10 2 #f 3)))

(test "1.23" (fmt #f (fix 2 (num/fit 4 1.2345))))
(test "1.00" (fmt #f (fix 2 (num/fit 4 1))))
(test "#.##" (fmt #f (fix 2 (num/fit 4 12.345))))

;; (cond
;;  ((feature? 'full-numeric-tower)
;;   (test "1+2i" (fmt #f (string->number "1+2i")))
;;   (test "1+2i" (fmt #f (num (string->number "1+2i"))))
;;   (test "1.00+2.00i" (fmt #f (fix 2 (num (string->number "1+2i")))))
;;   (test "3.14+2.00i" (fmt #f (fix 2 (num (string->number "3.14159+2i")))))))

(test "3.9Ki" (fmt #f (num/si 3986)))
(test "4k" (fmt #f (num/si 3986 1000)))
(test "608" (fmt #f (num/si 608)))
(test "3G" (fmt #f (num/si 12345.12355 16)))

;; padding/trimming

(test "abc  " (fmt #f (pad 5 "abc")))
(test "  abc" (fmt #f (pad/left 5 "abc")))
(test " abc " (fmt #f (pad/both 5 "abc")))
(test "abcde" (fmt #f (pad 5 "abcde")))
(test "abcdef" (fmt #f (pad 5 "abcdef")))

(test "abc" (fmt #f (trim 3 "abcde")))
(test "abc" (fmt #f (trim/length 3 "abcde")))
(test "abc" (fmt #f (trim/length 3 "abc\nde")))
(test "cde" (fmt #f (trim/left 3 "abcde")))
(test "bcd" (fmt #f (trim/both 3 "abcde")))

(test "prefix: abc" (fmt #f "prefix: " (trim 3 "abcde")))
(test "prefix: abc" (fmt #f "prefix: " (trim/length 3 "abcde")))
(test "prefix: abc" (fmt #f "prefix: " (trim/length 3 "abc\nde")))
(test "prefix: cde" (fmt #f "prefix: " (trim/left 3 "abcde")))
(test "prefix: bcd" (fmt #f "prefix: " (trim/both 3 "abcde")))

(test "abcde" (fmt #f (ellipses "..." (trim 5 "abcde"))))
(test "ab..." (fmt #f (ellipses "..." (trim 5 "abcdef"))))
(test "abc..." (fmt #f (ellipses "..." (trim 6 "abcdefg"))))
(test "abcde" (fmt #f (ellipses "..." (trim/left 5 "abcde"))))
(test "...ef" (fmt #f (ellipses "..." (trim/left 5 "abcdef"))))
(test "...efg" (fmt #f (ellipses "..." (trim/left 6 "abcdefg"))))
(test "abcdefg" (fmt #f (ellipses "..." (trim/both 7 "abcdefg"))))
(test "...d..." (fmt #f (ellipses "..." (trim/both 7 "abcdefgh"))))
(test "...e..." (fmt #f (ellipses "..." (trim/both 7 "abcdefghi"))))

(test "abc  " (fmt #f (fit 5 "abc")))
(test "  abc" (fmt #f (fit/left 5 "abc")))
(test " abc " (fmt #f (fit/both 5 "abc")))
(test "abcde" (fmt #f (fit 5 "abcde")))
(test "abcde" (fmt #f (fit/left 5 "abcde")))
(test "abcde" (fmt #f (fit/both 5 "abcde")))
(test "abcde" (fmt #f (fit 5 "abcdefgh")))
(test "defgh" (fmt #f (fit/left 5 "abcdefgh")))
(test "cdefg" (fmt #f (fit/both 5 "abcdefgh")))

(test "prefix: abc  " (fmt #f "prefix: " (fit 5 "abc")))
(test "prefix:   abc" (fmt #f "prefix: " (fit/left 5 "abc")))
(test "prefix:  abc " (fmt #f "prefix: " (fit/both 5 "abc")))
(test "prefix: abcde" (fmt #f "prefix: " (fit 5 "abcde")))
(test "prefix: abcde" (fmt #f "prefix: " (fit/left 5 "abcde")))
(test "prefix: abcde" (fmt #f "prefix: " (fit/both 5 "abcde")))
(test "prefix: abcde" (fmt #f "prefix: " (fit 5 "abcdefgh")))
(test "prefix: defgh" (fmt #f "prefix: " (fit/left 5 "abcdefgh")))
(test "prefix: cdefg" (fmt #f "prefix: " (fit/both 5 "abcdefgh")))

(test "abc\n123\n" (fmt #f (fmt-join/suffix (cut trim 3 <>) (string-split "abcdef\n123456\n" "\n") nl)))

;; utilities

(test "1 2 3" (fmt #f (fmt-join dsp '(1 2 3) " ")))

;; shared structures

(test "#0=(1 . #0#)"
    (fmt #f (wrt (let ((ones (list 1))) (set-cdr! ones ones) ones))))
(test "(0 . #0=(1 . #0#))"
    (fmt #f (wrt (let ((ones (list 1)))
                   (set-cdr! ones ones)
                   (cons 0 ones)))))
(test "(sym . #0=(sym . #0#))"
    (fmt #f (wrt (let ((syms (list 'sym)))
                   (set-cdr! syms syms)
                   (cons 'sym syms)))))
(test "(#0=(1 . #0#) #1=(2 . #1#))"
    (fmt #f (wrt (let ((ones (list 1))
                       (twos (list 2)))
                   (set-cdr! ones ones)
                   (set-cdr! twos twos)
                   (list ones twos)))))

;; without shared detection

(test "(1 1 1 1 1"
    (fmt #f (trim/length
             10
             (wrt/unshared
              (let ((ones (list 1))) (set-cdr! ones ones) ones)))))

(test "(1 1 1 1 1 "
    (fmt #f (trim/length
             11
             (wrt/unshared
              (let ((ones (list 1))) (set-cdr! ones ones) ones)))))

;; pretty printing

;; (define-macro (test-pretty str)
;;   (let ((sexp (with-input-from-string str read)))
;;     `(test ,str (fmt #f (pretty ',sexp)))))

(define-syntax test-pretty
  (syntax-rules ()
    ((test-pretty str)
     (let ((sexp (with-input-from-string str read)))
       (test str (fmt #f (pretty sexp)))))))

(test-pretty "(foo bar)\n")

(test-pretty
"((self . aquanet-paper-1991)
 (type . paper)
 (title . \"Aquanet: a hypertext tool to hold your\"))
")

(test-pretty
"(abracadabra xylophone
             bananarama
             yellowstonepark
             cryptoanalysis
             zebramania
             delightful
             wubbleflubbery)\n")

(test-pretty
 "#(0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   25 26 27 28 29 30 31 32 33 34 35 36 37)\n")

(test-pretty
 "(0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
  25 26 27 28 29 30 31 32 33 34 35 36 37)\n")

(test-pretty
 "(define (fold kons knil ls)
  (define (loop ls acc)
    (if (null? ls) acc (loop (cdr ls) (kons (car ls) acc))))
  (loop ls knil))\n")

(test-pretty
"(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i i))\n")

(test-pretty
"(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec)
  (vector-set! vec i 'supercalifrajalisticexpialidocious))\n")

(test-pretty
"(do ((my-vector (make-vector 5)) (index 0 (+ index 1)))
    ((= index 5) my-vector)
  (vector-set! my-vector index index))\n")

(test-pretty
 "(define (fold kons knil ls)
  (let loop ((ls ls) (acc knil))
    (if (null? ls) acc (loop (cdr ls) (kons (car ls) acc)))))\n")

(test-pretty
 "(define (file->sexp-list pathname)
  (call-with-input-file pathname
    (lambda (port)
      (let loop ((res '()))
        (let ((line (read port)))
          (if (eof-object? line) (reverse res) (loop (cons line res))))))))\n")

(test "(let ((ones '#0=(1 . #0#))) ones)\n"
    (fmt #f (pretty (let ((ones (list 1))) (set-cdr! ones ones) `(let ((ones ',ones)) ones)))))

'(test
"(let ((zeros '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
      (ones '#0=(1 . #0#)))
  (append zeros ones))\n"
    (fmt #f (pretty
             (let ((ones (list 1)))
               (set-cdr! ones ones)
               `(let ((zeros '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
                      (ones ',ones))
                  (append zeros ones))))))

;; slashify

(test "\"note\",\"very simple\",\"csv\",\"writer\",\"\"\"yay!\"\"\""
    (fmt #f (fmt-join (lambda (x) (cat "\"" (slashified x #\" #f) "\""))
                  '("note" "very simple" "csv" "writer" "\"yay!\"")
                  ",")))

(test "note,\"very simple\",csv,writer,\"\"\"yay!\"\"\""
    (fmt #f (fmt-join (cut maybe-slashified <> char-whitespace? #\" #f)
                  '("note" "very simple" "csv" "writer" "\"yay!\"")
                  ",")))

;; columnar formatting

(test "abc\ndef\n" (fmt #f (fmt-columns (list dsp "abc\ndef\n"))))
(test "abc123\ndef456\n" (fmt #f (fmt-columns (list dsp "abc\ndef\n") (list dsp "123\n456\n"))))
(test "abc123\ndef456\n" (fmt #f (fmt-columns (list dsp "abc\ndef\n") (list dsp "123\n456"))))
(test "abc123\ndef456\n" (fmt #f (fmt-columns (list dsp "abc\ndef") (list dsp "123\n456\n"))))
(test "abc123\ndef456\nghi789\n"
    (fmt #f (fmt-columns (list dsp "abc\ndef\nghi\n") (list dsp "123\n456\n789\n"))))
(test "abc123wuv\ndef456xyz\n"
    (fmt #f (fmt-columns (list dsp "abc\ndef\n") (list dsp "123\n456\n") (list dsp "wuv\nxyz\n"))))
(test "abc  123\ndef  456\n"
    (fmt #f (fmt-columns (list (cut pad/right 5 <>) "abc\ndef\n") (list dsp "123\n456\n"))))
(test "ABC  123\nDEF  456\n"
    (fmt #f (fmt-columns (list (compose upcase (cut pad/right 5 <>)) "abc\ndef\n")
                         (list dsp "123\n456\n"))))
(test "ABC  123\nDEF  456\n"
    (fmt #f (fmt-columns (list (compose (cut pad/right 5 <>) upcase) "abc\ndef\n")
                         (list dsp "123\n456\n"))))

(test "hello\nworld\n" (fmt #f (with-width 8 (wrap-lines "hello world"))))
(test "\n" (fmt #f (wrap-lines "    ")))

(test          ;; test divide by zero error
 "The  quick
brown  fox
jumped
over   the
lazy dog
"
 (fmt #f (with-width 10 (justify "The quick brown fox jumped over the lazy dog"))))

(test "his message
(http://lists.nongnu.org/archive/html/chicken-users/2010-10/msg00171.html)
to the chicken-users
(http://lists.nongnu.org/mailman/listinfo/chicken-users)\n"
      (fmt #f (with-width 67 (wrap-lines "his message (http://lists.nongnu.org/archive/html/chicken-users/2010-10/msg00171.html) to the chicken-users (http://lists.nongnu.org/mailman/listinfo/chicken-users)"))))

(test "The fundamental list iterator.
Applies KONS to each element of
LS and the result of the previous
application, beginning with KNIL.
With KONS as CONS and KNIL as '(),
equivalent to REVERSE.
"
    (fmt #f (with-width 36 (wrap-lines "The fundamental list iterator.  Applies KONS to each element of LS and the result of the previous application, beginning with KNIL.  With KONS as CONS and KNIL as '(), equivalent to REVERSE."))))

(test
"The   fundamental   list   iterator.
Applies  KONS  to  each  element  of
LS  and  the  result of the previous
application,  beginning  with  KNIL.
With  KONS  as CONS and KNIL as '(),
equivalent to REVERSE.
"
    (fmt #f (with-width 36 (justify "The fundamental list iterator.  Applies KONS to each element of LS and the result of the previous application, beginning with KNIL.  With KONS as CONS and KNIL as '(), equivalent to REVERSE."))))

(test
"(define (fold kons knil ls)          ; The fundamental list iterator.
  (let lp ((ls ls) (acc knil))       ; Applies KONS to each element of
    (if (null? ls)                   ; LS and the result of the previous
        acc                          ; application, beginning with KNIL.
        (lp (cdr ls)                 ; With KONS as CONS and KNIL as '(),
            (kons (car ls) acc)))))  ; equivalent to REVERSE.
"
    (fmt #f (fmt-columns
             (list
              (cut pad/right 36 <>)
              (with-width 36
                (pretty '(define (fold kons knil ls)
                           (let lp ((ls ls) (acc knil))
                             (if (null? ls)
                                 acc
                                 (lp (cdr ls)
                                     (kons (car ls) acc))))))))
             (list
              (cut cat " ; " <>)
              (with-width 36
                (wrap-lines "The fundamental list iterator.  Applies KONS to each element of LS and the result of the previous application, beginning with KNIL.  With KONS as CONS and KNIL as '(), equivalent to REVERSE."))))))

(test
"(define (fold kons knil ls)          ; The fundamental list iterator.
  (let lp ((ls ls) (acc knil))       ; Applies KONS to each element of
    (if (null? ls)                   ; LS and the result of the previous
        acc                          ; application, beginning with KNIL.
        (lp (cdr ls)                 ; With KONS as CONS and KNIL as '(),
            (kons (car ls) acc)))))  ; equivalent to REVERSE.
"
    (fmt #f (with-width 76
              (columnar
               (pretty '(define (fold kons knil ls)
                          (let lp ((ls ls) (acc knil))
                            (if (null? ls)
                                acc
                                (lp (cdr ls)
                                    (kons (car ls) acc))))))
               " ; "
               (wrap-lines "The fundamental list iterator.  Applies KONS to each element of LS and the result of the previous application, beginning with KNIL.  With KONS as CONS and KNIL as '(), equivalent to REVERSE.")))))

(test
"- Item 1: The text here is
          indented according
          to the space \"Item
          1\" takes, and one
          does not known what
          goes here.
"
    (fmt #f (columnar 9 (dsp "- Item 1:") " " (with-width 20 (wrap-lines "The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here.")))))

(test
"- Item 1: The text here is
          indented according
          to the space \"Item
          1\" takes, and one
          does not known what
          goes here.
"
    (fmt #f (columnar 9 (dsp "- Item 1:\n") " " (with-width 20 (wrap-lines "The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here.")))))

(test
"- Item 1: The text here is----------------------------------------------------
--------- indented according--------------------------------------------------
--------- to the space \"Item--------------------------------------------------
--------- 1\" takes, and one---------------------------------------------------
--------- does not known what-------------------------------------------------
--------- goes here.----------------------------------------------------------
"
    (fmt #f (pad-char #\- (columnar 9 (dsp "- Item 1:\n") " " (with-width 20 (wrap-lines "The text here is indented according to the space \"Item 1\" takes, and one does not known what goes here."))))))

(test
"a   | 123
bc  | 45
def | 6
"
    (fmt #f (with-width
             20
             (tabular (dsp "a\nbc\ndef\n") " | " (dsp "123\n45\n6\n")))))

;; misc extras

(define (string-hide-passwords str)
  (string-substitute (regexp "(pass(?:w(?:or)?d)?\\s?[:=>]\\s+)\\S+" #t)
                     "\\1******"
                     str
                     #t))

(define hide-passwords
  (make-string-fmt-transformer string-hide-passwords))

(define (string-mangle-email str)
  (string-substitute
   (regexp "\\b([-+.\\w]+)@((?:[-+\\w]+\\.)+[a-z]{2,4})\\b" #t)
   "\\1 _at_ \\2"
   str
   #t))

(define mangle-email
  (make-string-fmt-transformer string-mangle-email))

(test-end)

Added fmt/test-round.scm.























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

(use fmt test)
;;(use numbers) ; test with and without numbers via -R numbers

(define (check-representation n)
  (define pence
    (inexact->exact (round (/ (modulo n 1000) 10))))
  (define pounds (quotient n 1000))

  (if (> pence 99)
      (begin
        (set! pence (- 100 pence))
        (set! pounds (add1 pounds))))

  (define expected-result
    (cond
     ((= pence 0) (sprintf "~S.00" pounds))
     ((< pence 10) (sprintf "~S.0~S" pounds pence))
     (else (sprintf "~S.~S" pounds pence))))

  (test (sprintf "~S = ~S?" (exact->inexact (/ n 1000)) expected-result)
      expected-result
    (fmt #f (num (/ n 1000) 10 2))))

(test-begin)
(for-each check-representation (iota 10000))
(test-end)

Added match.scm.









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
;;;; match.scm -- portable hygienic pattern matcher -*- coding: utf-8 -*-
;;
;; This code is written by Alex Shinn and placed in the
;; Public Domain.  All warranties are disclaimed.

;;> \example-import[(srfi 9)]

;;> A portable hygienic pattern matcher.

;;> This is a full superset of the popular \hyperlink[
;;> "http://www.cs.indiana.edu/scheme-repository/code.match.html"]{match}
;;> package by Andrew Wright, written in fully portable \scheme{syntax-rules}
;;> and thus preserving hygiene.

;;> The most notable extensions are the ability to use \emph{non-linear}
;;> patterns - patterns in which the same identifier occurs multiple
;;> times, tail patterns after ellipsis, and the experimental tree patterns.

;;> \section{Patterns}

;;> Patterns are written to look like the printed representation of
;;> the objects they match.  The basic usage is

;;> \scheme{(match expr (pat body ...) ...)}

;;> where the result of \var{expr} is matched against each pattern in
;;> turn, and the corresponding body is evaluated for the first to
;;> succeed.  Thus, a list of three elements matches a list of three
;;> elements.

;;> \example{(let ((ls (list 1 2 3))) (match ls ((1 2 3) #t)))}

;;> If no patterns match an error is signalled.

;;> Identifiers will match anything, and make the corresponding
;;> binding available in the body.

;;> \example{(match (list 1 2 3) ((a b c) b))}

;;> If the same identifier occurs multiple times, the first instance
;;> will match anything, but subsequent instances must match a value
;;> which is \scheme{equal?} to the first.

;;> \example{(match (list 1 2 1) ((a a b) 1) ((a b a) 2))}

;;> The special identifier \scheme{_} matches anything, no matter how
;;> many times it is used, and does not bind the result in the body.

;;> \example{(match (list 1 2 1) ((_ _ b) 1) ((a b a) 2))}

;;> To match a literal identifier (or list or any other literal), use
;;> \scheme{quote}.

;;> \example{(match 'a ('b 1) ('a 2))}

;;> Analogous to its normal usage in scheme, \scheme{quasiquote} can
;;> be used to quote a mostly literally matching object with selected
;;> parts unquoted.

;;> \example|{(match (list 1 2 3) (`(1 ,b ,c) (list b c)))}|

;;> Often you want to match any number of a repeated pattern.  Inside
;;> a list pattern you can append \scheme{...} after an element to
;;> match zero or more of that pattern (like a regexp Kleene star).

;;> \example{(match (list 1 2) ((1 2 3 ...) #t))}
;;> \example{(match (list 1 2 3) ((1 2 3 ...) #t))}
;;> \example{(match (list 1 2 3 3 3) ((1 2 3 ...) #t))}

;;> Pattern variables matched inside the repeated pattern are bound to
;;> a list of each matching instance in the body.

;;> \example{(match (list 1 2) ((a b c ...) c))}
;;> \example{(match (list 1 2 3) ((a b c ...) c))}
;;> \example{(match (list 1 2 3 4 5) ((a b c ...) c))}

;;> More than one \scheme{...} may not be used in the same list, since
;;> this would require exponential backtracking in the general case.
;;> However, \scheme{...} need not be the final element in the list,
;;> and may be succeeded by a fixed number of patterns.

;;> \example{(match (list 1 2 3 4) ((a b c ... d e) c))}
;;> \example{(match (list 1 2 3 4 5) ((a b c ... d e) c))}
;;> \example{(match (list 1 2 3 4 5 6 7) ((a b c ... d e) c))}

;;> \scheme{___} is provided as an alias for \scheme{...} when it is
;;> inconvenient to use the ellipsis (as in a syntax-rules template).

;;> The \scheme{..1} syntax is exactly like the \scheme{...} except
;;> that it matches one or more repetitions (like a regexp "+").

;;> \example{(match (list 1 2) ((a b c ..1) c))}
;;> \example{(match (list 1 2 3) ((a b c ..1) c))}

;;> The boolean operators \scheme{and}, \scheme{or} and \scheme{not}
;;> can be used to group and negate patterns analogously to their
;;> Scheme counterparts.

;;> The \scheme{and} operator ensures that all subpatterns match.
;;> This operator is often used with the idiom \scheme{(and x pat)} to
;;> bind \var{x} to the entire value that matches \var{pat}
;;> (c.f. "as-patterns" in ML or Haskell).  Another common use is in
;;> conjunction with \scheme{not} patterns to match a general case
;;> with certain exceptions.

;;> \example{(match 1 ((and) #t))}
;;> \example{(match 1 ((and x) x))}
;;> \example{(match 1 ((and x 1) x))}

;;> The \scheme{or} operator ensures that at least one subpattern
;;> matches.  If the same identifier occurs in different subpatterns,
;;> it is matched independently.  All identifiers from all subpatterns
;;> are bound if the \scheme{or} operator matches, but the binding is
;;> only defined for identifiers from the subpattern which matched.

;;> \example{(match 1 ((or) #t) (else #f))}
;;> \example{(match 1 ((or x) x))}
;;> \example{(match 1 ((or x 2) x))}

;;> The \scheme{not} operator succeeds if the given pattern doesn't
;;> match.  None of the identifiers used are available in the body.

;;> \example{(match 1 ((not 2) #t))}

;;> The more general operator \scheme{?} can be used to provide a
;;> predicate.  The usage is \scheme{(? predicate pat ...)} where
;;> \var{predicate} is a Scheme expression evaluating to a predicate
;;> called on the value to match, and any optional patterns after the
;;> predicate are then matched as in an \scheme{and} pattern.

;;> \example{(match 1 ((? odd? x) x))}

;;> The field operator \scheme{=} is used to extract an arbitrary
;;> field and match against it.  It is useful for more complex or
;;> conditional destructuring that can't be more directly expressed in
;;> the pattern syntax.  The usage is \scheme{(= field pat)}, where
;;> \var{field} can be any expression, and should result in a
;;> procedure of one argument, which is applied to the value to match
;;> to generate a new value to match against \var{pat}.

;;> Thus the pattern \scheme{(and (= car x) (= cdr y))} is equivalent
;;> to \scheme{(x . y)}, except it will result in an immediate error
;;> if the value isn't a pair.

;;> \example{(match '(1 . 2) ((= car x) x))}
;;> \example{(match 4 ((= square x) x))}

;;> The record operator \scheme{$} is used as a concise way to match
;;> records defined by SRFI-9 (or SRFI-99).  The usage is
;;> \scheme{($ rtd field ...)}, where \var{rtd} should be the record
;;> type descriptor specified as the first argument to
;;> \scheme{define-record-type}, and each \var{field} is a subpattern
;;> matched against the fields of the record in order.  Not all fields
;;> must be present.

;;> \example{
;;> (let ()
;;>   (define-record-type employee
;;>     (make-employee name title)
;;>     employee?
;;>     (name get-name)
;;>     (title get-title))
;;>   (match (make-employee "Bob" "Doctor")
;;>     (($ employee n t) (list t n))))
;;> }

;;> For records with more fields it can be helpful to match them by
;;> name rather than position.  For this you can use the \scheme{@}
;;> operator, originally a Gauche extension:

;;> \example{
;;> (let ()
;;>   (define-record-type employee
;;>     (make-employee name title)
;;>     employee?
;;>     (name get-name)
;;>     (title get-title))
;;>   (match (make-employee "Bob" "Doctor")
;;>     ((@ employee (title t) (name n)) (list t n))))
;;> }

;;> The \scheme{set!} and \scheme{get!} operators are used to bind an
;;> identifier to the setter and getter of a field, respectively.  The
;;> setter is a procedure of one argument, which mutates the field to
;;> that argument.  The getter is a procedure of no arguments which
;;> returns the current value of the field.

;;> \example{(let ((x (cons 1 2))) (match x ((1 . (set! s)) (s 3) x)))}
;;> \example{(match '(1 . 2) ((1 . (get! g)) (g)))}

;;> The new operator \scheme{***} can be used to search a tree for
;;> subpatterns.  A pattern of the form \scheme{(x *** y)} represents
;;> the subpattern \var{y} located somewhere in a tree where the path
;;> from the current object to \var{y} can be seen as a list of the
;;> form \scheme{(x ...)}.  \var{y} can immediately match the current
;;> object in which case the path is the empty list.  In a sense it's
;;> a 2-dimensional version of the \scheme{...} pattern.

;;> As a common case the pattern \scheme{(_ *** y)} can be used to
;;> search for \var{y} anywhere in a tree, regardless of the path
;;> used.

;;> \example{(match '(a (a (a b))) ((x *** 'b) x))}
;;> \example{(match '(a (b) (c (d e) (f g))) ((x *** 'g) x))}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Notes

;; The implementation is a simple generative pattern matcher - each
;; pattern is expanded into the required tests, calling a failure
;; continuation if the tests fail.  This makes the logic easy to
;; follow and extend, but produces sub-optimal code in cases where you
;; have many similar clauses due to repeating the same tests.
;; Nonetheless a smart compiler should be able to remove the redundant
;; tests.  For MATCH-LET and DESTRUCTURING-BIND type uses there is no
;; performance hit.

;; The original version was written on 2006/11/29 and described in the
;; following Usenet post:
;;   http://groups.google.com/group/comp.lang.scheme/msg/0941234de7112ffd
;; and is still available at
;;   http://synthcode.com/scheme/match-simple.scm
;; It's just 80 lines for the core MATCH, and an extra 40 lines for
;; MATCH-LET, MATCH-LAMBDA and other syntactic sugar.
;;
;; A variant of this file which uses COND-EXPAND in a few places for
;; performance can be found at
;;   http://synthcode.com/scheme/match-cond-expand.scm
;;
;; 2016/03/06 - fixing named match-let (thanks to Stefan Israelsson Tampe)
;; 2015/05/09 - fixing bug in var extraction of quasiquote patterns
;; 2014/11/24 - adding Gauche's `@' pattern for named record field matching
;; 2012/12/26 - wrapping match-let&co body in lexical closure
;; 2012/11/28 - fixing typo s/vetor/vector in largely unused set! code
;; 2012/05/23 - fixing combinatorial explosion of code in certain or patterns
;; 2011/09/25 - fixing bug when directly matching an identifier repeated in
;;              the pattern (thanks to Stefan Israelsson Tampe)
;; 2011/01/27 - fixing bug when matching tail patterns against improper lists
;; 2010/09/26 - adding `..1' patterns (thanks to Ludovic Courtès)
;; 2010/09/07 - fixing identifier extraction in some `...' and `***' patterns
;; 2009/11/25 - adding `***' tree search patterns
;; 2008/03/20 - fixing bug where (a ...) matched non-lists
;; 2008/03/15 - removing redundant check in vector patterns
;; 2008/03/06 - you can use `...' portably now (thanks to Taylor Campbell)
;; 2007/09/04 - fixing quasiquote patterns
;; 2007/07/21 - allowing ellipsis patterns in non-final list positions
;; 2007/04/10 - fixing potential hygiene issue in match-check-ellipsis
;;              (thanks to Taylor Campbell)
;; 2007/04/08 - clean up, commenting
;; 2006/12/24 - bugfixes
;; 2006/12/01 - non-linear patterns, shared variables in OR, get!/set!

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; force compile-time syntax errors with useful messages

(define-syntax match-syntax-error
  (syntax-rules ()
    ((_) (match-syntax-error "invalid match-syntax-error usage"))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;> \section{Syntax}

;;> \macro{(match expr (pattern . body) ...)\br{}
;;> (match expr (pattern (=> failure) . body) ...)}

;;> The result of \var{expr} is matched against each \var{pattern} in
;;> turn, according to the pattern rules described in the previous
;;> section, until the the first \var{pattern} matches.  When a match is
;;> found, the corresponding \var{body}s are evaluated in order,
;;> and the result of the last expression is returned as the result
;;> of the entire \scheme{match}.  If a \var{failure} is provided,
;;> then it is bound to a procedure of no arguments which continues,
;;> processing at the next \var{pattern}.  If no \var{pattern} matches,
;;> an error is signalled.

;; The basic interface.  MATCH just performs some basic syntax
;; validation, binds the match expression to a temporary variable `v',
;; and passes it on to MATCH-NEXT.  It's a constant throughout the
;; code below that the binding `v' is a direct variable reference, not
;; an expression.

(define-syntax match
  (syntax-rules ()
    ((match)
     (match-syntax-error "missing match expression"))
    ((match atom)
     (match-syntax-error "no match clauses"))
    ((match (app ...) (pat . body) ...)
     (let ((v (app ...)))
       (match-next v ((app ...) (set! (app ...))) (pat . body) ...)))
    ((match #(vec ...) (pat . body) ...)
     (let ((v #(vec ...)))
       (match-next v (v (set! v)) (pat . body) ...)))
    ((match atom (pat . body) ...)
     (let ((v atom))
       (match-next v (atom (set! atom)) (pat . body) ...)))
    ))

;; MATCH-NEXT passes each clause to MATCH-ONE in turn with its failure
;; thunk, which is expanded by recursing MATCH-NEXT on the remaining
;; clauses.  `g+s' is a list of two elements, the get! and set!
;; expressions respectively.

(define-syntax match-next
  (syntax-rules (=>)
    ;; no more clauses, the match failed
    ((match-next v g+s)
     (error 'match "no matching pattern"))
    ;; named failure continuation
    ((match-next v g+s (pat (=> failure) . body) . rest)
     (let ((failure (lambda () (match-next v g+s . rest))))
       ;; match-one analyzes the pattern for us
       (match-one v pat g+s (match-drop-ids (begin . body)) (failure) ())))
    ;; anonymous failure continuation, give it a dummy name
    ((match-next v g+s (pat . body) . rest)
     (match-next v g+s (pat (=> failure) . body) . rest))))

;; MATCH-ONE first checks for ellipsis patterns, otherwise passes on to
;; MATCH-TWO.

(define-syntax match-one
  (syntax-rules ()
    ;; If it's a list of two or more values, check to see if the
    ;; second one is an ellipsis and handle accordingly, otherwise go
    ;; to MATCH-TWO.
    ((match-one v (p q . r) g+s sk fk i)
     (match-check-ellipsis
      q
      (match-extract-vars p (match-gen-ellipsis v p r  g+s sk fk i) i ())
      (match-two v (p q . r) g+s sk fk i)))
    ;; Go directly to MATCH-TWO.
    ((match-one . x)
     (match-two . x))))

;; This is the guts of the pattern matcher.  We are passed a lot of
;; information in the form:
;;
;;   (match-two var pattern getter setter success-k fail-k (ids ...))
;;
;; usually abbreviated
;;
;;   (match-two v p g+s sk fk i)
;;
;; where VAR is the symbol name of the current variable we are
;; matching, PATTERN is the current pattern, getter and setter are the
;; corresponding accessors (e.g. CAR and SET-CAR! of the pair holding
;; VAR), SUCCESS-K is the success continuation, FAIL-K is the failure
;; continuation (which is just a thunk call and is thus safe to expand
;; multiple times) and IDS are the list of identifiers bound in the
;; pattern so far.

;; Replace '_' with ':_' as the former is forbidden as an auxiliariy
;; keyword in R6RS. (FBE)
(define-syntax match-two
  (syntax-rules (:_ ___ ..1 *** quote quasiquote ? $ struct @ object = and or not set! get!)
    ((match-two v () g+s (sk ...) fk i)
     (if (null? v) (sk ... i) fk))
    ((match-two v (quote p) g+s (sk ...) fk i)
     (if (equal? v 'p) (sk ... i) fk))
    ((match-two v (quasiquote p) . x)
     (match-quasiquote v p . x))
    ((match-two v (and) g+s (sk ...) fk i) (sk ... i))
    ((match-two v (and p q ...) g+s sk fk i)
     (match-one v p g+s (match-one v (and q ...) g+s sk fk) fk i))
    ((match-two v (or) g+s sk fk i) fk)
    ((match-two v (or p) . x)
     (match-one v p . x))
    ((match-two v (or p ...) g+s sk fk i)
     (match-extract-vars (or p ...) (match-gen-or v (p ...) g+s sk fk i) i ()))
    ((match-two v (not p) g+s (sk ...) fk i)
     (match-one v p g+s (match-drop-ids fk) (sk ... i) i))
    ((match-two v (get! getter) (g s) (sk ...) fk i)
     (let ((getter (lambda () g))) (sk ... i)))
    ((match-two v (set! setter) (g (s ...)) (sk ...) fk i)
     (let ((setter (lambda (x) (s ... x)))) (sk ... i)))
    ((match-two v (? pred . p) g+s sk fk i)
     (if (pred v) (match-one v (and . p) g+s sk fk i) fk))
    ((match-two v (= proc p) . x)
     (let ((w (proc v))) (match-one w p . x)))
    ((match-two v (p ___ . r) g+s sk fk i)
     (match-extract-vars p (match-gen-ellipsis v p r g+s sk fk i) i ()))
    ((match-two v (p) g+s sk fk i)
     (if (and (pair? v) (null? (cdr v)))
         (let ((w (car v)))
           (match-one w p ((car v) (set-car! v)) sk fk i))
         fk))
    ((match-two v (p *** q) g+s sk fk i)
     (match-extract-vars p (match-gen-search v p q g+s sk fk i) i ()))
    ((match-two v (p *** . q) g+s sk fk i)
     (match-syntax-error "invalid use of ***" (p *** . q)))
    ((match-two v (p ..1) g+s sk fk i)
     (if (pair? v)
         (match-one v (p ___) g+s sk fk i)
         fk))
    ((match-two v ($ rec p ...) g+s sk fk i)
     (if (is-a? v rec)
         (match-record-refs v rec 0 (p ...) g+s sk fk i)
         fk))
    ((match-two v (struct rec p ...) g+s sk fk i)
     (if (is-a? v rec)
         (match-record-refs v rec 0 (p ...) g+s sk fk i)
         fk))
    ((match-two v (@ rec p ...) g+s sk fk i)
     (if (is-a? v rec)
         (match-record-named-refs v rec (p ...) g+s sk fk i)
         fk))
    ((match-two v (object rec p ...) g+s sk fk i)
     (if (is-a? v rec)
         (match-record-named-refs v rec (p ...) g+s sk fk i)
         fk))
    ((match-two v (p . q) g+s sk fk i)
     (if (pair? v)
         (let ((w (car v)) (x (cdr v)))
           (match-one w p ((car v) (set-car! v))
                      (match-one x q ((cdr v) (set-cdr! v)) sk fk)
                      fk
                      i))
         fk))
    ((match-two v #(p ...) g+s . x)
     (match-vector v 0 () (p ...) . x))
    ;; Next line: replace '_' with ':_'. (FBE)
    ((match-two v :_ g+s (sk ...) fk i) (sk ... i))
    ;; Not a pair or vector or special literal, test to see if it's a
    ;; new symbol, in which case we just bind it, or if it's an
    ;; already bound symbol or some other literal, in which case we
    ;; compare it with EQUAL?.
    ((match-two v x g+s (sk ...) fk (id ...))
     (let-syntax
         ((new-sym?
           (syntax-rules (id ...)
             ((new-sym? x sk2 fk2) sk2)
             ((new-sym? y sk2 fk2) fk2))))
       (new-sym? random-sym-to-match
                 (let ((x v)) (sk ... (id ... x)))
                 (if (equal? v x) (sk ... (id ...)) fk))))
    ))

;; QUASIQUOTE patterns

(define-syntax match-quasiquote
  (syntax-rules (unquote unquote-splicing quasiquote)
    ((_ v (unquote p) g+s sk fk i)
     (match-one v p g+s sk fk i))
    ((_ v ((unquote-splicing p) . rest) g+s sk fk i)
     (if (pair? v)
       (match-one v
                  (p . tmp)
                  (match-quasiquote tmp rest g+s sk fk)
                  fk
                  i)
       fk))
    ((_ v (quasiquote p) g+s sk fk i . depth)
     (match-quasiquote v p g+s sk fk i #f . depth))
    ((_ v (unquote p) g+s sk fk i x . depth)
     (match-quasiquote v p g+s sk fk i . depth))
    ((_ v (unquote-splicing p) g+s sk fk i x . depth)
     (match-quasiquote v p g+s sk fk i . depth))
    ((_ v (p . q) g+s sk fk i . depth)
     (if (pair? v)
       (let ((w (car v)) (x (cdr v)))
         (match-quasiquote
          w p g+s
          (match-quasiquote-step x q g+s sk fk depth)
          fk i . depth))
       fk))
    ((_ v #(elt ...) g+s sk fk i . depth)
     (if (vector? v)
       (let ((ls (vector->list v)))
         (match-quasiquote ls (elt ...) g+s sk fk i . depth))
       fk))
    ((_ v x g+s sk fk i . depth)
     (match-one v 'x g+s sk fk i))))

(define-syntax match-quasiquote-step
  (syntax-rules ()
    ((match-quasiquote-step x q g+s sk fk depth i)
     (match-quasiquote x q g+s sk fk i . depth))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Utilities

;; Takes two values and just expands into the first.
(define-syntax match-drop-ids
  (syntax-rules ()
    ((_ expr ids ...) expr)))

(define-syntax match-tuck-ids
  (syntax-rules ()
    ((_ (letish args (expr ...)) ids ...)
     (letish args (expr ... ids ...)))))

(define-syntax match-drop-first-arg
  (syntax-rules ()
    ((_ arg expr) expr)))

;; To expand an OR group we try each clause in succession, passing the
;; first that succeeds to the success continuation.  On failure for
;; any clause, we just try the next clause, finally resorting to the
;; failure continuation fk if all clauses fail.  The only trick is
;; that we want to unify the identifiers, so that the success
;; continuation can refer to a variable from any of the OR clauses.

(define-syntax match-gen-or
  (syntax-rules ()
    ((_ v p g+s (sk ...) fk (i ...) ((id id-ls) ...))
     (let ((sk2 (lambda (id ...) (sk ... (i ... id ...)))))
       (match-gen-or-step v p g+s (match-drop-ids (sk2 id ...)) fk (i ...))))))

(define-syntax match-gen-or-step
  (syntax-rules ()
    ((_ v () g+s sk fk . x)
     ;; no OR clauses, call the failure continuation
     fk)
    ((_ v (p) . x)
     ;; last (or only) OR clause, just expand normally
     (match-one v p . x))
    ((_ v (p . q) g+s sk fk i)
     ;; match one and try the remaining on failure
     (let ((fk2 (lambda () (match-gen-or-step v q g+s sk fk i))))
       (match-one v p g+s sk (fk2) i)))
    ))

;; We match a pattern (p ...) by matching the pattern p in a loop on
;; each element of the variable, accumulating the bound ids into lists.

;; Look at the body of the simple case - it's just a named let loop,
;; matching each element in turn to the same pattern.  The only trick
;; is that we want to keep track of the lists of each extracted id, so
;; when the loop recurses we cons the ids onto their respective list
;; variables, and on success we bind the ids (what the user input and
;; expects to see in the success body) to the reversed accumulated
;; list IDs.

(define-syntax match-gen-ellipsis
  (syntax-rules ()
    ((_ v p () g+s (sk ...) fk i ((id id-ls) ...))
     (match-check-identifier p
       ;; simplest case equivalent to (p ...), just bind the list
       (let ((p v))
         (if (list? p)
             (sk ... i)
             fk))
       ;; simple case, match all elements of the list
       (let loop ((ls v) (id-ls '()) ...)
         (cond
           ((null? ls)
            (let ((id (reverse id-ls)) ...) (sk ... i)))
           ((pair? ls)
            (let ((w (car ls)))
              (match-one w p ((car ls) (set-car! ls))
                         (match-drop-ids (loop (cdr ls) (cons id id-ls) ...))
                         fk i)))
           (else
            fk)))))
    ((_ v p r g+s (sk ...) fk i ((id id-ls) ...))
     ;; general case, trailing patterns to match, keep track of the
     ;; remaining list length so we don't need any backtracking
     (match-verify-no-ellipsis
      r
      (let* ((tail-len (length 'r))
             (ls v)
             (len (and (list? ls) (length ls))))
        (if (or (not len) (< len tail-len))
            fk
            (let loop ((ls ls) (n len) (id-ls '()) ...)
              (cond
                ((= n tail-len)
                 (let ((id (reverse id-ls)) ...)
                   (match-one ls r (#f #f) (sk ...) fk i)))
                ((pair? ls)
                 (let ((w (car ls)))
                   (match-one w p ((car ls) (set-car! ls))
                              (match-drop-ids
                               (loop (cdr ls) (- n 1) (cons id id-ls) ...))
                              fk
                              i)))
                (else
                 fk)))))))))

;; This is just a safety check.  Although unlike syntax-rules we allow
;; trailing patterns after an ellipsis, we explicitly disable multiple
;; ellipsis at the same level.  This is because in the general case
;; such patterns are exponential in the number of ellipsis, and we
;; don't want to make it easy to construct very expensive operations
;; with simple looking patterns.  For example, it would be O(n^2) for
;; patterns like (a ... b ...) because we must consider every trailing
;; element for every possible break for the leading "a ...".

(define-syntax match-verify-no-ellipsis
  (syntax-rules ()
    ((_ (x . y) sk)
     (match-check-ellipsis
      x
      (match-syntax-error
       "multiple ellipsis patterns not allowed at same level")
      (match-verify-no-ellipsis y sk)))
    ((_ () sk)
     sk)
    ((_ x sk)
     (match-syntax-error "dotted tail not allowed after ellipsis" x))))

;; To implement the tree search, we use two recursive procedures.  TRY
;; attempts to match Y once, and on success it calls the normal SK on
;; the accumulated list ids as in MATCH-GEN-ELLIPSIS.  On failure, we
;; call NEXT which first checks if the current value is a list
;; beginning with X, then calls TRY on each remaining element of the
;; list.  Since TRY will recursively call NEXT again on failure, this
;; effects a full depth-first search.
;;
;; The failure continuation throughout is a jump to the next step in
;; the tree search, initialized with the original failure continuation
;; FK.

(define-syntax match-gen-search
  (syntax-rules ()
    ((match-gen-search v p q g+s sk fk i ((id id-ls) ...))
     (letrec ((try (lambda (w fail id-ls ...)
                     (match-one w q g+s
                                (match-tuck-ids
                                 (let ((id (reverse id-ls)) ...)
                                   sk))
                                (next w fail id-ls ...) i)))
              (next (lambda (w fail id-ls ...)
                      (if (not (pair? w))
                          (fail)
                          (let ((u (car w)))
                            (match-one
                             u p ((car w) (set-car! w))
                             (match-drop-ids
                              ;; accumulate the head variables from
                              ;; the p pattern, and loop over the tail
                              (let ((id-ls (cons id id-ls)) ...)
                                (let lp ((ls (cdr w)))
                                  (if (pair? ls)
                                      (try (car ls)
                                           (lambda () (lp (cdr ls)))
                                           id-ls ...)
                                      (fail)))))
                             (fail) i))))))
       ;; the initial id-ls binding here is a dummy to get the right
       ;; number of '()s
       (let ((id-ls '()) ...)
         (try v (lambda () fk) id-ls ...))))))

;; Vector patterns are just more of the same, with the slight
;; exception that we pass around the current vector index being
;; matched.

(define-syntax match-vector
  (syntax-rules (___)
    ((_ v n pats (p q) . x)
     (match-check-ellipsis q
                          (match-gen-vector-ellipsis v n pats p . x)
                          (match-vector-two v n pats (p q) . x)))
    ((_ v n pats (p ___) sk fk i)
     (match-gen-vector-ellipsis v n pats p sk fk i))
    ((_ . x)
     (match-vector-two . x))))

;; Check the exact vector length, then check each element in turn.

(define-syntax match-vector-two
  (syntax-rules ()
    ((_ v n ((pat index) ...) () sk fk i)
     (if (vector? v)
         (let ((len (vector-length v)))
           (if (= len n)
               (match-vector-step v ((pat index) ...) sk fk i)
               fk))
         fk))
    ((_ v n (pats ...) (p . q) . x)
     (match-vector v (+ n 1) (pats ... (p n)) q . x))))

(define-syntax match-vector-step
  (syntax-rules ()
    ((_ v () (sk ...) fk i) (sk ... i))
    ((_ v ((pat index) . rest) sk fk i)
     (let ((w (vector-ref v index)))
       (match-one w pat ((vector-ref v index) (vector-set! v index))
                  (match-vector-step v rest sk fk)
                  fk i)))))

;; With a vector ellipsis pattern we first check to see if the vector
;; length is at least the required length.

(define-syntax match-gen-vector-ellipsis
  (syntax-rules ()
    ((_ v n ((pat index) ...) p sk fk i)
     (if (vector? v)
       (let ((len (vector-length v)))
         (if (>= len n)
           (match-vector-step v ((pat index) ...)
                              (match-vector-tail v p n len sk fk)
                              fk i)
           fk))
       fk))))

(define-syntax match-vector-tail
  (syntax-rules ()
    ((_ v p n len sk fk i)
     (match-extract-vars p (match-vector-tail-two v p n len sk fk i) i ()))))

(define-syntax match-vector-tail-two
  (syntax-rules ()
    ((_ v p n len (sk ...) fk i ((id id-ls) ...))
     (let loop ((j n) (id-ls '()) ...)
       (if (>= j len)
         (let ((id (reverse id-ls)) ...) (sk ... i))
         (let ((w (vector-ref v j)))
           (match-one w p ((vector-ref v j) (vector-set! v j))
                      (match-drop-ids (loop (+ j 1) (cons id id-ls) ...))
                      fk i)))))))

(define-syntax match-record-refs
  (syntax-rules ()
    ((_ v rec n (p . q) g+s sk fk i)
     (let ((w (slot-ref rec v n)))
       (match-one w p ((slot-ref rec v n) (slot-set! rec v n))
                  (match-record-refs v rec (+ n 1) q g+s sk fk) fk i)))
    ((_ v rec n () g+s (sk ...) fk i)
     (sk ... i))))

(define-syntax match-record-named-refs
  (syntax-rules ()
    ((_ v rec ((f p) . q) g+s sk fk i)
     (let ((w (slot-ref rec v 'f)))
       (match-one w p ((slot-ref rec v 'f) (slot-set! rec v 'f))
                  (match-record-named-refs v rec q g+s sk fk) fk i)))
    ((_ v rec () g+s (sk ...) fk i)
     (sk ... i))))

;; Extract all identifiers in a pattern.  A little more complicated
;; than just looking for symbols, we need to ignore special keywords
;; and non-pattern forms (such as the predicate expression in ?
;; patterns), and also ignore previously bound identifiers.
;;
;; Calls the continuation with all new vars as a list of the form
;; ((orig-var tmp-name) ...), where tmp-name can be used to uniquely
;; pair with the original variable (e.g. it's used in the ellipsis
;; generation for list variables).
;;
;; (match-extract-vars pattern continuation (ids ...) (new-vars ...))

;; Replace '_' with ':_' as the former is forbidden as an auxiliariy
;; keyword in R6RS. (FBE)
(define-syntax match-extract-vars
  (syntax-rules (:_ ___ ..1 *** ? $ struct @ object = quote quasiquote and or not get! set!)
    ((match-extract-vars (? pred . p) . x)
     (match-extract-vars p . x))
    ((match-extract-vars ($ rec . p) . x)
     (match-extract-vars p . x))
    ((match-extract-vars (struct rec . p) . x)
     (match-extract-vars p . x))
    ((match-extract-vars (@ rec (f p) ...) . x)
     (match-extract-vars (p ...) . x))
    ((match-extract-vars (object rec (f p) ...) . x)
     (match-extract-vars (p ...) . x))
    ((match-extract-vars (= proc p) . x)
     (match-extract-vars p . x))
    ((match-extract-vars (quote x) (k ...) i v)
     (k ... v))
    ((match-extract-vars (quasiquote x) k i v)
     (match-extract-quasiquote-vars x k i v (#t)))
    ((match-extract-vars (and . p) . x)
     (match-extract-vars p . x))
    ((match-extract-vars (or . p) . x)
     (match-extract-vars p . x))
    ((match-extract-vars (not . p) . x)
     (match-extract-vars p . x))
    ;; A non-keyword pair, expand the CAR with a continuation to
    ;; expand the CDR.
    ((match-extract-vars (p q . r) k i v)
     (match-check-ellipsis
      q
      (match-extract-vars (p . r) k i v)
      (match-extract-vars p (match-extract-vars-step (q . r) k i v) i ())))
    ((match-extract-vars (p . q) k i v)
     (match-extract-vars p (match-extract-vars-step q k i v) i ()))
    ((match-extract-vars #(p ...) . x)
     (match-extract-vars (p ...) . x))
    ;; Next line: replace '_' with ':_'. (FBE)
    ((match-extract-vars :_ (k ...) i v)    (k ... v))
    ((match-extract-vars ___ (k ...) i v)  (k ... v))
    ((match-extract-vars *** (k ...) i v)  (k ... v))
    ((match-extract-vars ..1 (k ...) i v)  (k ... v))
    ;; This is the main part, the only place where we might add a new
    ;; var if it's an unbound symbol.
    ((match-extract-vars p (k ...) (i ...) v)
     (let-syntax
         ((new-sym?
           (syntax-rules (i ...)
             ((new-sym? p sk fk) sk)
             ((new-sym? any sk fk) fk))))
       (new-sym? random-sym-to-match
                 (k ... ((p p-ls) . v))
                 (k ... v))))
    ))

;; Stepper used in the above so it can expand the CAR and CDR
;; separately.

(define-syntax match-extract-vars-step
  (syntax-rules ()
    ((_ p k i v ((v2 v2-ls) ...))
     (match-extract-vars p k (v2 ... . i) ((v2 v2-ls) ... . v)))
    ))

(define-syntax match-extract-quasiquote-vars
  (syntax-rules (quasiquote unquote unquote-splicing)
    ((match-extract-quasiquote-vars (quasiquote x) k i v d)
     (match-extract-quasiquote-vars x k i v (#t . d)))
    ((match-extract-quasiquote-vars (unquote-splicing x) k i v d)
     (match-extract-quasiquote-vars (unquote x) k i v d))
    ((match-extract-quasiquote-vars (unquote x) k i v (#t))
     (match-extract-vars x k i v))
    ((match-extract-quasiquote-vars (unquote x) k i v (#t . d))
     (match-extract-quasiquote-vars x k i v d))
    ((match-extract-quasiquote-vars (x . y) k i v d)
     (match-extract-quasiquote-vars
      x
      (match-extract-quasiquote-vars-step y k i v d) i () d))
    ((match-extract-quasiquote-vars #(x ...) k i v d)
     (match-extract-quasiquote-vars (x ...) k i v d))
    ((match-extract-quasiquote-vars x (k ...) i v d)
     (k ... v))
    ))

(define-syntax match-extract-quasiquote-vars-step
  (syntax-rules ()
    ((_ x k i v d ((v2 v2-ls) ...))
     (match-extract-quasiquote-vars x k (v2 ... . i) ((v2 v2-ls) ... . v) d))
    ))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Gimme some sugar baby.

;;> Shortcut for \scheme{lambda} + \scheme{match}.  Creates a
;;> procedure of one argument, and matches that argument against each
;;> clause.

(define-syntax match-lambda
  (syntax-rules ()
    ((_ (pattern . body) ...) (lambda (expr) (match expr (pattern . body) ...)))))

;;> Similar to \scheme{match-lambda}.  Creates a procedure of any
;;> number of arguments, and matches the argument list against each
;;> clause.

(define-syntax match-lambda*
  (syntax-rules ()
    ((_ (pattern . body) ...) (lambda expr (match expr (pattern . body) ...)))))

;;> Matches each var to the corresponding expression, and evaluates
;;> the body with all match variables in scope.  Raises an error if
;;> any of the expressions fail to match.  Syntax analogous to named
;;> let can also be used for recursive functions which match on their
;;> arguments as in \scheme{match-lambda*}.

(define-syntax match-let
  (syntax-rules ()
    ((_ ((var value) ...) . body)
     (match-let/helper let () () ((var value) ...) . body))
    ((_ loop ((var init) ...) . body)
     (match-named-let loop () ((var init) ...) . body))))

;;> Similar to \scheme{match-let}, but analogously to \scheme{letrec}
;;> matches and binds the variables with all match variables in scope.

(define-syntax match-letrec
  (syntax-rules ()
    ((_ ((var value) ...) . body)
     (match-let/helper letrec () () ((var value) ...) . body))))

(define-syntax match-let/helper
  (syntax-rules ()
    ((_ let ((var expr) ...) () () . body)
     (let ((var expr) ...) . body))
    ((_ let ((var expr) ...) ((pat tmp) ...) () . body)
     (let ((var expr) ...)
       (match-let* ((pat tmp) ...)
         . body)))
    ((_ let (v ...) (p ...) (((a . b) expr) . rest) . body)
     (match-let/helper
      let (v ... (tmp expr)) (p ... ((a . b) tmp)) rest . body))
    ((_ let (v ...) (p ...) ((#(a ...) expr) . rest) . body)
     (match-let/helper
      let (v ... (tmp expr)) (p ... (#(a ...) tmp)) rest . body))
    ((_ let (v ...) (p ...) ((a expr) . rest) . body)
     (match-let/helper let (v ... (a expr)) (p ...) rest . body))))

(define-syntax match-named-let
  (syntax-rules ()
    ((_ loop ((pat expr var) ...) () . body)
     (let loop ((var expr) ...)
       (match-let ((pat var) ...)
         . body)))
    ((_ loop (v ...) ((pat expr) . rest) . body)
     (match-named-let loop (v ... (pat expr tmp)) rest . body))))

;;> \macro{(match-let* ((var value) ...) body ...)}

;;> Similar to \scheme{match-let}, but analogously to \scheme{let*}
;;> matches and binds the variables in sequence, with preceding match
;;> variables in scope.

(define-syntax match-let*
  (syntax-rules ()
    ((_ () . body)
     (let () . body))
    ((_ ((pat expr) . rest) . body)
     (match expr (pat (match-let* rest . body))))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Otherwise COND-EXPANDed bits.

;; To avoid depending on srfi-0 we comment the following form and copy
;; the generic version below it. (FBE)

;; (cond-expand
;;  (chibi
;;   (define-syntax match-check-ellipsis
;;     (er-macro-transformer
;;      (lambda (expr rename compare)
;;        (if (compare '... (cadr expr))
;;            (car (cddr expr))
;;            (cadr (cddr expr))))))
;;   (define-syntax match-check-identifier
;;     (er-macro-transformer
;;      (lambda (expr rename compare)
;;        (if (identifier? (cadr expr))
;;            (car (cddr expr))
;;            (cadr (cddr expr)))))))

;;  (else
;;   ;; Portable versions
;;   ;;
;;   ;; This *should* work, but doesn't :(
;;   ;;   (define-syntax match-check-ellipsis
;;   ;;     (syntax-rules (...)
;;   ;;       ((_ ... sk fk) sk)
;;   ;;       ((_ x sk fk) fk)))
;;   ;;
;;   ;; This is a little more complicated, and introduces a new let-syntax,
;;   ;; but should work portably in any R[56]RS Scheme.  Taylor Campbell
;;   ;; originally came up with the idea.
;;   (define-syntax match-check-ellipsis
;;     (syntax-rules ()
;;       ;; these two aren't necessary but provide fast-case failures
;;       ((match-check-ellipsis (a . b) success-k failure-k) failure-k)
;;       ((match-check-ellipsis #(a ...) success-k failure-k) failure-k)
;;       ;; matching an atom
;;       ((match-check-ellipsis id success-k failure-k)
;;        (let-syntax ((ellipsis? (syntax-rules ()
;;                                  ;; iff `id' is `...' here then this will
;;                                  ;; match a list of any length
;;                                  ((ellipsis? (foo id) sk fk) sk)
;;                                  ((ellipsis? other sk fk) fk))))
;;          ;; this list of three elements will only match the (foo id) list
;;          ;; above if `id' is `...'
;;          (ellipsis? (a b c) success-k failure-k)))))

;;   ;; This is portable but can be more efficient with non-portable
;;   ;; extensions.  This trick was originally discovered by Oleg Kiselyov.
;;   (define-syntax match-check-identifier
;;     (syntax-rules ()
;;       ;; fast-case failures, lists and vectors are not identifiers
;;       ((_ (x . y) success-k failure-k) failure-k)
;;       ((_ #(x ...) success-k failure-k) failure-k)
;;       ;; x is an atom
;;       ((_ x success-k failure-k)
;;        (let-syntax
;;            ((sym?
;;              (syntax-rules ()
;;                ;; if the symbol `abracadabra' matches x, then x is a
;;                ;; symbol
;;                ((sym? x sk fk) sk)
;;                ;; otherwise x is a non-symbol datum
;;                ((sym? y sk fk) fk))))
;;          (sym? abracadabra success-k failure-k)))))))

;; Portable versions
;;
;; This *should* work, but doesn't :(
;;   (define-syntax match-check-ellipsis
;;     (syntax-rules (...)
;;       ((_ ... sk fk) sk)
;;       ((_ x sk fk) fk)))
;;
;; This is a little more complicated, and introduces a new let-syntax,
;; but should work portably in any R[56]RS Scheme.  Taylor Campbell
;; originally came up with the idea.
(define-syntax match-check-ellipsis
  (syntax-rules ()
    ;; these two aren't necessary but provide fast-case failures
    ((match-check-ellipsis (a . b) success-k failure-k) failure-k)
    ((match-check-ellipsis #(a ...) success-k failure-k) failure-k)
    ;; matching an atom
    ((match-check-ellipsis id success-k failure-k)
     (let-syntax ((ellipsis? (syntax-rules ()
                               ;; iff `id' is `...' here then this will
                               ;; match a list of any length
                               ((ellipsis? (foo id) sk fk) sk)
                               ((ellipsis? other sk fk) fk))))
       ;; this list of three elements will only match the (foo id) list
       ;; above if `id' is `...'
       (ellipsis? (a b c) success-k failure-k)))))

;; This is portable but can be more efficient with non-portable
;; extensions.  This trick was originally discovered by Oleg Kiselyov.
(define-syntax match-check-identifier
  (syntax-rules ()
    ;; fast-case failures, lists and vectors are not identifiers
    ((_ (x . y) success-k failure-k) failure-k)
    ((_ #(x ...) success-k failure-k) failure-k)
    ;; x is an atom
    ((_ x success-k failure-k)
     (let-syntax
         ((sym?
           (syntax-rules ()
             ;; if the symbol `abracadabra' matches x, then x is a
             ;; symbol
             ((sym? x sk fk) sk)
             ;; otherwise x is a non-symbol datum
             ((sym? y sk fk) fk))))
       (sym? abracadabra success-k failure-k)))))

Added matchable.sls.





















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
;;; Chez-Scheme Wrappers for Alex Shinn's Match (Wright Compatible)
;;; 
;;; Copyright (c) 2016 Federico Beffa <beffa@fbengineering.ch>
;;; 
;;; Permission to use, copy, modify, and distribute this software for
;;; any purpose with or without fee is hereby granted, provided that the
;;; above copyright notice and this permission notice appear in all
;;; copies.
;;; 
;;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
;;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
;;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
;;; DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
;;; OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
;;; TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
;;; PERFORMANCE OF THIS SOFTWARE.

;; The reader in #!r6rs mode doesn't allow the '..1' identifier.
#!chezscheme
(library (matchable)
  (export 
    match
    match-lambda 
    match-lambda* 
    match-let 
    match-let* 
    match-letrec
    match-named-let
    :_ ___ ..1 *** ? $ struct @ object)
  
  #;(import 
   (rnrs base)
   (rnrs lists)
   (rnrs mutable-pairs)
   (rnrs records syntactic)
   (rnrs records procedural)
   (rnrs records inspection)
   (rnrs syntax-case)
   (only (chezscheme) iota include)
   ;; avoid dependence on chez-srfi (apart for tests)
   ;; (srfi private aux-keywords)
   ;; (srfi private include)
   )
   (import (chezscheme))

  ;; We declare end export the symbols used as auxiliary identifiers
  ;; in 'syntax-rules' to make them work in Chez Scheme's interactive
  ;; environment. (FBE)

  ;; Also we replaced '_' with ':_' as the special identifier matching
  ;; anything and not binding.  This is because R6RS forbids its use
  ;; as an auxiliary literal in a syntax-rules form.
  (define-syntax define-auxiliary-keyword
    (syntax-rules ()
      [(_ name)
       (define-syntax name 
         (lambda (x)
           (syntax-violation #f "misplaced use of auxiliary keyword" x)))]))

  (define-syntax define-auxiliary-keywords
    (syntax-rules ()
      [(_ name* ...)
       (begin (define-auxiliary-keyword name*) ...)]))

  (define-auxiliary-keywords :_ ___ ..1 *** ? $ struct @ object)

  (define-syntax is-a?
    (syntax-rules ()
      ((_ rec rtn)
       (and (record? rec)
            (eq? (record-type-name (record-rtd rec)) (quote rtn))))))

  (define-syntax slot-ref
    (syntax-rules ()
      ((_ rtn rec n)
       (if (number? n)
           ((record-accessor (record-rtd rec) n) rec)
           ;; If it's not a number, then it should be a symbol with
           ;; the name of a field.
           (let* ((rtd (record-rtd rec))
                  (fields (record-type-field-names rtd))
                  (fields-idxs (map (lambda (f i) (cons f i))
                                    (vector->list fields)
                                    (iota (vector-length fields))))
                  (idx (cdr (assv n fields-idxs))))
             ((record-accessor rtd idx) rec))))))

  (define-syntax slot-set!
    (syntax-rules ()
      ((_ rtn rec n)
       (if (number? n)
           ((record-mutator (record-rtd rec) n) rec)
           ;; If it's not a number, then it should be a symbol with
           ;; the name of a field.
           (let* ((rtd (record-rtd rec))
                  (fields (record-type-field-names rtd))
                  (fields-idxs (map (lambda (f i) (cons f i))
                                    (vector->list fields)
                                    (iota (vector-length fields))))
                  (idx (cdr (assv n fields-idxs))))
             ((record-mutator rtd idx) rec))))))
  
  (include "match.scm")

  )

Added sql-null.sls.







































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
;;; "sql-null.scm" -- SQL NULL object and the ternary logic  -*- Scheme -*-

;;; Ivan Shmakov, 2007  This code is in public domain.

(library (sql-null)
  (export sql-null sql-null? sql-not sql-or sql-or sql-and sql-coalesce)

  (import (chezscheme))

  ;;from chicken data-structures.scm  Copyright (c) 2008-2014, The Chicken Team
  (define (constantly . xs)
    (if (eq? 1 (length xs))
	(let ([x (car xs)])
	  (lambda _ x) )
	(lambda _ (apply values xs)) ) )

  ;; We could also (define-record sql-null) and alias sql-null to make-sql-null
  ;; but that implies creating many new objects, which we don't want.
  (define-record-type sql-null-type)
  (define sql-null-object (make-sql-null-type))
  (define sql-null (constantly sql-null-object))
  (define sql-null? sql-null-type?)

  (define (sql-not o)
    (if (sql-null? o) o (not o)))

  (define-syntax sql-or
    (syntax-rules ()
      ((sql-or a ...)
       (sql-or/null #f a ...))))

  (define-syntax sql-or/null
    (syntax-rules ()
      ((sql-or/null null)
       null)
      ((sql-or/null null a b ...)
       (let ((ea a))
	 (cond ((sql-null? ea) (sql-or/null ea    b ...))
	       ((not ea)       (sql-or/null null b ...))
	       (else           ea))))))

  (define-syntax sql-and
    (syntax-rules ()
      ((sql-and a ...)
       (sql-and/null #t a ...))))

  (define-syntax sql-and/null
    (syntax-rules ()
      ((sql-and/null null)
       null)
      ((sql-and/null null a b ...)
       (let ((ea a))
	 (cond ((sql-null? ea) (sql-and/null ea    b ...))
	       (ea             (sql-and/null null b ...))
	       (else           ea))))))

  (define-syntax sql-coalesce
    (syntax-rules ()
      ((sql-coalesce)
       (sql-null))
      ((sql-coalesce a b ...)
       (let ((ea a))
	 (if (sql-null? ea)
	     (sql-coalesce b ...)
	     ea)))))

  )

Added sqlite3.scm.





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
;;; Foreign types & values

(define (hashtable-walk ht f) 
  (vector-for-each (lambda (x) 
		     (f x (hashtable-ref ht x #f))) 
		   (hashtable-keys ht)))

(define (->string x)
  (with-output-to-string (lambda () (display x))))

(define (conc . args)
    (apply string-append (map ->string args)) )

;; Enumeration and constant definitions
(define sqlite3:status
  '((ok         .  0) ; Successful result (#f?)
    (error      .  1) ; SQL error or missing database
    (internal   .  2) ; NOT USED. Internal logic error in SQLite
    (permission .  3) ; Access permission denied
    (abort      .  4) ; Callback routine requested an abort
    (busy       .  5) ; The database file is locked
    (locked     .  6) ; A table in the database is locked
    (no-memory  .  7) ; A malloc() failed
    (read-only  .  8) ; Attempt to write a readonly database
    (interrupt  .  9) ; Operation terminated by sqlite3_interrupt()
    (io-error   . 10) ; Some kind of disk I/O error occurred
    (corrupt    . 11) ; The database disk image is malformed
    (not-found  . 12) ; NOT USED. Table or record not found
    (full       . 13) ; Insertion failed because database is full
    (cant-open  . 14) ; Unable to open the database file
    (protocol   . 15) ; NOT USED. Database lock protocol error
    (empty      . 16) ; Database is empty
    (schema     . 17) ; The database schema changed
    (too-big    . 18)  ; String or BLOB exceeds size limit
    (constraint . 19) ; Abort due to contraint violation
    (mismatch   . 20) ; Data type mismatch
    (misuse     . 21) ; Library used incorrectly
    (no-lfs     . 22) ; Uses OS features not supported on host
    (auth       . 23) ; Authorization denied
    (format     . 24) ; Auxiliary database format error
    (range      . 25) ; 2nd parameter to sqlite3_bind out of range
    (not-a-db   . 26) ; File opened that is not a database file
    (notice     . 27) ; Notifications from sqlite3_log()
    (warning    . 28) ; Warnings from sqlite3_log()
    (row       . 100) ; sqlite3_step() has another row ready
    (done      . 101) ; sqlite3_step() has finished executing
    ))

(define (number->sqlite3:status status)
  (let ([x (find (lambda (a) (equal? (cdr a) status)) sqlite3:status)])
    (if (pair? x) (car x) #f)))

(define sqlite3:type-enum (make-enumeration '(undefined integer float text blob null)))
(define sqlite3:type-index (enum-set-indexer sqlite3:type-enum))
(define (sqlite3:type-ref index)
  (list-ref (enum-set->list sqlite3:type-enum) index))

;; Auxiliary types

(define-ftype sqlite3:context void*)

(define-ftype sqlite3:value void*)

;; Types for databases and statements

(define-ftype sqlite3:database* void*)

(define-ftype sqlite3:database** (* sqlite3:database*))
                                        ;(define-check+error-type database)
(define-ftype sqlite3:statement* void*)
(define-ftype sqlite3:statement** (* sqlite3:statement*))

(define-record-type (&sqlite3 make-sqlite3-condition $sqlite3-condition?)
  (parent &condition)
  (fields (immutable status $sqlite3-condition-status)))

(define-record-type database
  (fields
   (mutable ptr)
   (mutable busy-handler)))

(define-record-type statement
  (fields
   (mutable ptr)
   (mutable database)))


#;(record-writer
 (type-descriptor statement)
 (lambda (r p wr)
   (wr
    (if (statement-ptr r)
        (format "#<sqlite3:statement sql=~s>" (source-sql r))
        "#<sqlite3:statement zombie>")
    p)))

                                        ;(define-check+error-type statement)


;;; Helpers

;; Conditions
(define rtd (record-type-descriptor &sqlite3))
(define sqlite3-condition? (condition-predicate rtd))
(define sqlite3-status (condition-accessor rtd $sqlite3-condition-status))


(define (make-sqlite3-error-condition loc msg sta . args)
  (condition
   (make-sqlite3-condition sta)
   (make-who-condition loc)
   (make-message-condition msg)
   (make-irritants-condition args)
   ))

(define (make-no-data-condition loc stmt params)
  (make-sqlite3-error-condition loc
                                "the statement returned no data"
                                'done
                                stmt params))

;; Errors
(define (abort-sqlite3-error loc db . args)
  (lambda (sta)
    (if (not (equal? sta 0))
        (raise
         (apply make-sqlite3-error-condition
                loc
                (if db (sqlite3-errmsg db) "sta")
                sta
                args)))))

(define (print-error-message obj port str)
  (display obj port) (display str port) (newline port))

(define (print-error msg obj)
  (print-error-message obj (current-error-port) (string-append "Error: " msg)))

;;; Database interface

;; Get any error message
(define sqlite3_errmsg
  (foreign-procedure "sqlite3_errmsg" (sqlite3:database*) string))
(define (sqlite3-errmsg db)
  (check-database 'sqlite3-errmsg db)
  (sqlite3_errmsg (database-addr db)))

;; Open a database
(define (open-database path)
  (assert (and open-database (string? path)))
  (let* ([ptr (make-ftype-pointer sqlite3:database**
                                  (foreign-alloc (ftype-sizeof sqlite3:database**)))]
         [f (foreign-procedure "sqlite3_open" (string void*) int)]
         [e (f path (ftype-pointer-address ptr))])
    (if (< e 0)
        (abort-sqlite3-error 'open-database #f path)
        (make-database (ftype-&ref sqlite3:database** (*) ptr) #f))))

(define (check-database context db)
  (assert (and context (database? db))))

(define (check-statement context db)
  (assert (and context (statement? db))))

;; Set application busy handler.  Does not use a callback, so it is safe
;; to yield.  Handler is called with DB, COUNT and LAST (the last value
;; it returned).  Return true value to continue trying, or #f to stop.
(define (set-busy-handler! db handler)
  (check-database 'set-busy-handler! db)
  (database-busy-handler-set! db handler))

(define (database-addr db)
  (ftype-pointer-address (database-ptr db)))

(define (statement-addr stmt)
  (ftype-pointer-address (statement-ptr stmt)))

;; Cancel any running database operation as soon as possible
(define (interrupt! db)
  (check-database 'interrupt! db)
  (let ([f (foreign-procedure "sqlite3_interrupt" (sqlite3:database*) void)])
    (f (database-addr db))))

;; Check whether the database is in autocommit mode
(define (auto-committing? db)
  (check-database 'auto-committing? db)
  (let ([f (foreign-procedure "sqlite3_get_autocommit" (sqlite3:database*) boolean)])
    (f (database-addr db))))

;; Get the number of changes made to the database
(define change-count
  (case-lambda
    [(db) (change-count db #f)]
    [(db total)
     (check-database 'change-count db)
     (let ([total-changes (foreign-procedure "sqlite3_total_changes" (sqlite3:database*) int)]
           [changes (foreign-procedure "sqlite3_changes" (sqlite3:database*) int)])
       (if total
           (total-changes (database-addr db))
           (changes (database-addr db))))]))

;; Get the row ID of the last inserted row
(define (last-insert-rowid db)
  (check-database 'last-insert-rowid db)
  (let ([f (foreign-procedure "sqlite3_last_insert_rowid" (sqlite3:database*) int)])
    (f (database-addr db))))

;; Close a database or statement handle
(define (sqlite3-finalize db)
  (check-database 'sqlite3-finalize db)
  (let* ([f (foreign-procedure "sqlite3_finalize"  (sqlite3:database*) int)]
         [n (f (database-addr db))])
    (database-ptr-set! db #f)
    n))

(define (sqlite3-next-stmt db)
  (check-database 'sqlite3-next-stmt db)
  (let* ([f (foreign-procedure "sqlite3_next_stmt" (sqlite3:database*) sqlite3:statement*)]
         [stmt* (f (database-addr db))])
    (make-statement (make-ftype-pointer sqlite3:statement* stmt*)
                    db)))

(define (finalize! x)
  (warning 'finalize! "not implemented!"))
;; #;(define finalize!
;;   (match-lambda*
;;    [((? database? db) . finalize-statements?)
;;      (cond
;;       [(not (database-ptr db))
;;       (void)]
;;       [(let loop ([stmt
;;                   (and
;;                     (optional finalize-statements? #f)
;;                     (sqlite3_next_stmt db #f))])
;;          (if stmt
;;              (or (sqlite3_finalize stmt) (loop (sqlite3_next_stmt db stmt)))
;;              ((foreign-safe-lambda sqlite3:status "sqlite3_close" sqlite3:database) db)))
;;       => (abort-sqlite3-error 'finalize! db db)]
;;       [else
;;       (let ([id (pointer->address (database-ptr db))]
;;             [release-qns (lambda (_ info) (object-release (vector-ref info 0)))])
;;         (call-with/synch *collations*
;;                           (cute hash-table-tree-clear! <> id release-qns))
;;         (call-with/synch *functions*
;;                           (cute hash-table-tree-clear! <> id release-qns))
;;         (database-ptr-set! db #f)
;;         (database-busy-handler-set! db #f))])]
;;    [((? statement? stmt))
;;      (cond
;;       [(not (statement-ptr stmt))
;;       (void)]
;;       [(sqlite3_finalize (statement-ptr stmt))
;;       => (abort-sqlite3-error 'finalize! (statement-database stmt) stmt)]
;;       [else
;;       (statement-ptr-set! stmt #f)])]
;;    [(v . _)
;;      (error-argument-type 'finalize! v "database or statement")]))

;;; Statement interface
(define sqlite3_prepare_v2 (foreign-procedure "sqlite3_prepare_v2"
                                              ( sqlite3:database* u8* int void* u8*) int))
(define (alloc-statement*)
  (make-ftype-pointer sqlite3:statement**
                      (foreign-alloc (ftype-sizeof sqlite3:statement**))))

;; Create a new statement
(define (prepare db sql)
  (check-database 'prepare db)
  (assert (and prepare (string? sql)))
  (let retry ([retries 0])
    (let* ([ptr (alloc-statement*)]
           [zSql (string->utf8 sql)]
           [nByte (bytevector-length zSql)]
           [e (sqlite3_prepare_v2 (database-addr db) zSql nByte (ftype-pointer-address ptr) #f)])
      (cond [(equal? e 0)
             (make-statement (ftype-&ref sqlite3:statement** (*) ptr) db)]
            [else
             (case (number->sqlite3:status e)
               [(busy)
                (let ([h (database-busy-handler db)])
                  (cond
                   [(and h (h db retries))
                    (retry (fx+ retries 1))]
                   [else
                    ((abort-sqlite3-error 'prepare db db sql) e)]))]
               [else
                ((abort-sqlite3-error 'prepare db db sql) e)])]))))

;; Retrieve the SQL source code of a statement
(define (source-sql stmt)
  (check-statement 'source-sql stmt)
  (let* ([f (foreign-procedure "sqlite3_sql" (sqlite3:statement*) string)])
    (f (statement-addr stmt))))

(define (finalize-statement! stmt)
  (check-statement 'finalize-statement! stmt)
  (let* ([f (foreign-procedure "sqlite3_finalize"  (sqlite3:statement*) int)]
         [n (f (statement-addr stmt))])
    (statement-ptr-set! stmt #f)
    n))

;; Reset an existing statement to process it again
(define (reset! stmt)
  (check-statement 'reset! stmt)
  (cond [((foreign-procedure  "sqlite3_reset" (sqlite3:statement*) int)
          (statement-addr stmt))
         => (abort-sqlite3-error 'reset! (statement-database stmt) stmt)]))

;; Get number of bindable parameters
(define (bind-parameter-count stmt)
  (check-statement 'bind-parameter-count stmt)
  ((foreign-procedure "sqlite3_bind_parameter_count" (sqlite3:statement*) int)
   (statement-addr stmt)))

;; Get index of a bindable parameter or #f if no parameter with the
;; given name exists
(define (bind-parameter-index stmt name)
  (check-statement 'bind-parameter-index stmt)
  (let ([i ((foreign-procedure "sqlite3_bind_parameter_index"
                               (sqlite3:statement* string) int)
            (statement-addr stmt) name)])
    (if (zero? i)
        #f
        (fx- i 1))))

;; Get the name of a bindable parameter
(define (bind-parameter-name stmt i)
  (check-statement 'bind-parameter-name stmt)
  ((foreign-procedure "sqlite3_bind_parameter_name" (sqlite3:statement* int) string)
   (statement-addr stmt) (fx+ i 1)))

;; Bind data as parameters to an existing statement

(define SQLITE_TRANSIENT -1)
(define (bind! stmt i v)
  (check-statement 'bind! stmt)
  (assert (and bind! (number? i) (>= i 0)))
  (cond
   [(bytevector? v)
    (cond [((foreign-procedure "sqlite3_bind_blob" (sqlite3:statement* int u8* int void*) int)
            (statement-addr stmt) (fx+ i 1) v (bytevector-length v) SQLITE_TRANSIENT)
           => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)])]
   [(or (and (fixnum? v) v) (and (boolean? v) (if v 1 0)))
    => (lambda (v)
         (cond [((foreign-procedure "sqlite3_bind_int"
                                    (sqlite3:statement* int int) int)
                 (statement-addr stmt) (fx+ i 1) v)
                => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)]))]
   [(real? v)
    (cond [((foreign-procedure "sqlite3_bind_double"
                               (sqlite3:statement* int double) int)
            (statement-addr stmt) (fx+ i 1) v)
           => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)])]
   [(string? v)
    (let ([f (foreign-procedure "sqlite3_bind_text"
                                (sqlite3:statement* int u8* int void*) int)]
          [s (string->utf8 v)])
      (cond [(f (statement-addr stmt) (fx+ i 1) s (bytevector-length s) SQLITE_TRANSIENT)
             => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i v)]))]
   [(sql-null? v)
    (cond [((foreign-procedure "sqlite3_bind_null" (sqlite3:statement* int) int)
            (statement-addr stmt) (fx+ i 1))
           => (abort-sqlite3-error 'bind! (statement-database stmt) stmt i)])]
   [else
    (error 'bind! "blob, number, boolean, string or sql-null" v)]))

; Helper

(define (%bind-parameters! loc stmt params)
  (reset! stmt)
  (let ([cnt (bind-parameter-count stmt)]
        [vs (make-eq-hashtable)])
    (let loop ([i 0] [params params])
      (match params
        [((? symbol? k) v . rest)
         (cond
          [(bind-parameter-index stmt (string-append ":" (symbol->string k)))
           => (lambda (j)
                (hashtable-set! vs j v)
                (loop i rest))]
          [else
           (error loc "value or keyword matching a bind parameter name" k)])]
        [(v . rest)
         (hashtable-set! vs i v)
         (loop (fx+ i 1) rest)]
        [()
         (void)]))
    (if (= (hashtable-size vs) cnt)
        (unless (zero? cnt)
          (hashtable-walk vs (cut bind! stmt <> <>)))
        (raise
         (condition
          (make-who-condition loc)
	  (make-message-condition (conc "bad parameter count - received " (hashtable-size vs) " but expected " cnt))
          (make-sqlite3-condition 'error))))))

(define (bind-parameters! stmt . params)
  (%bind-parameters! 'bind-parameters! stmt params))

;; Single-step a prepared statement, return #t if data is available,
;; #f otherwise
(define (step! stmt)
  (check-statement 'step! stmt)
  (let ([db (statement-database stmt)])
    (let retry ([retries 0])
      (let ([s ((foreign-procedure
                 "sqlite3_step" (sqlite3:statement*) int) (statement-addr stmt))])
        (case (number->sqlite3:status s)
          [(row)
           #t]
          [(done)
           #f]
          [(busy)
           (let ([h (database-busy-handler db)])
             (cond
              [(and h (h db retries))
               (retry (fx+ retries 1))]
              [else
               ((abort-sqlite3-error 'step! db stmt) s)]))]
          [else
           ((abort-sqlite3-error 'step! db stmt) s)])))))

;; Retrieve information from a prepared/stepped statement
(define (column-count stmt)
  (check-statement 'column-count stmt)
  ((foreign-procedure "sqlite3_column_count" (sqlite3:statement*) int) (statement-addr stmt)))

(define (column-type stmt i)
  (check-statement 'column-type stmt)
  (sqlite3:type-ref ((foreign-procedure  "sqlite3_column_type" (sqlite3:statement* int) int) (statement-addr stmt) i)))

(define (column-declared-type stmt i)
  (check-statement 'column-declared-type stmt)
  ((foreign-procedure "sqlite3_column_decltype" (sqlite3:statement* int) string) (statement-addr stmt) i))

(define (column-name stmt i)
  (check-statement 'column-name stmt)
  ((foreign-procedure "sqlite3_column_name" (sqlite3:statement* int) string) (statement-addr stmt) i))

(define sqlite3_column_double
  (foreign-procedure "sqlite3_column_double" (sqlite3:statement* int) double))

(define sqlite3_column_boolean
  (foreign-procedure "sqlite3_column_int" (sqlite3:statement* int) boolean))

(define (sqlite3-column-bytes stmt i)
  ((foreign-procedure "sqlite3_column_bytes" (sqlite3:statement* int) int)
   (statement-addr stmt) i))

(define (void*->bytevector ptr len)
  (define-ftype byte-array (array 0 unsigned-8))
  (let ([arr (make-ftype-pointer byte-array ptr)]
        [bv  (make-bytevector len)])
    (let loop ((i 0))
      (when (< i len)
        (bytevector-u8-set! bv i (ftype-ref byte-array (i) arr))
        (loop (fx+ 1 i))))
    bv))

(define (void*->string ptr len)
  (utf8->string (void*->bytevector ptr len)))

(define (sqlite3-column-text stmt i)
  (let* ([ptr ((foreign-procedure "sqlite3_column_text" (sqlite3:statement* int) void*)
               (statement-addr stmt) i)]
         [len (sqlite3-column-bytes stmt i)])
    (void*->string ptr len)))

(define (sqlite3-column-blob stmt i)
  (let* ([ptr ((foreign-procedure "sqlite3_column_blob" (sqlite3:statement* int) void*)
               (statement-addr stmt) i)]
         [len (sqlite3-column-bytes stmt i)])
    (void*->bytevector ptr len)))

;; Retrieve data from a stepped statement
(define (column-data stmt i)
  (case (column-type stmt i)
    [(integer)
     (if (and-let* ([type (column-declared-type stmt i)])
                   (string-contains-ci type "bool"))
         (sqlite3_column_boolean (statement-addr stmt) i)
         (sqlite3_column_double (statement-addr stmt) i))]
    [(float)
     (sqlite3_column_double (statement-addr stmt) i)]
    [(text)
     (sqlite3-column-text stmt i)]
    [(blob)
     (sqlite3-column-blob stmt i)]
    [else
     (sql-null)]))

;;; Easy statement interface

;; Compile a statement and call a procedure on it, then finalize the
;; statement in a dynamic-wind exit block if it hasn't been finalized yet.
(define (call-with-temporary-statements proc db . sqls)
  (check-database 'call-with-temporary-statements db)
  (let ([stmts #f] [exn #f])
    (dynamic-wind
      (lambda ()
        (unless stmts
          (set! stmts (map (cute prepare db <>) sqls))))
      (lambda ()
        (guard (e [else (set! exn e)])
	       (apply proc stmts)))
      (lambda ()
        (and-let* ([s stmts])
                  (set! stmts #f)
                  (for-each finalize! s)) ;; leaks if error occurs before last stmt
        (and-let* ([e exn])
                  (set! exn #f)
                  (raise e))))))

(define-syntax %define/statement+params
  (syntax-rules ()
    [(%define/statement+params ((name loc) (init ...) (stmt params))
                               body ...)
     (define name
       (let ([impl (lambda (init ... stmt params) body ...)])
         (lambda (init ... db-or-stmt . params)
           (cond
            [(database? db-or-stmt)
             (call-with-temporary-statements
              (cute impl init ... <> (cdr params))
              db-or-stmt (car params))]
            [(statement? db-or-stmt)
             (impl init ... db-or-stmt params)]
            [else
             (error loc "database or statement" db-or-stmt)]))))]
    [(%define/statement+params (name (init ...) (stmt params))
                               body ...)
     (%define/statement+params ((name 'name) (init ...) (stmt params))
                               body ...)]
    [(%define/statement+params (name stmt params)
                               body ...)
     (%define/statement+params ((name 'name) () (stmt params))
                               body ...)]))

; from chicken miscmacros.scm
  (define-syntax while
    (syntax-rules ()
      ((while test body ...)
       (let loop ()
         (if test
             (begin
               body ...
               (loop)))))))

;; Step through a statement and ignore possible results
(define (%execute loc stmt params)
  (%bind-parameters! loc stmt params)
  (while (step! stmt))
  (void))

(%define/statement+params (execute stmt params)
                          (%execute 'execute stmt params))

;; Step through a statement, ignore possible results and return the
;; count of changes performed by this statement
(%define/statement+params (update stmt params)
                          (%execute 'update stmt params)
                          (change-count (statement-database stmt)))

;; Return only the first column of the first result row produced by this
;; statement

(%define/statement+params (first-result stmt params)
                          (%bind-parameters! 'first-result stmt params)
                          (if (step! stmt)
                              (let ([r (column-data stmt 0)])
                                (reset! stmt)
                                r)
                              (raise (make-no-data-condition 'first-result stmt params))))

;; Return only the first result row produced by this statement as a list

(%define/statement+params (first-row stmt params)
                          (%bind-parameters! 'first-row stmt params)
                          (if (step! stmt)
                              (map (cute column-data stmt <>)
                                   (iota (column-count stmt)))
                              (raise (make-no-data-condition 'first-row stmt params))))

;; Apply a procedure to the values of the result columns for each result row
;; while executing the statement and accumulating results.

(%define/statement+params ((%fold-row loc) (loc proc init) (stmt params))
                          (%bind-parameters! loc stmt params)
                          (let ([cl (iota (column-count stmt))])
                            (let loop ([acc init])
                              (if (step! stmt)
                                  (loop (apply proc acc (map (cute column-data stmt <>) cl)))
                                  acc))))


(define-syntax check-procedure
  (syntax-rules ()
    [(_ loc proc)
     (assert (and loc (procedure? proc)))]))

(define (fold-row proc init db-or-stmt . params)
  (apply %fold-row 'fold-row proc init db-or-stmt params))

;; Apply a procedure to the values of the result columns for each result row
;; while executing the statement and discard the results

(define (for-each-row proc db-or-stmt . params)
  (check-procedure fold-row proc)
  (apply %fold-row
         'for-each-row
         (lambda (acc . columns)
           (apply proc columns))
         (void)
         db-or-stmt params))

;; Apply a procedure to the values of the result columns for each result row
;; while executing the statement and accumulate the results in a list

(define (map-row proc db-or-stmt . params)
  (check-procedure 'map-row proc)
  (reverse!
   (apply %fold-row
          'map-row
          (lambda (acc . columns)
            (cons (apply proc columns) acc))
          '()
          db-or-stmt params)))

;;; Utility procedures

;; Run a thunk within a database transaction, commit if return value is
;; true, rollback if return value is false or the thunk is interrupted by
;; an exception
(define with-transaction
  (case-lambda
    ((db thunk) (with-transaction db thunk 'deferred))
    ((db thunk type)
     (check-database 'with-transaction db)
     (check-procedure 'with-transaction thunk)
     (unless (memq type '(deferred immediate exclusive))
       (error 'with-transaction  "bad argument: expected deferred, immediate or exclusive")
	 type)
     (let ([success? #f] [exn #f])
       (dynamic-wind
         (lambda ()
           (execute db
                    (string-append "BEGIN " (symbol->string type) " TRANSACTION;")))
         (lambda ()
           (guard (e [else (begin
			     (print-error "with-transaction" exn)
			     (set! exn e))])
		  (set! success? (thunk))
		  success?))
         (lambda ()
           (execute db
                    (if success?
                        "COMMIT TRANSACTION;"
                        "ROLLBACK TRANSACTION;"))
           (and-let* ([e exn])
                     (set! exn #f)
                     (raise e))))))))

;; Check if the given string is a valid SQL statement
(define sql-complete?
  (foreign-procedure "sqlite3_complete" (string) boolean))

;; Return a descriptive version string
(define database-version
  (foreign-procedure "sqlite3_libversion" () string))

;; Return the amount of memory currently allocated by the database
(define database-memory-used
  (foreign-procedure "sqlite3_memory_used" () int))

;; Return the maximum amount of memory allocated by the database since
;; the counter was last reset
(define database-memory-highwater
  (case-lambda
    (() (database-memory-highwater #f))
    ((reset?) ((foreign-procedure "sqlite3_memory_highwater" (boolean) int) reset?))))

;; Enables (disables) the sharing of the database cache and schema data
;; structures between connections to the same database.
(define (enable-shared-cache! enable?)
  (cond-expand
   [disable-shared-cache
    #f]
   [else
    (cond
     [((foreign-procedure "sqlite3_enable_shared_cache" (boolean) int) enable?)
      => (abort-sqlite3-error 'enable-shared-cache! #f)]
     [else
      enable?])]))

;; Enables (disables) the loading of native extensions using SQL statements.
(define (enable-load-extension! db enable?)
  (cond-expand
   [disable-load-extension
    #f]
   [else
    (cond
     [((foreign-procedure "sqlite3_enable_load_extension" (sqlite3:database* boolean) int) (database-addr db) enable?)
      => (abort-sqlite3-error 'enable-load-extension! db)]
     [else
      enable?])]))

(record-writer
 (type-descriptor database)
 (lambda (r p wr)
   (wr
    (if (database-ptr r)
        "#<sqlite3:database>"
        "#<sqlite3:database zombie>")
    p)))

Added sqlite3.sls.































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
;;;; sqlite3.scm
;;;; :tabSize=2:indentSize=2:noTabs=true:
;;;; bindings to the SQLite3 database library

#!chezscheme
(library (sqlite3)
  (export 
    ;; procedures
    open-database 
    ;define-collation
    ;define-function
    set-busy-handler!
    ;make-busy-timeout
    interrupt!
    auto-committing?
    change-count
    last-insert-rowid
    finalize!
    prepare
    source-sql
    reset!
    bind-parameter-count
    bind-parameter-index
    bind-parameter-name
    bind!
    bind-parameters!
    step!
    column-count
    column-type
    column-declared-type
    column-name
    column-data
    call-with-temporary-statements
    execute
    update
    first-result
    first-row
    fold-row
    for-each-row
    map-row
    with-transaction
    sql-complete?
    database-version
    database-memory-used
    database-memory-highwater
    enable-shared-cache!
    enable-load-extension!)

  (import 
   (chezscheme)
   (srfi s0 cond-expand)
   (srfi s2 and-let)
   (matchable)
   (only (srfi s13 strings) string-contains-ci)
   (srfi s11 let-values)
   (srfi s26 cut)
   (sql-null))

  (include "sqlite3.scm")

  (load-shared-object "libsqlite3.so.0")

) ; library sqlite3

Added srfi/LICENSE.

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
The following license applies to all files written by Derick Eddington,
unless otherwise stated.

===========================================================================
 Copyright (c) 2008-2009 Derick Eddington
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
 to deal in the Software without restriction, including without limitation
 the rights to use, copy, modify, merge, publish, distribute, sublicense,
 and/or sell copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 Except as contained in this notice, the name(s) of the above copyright 
 holders shall not be used in advertising or otherwise to promote the sale, 
 use or other dealings in this Software without prior written authorization.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 DEALINGS IN THE SOFTWARE. 
===========================================================================


Files written by others retain any copyright, license, and/or other notice
they originally had.

Added srfi/README.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

There is an existing R6RS srfi project at:

https://code.launchpad.net/~scheme-libraries-team/scheme-libraries/srfi

In that project, the library names use the colon character. E.g.:

    (srfi :1 lists)

Filenames with a colon are not portable across UNIX and Windows. Some
Scheme implementations support an encoding whereby ':1' is
mapped to '%3a1'. Chez Scheme does not perform the conversion.

The surfage libraries are a port of the R6RS srfi libraries to have
names such as:

    (surfage s1 lists)

Added srfi/private/OS-id-features.sls.



















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi private OS-id-features)
  (export
    OS-id-features)
  (import 
    (rnrs))

  (define (OS-id-features OS-id features-alist)
    (define OS-id-len (string-length OS-id))
    (define (OS-id-contains? str)
      (define str-len (string-length str))
      (let loop ((i 0))
        (and (<= (+ i str-len) OS-id-len)
             (or (string-ci=? str (substring OS-id i (+ i str-len)))
                 (loop (+ 1 i))))))
    (apply append
           (map cdr (filter (lambda (x) (OS-id-contains? (car x)))
                            features-alist))))
)

Added srfi/private/feature-cond.sls.











































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private feature-cond)
  (export
    feature-cond)
  (import
    (rnrs)
    (srfi private registry))

  (define-syntax feature-cond
    (lambda (stx)
      (define (identifier?/name=? x n)
        (and (identifier? x)
             (symbol=? n (syntax->datum x))))
      (define (make-test t)
        (define (invalid-test)
          (syntax-violation #F "invalid test syntax" stx t))
        (syntax-case t ()
          ((c x ...)
           (identifier?/name=? (syntax c) (quote and))
           (cons (syntax and) (map make-test (syntax (x ...)))))
          ((c x ...)
           (identifier?/name=? (syntax c) (quote or))
           (cons (syntax or) (map make-test (syntax (x ...)))))
          ((c x ...)
           (identifier?/name=? (syntax c) (quote not))
           (if (= 1 (length (syntax (x ...))))
             (list (syntax not) (make-test (car (syntax (x ...)))))
             (invalid-test)))
          (datum
           (not (and (identifier? (syntax datum))
                     (memq (syntax->datum (syntax datum))
                           (quote (and or not else)))))
           (syntax (and (member (quote datum) available-features) #T)))
          (_ (invalid-test))))
      (syntax-case stx ()
        ((_ (test . exprs) ... (e . eexprs))
         (identifier?/name=? (syntax e) (quote else))
         (with-syntax (((clause ...)
                        (map cons (map make-test (syntax (test ...)))
                                  (syntax (exprs ...)))))
           (syntax (cond clause ... (else . eexprs)))))
        ((kw (test . exprs) ...)
         (syntax (kw (test . exprs) ... (else (no-clause-true))))))))

  (define (no-clause-true)
    (assertion-violation (quote feature-cond) "no clause true"))
)

Added srfi/private/include.sls.







































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi private include)
  (export 
    include/resolve)
  (import 
    (rnrs) 
    (for (srfi private include compat) expand))
  
  (define-syntax include/resolve
    (lambda (stx)
      (define (include/lexical-context ctxt filename)
        (with-exception-handler
          (lambda (ex)
            (raise
             (condition
              (make-error)
              (make-who-condition 'include/resolve)
              (make-message-condition "error while trying to include")
              (make-irritants-condition (list filename))
              (if (condition? ex) ex (make-irritants-condition (list ex))))))
          (lambda ()
            (call-with-input-file filename
              (lambda (fip)
                (let loop ([a '()])
                  (let ([x (read fip)])
                    (if (eof-object? x)
                      (cons #'begin (datum->syntax ctxt (reverse a)))
                      (loop (cons x a))))))))))
      (syntax-case stx ()
        [(ctxt (lib-path* ...) file-path)
         (for-all (lambda (s) (and (string? s) (positive? (string-length s)))) 
                  (syntax->datum #'(lib-path* ... file-path)))
         (let ([p (apply string-append 
                         (map (lambda (ps) (string-append "/" ps)) 
                              (syntax->datum #'(lib-path* ... file-path))))]
               [sp (search-paths)])
           (let loop ([search sp])
             (if (null? search)
               (error 'include/resolve "cannot find file in search paths"
                      (substring p 1 (string-length p)) sp)
               (let ([full (string-append (car search) p)])
                 (if (file-exists? full)
                   (include/lexical-context #'ctxt full)
                   (loop (cdr search)))))))])))
)

Added srfi/private/include/compat.chezscheme.sls.























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11

(library (srfi private include compat)

  (export search-paths)

  (import (chezscheme))

  (define (search-paths)
    (map car (library-directories)))

  )

Added srfi/private/include/compat.ikarus.sls.

































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private include compat)
  (export
    search-paths)
  (import
    (rnrs base)
    (only (ikarus) library-path))

  (define (search-paths)
    (library-path))
)

Added srfi/private/include/compat.larceny.sls.













































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private include compat)
  (export
    search-paths)
  (import
    (rnrs base)
    (primitives current-require-path getenv absolute-path-string?))
  
  (define (search-paths)
    (let ([larceny-root (getenv "LARCENY_ROOT")])
      (map (lambda (crp)
             (if (absolute-path-string? crp)
               crp
               (string-append larceny-root "/" crp)))
           (current-require-path))))

)

Added srfi/private/include/compat.mosh.sls.





















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
(library (srfi private include compat)
  (export
    search-paths)
  (import
    (rnrs base)
    (only (mosh) library-path))

  (define (search-paths)
    (library-path))
)

Added srfi/private/include/compat.mzscheme.sls.







































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi private include compat)
  (export
    search-paths)
  (import
    (rnrs base)
    (only (scheme base) current-library-collection-paths path->string)
    (only (scheme mpair) list->mlist))

  (define (search-paths)
    (map path->string 
         (list->mlist (current-library-collection-paths))))
)

Added srfi/private/include/compat.ypsilon.sls.

































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private include compat)
  (export
    search-paths)
  (import
    (rnrs base)
    (only (core) scheme-library-paths))

  (define (search-paths)
    (scheme-library-paths))
)

Added srfi/private/let-opt.sls.





































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!r6rs
;;; LET-OPTIONALS macros
;;; Copyright (c) 2001 by Olin Shivers.

;;; Copyright (c) 1993-2003 Richard Kelsey and Jonathan Rees
;;; Copyright (c) 1994-2003 by Olin Shivers and Brian D. Carlstrom.
;;; Copyright (c) 1999-2003 by Martin Gasbichler.
;;; Copyright (c) 2001-2003 by Michael Sperber.
;;;
;;; All rights reserved.
;;;
;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:
;;; 1. Redistributions of source code must retain the above copyright
;;;    notice, this list of conditions and the following disclaimer.
;;; 2. Redistributions in binary form must reproduce the above copyright
;;;    notice, this list of conditions and the following disclaimer in the
;;;    documentation and/or other materials provided with the distribution.
;;; 3. The name of the authors may not be used to endorse or promote products
;;;    derived from this software without specific prior written permission.
;;;
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

;;; Made into an R6RS library by Derick Eddington.

(library (srfi private let-opt)
  (export
    let-optionals* :optional)
  (import
    (rnrs))

;;; (:optional rest-arg default-exp [test-pred])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This form is for evaluating optional arguments and their defaults
;;; in simple procedures that take a *single* optional argument. It is
;;; a macro so that the default will not be computed unless it is needed.
;;; 
;;; REST-ARG is a rest list from a lambda -- e.g., R in
;;;     (lambda (a b . r) ...)
;;; - If REST-ARG has 0 elements, evaluate DEFAULT-EXP and return that.
;;; - If REST-ARG has 1 element, return that element.
;;; - If REST-ARG has >1 element, error.
;;;
;;; If there is an TEST-PRED form, it is a predicate that is used to test
;;; a non-default value. If the predicate returns false, an error is raised.

(define-syntax :optional
  (syntax-rules ()
    ([_ rest default-exp]
     (let ((maybe-arg rest))
       (if (pair? maybe-arg)
	   (if (null? (cdr maybe-arg)) (car maybe-arg)
	       (error ':optional "too many optional arguments" maybe-arg))
	   default-exp)))
    ([_ rest default-exp arg-test]
     (let ((maybe-arg rest))
       (if (pair? maybe-arg)
	   (if (null? (cdr maybe-arg))
	       (let ((val (car maybe-arg)))
		 (if (arg-test val) val
		     (error ':optional "optional argument failed test" val)))
	       (error ':optional "too many optional arguments" maybe-arg))
	   default-exp)))))
 ; erutcurts-enifed

;;; Here is a simpler but less-efficient version of LET-OPTIONALS*.
;;; It redundantly performs end-of-list checks for every optional var,
;;; even after the list runs out.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define-syntax let-optionals*
  (syntax-rules ()
    ((let-optionals* arg (opt-clause ...) body ...)
     (let ((rest arg))
       (%let-optionals* rest (opt-clause ...) 
			(let () body ...))))))

;;; The arg-list expression *must* be a variable.
;;; (Or must be side-effect-free, in any event.)

(define-syntax %let-optionals*
  (syntax-rules ()
    ((%let-optionals* arg (((var ...) xparser) opt-clause ...) body ...)
     (call-with-values (lambda () (xparser arg))
       (lambda (rest var ...)
         (%let-optionals* rest (opt-clause ...) body ...))))
    
    ((%let-optionals* arg ((var default) opt-clause ...) body ...)
     (call-with-values (lambda () (if (null? arg) (values default '())
				      (values (car arg) (cdr arg))))
       (lambda (var rest)
	 (%let-optionals* rest (opt-clause ...) body ...))))

    ((%let-optionals* arg ((var default test) opt-clause ...) body ...)
     (call-with-values (lambda ()
			 (if (null? arg) (values default '())
			     (let ((var (car arg)))
			       (if test (values var (cdr arg))
				   (error 'let-optionals* "arg failed LET-OPT test" var)))))
       (lambda (var rest)
	 (%let-optionals* rest (opt-clause ...) body ...))))

    ((%let-optionals* arg ((var default test supplied?) opt-clause ...) body ...)
     (call-with-values (lambda ()
			 (if (null? arg) (values default #f '())
			     (let ((var (car arg)))
			       (if test (values var #t (cdr arg))
				   (error 'let-optionals* "arg failed LET-OPT test" var)))))
       (lambda (var supplied? rest)
	 (%let-optionals* rest (opt-clause ...) body ...))))

    ((%let-optionals* arg (rest) body ...)
     (let ((rest arg)) body ...))

    ((%let-optionals* arg () body ...)
     (if (null? arg) (begin body ...)
	 (error 'let-optionals* "too many arguments in let-opt" arg)))))
; erutcurts-enifed

)

Added srfi/private/make-aliased-libraries.sps.













































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!r6rs
(import
  (rnrs)
  (only (srfi private registry) available-features)
  (only (xitomatl lists) map/filter)
  (only (xitomatl match) match-lambda)
  (only (xitomatl common) format fprintf printf)
  (only (xitomatl strings) string-intersperse)
  (only (xitomatl predicates) symbol<?)
  (only (xitomatl environments) environment environment-symbols))

(define srfi-libraries/mnemonics
  (map/filter (match-lambda
                ;; NOTE: Uses only the 3-element names.
                ((:and ('srfi (:symbol ":(\\d+)" num) _)
                       name)
                 (list (string->number (symbol->string num))
                       name))
                (_ #F))
              available-features))

(define alias-template
";; Automatically generated by ~a
#!r6rs
(library ~s
  (export
    ~a)
  (import ~s)
)
")

(define program-name (car (command-line)))

(for-each
 (lambda (x)
   (let* ((srfi-num (car x))
          (lib-name (cadr x))
          (exports (list-sort symbol<?
                              (environment-symbols (environment lib-name))))
          (alias-name `(srfi ,(string->symbol (format ":~d" srfi-num))))
          (out-file (format "~d.sls" srfi-num)))
     (cond
       ((file-exists? out-file)
        (printf "Skipping ~a because it already exists.\n" out-file))
       (else
        (call-with-output-file out-file
          (lambda (fop)
            (fprintf fop alias-template
                     program-name
                     alias-name
                     (string-intersperse (map symbol->string exports) "\n    ")
                     lib-name)))
        (printf "~a\n" out-file)))))
 srfi-libraries/mnemonics)

Added srfi/private/platform-features.chezscheme.sls.















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private platform-features)
  (export
    OS-features
    implementation-features)
  (import
    (rnrs)
    (only (chezscheme) machine-type)
    (srfi private OS-id-features))
  
  (define (OS-features)
    (OS-id-features
     (symbol->string (machine-type))
     '(("i3la" linux posix))))
  
  (define (implementation-features)
    '(chezscheme))
)

Added srfi/private/platform-features.ikarus.sls.





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private platform-features)
  (export
    OS-features
    implementation-features)
  (import
    (rnrs)
    (only (ikarus) host-info)
    (srfi private OS-id-features))
  
  (define (OS-features)
    (OS-id-features
     (host-info)
     '(("linux" linux posix)
       ("solaris" solaris posix)
       ("darwin" darwin posix)
       ("bsd" bsd)
       ("freebsd" freebsd posix)
       ("openbsd" openbsd posix)
       ("cygwin" cygwin posix)  ;; correct?
       ("gnu" gnu))))
  
  (define (implementation-features)
    '(ikarus))
)

Added srfi/private/platform-features.larceny.sls.





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private platform-features)
  (export
    OS-features
    implementation-features)
  (import
    (rnrs base)
    (rnrs lists)
    (primitives system-features)
    (srfi private OS-id-features))
  
  (define (OS-features)
    (OS-id-features
     (cdr (assq 'os-name (system-features)))
     '(("linux" linux posix)
       ("solaris" solaris posix)
       ("darwin" darwin posix)
       ("bsd" bsd)
       ("freebsd" freebsd posix)
       ("openbsd" openbsd posix)
       ("windows" windows))))
  
  (define (implementation-features)
    '(larceny))
)

Added srfi/private/platform-features.mosh.sls.







































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

(library (srfi private platform-features)
  
  (export OS-features implementation-features)
  
  (import
   (only (rnrs) define quote)
   (only (mosh) host-os)
   (srfi private OS-id-features))

  (define (OS-features)
    (OS-id-features
     (host-os)
     '(("linux" linux posix)
       ("bsd" linux posix)
       ("darwin" darwin posix))))

  (define (implementation-features)
    '(mosh)))

Added srfi/private/platform-features.mzscheme.sls.

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi private platform-features)
  (export
    OS-features
    implementation-features)
  (import
    (rnrs)
    (only (scheme base) system-type)
    (srfi private OS-id-features))
  
  (define (OS-features)
    (OS-id-features
     (string-append (symbol->string (system-type 'os))
                    " " (system-type 'machine))
     '(("linux" linux posix)
       ("macosx" mac-os-x darwin posix)
       ("solaris" solaris posix)
       ("gnu" gnu)
       ("bsd" bsd)
       ("freebsd" freebsd posix)
       ("openbsd" openbsd posix)
       ("windows" windows))))
  
  (define (implementation-features)
    '(mzscheme))
)

Added srfi/private/platform-features.ypsilon.sls.



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private platform-features)
  (export
    OS-features
    implementation-features)
  (import
    (rnrs)
    (only (core) architecture-feature)
    (srfi private OS-id-features))
  
  (define (OS-features)
    (OS-id-features
     (architecture-feature 'operating-system)
     '(("linux" linux posix)
       ("solaris" solaris posix)
       ("darwin" darwin posix)
       ("bsd" bsd)
       ("freebsd" freebsd posix)
       ("openbsd" openbsd posix)
       ("windows" windows))))
  
  (define (implementation-features)
    '(ypsilon))
)

Added srfi/private/registry.sls.















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi private registry)
  (export
    available-features)
  (import 
    (rnrs)
    (srfi private platform-features))
  
  (define available-features
    (let-syntax
        ((SRFI-features
          (lambda (stx)
            (define SRFIs
              '((0    cond-expand)
                (1    lists)
                (2    and-let*)
                #;(5    let)
                (6    basic-string-ports)
                (8    receive)
                (9    records)
                (11   let-values)
                (13   strings)
                (14   char-sets)
                (16   case-lambda)
                #;(17   generalized-set!)
                #;(18   multithreading)
                (19   time)
                #;(21   real-time-multithreading)
                (23   error)
                (25   multi-dimensional-arrays)
                (26   cut)
                (27   random-bits)
                #;(28   basic-format-strings)
                #;(29   localization)
                (31   rec)
                (37   args-fold)
                (38   with-shared-structure)
                (39   parameters)
                (41   streams)
                (42   eager-comprehensions)
                (43   vectors)
                #;(44   collections)
                #;(45   lazy)
                #;(46   syntax-rules)
                #;(47   arrays)
                (48   intermediate-format-strings)
                #;(51   rest-values)
                #;(54   cat)
                #;(57   records)
                #;(59   vicinities)
                #;(60   integer-bits)
                (61   cond)
                #;(63   arrays)
                (64   testing)
                #;(66   octet-vectors)
                (67   compare-procedures)
                (69   basic-hash-tables)
                #;(71   let)
                #;(74   blobs)
                (78   lightweight-testing)
                #;(86   mu-and-nu)
                #;(87   case)
                #;(95   sorting-and-merging)
                (98   os-environment-variables)
                (99   records)))
            (define (make-feature-names x)
              (define number car)
              (define mnemonic cdr)
              (define (make-symbol . args)
                (string->symbol (apply string-append
                                       (map (lambda (a)
                                              (if (symbol? a)
                                                (symbol->string a)
                                                a))
                                            args))))
              (let* ((n-str (number->string (number x)))
                     (colon-n (make-symbol ":" n-str))
                     (srfi-n (make-symbol "srfi-" n-str))
                     (srfi-n-m (apply make-symbol srfi-n
                                      (map (lambda (m) (make-symbol "-" m))
                                           (mnemonic x)))))
                ;; The first two are recommended by SRFI-97.
                ;; The last two are the two types of SRFI-97 library name.
                (list srfi-n
                      srfi-n-m
                      `(srfi ,colon-n)
                      `(srfi ,colon-n . ,(mnemonic x)))))
            (syntax-case stx ()
              ((kw)
               #`(quote #,(datum->syntax #'kw
                           (apply append (map make-feature-names SRFIs)))))))))
      `(,@(OS-features)
        ,@(implementation-features)
        ,@(SRFI-features)
        r6rs)))
  
)

Added srfi/private/vanish.sls.























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi private vanish)
  (export
    vanish-define)
  (import
    (rnrs)
    (for (only (rnrs base) begin) (meta -1)))

  #;(define (show stx)
    (display (make-string 60 #\-)) (newline)
    (write (syntax->datum stx)) (newline))

  (define-syntax vanish-define
    (lambda (stx)
      (syntax-case stx ()
        ((_ def (vanish ...))
         (for-all identifier? #'(vanish ...))
         #'(make-vanish-define (syntax def) (syntax vanish) ...)))))

  (define (make-vanish-define def . to-vanish)
    (lambda (stx)
      (define (vanish? id)
        (memp (lambda (x) (free-identifier=? id x))
              to-vanish))
      #;(show stx)
      (syntax-case stx ()
        ((_ name . _)
         (and (identifier? #'name)
              (vanish? #'name))
         #'(begin))
        ((_ (name . _) . _)
         (and (identifier? #'name)
              (vanish? #'name))
         #'(begin))
        ((_ . r)
         (cons def #'r)))))
)

Added srfi/s0/cond-expand.sls.







































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s0 cond-expand)
  (export 
    cond-expand)
  (import
    (rnrs)
    (for (srfi private registry) expand))
  
  (define-syntax cond-expand
    (lambda (stx)
      (syntax-case stx (and or not else)
        [(_) 
         (syntax-violation #f "Unfulfilled cond-expand" stx)]
        [(_ (else body ...))
         #'(begin body ...)]
        [(_ ((and) body ...) more-clauses ...)
         #'(begin body ...)]
        [(_ ((and req1 req2 ...) body ...) more-clauses ...)
         #'(cond-expand
            (req1
             (cond-expand
              ((and req2 ...) body ...)
              more-clauses ...))
            more-clauses ...)]
        [(_ ((or) body ...) more-clauses ...)
         #'(cond-expand more-clauses ...)]
        [(_ ((or req1 req2 ...) body ...) more-clauses ...)
         #'(cond-expand
            (req1
             (begin body ...))
            (else
             (cond-expand
              ((or req2 ...) body ...)
              more-clauses ...)))]
        [(_ ((not req) body ...) more-clauses ...)
         #'(cond-expand
            (req
             (cond-expand more-clauses ...))
            (else body ...))]
        [(_ (feature-id body ...) more-clauses ...)
         (if (member (syntax->datum #'feature-id) available-features)
           #'(begin body ...)
           #'(cond-expand more-clauses ...))])))
  
)

Added srfi/s1/lists.sls.













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
#!r6rs

;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with
;; this code as long as you do not remove this copyright notice or
;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.
;;     -Olin

;; Ikarus porting begun by Abdulaziz Ghuloum, 
;; and continued by Derick Eddington.

(library (srfi s1 lists)
  (export
   xcons make-list list-tabulate list-copy 
   proper-list? circular-list? dotted-list? not-pair? null-list? list=
   circular-list length+
   iota
   first second third fourth fifth sixth seventh eighth ninth tenth
   car+cdr
   take       drop       
   take-right drop-right 
   take!      drop-right!
   split-at   split-at!
   last last-pair
   zip unzip1 unzip2 unzip3 unzip4 unzip5
   count
   append! append-reverse append-reverse! concatenate concatenate! 
   unfold       fold       pair-fold       reduce
   unfold-right            pair-fold-right reduce-right
   append-map append-map! map! pair-for-each filter-map map-in-order
   filter! partition! remove! 
   find-tail any every list-index
   take-while drop-while take-while!
   span break span! break!
   delete delete!
   alist-cons alist-copy
   delete-duplicates delete-duplicates!
   alist-delete alist-delete!
   reverse! 
   lset<= lset= lset-adjoin  
   lset-union  lset-intersection  lset-difference  lset-xor  
   lset-diff+intersection
   lset-union! lset-intersection! lset-difference! lset-xor!
   lset-diff+intersection!
   ;; re-exported:
   append assq assv caaaar caaadr caaar caadar caaddr
   caadr caar cadaar cadadr cadar caddar cadddr caddr cadr
   car cdaaar cdaadr cdaar cdadar cdaddr cdadr cdar cddaar
   cddadr cddar cdddar cddddr cdddr cddr cdr cons cons*
   length list list-ref memq memv null? pair?
   reverse set-car! set-cdr!
   ;; different than R6RS:
   assoc filter find fold-right for-each map member partition remove)
  (import 
   (except (rnrs)
           assoc error filter find fold-right
           for-each map member partition remove)
   (rnrs mutable-pairs))

  (define-syntax check-arg
    (lambda (stx)
      (syntax-case stx ()
        [(_ pred val caller)
         (and (identifier? #'val) (identifier? #'caller))
         #'(unless (pred val)
             (assertion-violation 'caller "check-arg failed" val))])))

  (define (error . args)
    (if (and (<= 2 (length args)) (symbol? (car args)) (string? (cadr args)))
        (apply assertion-violation args)
        (apply assertion-violation "(library (srfi s1 lists))"
               "misuse of error procedure" args)))
  
  ;; Constructors
  ;; ;;;;;;;;;;;;;

  (define (xcons d a) (cons a d))

  (define (make-list len . maybe-elt)
    (check-arg (lambda (n) (and (integer? n) (>= n 0))) len make-list)
    (let ((elt (cond ((null? maybe-elt) #f) ; Default value
                     ((null? (cdr maybe-elt)) (car maybe-elt))
                     (else (error 'make-list "Too many arguments"
                                  (cons len maybe-elt))))))
      (do ((i len (- i 1))
           (ans '() (cons elt ans)))
          ((<= i 0) ans))))

  (define (list-tabulate len proc)
    (check-arg (lambda (n) (and (integer? n) (>= n 0))) len list-tabulate)
    (check-arg procedure? proc list-tabulate)
    (do ((i (- len 1) (- i 1))
         (ans '() (cons (proc i) ans)))
        ((< i 0) ans)))

  (define (list-copy lis)				
    (let recur ((lis lis))			
      (if (pair? lis)				
          (cons (car lis) (recur (cdr lis)))	
          lis)))					

  (define iota
    (case-lambda
     [(count) (iota count 0 1)]
     [(count start) (iota count start 1)]
     [(count start step)
      (check-arg integer? count iota)
      (if (< count 0) (error 'iota "Negative step count" count))
      (check-arg number? start iota)
      (check-arg number? step iota)
      (let ((last-val (+ start (* (- count 1) step))))
        (do ((count count (- count 1))
             (val last-val (- val step))
             (ans '() (cons val ans)))
            ((<= count 0)  ans)))]))
  
  (define (circular-list val1 . vals)
    (let ((ans (cons val1 vals)))
      (set-cdr! (last-pair ans) ans)
      ans))

  (define (proper-list? x)
    (let lp ((x x) (lag x))
      (if (pair? x)
          (let ((x (cdr x)))
            (if (pair? x)
                (let ((x   (cdr x))
                      (lag (cdr lag)))
                  (and (not (eq? x lag)) (lp x lag)))
                (null? x)))
          (null? x))))

  (define (dotted-list? x)
    (let lp ((x x) (lag x))
      (if (pair? x)
          (let ((x (cdr x)))
            (if (pair? x)
                (let ((x   (cdr x))
                      (lag (cdr lag)))
                  (and (not (eq? x lag)) (lp x lag)))
                (not (null? x))))
          (not (null? x)))))

  (define (circular-list? x)
    (let lp ((x x) (lag x))
      (and (pair? x)
           (let ((x (cdr x)))
             (and (pair? x)
                  (let ((x   (cdr x))
                        (lag (cdr lag)))
                    (or (eq? x lag) (lp x lag))))))))

  (define (not-pair? x) (not (pair? x)))	; Inline me.

  (define (null-list? l)
    (cond ((pair? l) #f)
          ((null? l) #t)
          (else (error 'null-list? "argument out of domain" l))))
  

  (define (list= elt= . lists)
    (or (null? lists) ; special case
        (let lp1 ((list-a (car lists)) (others (cdr lists)))
          (or (null? others)
              (let ((list-b-orig (car others))
                    (others      (cdr others)))
                (if (eq? list-a list-b-orig)	; EQ? => LIST=
                    (lp1 list-b-orig others)
                    (let lp2 ((list-a list-a) (list-b list-b-orig))
                      (if (null-list? list-a)
                          (and (null-list? list-b)
                               (lp1 list-b-orig others))
                          (and (not (null-list? list-b))
                               (elt= (car list-a) (car list-b))
                               (lp2 (cdr list-a) (cdr list-b)))))))))))

  (define (length+ x)			; Returns #f if X is circular.
    (let lp ((x x) (lag x) (len 0))
      (if (pair? x)
          (let ((x (cdr x))
                (len (+ len 1)))
            (if (pair? x)
                (let ((x   (cdr x))
                      (lag (cdr lag))
                      (len (+ len 1)))
                  (and (not (eq? x lag)) (lp x lag len)))
                len))
          len)))

  (define (zip list1 . more-lists) (apply map list list1 more-lists))


  ;; Selectors
  ;; ;;;;;;;;;;

  (define first  car)
  (define second cadr)
  (define third  caddr)
  (define fourth cadddr)
  (define (fifth   x) (car    (cddddr x)))
  (define (sixth   x) (cadr   (cddddr x)))
  (define (seventh x) (caddr  (cddddr x)))
  (define (eighth  x) (cadddr (cddddr x)))
  (define (ninth   x) (car  (cddddr (cddddr x))))
  (define (tenth   x) (cadr (cddddr (cddddr x))))

  (define (car+cdr pair) (values (car pair) (cdr pair)))

  (define (take lis k)
    (check-arg integer? k take)
    (let recur ((lis lis) (k k))
      (if (zero? k) '()
          (cons (car lis)
                (recur (cdr lis) (- k 1))))))

  (define (drop lis k)
    (check-arg integer? k drop)
    (let iter ((lis lis) (k k))
      (if (zero? k) lis (iter (cdr lis) (- k 1)))))

  (define (take! lis k)
    (check-arg integer? k take!)
    (if (zero? k) '()
        (begin (set-cdr! (drop lis (- k 1)) '())
               lis)))

  (define (take-right lis k)
    (check-arg integer? k take-right)
    (let lp ((lag lis)  (lead (drop lis k)))
      (if (pair? lead)
          (lp (cdr lag) (cdr lead))
          lag)))

  (define (drop-right lis k)
    (check-arg integer? k drop-right)
    (let recur ((lag lis) (lead (drop lis k)))
      (if (pair? lead)
          (cons (car lag) (recur (cdr lag) (cdr lead)))
          '())))

  (define (drop-right! lis k)
    (check-arg integer? k drop-right!)
    (let ((lead (drop lis k)))
      (if (pair? lead)

          (let lp ((lag lis)  (lead (cdr lead)))	; Standard case
            (if (pair? lead)
                (lp (cdr lag) (cdr lead))
                (begin (set-cdr! lag '())
                       lis)))

          '())))	; Special case dropping everything -- no cons to side-effect.

  (define-syntax receive
    (syntax-rules ()
      [(_ (id* ...) expr body body* ...)
       (let-values ([(id* ...) expr]) body body* ...)]))


  (define (split-at x k)
    (check-arg integer? k split-at)
    (let recur ((lis x) (k k))
      (if (zero? k) (values '() lis)
          (receive (prefix suffix) (recur (cdr lis) (- k 1))
                   (values (cons (car lis) prefix) suffix)))))

  (define (split-at! x k)
    (check-arg integer? k split-at!)
    (if (zero? k) (values '() x)
        (let* ((prev (drop x (- k 1)))
               (suffix (cdr prev)))
          (set-cdr! prev '())
          (values x suffix))))


  (define (last lis) (car (last-pair lis)))

  (define (last-pair lis)
    (check-arg pair? lis last-pair)
    (let lp ((lis lis))
      (let ((tail (cdr lis)))
        (if (pair? tail) (lp tail) lis))))


  ;; Unzippers -- 1 through 5
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (define (unzip1 lis) (map car lis))

  (define (unzip2 lis)
    (let recur ((lis lis))
      (if (null-list? lis) (values lis lis)	; Use NOT-PAIR? to handle
          (let ((elt (car lis)))			; dotted lists.
            (receive (a b) (recur (cdr lis))
                     (values (cons (car  elt) a)
                             (cons (cadr elt) b)))))))

  (define (unzip3 lis)
    (let recur ((lis lis))
      (if (null-list? lis) (values lis lis lis)
          (let ((elt (car lis)))
            (receive (a b c) (recur (cdr lis))
                     (values (cons (car   elt) a)
                             (cons (cadr  elt) b)
                             (cons (caddr elt) c)))))))

  (define (unzip4 lis)
    (let recur ((lis lis))
      (if (null-list? lis) (values lis lis lis lis)
          (let ((elt (car lis)))
            (receive (a b c d) (recur (cdr lis))
                     (values (cons (car    elt) a)
                             (cons (cadr   elt) b)
                             (cons (caddr  elt) c)
                             (cons (cadddr elt) d)))))))

  (define (unzip5 lis)
    (let recur ((lis lis))
      (if (null-list? lis) (values lis lis lis lis lis)
          (let ((elt (car lis)))
            (receive (a b c d e) (recur (cdr lis))
                     (values (cons (car     elt) a)
                             (cons (cadr    elt) b)
                             (cons (caddr   elt) c)
                             (cons (cadddr  elt) d)
                             (cons (car (cddddr  elt)) e)))))))


  ;; append! append-reverse append-reverse! concatenate concatenate!
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (define (append! . lists)
    ;; First, scan through lists looking for a non-empty one.
    (let lp ((lists lists) (prev '()))
      (if (not (pair? lists)) prev
          (let ((first (car lists))
                (rest (cdr lists)))
            (if (not (pair? first)) (lp rest first)

                ;; Now, do the splicing.
                (let lp2 ((tail-cons (last-pair first))
                          (rest rest))
                  (if (pair? rest)
                      (let ((next (car rest))
                            (rest (cdr rest)))
                        (set-cdr! tail-cons next)
                        (lp2 (if (pair? next) (last-pair next) tail-cons)
                             rest))
                      first)))))))

  (define (append-reverse rev-head tail)
    (let lp ((rev-head rev-head) (tail tail))
      (if (null-list? rev-head) tail
          (lp (cdr rev-head) (cons (car rev-head) tail)))))

  (define (append-reverse! rev-head tail)
    (let lp ((rev-head rev-head) (tail tail))
      (if (null-list? rev-head) tail
          (let ((next-rev (cdr rev-head)))
            (set-cdr! rev-head tail)
            (lp next-rev rev-head)))))


  (define (concatenate  lists) (reduce-right append  '() lists))
  (define (concatenate! lists) (reduce-right append! '() lists))

  ;; Fold/map internal utilities
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (define (%cdrs lists)
    (call-with-current-continuation
     (lambda (abort)
       (let recur ((lists lists))
         (if (pair? lists)
             (let ((lis (car lists)))
               (if (null-list? lis) (abort '())
                   (cons (cdr lis) (recur (cdr lists)))))
             '())))))

  (define (%cars+ lists last-elt)	; (append! (map car lists) (list last-elt))
    (let recur ((lists lists))
      (if (pair? lists) (cons (caar lists) (recur (cdr lists))) (list last-elt))))

  (define (%cars+cdrs lists)
    (let f ([ls lists] [a* '()] [d* '()])
      (cond
       [(pair? ls) 
        (let ([a (car ls)])
          (if (pair? a) 
              (f (cdr ls) (cons (car a) a*) (cons (cdr a) d*))
              (values '() '())))]
       [else (values (reverse a*) (reverse d*))])))

  (define (%cars+cdrs+ lists cars-final)
    (call-with-current-continuation
     (lambda (abort)
       (let recur ((lists lists))
         (if (pair? lists)
             (receive (list other-lists) (car+cdr lists)
                      (if (null-list? list) (abort '() '()) ; LIST is empty -- bail out
                          (receive (a d) (car+cdr list)
                                   (receive (cars cdrs) (recur other-lists)
                                            (values (cons a cars) (cons d cdrs))))))
             (values (list cars-final) '()))))))

  (define (%cars+cdrs/no-test lists)
    (let recur ((lists lists))
      (if (pair? lists)
          (receive (list other-lists) (car+cdr lists)
                   (receive (a d) (car+cdr list)
                            (receive (cars cdrs) (recur other-lists)
                                     (values (cons a cars) (cons d cdrs)))))
          (values '() '()))))


  ;; count
  ;; ;;;;;;
  (define (count pred list1 . lists)
    (check-arg procedure? pred count)
    (if (pair? lists)

        ;; N-ary case
        (let lp ((list1 list1) (lists lists) (i 0))
          (if (null-list? list1) i
              (receive (as ds) (%cars+cdrs lists)
                       (if (null? as) i
                           (lp (cdr list1) ds
                               (if (apply pred (car list1) as) (+ i 1) i))))))

        ;; Fast path
        (let lp ((lis list1) (i 0))
          (if (null-list? lis) i
              (lp (cdr lis) (if (pred (car lis)) (+ i 1) i))))))


  ;; fold/unfold
  ;; ;;;;;;;;;;;;

  (define unfold-right
    (case-lambda
     [(p f g seed)
      (unfold-right p f g seed '())]
     [(p f g seed tail)
      (check-arg procedure? p unfold-right)
      (check-arg procedure? f unfold-right)
      (check-arg procedure? g unfold-right)
      (let lp ((seed seed) (ans tail))
        (if (p seed) ans
            (lp (g seed)
                (cons (f seed) ans))))]))


  (define (unfold p f g seed . maybe-tail-gen)
    (check-arg procedure? p unfold)
    (check-arg procedure? f unfold)
    (check-arg procedure? g unfold)
    (if (pair? maybe-tail-gen) ;;; so much for :optional (aghuloum)

        (let ((tail-gen (car maybe-tail-gen)))
          (if (pair? (cdr maybe-tail-gen))
              (apply error 'unfold "Too many arguments" p f g seed maybe-tail-gen)

              (let recur ((seed seed))
                (if (p seed) (tail-gen seed)
                    (cons (f seed) (recur (g seed)))))))

        (let recur ((seed seed))
          (if (p seed) '()
              (cons (f seed) (recur (g seed)))))))
  

  (define (fold kons knil lis1 . lists)
    (check-arg procedure? kons fold)
    (if (pair? lists)
        (let lp ((lists (cons lis1 lists)) (ans knil))	; N-ary case
          (receive (cars+ans cdrs) (%cars+cdrs+ lists ans)
                   (if (null? cars+ans) ans ; Done.
                       (lp cdrs (apply kons cars+ans)))))
        
        (let lp ((lis lis1) (ans knil))			; Fast path
          (if (null-list? lis) ans
              (lp (cdr lis) (kons (car lis) ans))))))


  (define (fold-right kons knil lis1 . lists)
    (check-arg procedure? kons fold-right)
    (if (pair? lists)
        (let recur ((lists (cons lis1 lists)))		; N-ary case
          (let ((cdrs (%cdrs lists)))
            (if (null? cdrs) knil
                (apply kons (%cars+ lists (recur cdrs))))))

        (let recur ((lis lis1))				; Fast path
          (if (null-list? lis) knil
              (let ((head (car lis)))
                (kons head (recur (cdr lis))))))))


  (define (pair-fold-right f zero lis1 . lists)
    (check-arg procedure? f pair-fold-right)
    (if (pair? lists)
        (let recur ((lists (cons lis1 lists)))		; N-ary case
          (let ((cdrs (%cdrs lists)))
            (if (null? cdrs) zero
                (apply f (append! lists (list (recur cdrs)))))))

        (let recur ((lis lis1))				; Fast path
          (if (null-list? lis) zero (f lis (recur (cdr lis)))))))

  (define (pair-fold f zero lis1 . lists)
    (check-arg procedure? f pair-fold)
    (if (pair? lists)
        (let lp ((lists (cons lis1 lists)) (ans zero))	; N-ary case
          (let ((tails (%cdrs lists)))
            (if (null? tails) ans
                (lp tails (apply f (append! lists (list ans)))))))

        (let lp ((lis lis1) (ans zero))
          (if (null-list? lis) ans
              (let ((tail (cdr lis)))		; Grab the cdr now,
                (lp tail (f lis ans)))))))	; in case F SET-CDR!s LIS.

  ;; REDUCE and REDUCE-RIGHT only use RIDENTITY in the empty-list case.
  ;; These cannot meaningfully be n-ary.

  (define (reduce f ridentity lis)
    (check-arg procedure? f reduce)
    (if (null-list? lis) ridentity
        (fold f (car lis) (cdr lis))))

  (define (reduce-right f ridentity lis)
    (check-arg procedure? f reduce-right)
    (if (null-list? lis) ridentity
        (let recur ((head (car lis)) (lis (cdr lis)))
          (if (pair? lis)
              (f head (recur (car lis) (cdr lis)))
              head))))

  ;; Mappers: append-map append-map! pair-for-each map! filter-map map-in-order
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (define (append-map f lis1 . lists)
    (check-arg procedure? f append-map)
    (really-append-map append  f lis1 lists))
  (define (append-map! f lis1 . lists) 
    (check-arg procedure? f append-map!)
    (really-append-map append! f lis1 lists))

  (define (really-append-map appender f lis1 lists)
    (if (pair? lists)
        (receive (cars cdrs) (%cars+cdrs (cons lis1 lists))
                 (if (null? cars) '()
                     (let recur ((cars cars) (cdrs cdrs))
                       (let ((vals (apply f cars)))
                         (receive (cars2 cdrs2) (%cars+cdrs cdrs)
                                  (if (null? cars2) vals
                                      (appender vals (recur cars2 cdrs2))))))))

        ;; Fast path
        (if (null-list? lis1) '()
            (let recur ((elt (car lis1)) (rest (cdr lis1)))
              (let ((vals (f elt)))
                (if (null-list? rest) vals
                    (appender vals (recur (car rest) (cdr rest)))))))))


  (define (pair-for-each proc lis1 . lists)
    (check-arg procedure? proc pair-for-each)
    (if (pair? lists)

        (let lp ((lists (cons lis1 lists)))
          (let ((tails (%cdrs lists)))
            (if (pair? tails)
                (begin (apply proc lists)
                       (lp tails)))))

        ;; Fast path.
        (let lp ((lis lis1))
          (if (not (null-list? lis))
              (let ((tail (cdr lis)))	; Grab the cdr now,
                (proc lis)		; in case PROC SET-CDR!s LIS.
                (lp tail))))))

  ;; We stop when LIS1 runs out, not when any list runs out.
  (define (map! f lis1 . lists)
    (check-arg procedure? f map!)
    (if (pair? lists)
        (let lp ((lis1 lis1) (lists lists))
          (if (not (null-list? lis1))
              (receive (heads tails) (%cars+cdrs/no-test lists)
                       (set-car! lis1 (apply f (car lis1) heads))
                       (lp (cdr lis1) tails))))

        ;; Fast path.
        (pair-for-each (lambda (pair) (set-car! pair (f (car pair)))) lis1))
    lis1)


  ;; Map F across L, and save up all the non-false results.
  (define (filter-map f lis1 . lists)
    (check-arg procedure? f filter-map)
    (if (pair? lists)
        (let recur ((lists (cons lis1 lists)))
          (receive (cars cdrs) (%cars+cdrs lists)
                   (if (pair? cars)
                       (cond ((apply f cars) => (lambda (x) (cons x (recur cdrs))))
                             (else (recur cdrs))) ; Tail call in this arm.
                       '())))
        
        ;; Fast path.
        (let recur ((lis lis1))
          (if (null-list? lis) lis
              (let ((tail (recur (cdr lis))))
                (cond ((f (car lis)) => (lambda (x) (cons x tail)))
                      (else tail)))))))


  ;; Map F across lists, guaranteeing to go left-to-right.
  ;; NOTE: Some implementations of R5RS MAP are compliant with this spec;
  ;; in which case this procedure may simply be defined as a synonym for MAP.

  (define (map-in-order f lis1 . lists)
    (check-arg procedure? f map-in-order)
    (if (pair? lists)
        (let recur ((lists (cons lis1 lists)))
          (receive (cars cdrs) (%cars+cdrs lists)
                   (if (pair? cars)
                       (let ((x (apply f cars)))		; Do head first,
                         (cons x (recur cdrs)))		; then tail.
                       '())))
        
        ;; Fast path.
        (let recur ((lis lis1))
          (if (null-list? lis) lis
              (let ((tail (cdr lis))
                    (x (f (car lis))))		; Do head first,
                (cons x (recur tail)))))))	; then tail.


  ;; We extend MAP to handle arguments of unequal length.
  (define map map-in-order)	

  ;; Contributed by Michael Sperber since it was missing from the
  ;; reference implementation.
  (define (for-each f lis1 . lists)
    (if (pair? lists)
        (let recur ((lists (cons lis1 lists)))
          (receive (cars cdrs) (%cars+cdrs lists)
                   (if (pair? cars)
                       (begin
                         (apply f cars)	; Do head first,
                         (recur cdrs)))))	; then tail.
        
        ;; Fast path.
        (let recur ((lis lis1))
          (if (not (null-list? lis))
              (begin
                (f (car lis))		; Do head first,
                (recur (cdr lis)))))))	; then tail.

  ;; filter, remove, partition
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; FILTER, REMOVE, PARTITION and their destructive counterparts do not
  ;; disorder the elements of their argument.

  ;; This FILTER shares the longest tail of L that has no deleted elements.
  ;; If Scheme had multi-continuation calls, they could be made more efficient.

  (define (filter pred lis)			; Sleazing with EQ? makes this
    (check-arg procedure? pred filter)		; one faster.
    (let recur ((lis lis))		
      (if (null-list? lis) lis			; Use NOT-PAIR? to handle dotted lists.
          (let ((head (car lis))
                (tail (cdr lis)))
            (if (pred head)
                (let ((new-tail (recur tail)))	; Replicate the RECUR call so
                  (if (eq? tail new-tail) lis
                      (cons head new-tail)))
                (recur tail))))))			; this one can be a tail call.


  (define (filter! pred lis)
    (check-arg procedure? pred filter!)
    (let lp ((ans lis))
      (cond ((null-list? ans)       ans)			; Scan looking for
            ((not (pred (car ans))) (lp (cdr ans)))	; first cons of result.
            (else (letrec ((scan-in (lambda (prev lis)
                                      (if (pair? lis)
                                          (if (pred (car lis))
                                              (scan-in lis (cdr lis))
                                              (scan-out prev (cdr lis))))))
                           (scan-out (lambda (prev lis)
                                       (let lp ((lis lis))
                                         (if (pair? lis)
                                             (if (pred (car lis))
                                                 (begin (set-cdr! prev lis)
                                                        (scan-in lis (cdr lis)))
                                                 (lp (cdr lis)))
                                             (set-cdr! prev lis))))))
                    (scan-in ans (cdr ans))
                    ans)))))

  (define (partition pred lis)
    (check-arg procedure? pred partition)
    (let recur ((lis lis))
      (if (null-list? lis) (values lis lis)	; Use NOT-PAIR? to handle dotted lists.
          (let ((elt (car lis))
                (tail (cdr lis)))
            (receive (in out) (recur tail)
                     (if (pred elt)
                         (values (if (pair? out) (cons elt in) lis) out)
                         (values in (if (pair? in) (cons elt out) lis))))))))

  (define (partition! pred lis)
    (check-arg procedure? pred partition!)
    (if (null-list? lis) (values lis lis)
        (letrec ((scan-in (lambda (in-prev out-prev lis)
                            (let lp ((in-prev in-prev) (lis lis))
                              (if (pair? lis)
                                  (if (pred (car lis))
                                      (lp lis (cdr lis))
                                      (begin (set-cdr! out-prev lis)
                                             (scan-out in-prev lis (cdr lis))))
                                  (set-cdr! out-prev lis))))) ; Done.

                 (scan-out (lambda (in-prev out-prev lis)
                             (let lp ((out-prev out-prev) (lis lis))
                               (if (pair? lis)
                                   (if (pred (car lis))
                                       (begin (set-cdr! in-prev lis)
                                              (scan-in lis out-prev (cdr lis)))
                                       (lp lis (cdr lis)))
                                   (set-cdr! in-prev lis)))))) ; Done.

          ;; Crank up the scan&splice loops.
          (if (pred (car lis))
              ;; LIS begins in-list. Search for out-list's first pair.
              (let lp ((prev-l lis) (l (cdr lis)))
                (cond ((not (pair? l)) (values lis l))
                      ((pred (car l)) (lp l (cdr l)))
                      (else (scan-out prev-l l (cdr l))
                            (values lis l))))	; Done.

              ;; LIS begins out-list. Search for in-list's first pair.
              (let lp ((prev-l lis) (l (cdr lis)))
                (cond ((not (pair? l)) (values l lis))
                      ((pred (car l))
                       (scan-in l prev-l (cdr l))
                       (values l lis))		; Done.
                      (else (lp l (cdr l)))))))))


  ;; Inline us, please.
  (define (remove  pred l) (filter  (lambda (x) (not (pred x))) l))
  (define (remove! pred l) (filter! (lambda (x) (not (pred x))) l))

  (define delete 
    (case-lambda
     [(x lis)
      (delete x lis equal?)]
     [(x lis =) 
      (filter (lambda (y) (not (= x y))) lis)]))

  (define delete! 
    (case-lambda
     [(x lis)
      (delete! x lis equal?)]
     [(x lis =)
      (filter! (lambda (y) (not (= x y))) lis)]))

  ;; Extended from R4RS to take an optional comparison argument.
  (define member  
    (case-lambda
     [(x lis)
      (member x lis equal?)]
     [(x lis =)
      (find-tail (lambda (y) (= x y)) lis)]))

  (define delete-duplicates 
    (case-lambda
     [(lis)
      (delete-duplicates lis equal?)]
     [(lis elt=)
      (check-arg procedure? elt= delete-duplicates)
      (let recur ((lis lis))
        (if (null-list? lis) lis
            (let* ((x (car lis))
                   (tail (cdr lis))
                   (new-tail (recur (delete x tail elt=))))
              (if (eq? tail new-tail) lis (cons x new-tail)))))]))

  (define delete-duplicates! 
    (case-lambda
     [(lis)
      (delete-duplicates! lis equal?)]
     [(lis elt=)
      (check-arg procedure? elt= delete-duplicates!)
      (let recur ((lis lis))
        (if (null-list? lis) lis
            (let* ((x (car lis))
                   (tail (cdr lis))
                   (new-tail (recur (delete! x tail elt=))))
              (when (not (eq? tail new-tail))
                (set-cdr! lis new-tail))
              lis)))]))


  ;; alist stuff
  ;; ;;;;;;;;;;;;

  (define assoc 
    (case-lambda
     [(x lis)
      (assoc x lis equal?)]
     [(x lis =)
      (find (lambda (entry) (= x (car entry))) lis)]))

  (define (alist-cons key datum alist) (cons (cons key datum) alist))

  (define (alist-copy alist)
    (map (lambda (elt) (cons (car elt) (cdr elt)))
         alist))

  (define alist-delete 
    (case-lambda
     [(key alist)
      (alist-delete key alist equal?)]
     [(key alist =)
      (filter (lambda (elt) (not (= key (car elt)))) alist)]))

  (define alist-delete! 
    (case-lambda
     [(key alist)
      (alist-delete! key alist equal?)]
     [(key alist =)
      (filter! (lambda (elt) (not (= key (car elt)))) alist)]))


  ;; find find-tail take-while drop-while span break any every list-index
  ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  (define (find pred list)
    (cond ((find-tail pred list) => car)
          (else #f)))

  (define (find-tail pred list)
    (check-arg procedure? pred find-tail)
    (let lp ((list list))
      (and (not (null-list? list))
           (if (pred (car list)) list
               (lp (cdr list))))))

  (define (take-while pred lis)
    (check-arg procedure? pred take-while)
    (let recur ((lis lis))
      (if (null-list? lis) '()
          (let ((x (car lis)))
            (if (pred x)
                (cons x (recur (cdr lis)))
                '())))))

  (define (drop-while pred lis)
    (check-arg procedure? pred drop-while)
    (let lp ((lis lis))
      (if (null-list? lis) '()
          (if (pred (car lis))
              (lp (cdr lis))
              lis))))

  (define (take-while! pred lis)
    (check-arg procedure? pred take-while!)
    (if (or (null-list? lis) (not (pred (car lis)))) '()
        (begin (let lp ((prev lis) (rest (cdr lis)))
                 (if (pair? rest)
                     (let ((x (car rest)))
                       (if (pred x) (lp rest (cdr rest))
                           (set-cdr! prev '())))))
               lis)))

  (define (span pred lis)
    (check-arg procedure? pred span)
    (let recur ((lis lis))
      (if (null-list? lis) (values '() '())
          (let ((x (car lis)))
            (if (pred x)
                (receive (prefix suffix) (recur (cdr lis))
                         (values (cons x prefix) suffix))
                (values '() lis))))))

  (define (span! pred lis)
    (check-arg procedure? pred span!)
    (if (or (null-list? lis) (not (pred (car lis)))) (values '() lis)
        (let ((suffix (let lp ((prev lis) (rest (cdr lis)))
                        (if (null-list? rest) rest
                            (let ((x (car rest)))
                              (if (pred x) (lp rest (cdr rest))
                                  (begin (set-cdr! prev '())
                                         rest)))))))
          (values lis suffix))))
  

  (define (break  pred lis) (span  (lambda (x) (not (pred x))) lis))
  (define (break! pred lis) (span! (lambda (x) (not (pred x))) lis))

  (define (any pred lis1 . lists)
    (check-arg procedure? pred any)
    (if (pair? lists)

        ;; N-ary case
        (receive (heads tails) (%cars+cdrs (cons lis1 lists))
                 (and (pair? heads)
                      (let lp ((heads heads) (tails tails))
                        (receive (next-heads next-tails) (%cars+cdrs tails)
                                 (if (pair? next-heads)
                                     (or (apply pred heads) (lp next-heads next-tails))
                                     (apply pred heads)))))) ; Last PRED app is tail call.

        ;; Fast path
        (and (not (null-list? lis1))
             (let lp ((head (car lis1)) (tail (cdr lis1)))
               (if (null-list? tail)
                   (pred head)		; Last PRED app is tail call.
                   (or (pred head) (lp (car tail) (cdr tail))))))))

  (define every
    (case-lambda
     [(p ls) 
      (or (null-list? ls)
          (let f ([p p] [a (car ls)] [d (cdr ls)])
            (cond
             [(pair? d) 
              (and (p a) (f p (car d) (cdr d)))]
             [else (p a)])))]
     [(p ls1 ls2)
      (cond
       [(and (pair? ls1) (pair? ls2)) 
        (let f ([p p] [a1 (car ls1)] [d1 (cdr ls1)] [a2 (car ls2)] [d2 (cdr ls2)])
          (cond
           [(and (pair? d1) (pair? d2)) 
            (and (p a1 a2) (f p (car d1) (cdr d1) (car d2) (cdr d2)))]
           [else (p a1 a2)]))]
       [else #t])]
     [(pred lis1 . lists)
      (receive (heads tails) (%cars+cdrs (cons lis1 lists))
               (or (not (pair? heads))
                   (let lp ((heads heads) (tails tails))
                     (receive (next-heads next-tails) (%cars+cdrs tails)
                              (if (pair? next-heads)
                                  (and (apply pred heads) (lp next-heads next-tails))
                                  (apply pred heads))))))]))

  (define (list-index pred lis1 . lists)
    (check-arg procedure? pred list-index)
    (if (pair? lists)

        ;; N-ary case
        (let lp ((lists (cons lis1 lists)) (n 0))
          (receive (heads tails) (%cars+cdrs lists)
                   (and (pair? heads)
                        (if (apply pred heads) n
                            (lp tails (+ n 1))))))

        ;; Fast path
        (let lp ((lis lis1) (n 0))
          (and (not (null-list? lis))
               (if (pred (car lis)) n (lp (cdr lis) (+ n 1)))))))

  ;; Reverse
  ;; ;;;;;;;;

  (define (reverse! lis)
    (let lp ((lis lis) (ans '()))
      (if (null-list? lis) ans
          (let ((tail (cdr lis)))
            (set-cdr! lis ans)
            (lp tail lis)))))

  ;; Lists-as-sets
  ;; ;;;;;;;;;;;;;;

  (define (%lset2<= = lis1 lis2) (every (lambda (x) (member x lis2 =)) lis1))

  (define (lset<= = . lists)
    (check-arg procedure? = lset<=)
    (or (not (pair? lists)) ; 0-ary case
        (let lp ((s1 (car lists)) (rest (cdr lists)))
          (or (not (pair? rest))
              (let ((s2 (car rest))  (rest (cdr rest)))
                (and (or (eq? s2 s1)	; Fast path
                         (%lset2<= = s1 s2)) ; Real test
                     (lp s2 rest)))))))

  (define (lset= = . lists)
    (check-arg procedure? = lset=)
    (or (not (pair? lists)) ; 0-ary case
        (let lp ((s1 (car lists)) (rest (cdr lists)))
          (or (not (pair? rest))
              (let ((s2   (car rest))
                    (rest (cdr rest)))
                (and (or (eq? s1 s2)	; Fast path
                         (and (%lset2<= = s1 s2) (%lset2<= = s2 s1))) ; Real test
                     (lp s2 rest)))))))


  (define (lset-adjoin = lis . elts)
    (check-arg procedure? = lset-adjoin)
    (fold (lambda (elt ans) (if (member elt ans =) ans (cons elt ans)))
          lis elts))


  (define (lset-union = . lists)
    (check-arg procedure? = lset-union)
    (reduce (lambda (lis ans)		; Compute ANS + LIS.
              (cond ((null? lis) ans)	; Don't copy any lists
                    ((null? ans) lis) 	; if we don't have to.
                    ((eq? lis ans) ans)
                    (else
                     (fold (lambda (elt ans) (if (any (lambda (x) (= x elt)) ans)
                                                 ans
                                                 (cons elt ans)))
                           ans lis))))
            '() lists))

  (define (lset-union! = . lists)
    (check-arg procedure? = lset-union!)
    (reduce (lambda (lis ans)		; Splice new elts of LIS onto the front of ANS.
              (cond ((null? lis) ans)	; Don't copy any lists
                    ((null? ans) lis) 	; if we don't have to.
                    ((eq? lis ans) ans)
                    (else
                     (pair-fold (lambda (pair ans)
                                  (let ((elt (car pair)))
                                    (if (any (lambda (x) (= x elt)) ans)
                                        ans
                                        (begin (set-cdr! pair ans) pair))))
                                ans lis))))
            '() lists))


  (define (lset-intersection = lis1 . lists)
    (check-arg procedure? = lset-intersection)
    (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
      (cond ((any null-list? lists) '())		; Short cut
            ((null? lists)          lis1)		; Short cut
            (else (filter (lambda (x)
                            (every (lambda (lis) (member x lis =)) lists))
                          lis1)))))

  (define (lset-intersection! = lis1 . lists)
    (check-arg procedure? = lset-intersection!)
    (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
      (cond ((any null-list? lists) '())		; Short cut
            ((null? lists)          lis1)		; Short cut
            (else (filter! (lambda (x)
                             (every (lambda (lis) (member x lis =)) lists))
                           lis1)))))


  (define (lset-difference = lis1 . lists)
    (check-arg procedure? = lset-difference)
    (let ((lists (filter pair? lists)))	; Throw out empty lists.
      (cond ((null? lists)     lis1)	; Short cut
            ((memq lis1 lists) '())	; Short cut
            (else (filter (lambda (x)
                            (every (lambda (lis) (not (member x lis =)))
                                   lists))
                          lis1)))))

  (define (lset-difference! = lis1 . lists)
    (check-arg procedure? = lset-difference!)
    (let ((lists (filter pair? lists)))	; Throw out empty lists.
      (cond ((null? lists)     lis1)	; Short cut
            ((memq lis1 lists) '())	; Short cut
            (else (filter! (lambda (x)
                             (every (lambda (lis) (not (member x lis =)))
                                    lists))
                           lis1)))))


  (define (lset-xor = . lists)
    (check-arg procedure? = lset-xor)
    (reduce (lambda (b a)			; Compute A xor B:
              ;; Note that this code relies on the constant-time
              ;; short-cuts provided by LSET-DIFF+INTERSECTION,
              ;; LSET-DIFFERENCE & APPEND to provide constant-time short
              ;; cuts for the cases A = (), B = (), and A eq? B. It takes
              ;; a careful case analysis to see it, but it's carefully
              ;; built in.

              ;; Compute a-b and a^b, then compute b-(a^b) and
              ;; cons it onto the front of a-b.
              (receive (a-b a-int-b)   (lset-diff+intersection = a b)
                       (cond ((null? a-b)     (lset-difference = b a))
                             ((null? a-int-b) (append b a))
                             (else (fold (lambda (xb ans)
                                           (if (member xb a-int-b =) ans (cons xb ans)))
                                         a-b
                                         b)))))
            '() lists))


  (define (lset-xor! = . lists)
    (check-arg procedure? = lset-xor!)
    (reduce (lambda (b a)			; Compute A xor B:
              ;; Note that this code relies on the constant-time
              ;; short-cuts provided by LSET-DIFF+INTERSECTION,
              ;; LSET-DIFFERENCE & APPEND to provide constant-time short
              ;; cuts for the cases A = (), B = (), and A eq? B. It takes
              ;; a careful case analysis to see it, but it's carefully
              ;; built in.

              ;; Compute a-b and a^b, then compute b-(a^b) and
              ;; cons it onto the front of a-b.
              (receive (a-b a-int-b)   (lset-diff+intersection! = a b)
                       (cond ((null? a-b)     (lset-difference! = b a))
                             ((null? a-int-b) (append! b a))
                             (else (pair-fold (lambda (b-pair ans)
                                                (if (member (car b-pair) a-int-b =) ans
                                                    (begin (set-cdr! b-pair ans) b-pair)))
                                              a-b
                                              b)))))
            '() lists))


  (define (lset-diff+intersection = lis1 . lists)
    (check-arg procedure? = lset-diff+intersection)
    (cond ((every null-list? lists) (values lis1 '()))	; Short cut
          ((memq lis1 lists)        (values '() lis1))	; Short cut
          (else (partition (lambda (elt)
                             (not (any (lambda (lis) (member elt lis =))
                                       lists)))
                           lis1))))

  (define (lset-diff+intersection! = lis1 . lists)
    (check-arg procedure? = lset-diff+intersection!)
    (cond ((every null-list? lists) (values lis1 '()))	; Short cut
          ((memq lis1 lists)        (values '() lis1))	; Short cut
          (else (partition! (lambda (elt)
                              (not (any (lambda (lis) (member elt lis =))
                                        lists)))
                            lis1))))
  ;; end of library
  ) 

Added srfi/s1/lists.so.

cannot compute difference between binary files

Added srfi/s101/random-access-lists.sls.

















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
#!r6rs
;; SRFI 101: Purely Functional Random-Access Pairs and Lists
;; Copyright (c) David Van Horn 2009.  All Rights Reserved.

;; Permission is hereby granted, free of charge, to any person obtaining
;; a copy of this software and associated documentation
;; files (the "Software"), to deal in the Software without restriction,
;; including without limitation the rights to use, copy, modify, merge,
;; publish, distribute, sublicense, and/or sell copies of the Software,
;; and to permit persons to whom the Software is furnished to do so,
;; subject to the following conditions:

;; The above copyright notice and this permission notice shall be
;; included in all copies or substantial portions of the Software.

;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;; NONINFRINGEMENT. REMEMBER, THERE IS NO SCHEME UNDERGROUND. IN NO EVENT
;; SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
;; DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
;; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
;; THE USE OR OTHER DEALINGS IN THE SOFTWARE.

(library (srfi s101 random-access-lists)
  (export (rename (ra:quote quote)
                  (ra:pair? pair?) 
                  (ra:cons cons)
                  (ra:car car) 
                  (ra:cdr cdr)
                  (ra:caar caar) 
                  (ra:cadr cadr)
                  (ra:cddr cddr)
                  (ra:cdar cdar)
                  (ra:caaar caaar)
                  (ra:caadr caadr)
                  (ra:caddr caddr)
                  (ra:cadar cadar)
                  (ra:cdaar cdaar)
                  (ra:cdadr cdadr)
                  (ra:cdddr cdddr)
                  (ra:cddar cddar)
                  (ra:caaaar caaaar)
                  (ra:caaadr caaadr)
                  (ra:caaddr caaddr)
                  (ra:caadar caadar)
                  (ra:cadaar cadaar)
                  (ra:cadadr cadadr)
                  (ra:cadddr cadddr)
                  (ra:caddar caddar)
                  (ra:cdaaar cdaaar)
                  (ra:cdaadr cdaadr)
                  (ra:cdaddr cdaddr)
                  (ra:cdadar cdadar)
                  (ra:cddaar cddaar)
                  (ra:cddadr cddadr)
                  (ra:cddddr cddddr)
                  (ra:cdddar cdddar)
                  (ra:null? null?)
                  (ra:list? list?)
                  (ra:list list)
                  (ra:make-list make-list)
                  (ra:length length)
                  (ra:append append)
                  (ra:reverse reverse)
                  (ra:list-tail list-tail)
                  (ra:list-ref list-ref)
                  (ra:list-set list-set)
                  (ra:list-ref/update list-ref/update)
                  (ra:map map)
                  (ra:for-each for-each)
                  (ra:random-access-list->linear-access-list
                   random-access-list->linear-access-list)
                  (ra:linear-access-list->random-access-list
                   linear-access-list->random-access-list)))
  
  (import (rnrs base)
          (rnrs lists)
          (rnrs control)
          (rnrs hashtables)
          (rnrs records syntactic)
          (rnrs arithmetic bitwise))          
  
  (define-record-type kons (fields size tree rest))
  (define-record-type node (fields val left right)) 

  ;; Nat -> Nat
  (define (sub1 n) (- n 1))
  (define (add1 n) (+ n 1))
    
  ;; [Tree X] -> X
  (define (tree-val t)
    (if (node? t) 
        (node-val t)
        t))
  
  ;; [X -> Y] [Tree X] -> [Tree Y]
  (define (tree-map f t)
    (if (node? t)
        (make-node (f (node-val t))
                   (tree-map f (node-left t))
                   (tree-map f (node-right t)))
        (f t)))

  ;; [X -> Y] [Tree X] -> unspecified
  (define (tree-for-each f t)
    (if (node? t)
        (begin (f (node-val t))
               (tree-for-each f (node-left t))
               (tree-for-each f (node-right t)))
        (f t)))

  ;; [X Y Z ... -> R] [List [Tree X] [Tree Y] [Tree Z] ...] -> [Tree R]
  (define (tree-map/n f ts)
    (let recr ((ts ts))
      (if (and (pair? ts)
               (node? (car ts)))
          (make-node (apply f (map node-val ts))
                     (recr (map node-left ts))
                     (recr (map node-right ts)))
          (apply f ts))))
  
  ;; [X Y Z ... -> R] [List [Tree X] [Tree Y] [Tree Z] ...] -> unspecified
  (define (tree-for-each/n f ts)
    (let recr ((ts ts))
      (if (and (pair? ts)
               (node? (car ts)))
          (begin (apply f (map node-val ts))
                 (recr (map node-left ts))
                 (recr (map node-right ts)))
          (apply f ts))))
  
  ;; Nat [Nat -> X] -> [Tree X]
  ;; like build-list, but for complete binary trees
  (define (build-tree i f) ;; i = 2^j-1
    (let rec ((i i) (o 0))
      (if (= 1 i) 
          (f o)
          (let ((i/2 (half i)))
            (make-node (f o)
                       (rec i/2 (add1 o))
                       (rec i/2 (+ 1 o i/2)))))))
  
  ;; Consumes n = 2^i-1 and produces 2^(i-1)-1.
  ;; Nat -> Nat
  (define (half n)
    (bitwise-arithmetic-shift n -1))

  ;; Nat X -> [Tree X]
  (define (tr:make-tree i x) ;; i = 2^j-1
    (let recr ((i i))
      (if (= 1 i) 
          x
          (let ((n (recr (half i))))
            (make-node x n n)))))
  
  ;; Nat [Tree X] Nat [X -> X] -> X [Tree X]
  (define (tree-ref/update mid t i f)
    (cond ((zero? i)
           (if (node? t) 
               (values (node-val t)
                       (make-node (f (node-val t))
                                  (node-left t)
                                  (node-right t)))
               (values t (f t))))
          ((<= i mid)
           (let-values (((v* t*) (tree-ref/update (half (sub1 mid)) 
                                                  (node-left t) 
                                                  (sub1 i) 
                                                  f)))
             (values v* (make-node (node-val t) t* (node-right t)))))
          (else           
           (let-values (((v* t*) (tree-ref/update (half (sub1 mid)) 
                                                  (node-right t) 
                                                  (sub1 (- i mid)) 
                                                  f)))
             (values v* (make-node (node-val t) (node-left t) t*))))))
  
  ;; Special-cased above to avoid logarathmic amount of cons'ing
  ;; and any multi-values overhead.  Operates in constant space.
  ;; [Tree X] Nat Nat -> X
  ;; invariant: (= mid (half (sub1 (tree-count t))))
  (define (tree-ref/a t i mid) 
    (cond ((zero? i) (tree-val t))
          ((<= i mid) 
           (tree-ref/a (node-left t) 
                       (sub1 i) 
                       (half (sub1 mid))))
          (else 
           (tree-ref/a (node-right t) 
                       (sub1 (- i mid)) 
                       (half (sub1 mid))))))
  
  ;; Nat [Tree X] Nat -> X
  ;; invariant: (= size (tree-count t))
  (define (tree-ref size t i)
    (if (zero? i)
        (tree-val t)
        (tree-ref/a t i (half (sub1 size)))))
  
  ;; Nat [Tree X] Nat [X -> X] -> [Tree X]
  (define (tree-update size t i f)
    (let recr ((mid (half (sub1 size))) (t t) (i i))
      (cond ((zero? i)
             (if (node? t)
                 (make-node (f (node-val t))
                            (node-left t)
                            (node-right t))
                 (f t)))
            ((<= i mid)
             (make-node (node-val t) 
                        (recr (half (sub1 mid))
                              (node-left t) 
                              (sub1 i)) 
                        (node-right t)))
            (else
             (make-node (node-val t) 
                        (node-left t) 
                        (recr (half (sub1 mid))
                              (node-right t) 
                              (sub1 (- i mid))))))))

  ;; ------------------------
  ;; Random access lists
  
  ;; [RaListof X]
  (define ra:null (quote ()))

  ;; [Any -> Boolean]
  (define ra:pair? kons?)
  
  ;; [Any -> Boolean]
  (define ra:null? null?)
  
  ;; X [RaListof X] -> [RaListof X]  /\
  ;; X Y -> [RaPair X Y]
  (define (ra:cons x ls)
    (if (kons? ls)
        (let ((s (kons-size ls)))
          (if (and (kons? (kons-rest ls))
                   (= (kons-size (kons-rest ls))
                      s))
              (make-kons (+ 1 s s) 
                         (make-node x 
                                    (kons-tree ls)
                                    (kons-tree (kons-rest ls)))
                         (kons-rest (kons-rest ls)))
              (make-kons 1 x ls)))
        (make-kons 1 x ls)))

  
  ;; [RaPair X Y] -> X Y
  (define ra:car+cdr 
    (lambda (p)
      (assert (kons? p))
      (if (node? (kons-tree p))
          (let ((s* (half (kons-size p))))
            (values (tree-val (kons-tree p))
                    (make-kons s* 
                               (node-left (kons-tree p))
                               (make-kons s*
                                          (node-right (kons-tree p))
                                          (kons-rest p)))))
          (values (kons-tree p) (kons-rest p)))))
  
  ;; [RaPair X Y] -> X
  (define (ra:car p)
    (call-with-values (lambda () (ra:car+cdr p))
                      (lambda (car cdr) car)))
  
  ;; [RaPair X Y] -> Y
  (define (ra:cdr p)
    (call-with-values (lambda () (ra:car+cdr p))
                      (lambda (car cdr) cdr)))
  
  ;; [RaListof X] Nat [X -> X] -> X [RaListof X]
  (define (ra:list-ref/update ls i f)
    ;(assert (< i (ra:length ls)))
    (let recr ((xs ls) (j i))
      (if (< j (kons-size xs))
          (let-values (((v* t*) 
                        (tree-ref/update (half (sub1 (kons-size xs))) 
                                         (kons-tree xs) j f)))
            (values v* (make-kons (kons-size xs) 
                                  t* 
                                  (kons-rest xs))))
          (let-values (((v* r*) 
                        (recr (kons-rest xs) 
                              (- j (kons-size xs)))))
            (values v* (make-kons (kons-size xs) 
                                  (kons-tree xs) 
                                  r*))))))
  
  ;; [RaListof X] Nat [X -> X] -> [RaListof X]
  (define (ra:list-update ls i f)
    ;(assert (< i (ra:length ls)))
    (let recr ((xs ls) (j i))
      (let ((s (kons-size xs)))
        (if (< j s) 
            (make-kons s (tree-update s (kons-tree xs) j f) (kons-rest xs))
            (make-kons s (kons-tree xs) (recr (kons-rest xs) (- j s)))))))

  ;; [RaListof X] Nat X -> (values X [RaListof X])
  (define (ra:list-ref/set ls i v)
    (ra:list-ref/update ls i (lambda (_) v)))

  ;; X ... -> [RaListof X]
  (define (ra:list . xs)
    (fold-right ra:cons ra:null xs))

  ;; Nat X -> [RaListof X]
  (define ra:make-list
    (case-lambda
     ((k) (ra:make-list k 0))
     ((k obj)
      (let loop ((n k) (a ra:null))
        (cond ((zero? n) a)
              (else 
               (let ((t (largest-skew-binary n)))
                 (loop (- n t)
                       (make-kons t (tr:make-tree t obj) a)))))))))

  ;; A Skew is a Nat 2^k-1 with k > 0.
  
  ;; Skew -> Skew
  (define (skew-succ t) (add1 (bitwise-arithmetic-shift t 1)))
  
  ;; Computes the largest skew binary term t <= n.
  ;; Nat -> Skew
  (define (largest-skew-binary n)
    (if (= 1 n) 
        1
        (let* ((t (largest-skew-binary (half n)))
               (s (skew-succ t)))
          (if (> s n) t s))))  

  ;; [Any -> Boolean]
  ;; Is x a PROPER list?
  (define (ra:list? x)
    (or (ra:null? x)
        (and (kons? x)
             (ra:list? (kons-rest x)))))
  
  (define ra:caar (lambda (ls) (ra:car (ra:car ls))))
  (define ra:cadr (lambda (ls) (ra:car (ra:cdr ls))))
  (define ra:cddr (lambda (ls) (ra:cdr (ra:cdr ls))))
  (define ra:cdar (lambda (ls) (ra:cdr (ra:car ls))))
    
  (define ra:caaar (lambda (ls) (ra:car (ra:car (ra:car ls)))))
  (define ra:caadr (lambda (ls) (ra:car (ra:car (ra:cdr ls)))))
  (define ra:caddr (lambda (ls) (ra:car (ra:cdr (ra:cdr ls)))))
  (define ra:cadar (lambda (ls) (ra:car (ra:cdr (ra:car ls)))))
  (define ra:cdaar (lambda (ls) (ra:cdr (ra:car (ra:car ls)))))
  (define ra:cdadr (lambda (ls) (ra:cdr (ra:car (ra:cdr ls)))))
  (define ra:cdddr (lambda (ls) (ra:cdr (ra:cdr (ra:cdr ls)))))
  (define ra:cddar (lambda (ls) (ra:cdr (ra:cdr (ra:car ls)))))
  
  (define ra:caaaar (lambda (ls) (ra:car (ra:car (ra:car (ra:car ls))))))
  (define ra:caaadr (lambda (ls) (ra:car (ra:car (ra:car (ra:cdr ls))))))
  (define ra:caaddr (lambda (ls) (ra:car (ra:car (ra:cdr (ra:cdr ls))))))
  (define ra:caadar (lambda (ls) (ra:car (ra:car (ra:cdr (ra:car ls))))))
  (define ra:cadaar (lambda (ls) (ra:car (ra:cdr (ra:car (ra:car ls))))))
  (define ra:cadadr (lambda (ls) (ra:car (ra:cdr (ra:car (ra:cdr ls))))))
  (define ra:cadddr (lambda (ls) (ra:car (ra:cdr (ra:cdr (ra:cdr ls))))))
  (define ra:caddar (lambda (ls) (ra:car (ra:cdr (ra:cdr (ra:car ls))))))
  (define ra:cdaaar (lambda (ls) (ra:cdr (ra:car (ra:car (ra:car ls))))))
  (define ra:cdaadr (lambda (ls) (ra:cdr (ra:car (ra:car (ra:cdr ls))))))
  (define ra:cdaddr (lambda (ls) (ra:cdr (ra:car (ra:cdr (ra:cdr ls))))))
  (define ra:cdadar (lambda (ls) (ra:cdr (ra:car (ra:cdr (ra:car ls))))))
  (define ra:cddaar (lambda (ls) (ra:cdr (ra:cdr (ra:car (ra:car ls))))))
  (define ra:cddadr (lambda (ls) (ra:cdr (ra:cdr (ra:car (ra:cdr ls))))))
  (define ra:cddddr (lambda (ls) (ra:cdr (ra:cdr (ra:cdr (ra:cdr ls))))))
  (define ra:cdddar (lambda (ls) (ra:cdr (ra:cdr (ra:cdr (ra:car ls))))))
  
  ;; [RaList X] -> Nat
  (define (ra:length ls)
    (assert (ra:list? ls))
    (let recr ((ls ls))
      (if (kons? ls)
          (+ (kons-size ls) (recr (kons-rest ls)))
          0)))

  (define (make-foldl empty? first rest)
    (letrec ((f (lambda (cons empty ls)
                  (if (empty? ls) 
                      empty
                      (f cons
                         (cons (first ls) empty) 
                         (rest ls))))))
      f))
  
  (define (make-foldr empty? first rest)
    (letrec ((f (lambda (cons empty ls)
                  (if (empty? ls) 
                      empty
                      (cons (first ls)
                            (f cons empty (rest ls)))))))
      f))

  ;; [X Y -> Y] Y [RaListof X] -> Y
  (define ra:foldl/1 (make-foldl ra:null? ra:car ra:cdr))
  (define ra:foldr/1 (make-foldr ra:null? ra:car ra:cdr))

  ;; [RaListof X] ... -> [RaListof X]
  (define (ra:append . lss)
    (cond ((null? lss) ra:null)
          (else (let recr ((lss lss))
                  (cond ((null? (cdr lss)) (car lss))
                        (else (ra:foldr/1 ra:cons
                                          (recr (cdr lss))
                                          (car lss))))))))
  
  ;; [RaListof X] -> [RaListof X]
  (define (ra:reverse ls)
    (ra:foldl/1 ra:cons ra:null ls))
  
  ;; [RaListof X] Nat -> [RaListof X]
  (define (ra:list-tail ls i)
    (let loop ((xs ls) (j i))
      (cond ((zero? j) xs)
            (else (loop (ra:cdr xs) (sub1 j))))))
  
  ;; [RaListof X] Nat -> X
  ;; Special-cased above to avoid logarathmic amount of cons'ing
  ;; and any multi-values overhead.  Operates in constant space.
  (define (ra:list-ref ls i)
    ;(assert (< i (ra:length ls)))
    (let loop ((xs ls) (j i))
      (if (< j (kons-size xs))
          (tree-ref (kons-size xs) (kons-tree xs) j)
          (loop (kons-rest xs) (- j (kons-size xs))))))
  
  ;; [RaListof X] Nat X -> [RaListof X]
  (define (ra:list-set ls i v)
    (let-values (((_ l*) (ra:list-ref/set ls i v))) l*))
  
  ;; [X ... -> y] [RaListof X] ... -> [RaListof Y]
  ;; Takes advantage of the fact that map produces a list of equal size.
  (define ra:map
    (case-lambda 
      ((f ls)
       (let recr ((ls ls))
         (if (kons? ls)
             (make-kons (kons-size ls) 
                        (tree-map f (kons-tree ls)) 
                        (recr (kons-rest ls)))
             ra:null)))
      ((f . lss)
       ;(check-nary-loop-args 'ra:map (lambda (x) x) f lss)
       (let recr ((lss lss))
         (cond ((ra:null? (car lss)) ra:null)
               (else
                ;; IMPROVE ME: make one pass over lss.
                (make-kons (kons-size (car lss))
                           (tree-map/n f (map kons-tree lss))
                           (recr (map kons-rest lss)))))))))


  ;; [X ... -> Y] [RaListof X] ... -> unspecified
  (define ra:for-each
    (case-lambda 
      ((f ls)
       (when (kons? ls)
         (tree-for-each f (kons-tree ls))
         (ra:for-each f (kons-rest ls))))
      ((f . lss)
       ;(check-nary-loop-args 'ra:map (lambda (x) x) f lss)
       (let recr ((lss lss))
         (when (ra:pair? (car lss))
           (tree-map/n f (map kons-tree lss))
           (recr (map kons-rest lss)))))))

  ;; [RaListof X] -> [Listof X]
  (define (ra:random-access-list->linear-access-list x)
    (ra:foldr/1 cons '() x))

  ;; [Listof X] -> [RaListof X]
  (define (ra:linear-access-list->random-access-list x)
    (fold-right ra:cons '() x))

  ;; This code based on code written by Abdulaziz Ghuloum
  ;; http://ikarus-scheme.org/pipermail/ikarus-users/2009-September/000595.html
  (define get-cached
    (let ((h (make-eq-hashtable)))
      (lambda (x)
        (define (f x)
          (cond
           ((pair? x) (ra:cons (f (car x)) (f (cdr x))))
           ((vector? x) (vector-map f x))
           (else x)))
        (cond
         ((not (or (pair? x) (vector? x))) x)
         ((hashtable-ref h x #f))
         (else
          (let ((v (f x)))
            (hashtable-set! h x v)
            v))))))

  (define-syntax ra:quote
    (syntax-rules ()
      ((ra:quote datum) (get-cached 'datum)))) 

      
  ) ; (srfi :101 random-access-lists)

Added srfi/s101/srfi-101-tests.sps.











































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!r6rs
;; SRFI 101: Purely Functional Random-Access Pairs and Lists
;; Copyright (c) David Van Horn 2009.  All Rights Reserved.

;; Permission is hereby granted, free of charge, to any person obtaining
;; a copy of this software and associated documentation
;; files (the "Software"), to deal in the Software without restriction,
;; including without limitation the rights to use, copy, modify, merge,
;; publish, distribute, sublicense, and/or sell copies of the Software,
;; and to permit persons to whom the Software is furnished to do so,
;; subject to the following conditions:

;; The above copyright notice and this permission notice shall be
;; included in all copies or substantial portions of the Software.

;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;; NONINFRINGEMENT. REMEMBER, THERE IS NO SCHEME UNDERGROUND. IN NO EVENT
;; SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
;; DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
;; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
;; THE USE OR OTHER DEALINGS IN THE SOFTWARE.

;; This test suite has been successfully run on Ikarus (0.0.3),
;; Larceny (0.97), and PLT Scheme (4.2.1.7).

;; To run:
;;    cp srfi-101.sls srfi/%3A101.sls
;;    ikarus --r6rs-script srfi-101-tests.sls
;;    larceny -r6rs -path . -program srfi-101-tests.sls
;;    plt-r6rs ++path . srfi-101-tests.sls

(import (except (rnrs base)
                quote pair? cons car cdr 
                caar cadr cddr cdar
                caaar caadr caddr cadar
                cdaar cdadr cdddr cddar
                caaaar caaadr caaddr caadar
                cadaar cadadr cadddr caddar
                cdaaar cdaadr cdaddr cdadar
                cddaar cddadr cddddr cdddar
                null? list? list length 
                append reverse list-tail
                list-ref map for-each)
        (prefix (rnrs base) r6:)
        (rnrs exceptions)
        (surfage s101 random-access-lists))

(define (check-expect c e)
  (if (pair? c)
      (begin (assert (pair? e))
             (check-expect (car c)
                           (car e))
             (check-expect (cdr c)
                           (cdr e)))
      (assert (equal? c e))))

(define-syntax check-error
  (syntax-rules ()
    ((_ e)
     (let ((f (cons 0 0)))
       (guard (g ((eq? f g) (assert #f))
                 (else 'OK))
              (begin e
                     (raise f)))))))

; quote  

; Bug in Larceny prevents this from working
; https://trac.ccs.neu.edu/trac/larceny/ticket/656
;(check-expect (quote 5) (r6:quote 5))
;(check-expect (quote x) (r6:quote x))

(check-expect (let ((f (lambda () '(x))))
                (eq? (f) (f)))
              #t)

(check-expect '(1 2 3) (list 1 2 3))

; pair?
(check-expect (pair? (cons 'a 'b)) #t)
(check-expect (pair? (list 'a 'b 'c)) #t)
(check-expect (pair? '()) #f)
(check-expect (pair? '#(a b)) #f)

; cons
(check-expect (cons 'a '()) (list 'a))
(check-expect (cons (list 'a) (list 'b 'c 'd))
              (list (list 'a) 'b 'c 'd))
(check-expect (cons "a" (list 'b 'c))
              (list "a" 'b 'c))
(check-expect (cons 'a 3)
              (cons 'a 3))
(check-expect (cons (list 'a 'b) 'c)
              (cons (list 'a 'b) 'c))

; car
(check-expect (car (list 'a 'b 'c))
              'a)
(check-expect (car (list (list 'a) 'b 'c 'd))
              (list 'a))
(check-expect (car (cons 1 2)) 1)
(check-error (car '()))

; cdr
(check-expect (cdr (list (list 'a) 'b 'c 'd))
              (list 'b 'c 'd))
(check-expect (cdr (cons 1 2))
              2)
(check-error (cdr '()))

; null?
(check-expect (eq? null? r6:null?) #t)
(check-expect (null? '()) #t)
(check-expect (null? (cons 1 2)) #f)
(check-expect (null? 4) #f)

; list?
(check-expect (list? (list 'a 'b 'c)) #t)
(check-expect (list? '()) #t)
(check-expect (list? (cons 'a 'b)) #f)

; list
(check-expect (list 'a (+ 3 4) 'c)
              (list 'a 7 'c))
(check-expect (list) '())

; make-list
(check-expect (length (make-list 5)) 5)
(check-expect (make-list 5 0)
              (list 0 0 0 0 0))

; length
(check-expect (length (list 'a 'b 'c)) 3)
(check-expect (length (list 'a (list 'b) (list 'c))) 3)
(check-expect (length '()) 0)

; append
(check-expect (append (list 'x) (list 'y)) (list 'x 'y))
(check-expect (append (list 'a) (list 'b 'c 'd)) (list 'a 'b 'c 'd))
(check-expect (append (list 'a (list 'b)) (list (list 'c))) 
              (list 'a (list 'b) (list 'c)))
(check-expect (append (list 'a 'b) (cons 'c 'd)) 
              (cons 'a (cons 'b (cons 'c 'd))))
(check-expect (append '() 'a) 'a)

; reverse
(check-expect (reverse (list 'a 'b 'c))
              (list 'c 'b 'a))
(check-expect (reverse (list 'a (list 'b 'c) 'd (list 'e (list 'f))))
              (list (list 'e (list 'f)) 'd (list 'b 'c) 'a))

; list-tail
(check-expect (list-tail (list 'a 'b 'c 'd) 2)
              (list 'c 'd))

; list-ref
(check-expect (list-ref (list 'a 'b 'c 'd) 2) 'c)

; list-set
(check-expect (list-set (list 'a 'b 'c 'd) 2 'x)
              (list 'a 'b 'x 'd))

; list-ref/update
(let-values (((a b) 
              (list-ref/update (list 7 8 9 10) 2 -)))
  (check-expect a 9)
  (check-expect b (list 7 8 -9 10)))

; map
(check-expect (map cadr (list (list 'a 'b) (list 'd 'e) (list 'g 'h)))
              (list 'b 'e 'h))
(check-expect (map (lambda (n) (expt n n))
                   (list 1 2 3 4 5))
              (list 1 4 27 256 3125))
(check-expect (map + (list 1 2 3) (list 4 5 6))
              (list 5 7 9))

; for-each
(check-expect (let ((v (make-vector 5)))
                (for-each (lambda (i)
                            (vector-set! v i (* i i)))
                          (list 0 1 2 3 4))
                v)
              '#(0 1 4 9 16))

; random-access-list->linear-access-list
; linear-access-list->random-access-list
(check-expect (random-access-list->linear-access-list '()) '())
(check-expect (linear-access-list->random-access-list '()) '())

(check-expect (random-access-list->linear-access-list (list 1 2 3))
              (r6:list 1 2 3))

(check-expect (linear-access-list->random-access-list (r6:list 1 2 3))
              (list 1 2 3))

Added srfi/s11/let-values.sls.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s11 let-values)
  (export 
    let-values
    let*-values)
  (import 
    (only (rnrs) let-values let*-values))  
)

Added srfi/s13/srfi-13.scm.







































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
;;; SRFI 13 string library reference implementation		-*- Scheme -*-
;;; Olin Shivers 7/2000
;;;
;;; Copyright (c) 1988-1994 Massachusetts Institute of Technology.
;;; Copyright (c) 1998, 1999, 2000 Olin Shivers. All rights reserved.
;;;   The details of the copyrights appear at the end of the file. Short
;;;   summary: BSD-style open source.

;;; Exports:
;;; string-map string-map!
;;; string-fold       string-unfold
;;; string-fold-right string-unfold-right 
;;; string-tabulate string-for-each string-for-each-index
;;; string-every string-any
;;; string-hash string-hash-ci
;;; string-compare string-compare-ci
;;; string=    string<    string>    string<=    string>=    string<>
;;; string-ci= string-ci< string-ci> string-ci<= string-ci>= string-ci<> 
;;; string-downcase  string-upcase  string-titlecase  
;;; string-downcase! string-upcase! string-titlecase! 
;;; string-take string-take-right
;;; string-drop string-drop-right
;;; string-pad string-pad-right
;;; string-trim string-trim-right string-trim-both
;;; string-filter string-delete
;;; string-index string-index-right 
;;; string-skip  string-skip-right
;;; string-count
;;; string-prefix-length string-prefix-length-ci
;;; string-suffix-length string-suffix-length-ci
;;; string-prefix? string-prefix-ci?
;;; string-suffix? string-suffix-ci?
;;; string-contains string-contains-ci
;;; string-copy! substring/shared
;;; string-reverse string-reverse! reverse-list->string
;;; string-concatenate string-concatenate/shared string-concatenate-reverse
;;; string-append/shared
;;; xsubstring string-xcopy!
;;; string-null?
;;; string-join
;;; string-tokenize
;;; string-replace
;;; 
;;; R5RS extended:
;;; string->list string-copy string-fill! 
;;;
;;; R5RS re-exports:
;;; string? make-string string-length string-ref string-set! 
;;;
;;; R5RS re-exports (also defined here but commented-out):
;;; string string-append list->string
;;;
;;; Low-level routines:
;;; make-kmp-restart-vector string-kmp-partial-search kmp-step
;;; string-parse-start+end
;;; string-parse-final-start+end
;;; let-string-start+end
;;; check-substring-spec
;;; substring-spec-ok?

;;; Imports
;;; This is a fairly large library. While it was written for portability, you
;;; must be aware of its dependencies in order to run it in a given scheme
;;; implementation. Here is a complete list of the dependencies it has and the
;;; assumptions it makes beyond stock R5RS Scheme:
;;;
;;; This code has the following non-R5RS dependencies:
;;; - (RECEIVE (var ...) mv-exp body ...) multiple-value binding macro;
;;;
;;; - Various imports from the char-set library for the routines that can
;;;   take char-set arguments;
;;;   
;;; - An n-ary ERROR procedure;
;;;   
;;; - BITWISE-AND for the hash functions;
;;;   
;;; - A simple CHECK-ARG procedure for checking parameter values; it is 
;;;   (lambda (pred val proc) 
;;;     (if (pred val) val (error "Bad arg" val pred proc)))
;;;   
;;; - :OPTIONAL and LET-OPTIONALS* macros for parsing, defaulting & 
;;;   type-checking optional parameters from a rest argument;
;;;   
;;; - CHAR-CASED? and CHAR-TITLECASE for the STRING-TITLECASE & 
;;;   STRING-TITLECASE! procedures. The former returns true iff a character is
;;;   one that has case distinctions; in ASCII it returns true on a-z and A-Z.
;;;   CHAR-TITLECASE is analagous to CHAR-UPCASE and CHAR-DOWNCASE. In ASCII &
;;;   Latin-1, it is the same as CHAR-UPCASE.
;;;
;;; The code depends upon a small set of core string primitives from R5RS:
;;;     MAKE-STRING STRING-REF STRING-SET! STRING? STRING-LENGTH SUBSTRING 
;;; (Actually, SUBSTRING is not a primitive, but we assume that an 
;;; implementation's native version is probably faster than one we could
;;; define, so we import it from R5RS.)
;;;
;;; The code depends upon a small set of R5RS character primitives:
;;;   char? char=? char-ci=? char<? char-ci<?
;;;   char-upcase char-downcase
;;;   char->integer (for the hash functions)
;;;   
;;; We assume the following:
;;; - CHAR-DOWNCASE o CHAR-UPCASE = CHAR-DOWNCASE
;;; - CHAR-CI=? is equivalent to
;;;     (lambda (c1 c2) (char=? (char-downcase (char-upcase c1))
;;;                             (char-downcase (char-upcase c2))))
;;; - CHAR-UPCASE, CHAR-DOWNCASE and CHAR-TITLECASE are locale-insensitive
;;;   and consistent with Unicode's 1-1 char-mapping spec.
;;; These things are typically true, but if not, you would need to modify
;;; the case-mapping and case-insensitive routines.

;;; Enough introductory blather. On to the source code. (But see the end of
;;; the file for further notes on porting & performance tuning.)

 
;;; Support for START/END substring specs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This macro parses optional start/end arguments from arg lists, defaulting
;;; them to 0/(string-length s), and checks them for correctness.

(define-syntax let-string-start+end
  (syntax-rules ()
    ((let-string-start+end (start end) proc s-exp args-exp body ...)
     (receive (start end) (string-parse-final-start+end proc s-exp args-exp)
       body ...))
    ((let-string-start+end (start end rest) proc s-exp args-exp body ...)
     (receive (rest start end) (string-parse-start+end proc s-exp args-exp)
       body ...))))

;;; This one parses out a *pair* of final start/end indices. 
;;; Not exported; for internal use.
(define-syntax let-string-start+end2
  (syntax-rules ()
    ((l-s-s+e2 (start1 end1 start2 end2) proc s1 s2 args body ...)
     (let ((procv proc)) ; Make sure PROC is only evaluated once.
       (let-string-start+end (start1 end1 rest) procv s1 args
         (let-string-start+end (start2 end2) procv s2 rest
           body ...))))))


;;; Returns three values: rest start end

(define (string-parse-start+end proc s args)
  (if (not (string? s)) (error "Non-string value" proc s))
  (let ((slen (string-length s)))
    (if (pair? args)

	(let ((start (car args))
	      (args (cdr args)))
	  (if (and (integer? start) (exact? start) (>= start 0))
	      (receive (end args)
		  (if (pair? args)
		      (let ((end (car args))
			    (args (cdr args)))
			(if (and (integer? end) (exact? end) (<= end slen))
			    (values end args)
			    (error "Illegal substring END spec" proc end s)))
		      (values slen args))
		(if (<= start end) (values args start end)
		    (error "Illegal substring START/END spec"
			   proc start end s)))
	      (error "Illegal substring START spec" proc start s)))

	(values '() 0 slen))))

(define (string-parse-final-start+end proc s args)
  (receive (rest start end) (string-parse-start+end proc s args)
    (if (pair? rest) (error "Extra arguments to procedure" proc rest)
	(values start end))))

(define (substring-spec-ok? s start end)
  (and (string? s)
       (integer? start)
       (exact? start)
       (integer? end)
       (exact? end)
       (<= 0 start)
       (<= start end)
       (<= end (string-length s))))

(define (check-substring-spec proc s start end)
  (if (not (substring-spec-ok? s start end))
      (error "Illegal substring spec." proc s start end)))


;;; Defined by R5RS, so commented out here.
;(define (string . chars)
;  (let* ((len (length chars))
;         (ans (make-string len)))
;    (do ((i 0 (+ i 1))
;	 (chars chars (cdr chars)))
;	((>= i len))
;      (string-set! ans i (car chars)))
;    ans))
;
;(define (string . chars) (string-unfold null? car cdr chars))


 
;;; substring/shared S START [END] 
;;; string-copy      S [START END]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; All this goop is just arg parsing & checking surrounding a call to the
;;; actual primitive, %SUBSTRING/SHARED.

(define (substring/shared s start . maybe-end)
  (check-arg string? s substring/shared)
  (let ((slen (string-length s)))
    (check-arg (lambda (start) (and (integer? start) (exact? start) (<= 0 start)))
	       start substring/shared)
    (%substring/shared s start
		       (:optional maybe-end slen
				  (lambda (end) (and (integer? end)
						     (exact? end)
						     (<= start end)
						     (<= end slen)))))))

;;; Split out so that other routines in this library can avoid arg-parsing
;;; overhead for END parameter.
(define (%substring/shared s start end)
  (if (and (zero? start) (= end (string-length s))) s
      (substring s start end)))

(define (string-copy s . maybe-start+end)
  (let-string-start+end (start end) string-copy s maybe-start+end
    (substring s start end)))

;This library uses the R5RS SUBSTRING, but doesn't export it.
;Here is a definition, just for completeness.
;(define (substring s start end)
;  (check-substring-spec substring s start end)
;  (let* ((slen (- end start))
;         (ans (make-string slen)))
;    (do ((i 0 (+ i 1))
;         (j start (+ j 1)))
;        ((>= i slen) ans)
;      (string-set! ans i (string-ref s j)))))

;;; Basic iterators and other higher-order abstractions
;;; (string-map proc s [start end])
;;; (string-map! proc s [start end])
;;; (string-fold kons knil s [start end])
;;; (string-fold-right kons knil s [start end])
;;; (string-unfold       p f g seed [base make-final])
;;; (string-unfold-right p f g seed [base make-final])
;;; (string-for-each       proc s [start end])
;;; (string-for-each-index proc s [start end])
;;; (string-every char-set/char/pred s [start end])
;;; (string-any   char-set/char/pred s [start end])
;;; (string-tabulate proc len)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; You want compiler support for high-level transforms on fold and unfold ops.
;;; You'd at least like a lot of inlining for clients of these procedures.
;;; Don't hold your breath.

(define (string-map proc s . maybe-start+end)
  (check-arg procedure? proc string-map)
  (let-string-start+end (start end) string-map s maybe-start+end
    (%string-map proc s start end)))

(define (%string-map proc s start end)	; Internal utility
  (let* ((len (- end start))
	 (ans (make-string len)))
    (do ((i (- end 1) (- i 1))
	 (j (- len 1) (- j 1)))
	((< j 0))
      (string-set! ans j (proc (string-ref s i))))
    ans))

(define (string-map! proc s . maybe-start+end)
  (check-arg procedure? proc string-map!)
  (let-string-start+end (start end) string-map! s maybe-start+end
    (%string-map! proc s start end)))

(define (%string-map! proc s start end)
  (do ((i (- end 1) (- i 1)))
      ((< i start))
    (string-set! s i (proc (string-ref s i)))))

(define (string-fold kons knil s . maybe-start+end)
  (check-arg procedure? kons string-fold)
  (let-string-start+end (start end) string-fold s maybe-start+end
    (let lp ((v knil) (i start))
      (if (< i end) (lp (kons (string-ref s i) v) (+ i 1))
	  v))))

(define (string-fold-right kons knil s . maybe-start+end)
  (check-arg procedure? kons string-fold-right)
  (let-string-start+end (start end) string-fold-right s maybe-start+end
    (let lp ((v knil) (i (- end 1)))
      (if (>= i start) (lp (kons (string-ref s i) v) (- i 1))
	  v))))

;;; (string-unfold p f g seed [base make-final])
;;; This is the fundamental constructor for strings. 
;;; - G is used to generate a series of "seed" values from the initial seed:
;;;     SEED, (G SEED), (G^2 SEED), (G^3 SEED), ...
;;; - P tells us when to stop -- when it returns true when applied to one 
;;;   of these seed values.
;;; - F maps each seed value to the corresponding character 
;;;   in the result string. These chars are assembled into the
;;;   string in a left-to-right order.
;;; - BASE is the optional initial/leftmost portion of the constructed string;
;;;   it defaults to the empty string "".
;;; - MAKE-FINAL is applied to the terminal seed value (on which P returns
;;;   true) to produce the final/rightmost portion of the constructed string.
;;;   It defaults to (LAMBDA (X) "").
;;;
;;; In other words, the following (simple, inefficient) definition holds:
;;; (define (string-unfold p f g seed base make-final)
;;;   (string-append base
;;;                  (let recur ((seed seed))
;;;                    (if (p seed) (make-final seed)
;;;                        (string-append (string (f seed))
;;;                                       (recur (g seed)))))))
;;; 
;;; STRING-UNFOLD is a fairly powerful constructor -- you can use it to
;;; reverse a string, copy a string, convert a list to a string, read
;;; a port into a string, and so forth. Examples:
;;; (port->string port) =
;;;   (string-unfold (compose eof-object? peek-char)
;;;                  read-char values port)
;;;
;;; (list->string lis) = (string-unfold null? car cdr lis)
;;; 
;;; (tabulate-string f size) = (string-unfold (lambda (i) (= i size)) f add1 0)

;;; A problem with the following simple formulation is that it pushes one
;;; stack frame for every char in the result string -- an issue if you are
;;; using it to read a 100kchar string. So we don't use it -- but I include
;;; it to give a clear, straightforward description of what the function
;;; does.

;(define (string-unfold p f g seed base make-final)
;  (let ((ans (let recur ((seed seed) (i (string-length base)))
;               (if (p seed)
;                   (let* ((final (make-final seed))
;                          (ans (make-string (+ i (string-length final)))))
;                     (string-copy! ans i final)
;                     ans)
;
;                   (let* ((c (f seed))
;                          (s (recur (g seed) (+ i 1))))
;                     (string-set! s i c)
;                     s)))))
;    (string-copy! ans 0 base)
;    ans))

;;; The strategy is to allocate a series of chunks into which we stash the
;;; chars as we generate them. Chunk size goes up in powers of two starting
;;; with 40 and levelling out at 4k, i.e.
;;;     40 40 80 160 320 640 1280 2560 4096 4096 4096 4096 4096...
;;; This should work pretty well for short strings, 1-line (80 char) strings,
;;; and longer ones. When done, we allocate an answer string and copy the
;;; chars over from the chunk buffers.

(define (string-unfold p f g seed . base+make-final)
  (check-arg procedure? p string-unfold)
  (check-arg procedure? f string-unfold)
  (check-arg procedure? g string-unfold)
  (let-optionals* base+make-final
                  ((base       ""              (string? base))
		   (make-final (lambda (x) "") (procedure? make-final)))
    (let lp ((chunks '())		; Previously filled chunks
	     (nchars 0)			; Number of chars in CHUNKS
	     (chunk (make-string 40))	; Current chunk into which we write
	     (chunk-len 40)
	     (i 0)			; Number of chars written into CHUNK
	     (seed seed))
      (let lp2 ((i i) (seed seed))
	(if (not (p seed))
	    (let ((c (f seed))
		  (seed (g seed)))
	      (if (< i chunk-len)
		  (begin (string-set! chunk i c)
			 (lp2 (+ i 1) seed))

		  (let* ((nchars2 (+ chunk-len nchars))
			 (chunk-len2 (min 4096 nchars2))
			 (new-chunk (make-string chunk-len2)))
		    (string-set! new-chunk 0 c)
		    (lp (cons chunk chunks) (+ nchars chunk-len)
			new-chunk chunk-len2 1 seed))))

	    ;; We're done. Make the answer string & install the bits.
	    (let* ((final (make-final seed))
		   (flen (string-length final))
		   (base-len (string-length base))
		   (j (+ base-len nchars i))
		   (ans (make-string (+ j flen))))
	      (%string-copy! ans j final 0 flen)	; Install FINAL.
	      (let ((j (- j i)))
		(%string-copy! ans j chunk 0 i)		; Install CHUNK[0,I).
		(let lp ((j j) (chunks chunks))		; Install CHUNKS.
		  (if (pair? chunks)
		      (let* ((chunk  (car chunks))
			     (chunks (cdr chunks))
			     (chunk-len (string-length chunk))
			     (j (- j chunk-len)))
			(%string-copy! ans j chunk 0 chunk-len)
			(lp j chunks)))))
	      (%string-copy! ans 0 base 0 base-len)	; Install BASE.
	      ans))))))

(define (string-unfold-right p f g seed . base+make-final)
  (let-optionals* base+make-final
                  ((base       ""              (string? base))
		   (make-final (lambda (x) "") (procedure? make-final)))
    (let lp ((chunks '())		; Previously filled chunks
	     (nchars 0)			; Number of chars in CHUNKS
	     (chunk (make-string 40))	; Current chunk into which we write
	     (chunk-len 40)
	     (i 40)			; Number of chars available in CHUNK
	     (seed seed))
      (let lp2 ((i i) (seed seed))	; Fill up CHUNK from right
	(if (not (p seed))		; to left.
	    (let ((c (f seed))
		  (seed (g seed)))
	      (if (> i 0)
		  (let ((i (- i 1)))
		    (string-set! chunk i c)
		    (lp2 i seed))

		  (let* ((nchars2 (+ chunk-len nchars))
			 (chunk-len2 (min 4096 nchars2))
			 (new-chunk (make-string chunk-len2))
			 (i (- chunk-len2 1)))
		    (string-set! new-chunk i c)
		    (lp (cons chunk chunks) (+ nchars chunk-len)
			new-chunk chunk-len2 i seed))))

	    ;; We're done. Make the answer string & install the bits.
	    (let* ((final (make-final seed))
		   (flen (string-length final))
		   (base-len (string-length base))
		   (chunk-used (- chunk-len i))
		   (j (+ base-len nchars chunk-used))
		   (ans (make-string (+ j flen))))
	      (%string-copy! ans 0 final 0 flen)	; Install FINAL.
	      (%string-copy! ans flen chunk i chunk-len); Install CHUNK[I,).
	      (let lp ((j (+ flen chunk-used))		; Install CHUNKS.
		       (chunks chunks))		
		  (if (pair? chunks)
		      (let* ((chunk  (car chunks))
			     (chunks (cdr chunks))
			     (chunk-len (string-length chunk)))
			(%string-copy! ans j chunk 0 chunk-len)
			(lp (+ j chunk-len) chunks))
		      (%string-copy! ans j base 0 base-len))); Install BASE.
	      ans))))))


(define (string-for-each proc s . maybe-start+end)
  (check-arg procedure? proc string-for-each)
  (let-string-start+end (start end) string-for-each s maybe-start+end
    (let lp ((i start))
      (if (< i end)
	  (begin (proc (string-ref s i)) 
		 (lp (+ i 1)))))))

(define (string-for-each-index proc s . maybe-start+end)
  (check-arg procedure? proc string-for-each-index)
  (let-string-start+end (start end) string-for-each-index s maybe-start+end
    (let lp ((i start))
      (if (< i end) (begin (proc i) (lp (+ i 1)))))))

(define (string-every criterion s . maybe-start+end)
  (let-string-start+end (start end) string-every s maybe-start+end
    (cond ((char? criterion)
	   (let lp ((i start))
	     (or (>= i end)
		 (and (char=? criterion (string-ref s i))
		      (lp (+ i 1))))))

	  ((char-set? criterion)
	   (let lp ((i start))
	     (or (>= i end)
		 (and (char-set-contains? criterion (string-ref s i))
		      (lp (+ i 1))))))

	  ((procedure? criterion)		; Slightly funky loop so that
	   (or (= start end)			; final (PRED S[END-1]) call
	       (let lp ((i start))		; is a tail call.
		 (let ((c (string-ref s i))
		       (i1 (+ i 1)))
		   (if (= i1 end) (criterion c)	; Tail call.
		       (and (criterion c) (lp i1)))))))

	  (else (error "Second param is neither char-set, char, or predicate procedure."
		       string-every criterion)))))


(define (string-any criterion s . maybe-start+end)
  (let-string-start+end (start end) string-any s maybe-start+end
    (cond ((char? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (or (char=? criterion (string-ref s i))
		      (lp (+ i 1))))))

	  ((char-set? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (or (char-set-contains? criterion (string-ref s i))
		      (lp (+ i 1))))))

	  ((procedure? criterion)		; Slightly funky loop so that
	   (and (< start end)			; final (PRED S[END-1]) call
		(let lp ((i start))		; is a tail call.
		  (let ((c (string-ref s i))
			(i1 (+ i 1)))
		    (if (= i1 end) (criterion c)	; Tail call
			(or (criterion c) (lp i1)))))))

	  (else (error "Second param is neither char-set, char, or predicate procedure."
		       string-any criterion)))))


(define (string-tabulate proc len)
  (check-arg procedure? proc string-tabulate)
  (check-arg (lambda (val) (and (integer? val) (exact? val) (<= 0 val)))
	     len string-tabulate)
  (let ((s (make-string len)))
    (do ((i (- len 1) (- i 1)))
	((< i 0))
      (string-set! s i (proc i)))
    s))


 
;;; string-prefix-length[-ci] s1 s2 [start1 end1 start2 end2]
;;; string-suffix-length[-ci] s1 s2 [start1 end1 start2 end2]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Find the length of the common prefix/suffix.
;;; It is not required that the two substrings passed be of equal length.
;;; This was microcode in MIT Scheme -- a very tightly bummed primitive.
;;; %STRING-PREFIX-LENGTH is the core routine of all string-comparisons,
;;; so should be as tense as possible.

(define (%string-prefix-length s1 start1 end1 s2 start2 end2)
  (let* ((delta (min (- end1 start1) (- end2 start2)))
	 (end1 (+ start1 delta)))

    (if (and (eq? s1 s2) (= start1 start2))	; EQ fast path
	delta

	(let lp ((i start1) (j start2))		; Regular path
	  (if (or (>= i end1)
		  (not (char=? (string-ref s1 i)
			       (string-ref s2 j))))
	      (- i start1)
	      (lp (+ i 1) (+ j 1)))))))

(define (%string-suffix-length s1 start1 end1 s2 start2 end2)
  (let* ((delta (min (- end1 start1) (- end2 start2)))
	 (start1 (- end1 delta)))

    (if (and (eq? s1 s2) (= end1 end2))		; EQ fast path
	delta

	(let lp ((i (- end1 1)) (j (- end2 1)))	; Regular path
	  (if (or (< i start1)
		  (not (char=? (string-ref s1 i)
			       (string-ref s2 j))))
	      (- (- end1 i) 1)
	      (lp (- i 1) (- j 1)))))))

(define (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)
  (let* ((delta (min (- end1 start1) (- end2 start2)))
	 (end1 (+ start1 delta)))

    (if (and (eq? s1 s2) (= start1 start2))	; EQ fast path
	delta

	(let lp ((i start1) (j start2))		; Regular path
	  (if (or (>= i end1)
		  (not (char-ci=? (string-ref s1 i)
				  (string-ref s2 j))))
	      (- i start1)
	      (lp (+ i 1) (+ j 1)))))))

(define (%string-suffix-length-ci s1 start1 end1 s2 start2 end2)
  (let* ((delta (min (- end1 start1) (- end2 start2)))
	 (start1 (- end1 delta)))

    (if (and (eq? s1 s2) (= end1 end2))		; EQ fast path
	delta

	(let lp ((i (- end1 1)) (j (- end2 1)))	; Regular path
	  (if (or (< i start1)
		  (not (char-ci=? (string-ref s1 i)
				  (string-ref s2 j))))
	      (- (- end1 i) 1)
	      (lp (- i 1) (- j 1)))))))


(define (string-prefix-length s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-prefix-length s1 s2 maybe-starts+ends
    (%string-prefix-length s1 start1 end1 s2 start2 end2)))

(define (string-suffix-length s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-suffix-length s1 s2 maybe-starts+ends
    (%string-suffix-length s1 start1 end1 s2 start2 end2)))

(define (string-prefix-length-ci s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-prefix-length-ci s1 s2 maybe-starts+ends
    (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)))

(define (string-suffix-length-ci s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-suffix-length-ci s1 s2 maybe-starts+ends
    (%string-suffix-length-ci s1 start1 end1 s2 start2 end2)))


;;; string-prefix?    s1 s2 [start1 end1 start2 end2]
;;; string-suffix?    s1 s2 [start1 end1 start2 end2]
;;; string-prefix-ci? s1 s2 [start1 end1 start2 end2]
;;; string-suffix-ci? s1 s2 [start1 end1 start2 end2]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; These are all simple derivatives of the previous counting funs.

(define (string-prefix? s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-prefix? s1 s2 maybe-starts+ends
    (%string-prefix? s1 start1 end1 s2 start2 end2)))

(define (string-suffix? s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-suffix? s1 s2 maybe-starts+ends
    (%string-suffix? s1 start1 end1 s2 start2 end2)))

(define (string-prefix-ci? s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-prefix-ci? s1 s2 maybe-starts+ends
    (%string-prefix-ci? s1 start1 end1 s2 start2 end2)))

(define (string-suffix-ci? s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-suffix-ci? s1 s2 maybe-starts+ends
    (%string-suffix-ci? s1 start1 end1 s2 start2 end2)))


;;; Here are the internal routines that do the real work.

(define (%string-prefix? s1 start1 end1 s2 start2 end2)
  (let ((len1 (- end1 start1)))
    (and (<= len1 (- end2 start2))	; Quick check
	 (= (%string-prefix-length s1 start1 end1
				   s2 start2 end2)
	    len1))))

(define (%string-suffix? s1 start1 end1 s2 start2 end2)
  (let ((len1 (- end1 start1)))
    (and (<= len1 (- end2 start2))	; Quick check
	 (= len1 (%string-suffix-length s1 start1 end1
					s2 start2 end2)))))

(define (%string-prefix-ci? s1 start1 end1 s2 start2 end2)
  (let ((len1 (- end1 start1)))
    (and (<= len1 (- end2 start2))	; Quick check
	 (= len1 (%string-prefix-length-ci s1 start1 end1
					   s2 start2 end2)))))

(define (%string-suffix-ci? s1 start1 end1 s2 start2 end2)
  (let ((len1 (- end1 start1)))
    (and (<= len1 (- end2 start2))	; Quick check
	 (= len1 (%string-suffix-length-ci s1 start1 end1
					   s2 start2 end2)))))

 
;;; string-compare    s1 s2 proc< proc= proc> [start1 end1 start2 end2]
;;; string-compare-ci s1 s2 proc< proc= proc> [start1 end1 start2 end2]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Primitive string-comparison functions.
;;; Continuation order is different from MIT Scheme.
;;; Continuations are applied to s1's mismatch index;
;;; in the case of equality, this is END1.

(define (%string-compare s1 start1 end1 s2 start2 end2
			   proc< proc= proc>)
  (let ((size1 (- end1 start1))
	(size2 (- end2 start2)))
    (let ((match (%string-prefix-length s1 start1 end1 s2 start2 end2)))
      (if (= match size1)
	  ((if (= match size2) proc= proc<) end1)
	  ((if (= match size2)
	       proc>
	       (if (char<? (string-ref s1 (+ start1 match))
			   (string-ref s2 (+ start2 match)))
		   proc< proc>))
	   (+ match start1))))))

(define (%string-compare-ci s1 start1 end1 s2 start2 end2
			      proc< proc= proc>)
  (let ((size1 (- end1 start1))
	(size2 (- end2 start2)))
    (let ((match (%string-prefix-length-ci s1 start1 end1 s2 start2 end2)))
      (if (= match size1)
	  ((if (= match size2) proc= proc<) end1)
	  ((if (= match size2) proc>
	       (if (char-ci<? (string-ref s1 (+ start1 match))
			      (string-ref s2 (+ start2 match)))
		   proc< proc>))
	   (+ start1 match))))))

(define (string-compare s1 s2 proc< proc= proc> . maybe-starts+ends)
  (check-arg procedure? proc< string-compare)
  (check-arg procedure? proc= string-compare)
  (check-arg procedure? proc> string-compare)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-compare s1 s2 maybe-starts+ends
    (%string-compare s1 start1 end1 s2 start2 end2 proc< proc= proc>)))

(define (string-compare-ci s1 s2 proc< proc= proc> . maybe-starts+ends)
  (check-arg procedure? proc< string-compare-ci)
  (check-arg procedure? proc= string-compare-ci)
  (check-arg procedure? proc> string-compare-ci)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-compare-ci s1 s2 maybe-starts+ends
    (%string-compare-ci s1 start1 end1 s2 start2 end2 proc< proc= proc>)))



;;; string=          string<>		string-ci=          string-ci<>
;;; string<          string>		string-ci<          string-ci>
;;; string<=         string>=		string-ci<=         string-ci>=
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Simple definitions in terms of the previous comparison funs.
;;; I sure hope the %STRING-COMPARE calls get integrated.

(define (string= s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string= s1 s2 maybe-starts+ends
    (and (= (- end1 start1) (- end2 start2))			; Quick filter
	 (or (and (eq? s1 s2) (= start1 start2))		; Fast path
	     (%string-compare s1 start1 end1 s2 start2 end2	; Real test
			      (lambda (i) #f)
			      values
			      (lambda (i) #f))))))

(define (string<> s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string<> s1 s2 maybe-starts+ends
    (or (not (= (- end1 start1) (- end2 start2)))		; Fast path
	(and (not (and (eq? s1 s2) (= start1 start2)))		; Quick filter
	     (%string-compare s1 start1 end1 s2 start2 end2	; Real test
			      values
			      (lambda (i) #f)
			      values)))))

(define (string< s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string< s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(< end1 end2)

	(%string-compare s1 start1 end1 s2 start2 end2 		; Real test
			 values
			 (lambda (i) #f)
			 (lambda (i) #f)))))

(define (string> s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string> s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(> end1 end2)

	(%string-compare s1 start1 end1 s2 start2 end2 		; Real test
			 (lambda (i) #f)
			 (lambda (i) #f)
			 values))))

(define (string<= s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string<= s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(<= end1 end2)

	(%string-compare s1 start1 end1 s2 start2 end2 		; Real test
			 values
			 values
			 (lambda (i) #f)))))

(define (string>= s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string>= s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(>= end1 end2)

	(%string-compare s1 start1 end1 s2 start2 end2 		; Real test
			 (lambda (i) #f)
			 values
			 values))))

(define (string-ci= s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-ci= s1 s2 maybe-starts+ends
    (and (= (- end1 start1) (- end2 start2))			; Quick filter
	 (or (and (eq? s1 s2) (= start1 start2))		; Fast path
	     (%string-compare-ci s1 start1 end1 s2 start2 end2	; Real test
				 (lambda (i) #f)
				 values
				 (lambda (i) #f))))))

(define (string-ci<> s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-ci<> s1 s2 maybe-starts+ends
    (or (not (= (- end1 start1) (- end2 start2)))		; Fast path
	(and (not (and (eq? s1 s2) (= start1 start2)))		; Quick filter
	     (%string-compare-ci s1 start1 end1 s2 start2 end2	; Real test
				 values
				 (lambda (i) #f)
				 values)))))

(define (string-ci< s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-ci< s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(< end1 end2)

	(%string-compare-ci s1 start1 end1 s2 start2 end2	; Real test
			    values
			    (lambda (i) #f)
			    (lambda (i) #f)))))

(define (string-ci> s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-ci> s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(> end1 end2)

	(%string-compare-ci s1 start1 end1 s2 start2 end2	; Real test
			    (lambda (i) #f)
			    (lambda (i) #f)
			    values))))

(define (string-ci<= s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-ci<= s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(<= end1 end2)

	(%string-compare-ci s1 start1 end1 s2 start2 end2	; Real test
			    values
			    values
			    (lambda (i) #f)))))

(define (string-ci>= s1 s2 . maybe-starts+ends)
  (let-string-start+end2 (start1 end1 start2 end2) 
			 string-ci>= s1 s2 maybe-starts+ends
    (if (and (eq? s1 s2) (= start1 start2))			; Fast path
	(>= end1 end2)

	(%string-compare-ci s1 start1 end1 s2 start2 end2	; Real test
			    (lambda (i) #f)
			    values
			    values))))

 
;;; Hash
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Compute (c + 37 c + 37^2 c + ...) modulo BOUND, with sleaze thrown in
;;; to keep the intermediate values small. (We do the calculation with just
;;; enough bits to represent BOUND, masking off high bits at each step in
;;; calculation. If this screws up any important properties of the hash
;;; function I'd like to hear about it. -Olin)
;;;
;;; If you keep BOUND small enough, the intermediate calculations will 
;;; always be fixnums. How small is dependent on the underlying Scheme system; 
;;; we use a default BOUND of 2^22 = 4194304, which should hack it in
;;; Schemes that give you at least 29 signed bits for fixnums. The core 
;;; calculation that you don't want to overflow is, worst case,
;;;     (+ 65535 (* 37 (- bound 1)))
;;; where 65535 is the max character code. Choose the default BOUND to be the
;;; biggest power of two that won't cause this expression to fixnum overflow, 
;;; and everything will be copacetic.

(define (%string-hash s char->int bound start end)
  (let ((iref (lambda (s i) (char->int (string-ref s i))))
	;; Compute a 111...1 mask that will cover BOUND-1:
	(mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh?
		(if (>= i bound) (- i 1) (lp (+ i i))))))
    (let lp ((i start) (ans 0))
      (if (>= i end) (modulo ans bound)
	  (lp (+ i 1) (bitwise-and mask (+ (* 37 ans) (iref s i))))))))

(define (string-hash s . maybe-bound+start+end)
  (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound)
							     (exact? bound)
							     (<= 0 bound)))
					 rest)
    (let ((bound (if (zero? bound) 4194304 bound)))	; 0 means default.
      (let-string-start+end (start end) string-hash s rest
        (%string-hash s char->integer bound start end)))))

(define (string-hash-ci s . maybe-bound+start+end)
  (let-optionals* maybe-bound+start+end ((bound 4194304 (and (integer? bound)
							     (exact? bound)
							     (<= 0 bound)))
					 rest)
    (let ((bound (if (zero? bound) 4194304 bound)))	; 0 means default.
      (let-string-start+end (start end) string-hash-ci s rest
        (%string-hash s (lambda (c) (char->integer (char-downcase c)))
		      bound start end)))))

;;; Case hacking
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-upcase  s [start end]
;;; string-upcase! s [start end]
;;; string-downcase  s [start end]
;;; string-downcase! s [start end]
;;;
;;; string-titlecase  s [start end]
;;; string-titlecase! s [start end]
;;;   Capitalize every contiguous alpha sequence: capitalise
;;;   first char, lowercase rest.

(define (string-upcase  s . maybe-start+end)
  (let-string-start+end (start end) string-upcase s maybe-start+end
    (%string-map char-upcase s start end)))

(define (string-upcase! s . maybe-start+end)
  (let-string-start+end (start end) string-upcase! s maybe-start+end
    (%string-map! char-upcase s start end)))

(define (string-downcase  s . maybe-start+end)
  (let-string-start+end (start end) string-downcase s maybe-start+end
    (%string-map char-downcase s start end)))

(define (string-downcase! s . maybe-start+end)
  (let-string-start+end (start end) string-downcase! s maybe-start+end
    (%string-map! char-downcase s start end)))

(define (%string-titlecase! s start end)
  (let lp ((i start))
    (cond ((string-index s char-cased? i end) =>
           (lambda (i)
	     (string-set! s i (char-titlecase (string-ref s i)))
	     (let ((i1 (+ i 1)))
	       (cond ((string-skip s char-cased? i1 end) =>
		      (lambda (j)
			(string-downcase! s i1 j)
			(lp (+ j 1))))
		     (else (string-downcase! s i1 end)))))))))

(define (string-titlecase! s . maybe-start+end)
  (let-string-start+end (start end) string-titlecase! s maybe-start+end
    (%string-titlecase! s start end)))

(define (string-titlecase s . maybe-start+end)
  (let-string-start+end (start end) string-titlecase! s maybe-start+end
    (let ((ans (substring s start end)))
      (%string-titlecase! ans 0 (- end start))
      ans)))

 
;;; Cutting & pasting strings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-take string nchars
;;; string-drop string nchars
;;;
;;; string-take-right string nchars
;;; string-drop-right string nchars
;;;
;;; string-pad string k [char start end] 
;;; string-pad-right string k [char start end] 
;;; 
;;; string-trim       string [char/char-set/pred start end] 
;;; string-trim-right string [char/char-set/pred start end] 
;;; string-trim-both  string [char/char-set/pred start end] 
;;;
;;; These trimmers invert the char-set meaning from MIT Scheme -- you
;;; say what you want to trim.

(define (string-take s n)
  (check-arg string? s string-take)
  (check-arg (lambda (val) (and (integer? n) (exact? n)
				(<= 0 n (string-length s))))
	     n string-take)
  (%substring/shared s 0 n))

(define (string-take-right s n)
  (check-arg string? s string-take-right)
  (let ((len (string-length s)))
    (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
	       n string-take-right)
    (%substring/shared s (- len n) len)))

(define (string-drop s n)
  (check-arg string? s string-drop)
  (let ((len (string-length s)))
    (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
	       n string-drop)
  (%substring/shared s n len)))

(define (string-drop-right s n)
  (check-arg string? s string-drop-right)
  (let ((len (string-length s)))
    (check-arg (lambda (val) (and (integer? n) (exact? n) (<= 0 n len)))
	       n string-drop-right)
    (%substring/shared s 0 (- len n))))


(define (string-trim s . criterion+start+end)
  (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
    (let-string-start+end (start end) string-trim s rest
      (cond ((string-skip s criterion start end) =>
	     (lambda (i) (%substring/shared s i end)))
	    (else "")))))

(define (string-trim-right s . criterion+start+end)
  (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
    (let-string-start+end (start end) string-trim-right s rest
      (cond ((string-skip-right s criterion start end) =>
	     (lambda (i) (%substring/shared s 0 (+ 1 i))))
	    (else "")))))

(define (string-trim-both s . criterion+start+end)
  (let-optionals* criterion+start+end ((criterion char-set:whitespace) rest)
    (let-string-start+end (start end) string-trim-both s rest
      (cond ((string-skip s criterion start end) =>
	     (lambda (i)
	       (%substring/shared s i (+ 1 (string-skip-right s criterion i end)))))
	    (else "")))))


(define (string-pad-right s n . char+start+end)
  (let-optionals* char+start+end ((char #\space (char? char)) rest)
    (let-string-start+end (start end) string-pad-right s rest
      (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n)))
		 n string-pad-right)
      (let ((len (- end start)))
	(if (<= n len)
	    (%substring/shared s start (+ start n))
	    (let ((ans (make-string n char)))
	      (%string-copy! ans 0 s start end)
	      ans))))))

(define (string-pad s n . char+start+end)
  (let-optionals* char+start+end ((char #\space (char? char)) rest)
    (let-string-start+end (start end) string-pad s rest
      (check-arg (lambda (n) (and (integer? n) (exact? n) (<= 0 n)))
		 n string-pad)
      (let ((len (- end start)))
	(if (<= n len)
	    (%substring/shared s (- end n) end)
	    (let ((ans (make-string n char)))
	      (%string-copy! ans (- n len) s start end)
	      ans))))))


 
;;; Filtering strings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-delete char/char-set/pred string [start end]
;;; string-filter char/char-set/pred string [start end]
;;;
;;; If the criterion is a char or char-set, we scan the string twice with
;;;   string-fold -- once to determine the length of the result string, 
;;;   and once to do the filtered copy.
;;; If the criterion is a predicate, we don't do this double-scan strategy, 
;;;   because the predicate might have side-effects or be very expensive to
;;;   compute. So we preallocate a temp buffer pessimistically, and only do
;;;   one scan over S. This is likely to be faster and more space-efficient
;;;   than consing a list.

(define (string-delete criterion s . maybe-start+end)
  (let-string-start+end (start end) string-delete s maybe-start+end
    (if (procedure? criterion)
	(let* ((slen (- end start))
	       (temp (make-string slen))
	       (ans-len (string-fold (lambda (c i)
				       (if (criterion c) i
					   (begin (string-set! temp i c)
						  (+ i 1))))
				     0 s start end)))
	  (if (= ans-len slen) temp (substring temp 0 ans-len)))

	(let* ((cset (cond ((char-set? criterion) criterion)
			   ((char? criterion) (char-set criterion))
			   (else (error "string-delete criterion not predicate, char or char-set" criterion))))
	       (len (string-fold (lambda (c i) (if (char-set-contains? cset c)
						   i
						   (+ i 1)))
				 0 s start end))
	       (ans (make-string len)))
	  (string-fold (lambda (c i) (if (char-set-contains? cset c)
					 i
					 (begin (string-set! ans i c)
						(+ i 1))))
		       0 s start end)
	  ans))))

(define (string-filter criterion s . maybe-start+end)
  (let-string-start+end (start end) string-filter s maybe-start+end
    (if (procedure? criterion)
	(let* ((slen (- end start))
	       (temp (make-string slen))
	       (ans-len (string-fold (lambda (c i)
				       (if (criterion c)
					   (begin (string-set! temp i c)
						  (+ i 1))
					   i))
				     0 s start end)))
	  (if (= ans-len slen) temp (substring temp 0 ans-len)))

	(let* ((cset (cond ((char-set? criterion) criterion)
			   ((char? criterion) (char-set criterion))
			   (else (error "string-delete criterion not predicate, char or char-set" criterion))))

	       (len (string-fold (lambda (c i) (if (char-set-contains? cset c)
						   (+ i 1)
						   i))
				 0 s start end))
	       (ans (make-string len)))
	  (string-fold (lambda (c i) (if (char-set-contains? cset c)
					 (begin (string-set! ans i c)
						(+ i 1))
					 i))
		       0 s start end)
	  ans))))

 
;;; String search
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-index       string char/char-set/pred [start end]
;;; string-index-right string char/char-set/pred [start end]
;;; string-skip        string char/char-set/pred [start end]
;;; string-skip-right  string char/char-set/pred [start end]
;;; string-count       string char/char-set/pred [start end]
;;;     There's a lot of replicated code here for efficiency.
;;;     For example, the char/char-set/pred discrimination has
;;;     been lifted above the inner loop of each proc.

(define (string-index str criterion . maybe-start+end)
  (let-string-start+end (start end) string-index str maybe-start+end
    (cond ((char? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (if (char=? criterion (string-ref str i)) i
		      (lp (+ i 1))))))
	  ((char-set? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (if (char-set-contains? criterion (string-ref str i)) i
		      (lp (+ i 1))))))
	  ((procedure? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (if (criterion (string-ref str i)) i
		      (lp (+ i 1))))))
	  (else (error "Second param is neither char-set, char, or predicate procedure."
		       string-index criterion)))))

(define (string-index-right str criterion . maybe-start+end)
  (let-string-start+end (start end) string-index-right str maybe-start+end
    (cond ((char? criterion)
	   (let lp ((i (- end 1)))
	     (and (>= i start)
		  (if (char=? criterion (string-ref str i)) i
		      (lp (- i 1))))))
	  ((char-set? criterion)
	   (let lp ((i (- end 1)))
	     (and (>= i start)
		  (if (char-set-contains? criterion (string-ref str i)) i
		      (lp (- i 1))))))
	  ((procedure? criterion)
	   (let lp ((i (- end 1)))
	     (and (>= i start)
		  (if (criterion (string-ref str i)) i
		      (lp (- i 1))))))
	  (else (error "Second param is neither char-set, char, or predicate procedure."
		       string-index-right criterion)))))

(define (string-skip str criterion . maybe-start+end)
  (let-string-start+end (start end) string-skip str maybe-start+end
    (cond ((char? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (if (char=? criterion (string-ref str i))
		      (lp (+ i 1))
		      i))))
	  ((char-set? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (if (char-set-contains? criterion (string-ref str i))
		      (lp (+ i 1))
		      i))))
	  ((procedure? criterion)
	   (let lp ((i start))
	     (and (< i end)
		  (if (criterion (string-ref str i)) (lp (+ i 1))
		      i))))
	  (else (error "Second param is neither char-set, char, or predicate procedure."
		       string-skip criterion)))))

(define (string-skip-right str criterion . maybe-start+end)
  (let-string-start+end (start end) string-skip-right str maybe-start+end
    (cond ((char? criterion)
	   (let lp ((i (- end 1)))
	     (and (>= i start)
		  (if (char=? criterion (string-ref str i))
		      (lp (- i 1))
		      i))))
	  ((char-set? criterion)
	   (let lp ((i (- end 1)))
	     (and (>= i start)
		  (if (char-set-contains? criterion (string-ref str i))
		      (lp (- i 1))
		      i))))
	  ((procedure? criterion)
	   (let lp ((i (- end 1)))
	     (and (>= i start)
		  (if (criterion (string-ref str i)) (lp (- i 1))
		      i))))
	  (else (error "CRITERION param is neither char-set or char."
		       string-skip-right criterion)))))


(define (string-count s criterion . maybe-start+end)
  (let-string-start+end (start end) string-count s maybe-start+end
    (cond ((char? criterion)
	   (do ((i start (+ i 1))
		(count 0 (if (char=? criterion (string-ref s i))
			     (+ count 1)
			     count)))
	       ((>= i end) count)))

	  ((char-set? criterion)
	   (do ((i start (+ i 1))
		(count 0 (if (char-set-contains? criterion (string-ref s i))
			     (+ count 1)
			     count)))
	       ((>= i end) count)))

	  ((procedure? criterion)
	   (do ((i start (+ i 1))
		(count 0 (if (criterion (string-ref s i)) (+ count 1) count)))
	       ((>= i end) count)))

	  (else (error "CRITERION param is neither char-set or char."
		       string-count criterion)))))


 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; string-fill! string char [start end]
;;; 
;;; string-copy! to tstart from [fstart fend]
;;; 	Guaranteed to work, even if s1 eq s2.

(define (string-fill! s char . maybe-start+end)
  (check-arg char? char string-fill!)
  (let-string-start+end (start end) string-fill! s maybe-start+end
    (do ((i (- end 1) (- i 1)))
	((< i start))
      (string-set! s i char))))

(define (string-copy! to tstart from . maybe-fstart+fend)
  (let-string-start+end (fstart fend) string-copy! from maybe-fstart+fend
    (check-arg integer? tstart string-copy!)
    (check-substring-spec string-copy! to tstart (+ tstart (- fend fstart)))
    (%string-copy! to tstart from fstart fend)))

;;; Library-internal routine
(define (%string-copy! to tstart from fstart fend)
  (if (> fstart tstart)
      (do ((i fstart (+ i 1))
	   (j tstart (+ j 1)))
	  ((>= i fend))
	(string-set! to j (string-ref from i)))

      (do ((i (- fend 1)                    (- i 1))
	   (j (+ -1 tstart (- fend fstart)) (- j 1)))
	  ((< i fstart))
	(string-set! to j (string-ref from i)))))


 
;;; Returns starting-position in STRING or #f if not true.
;;; This implementation is slow & simple. It is useful as a "spec" or for
;;; comparison testing with fancier implementations.
;;; See below for fast KMP version.

;(define (string-contains string substring . maybe-starts+ends)
;  (let-string-start+end2 (start1 end1 start2 end2) 
;                         string-contains string substring maybe-starts+ends
;    (let* ((len (- end2 start2))
;	   (i-bound (- end1 len)))
;      (let lp ((i start1))
;	(and (< i i-bound)
;	     (if (string= string substring i (+ i len) start2 end2)
;		 i
;		 (lp (+ i 1))))))))


;;; Searching for an occurrence of a substring
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (string-contains text pattern . maybe-starts+ends)
  (let-string-start+end2 (t-start t-end p-start p-end)
                         string-contains text pattern maybe-starts+ends
    (%kmp-search pattern text char=? p-start p-end t-start t-end)))

(define (string-contains-ci text pattern . maybe-starts+ends)
  (let-string-start+end2 (t-start t-end p-start p-end)
                         string-contains-ci text pattern maybe-starts+ends
    (%kmp-search pattern text char-ci=? p-start p-end t-start t-end)))


;;; Knuth-Morris-Pratt string searching
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; See
;;;     "Fast pattern matching in strings"
;;;     SIAM J. Computing 6(2):323-350 1977
;;;     D. E. Knuth, J. H. Morris and V. R. Pratt
;;; also described in
;;;     "Pattern matching in strings"
;;;     Alfred V. Aho
;;;     Formal Language Theory - Perspectives and Open Problems
;;;     Ronald V. Brook (editor)
;;; This algorithm is O(m + n) where m and n are the 
;;; lengths of the pattern and string respectively

;;; KMP search source[start,end) for PATTERN. Return starting index of
;;; leftmost match or #f.

(define (%kmp-search pattern text c= p-start p-end t-start t-end)
  (let ((plen (- p-end p-start))
	(rv (make-kmp-restart-vector pattern c= p-start p-end)))

    ;; The search loop. TJ & PJ are redundant state.
    (let lp ((ti t-start) (pi 0)
	     (tj (- t-end t-start)) ; (- tlen ti) -- how many chars left.
	     (pj plen))		 ; (- plen pi) -- how many chars left.

      (if (= pi plen)
	  (- ti plen)			; Win.
	  (and (<= pj tj)		; Lose.
	       (if (c= (string-ref text ti) ; Search.
		       (string-ref pattern (+ p-start pi)))
		   (lp (+ 1 ti) (+ 1 pi) (- tj 1) (- pj 1)) ; Advance.
		   
		   (let ((pi (vector-ref rv pi))) ; Retreat.
		     (if (= pi -1)
			 (lp (+ ti 1) 0  (- tj 1) plen) ; Punt.
			 (lp ti       pi tj       (- plen pi))))))))))

;;; (make-kmp-restart-vector pattern [c= start end]) -> integer-vector
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Compute the KMP restart vector RV for string PATTERN.  If
;;; we have matched chars 0..i-1 of PATTERN against a search string S, and
;;; PATTERN[i] doesn't match S[k], then reset i := RV[i], and try again to
;;; match S[k].  If RV[i] = -1, then punt S[k] completely, and move on to
;;; S[k+1] and PATTERN[0] -- no possible match of PAT[0..i] contains S[k].
;;;
;;; In other words, if you have matched the first i chars of PATTERN, but
;;; the i+1'th char doesn't match, RV[i] tells you what the next-longest
;;; prefix of PATTERN is that you have matched.
;;;
;;; - C= (default CHAR=?) is used to compare characters for equality.
;;;   Pass in CHAR-CI=? for case-folded string search.
;;;
;;; - START & END restrict the pattern to the indicated substring; the
;;;   returned vector will be of length END - START. The numbers stored
;;;   in the vector will be values in the range [0,END-START) -- that is,
;;;   they are valid indices into the restart vector; you have to add START
;;;   to them to use them as indices into PATTERN.
;;;
;;; I've split this out as a separate function in case other constant-string
;;; searchers might want to use it.
;;;
;;; E.g.:
;;;    a b d  a b x
;;; #(-1 0 0 -1 1 2)

(define (make-kmp-restart-vector pattern . maybe-c=+start+end)
  (let-optionals* maybe-c=+start+end
                  ((c= char=? (procedure? c=))
		   ((start end) (lambda (args)
				  (string-parse-start+end make-kmp-restart-vector
							  pattern args))))
    (let* ((rvlen (- end start))
	   (rv (make-vector rvlen -1)))
      (if (> rvlen 0)
	  (let ((rvlen-1 (- rvlen 1))
		(c0 (string-ref pattern start)))

	    ;; Here's the main loop. We have set rv[0] ... rv[i].
	    ;; K = I + START -- it is the corresponding index into PATTERN.
	    (let lp1 ((i 0) (j -1) (k start))	
	      (if (< i rvlen-1)
		  ;; lp2 invariant:
		  ;;   pat[(k-j) .. k-1] matches pat[start .. start+j-1]
		  ;;   or j = -1.
		  (let lp2 ((j j))
		    (cond ((= j -1)
			   (let ((i1 (+ 1 i)))
			     (if (not (c= (string-ref pattern (+ k 1)) c0))
				 (vector-set! rv i1 0))
			     (lp1 i1 0 (+ k 1))))
			  ;; pat[(k-j) .. k] matches pat[start..start+j].
			  ((c= (string-ref pattern k) (string-ref pattern (+ j start)))
			   (let* ((i1 (+ 1 i))
				  (j1 (+ 1 j)))
			     (vector-set! rv i1 j1)
			     (lp1 i1 j1 (+ k 1))))

			  (else (lp2 (vector-ref rv j)))))))))
      rv)))


;;; We've matched I chars from PAT. C is the next char from the search string.
;;; Return the new I after handling C. 
;;;
;;; The pattern is (VECTOR-LENGTH RV) chars long, beginning at index PAT-START
;;; in PAT (PAT-START is usually 0). The I chars of the pattern we've matched
;;; are 
;;;     PAT[PAT-START .. PAT-START + I].
;;;
;;; It's *not* an oversight that there is no friendly error checking or
;;; defaulting of arguments. This is a low-level, inner-loop procedure
;;; that we want integrated/inlined into the point of call.

(define (kmp-step pat rv c i c= p-start)
  (let lp ((i i))
    (if (c= c (string-ref pat (+ i p-start)))	; Match =>
	(+ i 1)					;   Done.
	(let ((i (vector-ref rv i)))		; Back up in PAT.
	  (if (= i -1) 0			; Can't back up further.
	      (lp i))))))			; Keep trying for match.

;;; Zip through S[start,end), looking for a match of PAT. Assume we've
;;; already matched the first I chars of PAT when we commence at S[start].
;;; - <0:  If we find a match *ending* at index J, return -J.
;;; - >=0: If we get to the end of the S[start,end) span without finding
;;;   a complete match, return the number of chars from PAT we'd matched
;;;   when we ran off the end.
;;;
;;; This is useful for searching *across* buffers -- that is, when your
;;; input comes in chunks of text. We hand-integrate the KMP-STEP loop
;;; for speed.

(define (string-kmp-partial-search pat rv s i . c=+p-start+s-start+s-end)
  (check-arg vector? rv string-kmp-partial-search)
  (let-optionals* c=+p-start+s-start+s-end
		  ((c=      char=? (procedure? c=))
		   (p-start 0 (and (integer? p-start) (exact? p-start) (<= 0 p-start)))
		   ((s-start s-end) (lambda (args)
				      (string-parse-start+end string-kmp-partial-search
							      s args))))
    (let ((patlen (vector-length rv)))
      (check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i) (< i patlen)))
		 i string-kmp-partial-search)

      ;; Enough prelude. Here's the actual code.
      (let lp ((si s-start)		; An index into S.
	       (vi i))			; An index into RV.
	(cond ((= vi patlen) (- si))	; Win.
	      ((= si s-end) vi)		; Ran off the end.
	      (else			; Match s[si] & loop.
	       (let ((c (string-ref s si)))
		 (lp (+ si 1)	
		     (let lp2 ((vi vi))	; This is just KMP-STEP.
		       (if (c= c (string-ref pat (+ vi p-start)))
			   (+ vi 1)
			   (let ((vi (vector-ref rv vi)))
			     (if (= vi -1) 0
				 (lp2 vi)))))))))))))

 
;;; Misc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; (string-null? s)
;;; (string-reverse  s [start end])
;;; (string-reverse! s [start end])
;;; (reverse-list->string clist)
;;; (string->list s [start end])

(define (string-null? s) (zero? (string-length s)))

(define (string-reverse s . maybe-start+end)
  (let-string-start+end (start end) string-reverse s maybe-start+end
    (let* ((len (- end start))
	   (ans (make-string len)))
      (do ((i start (+ i 1))
	   (j (- len 1) (- j 1)))
	  ((< j 0))
	(string-set! ans j (string-ref s i)))
      ans)))

(define (string-reverse! s . maybe-start+end)
  (let-string-start+end (start end) string-reverse! s maybe-start+end
    (do ((i (- end 1) (- i 1))
	 (j start (+ j 1)))
	((<= i j))
      (let ((ci (string-ref s i)))
	(string-set! s i (string-ref s j))
	(string-set! s j ci)))))


(define (reverse-list->string clist)
  (let* ((len (length clist))
	 (s (make-string len)))
    (do ((i (- len 1) (- i 1))   (clist clist (cdr clist)))
	((not (pair? clist)))
      (string-set! s i (car clist)))
    s))


;(define (string->list s . maybe-start+end)
;  (apply string-fold-right cons '() s maybe-start+end))

(define (string->list s . maybe-start+end)
  (let-string-start+end (start end) string->list s maybe-start+end
    (do ((i (- end 1) (- i 1))
	 (ans '() (cons (string-ref s i) ans)))
	((< i start) ans))))

;;; Defined by R5RS, so commented out here.
;(define (list->string lis) (string-unfold null? car cdr lis))


;;; string-concatenate        string-list -> string
;;; string-concatenate/shared string-list -> string
;;; string-append/shared s ... -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; STRING-APPEND/SHARED has license to return a string that shares storage
;;; with any of its arguments. In particular, if there is only one non-empty
;;; string amongst its parameters, it is permitted to return that string as
;;; its result. STRING-APPEND, by contrast, always allocates new storage.
;;;
;;; STRING-CONCATENATE & STRING-CONCATENATE/SHARED are passed a list of
;;; strings, which they concatenate into a result string. STRING-CONCATENATE
;;; always allocates a fresh string; STRING-CONCATENATE/SHARED may (or may
;;; not) return a result that shares storage with any of its arguments. In
;;; particular, if it is applied to a singleton list, it is permitted to
;;; return the car of that list as its value.

(define (string-append/shared . strings) (string-concatenate/shared strings))

(define (string-concatenate/shared strings)
  (let lp ((strings strings) (nchars 0) (first #f))
    (cond ((pair? strings)			; Scan the args, add up total
	   (let* ((string  (car strings))	; length, remember 1st 
		  (tail (cdr strings))		; non-empty string.
		  (slen (string-length string)))
	     (if (zero? slen)
		 (lp tail nchars first)
		 (lp tail (+ nchars slen) (or first strings)))))

	  ((zero? nchars) "")

	  ;; Just one non-empty string! Return it.
	  ((= nchars (string-length (car first))) (car first))

	  (else (let ((ans (make-string nchars)))
		  (let lp ((strings first) (i 0))
		    (if (pair? strings)
			(let* ((s (car strings))
			       (slen (string-length s)))
			  (%string-copy! ans i s 0 slen)
			  (lp (cdr strings) (+ i slen)))))
		  ans)))))
			

; Alas, Scheme 48's APPLY blows up if you have many, many arguments.
;(define (string-concatenate strings) (apply string-append strings))

;;; Here it is written out. I avoid using REDUCE to add up string lengths
;;; to avoid non-R5RS dependencies.
(define (string-concatenate strings)
  (let* ((total (do ((strings strings (cdr strings))
		     (i 0 (+ i (string-length (car strings)))))
		    ((not (pair? strings)) i)))
	 (ans (make-string total)))
    (let lp ((i 0) (strings strings))
      (if (pair? strings)
	  (let* ((s (car strings))
		 (slen (string-length s)))
	    (%string-copy! ans i s 0 slen)
	    (lp (+ i slen) (cdr strings)))))
    ans))
	  

;;; Defined by R5RS, so commented out here.
;(define (string-append . strings) (string-concatenate strings))

;;; string-concatenate-reverse        string-list [final-string end] -> string
;;; string-concatenate-reverse/shared string-list [final-string end] -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Return
;;;   (string-concatenate 
;;;     (reverse
;;;       (cons (substring final-string 0 end) string-list)))

(define (string-concatenate-reverse string-list . maybe-final+end)
  (let-optionals* maybe-final+end ((final "" (string? final))
				   (end (string-length final)
					(and (integer? end)
					     (exact? end)
					     (<= 0 end (string-length final)))))
    (let ((len (let lp ((sum 0) (lis string-list))
		 (if (pair? lis)
		     (lp (+ sum (string-length (car lis))) (cdr lis))
		     sum))))

      (%finish-string-concatenate-reverse len string-list final end))))

(define (string-concatenate-reverse/shared string-list . maybe-final+end)
  (let-optionals* maybe-final+end ((final "" (string? final))
				   (end (string-length final)
					(and (integer? end)
					     (exact? end)
					     (<= 0 end (string-length final)))))
    ;; Add up the lengths of all the strings in STRING-LIST; also get a
    ;; pointer NZLIST into STRING-LIST showing where the first non-zero-length
    ;; string starts.
    (let lp ((len 0) (nzlist #f) (lis string-list))
      (if (pair? lis)
	  (let ((slen (string-length (car lis))))
	    (lp (+ len slen)
		(if (or nzlist (zero? slen)) nzlist lis)
		(cdr lis)))

	  (cond ((zero? len) (substring/shared final 0 end))

		;; LEN > 0, so NZLIST is non-empty.

		((and (zero? end) (= len (string-length (car nzlist))))
		 (car nzlist))

		(else (%finish-string-concatenate-reverse len nzlist final end)))))))

(define (%finish-string-concatenate-reverse len string-list final end)
  (let ((ans (make-string (+ end len))))
    (%string-copy! ans len final 0 end)
    (let lp ((i len) (lis string-list))
      (if (pair? lis)
	  (let* ((s   (car lis))
		 (lis (cdr lis))
		 (slen (string-length s))
		 (i (- i slen)))
	    (%string-copy! ans i s 0 slen)
	    (lp i lis))))
    ans))




;;; string-replace s1 s2 start1 end1 [start2 end2] -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Replace S1[START1,END1) with S2[START2,END2).

(define (string-replace s1 s2 start1 end1 . maybe-start+end)
  (check-substring-spec string-replace s1 start1 end1)
  (let-string-start+end (start2 end2) string-replace s2 maybe-start+end
    (let* ((slen1 (string-length s1))
	   (sublen2 (- end2 start2))
	   (alen (+ (- slen1 (- end1 start1)) sublen2))
	   (ans (make-string alen)))
      (%string-copy! ans 0 s1 0 start1)
      (%string-copy! ans start1 s2 start2 end2)
      (%string-copy! ans (+ start1 sublen2) s1 end1 slen1)
      ans)))


;;; string-tokenize s [token-set start end] -> list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Break S up into a list of token strings, where a token is a maximal
;;; non-empty contiguous sequence of chars belonging to TOKEN-SET.
;;; (string-tokenize "hello, world") => ("hello," "world")

(define (string-tokenize s . token-chars+start+end)
  (let-optionals* token-chars+start+end
                  ((token-chars char-set:graphic (char-set? token-chars)) rest)
    (let-string-start+end (start end) string-tokenize s rest
      (let lp ((i end) (ans '()))
	(cond ((and (< start i) (string-index-right s token-chars start i)) =>
	       (lambda (tend-1)
		 (let ((tend (+ 1 tend-1)))
		   (cond ((string-skip-right s token-chars start tend-1) =>
			  (lambda (tstart-1)
			    (lp tstart-1
				(cons (substring s (+ 1 tstart-1) tend)
				      ans))))
			 (else (cons (substring s start tend) ans))))))
	      (else ans))))))

 
;;; xsubstring s from [to start end] -> string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; S is a string; START and END are optional arguments that demarcate
;;; a substring of S, defaulting to 0 and the length of S (e.g., the whole
;;; string). Replicate this substring up and down index space, in both the
;;  positive and negative directions. For example, if S = "abcdefg", START=3, 
;;; and END=6, then we have the conceptual bidirectionally-infinite string
;;;     ...  d  e  f  d  e  f  d  e  f  d  e  f  d  e  f  d  e  f  d  e  f ...
;;;     ... -9 -8 -7 -6 -5 -4 -3 -2 -1  0  1  2  3  4  5  6  7  8  9 ...
;;; XSUBSTRING returns the substring of this string beginning at index FROM,
;;; and ending at TO (which defaults to FROM+(END-START)).
;;; 
;;; You can use XSUBSTRING in many ways:
;;; - To rotate a string left:  (xsubstring "abcdef" 2)  => "cdefab"
;;; - To rotate a string right: (xsubstring "abcdef" -2) => "efabcd"
;;; - To replicate a string:    (xsubstring "abc" 0 7) => "abcabca"
;;;
;;; Note that 
;;;   - The FROM/TO indices give a half-open range -- the characters from
;;;     index FROM up to, but not including index TO.
;;;   - The FROM/TO indices are not in terms of the index space for string S.
;;;     They are in terms of the replicated index space of the substring
;;;     defined by S, START, and END.
;;;
;;; It is an error if START=END -- although this is allowed by special
;;; dispensation when FROM=TO.

(define (xsubstring s from . maybe-to+start+end)
  (check-arg (lambda (val) (and (integer? val) (exact? val)))
	     from xsubstring)
  (receive (to start end)
           (if (pair? maybe-to+start+end)
	       (let-string-start+end (start end) xsubstring s (cdr maybe-to+start+end)
		 (let ((to (car maybe-to+start+end)))
		   (check-arg (lambda (val) (and (integer? val)
						 (exact? val)
						 (<= from val)))
			      to xsubstring)
		   (values to start end)))
	       (let ((slen (string-length (check-arg string? s xsubstring))))
		 (values (+ from slen) 0 slen)))
    (let ((slen   (- end start))
	  (anslen (- to  from)))
      (cond ((zero? anslen) "")
	    ((zero? slen) (error "Cannot replicate empty (sub)string"
				  xsubstring s from to start end))

	    ((= 1 slen)		; Fast path for 1-char replication.
	     (make-string anslen (string-ref s start)))

	    ;; Selected text falls entirely within one span.
	    ((= (floor (/ from slen)) (floor (/ to slen)))
	     (substring s (+ start (modulo from slen))
			  (+ start (modulo to   slen))))

	    ;; Selected text requires multiple spans.
	    (else (let ((ans (make-string anslen)))
		    (%multispan-repcopy! ans 0 s from to start end)
		    ans))))))


;;; string-xcopy! target tstart s sfrom [sto start end] -> unspecific
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Exactly the same as xsubstring, but the extracted text is written
;;; into the string TARGET starting at index TSTART.
;;; This operation is not defined if (EQ? TARGET S) -- you cannot copy
;;; a string on top of itself.

(define (string-xcopy! target tstart s sfrom . maybe-sto+start+end)
  (check-arg (lambda (val) (and (integer? val) (exact? val)))
	     sfrom string-xcopy!)
  (receive (sto start end)
           (if (pair? maybe-sto+start+end)
	       (let-string-start+end (start end) string-xcopy! s (cdr maybe-sto+start+end)
		 (let ((sto (car maybe-sto+start+end)))
		   (check-arg (lambda (val) (and (integer? val) (exact? val)))
			      sto string-xcopy!)
		   (values sto start end)))
	       (let ((slen (string-length s)))
		 (values (+ sfrom slen) 0 slen)))

    (let* ((tocopy (- sto sfrom))
	   (tend (+ tstart tocopy))
	   (slen (- end start)))
      (check-substring-spec string-xcopy! target tstart tend)
      (cond ((zero? tocopy))
	    ((zero? slen) (error "Cannot replicate empty (sub)string"
				 string-xcopy!
				 target tstart s sfrom sto start end))

	    ((= 1 slen)			; Fast path for 1-char replication.
	     (string-fill! target (string-ref s start) tstart tend))

	    ;; Selected text falls entirely within one span.
	    ((= (floor (/ sfrom slen)) (floor (/ sto slen)))
	     (%string-copy! target tstart s 
			    (+ start (modulo sfrom slen))
			    (+ start (modulo sto   slen))))

	    ;; Multi-span copy.
	    (else (%multispan-repcopy! target tstart s sfrom sto start end))))))

;;; This is the core copying loop for XSUBSTRING and STRING-XCOPY!
;;; Internal -- not exported, no careful arg checking.
(define (%multispan-repcopy! target tstart s sfrom sto start end)
  (let* ((slen (- end start))
	 (i0 (+ start (modulo sfrom slen)))
	 (total-chars (- sto sfrom)))

    ;; Copy the partial span @ the beginning
    (%string-copy! target tstart s i0 end)
		    
    (let* ((ncopied (- end i0))			; We've copied this many.
	   (nleft (- total-chars ncopied))	; # chars left to copy.
	   (nspans (quotient nleft slen)))	; # whole spans to copy
			   
      ;; Copy the whole spans in the middle.
      (do ((i (+ tstart ncopied) (+ i slen))	; Current target index.
	   (nspans nspans (- nspans 1)))	; # spans to copy
	  ((zero? nspans)
	   ;; Copy the partial-span @ the end & we're done.
	   (%string-copy! target i s start (+ start (- total-chars (- i tstart)))))

	(%string-copy! target i s start end))))); Copy a whole span.


 
;;; (string-join string-list [delimiter grammar]) => string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Paste strings together using the delimiter string.
;;;
;;; (join-strings '("foo" "bar" "baz") ":") => "foo:bar:baz"
;;;
;;; DELIMITER defaults to a single space " "
;;; GRAMMAR is one of the symbols {prefix, infix, strict-infix, suffix} 
;;; and defaults to 'infix.
;;;
;;; I could rewrite this more efficiently -- precompute the length of the
;;; answer string, then allocate & fill it in iteratively. Using 
;;; STRING-CONCATENATE is less efficient.

(define (string-join strings . delim+grammar)
  (let-optionals* delim+grammar ((delim " " (string? delim))
				 (grammar 'infix))
    (let ((buildit (lambda (lis final)
		     (let recur ((lis lis))
		       (if (pair? lis)
			   (cons delim (cons (car lis) (recur (cdr lis))))
			   final)))))

      (cond ((pair? strings)
	     (string-concatenate
	      (case grammar

		((infix strict-infix)
		 (cons (car strings) (buildit (cdr strings) '())))

		((prefix) (buildit strings '()))

		((suffix)
		 (cons (car strings) (buildit (cdr strings) (list delim))))

		(else (error "Illegal join grammar"
			     grammar string-join)))))

	     ((not (null? strings))
	      (error "STRINGS parameter not list." strings string-join))

	     ;; STRINGS is ()

	     ((eq? grammar 'strict-infix)
	      (error "Empty list cannot be joined with STRICT-INFIX grammar."
		     string-join))

	     (else "")))))		; Special-cased for infix grammar.

 
;;; Porting & performance-tuning notes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; See the section at the beginning of this file on external dependencies.
;;;
;;; The biggest issue with respect to porting is the LET-OPTIONALS* macro.
;;; There are many, many optional arguments in this library; the complexity
;;; of parsing, defaulting & type-testing these parameters is handled with the
;;; aid of this macro. There are about 15 uses of LET-OPTIONALS*. You can
;;; rewrite the uses, port the hairy macro definition (which is implemented
;;; using a Clinger-Rees low-level explicit-renaming macro system), or port
;;; the simple, high-level definition, which is less efficient.
;;;
;;; There is a fair amount of argument checking. This is, strictly speaking,
;;; unnecessary -- the actual body of the procedures will blow up if, say, a
;;; START/END index is improper. However, the error message will not be as
;;; good as if the error were caught at the "higher level." Also, a very, very
;;; smart Scheme compiler may be able to exploit having the type checks done
;;; early, so that the actual body of the procedures can assume proper values.
;;; This isn't likely; this kind of compiler technology isn't common any 
;;; longer.
;;; 
;;; The overhead of optional-argument parsing is irritating. The optional
;;; arguments must be consed into a rest list on entry, and then parsed out.
;;; Function call should be a matter of a few register moves and a jump; it
;;; should not involve heap allocation! Your Scheme system may have a superior
;;; non-R5RS optional-argument system that can eliminate this overhead. If so,
;;; then this is a prime candidate for optimising these procedures,
;;; *especially* the many optional START/END index parameters.
;;;
;;; Note that optional arguments are also a barrier to procedure integration.
;;; If your Scheme system permits you to specify alternate entry points
;;; for a call when the number of optional arguments is known in a manner
;;; that enables inlining/integration, this can provide performance 
;;; improvements.
;;;
;;; There is enough *explicit* error checking that *all* string-index
;;; operations should *never* produce a bounds error. Period. Feel like
;;; living dangerously? *Big* performance win to be had by replacing
;;; STRING-REF's and STRING-SET!'s with unsafe equivalents in the loops. 
;;; Similarly, fixnum-specific operators can speed up the arithmetic done on 
;;; the index values in the inner loops. The only arguments that are not
;;; completely error checked are
;;;   - string lists (complete checking requires time proportional to the
;;;     length of the list)
;;;   - procedure arguments, such as char->char maps & predicates.
;;;     There is no way to check the range & domain of procedures in Scheme.
;;; Procedures that take these parameters cannot fully check their
;;; arguments. But all other types to all other procedures are fully
;;; checked.
;;;
;;; This does open up the alternate possibility of simply *removing* these 
;;; checks, and letting the safe primitives raise the errors. On a dumb
;;; Scheme system, this would provide speed (by eliminating the redundant
;;; error checks) at the cost of error-message clarity.
;;;
;;; See the comments preceding the hash function code for notes on tuning
;;; the default bound so that the code never overflows your implementation's
;;; fixnum size into bignum calculation.
;;;
;;; In an interpreted Scheme, some of these procedures, or the internal
;;; routines with % prefixes, are excellent candidates for being rewritten
;;; in C. Consider STRING-HASH, %STRING-COMPARE, the 
;;; %STRING-{SUF,PRE}FIX-LENGTH routines, STRING-COPY!, STRING-INDEX &
;;; STRING-SKIP (char-set & char cases), SUBSTRING and SUBSTRING/SHARED,
;;; %KMP-SEARCH, and %MULTISPAN-REPCOPY!.
;;;
;;; It would also be nice to have the ability to mark some of these
;;; routines as candidates for inlining/integration.
;;; 
;;; All the %-prefixed routines in this source code are written
;;; to be called internally to this library. They do *not* perform
;;; friendly error checks on the inputs; they assume everything is
;;; proper. They also do not take optional arguments. These two properties
;;; save calling overhead and enable procedure integration -- but they
;;; are not appropriate for exported routines.

 
;;; Copyright details
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The prefix/suffix and comparison routines in this code had (extremely
;;; distant) origins in MIT Scheme's string lib, and was substantially
;;; reworked by Olin Shivers (shivers@ai.mit.edu) 9/98. As such, it is
;;; covered by MIT Scheme's open source copyright. See below for details.
;;; 
;;; The KMP string-search code was influenced by implementations written
;;; by Stephen Bevan, Brian Dehneyer and Will Fitzgerald. However, this
;;; version was written from scratch by myself.
;;;
;;; The remainder of this code was written from scratch by myself for scsh.
;;; The scsh copyright is a BSD-style open source copyright. See below for
;;; details.
;;;     -Olin Shivers

;;; MIT Scheme copyright terms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This material was developed by the Scheme project at the Massachusetts
;;; Institute of Technology, Department of Electrical Engineering and
;;; Computer Science.  Permission to copy and modify this software, to
;;; redistribute either the original software or a modified version, and
;;; to use this software for any purpose is granted, subject to the
;;; following restrictions and understandings.
;;; 
;;; 1. Any copy made of this software must include this copyright notice
;;; in full.
;;; 
;;; 2. Users of this software agree to make their best efforts (a) to
;;; return to the MIT Scheme project any improvements or extensions that
;;; they make, so that these may be included in future releases; and (b)
;;; to inform MIT of noteworthy uses of this software.
;;; 
;;; 3. All materials developed as a consequence of the use of this
;;; software shall duly acknowledge such use, in accordance with the usual
;;; standards of acknowledging credit in academic research.
;;; 
;;; 4. MIT has made no warrantee or representation that the operation of
;;; this software will be error-free, and MIT is under no obligation to
;;; provide any services, by way of maintenance, update, or otherwise.
;;; 
;;; 5. In conjunction with products arising from the use of this material,
;;; there shall be no use of the name of the Massachusetts Institute of
;;; Technology nor of any adaptation thereof in any advertising,
;;; promotional, or sales literature without prior written consent from
;;; MIT in each case.

;;; Scsh copyright terms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; All rights reserved.
;;; 
;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:
;;; 1. Redistributions of source code must retain the above copyright
;;;    notice, this list of conditions and the following disclaimer.
;;; 2. Redistributions in binary form must reproduce the above copyright
;;;    notice, this list of conditions and the following disclaimer in the
;;;    documentation and/or other materials provided with the distribution.
;;; 3. The name of the authors may not be used to endorse or promote products
;;;    derived from this software without specific prior written permission.
;;; 
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Added srfi/s13/strings.sls.











































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s13 strings)
  (export
    string-map string-map!
    string-fold       string-unfold
    string-fold-right string-unfold-right 
    string-tabulate string-for-each string-for-each-index
    string-every string-any
    string-hash string-hash-ci
    string-compare string-compare-ci
    string=    string<    string>    string<=    string>=    string<>
    string-ci= string-ci< string-ci> string-ci<= string-ci>= string-ci<> 
    string-downcase  string-upcase  string-titlecase  
    string-downcase! string-upcase! string-titlecase! 
    string-take string-take-right
    string-drop string-drop-right
    string-pad string-pad-right
    string-trim string-trim-right string-trim-both
    string-filter string-delete
    string-index string-index-right 
    string-skip  string-skip-right
    string-count
    string-prefix-length string-prefix-length-ci
    string-suffix-length string-suffix-length-ci
    string-prefix? string-prefix-ci?
    string-suffix? string-suffix-ci?
    string-contains string-contains-ci
    string-copy! substring/shared
    string-reverse string-reverse! reverse-list->string
    string-concatenate string-concatenate/shared string-concatenate-reverse
    string-concatenate-reverse/shared
    string-append/shared
    xsubstring string-xcopy!
    string-null?
    string-join
    string-tokenize
    string-replace
    ; R5RS extended:
    string->list string-copy string-fill! 
    ; R5RS re-exports:
    string? make-string string-length string-ref string-set! 
    string string-append list->string
    ; Low-level routines:
    #;(make-kmp-restart-vector string-kmp-partial-search kmp-step
    string-parse-start+end
    string-parse-final-start+end
    let-string-start+end
    check-substring-spec
    substring-spec-ok?)
    )
  (import
    (except (rnrs) string-copy string-for-each string->list
                   string-upcase string-downcase string-titlecase string-hash)
    (except (rnrs mutable-strings) string-fill!)
    (rnrs r5rs)
    (srfi s23 error tricks)
    (srfi s8 receive)
    (srfi s14 char-sets)
    (srfi private let-opt)
    (srfi private include))
  
  
  (define-syntax check-arg
    (lambda (stx)
      (syntax-case stx ()
        [(_ pred val caller)
         (and (identifier? #'val) (identifier? #'caller))
         #'(unless (pred val)
             (assertion-violation 'caller "check-arg failed" val))])))
  
  (define (char-cased? c)
    (char-upper-case? (char-upcase c)))
  
  ;; (SRFI-23-error->R6RS "(library (srfi s13 strings))"
  ;;  (include/resolve ("srfi" "%3a13") "srfi-13.scm"))

  (SRFI-23-error->R6RS "(library (srfi s13 strings))"
   (include/resolve ("srfi" "s13") "srfi-13.scm"))
)

Added srfi/s14/char-sets.sls.





































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s14 char-sets)
  (export
    ; Predicates & comparison
    char-set? char-set= char-set<= char-set-hash
    ; Iterating over character sets
    char-set-cursor char-set-ref char-set-cursor-next end-of-char-set? 
    char-set-fold char-set-unfold char-set-unfold!
    char-set-for-each char-set-map
    ; Creating character sets
    char-set-copy char-set
    list->char-set  string->char-set
    list->char-set! string->char-set!
    char-set-filter  ucs-range->char-set 
    char-set-filter! ucs-range->char-set!
    ->char-set
    ; Querying character sets
    char-set->list char-set->string
    char-set-size char-set-count char-set-contains?
    char-set-every char-set-any
    ; Character-set algebra
    char-set-adjoin  char-set-delete
    char-set-adjoin! char-set-delete!
    char-set-complement  char-set-union  char-set-intersection
    char-set-complement! char-set-union! char-set-intersection!
    char-set-difference  char-set-xor  char-set-diff+intersection
    char-set-difference! char-set-xor! char-set-diff+intersection!
    ; Standard character sets
    char-set:lower-case  char-set:upper-case  char-set:title-case
    char-set:letter      char-set:digit       char-set:letter+digit
    char-set:graphic     char-set:printing    char-set:whitespace
    char-set:iso-control char-set:punctuation char-set:symbol
    char-set:hex-digit   char-set:blank       char-set:ascii
    char-set:empty       char-set:full
    )
  (import
    (except (rnrs) define-record-type)
    (rnrs mutable-strings)
    (rnrs r5rs)
    (srfi s23 error tricks)
    (srfi s9 records)
    (srfi private let-opt)
    (srfi private include))
  
  (define (%latin1->char i)
    (integer->char i))
  
  (define (%char->latin1 c)
    (char->integer c))
      
  (define-syntax check-arg
    (lambda (stx)
      (syntax-case stx ()
        [(_ pred val caller)
         (identifier? #'val)
         #'(unless (pred val)
             (assertion-violation caller "check-arg failed" val))])))
  
  (SRFI-23-error->R6RS "(library (srfi s14 char-sets))"
   (include/resolve ("srfi" "s14") "srfi-14.scm")))

Added srfi/s14/srfi-14.scm.













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
;;; SRFI-14 character-sets library				-*- Scheme -*-
;;;
;;; - Ported from MIT Scheme runtime by Brian D. Carlstrom.
;;; - Massively rehacked & extended by Olin Shivers 6/98.
;;; - Massively redesigned and rehacked 5/2000 during SRFI process.
;;; At this point, the code bears the following relationship to the
;;; MIT Scheme code: "This is my grandfather's axe. My father replaced
;;; the head, and I have replaced the handle." Nonetheless, we preserve
;;; the MIT Scheme copyright:
;;;     Copyright (c) 1988-1995 Massachusetts Institute of Technology
;;; The MIT Scheme license is a "free software" license. See the end of
;;; this file for the tedious details. 

;;; Exports:
;;; char-set? char-set= char-set<=
;;; char-set-hash 
;;; char-set-cursor char-set-ref char-set-cursor-next end-of-char-set?
;;; char-set-fold char-set-unfold char-set-unfold!
;;; char-set-for-each char-set-map
;;; char-set-copy char-set
;;;
;;; list->char-set  string->char-set 
;;; list->char-set! string->char-set! 
;;;
;;; filterchar-set  ucs-range->char-set  ->char-set
;;; filterchar-set! ucs-range->char-set!
;;;
;;; char-set->list char-set->string
;;;
;;; char-set-size char-set-count char-set-contains?
;;; char-set-every char-set-any
;;;
;;; char-set-adjoin  char-set-delete 
;;; char-set-adjoin! char-set-delete!
;;; 

;;; char-set-complement  char-set-union  char-set-intersection  
;;; char-set-complement! char-set-union! char-set-intersection! 
;;;
;;; char-set-difference  char-set-xor  char-set-diff+intersection
;;; char-set-difference! char-set-xor! char-set-diff+intersection!
;;;
;;; char-set:lower-case		char-set:upper-case	char-set:title-case
;;; char-set:letter		char-set:digit		char-set:letter+digit
;;; char-set:graphic		char-set:printing	char-set:whitespace
;;; char-set:iso-control	char-set:punctuation	char-set:symbol
;;; char-set:hex-digit		char-set:blank		char-set:ascii
;;; char-set:empty		char-set:full

;;; Imports
;;; This code has the following non-R5RS dependencies:
;;; - ERROR
;;; - %LATIN1->CHAR %CHAR->LATIN1
;;; - LET-OPTIONALS* and :OPTIONAL macros for parsing, checking & defaulting
;;;   optional arguments from rest lists.
;;; - BITWISE-AND for CHAR-SET-HASH
;;; - The SRFI-19 DEFINE-RECORD-TYPE record macro
;;; - A simple CHECK-ARG procedure: 
;;;   (lambda (pred val caller) (if (not (pred val)) (error val caller)))

;;; This is simple code, not great code. Char sets are represented as 256-char
;;; strings. If char I is ASCII/Latin-1 0, then it isn't in the set; if char I
;;; is ASCII/Latin-1 1, then it is in the set.
;;; - Should be rewritten to use bit strings or byte vecs.
;;; - Is Latin-1 specific. Would certainly have to be rewritten for Unicode.

;;; See the end of the file for porting and performance-tuning notes.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define-record-type :char-set
  (make-char-set s)
  char-set?
  (s char-set:s))


(define (%string-copy s) (substring s 0 (string-length s)))

;;; Parse, type-check & default a final optional BASE-CS parameter from
;;; a rest argument. Return a *fresh copy* of the underlying string.
;;; The default is the empty set. The PROC argument is to help us
;;; generate informative error exceptions.

(define (%default-base maybe-base proc)
  (if (pair? maybe-base)
      (let ((bcs  (car maybe-base))
	    (tail (cdr maybe-base)))
	(if (null? tail)
	    (if (char-set? bcs) (%string-copy (char-set:s bcs))
		(assertion-violation proc "BASE-CS parameter not a char-set" bcs))
	    (assertion-violation proc
             "Expected final base char set -- too many parameters" maybe-base)))
      (make-string 256 (%latin1->char 0))))

;;; If CS is really a char-set, do CHAR-SET:S, otw report an error msg on
;;; behalf of our caller, PROC. This procedure exists basically to provide
;;; explicit error-checking & reporting.

(define (%char-set:s/check cs proc)
  (let lp ((cs cs))
    (if (char-set? cs) (char-set:s cs)
	(lp (assertion-violation proc "Not a char-set" cs)))))



;;; These internal functions hide a lot of the dependency on the
;;; underlying string representation of char sets. They should be
;;; inlined if possible.

(define (si=0? s i) (zero? (%char->latin1 (string-ref s i))))
(define (si=1? s i) (not (si=0? s i)))
(define c0 (%latin1->char 0))
(define c1 (%latin1->char 1))
(define (si s i) (%char->latin1 (string-ref s i)))
(define (%set0! s i) (string-set! s i c0))
(define (%set1! s i) (string-set! s i c1))

;;; These do various "s[i] := s[i] op val" operations -- see 
;;; %CHAR-SET-ALGEBRA. They are used to implement the various
;;; set-algebra procedures.
(define (setv!   s i v) (string-set! s i (%latin1->char v))) ; SET to a Value.
(define (%not!   s i v) (setv! s i (- 1 v)))
(define (%and!   s i v) (if (zero? v) (%set0! s i)))
(define (%or!    s i v) (if (not (zero? v)) (%set1! s i)))
(define (%minus! s i v) (if (not (zero? v)) (%set0! s i)))
(define (%xor!   s i v) (if (not (zero? v)) (setv! s i (- 1 (si s i)))))


(define (char-set-copy cs)
  (make-char-set (%string-copy (%char-set:s/check cs 'char-set-copy))))

(define (char-set= . rest)
  (or (null? rest)
      (let* ((cs1  (car rest))
	     (rest (cdr rest))
	     (s1 (%char-set:s/check cs1 'char-set=)))
	(let lp ((rest rest))
	  (or (not (pair? rest))
	      (and (string=? s1 (%char-set:s/check (car rest) 'char-set=))
		   (lp (cdr rest))))))))

(define (char-set<= . rest)
  (or (null? rest)
      (let ((cs1  (car rest))
	    (rest (cdr rest)))
	(let lp ((s1 (%char-set:s/check cs1 'char-set<=))  (rest rest))
	  (or (not (pair? rest))
	      (let ((s2 (%char-set:s/check (car rest) 'char-set<=))
		    (rest (cdr rest)))
		(if (eq? s1 s2) (lp s2 rest)	; Fast path
		    (let lp2 ((i 255))		; Real test
		      (if (< i 0) (lp s2 rest)
			  (and (<= (si s1 i) (si s2 i))
			       (lp2 (- i 1))))))))))))

;;; Hash
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Compute (c + 37 c + 37^2 c + ...) modulo BOUND, with sleaze thrown in
;;; to keep the intermediate values small. (We do the calculation with just
;;; enough bits to represent BOUND, masking off high bits at each step in
;;; calculation. If this screws up any important properties of the hash
;;; function I'd like to hear about it. -Olin)
;;;
;;; If you keep BOUND small enough, the intermediate calculations will 
;;; always be fixnums. How small is dependent on the underlying Scheme system; 
;;; we use a default BOUND of 2^22 = 4194304, which should hack it in
;;; Schemes that give you at least 29 signed bits for fixnums. The core 
;;; calculation that you don't want to overflow is, worst case,
;;;     (+ 65535 (* 37 (- bound 1)))
;;; where 65535 is the max character code. Choose the default BOUND to be the
;;; biggest power of two that won't cause this expression to fixnum overflow, 
;;; and everything will be copacetic.

(define (char-set-hash cs . maybe-bound)
  (let* ((bound (:optional maybe-bound 4194304 (lambda (n) (and (integer? n)
								(exact? n)
								(<= 0 n)))))
	 (bound (if (zero? bound) 4194304 bound))	; 0 means default.
	 (s (%char-set:s/check cs 'char-set-hash))
	 ;; Compute a 111...1 mask that will cover BOUND-1:
	 (mask (let lp ((i #x10000)) ; Let's skip first 16 iterations, eh?
		 (if (>= i bound) (- i 1) (lp (+ i i))))))

      (let lp ((i 255) (ans 0))
	(if (< i 0) (modulo ans bound)
	    (lp (- i 1)
		(if (si=0? s i) ans
		    (bitwise-and mask (+ (* 37 ans) i))))))))


(define (char-set-contains? cs char)
  (check-arg char? char 'char-set-contains?)
  (si=1? (%char-set:s/check cs 'char-set-contains?)
	 (%char->latin1 char)))

(define (char-set-size cs)
  (let ((s (%char-set:s/check cs 'char-set-size)))
    (let lp ((i 255) (size 0))
      (if (< i 0) size
	  (lp (- i 1) (+ size (si s i)))))))

(define (char-set-count pred cset)
  (check-arg procedure? pred 'char-set-count)
  (let ((s (%char-set:s/check cset 'char-set-count)))
    (let lp ((i 255) (count 0))
      (if (< i 0) count
	  (lp (- i 1)
	      (if (and (si=1? s i) (pred (%latin1->char i)))
		  (+ count 1)
		  count))))))


;;; -- Adjoin & delete

(define (%set-char-set set proc cs chars)
  (let ((s (%string-copy (%char-set:s/check cs proc))))
    (for-each (lambda (c) (set s (%char->latin1 c)))
	      chars)
    (make-char-set s)))

(define (%set-char-set! set proc cs chars)
  (let ((s (%char-set:s/check cs proc)))
    (for-each (lambda (c) (set s (%char->latin1 c)))
	      chars))
  cs)

(define (char-set-adjoin cs . chars)
  (%set-char-set  %set1! 'char-set-adjoin cs chars))
(define (char-set-adjoin! cs . chars)
  (%set-char-set! %set1! 'char-set-adjoin! cs chars))
(define (char-set-delete cs . chars)
  (%set-char-set  %set0! 'char-set-delete cs chars))
(define (char-set-delete! cs . chars)
  (%set-char-set! %set0! 'char-set-delete! cs chars))


;;; Cursors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Simple implementation. A cursors is an integer index into the
;;; mark vector, and -1 for the end-of-char-set cursor.
;;;
;;; If we represented char sets as a bit set, we could do the following
;;; trick to pick the lowest bit out of the set: 
;;;   (count-bits (xor (- cset 1) cset))
;;; (But first mask out the bits already scanned by the cursor first.)

(define (char-set-cursor cset)
  (%char-set-cursor-next cset 256 'char-set-cursor))
  
(define (end-of-char-set? cursor) (< cursor 0))

(define (char-set-ref cset cursor) (%latin1->char cursor))

(define (char-set-cursor-next cset cursor)
  (check-arg (lambda (i) (and (integer? i) (exact? i) (<= 0 i 255))) cursor
	     'char-set-cursor-next)
  (%char-set-cursor-next cset cursor 'char-set-cursor-next))

(define (%char-set-cursor-next cset cursor proc)	; Internal
  (let ((s (%char-set:s/check cset proc)))
    (let lp ((cur cursor))
      (let ((cur (- cur 1)))
	(if (or (< cur 0) (si=1? s cur)) cur
	    (lp cur))))))


;;; -- for-each map fold unfold every any

(define (char-set-for-each proc cs)
  (check-arg procedure? proc 'char-set-for-each)
  (let ((s (%char-set:s/check cs 'char-set-for-each)))
    (let lp ((i 255))
      (cond ((>= i 0)
	     (if (si=1? s i) (proc (%latin1->char i)))
	     (lp (- i 1)))))))

(define (char-set-map proc cs)
  (check-arg procedure? proc 'char-set-map)
  (let ((s (%char-set:s/check cs 'char-set-map))
	(ans (make-string 256 c0)))
    (let lp ((i 255))
      (cond ((>= i 0)
	     (if (si=1? s i)
		 (%set1! ans (%char->latin1 (proc (%latin1->char i)))))
	     (lp (- i 1)))))
    (make-char-set ans)))

(define (char-set-fold kons knil cs)
  (check-arg procedure? kons 'char-set-fold)
  (let ((s (%char-set:s/check cs 'char-set-fold)))
    (let lp ((i 255) (ans knil))
      (if (< i 0) ans
	  (lp (- i 1)
	      (if (si=0? s i) ans
		  (kons (%latin1->char i) ans)))))))

(define (char-set-every pred cs)
  (check-arg procedure? pred 'char-set-every)
  (let ((s (%char-set:s/check cs 'char-set-every)))
    (let lp ((i 255))
      (or (< i 0)
	  (and (or (si=0? s i) (pred (%latin1->char i)))
	       (lp (- i 1)))))))

(define (char-set-any pred cs)
  (check-arg procedure? pred 'char-set-any)
  (let ((s (%char-set:s/check cs 'char-set-any)))
    (let lp ((i 255))
      (and (>= i 0)
	   (or (and (si=1? s i) (pred (%latin1->char i)))
	       (lp (- i 1)))))))


(define (%char-set-unfold! proc p f g s seed)
  (check-arg procedure? p proc)
  (check-arg procedure? f proc)
  (check-arg procedure? g proc)
  (let lp ((seed seed))
    (cond ((not (p seed))			; P says we are done.
	   (%set1! s (%char->latin1 (f seed)))	; Add (F SEED) to set.
	   (lp (g seed))))))			; Loop on (G SEED).

(define (char-set-unfold p f g seed . maybe-base)
  (let ((bs (%default-base maybe-base 'char-set-unfold)))
    (%char-set-unfold! 'char-set-unfold p f g bs seed)
    (make-char-set bs)))

(define (char-set-unfold! p f g seed base-cset)
  (%char-set-unfold! 'char-set-unfold! p f g
		     (%char-set:s/check base-cset 'char-set-unfold!)
		     seed)
  base-cset)



;;; list <--> char-set

(define (%list->char-set! chars s)
  (for-each (lambda (char) (%set1! s (%char->latin1 char)))
	    chars))

(define (char-set . chars)
  (let ((s (make-string 256 c0)))
    (%list->char-set! chars s)
    (make-char-set s)))

(define (list->char-set chars . maybe-base)
  (let ((bs (%default-base maybe-base 'list->char-set)))
    (%list->char-set! chars bs)
    (make-char-set bs)))

(define (list->char-set! chars base-cs)
  (%list->char-set! chars (%char-set:s/check base-cs 'list->char-set!))
  base-cs)


(define (char-set->list cs)
  (let ((s (%char-set:s/check cs 'char-set->list)))
    (let lp ((i 255) (ans '()))
      (if (< i 0) ans
	  (lp (- i 1)
	      (if (si=0? s i) ans
		  (cons (%latin1->char i) ans)))))))



;;; string <--> char-set

(define (%string->char-set! str bs proc)
  (check-arg string? str proc)
  (do ((i (- (string-length str) 1) (- i 1)))
      ((< i 0))
    (%set1! bs (%char->latin1 (string-ref str i)))))

(define (string->char-set str . maybe-base)
  (let ((bs (%default-base maybe-base 'string->char-set)))
    (%string->char-set! str bs 'string->char-set)
    (make-char-set bs)))

(define (string->char-set! str base-cs)
  (%string->char-set! str (%char-set:s/check base-cs 'string->char-set!)
		      'string->char-set!)
  base-cs)


(define (char-set->string cs)
  (let* ((s (%char-set:s/check cs 'char-set->string))
	 (ans (make-string (char-set-size cs))))
    (let lp ((i 255) (j 0))
      (if (< i 0) ans
	  (let ((j (if (si=0? s i) j
		       (begin (string-set! ans j (%latin1->char i))
			      (+ j 1)))))
	    (lp (- i 1) j))))))


;;; -- UCS-range -> char-set

(define (%ucs-range->char-set! lower upper error? bs proc)
  (check-arg (lambda (x) (and (integer? x) (exact? x) (<= 0 x))) lower proc)
  (check-arg (lambda (x) (and (integer? x) (exact? x) (<= lower x))) upper proc)

  (if (and (< lower upper) (< 256 upper) error?)
      (assertion-violation proc
       "Requested UCS range contains unavailable characters -- this implementation only supports Latin-1"
	     lower upper))

  (let lp ((i (- (min upper 256) 1)))
    (cond ((<= lower i) (%set1! bs i) (lp (- i 1))))))

(define (ucs-range->char-set lower upper . rest)
  (let-optionals* rest ((error? #f) rest)
    (let ((bs (%default-base rest 'ucs-range->char-set)))
      (%ucs-range->char-set! lower upper error? bs 'ucs-range->char-set)
      (make-char-set bs))))

(define (ucs-range->char-set! lower upper error? base-cs)
  (%ucs-range->char-set! lower upper error?
			 (%char-set:s/check base-cs 'ucs-range->char-set!)
			 'ucs-range->char-set)
  base-cs)


;;; -- predicate -> char-set

(define (%char-set-filter! pred ds bs proc)
  (check-arg procedure? pred proc)
  (let lp ((i 255))
    (cond ((>= i 0)
	   (if (and (si=1? ds i) (pred (%latin1->char i)))
	       (%set1! bs i))
	   (lp (- i 1))))))

(define (char-set-filter predicate domain . maybe-base)
  (let ((bs (%default-base maybe-base 'char-set-filter)))
    (%char-set-filter! predicate
		       (%char-set:s/check domain 'char-set-filter!)
		       bs
		       'char-set-filter)
    (make-char-set bs)))

(define (char-set-filter! predicate domain base-cs)
  (%char-set-filter! predicate
		     (%char-set:s/check domain 'char-set-filter!)
		     (%char-set:s/check base-cs 'char-set-filter!)
		     'char-set-filter!)
  base-cs)


;;; {string, char, char-set, char predicate} -> char-set

(define (->char-set x)
  (cond ((char-set? x) x)
	((string? x) (string->char-set x))
	((char? x) (char-set x))
	(else (error "Not a charset, string or char." x))))



;;; Set algebra
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The exported ! procs are "linear update" -- allowed, but not required, to
;;; side-effect their first argument when computing their result. In other
;;; words, you must use them as if they were completely functional, just like
;;; their non-! counterparts, and you must additionally ensure that their
;;; first arguments are "dead" at the point of call. In return, we promise a
;;; more efficient result, plus allowing you to always assume char-sets are
;;; unchangeable values.

;;; Apply P to each index and its char code in S: (P I VAL).
;;; Used by the set-algebra ops.

(define (%string-iter p s)
  (let lp ((i (- (string-length s) 1)))
    (cond ((>= i 0)
	   (p i (%char->latin1 (string-ref s i)))
	   (lp (- i 1))))))

;;; String S represents some initial char-set. (OP s i val) does some
;;; kind of s[i] := s[i] op val update. Do
;;;     S := S OP CSETi
;;; for all the char-sets in the list CSETS. The n-ary set-algebra ops
;;; all use this internal proc.

(define (%char-set-algebra s csets op proc)
  (for-each (lambda (cset)
	      (let ((s2 (%char-set:s/check cset proc)))
		(let lp ((i 255))
		  (cond ((>= i 0)
			 (op s i (si s2 i))
			 (lp (- i 1)))))))
	    csets))


;;; -- Complement

(define (char-set-complement cs)
  (let ((s (%char-set:s/check cs 'char-set-complement))
	(ans (make-string 256)))
    (%string-iter (lambda (i v) (%not! ans i v)) s)
    (make-char-set ans)))

(define (char-set-complement! cset)
  (let ((s (%char-set:s/check cset 'char-set-complement!)))
    (%string-iter (lambda (i v) (%not! s i v)) s))
  cset)


;;; -- Union

(define (char-set-union! cset1 . csets)
  (%char-set-algebra (%char-set:s/check cset1 'char-set-union!)
		     csets %or! 'char-set-union!)
  cset1)

(define (char-set-union . csets)
  (if (pair? csets)
      (let ((s (%string-copy (%char-set:s/check (car csets) 'char-set-union))))
	(%char-set-algebra s (cdr csets) %or! 'char-set-union)
	(make-char-set s))
      (char-set-copy char-set:empty)))


;;; -- Intersection

(define (char-set-intersection! cset1 . csets)
  (%char-set-algebra (%char-set:s/check cset1 'char-set-intersection!)
		     csets %and! 'char-set-intersection!)
  cset1)

(define (char-set-intersection . csets)
  (if (pair? csets)
      (let ((s (%string-copy (%char-set:s/check (car csets) 'char-set-intersection))))
	(%char-set-algebra s (cdr csets) %and! 'char-set-intersection)
	(make-char-set s))
      (char-set-copy char-set:full)))


;;; -- Difference

(define (char-set-difference! cset1 . csets)
  (%char-set-algebra (%char-set:s/check cset1 'char-set-difference!)
		     csets %minus! 'char-set-difference!)
  cset1)

(define (char-set-difference cs1 . csets)
  (if (pair? csets)
      (let ((s (%string-copy (%char-set:s/check cs1 'char-set-difference))))
	(%char-set-algebra s csets %minus! 'char-set-difference)
	(make-char-set s))
      (char-set-copy cs1)))


;;; -- Xor

(define (char-set-xor! cset1 . csets)
  (%char-set-algebra (%char-set:s/check cset1 'char-set-xor!)
		      csets %xor! 'char-set-xor!)
  cset1)

(define (char-set-xor . csets)
  (if (pair? csets)
      (let ((s (%string-copy (%char-set:s/check (car csets) 'char-set-xor))))
	(%char-set-algebra s (cdr csets) %xor! 'char-set-xor)
	(make-char-set s))
      (char-set-copy char-set:empty)))


;;; -- Difference & intersection

(define (%char-set-diff+intersection! diff int csets proc)
  (for-each (lambda (cs)
	      (%string-iter (lambda (i v)
			      (if (not (zero? v))
				  (cond ((si=1? diff i)
					 (%set0! diff i)
					 (%set1! int  i)))))
			    (%char-set:s/check cs proc)))
	    csets))

(define (char-set-diff+intersection! cs1 cs2 . csets)
  (let ((s1 (%char-set:s/check cs1 'char-set-diff+intersection!))
	(s2 (%char-set:s/check cs2 'char-set-diff+intersection!)))
    (%string-iter (lambda (i v) (if (zero? v)
				    (%set0! s2 i)
				    (if (si=1? s2 i) (%set0! s1 i))))
		  s1)
    (%char-set-diff+intersection! s1 s2 csets 'char-set-diff+intersection!))
  (values cs1 cs2))

(define (char-set-diff+intersection cs1 . csets)
  (let ((diff (string-copy (%char-set:s/check cs1 'char-set-diff+intersection)))
	(int  (make-string 256 c0)))
    (%char-set-diff+intersection! diff int csets 'char-set-diff+intersection)
    (values (make-char-set diff) (make-char-set int))))


;;;; System character sets
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; These definitions are for Latin-1.
;;;
;;; If your Scheme implementation allows you to mark the underlying strings
;;; as immutable, you should do so -- it would be very, very bad if a client's
;;; buggy code corrupted these constants.

(define char-set:empty (char-set))
(define char-set:full (char-set-complement char-set:empty))

(define char-set:lower-case
  (let* ((a-z (ucs-range->char-set #x61 #x7B))
	 (latin1 (ucs-range->char-set! #xdf #xf7  #t a-z))
	 (latin2 (ucs-range->char-set! #xf8 #x100 #t latin1)))
    (char-set-adjoin! latin2 (%latin1->char #xb5))))

(define char-set:upper-case
  (let ((A-Z (ucs-range->char-set #x41 #x5B)))
    ;; Add in the Latin-1 upper-case chars.
    (ucs-range->char-set! #xd8 #xdf #t
			  (ucs-range->char-set! #xc0 #xd7 #t A-Z))))

(define char-set:title-case char-set:empty)

(define char-set:letter
  (let ((u/l (char-set-union char-set:upper-case char-set:lower-case)))
    (char-set-adjoin! u/l
		      (%latin1->char #xaa)	; FEMININE ORDINAL INDICATOR
		      (%latin1->char #xba))))	; MASCULINE ORDINAL INDICATOR

(define char-set:digit     (string->char-set "0123456789"))
(define char-set:hex-digit (string->char-set "0123456789abcdefABCDEF"))

(define char-set:letter+digit
  (char-set-union char-set:letter char-set:digit))

(define char-set:punctuation
  (let ((ascii (string->char-set "!\"#%&'()*,-./:;?@[\\]_{}"))
	(latin-1-chars (map %latin1->char '(#xA1 ; INVERTED EXCLAMATION MARK
					    #xAB ; LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
					    #xAD ; SOFT HYPHEN
					    #xB7 ; MIDDLE DOT
					    #xBB ; RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
					    #xBF)))) ; INVERTED QUESTION MARK
    (list->char-set! latin-1-chars ascii)))

(define char-set:symbol
  (let ((ascii (string->char-set "$+<=>^`|~"))
	(latin-1-chars (map %latin1->char '(#x00A2 ; CENT SIGN
					    #x00A3 ; POUND SIGN
					    #x00A4 ; CURRENCY SIGN
					    #x00A5 ; YEN SIGN
					    #x00A6 ; BROKEN BAR
					    #x00A7 ; SECTION SIGN
					    #x00A8 ; DIAERESIS
					    #x00A9 ; COPYRIGHT SIGN
					    #x00AC ; NOT SIGN
					    #x00AE ; REGISTERED SIGN
					    #x00AF ; MACRON
					    #x00B0 ; DEGREE SIGN
					    #x00B1 ; PLUS-MINUS SIGN
					    #x00B4 ; ACUTE ACCENT
					    #x00B6 ; PILCROW SIGN
					    #x00B8 ; CEDILLA
					    #x00D7 ; MULTIPLICATION SIGN
					    #x00F7)))) ; DIVISION SIGN
    (list->char-set! latin-1-chars ascii)))
  

(define char-set:graphic
  (char-set-union char-set:letter+digit char-set:punctuation char-set:symbol))

(define char-set:whitespace
  (list->char-set (map %latin1->char '(#x09 ; HORIZONTAL TABULATION
				       #x0A ; LINE FEED		
				       #x0B ; VERTICAL TABULATION
				       #x0C ; FORM FEED
				       #x0D ; CARRIAGE RETURN
				       #x20 ; SPACE
				       #xA0))))

(define char-set:printing (char-set-union char-set:whitespace char-set:graphic)) ; NO-BREAK SPACE

(define char-set:blank
  (list->char-set (map %latin1->char '(#x09 ; HORIZONTAL TABULATION
				       #x20 ; SPACE
				       #xA0)))) ; NO-BREAK SPACE


(define char-set:iso-control
  (ucs-range->char-set! #x7F #xA0 #t (ucs-range->char-set 0 32)))

(define char-set:ascii (ucs-range->char-set 0 128))

 
;;; Porting & performance-tuning notes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; See the section at the beginning of this file on external dependencies.
;;;
;;; First and foremost, rewrite this code to use bit vectors of some sort.
;;; This will give big speedup and memory savings.
;;;
;;; - LET-OPTIONALS* macro.
;;; This is only used once. You can rewrite the use, port the hairy macro
;;; definition (which is implemented using a Clinger-Rees low-level
;;; explicit-renaming macro system), or port the simple, high-level
;;; definition, which is less efficient.
;;;
;;; - :OPTIONAL macro
;;; Very simply defined using an R5RS high-level macro.
;;;
;;; Implementations that can arrange for the base char sets to be immutable
;;; should do so. (E.g., Scheme 48 allows one to mark a string as immutable,
;;; which can be used to protect the underlying strings.) It would be very,
;;; very bad if a client's buggy code corrupted these constants.
;;;
;;; There is a fair amount of argument checking. This is, strictly speaking,
;;; unnecessary -- the actual body of the procedures will blow up if an
;;; illegal value is passed in. However, the error message will not be as good
;;; as if the error were caught at the "higher level." Also, a very, very
;;; smart Scheme compiler may be able to exploit having the type checks done
;;; early, so that the actual body of the procedures can assume proper values.
;;; This isn't likely; this kind of compiler technology isn't common any
;;; longer.
;;; 
;;; The overhead of optional-argument parsing is irritating. The optional
;;; arguments must be consed into a rest list on entry, and then parsed out.
;;; Function call should be a matter of a few register moves and a jump; it
;;; should not involve heap allocation! Your Scheme system may have a superior
;;; non-R5RS optional-argument system that can eliminate this overhead. If so,
;;; then this is a prime candidate for optimising these procedures,
;;; *especially* the many optional BASE-CS parameters.
;;;
;;; Note that optional arguments are also a barrier to procedure integration.
;;; If your Scheme system permits you to specify alternate entry points
;;; for a call when the number of optional arguments is known in a manner
;;; that enables inlining/integration, this can provide performance 
;;; improvements.
;;;
;;; There is enough *explicit* error checking that *all* internal operations
;;; should *never* produce a type or index-range error. Period. Feel like
;;; living dangerously? *Big* performance win to be had by replacing string
;;; and record-field accessors and setters with unsafe equivalents in the
;;; code. Similarly, fixnum-specific operators can speed up the arithmetic
;;; done on the index values in the inner loops. The only arguments that are
;;; not completely error checked are
;;;   - string lists (complete checking requires time proportional to the
;;;     length of the list)
;;;   - procedure arguments, such as char->char maps & predicates.
;;;     There is no way to check the range & domain of procedures in Scheme.
;;; Procedures that take these parameters cannot fully check their
;;; arguments. But all other types to all other procedures are fully
;;; checked.
;;;
;;; This does open up the alternate possibility of simply *removing* these 
;;; checks, and letting the safe primitives raise the errors. On a dumb
;;; Scheme system, this would provide speed (by eliminating the redundant
;;; error checks) at the cost of error-message clarity.
;;;
;;; In an interpreted Scheme, some of these procedures, or the internal
;;; routines with % prefixes, are excellent candidates for being rewritten
;;; in C.
;;;
;;; It would also be nice to have the ability to mark some of these
;;; routines as candidates for inlining/integration.
;;; 
;;; See the comments preceding the hash function code for notes on tuning
;;; the default bound so that the code never overflows your implementation's
;;; fixnum size into bignum calculation.
;;;
;;; All the %-prefixed routines in this source code are written
;;; to be called internally to this library. They do *not* perform
;;; friendly error checks on the inputs; they assume everything is
;;; proper. They also do not take optional arguments. These two properties
;;; save calling overhead and enable procedure integration -- but they
;;; are not appropriate for exported routines.

;;; Copyright notice
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Copyright (c) 1988-1995 Massachusetts Institute of Technology
;;; 
;;; This material was developed by the Scheme project at the Massachusetts
;;; Institute of Technology, Department of Electrical Engineering and
;;; Computer Science.  Permission to copy and modify this software, to
;;; redistribute either the original software or a modified version, and
;;; to use this software for any purpose is granted, subject to the
;;; following restrictions and understandings.
;;; 
;;; 1. Any copy made of this software must include this copyright notice
;;; in full.
;;; 
;;; 2. Users of this software agree to make their best efforts (a) to
;;; return to the MIT Scheme project any improvements or extensions that
;;; they make, so that these may be included in future releases; and (b)
;;; to inform MIT of noteworthy uses of this software.
;;; 
;;; 3. All materials developed as a consequence of the use of this
;;; software shall duly acknowledge such use, in accordance with the usual
;;; standards of acknowledging credit in academic research.
;;; 
;;; 4. MIT has made no warrantee or representation that the operation of
;;; this software will be error-free, and MIT is under no obligation to
;;; provide any services, by way of maintenance, update, or otherwise.
;;; 
;;; 5. In conjunction with products arising from the use of this material,
;;; there shall be no use of the name of the Massachusetts Institute of
;;; Technology nor of any adaptation thereof in any advertising,
;;; promotional, or sales literature without prior written consent from
;;; MIT in each case.

Added srfi/s16/case-lambda.sls.



























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s16 case-lambda)
  (export 
    case-lambda)
  (import 
    (only (rnrs control) case-lambda))
)

Added srfi/s19/srfi-19.scm.









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
;; SRFI-19: Time Data Types and Procedures.
;;
;; Modified by Derick Eddington to be included into the (srfi :19 time) R6RS library.
;; TODO: For implementations which have threads, 
;;       the thread timing stuff can probably be made to work.
;; 
;; Copyright (C) I/NET, Inc. (2000, 2002, 2003). All Rights Reserved. 
;; 
;; This document and translations of it may be copied and furnished to others, 
;; and derivative works that comment on or otherwise explain it or assist in its 
;; implementation may be prepared, copied, published and distributed, in whole or 
;; in part, without restriction of any kind, provided that the above copyright 
;; notice and this paragraph are included on all such copies and derivative works. 
;; However, this document itself may not be modified in any way, such as by 
;; removing the copyright notice or references to the Scheme Request For 
;; Implementation process or editors, except as needed for the purpose of 
;; developing SRFIs in which case the procedures for copyrights defined in the SRFI 
;; process must be followed, or as required to translate it into languages other 
;; than English. 
;; 
;; The limited permissions granted above are perpetual and will not be revoked 
;; by the authors or their successors or assigns. 
;; 
;; This document and the information contained herein is provided on an "AS IS" 
;; basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR 
;; IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE 
;; INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF 
;; MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 


;; -- Bug fixes.
;;
;; MAKE-TIME had parameters seconds and nanoseconds reversed; change all
;;           references in file to match.  Will F: 2002-10-15
;;
;; DATE-YEAR-DAY returned the wrong day; tm:year-day fixed to do the right
;;               thing. Will F: 2002-10-15
;;               It also called an undefined error procedure.
;;
;; DISPLAYING procedure removed. Will F: 2002-10-15.
;;
;; TM:NANO constant corrected. 2002-11-04.
;;
;; The following fixes by Will Fitzgerald, February, 2003.
;;  -- Thanks to Steven Ma and others.
;;
;; (CURRENT-TIME 'TIME-THREAD) added.
;;
;; TIME-RESOLUTION for TIME-PROCESS added. 
;;
;; TIME comparison procedures (time=?, etc. fixed. 
;;
;; Corrected errors in converting between TAI and UTC time.
;;
;; TAI and UTC date converters no longer look at leap seconds,
;; which was an error.
;;
;; corrections to calls to tm:time-error
;;
;; timezone offset not used in date->time-utc and date->julian-day
;;
;; typos in tm:integer-reader-exact, tm:string->date,
;; time-monotonic->time-utc!, tm:char->int fixed
;;
;; corrected "~k", "~f" formatting for date->string (includes fix for
;; "~4"
;;
;; 'split-real' fixed.
;;
;; fixed julian-day->time-utc and variants.
;;
;; changes 2003-02-26, based on comments by Martin Gasbichler.
;; 
;; moronic, overly complicated COPY-TIME procedure changed
;; to simple version suggested by Martin Gasbichler.
;;
;; To provide more portability, changed #\Space to #\space
;; and #\tab to #\Tab to (integer->char 9)
;;
;; changed arity-3 calls to / and - to arity 2 calls (again,
;; for more general portability). 
;;
;; split-real fixed again -- by removing it, and using
;; 'fractional part'. Will Fitzgerald 5/16/2003.
;; --------------------------------------------------------------

(define-syntax receive
  (syntax-rules ()
    ((receive formals expression body ...)
     (call-with-values (lambda () expression)
                       (lambda formals body ...)))))

;;; -- we want receive later on for a couple of small things
;; 

;; :OPTIONAL is nice, too

(define-syntax :optional
  (syntax-rules ()
    ((_ val default-value)
     (if (null? val) default-value (car val)))))

(define time-tai 'time-tai)
(define time-utc 'time-utc)
(define time-monotonic 'time-monotonic)
#|(define time-thread 'time-thread)
(define time-process 'time-process)|#
(define time-duration 'time-duration)

;; example of extension (MZScheme specific)
;(define time-gc 'time-gc)

;;-- LOCALE dependent constants

(define tm:locale-number-separator ".")

(define tm:locale-abbr-weekday-vector (vector "Sun" "Mon" "Tue" "Wed"
                                              "Thu" "Fri" "Sat")) 
(define tm:locale-long-weekday-vector (vector "Sunday" "Monday"
                                              "Tuesday" "Wednesday"
                                              "Thursday" "Friday"
                                              "Saturday"))
;; note empty string in 0th place. 
(define tm:locale-abbr-month-vector   (vector "" "Jan" "Feb" "Mar"
                                              "Apr" "May" "Jun" "Jul"
                                              "Aug" "Sep" "Oct" "Nov"
                                              "Dec")) 
(define tm:locale-long-month-vector   (vector "" "January" "February"
                                              "March" "April" "May"
                                              "June" "July" "August"
                                              "September" "October"
                                              "November" "December")) 

(define tm:locale-pm "PM")
(define tm:locale-am "AM")

;; See date->string
(define tm:locale-date-time-format "~a ~b ~d ~H:~M:~S~z ~Y")
(define tm:locale-short-date-format "~m/~d/~y")
(define tm:locale-time-format "~H:~M:~S")
(define tm:iso-8601-date-time-format "~Y-~m-~dT~H:~M:~S~z")
;;-- Miscellaneous Constants.
;;-- only the tm:tai-epoch-in-jd might need changing if
;;   a different epoch is used.

(define tm:nano (expt 10 9))
(define tm:sid  86400)    ; seconds in a day
(define tm:sihd 43200)    ; seconds in a half day
(define tm:tai-epoch-in-jd 4881175/2) ; julian day number for 'the epoch'


;;; A Very simple Error system for the time procedures
;;; 
(define tm:time-error-types
  '((invalid-clock-type . "invalid clock type")
    (unsupported-clock-type . "unsupported clock type")
    (incompatible-time-types . "incompatible time types")
    (not-duration . "not duration")
    (dates-are-immutable . "dates are immutable")
    (bad-date-format-string . "bad date format string")
    (bad-date-template-string . "bad date template string")
    (invalid-month-specification . "invalid month specification")
    ))

(define (tm:time-error caller type value)
  (cond 
    [(assoc type tm:time-error-types)
     =>
     (lambda (p)
       (if value
         (error caller (cdr p) value)
         (error caller (cdr p))))]
    [else
     (error caller "(library (srfi :19 time)) internal error: unsupported error type" type)]))


;; A table of leap seconds
;; See ftp://maia.usno.navy.mil/ser7/tai-utc.dat
;; and update as necessary.
;; this procedures reads the file in the abover
;; format and creates the leap second table
;; it also calls the almost standard, but not R5 procedures read-line 
;; & open-input-string
;; ie (set! tm:leap-second-table (tm:read-tai-utc-date "tai-utc.dat"))

(define (tm:read-tai-utc-data filename)
  (define (convert-jd jd)
    (* (- (inexact->exact jd) tm:tai-epoch-in-jd) tm:sid))
  (define (convert-sec sec)
    (inexact->exact sec))
  (let ( (port (open-input-file filename))
	 (table '()) )
    (let loop ((line (read-line port)))
      (if (not (eq? line eof))
	  (begin
	    (let* ( (data (read (open-input-string (string-append "(" line ")")))) 
		    (year (car data))
		    (jd   (cadddr (cdr data)))
		    (secs (cadddr (cdddr data))) )
	      (if (>= year 1972)
		  (set! table (cons (cons (convert-jd jd) (convert-sec secs)) table)))
	      (loop (read-line port))))))
    table))

;; each entry is ( utc seconds since epoch . # seconds to add for tai )
;; note they go higher to lower, and end in 1972.
(define tm:leap-second-table
  '((1136073600 . 33)
    (915148800 . 32)
    (867715200 . 31)
    (820454400 . 30)
    (773020800 . 29)
    (741484800 . 28)
    (709948800 . 27)
    (662688000 . 26)
    (631152000 . 25)
    (567993600 . 24)
    (489024000 . 23)
    (425865600 . 22)
    (394329600 . 21)
    (362793600 . 20)
    (315532800 . 19)
    (283996800 . 18)
    (252460800 . 17)
    (220924800 . 16)
    (189302400 . 15)
    (157766400 . 14)
    (126230400 . 13)
    (94694400 . 12)
    (78796800 . 11)
    (63072000 . 10)))

(define (read-leap-second-table filename)
  (set! tm:leap-second-table (tm:read-tai-utc-data filename))
  (values))


(define (tm:leap-second-delta utc-seconds)
  (letrec ( (lsd (lambda (table) 
		   (cond
		    ((>= utc-seconds (caar table))
		     (cdar table))
		    (else (lsd (cdr table)))))) )
    (if (< utc-seconds  (* (- 1972 1970) 365 tm:sid)) 0
	(lsd  tm:leap-second-table))))

;; going from tai seconds to utc seconds ... 
(define (tm:leap-second-neg-delta tai-seconds)
  (letrec ( (lsd (lambda (table)
		   (cond ((null? table) 0)
			 ((<= (cdar table) (- tai-seconds (caar table)))
			  (cdar table))
			 (else (lsd (cdr table)))))) )
    (if (< tai-seconds  (* (- 1972 1970) 365 tm:sid)) 0
	(lsd  tm:leap-second-table))))


;;; the time structure; creates the accessors, too.
;;; wf: changed to match srfi documentation.

(define-record-type time 
  (fields 
    (mutable type) 
    (mutable nanosecond)
    (mutable second)))

;; thanks, Martin Gasbichler ...

(define (copy-time time)
  (make-time (time-type time)
	     (time-nanosecond time)  ; original had this mistakenly swapped with time-second
	     (time-second time)))


;;; current-time

;;; specific time getters.

;; I'm not sure why the original was using time-nanoseconds
;; as 10000 * the milliseconds

(define (tm:get-time-of-day)
  (let ([ct (host:current-time)])
    (values (host:time-second ct)
            (host:time-nanosecond ct))))

(define (tm:current-time-utc)
  (receive (seconds nanos) (tm:get-time-of-day)
	   (make-time time-utc nanos seconds)))

(define (tm:current-time-tai)
  (receive (seconds nanos) (tm:get-time-of-day)
	   (make-time time-tai
		      nanos
		      (+ seconds (tm:leap-second-delta seconds)) )))

#|(define (tm:current-time-ms-time time-type proc)
  (let ((current-ms (proc)))
    (make-time time-type 
	       XXX
	       ZZZ
	       ))) |#

;; -- we define it to be the same as tai.
;;    a different implemation of current-time-montonic
;;    will require rewriting all of the time-monotonic converters,
;;    of course.

(define (tm:current-time-monotonic)
  (receive (seconds nanos) (tm:get-time-of-day)
	   (make-time time-monotonic
		      nanos
		      (+ seconds (tm:leap-second-delta seconds)) )))


#|(define (tm:current-time-thread)
  (tm:current-time-ms-time time-process current-process-milliseconds))

(define (tm:current-time-process)
  (tm:current-time-ms-time time-process current-process-milliseconds))

(define (tm:current-time-gc)
  (tm:current-time-ms-time time-gc current-gc-milliseconds)) |#

(define (current-time . clock-type)
  (let ( (clock-type (:optional clock-type time-utc)) )
    (cond
      ((eq? clock-type time-tai) (tm:current-time-tai))
      ((eq? clock-type time-utc) (tm:current-time-utc))
      ((eq? clock-type time-monotonic) (tm:current-time-monotonic))
      #|((eq? clock-type time-thread) (tm:current-time-thread))
      ((eq? clock-type time-process) (tm:current-time-process))
      ((eq? clock-type time-gc) (tm:current-time-gc))|#
      (else (tm:time-error 'current-time 'invalid-clock-type clock-type)))))


;; -- time resolution
;; this is the resolution of the clock in nanoseconds.
;; this will be implementation specific.
(define (time-resolution . clock-type)
  (let ((clock-type (:optional clock-type time-utc)))
    (cond
      ((eq? clock-type time-tai) host:time-resolution)
      ((eq? clock-type time-utc) host:time-resolution)
      ((eq? clock-type time-monotonic) host:time-resolution)
      #|((eq? clock-type time-thread) host:time-resolution)
      ((eq? clock-type time-process) host:time-resolution)
      ((eq? clock-type time-gc) host:time-resolution)|#
      (else (tm:time-error 'time-resolution 'invalid-clock-type clock-type)))))

;; -- time comparisons

(define (tm:time-compare-check time1 time2 caller)
  (if (or (not (and (time? time1) (time? time2)))
	  (not (eq? (time-type time1) (time-type time2))))
      (tm:time-error caller 'incompatible-time-types #f)
      #t))

(define (time=? time1 time2)
  (tm:time-compare-check time1 time2 'time=?)
  (and (= (time-second time1) (time-second time2))
       (= (time-nanosecond time1) (time-nanosecond time2))))

(define (time>? time1 time2)
  (tm:time-compare-check time1 time2 'time>?)
  (or (> (time-second time1) (time-second time2))
      (and (= (time-second time1) (time-second time2))
	   (> (time-nanosecond time1) (time-nanosecond time2)))))

(define (time<? time1 time2)
  (tm:time-compare-check time1 time2 'time<?)
  (or (< (time-second time1) (time-second time2))
      (and (= (time-second time1) (time-second time2))
	   (< (time-nanosecond time1) (time-nanosecond time2)))))

(define (time>=? time1 time2)
  (tm:time-compare-check time1 time2 'time>=?)
  (or (>= (time-second time1) (time-second time2))
      (and (= (time-second time1) (time-second time2))
	   (>= (time-nanosecond time1) (time-nanosecond time2)))))

(define (time<=? time1 time2)
  (tm:time-compare-check time1 time2 'time<=?)
  (or (<= (time-second time1) (time-second time2))
      (and (= (time-second time1) (time-second time2))
	   (<= (time-nanosecond time1) (time-nanosecond time2)))))

;; -- time arithmetic

(define (tm:time->nanoseconds time)
  #|(define (sign1 n)   ; must be code rot
    (if (negative? n) -1 1))|#
  (+ (* (time-second time) tm:nano)
     (time-nanosecond time)))

(define (tm:nanoseconds->time time-type nanoseconds)
  (make-time time-type
             (remainder nanoseconds tm:nano)
             (quotient nanoseconds tm:nano)))

(define (tm:nanoseconds->values nanoseconds)
  (div-and-mod nanoseconds tm:nano))

(define (tm:time-difference time1 time2 time3)
  (if (or (not (and (time? time1) (time? time2)))
	  (not (eq? (time-type time1) (time-type time2))))
      (tm:time-error 'time-difference 'incompatible-time-types #f))
  (time-type-set! time3 time-duration)
  (if (time=? time1 time2)
      (begin
	(time-second-set! time3 0)
	(time-nanosecond-set! time3 0))
      (receive 
       (secs nanos)
       (tm:nanoseconds->values (- (tm:time->nanoseconds time1)
                                  (tm:time->nanoseconds time2)))
       (time-second-set! time3 secs)
       (time-nanosecond-set! time3 nanos)))
  time3)

(define (time-difference time1 time2)
  (tm:time-difference time1 time2 (make-time #f #f #f)))

(define (time-difference! time1 time2)
  (tm:time-difference time1 time2 time1))

(define (tm:add-duration time1 duration time3)
  (if (not (and (time? time1) (time? duration)))
      (tm:time-error 'add-duration 'incompatible-time-types #f))
  (if (not (eq? (time-type duration) time-duration))
      (tm:time-error 'add-duration 'not-duration duration)
      (let ( (sec-plus (+ (time-second time1) (time-second duration)))
	     (nsec-plus (+ (time-nanosecond time1) (time-nanosecond duration))) )
	(let ((r (remainder nsec-plus tm:nano))
	      (q (quotient nsec-plus tm:nano)))
          ; (time-type-set! time3 (time-type time1))
	  (if (negative? r)
	      (begin
		(time-second-set! time3 (+ sec-plus q -1))
		(time-nanosecond-set! time3 (+ tm:nano r)))
	      (begin
		(time-second-set! time3 (+ sec-plus q))
		(time-nanosecond-set! time3 r)))
	  time3))))

(define (add-duration time1 duration)
  (tm:add-duration time1 duration (make-time (time-type time1) #f #f)))

(define (add-duration! time1 duration)
  (tm:add-duration time1 duration time1))

(define (tm:subtract-duration time1 duration time3)
  (if (not (and (time? time1) (time? duration)))
      (tm:time-error 'add-duration 'incompatible-time-types #f))
  (if (not (eq? (time-type duration) time-duration))
      (tm:time-error 'tm:subtract-duration 'not-duration duration)
      (let ( (sec-minus  (- (time-second time1) (time-second duration)))
	     (nsec-minus (- (time-nanosecond time1) (time-nanosecond duration))) )
	(let ((r (remainder nsec-minus tm:nano))
	      (q (quotient nsec-minus tm:nano)))
	  (if (negative? r)
	      (begin
		(time-second-set! time3 (- sec-minus q 1))
		(time-nanosecond-set! time3 (+ tm:nano r)))
	      (begin
		(time-second-set! time3 (- sec-minus q))
		(time-nanosecond-set! time3 r)))
	  time3))))

(define (subtract-duration time1 duration)
  (tm:subtract-duration time1 duration (make-time (time-type time1) #f #f)))

(define (subtract-duration! time1 duration)
  (tm:subtract-duration time1 duration time1))


;; -- converters between types.

(define (tm:time-tai->time-utc! time-in time-out caller)
  (if (not (eq? (time-type time-in) time-tai))
      (tm:time-error caller 'incompatible-time-types time-in))
  (time-type-set! time-out time-utc)
  (time-nanosecond-set! time-out (time-nanosecond time-in))
  (time-second-set!     time-out (- (time-second time-in)
				    (tm:leap-second-neg-delta 
				     (time-second time-in))))
  time-out)

(define (time-tai->time-utc time-in)
  (tm:time-tai->time-utc! time-in (make-time #f #f #f) 'time-tai->time-utc))


(define (time-tai->time-utc! time-in)
  (tm:time-tai->time-utc! time-in time-in 'time-tai->time-utc!))


(define (tm:time-utc->time-tai! time-in time-out caller)
  (if (not (eq? (time-type time-in) time-utc))
      (tm:time-error caller 'incompatible-time-types time-in))
  (time-type-set! time-out time-tai)
  (time-nanosecond-set! time-out (time-nanosecond time-in))
  (time-second-set!     time-out (+ (time-second time-in)
				    (tm:leap-second-delta 
				     (time-second time-in))))
  time-out)


(define (time-utc->time-tai time-in)
  (tm:time-utc->time-tai! time-in (make-time #f #f #f) 'time-utc->time-tai))

(define (time-utc->time-tai! time-in)
  (tm:time-utc->time-tai! time-in time-in 'time-utc->time-tai!))

;; -- these depend on time-monotonic having the same definition as time-tai!
(define (time-monotonic->time-utc time-in)
  (if (not (eq? (time-type time-in) time-monotonic))
      (tm:time-error 'time-monotoinc->time-utc 'incompatible-time-types time-in))
  (let ((ntime (copy-time time-in)))
    (time-type-set! ntime time-tai)
    (tm:time-tai->time-utc! ntime ntime 'time-monotonic->time-utc)))

(define (time-monotonic->time-utc! time-in)
  (if (not (eq? (time-type time-in) time-monotonic))
      (tm:time-error 'time-monotonic->time-utc! 'incompatible-time-types time-in))
  (time-type-set! time-in time-tai)
  (tm:time-tai->time-utc! time-in time-in 'time-monotonic->time-utc))

(define (time-monotonic->time-tai time-in)
  (if (not (eq? (time-type time-in) time-monotonic))
      (tm:time-error 'time-monotonic->time-tai 'incompatible-time-types time-in))
  (let ((ntime (copy-time time-in)))
    (time-type-set! ntime time-tai)
    ntime))

(define (time-monotonic->time-tai! time-in)
  (if (not (eq? (time-type time-in) time-monotonic))
      (tm:time-error 'time-monotonic->time-tai! 'incompatible-time-types time-in))
  (time-type-set! time-in time-tai)
  time-in)

(define (time-utc->time-monotonic time-in)
  (if (not (eq? (time-type time-in) time-utc))
      (tm:time-error 'time-utc->time-monotonic 'incompatible-time-types time-in))
  (let ((ntime (tm:time-utc->time-tai! time-in (make-time #f #f #f)
				       'time-utc->time-monotonic)))
    (time-type-set! ntime time-monotonic)
    ntime))


(define (time-utc->time-monotonic! time-in)
  (if (not (eq? (time-type time-in) time-utc))
      (tm:time-error 'time-utc->time-montonic! 'incompatible-time-types time-in))
  (let ((ntime (tm:time-utc->time-tai! time-in time-in
				       'time-utc->time-monotonic!)))
    (time-type-set! ntime time-monotonic)
    ntime))


(define (time-tai->time-monotonic time-in)
  (if (not (eq? (time-type time-in) time-tai))
      (tm:time-error 'time-tai->time-monotonic 'incompatible-time-types time-in))
  (let ((ntime (copy-time time-in)))
    (time-type-set! ntime time-monotonic)
    ntime))

(define (time-tai->time-monotonic! time-in)
  (if (not (eq? (time-type time-in) time-tai))
      (tm:time-error 'time-tai->time-monotonic!  'incompatible-time-types time-in))
  (time-type-set! time-in time-monotonic)
  time-in)


;; -- date structures

(define-record-type date 
  (fields
    (mutable nanosecond)
    (mutable second)
    (mutable minute)
    (mutable hour)
    (mutable day)
    (mutable month)
    (mutable year)
    (mutable zone-offset)))

;; redefine setters (in Ikarus version, only to keep names the same in below code)
(define tm:set-date-nanosecond! date-nanosecond-set!)
(define tm:set-date-second! date-second-set!)
(define tm:set-date-minute! date-minute-set!)
(define tm:set-date-hour! date-hour-set!)
(define tm:set-date-day! date-day-set!)
(define tm:set-date-month! date-month-set!)
(define tm:set-date-year! date-year-set!)
(define tm:set-date-zone-offset! date-zone-offset-set!)

;; gives the julian day which starts at noon.
(define (tm:encode-julian-day-number day month year)
  (let* ((a (quotient (- 14 month) 12))
	 (y (- (- (+ year 4800) a) (if (negative? year) -1 0)))
	 (m (- (+ month (* 12 a)) 3)))
    (+ day
       (quotient (+ (* 153 m) 2) 5)
       (* 365 y)
       (quotient y 4)
       (- (quotient y 100))
       (quotient y 400)
       -32045)))

(define (tm:char-pos char str index len)
  (cond
   ((>= index len) #f)
   ((char=? (string-ref str index) char)
    index)
   (else
    (tm:char-pos char str (+ index 1) len))))
  

(define (tm:fractional-part r)
  (if (integer? r) "0"
      (let ((str (number->string (exact->inexact r))))
	(let ((ppos (tm:char-pos #\. str 0 (string-length str))))
	  (substring str  (+ ppos 1) (string-length str))))))


;; gives the seconds/date/month/year 
(define (tm:decode-julian-day-number jdn)
  (let* ((days (truncate jdn))
	 (a (+ days 32044))
	 (b (quotient (+ (* 4 a) 3) 146097))
	 (c (- a (quotient (* 146097 b) 4)))
	 (d (quotient (+ (* 4 c) 3) 1461))
	 (e (- c (quotient (* 1461 d) 4)))
	 (m (quotient (+ (* 5 e) 2) 153))
	 (y (+ (* 100 b) d -4800 (quotient m 10))))
    (values ; seconds date month year
     (* (- jdn days) tm:sid)
     (+ e (- (quotient (+ (* 153 m) 2) 5)) 1)
     (+ m 3 (* -12 (quotient m 10)))
     (if (>= 0 y) (- y 1) y))
    ))


(define (tm:local-tz-offset)
  (host:time-gmt-offset (host:current-time)))

;; special thing -- ignores nanos
(define (tm:time->julian-day-number seconds tz-offset)
  (+ (/ (+ seconds
	   tz-offset
	   tm:sihd)
	tm:sid)
     tm:tai-epoch-in-jd))

(define (tm:find proc l)
  (if (null? l)
      #f
      (if (proc (car l))
	  #t
	  (tm:find proc (cdr l)))))

(define (tm:tai-before-leap-second? second)
  (tm:find (lambda (x)
	     (= second (- (+ (car x) (cdr x)) 1)))
	   tm:leap-second-table))

(define (tm:time->date time tz-offset ttype)
  (if (not (eq? (time-type time) ttype))
      (tm:time-error 'time->date 'incompatible-time-types  time))
  (let* ( (offset (:optional tz-offset (tm:local-tz-offset))) )
    (receive (secs date month year)
	     (tm:decode-julian-day-number
	      (tm:time->julian-day-number (time-second time) offset))
	     (let* ( (hours    (quotient secs (* 60 60)))
		     (rem      (remainder secs (* 60 60)))
		     (minutes  (quotient rem 60))
		     (seconds  (remainder rem 60)) )
	       (make-date (time-nanosecond time)
			  seconds
			  minutes
			  hours
			  date
			  month
			  year
			  offset)))))

(define (time-tai->date time . tz-offset)
  (if (tm:tai-before-leap-second? (time-second time))
      ;; if it's *right* before the leap, we need to pretend to subtract a second ...
      (let ((d (tm:time->date (subtract-duration! (time-tai->time-utc time) (make-time time-duration 0 1)) tz-offset time-utc)))
	(tm:set-date-second! d 60)
	d)
      (tm:time->date (time-tai->time-utc time) tz-offset time-utc)))

(define (time-utc->date time . tz-offset)
  (tm:time->date time tz-offset time-utc))

;; again, time-monotonic is the same as time tai
(define (time-monotonic->date time . tz-offset)
  (tm:time->date time tz-offset time-monotonic))

(define (date->time-utc date)
  (let ( (nanosecond (date-nanosecond date))
	 (second (date-second date))
	 (minute (date-minute date))
	 (hour (date-hour date))
	 (day (date-day date))
	 (month (date-month date))
	 (year (date-year date))
	 (offset (date-zone-offset date)) )
    (let ( (jdays (- (tm:encode-julian-day-number day month year)
		     tm:tai-epoch-in-jd)) )
      (make-time 
       time-utc
       nanosecond
       (+ (* (- jdays 1/2) 24 60 60)
	  (* hour 60 60)
	  (* minute 60)
	  second
	  (- offset))
       ))))

(define (date->time-tai d)
  (if (= (date-second d) 60)
      (subtract-duration! (time-utc->time-tai! (date->time-utc d)) (make-time time-duration 0 1))
      (time-utc->time-tai! (date->time-utc d))))

(define (date->time-monotonic date)
  (time-utc->time-monotonic! (date->time-utc date)))


(define (tm:leap-year? year)
  (or (= (modulo year 400) 0)
      (and (= (modulo year 4) 0) (not (= (modulo year 100) 0)))))

(define (leap-year? date)
  (tm:leap-year? (date-year date)))

;; tm:year-day fixed: adding wrong number of days.
(define  tm:month-assoc '((0 . 0) (1 . 31)  (2 . 59)   (3 . 90)   (4 . 120) 
			  (5 . 151) (6 . 181)  (7 . 212)  (8 . 243)
			  (9 . 273) (10 . 304) (11 . 334)))

(define (tm:year-day day month year)
  (let ((days-pr (assoc (- month 1) tm:month-assoc)))
    (if (not days-pr)
	(tm:time-error 'date-year-day 'invalid-month-specification month))
    (if (and (tm:leap-year? year) (> month 2))
	(+ day (cdr days-pr) 1)
	(+ day (cdr days-pr)))))

(define (date-year-day date)
  (tm:year-day (date-day date) (date-month date) (date-year date)))

;; from calendar faq 
(define (tm:week-day day month year)
  (let* ((a (quotient (- 14 month) 12))
	 (y (- year a))
	 (m (+ month (* 12 a) -2)))
    (modulo (+ day y (quotient y 4) (- (quotient y 100))
	       (quotient y 400) (quotient (* 31 m) 12))
	    7)))

(define (date-week-day date)
  (tm:week-day (date-day date) (date-month date) (date-year date)))

(define (tm:days-before-first-week date day-of-week-starting-week)
  (let* ( (first-day (make-date 0 0 0 0
                                1
                                1
                                (date-year date)
                                #f))
          (fdweek-day (date-week-day first-day))  )
    (modulo (- day-of-week-starting-week fdweek-day)
            7)))

(define (date-week-number date day-of-week-starting-week)
  (quotient (- (date-year-day date)
	       (tm:days-before-first-week  date day-of-week-starting-week))
	    7))

(define (current-date . tz-offset) 
  (time-utc->date (current-time time-utc)
		  (:optional tz-offset (tm:local-tz-offset))))

;; given a 'two digit' number, find the year within 50 years +/-
(define (tm:natural-year n)
  (let* ( (current-year (date-year (current-date)))
	  (current-century (* (quotient current-year 100) 100)) )
    (cond
      ((>= n 100) n)
      ((<  n 0) n)
      ((<=  (- (+ current-century n) current-year) 50)
       (+ current-century n))
      (else
       (+ (- current-century 100) n)))))

(define (date->julian-day date)
  (let ( (nanosecond (date-nanosecond date))
	 (second (date-second date))
	 (minute (date-minute date))
	 (hour (date-hour date))
	 (day (date-day date))
	 (month (date-month date))
	 (year (date-year date))
	 (offset (date-zone-offset date)) )
    (+ (tm:encode-julian-day-number day month year)
       (- 1/2)
       (+ (/ (+ (* hour 60 60)
                (* minute 60)
                second
                (/ nanosecond tm:nano)
                (- offset))
             tm:sid)))))

(define (date->modified-julian-day date)
  (- (date->julian-day date)
     4800001/2))


(define (time-utc->julian-day time)
  (if (not (eq? (time-type time) time-utc))
      (tm:time-error 'time-utc->julian-day 'incompatible-time-types  time))
  (+ (/ (+ (time-second time) (/ (time-nanosecond time) tm:nano))
	tm:sid)
     tm:tai-epoch-in-jd))

(define (time-utc->modified-julian-day time)
  (- (time-utc->julian-day time)
     4800001/2))

(define (time-tai->julian-day time)
  (if (not (eq? (time-type time) time-tai))
      (tm:time-error 'time-tai->julian-day 'incompatible-time-types  time))
  (+ (/ (+ (- (time-second time) 
	      (tm:leap-second-delta (time-second time)))
	   (/ (time-nanosecond time) tm:nano))
	tm:sid)
     tm:tai-epoch-in-jd))

(define (time-tai->modified-julian-day time)
  (- (time-tai->julian-day time)
     4800001/2))

;; this is the same as time-tai->julian-day
(define (time-monotonic->julian-day time)
  (if (not (eq? (time-type time) time-monotonic))
      (tm:time-error 'time-monotonic->julian-day 'incompatible-time-types  time))
  (+ (/ (+ (- (time-second time) 
	      (tm:leap-second-delta (time-second time)))
	   (/ (time-nanosecond time) tm:nano))
	tm:sid)
     tm:tai-epoch-in-jd))


(define (time-monotonic->modified-julian-day time)
  (- (time-monotonic->julian-day time)
     4800001/2))


(define (julian-day->time-utc jdn)
  (let ( (nanosecs (* tm:nano tm:sid (- jdn tm:tai-epoch-in-jd))) )
    (make-time time-utc
	       (remainder nanosecs tm:nano)
	       (floor (/ nanosecs tm:nano)))))

(define (julian-day->time-tai jdn)
  (time-utc->time-tai! (julian-day->time-utc jdn)))

(define (julian-day->time-monotonic jdn)
  (time-utc->time-monotonic! (julian-day->time-utc jdn)))

(define (julian-day->date jdn . tz-offset)
  (let ((offset (:optional tz-offset (tm:local-tz-offset))))
    (time-utc->date (julian-day->time-utc jdn) offset)))

(define (modified-julian-day->date jdn . tz-offset)
  (let ((offset (:optional tz-offset (tm:local-tz-offset))))
    (julian-day->date (+ jdn 4800001/2) offset)))

(define (modified-julian-day->time-utc jdn)
  (julian-day->time-utc (+ jdn 4800001/2)))

(define (modified-julian-day->time-tai jdn)
  (julian-day->time-tai (+ jdn 4800001/2)))

(define (modified-julian-day->time-monotonic jdn)
  (julian-day->time-monotonic (+ jdn 4800001/2)))

(define (current-julian-day)
  (time-utc->julian-day (current-time time-utc)))

(define (current-modified-julian-day)
  (time-utc->modified-julian-day (current-time time-utc)))

;; returns a string rep. of number N, of minimum LENGTH,
;; padded with character PAD-WITH. If PAD-WITH if #f, 
;; no padding is done, and it's as if number->string was used.
;; if string is longer than LENGTH, it's as if number->string was used.

(define (tm:padding n pad-with length)
  (let* ( (str (number->string n))
	  (str-len (string-length str)) )
    (if (or (> str-len length)
            (not pad-with))
	str
	(let* ( (new-str (make-string length pad-with))
		(new-str-offset (- (string-length new-str)
				   str-len)) )
	  (do ((i 0 (+ i 1)))
            ((>= i (string-length str)))
            (string-set! new-str (+ new-str-offset i) 
                         (string-ref str i)))
	  new-str))))

(define (tm:last-n-digits i n)
  (abs (remainder i (expt 10 n))))

(define (tm:locale-abbr-weekday n) 
  (vector-ref tm:locale-abbr-weekday-vector n))

(define (tm:locale-long-weekday n)
  (vector-ref tm:locale-long-weekday-vector n))

(define (tm:locale-abbr-month n)
  (vector-ref tm:locale-abbr-month-vector n))

(define (tm:locale-long-month n)
  (vector-ref tm:locale-long-month-vector n))

(define (tm:vector-find needle haystack comparator)
  (let ((len (vector-length haystack)))
    (define (tm:vector-find-int index)
      (cond
        ((>= index len) #f)
        ((comparator needle (vector-ref haystack index)) index)
        (else (tm:vector-find-int (+ index 1)))))
    (tm:vector-find-int 0)))

(define (tm:locale-abbr-weekday->index string)
  (tm:vector-find string tm:locale-abbr-weekday-vector string=?))

(define (tm:locale-long-weekday->index string)
  (tm:vector-find string tm:locale-long-weekday-vector string=?))

(define (tm:locale-abbr-month->index string)
  (tm:vector-find string tm:locale-abbr-month-vector string=?))

(define (tm:locale-long-month->index string)
  (tm:vector-find string tm:locale-long-month-vector string=?))



;; do nothing. 
;; Your implementation might want to do something...
;; 
(define (tm:locale-print-time-zone date port)
  (values))

;; Again, locale specific.
(define (tm:locale-am/pm hr)
  (if (> hr 11) tm:locale-pm tm:locale-am))

(define (tm:tz-printer offset port)
  (cond
    ((= offset 0) (display "Z" port))
    ((negative? offset) (display "-" port))
    (else (display "+" port)))
  (if (not (= offset 0))
      (let ( (hours   (abs (quotient offset (* 60 60))))
	     (minutes (abs (quotient (remainder offset (* 60 60)) 60))) )
	(display (tm:padding hours #\0 2) port)
	(display (tm:padding minutes #\0 2) port))))

;; A table of output formatting directives.
;; the first time is the format char.
;; the second is a procedure that takes the date, a padding character
;; (which might be #f), and the output port.
;;
(define tm:directives 
  (list
   (cons #\~ (lambda (date pad-with port) (display #\~ port)))
   
   (cons #\a (lambda (date pad-with port)
	       (display (tm:locale-abbr-weekday (date-week-day date))
			port)))
   (cons #\A (lambda (date pad-with port)
	       (display (tm:locale-long-weekday (date-week-day date))
			port)))
   (cons #\b (lambda (date pad-with port)
	       (display (tm:locale-abbr-month (date-month date))
			port)))
   (cons #\B (lambda (date pad-with port)
	       (display (tm:locale-long-month (date-month date))
			port)))
   (cons #\c (lambda (date pad-with port)
	       (display (date->string date tm:locale-date-time-format) port)))
   (cons #\d (lambda (date pad-with port)
	       (display (tm:padding (date-day date)
				    #\0 2)
                        port)))
   (cons #\D (lambda (date pad-with port)
	       (display (date->string date "~m/~d/~y") port)))
   (cons #\e (lambda (date pad-with port)
	       (display (tm:padding (date-day date)
				    #\space 2)
			port)))
   (cons #\f (lambda (date pad-with port)
	       (if (> (date-nanosecond date)
		      tm:nano)
		   (display (tm:padding (+ (date-second date) 1)
					pad-with 2)
			    port)
		   (display (tm:padding (date-second date)
					pad-with 2)
			    port))
	       (let* ((ns (tm:fractional-part (/ 
					       (date-nanosecond date)
					       tm:nano 1.0)))
		      (le (string-length ns)))
		 (if (> le 2)
		     (begin
		       (display tm:locale-number-separator port)
		       (display (substring ns 2 le) port))))))
   (cons #\h (lambda (date pad-with port)
	       (display (date->string date "~b") port)))
   (cons #\H (lambda (date pad-with port)
	       (display (tm:padding (date-hour date)
				    pad-with 2)
			port)))
   (cons #\I (lambda (date pad-with port)
	       (let ((hr (date-hour date)))
		 (if (> hr 12)
		     (display (tm:padding (- hr 12)
					  pad-with 2)
			      port)
		     (display (tm:padding hr
					  pad-with 2)
			      port)))))
   (cons #\j (lambda (date pad-with port)
	       (display (tm:padding (date-year-day date)
				    pad-with 3)
			port)))
   (cons #\k (lambda (date pad-with port)
	       (display (tm:padding (date-hour date)
				    #\0 2)
                        port)))
   (cons #\l (lambda (date pad-with port)
	       (let ((hr (if (> (date-hour date) 12)
			     (- (date-hour date) 12) (date-hour date))))
		 (display (tm:padding hr  #\space 2)
			  port))))
   (cons #\m (lambda (date pad-with port)
	       (display (tm:padding (date-month date)
				    pad-with 2)
			port)))
   (cons #\M (lambda (date pad-with port)
	       (display (tm:padding (date-minute date)
				    pad-with 2)
			port)))
   (cons #\n (lambda (date pad-with port)
	       (newline port)))
   (cons #\N (lambda (date pad-with port)
	       (display (tm:padding (date-nanosecond date)
				    pad-with 9)
			port)))
   (cons #\p (lambda (date pad-with port)
	       (display (tm:locale-am/pm (date-hour date)) port)))
   (cons #\r (lambda (date pad-with port)
	       (display (date->string date "~I:~M:~S ~p") port)))
   (cons #\s (lambda (date pad-with port)
	       (display (time-second (date->time-utc date)) port)))
   (cons #\S (lambda (date pad-with port)
	       (if (> (date-nanosecond date)
		      tm:nano)
                   (display (tm:padding (+ (date-second date) 1)
                                        pad-with 2)
                            port)
                   (display (tm:padding (date-second date)
                                        pad-with 2)
                            port))))
   (cons #\t (lambda (date pad-with port)
	       (display (integer->char 9) port)))
   (cons #\T (lambda (date pad-with port)
	       (display (date->string date "~H:~M:~S") port)))
   (cons #\U (lambda (date pad-with port)
	       (if (> (tm:days-before-first-week date 0) 0)
		   (display (tm:padding (+ (date-week-number date 0) 1)
					#\0 2) port)
		   (display (tm:padding (date-week-number date 0)
					#\0 2) port))))
   (cons #\V (lambda (date pad-with port)
	       (display (tm:padding (date-week-number date 1)
				    #\0 2) port)))
   (cons #\w (lambda (date pad-with port)
	       (display (date-week-day date) port)))
   (cons #\x (lambda (date pad-with port)
	       (display (date->string date tm:locale-short-date-format) port)))
   (cons #\X (lambda (date pad-with port)
	       (display (date->string date tm:locale-time-format) port)))
   (cons #\W (lambda (date pad-with port)
	       (if (> (tm:days-before-first-week date 1) 0)
		   (display (tm:padding (+ (date-week-number date 1) 1)
					#\0 2) port)
		   (display (tm:padding (date-week-number date 1)
					#\0 2) port))))
   (cons #\y (lambda (date pad-with port)
	       (display (tm:padding (tm:last-n-digits 
				     (date-year date) 2)
				    pad-with
				    2)
			port)))
   (cons #\Y (lambda (date pad-with port)
	       (display (date-year date) port)))
   (cons #\z (lambda (date pad-with port)
	       (tm:tz-printer (date-zone-offset date) port)))
   (cons #\Z (lambda (date pad-with port)
	       (tm:locale-print-time-zone date port)))
   (cons #\1 (lambda (date pad-with port)
	       (display (date->string date "~Y-~m-~d") port)))
   (cons #\2 (lambda (date pad-with port)
	       (display (date->string date "~k:~M:~S~z") port)))
   (cons #\3 (lambda (date pad-with port)
	       (display (date->string date "~k:~M:~S") port)))
   (cons #\4 (lambda (date pad-with port)
	       (display (date->string date "~Y-~m-~dT~k:~M:~S~z") port)))
   (cons #\5 (lambda (date pad-with port)
	       (display (date->string date "~Y-~m-~dT~k:~M:~S") port)))
   ))


(define (tm:get-formatter char)
  (let ( (associated (assoc char tm:directives)) )
    (if associated (cdr associated) #f)))

(define (tm:date-printer date index format-string str-len port)
  (if (>= index str-len)
      (values)
      (let ( (current-char (string-ref format-string index)) )
	(if (not (char=? current-char #\~))
	    (begin
	      (display current-char port)
	      (tm:date-printer date (+ index 1) format-string str-len port))

	    (if (= (+ index 1) str-len) ; bad format string.
		(tm:time-error 'tm:date-printer 'bad-date-format-string 
			       format-string)
                (let ( (pad-char? (string-ref format-string (+ index 1))) )
                  (cond
                    ((char=? pad-char? #\-)
                     (if (= (+ index 2) str-len) ; bad format string.
                         (tm:time-error 'tm:date-printer 'bad-date-format-string 
                                        format-string)
                         (let ( (formatter (tm:get-formatter 
                                            (string-ref format-string
                                                        (+ index 2)))) )
                           (if (not formatter)
                               (tm:time-error 'tm:date-printer 'bad-date-format-string 
                                              format-string)
                               (begin
                                 (formatter date #f port)
                                 (tm:date-printer date (+ index 3)
                                                  format-string str-len port))))))
                    
                    ((char=? pad-char? #\_)
                     (if (= (+ index 2) str-len) ; bad format string.
                         (tm:time-error 'tm:date-printer 'bad-date-format-string 
                                        format-string)
                         (let ( (formatter (tm:get-formatter 
                                            (string-ref format-string
                                                        (+ index 2)))) )
                           (if (not formatter)
                               (tm:time-error 'tm:date-printer 'bad-date-format-string 
                                              format-string)
                               (begin
                                 (formatter date #\space port)
                                 (tm:date-printer date (+ index 3)
                                                  format-string str-len port))))))
                    (else
                     (let ( (formatter (tm:get-formatter 
                                        (string-ref format-string
                                                    (+ index 1)))) )
                       (if (not formatter)
                           (tm:time-error 'tm:date-printer 'bad-date-format-string 
                                          format-string)
                           (begin
                             (formatter date #\0 port)
                             (tm:date-printer date (+ index 2)
                                              format-string str-len port))))))))))))


(define (date->string date .  format-string)
  (let ( (str-port (open-output-string))
	 (fmt-str (:optional format-string "~c")) )
    (tm:date-printer date 0 fmt-str (string-length fmt-str) str-port)
    (get-output-string str-port)))

(define (tm:char->int ch)
  (cond
    ((char=? ch #\0) 0)
    ((char=? ch #\1) 1)
    ((char=? ch #\2) 2)
    ((char=? ch #\3) 3)
    ((char=? ch #\4) 4)
    ((char=? ch #\5) 5)
    ((char=? ch #\6) 6)
    ((char=? ch #\7) 7)
    ((char=? ch #\8) 8)
    ((char=? ch #\9) 9)
    (else (tm:time-error 'string->date 'bad-date-template-string
                         (list "Non-integer character" ch )))))

;; read an integer upto n characters long on port; upto -> #f if any length
(define (tm:integer-reader upto port)
  (define (accum-int port accum nchars)
    (let ((ch (peek-char port)))
      (if (or (eof-object? ch)
              (not (char-numeric? ch))
              (and upto (>= nchars  upto )))
          accum
          (accum-int port (+ (* accum 10) (tm:char->int (read-char
                                                         port))) (+
                                                                  nchars 1)))))
  (accum-int port 0 0))

(define (tm:make-integer-reader upto)
  (lambda (port)
    (tm:integer-reader upto port)))

;; read an fractional integer upto n characters long on port; upto -> #f if any length
;;
;; The return value is normalized to upto decimal places. For example, if upto is 9 and 
;; the string read is "123", the return value is 123000000.
(define (tm:fractional-integer-reader upto port)
  (define (accum-int port accum nchars)
    (let ((ch (peek-char port)))
     (if (or (eof-object? ch)
     	(not (char-numeric? ch))
     	(and upto (>= nchars  upto )))
         (* accum (expt 10 (- upto nchars)))
         (accum-int port (+ (* accum 10) (tm:char->int (read-char port))) (+ nchars 1)))))
  (accum-int port 0 0))

(define (tm:make-fractional-integer-reader upto)
  (lambda (port)
    (tm:fractional-integer-reader upto port)))


;; read *exactly* n characters and convert to integer; could be padded
(define (tm:integer-reader-exact n port)
  (let ( (padding-ok #t) )
    (define (accum-int port accum nchars)
      (let ((ch (peek-char port)))
	(cond
          ((>= nchars n) accum)
          ((eof-object? ch) 
           (tm:time-error 'string->date 'bad-date-template-string 
                          "Premature ending to integer read."))
          ((char-numeric? ch)
           (set! padding-ok #f)
           (accum-int port (+ (* accum 10) (tm:char->int (read-char
                                                          port)))
                      (+ nchars 1)))
          (padding-ok
           (read-char port) ; consume padding
           (accum-int port accum (+ nchars 1)))
          (else ; padding where it shouldn't be
           (tm:time-error 'string->date 'bad-date-template-string 
			  "Non-numeric characters in integer read.")))))
    (accum-int port 0 0)))


(define (tm:make-integer-exact-reader n)
  (lambda (port)
    (tm:integer-reader-exact n port)))

(define (tm:zone-reader port) 
  (let ( (offset 0) 
	 (positive? #f) )
    (let ( (ch (read-char port)) )
      (if (eof-object? ch)
	  (tm:time-error 'string->date 'bad-date-template-string
			 (list "Invalid time zone +/-" ch)))
      (if (or (char=? ch #\Z) (char=? ch #\z))
	  0
	  (begin
	    (cond
              ((char=? ch #\+) (set! positive? #t))
              ((char=? ch #\-) (set! positive? #f))
              (else
               (tm:time-error 'string->date 'bad-date-template-string
                              (list "Invalid time zone +/-" ch))))
	    (let ((ch (read-char port)))
	      (if (eof-object? ch)
		  (tm:time-error 'string->date 'bad-date-template-string
                                 (list "Invalid time zone number" ch)))
	      (set! offset (* (tm:char->int ch)
			      10 60 60)))
	    (let ((ch (read-char port)))
	      (if (eof-object? ch)
		  (tm:time-error 'string->date 'bad-date-template-string
				 (list "Invalid time zone number" ch)))
	      (set! offset (+ offset (* (tm:char->int ch)
					60 60))))
	    (let ((ch (read-char port)))
	      (if (eof-object? ch)
		  (tm:time-error 'string->date 'bad-date-template-string
				 (list "Invalid time zone number" ch)))
	      (set! offset (+ offset (* (tm:char->int ch)
					10 60))))
	    (let ((ch (read-char port)))
	      (if (eof-object? ch)
		  (tm:time-error 'string->date 'bad-date-template-string
				 (list "Invalid time zone number" ch)))
	      (set! offset (+ offset (* (tm:char->int ch)
					60))))
	    (if positive? offset (- offset)))))))

;; looking at a char, read the char string, run thru indexer, return index
(define (tm:locale-reader port indexer)
  (let ( (string-port (open-output-string)) )
    (define (read-char-string)
      (let ((ch (peek-char port)))
	(if (char-alphabetic? ch)
	    (begin (write-char (read-char port) string-port) 
		   (read-char-string))
	    (get-output-string string-port))))
    (let* ( (str (read-char-string)) 
	    (index (indexer str)) )
      (if index index (tm:time-error 'string->date
				     'bad-date-template-string
				     (list "Invalid string for " indexer))))))

(define (tm:make-locale-reader indexer)
  (lambda (port)
    (tm:locale-reader port indexer)))

(define (tm:make-char-id-reader char)
  (lambda (port)
    (if (char=? char (read-char port))
	char
	(tm:time-error 'string->date
		       'bad-date-template-string
		       "Invalid character match."))))

;; A List of formatted read directives.
;; Each entry is a list.
;; 1. the character directive; 
;; a procedure, which takes a character as input & returns
;; 2. #t as soon as a character on the input port is acceptable
;; for input,
;; 3. a port reader procedure that knows how to read the current port
;; for a value. Its one parameter is the port.
;; 4. a action procedure, that takes the value (from 3.) and some
;; object (here, always the date) and (probably) side-effects it.
;; In some cases (e.g., ~A) the action is to do nothing

(define tm:read-directives 
  (let ( (ireader4 (tm:make-integer-reader 4))
	 (ireader2 (tm:make-integer-reader 2))
	 (fireader9 (tm:make-fractional-integer-reader 9))
	 (ireaderf (tm:make-integer-reader #f))
	 (eireader2 (tm:make-integer-exact-reader 2))
	 (eireader4 (tm:make-integer-exact-reader 4))
	 (locale-reader-abbr-weekday (tm:make-locale-reader
				      tm:locale-abbr-weekday->index))
	 (locale-reader-long-weekday (tm:make-locale-reader
				      tm:locale-long-weekday->index))
	 (locale-reader-abbr-month   (tm:make-locale-reader
				      tm:locale-abbr-month->index))
	 (locale-reader-long-month   (tm:make-locale-reader
				      tm:locale-long-month->index))
	 (char-fail (lambda (ch) #t))
	 (do-nothing (lambda (val object) (values)))
	 )
    
    (list
     (list #\~ char-fail (tm:make-char-id-reader #\~) do-nothing)
     (list #\a char-alphabetic? locale-reader-abbr-weekday do-nothing)
     (list #\A char-alphabetic? locale-reader-long-weekday do-nothing)
     (list #\b char-alphabetic? locale-reader-abbr-month
           (lambda (val object)
             (tm:set-date-month! object val)))
     (list #\B char-alphabetic? locale-reader-long-month
           (lambda (val object)
             (tm:set-date-month! object val)))
     (list #\d char-numeric? ireader2 (lambda (val object)
                                        (tm:set-date-day!
                                         object val)))
     (list #\e char-fail eireader2 (lambda (val object)
                                     (tm:set-date-day! object val)))
     (list #\h char-alphabetic? locale-reader-abbr-month
           (lambda (val object)
             (tm:set-date-month! object val)))
     (list #\H char-numeric? ireader2 (lambda (val object)
                                        (tm:set-date-hour! object val)))
     (list #\k char-fail eireader2 (lambda (val object)
                                     (tm:set-date-hour! object val)))
     (list #\m char-numeric? ireader2 (lambda (val object)
                                        (tm:set-date-month! object val)))
     (list #\M char-numeric? ireader2 (lambda (val object)
                                        (tm:set-date-minute!
                                         object val)))
     (list #\N char-numeric? fireader9 (lambda (val object)
					 (tm:set-date-nanosecond! object val)))
     (list #\S char-numeric? ireader2 (lambda (val object)
                                        (tm:set-date-second! object val)))
     (list #\y char-fail eireader2 
           (lambda (val object)
             (tm:set-date-year! object (tm:natural-year val))))
     (list #\Y char-numeric? ireader4 (lambda (val object)
                                        (tm:set-date-year! object val)))
     (list #\z (lambda (c)
                 (or (char=? c #\Z)
                     (char=? c #\z)
                     (char=? c #\+)
                     (char=? c #\-)))
           tm:zone-reader (lambda (val object)
                            (tm:set-date-zone-offset! object val)))
     )))

(define (tm:string->date date index format-string str-len port template-string)
  (define (skip-until port skipper)
    (let ((ch (peek-char port)))
      (if (eof-object? ch)
	  (tm:time-error 'string->date 'bad-date-format-string template-string)
	  (if (not (skipper ch))
	      (begin (read-char port) (skip-until port skipper))))))
  (if (>= index str-len)
      (begin 
	(values))
      (let ( (current-char (string-ref format-string index)) )
	(if (not (char=? current-char #\~))
	    (let ((port-char (read-char port)))
	      (if (or (eof-object? port-char)
		      (not (char=? current-char port-char)))
		  (tm:time-error 'string->date 'bad-date-format-string template-string))
	      (tm:string->date date (+ index 1) format-string str-len port template-string))
	    ;; otherwise, it's an escape, we hope
	    (if (> (+ index 1) str-len)
		(tm:time-error 'string->date 'bad-date-format-string template-string)
		(let* ( (format-char (string-ref format-string (+ index 1)))
			(format-info (assoc format-char tm:read-directives)) )
		  (if (not format-info)
		      (tm:time-error 'string->date 'bad-date-format-string template-string)
		      (begin
			(let ((skipper (cadr format-info))
			      (reader  (caddr format-info))
			      (actor   (cadddr format-info)))
			  (skip-until port skipper)
			  (let ((val (reader port)))
			    (if (eof-object? val)
				(tm:time-error 'string->date 'bad-date-format-string template-string)
				(actor val date)))
			  (tm:string->date date (+ index 2) format-string  str-len port template-string))))))))))

(define (string->date input-string template-string)
  (define (tm:date-ok? date)
    (and (date-nanosecond date)
	 (date-second date)
	 (date-minute date)
	 (date-hour date)
	 (date-day date)
	 (date-month date)
	 (date-year date)
	 (date-zone-offset date)))
  (let ( (newdate (make-date 0 0 0 0 #f #f #f (tm:local-tz-offset))) )
    (tm:string->date newdate
		     0
		     template-string
		     (string-length template-string)
		     (open-input-string input-string)
		     template-string)
    (if (tm:date-ok? newdate)
	newdate
	(tm:time-error 'string->date 'bad-date-format-string (list "Incomplete date read. " newdate template-string)))))

Added srfi/s19/time.sls.





















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s19 time)
  (export
    time make-time time? time-type time-nanosecond time-second
    date make-date date? date-nanosecond date-second date-minute 
    date-hour date-day date-month date-year date-zone-offset
    time-tai time-utc time-monotonic
    #|time-thread time-process|# time-duration
    read-leap-second-table copy-time current-time
    time-resolution time=? time>? time<? time>=? time<=?
    time-difference time-difference! add-duration
    add-duration! subtract-duration subtract-duration!
    time-tai->time-utc time-tai->time-utc! time-utc->time-tai
    time-utc->time-tai! time-monotonic->time-utc
    time-monotonic->time-utc! time-monotonic->time-tai
    time-monotonic->time-tai! time-utc->time-monotonic
    time-utc->time-monotonic! time-tai->time-monotonic
    time-tai->time-monotonic! time-tai->date time-utc->date
    time-monotonic->date date->time-utc date->time-tai
    date->time-monotonic leap-year? date-year-day
    date-week-day date-week-number current-date
    date->julian-day date->modified-julian-day
    time-utc->julian-day time-utc->modified-julian-day
    time-tai->julian-day time-tai->modified-julian-day
    time-monotonic->julian-day
    time-monotonic->modified-julian-day julian-day->time-utc
    julian-day->time-tai julian-day->time-monotonic
    julian-day->date modified-julian-day->date
    modified-julian-day->time-utc
    modified-julian-day->time-tai
    modified-julian-day->time-monotonic current-julian-day
    current-modified-julian-day date->string string->date)
  (import
    (rnrs)
    (rnrs r5rs)
    (rnrs mutable-strings)
    (srfi s19 time compat)
    (srfi s6 basic-string-ports)
    (srfi private include))
  
  (define read-line
    (case-lambda
      [()
       (get-line (current-input-port))]
      [(port)
       (get-line port)]))
  
  (define eof (eof-object))
  
  (include/resolve ("srfi" "s19") "srfi-19.scm")
)

Added srfi/s19/time/compat.chezscheme.sls.





















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

(library (srfi s19 time compat)

  (export format
          host:time-resolution
          host:current-time 
          host:time-nanosecond 
          host:time-second 
          host:time-gmt-offset)

  (import (chezscheme)
          (prefix (only (chezscheme)
                        current-time
                        time-nanosecond
                        time-second)
                  host:))

  (define host:time-resolution 1000)

  ;; (define (host:time-gmt-offset t)
  ;;   (date-zone-offset t))

  (define (host:time-gmt-offset t)
    (date-zone-offset (time-utc->date t)))
    
  )

Added srfi/s19/time/compat.ikarus.sls.

















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s19 time compat)
  (export
    format
    host:time-resolution
    host:current-time 
    host:time-nanosecond 
    host:time-second 
    host:time-gmt-offset)
  (import
    (rnrs base)
    (only (ikarus) format)
    (prefix (only (ikarus) current-time time-nanosecond time-second time-gmt-offset) 
            host:))

  ;; Ikarus uses gettimeofday() which gives microseconds,
  ;; so our resolution is 1000 nanoseconds
  (define host:time-resolution 1000)
)

Added srfi/s19/time/compat.larceny.sls.























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s19 time compat)
  (export
    format
    host:time-resolution
    host:current-time 
    host:time-nanosecond 
    host:time-second 
    host:time-gmt-offset)
  (import
    (r5rs) 
    (rnrs)
    (larceny load)
    (primitives r5rs:require current-utc-time timezone-offset)    
    (srfi s48 intermediate-format-strings))
  
  (define-record-type time (fields secs usecs))
  
  ;; Larceny uses gettimeofday() which gives microseconds,
  ;; so our resolution is 1000 nanoseconds
  (define host:time-resolution 1000)
  
  (define (host:current-time)
    (let-values ([(secs usecs) (current-utc-time)])
      (make-time secs usecs)))
  
  (define (host:time-nanosecond t)
    (* (time-usecs t) 1000))
  
  (define (host:time-second t)
    (time-secs t))
  
  (define (host:time-gmt-offset t) 
    (timezone-offset (time-secs t)))
  
  (r5rs:require 'time)

)

Added srfi/s19/time/compat.mosh.sls.























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
;; Copyright (c) Higepon(Taro Minowa)
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; Except as contained in this notice, the name(s) of the above copyright
;; holders shall not be used in advertising or otherwise to promote the sale,
;; use or other dealings in this Software without prior written authorization.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.

(library (srfi s19 time compat)
  (export
   format
   host:time-resolution
   host:current-time
   host:time-nanosecond
   host:time-second
   host:time-gmt-offset)
  (import
   (rnrs base)
   (only (mosh) format)
   (only (system) microseconds local-tz-offset))

  (define host:time-resolution 1000)
  (define (host:current-time) (microseconds))
  (define (host:time-nanosecond t) (* (mod t 1000000) 1000))
  (define (host:time-second t) (div t 1000000))
  (define (host:time-gmt-offset t) (local-tz-offset))
)

Added srfi/s19/time/compat.mzscheme.sls.







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s19 time compat)
  (export
    format
    host:time-resolution
    host:current-time 
    host:time-nanosecond 
    host:time-second 
    host:time-gmt-offset)
  (import
    (rnrs base)
    (only (scheme base) format current-inexact-milliseconds date-time-zone-offset
                        seconds->date current-seconds))

  ;; MzScheme uses milliseconds, so our resolution in nanoseconds is #e1e6
  (define host:time-resolution #e1e6)
  
  (define (host:current-time)
    (exact (floor (current-inexact-milliseconds))))
  
  (define (host:time-nanosecond t)
    (* (mod t 1000) #e1e6))
  
  (define (host:time-second t)
    (div t 1000))
  
  (define (host:time-gmt-offset t)
    (date-time-zone-offset (seconds->date (host:time-second t))))
)

Added srfi/s19/time/compat.ypsilon.sls.

















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s19 time compat)
  (export
   format
   host:time-resolution
   host:current-time
   host:time-nanosecond
   host:time-second
   host:time-gmt-offset)
  (import
   (rnrs base)
   (only (core) format microsecond microsecond->utc))

  (define host:time-resolution 1000)
  (define (host:current-time) (microsecond))
  (define (host:time-nanosecond t) (* (mod t 1000000) 1000))
  (define (host:time-second t) (div t 1000000))
  (define (host:time-gmt-offset t) (/ (- t (microsecond->utc t)) 1000000))
)

Added srfi/s2/and-let.sls.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s2 and-let)  
  (export 
    and-let*)
  (import 
    (rnrs))
  
  (define-syntax and-let*
    (lambda (stx)
      (define (get-id c)
        (syntax-case c () [(var expr) #'var] [_ #f]))
      (syntax-case stx ()
        [(_ (clause* ...) body* ...)
         (for-all identifier? (filter values (map get-id #'(clause* ...))))
         #'(and-let*-core #t (clause* ...) body* ...)])))
  
  (define-syntax and-let*-core
    (lambda (stx)
      (syntax-case stx ()
        [(kw _ ([var expr] clause* ...) body* ...)
         #'(let ([var expr])
             (if var
               (kw var (clause* ...) body* ...)
               #f))]
        [(kw _ ([expr] clause* ...) body* ...)
         #'(let ([t expr])
             (if t
               (kw t (clause* ...) body* ...)
               #f))]
        [(kw _ (id clause* ...) body* ...)
         (or (identifier? #'id)
             (syntax-violation #f "invalid clause" stx #'id))
         #'(if id
             (kw id (clause* ...) body* ...)
             #f)]
        [(kw last () body* ...)
         (if (positive? (length #'(body* ...)))
           #'(begin body* ...)
           #'last)])))
)

Added srfi/s23/error.sls.

































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s23 error)
  (export
    error)
  (import 
    (rename (rnrs base) (error rnrs:error)))
    
  (define (error . args)
    (apply rnrs:error #F args))
)

Added srfi/s23/error/tricks.sls.























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s23 error tricks)
  (export
    SRFI-23-error->R6RS)
  (import
    (rnrs))

  (define-syntax error-wrap
    (lambda (stx)
      (syntax-case stx ()
        ((_ ctxt signal expr ...)
         (with-syntax ((e (datum->syntax #'ctxt 'error)))
           #'(let-syntax ((e (identifier-syntax signal)))
               expr ...))))))

  (define (AV who)
    (lambda args (apply assertion-violation who args)))

  (define-syntax SRFI-23-error->R6RS
    (lambda (stx)
      (syntax-case stx ()
        ((ctxt ewho expr ...)
         (with-syntax ((e (datum->syntax #'ctxt 'error))
                       (d (datum->syntax #'ctxt 'define)))
           #'(let-syntax ((e (identifier-syntax (AV 'ewho)))
                          (d (lambda (stx)
                               (syntax-case stx ()
                                 ((kw (id . formals) . body)
                                  (identifier? #'id)
                                  #'(error-wrap kw (AV 'id)
                                     (d (id . formals) . body)))
                                 ((kw id . r)
                                  (identifier? #'id)
                                  #'(error-wrap kw (AV 'id)
                                     (d id . r)))))))
               expr ...))))))
)

Added srfi/s25/arlib.scm.

























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
;;; array arlib

;;; 2001 Jussi Piitulainen

;;; This is a high level implementation of some generally useful
;;; array procedures. In addition to R5RS and SRFI-25, only one
;;; tool is used, namely array:apply-to-vector and friends. Thus
;;; this library serves to prove that the primitives really are
;;; primitives. - A lower level implementation would access some
;;; implementation details to bypass redundant checking and such.

;;; Note that these procedures are not necessarily designed with
;;; full care. Think of them as examples of what can be done.
;;; Important tools are also missing, including scans and reduces
;;; and many thinks that I have not even heard of yet.

;;; (array-shape arr) (array-length arr dim) (array-size arr)
;;; (array-equal? arr1 arr2)
;;; (shape-for-each shp proc [ind])
;;; (array-for-each-index arr proc [ind])
;;; (tabulate-array shp proc) (tabulate-array! shp proc ind)
;;; (array-retabulate! arr shp proc [ind])
;;; (array-map [shp] proc arr0 arr1 ...)
;;; (array-map! arr [shp] proc arr0 arr1 ...)
;;; (array->vector arr) (array->list arr)
;;; (share-array/prefix arr k ...) (share-row arr k) (share-column arr k)
;;; (share-array/origin arr k ...) (share-array/origin arr ind)
;;; (array-append dim arr0 arr1 ...)
;;; (transpose arr dim ...)
;;; (share-nths arr dim n)

;;; Naming problem: should all those index-object using procedures
;;; bang? The main argument, like shape, is not mutated.

;;; (array-shape arr)

(define (array-shape arr)
  (let ((r (array-rank arr)))
    (let ((m (make-array (shape 0 r 0 2))))
      (do ((d 0 (+ d 1)))
        ((= d r)
         m)
        (array-set! m d 0 (array-start arr d))
        (array-set! m d 1 (array-end arr d))))))

;;; (array-length arr dim)

(define (array-length arr dim)
  (- (array-end arr dim)
     (array-start arr dim)))

;;; (array-size arr)

(define (array-size arr)
  (let ((r (array-rank arr)))
    (do ((k 0 (+ k 1))
         (p 1 (* p (array-length arr k))))
      ((= k r) p))))

;;; (array-equal? a b)
;;; compares elements with equal? so elements better not contain
;;; arrays.

(define (array-equal? a b)
  (let ((r (array-rank a)))
    (and (= r (array-rank b))
         (and (do ((k 0 (+ k 1))
                   (true #t (and true
                                 (= (array-start a k)
                                    (array-start b k))
                                 (= (array-end a k)
                                    (array-end b k)))))
                ((= k r) true))
              (let ((ks (make-vector r 0)))
                (let wok ((d 0))
                  (if (< d r)
                      (let ((e (array-end a d)))
                        (do ((k (array-start a d) (+ k 1))
                             (true #t (and true (wok (+ d 1)))))
                          ((= k e) true)
                          (vector-set! ks d k)))
                      (equal? (array-ref a ks)
                              (array-ref b ks)))))))))

;;; (shape-for-each shp proc [index-object])
;;; passes each index in shape to proc in row-major orderd, using
;;; index-object if provided.

(define (shape-for-each shp proc . o)
  (if (null? o)
      (array:arlib:shape-for-each/arguments shp proc)
      (if (vector? (car o))
          (array:arlib:shape-for-each/vector shp proc (car o))
          (array:arlib:shape-for-each/array shp proc (car o)))))

(define (array:arlib:shape-for-each/arguments shp proc)
  (let ((r (array-end shp 0)))
    (let ((vec (make-vector r)))
      (let do-dim ((d 0))
        (if (= d r)
            (array:apply-to-vector r proc vec)
            (let ((e (array-ref shp d 1)))
              (do ((k (array-ref shp d 0) (+ k 1)))
                ((= k e))
                (vector-set! vec d k)
                (do-dim (+ d 1)))))))))

(define (array:arlib:shape-for-each/vector shp proc vec)
  (let ((r (array-end shp 0)))
    (let do-dim ((d 0))
      (if (= d r)
          (proc vec)
          (let ((e (array-ref shp d 1)))
            (do ((k (array-ref shp d 0) (+ k 1)))
              ((= k e))
              (vector-set! vec d k)
              (do-dim (+ d 1))))))))

(define (array:arlib:shape-for-each/array shp proc arr)
  ;; arr is not vector
  (let ((r (array-end shp 0)))
    (let do-dim ((d 0))
      (if (= d r)
          (proc arr)
          (let ((e (array-ref shp d 1)))
            (do ((k (array-ref shp d 0) (+ k 1)))
              ((= k e))
              (array-set! arr d k)
              (do-dim (+ d 1))))))))

;;; (array-for-each-index arr proc [ind])
;;; is equivalent to
;;;
;;;   (shape-for-each-index (array-shape arr) proc [ind])
;;;
;;; but is implemented without allocation of the shape, to prove
;;; that it can be so implemented.

(define (array-for-each-index arr proc . o)
  (if (null? o)
      (array:arlib:array-for-each-index/arguments arr proc)
      (if (vector? (car o))
          (array:arlib:array-for-each-index/vector arr proc (car o))
          (array:arlib:array-for-each-index/array arr proc (car o)))))

(define (array:arlib:array-for-each-index/arguments arr proc)
  (let ((r (array-rank arr)))
    (let ((vec (make-vector r))
          (apply (array:applier-to-vector r)))
      (let do-dim ((d 0))
        (if (= d r)
            (apply proc vec)
            (let ((e (array-end arr d)))
              (do ((k (array-start arr d) (+ k 1)))
                ((= k e))
                (vector-set! vec d k)
                (do-dim (+ d 1)))))))))

(define (array:arlib:array-for-each-index/vector arr proc ind)
  ;; ind is a vector
  (let ((r (array-rank arr)))
    (let do-dim ((d 0))
      (if (= d r)
          (proc ind)
          (let ((e (array-end arr d)))
            (do ((k (array-start arr d) (+ k 1)))
              ((= k e))
              (vector-set! ind d k)
              (do-dim (+ d 1))))))))

(define (array:arlib:array-for-each-index/array arr proc ind)
  ;; ind is an array but not a vector
  (let ((r (array-rank arr)))
    (let do-dim ((d 0))
      (if (= d r)
          (proc ind)
          (let ((e (array-end arr d)))
            (do ((k (array-start arr d) (+ k 1)))
              ((= k e))
              (array-set! ind d k)
              (do-dim (+ d 1))))))))

;;; (tabulate-array shp proc)
;;; (tabulate-array! shp proc ind)
;;; returns a newly allocated array of the given shape with initial
;;; contents at each index whatever proc returns given the indices.
;;; The latter procedure reuses ind for package of indices.

(define (tabulate-array shp proc)
  (let ((arr (make-array shp)))
    (array:arlib:shape-for-each/vector
     shp
     (let ((apply (array:applier-to-vector (array-end shp 0))))
       (lambda (ix) (array-set! arr ix (apply proc ix))))
     (make-vector (array-end shp 0)))
    arr))
        
(define (tabulate-array! shp proc ind)
  (let ((arr (make-array shp)))
    (if (vector? ind)
        (array:arlib:shape-for-each/vector
         shp
         (lambda (ix) (array-set! arr ix (proc ix)))
         ind)
        (array:arlib:shape-for-each/array
         shp
         (lambda (ix) (array-set! arr ix (proc ix)))
         ind))
    arr))

;;; (array-retabulate! arr shp proc [index-object])
;;; sets the elements of arr in shape to the value of proc at that
;;; index, using index-object if provided.

(define (array-retabulate! arr shp proc . o)
  (if (null? o)
      (array:arlib:shape-for-each/vector
       shp
       (let ((apply (array:applier-to-vector (array-end shp 0))))
         (lambda (ix)
           (array-set! arr ix (apply proc ix))))
       (make-vector (array-end shp 0)))
      (if (vector? (car o))
          (array:arlib:shape-for-each/vector
           shp
           (lambda (ix)
             (array-set! arr ix (proc ix)))
           (car o))
          (array:arlib:shape-for-each/array
           shp
           (lambda (ix)
             (array-set! arr ix (proc ix)))
           (car o)))))

;;; (array-map! array [shape] proc array0 array1...)
;;; stores to the elements of array (in shape) the values of proc at
;;; the contents of arrayk at corresponding index.

(define (array-map! arr x y . o)
  (if (array:array? x)
      (array:arlib:map! arr x y (apply vector o))
      (array:arlib:map! arr (array-shape arr) x (apply vector y o))))

(define (array:arlib:map! arr shp proc args)
  (let ((rank (vector-length args)))
    (let ((argv (make-vector rank)))
      (array:arlib:shape-for-each/vector
       shp
       (let ((apply (array:applier-to-vector rank)))
         (lambda (ix)
           (do ((k 0 (+ k 1)))
             ((= k rank))
             (vector-set! argv k (array-ref (vector-ref args k) ix)))
           (array-set! arr ix (apply proc argv))))
       (make-vector (array-end shp 0))))))

;;; (array-map [shape] proc array0 array1 ...)
;;; creates a new array with elements initialized to the values of
;;; proc at contents of arrayk (in shape).

(define (array-map x y . o)
  (if (array:array? x)
      (let ((arr (make-array x)))
        (array:arlib:map! arr x y (apply vector o))
        arr)
      (let ((shp (array-shape y)))
        (let ((arr (make-array shp)))
          (array:arlib:map! arr shp x (apply vector y o))
          arr))))

;;; SRFI-25 mailing list requested array->vector; they also requested the
;;; ability to use an array as an index of an element, and array->list is
;;; an attempt to provide for that.

(define (array->vector arr)
  (let ((vec (make-vector (array-size arr))))
    (let ((k 0))
      (shape-for-each
       (array-shape arr)
       (lambda (index)
         (vector-set! vec k (array-ref arr index))
         (set! k (+ k 1)))
       (make-vector (array-rank arr)))
      vec)))

;;; It needs to be said that more efficient implementations are
;;; possible, even within SRFI-25.

(define (array->list arr)
  (vector->list (array->vector arr)))

;;; (share-row arr k)
;;; shares whatever the first index is about.
;;; The result has one dimension less.

(define (share-row arr k)
  (share-array
   arr
   (let ((bounds (array->list (array-shape arr))))
     (apply shape (cddr bounds)))
   (lambda ks
     (apply values k ks))))

;;; (share-array/prefix arr k ...)

(define (share-array/prefix arr . js)
  (if (or (null? js)
          (integer? (car js)))
      (share-array
       arr
       (let ((bounds (array->list (array-shape arr))))
         (apply shape (list-tail bounds (* 2 (length js)))))
       (lambda ks
         (apply values (append js ks))))
      (apply (lambda (fix)
               (share-array/prefix!
                arr
                fix
                (make-vector (- (array-rank arr)
                                (if (vector? fix)
                                    (vector-length fix)
                                    (array-end fix 0))))))
             js)))

(define (share-array/prefix! arr fix in . out)
  (let* ((out (if (pair? out)
                  ((lambda (out) out) out)
                  (make-vector (array-rank arr))))
         (fix-ref (if (vector? fix) vector-ref array-ref))
         (in-ref (if (vector? in) vector-ref array-ref))
         (out-set! (if (vector? out) vector-set! array-set!))
         (m (if (vector? fix)
                (vector-length fix)
                (array-end fix 0)))
         (n (if (vector? out)
                (vector-length out)
                (array-end out 0))))
    (do ((k 0 (+ k 1)))
      ((= k m))
      (out-set! out k (fix-ref fix k)))
    (share-array/index!
     arr
     (let ((bounds (array->list (array-shape arr))))
       (apply shape (list-tail bounds (if (vector? fix)
                                          (* 2 (vector-length fix))
                                          (* 2 (array-end fix 0))))))
     (lambda (in)
       (do ((k m (+ k 1)))
         ((= k n))
         (out-set! out k (in-ref in (- k m))))
       out)
     in)))

;;; (share-column arr k)
;;; shares whatever the second index is about.
;;; The result has one dimension less.

(define (share-column arr k)
  (share-array
   arr
   (let ((bounds (array->list (array-shape arr))))
     (apply shape
            (car bounds) (cadr bounds)
            (cddddr bounds)))
   (lambda ks
     (apply values (car ks) k (cdr ks)))))

;;; (share-array/origin arr k ...)
;;; (share-array/origin arr index)
;;; change origin to k ..., with index a vector or zero-based
;;; one-dimensional array that contains k ...
;;;
;;; This is useful for writing array-append. Maybe for something
;;; else too - who knows.

(define (share-array/origin arr . xs)
  (let ((new (if (or (null? xs)
                     (integer? (car xs)))
                 xs
                 (apply (lambda (x)
                          (if (vector? x)
                              (vector->list x)
                              (if (array? x)
                                  (array->list x)
                                  (error "share-array/origin: bad thing"))))
                        xs))))
    (do ((k (array-rank arr) (- k 1))
         (old '() (cons (array-start arr (- k 1)) old)))
      ((= k 0)
       (let ((ds (map - new old)))
         (share-array
          arr
          (tabulate-array
           (shape 0 (array-rank arr) 0 2)
           (lambda (r k)
             (case k
               ((0) (+ (array-start arr r) (list-ref ds r)))
               ((1) (+ (array-end arr r) (list-ref ds r))))))
          (lambda ks
            (apply values (map - ks ds)))))))))

;;; SRFI-25 mailing list requested making shapes their own type. Here's
;;; an example of how manipulating shapes as arrays can be useful. The
;;; example also tests that higher level libraries are indeed easy to
;;; write on top of this SRFI.

;;; (array-append arr1 arr2 dim)
;;; appends two arrays along a specified dimension. The arrays must
;;; have equally many dimensions and all other dimensions equally long.
;;;
;;; Generalize to more arrays and maybe rewrite with shape-for-each or
;;; what have you.

(define (array-append dim arr . ars)
  (let* ((total (do ((m (array-length arr dim)
                        (+ m (array-length (car r) dim)))
                     (r ars (cdr r)))
                  ((null? r) m)))
         (common (array-shape arr))
         (origin (array->vector (share-column common 0)))
         (index (make-vector (array-rank arr))))
    (array-set! common dim 1 (+ (array-start arr dim) total))
    (let ((result (make-array common)))
      (array-set! common dim 1 (array-start arr dim))
      (let wok ((arr arr)
                (ars ars))
        (vector-set! origin dim (array-ref common dim 1))
        (let ((arr1 (share-array/origin arr origin)))
          (array-set! common dim 0 (array-start arr1 dim))
          (array-set! common dim 1 (array-end arr1 dim))
          (shape-for-each
           common
           (lambda (index)
             (array-set! result index (array-ref arr1 index)))
           index))
        (if (pair? ars)
            (wok (car ars) (cdr ars))))
      result)))

;;; Transpose, as permutation of dimensions, is applicable to all
;;; arrays. The default is reversal.

;;; The implementation uses multiplication by permutation
;;; matrix but matrix multiplication is not exported.

(define (array:arlib:matrix-times a b)
  (or (and (= (array-rank a) 2)
           (= (array-rank b) 2))
      (error "times: arrays are not matrices"))
  (let ((r0 (array-start a 0))  (rn (array-end a 0))
        (t0 (array-start a 1))  (tn (array-end a 1))
        (u0 (array-start b 0))  (un (array-end b 0)) 
        (k0 (array-start b 1))  (kn (array-end b 1)))
    (or (= (- tn t0) (- un u0))
        (error "times: matrices are not compatible"))
    (let ((ab (make-array (shape r0 rn k0 kn))))
      (do ((r r0 (+ r 1)))
        ((= r rn))
        (do ((k k0 (+ k 1)))
          ((= k kn))
          (do ((t t0 (+ t 1))
               (u u0 (+ u 1))
               (s 0 (+ s (* (array-ref a r t)
                            (array-ref b u k)))))
            ((and (= t tn)
                  (= u un))
             (array-set! ab r k s)))))
      ab)))

; This is a generalized transpose. It can permute the dimensions any which 
; way. The permutation is provided by a permutation matrix: a square matrix
; of zeros and ones, with exactly one one in each row and column, or a
; permutation of the rows of an identity matrix; the size of the matrix
; must match the number of dimensions of the array.
;
; The default permutation is [ 0 1 | 1 0 ] of course, but any permutation
; array can be specified, and the shape array of the original array is then
; multiplied with it, and index column vectors of the new array with its
; inverse, from left, to permute the rows appropriately.

(define (array:arlib:permutation-matrix . ds)
  (let* ((n (length ds))
         (arr (make-array (shape 0 n 0 n) 0)))
    (do ((k 0 (+ k 1))
         (ds ds (cdr ds)))
      ((= k n))
      (array-set! arr k (car ds) 1))
    arr))

;;; (transpose arr k ...)
;;; shares arr with permuted dimensions. Each dimension from 0
;;; inclusive to rank exclusive must appear once in k ...

(define (transpose a . p0)
  (let* ((r (array-rank a))
         (permutation (apply array:arlib:permutation-matrix
                             (if (pair? p0)
                                 p0
                                 (do ((ds '() (cons d ds))
                                      (d 0 (+ d 1)))
                                   ((= d r)
                                    ;; reverse dimensions
                                    ds)))))
         (inverse-permutation (share-array permutation
                                           (array-shape permutation)
                                           (lambda (r k)
                                             ;; transpose
                                             (values k r)))))
    (share-array
     a
     (array:arlib:matrix-times permutation (array-shape a))
     (lambda ks0
       (apply values
              (array->list
               (array:arlib:matrix-times
                inverse-permutation
                (apply array (shape 0 r 0 1) ks0))))))))

;;; (share-array/index! array subshape proc index)

(define (share-array/index! array subshape proc index)
  (array:share/index! array subshape proc index))

;;; Take every nth slice along dimension d into a shared array. This
;;; preserves the origin.

(define (share-nths arr d n)
  (let* ((bounds (array->vector (array-shape arr)))
         (b (vector-ref bounds (* 2 d)))
         (e (vector-ref bounds (+ (* 2 d) 1))))
    (vector-set! bounds (+ (* 2 d) 1) (+ b (quotient (+ n (- e b 1)) n)))
    (share-array
     arr
     (apply shape (vector->list bounds))
     (lambda ks
       (apply values
              (let d/nk ((u 0) (ks ks))
                (if (= u d)
                    (cons (+ b (* n (- (car ks) b))) (cdr ks))
                    (cons (car ks) (d/nk (+ u 1) (cdr ks))))))))))

Added srfi/s25/array.scm.



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
;;; array
;;; 1997 - 2001 Jussi Piitulainen

;;; --- Intro ---

;;; This interface to arrays is based on Alan Bawden's array.scm of
;;; 1993 (earlier version in the Internet Repository and another
;;; version in SLIB). This is a complete rewrite, to be consistent
;;; with the rest of Scheme and to make arrays independent of lists.

;;; Some modifications are due to discussion in srfi-25 mailing list.

;;; (array? obj)
;;; (make-array shape [obj])             changed arguments
;;; (shape bound ...)                    new
;;; (array shape obj ...)                new
;;; (array-rank array)                   changed name back
;;; (array-start array dimension)        new
;;; (array-end array dimension)          new
;;; (array-ref array k ...)
;;; (array-ref array index)              new variant
;;; (array-set! array k ... obj)         changed argument order
;;; (array-set! array index obj)         new variant
;;; (share-array array shape proc)       changed arguments

;;; All other variables in this file have names in "array:".

;;; Should there be a way to make arrays with initial values mapped
;;; from indices? Sure. The current "initial object" is lame.
;;;
;;; Removed (array-shape array) from here. There is a new version
;;; in arlib though.

;;; --- Representation type dependencies ---

;;; The mapping from array indices to the index to the underlying vector
;;; is whatever array:optimize returns. The file "opt" provides three
;;; representations:
;;; 
;;; mbda) mapping is a procedure that allows an optional argument
;;; tter) mapping is two procedures that takes exactly the indices
;;; ctor) mapping is a vector of a constant term and coefficients
;;;
;;; Choose one in "opt" to make the optimizer. Then choose the matching
;;; implementation of array-ref and array-set!.
;;;
;;; These should be made macros to inline them. Or have a good compiler
;;; and plant the package as a module.

;;; 1. Pick an optimizer.
;;; 2. Pick matching index representation.
;;; 3. Pick a record implementation; as-procedure is generic; syntax inlines.
;;; 3. This file is otherwise portable.

;;; --- Portable R5RS (R4RS and multiple values) ---

;;; (array? obj)
;;; returns #t if `obj' is an array and #t or #f otherwise.

(define (array? obj)
   (array:array? obj))

;;; (make-array shape)
;;; (make-array shape obj)
;;; makes array of `shape' with each cell containing `obj' initially.

(define (make-array shape . rest)
  (or (array:good-shape? shape)
      (error "make-array: shape is not a shape"))
  (apply array:make-array shape rest))

(define (array:make-array shape . rest)
  (let ((size (array:size shape)))
    (array:make
     (if (pair? rest)
         (apply (lambda (o) (make-vector size o)) rest)
         (make-vector size))
     (if (= size 0)
         (array:optimize-empty
          (vector-ref (array:shape shape) 1))
         (array:optimize
          (array:make-index shape)
          (vector-ref (array:shape shape) 1)))
     (array:shape->vector shape))))

;;; (shape bound ...)
;;; makes a shape. Bounds must be an even number of exact, pairwise
;;; non-decreasing integers. Note that any such array can be a shape.

(define (shape . bounds)
  (let ((v (list->vector bounds)))
    (or (even? (vector-length v))
        (error (string-append "shape: uneven number of bounds: "
                              (array:list->string bounds))))
    (let ((shp (array:make
                v
                (if (pair? bounds)
                    (array:shape-index)
                    (array:empty-shape-index))
                (vector 0 (quotient (vector-length v) 2)
                        0 2))))
      (or (array:good-shape? shp)
          (error (string-append "shape: bounds are not pairwise "
                                "non-decreasing exact integers: "
                                (array:list->string bounds))))
      shp)))

;;; (array shape obj ...)
;;; is analogous to `vector'.

(define (array shape . elts)
  (or (array:good-shape? shape)
      (error (string-append "array: shape " (array:thing->string shape)
                            " is not a shape")))
  (let ((size (array:size shape)))
    (let ((vector (list->vector elts)))
      (or (= (vector-length vector) size)
          (error (string-append "array: an array of shape "
                                (array:shape-vector->string
                                 (array:vector shape))
                                " has "
                                (number->string size)
                                " elements but got "
                                (number->string (vector-length vector))
                                " values: "
                                (array:list->string elts))))
      (array:make
       vector
       (if (= size 0)
           (array:optimize-empty
            (vector-ref (array:shape shape) 1))
           (array:optimize
            (array:make-index shape)
            (vector-ref (array:shape shape) 1)))
       (array:shape->vector shape)))))

;;; (array-rank array)
;;; returns the number of dimensions of `array'.

(define (array-rank array)
   (quotient (vector-length (array:shape array)) 2))

;;; (array-start array k)
;;; returns the lower bound index of array along dimension k. This is
;;; the least valid index along that dimension if the dimension is not
;;; empty.

(define (array-start array d)
  (vector-ref (array:shape array) (+ d d)))

;;; (array-end array k)
;;; returns the upper bound index of array along dimension k. This is
;;; not a valid index. If the dimension is empty, this is the same as
;;; the lower bound along it.

(define (array-end array d)
  (vector-ref (array:shape array) (+ d d 1)))

;;; (share-array array shape proc)
;;; makes an array that shares elements of `array' at shape `shape'.
;;; The arguments to `proc' are indices of the result.  The values of
;;; `proc' are indices of `array'.

;;; Todo: in the error message, should recognise the mapping and show it.

(define (share-array array subshape f)
  (or (array:good-shape? subshape)
      (error (string-append "share-array: shape "
                            (array:thing->string subshape)
                            " is not a shape")))
  (let ((subsize (array:size subshape)))
    (or (array:good-share? subshape subsize f (array:shape array))
        (error (string-append "share-array: subshape "
                              (array:shape-vector->string
                               (array:vector subshape))
                              " does not map into supershape "
                              (array:shape-vector->string
                               (array:shape array))
                              " under mapping "
                              (array:map->string
                               f
                               (vector-ref (array:shape subshape) 1)))))    
    (let ((g (array:index array)))
      (array:make
       (array:vector array)
       (if (= subsize 0)
           (array:optimize-empty
            (vector-ref (array:shape subshape) 1))
           (array:optimize
            (lambda ks
              (call-with-values
               (lambda () (apply f ks))
               (lambda ks (array:vector-index g ks))))
            (vector-ref (array:shape subshape) 1)))
       (array:shape->vector subshape)))))

;;; --- Hrmph ---

;;; (array:share/index! ...)
;;; reuses a user supplied index object when recognising the
;;; mapping. The mind balks at the very nasty side effect that
;;; exposes the implementation. So this is not in the spec.
;;; But letting index objects in at all creates a pressure
;;; to go the whole hog. Arf.

;;; Use array:optimize-empty for an empty array to get a
;;; clearly invalid vector index.

;;; Surely it's perverse to use an actor for index here? But
;;; the possibility is provided for completeness.

(define (array:share/index! array subshape proc index)
  (array:make
   (array:vector array)
   (if (= (array:size subshape) 0)
       (array:optimize-empty
        (quotient (vector-length (array:shape array)) 2))
       ((if (vector? index)
            array:optimize/vector
            array:optimize/actor)
        (lambda (subindex)
          (let ((superindex (proc subindex)))
            (if (vector? superindex)
                (array:index/vector
                 (quotient (vector-length (array:shape array)) 2)
                 (array:index array)
                 superindex)
                (array:index/array
                 (quotient (vector-length (array:shape array)) 2)
                 (array:index array)
                 (array:vector superindex)
                 (array:index superindex)))))
        index))
   (array:shape->vector subshape)))

(define (array:optimize/vector f v)
  (let ((r (vector-length v)))
    (do ((k 0 (+ k 1)))
      ((= k r))
      (vector-set! v k 0))
    (let ((n0 (f v))
          (cs (make-vector (+ r 1)))
          (apply (array:applier-to-vector (+ r 1))))
      (vector-set! cs 0 n0)
      (let wok ((k 0))
        (if (< k r)
            (let ((k1 (+ k 1)))
              (vector-set! v k 1)
              (let ((nk (- (f v) n0)))
                (vector-set! v k 0)
                (vector-set! cs k1 nk)
                (wok k1)))))
      (apply (array:maker r) cs))))

(define (array:optimize/actor f a)
  (let ((r (array-end a 0))
        (v (array:vector a))
        (i (array:index a)))
    (do ((k 0 (+ k 1)))
      ((= k r))
      (vector-set! v (array:actor-index i k) 0))
    (let ((n0 (f a))
          (cs (make-vector (+ r 1)))
          (apply (array:applier-to-vector (+ r 1))))
      (vector-set! cs 0 n0)
      (let wok ((k 0))
        (if (< k r)
            (let ((k1 (+ k 1))
                  (t (array:actor-index i k)))
              (vector-set! v t 1)
              (let ((nk (- (f a) n0)))
                (vector-set! v t 0)
                (vector-set! cs k1 nk)
                (wok k1)))))
      (apply (array:maker r) cs))))

;;; --- Internals ---

(define (array:shape->vector shape)
  (let ((idx (array:index shape))
        (shv (array:vector shape))
        (rnk (vector-ref (array:shape shape) 1)))
    (let ((vec (make-vector (* rnk 2))))
      (do ((k 0 (+ k 1)))
        ((= k rnk)
         vec)
        (vector-set! vec (+ k k)
                     (vector-ref shv (array:shape-vector-index idx k 0)))
        (vector-set! vec (+ k k 1)
                     (vector-ref shv (array:shape-vector-index idx k 1)))))))

;;; (array:size shape)
;;; returns the number of elements in arrays of shape `shape'.

(define (array:size shape)
   (let ((idx (array:index shape))
         (shv (array:vector shape))
         (rnk (vector-ref (array:shape shape) 1)))
     (do   ((k 0 (+ k 1))
            (s 1 (* s
                    (- (vector-ref shv (array:shape-vector-index idx k 1))
                       (vector-ref shv (array:shape-vector-index idx k 0))))))
       ((= k rnk) s))))

;;; (array:make-index shape)
;;; returns an index function for arrays of shape `shape'. This is a
;;; runtime composition of several variable arity procedures, to be
;;; passed to array:optimize for recognition as an affine function of
;;; as many variables as there are dimensions in arrays of this shape.

(define (array:make-index shape)
   (let ((idx (array:index shape))
         (shv (array:vector shape))
         (rnk (vector-ref (array:shape shape) 1)))
     (do ((f (lambda () 0)
             (lambda (k . ks)
               (+ (* s (- k (vector-ref
                             shv
                             (array:shape-vector-index idx (- j 1) 0))))
                  (apply f ks))))
          (s 1 (* s (- (vector-ref
                        shv
                        (array:shape-vector-index idx (- j 1) 1))
                       (vector-ref
                        shv
                        (array:shape-vector-index idx (- j 1) 0)))))
          (j rnk (- j 1)))
       ((= j 0)
        f))))


;;; --- Error checking ---

;;; (array:good-shape? shape)
;;; returns true if `shape' is an array of the right shape and its
;;; elements are exact integers that pairwise bound intervals `[lo..hi)´.

(define (array:good-shape? shape)
  (and (array:array? shape)
       (let ((u (array:shape shape))
             (v (array:vector shape))
             (x (array:index shape)))
         (and (= (vector-length u) 4)
              (= (vector-ref u 0) 0)
              (= (vector-ref u 2) 0)
              (= (vector-ref u 3) 2))
         (let ((p (vector-ref u 1)))
           (do ((k 0 (+ k 1))
                (true #t (let ((lo (vector-ref
                                    v
                                    (array:shape-vector-index x k 0)))
                               (hi (vector-ref
                                    v
                                    (array:shape-vector-index x k 1))))
                           (and true
                                (integer? lo)
                                (exact? lo)
                                (integer? hi)
                                (exact? hi)
                                (<= lo hi)))))
             ((= k p) true))))))

;;; (array:good-share? subv subsize mapping superv)
;;; returns true if the extreme indices in the subshape vector map
;;; into the bounds in the supershape vector.

;;; If some interval in `subv' is empty, then `subv' is empty and its
;;; image under `f' is empty and it is trivially alright.  One must
;;; not call `f', though.

(define (array:good-share? subshape subsize f super)
  (or (zero? subsize)
      (letrec
          ((sub (array:vector subshape))
           (dex (array:index subshape))
           (ck (lambda (k ks)
		 (if (zero? k)
                     (call-with-values
                      (lambda () (apply f ks))
                      (lambda qs (array:good-indices? qs super)))
                     (and (ck (- k 1)
                              (cons (vector-ref
                                     sub
                                     (array:shape-vector-index
                                      dex
                                      (- k 1)
                                      0))
                                    ks))
                          (ck (- k 1)
                              (cons (- (vector-ref
                                        sub
                                        (array:shape-vector-index
                                         dex
                                         (- k 1)
                                         1))
                                       1)
                                    ks)))))))
        (let ((rnk (vector-ref (array:shape subshape) 1)))
          (or (array:unchecked-share-depth? rnk)
              (ck rnk '()))))))

;;; Check good-share on 10 dimensions at most. The trouble is,
;;; the cost of this check is exponential in the number of dimensions.

(define (array:unchecked-share-depth? rank)
  (if (> rank 10)
      (begin
        (display `(warning: unchecked depth in share:
                            ,rank subdimensions))
        (newline)
        #t)
      #f))

;;; (array:check-indices caller indices shape-vector)
;;; (array:check-indices.o caller indices shape-vector)
;;; (array:check-index-vector caller index-vector shape-vector)
;;; return if the index is in bounds, else signal error.
;;;
;;; Shape-vector is the internal representation, with
;;; b and e for dimension k at 2k and 2k + 1.

(define (array:check-indices who ks shv)
  (or (array:good-indices? ks shv)
      (error (array:not-in who ks shv))))

(define (array:check-indices.o who ks shv)
  (or (array:good-indices.o? ks shv)
      (error (array:not-in who (reverse (cdr (reverse ks))) shv))))

(define (array:check-index-vector who ks shv)
  (or (array:good-index-vector? ks shv)
      (error (array:not-in who (vector->list ks) shv))))

(define (array:check-index-actor who ks shv)
  (let ((shape (array:shape ks)))
    (or (and (= (vector-length shape) 2)
             (= (vector-ref shape 0) 0))
        (error "not an actor"))
    (or (array:good-index-actor?
         (vector-ref shape 1)
         (array:vector ks)
         (array:index ks)
         shv)
        (array:not-in who (do ((k (vector-ref shape 1) (- k 1))
                               (m '() (cons (vector-ref
                                             (array:vector ks)
                                             (array:actor-index
                                              (array:index ks)
                                              (- k 1)))
                                            m)))
                            ((= k 0) m))
                      shv))))

(define (array:good-indices? ks shv)
   (let ((d2 (vector-length shv)))
      (do ((kp ks (if (pair? kp)
                      (cdr kp)))
           (k 0 (+ k 2))
           (true #t (and true (pair? kp)
                         (array:good-index? (car kp) shv k))))
        ((= k d2)
         (and true (null? kp))))))

(define (array:good-indices.o? ks.o shv)
   (let ((d2 (vector-length shv)))
     (do   ((kp ks.o (if (pair? kp)
                         (cdr kp)))
            (k 0 (+ k 2))
            (true #t (and true (pair? kp)
                          (array:good-index? (car kp) shv k))))
       ((= k d2)
        (and true (pair? kp) (null? (cdr kp)))))))

(define (array:good-index-vector? ks shv)
  (let ((r2 (vector-length shv)))
    (and (= (* 2 (vector-length ks)) r2)
         (do ((j 0 (+ j 1))
              (k 0 (+ k 2))
              (true #t (and true
                            (array:good-index? (vector-ref ks j) shv k))))
           ((= k r2) true)))))

(define (array:good-index-actor? r v i shv)
  (and (= (* 2 r) (vector-length shv))
       (do ((j 0 (+ j 1))
            (k 0 (+ k 2))
            (true #t (and true
                          (array:good-index? (vector-ref
                                              v
                                              (array:actor-index i j))
                                             shv
                                             k))))
         ((= j r) true))))

;;; (array:good-index? index shape-vector 2d)
;;; returns true if index is within bounds for dimension 2d/2.

(define (array:good-index? w shv k)
  (and (integer? w)
       (exact? w)
       (<= (vector-ref shv k) w)
       (< w (vector-ref shv (+ k 1)))))

(define (array:not-in who ks shv)
  (let ((index (array:list->string ks))
        (bounds (array:shape-vector->string shv)))
    (error (string-append who
                          ": index " index
                          " not in bounds " bounds))))

(define (array:list->string ks)
  (do ((index "" (string-append index (array:thing->string (car ks)) " "))
       (ks ks (cdr ks)))
    ((null? ks) index)))

(define (array:shape-vector->string shv)
  (do ((bounds "" (string-append bounds
                                 "["
                                 (number->string (vector-ref shv t))
                                 ".."
                                 (number->string (vector-ref shv (+ t 1)))
                                 ")"
                                 " "))
       (t 0 (+ t 2)))
    ((= t (vector-length shv)) bounds)))

(define (array:thing->string thing)
  (cond
    ((number? thing) (number->string thing))
    ((symbol? thing) (string-append "#<symbol>" (symbol->string thing)))
    ((char? thing) "#<char>")
    ((string? thing) "#<string>")
    ((list? thing) (string-append "#" (number->string (length thing))
                                  "<list>"))
                                  
    ((pair? thing) "#<pair>")
    ((array? thing) "#<array>")
    ((vector? thing) (string-append "#" (number->string
                                         (vector-length thing))
                                    "<vector>"))
    ((procedure? thing) "#<procedure>")
    (else
     (case thing
       ((()) "()")
       ((#t) "#t")
       ((#f) "#f")
       (else
        "#<whatsit>")))))

;;; And to grok an affine map, vector->vector type. Column k of arr
;;; will contain coefficients n0 ... nm of 1 k1 ... km for kth value.
;;; 
;;; These are for the error message when share fails.

(define (array:index-ref ind k)
  (if (vector? ind)
      (vector-ref ind k)
      (vector-ref
       (array:vector ind)
       (array:actor-index (array:index ind) k))))

(define (array:index-set! ind k o)
  (if (vector? ind)
      (vector-set! ind k o)
      (vector-set!
       (array:vector ind)
       (array:actor-index (array:index ind) k)
       o)))

(define (array:index-length ind)
  (if (vector? ind)
      (vector-length ind)
      (vector-ref (array:shape ind) 1)))

(define (array:map->string proc r)
  (let* ((m (array:grok/arguments proc r))
         (s (vector-ref (array:shape m) 3)))
    (do ((i "" (string-append i c "k" (number->string k)))
         (c "" ", ")
         (k 1 (+ k 1)))
      ((< r k)
       (do ((o "" (string-append o c (array:map-column->string m r k)))
            (c "" ", ")
            (k 0 (+ k 1)))
         ((= k s)
          (string-append i " => " o)))))))

(define (array:map-column->string m r k)
  (let ((v (array:vector m))
        (i (array:index m)))
    (let ((n0 (vector-ref v (array:vector-index i (list 0 k)))))
      (let wok ((j 1)
                (e (if (= n0 0) "" (number->string n0))))
        (if (<= j r)
            (let ((nj (vector-ref v (array:vector-index i (list j k)))))
              (if (= nj 0)
                  (wok (+ j 1) e)
                  (let* ((nj (if (= nj 1) ""
                                 (if (= nj -1) "-"
                                     (string-append (number->string nj)
                                                    " "))))
                         (njkj (string-append nj "k" (number->string j))))
                    (if (string=? e "")
                        (wok (+ j 1) njkj)
                        (wok (+ j 1) (string-append e " + " njkj))))))
            (if (string=? e "") "0" e))))))

(define (array:grok/arguments proc r)
  (array:grok/index!
   (lambda (vec)
     (call-with-values
      (lambda ()
        (array:apply-to-vector r proc vec))
      vector))
   (make-vector r)))

(define (array:grok/index! proc in)
  (let ((m (array:index-length in)))
    (do ((k 0 (+ k 1)))
      ((= k m))
      (array:index-set! in k 0))
    (let* ((n0 (proc in))
           (n (array:index-length n0)))
      (let ((arr (make-array (shape 0 (+ m 1) 0 n))))  ; (*)
        (do ((k 0 (+ k 1)))
          ((= k n))
          (array-set! arr 0 k (array:index-ref n0 k))) ; (**)
        (do ((j 0 (+ j 1)))
          ((= j m))
          (array:index-set! in j 1)
          (let ((nj (proc in)))
            (array:index-set! in j 0)
            (do ((k 0 (+ k 1)))
              ((= k n))
              (array-set! arr (+ j 1) k (- (array:index-ref nj k) ; (**)
                                           (array:index-ref n0 k))))))
        arr))))
;; (*)  Should not use `make-array' and `shape' here
;; (**) Should not use `array-set!' here
;; Should use something internal to the library instead: either lower
;; level code (preferable but complex) or alternative names to these same.

Added srfi/s25/ix-ctor.scm.



























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
(define (array-ref a . xs)
  (or (array:array? a)
      (error "not an array"))
  (let ((shape (array:shape a)))
    (if (null? xs)
        (array:check-indices "array-ref" xs shape)
        (let ((x (car xs)))
          (if (vector? x)
              (array:check-index-vector "array-ref" x shape)
              (if (integer? x)
                  (array:check-indices "array-ref" xs shape)
                  (if (array:array? x)
                      (array:check-index-actor "array-ref" x shape)
                      (error "not an index object"))))))
    (vector-ref
     (array:vector a)
     (if (null? xs)
         (vector-ref (array:index a) 0)
         (let ((x (car xs)))
           (if (vector? x)
               (array:index/vector
                (quotient (vector-length shape) 2)
                (array:index a)
                x)
               (if (integer? x)
                   (array:vector-index (array:index a) xs)
                   (if (array:array? x)
                       (array:index/array
                        (quotient (vector-length shape) 2)
                        (array:index a)
                        (array:vector x)
                        (array:index x))
                       (error "array-ref: bad index object")))))))))

(define (array-set! a x . xs)
  (or (array:array? a)
      (error "array-set!: not an array"))
  (let ((shape (array:shape a)))
    (if (null? xs)
        (array:check-indices "array-set!" '() shape)
        (if (vector? x)
            (array:check-index-vector "array-set!" x shape)
            (if (integer? x)
                (array:check-indices.o "array-set!" (cons x xs) shape)
                (if (array:array? x)
                    (array:check-index-actor "array-set!" x shape)
                    (error "not an index object")))))
    (if (null? xs)
        (vector-set! (array:vector a) (vector-ref (array:index a) 0) x)
        (if (vector? x)
            (vector-set! (array:vector a)
                         (array:index/vector
                          (quotient (vector-length shape) 2)
                          (array:index a)
                          x)
                         (car xs))
            (if (integer? x)
                (let ((v (array:vector a))
                      (i (array:index a))
                      (r (quotient (vector-length shape) 2)))
                  (do ((sum (* (vector-ref i 0) x)
                            (+ sum (* (vector-ref i k) (car ks))))
                       (ks xs (cdr ks))
                       (k 1 (+ k 1)))
                    ((= k r)
                     (vector-set! v (+ sum (vector-ref i k)) (car ks)))))
                (if (array:array? x)
                    (vector-set! (array:vector a)
                                 (array:index/array
                                  (quotient (vector-length shape) 2)
                                  (array:index a)
                                  (array:vector x)
                                  (array:index x))
                                 (car xs))
                    (error (string-append
                            "array-set!: bad index object: "
                            (array:thing->string x)))))))))

Added srfi/s25/list.scm.











































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
;;; An identity matrix.

(define i_4
  (let* ((i (make-array
             (shape 0 4 0 4)
             0))
         (d (share-array i
                         (shape 0 4)
                         (lambda (k)
                           (values k k)))))
    (do   ((k 0 (+ k 1))) ((= k 4))
      (array-set! d k 1))
    i))

(past "i_4")

(or (array-equal? i_4
                  (tabulate-array
                   (shape 0 4 0 4)
                   (lambda (j k)
                     (if (= j k) 1 0))))
    (error "failed to build i_4"))

(past "i_4 vs tabulate-array")

(or (array-equal? i_4
                  (array
                   (shape 0 4 0 4)
                   1 0 0 0
                   0 1 0 0 
                   0 0 1 0
                   0 0 0 1))
    (error "failed to array i_4"))

(past "i_4 vs array")

(or (array-equal? (share-array
                   i_4
                   (shape 0 4)
                   (lambda (k)
                     (values k k)))
                  (share-array
                   (array (shape) 1)
                   (shape 0 4)
                   (lambda (k)
                     (values))))
    (error "failed to share diagonal of i_4 or cell of 1"))

(past "i_4 diagonal")

(or (array-equal? (share-array
                   i_4
                   (shape 0 4)
                   (lambda (k)
                     (values (- 3 k) k)))
                  (share-array
                   (array (shape) 0)
                   (shape 0 4)
                   (lambda (k)
                     (values))))
    (error "failed to share codiagonal of i_4 or cell of 0"))

(past "i_4 codiagonal")

(or (array-equal? (share-array
                   i_4
                   (shape 0 2 0 2)
                   (lambda (j k)
                     (values (* 3 j) (* 3 k))))
                  (share-array
                   i_4
                   (shape 0 2 0 2)
                   (lambda (j k)
                     (values (+ j 1) (+ k 1)))))
    (error "failed to share corners or center of i_4"))

(past "i_4 corners and center")

(or (array-equal? i_4 (transpose i_4))
    (error "failed to transpose i_4"))

(past "i_4 transpose")

;;; Try a three dimensional transpose. This will also exercise matrix
;;; multiplication.

(define threed123
  (array (shape 0 1 0 2 0 3)
         'a 'b 'c
         'd 'e 'f))

(past "threed123")

(define threed312
  (array (shape 0 3 0 1 0 2)
         'a 'd
         'b 'e
         'c 'f))

(past "threed312")

(define rot231 (list 1 2 0))
  ;; 0 1 0
  ;; 0 0 1
  ;; 1 0 0

(or (array-equal? threed123
                  (apply transpose threed312 rot231))
    (error "failed to transpose three dimensions"))

(past "threed123 transpose")

;;; The frivolous board game exercises share of share of share.

;;; A three dimensional chess board with two phases: piece and colour
;;; of piece. Think of pieces in a cube with height, width and depth,
;;; and piece colours in a parallel cube. We put pink jays around and
;;; grey crows inside the board proper. Later we put in a blue rook.

(define board
  (tabulate-array
   (shape -1 9 -1 9 -1 9 0 2)
   (lambda (t u v w)
     (case w
       ((0) (if (and (< -1 u 8)
                     (< -1 v 8)
                     (< -1 t 8))
                'crow
                'jay))
       ((1) (if (and (< -1 u 8)
                     (< -1 v 8)
                     (< -1 t 8))
                'grey
                'pink))))))

(past "board")

;;; A cylinder with height 4, width 4, depth 6, both phases, centered
;;; inside the board. Top left front corner is at 0 0 0 of cylinder but
;;; 2 2 1 of board.

(define board-cylinder
  (share-array
   board
   (shape 0 4 0 4 0 6 0 2)
   (lambda (t u v w)
     (values (+ t 2) (+ u 2) (+ v 1) w))))

(past "board-cylinder")

;;; The center cube with side 2 of the cylinder, hence of the board,
;;; with both phases. Top left corner is 0 0 0 of center but 1 1 2
;;; of cylinder and 3 3 3 of board.

(define board-center
  (share-array
   board-cylinder
   (shape 0 2 0 2 0 2 0 2)
   (lambda (t u v w)
     (values (+ t 1) (+ u 1) (+ v 2) w))))

(past "board-center")

;;; Front face of center cube, in two dimensions plus phase. Top left
;;; corner is 0 0 of face but 0 0 0 of center and 1 1 2 of cylinder
;;; 3 3 3 of board.

(define board-face
  (share-array
   board-center
   (shape 0 2 0 2 0 2)
   (lambda (t u w)
     (values t u 0 w))))

(past "board-face")

;;; Left side of face in three dimensions plus phase. Top is 0 0 0 of
;;; pillar but 0 0 of face and 0 0 0 of center and 1 1 2 of cylinder
;;; and 3 3 3 of board. Bottom is 1 0 0 of pillar but 1 0 of face and
;;; 1 0 0 of center and 2 1 2 of cylinder and 4 3 3 of board.

(define board-pillar
  (share-array
   board-face
   (shape 0 2 0 1 0 1 0 2)
   (lambda (t u v w)
     (values t 0 w))))

(past "board-pillar")

;;; Pillar upside down. Now top 0 0 0 is 1 0 of face and 1 0 0 of center
;;; and 2 1 2 of cylinder and 4 3 3 of board.

(define board-reverse-pillar
  (share-array
   board-pillar
   (shape 0 2 0 1 0 1 0 2)
   (lambda (t u v w)
     (values (- 1 t) u v w))))

(past "board-reverse-pillar")

;;; Bottom of pillar.

(define board-cubicle
  (share-array
   board-pillar
   (shape 0 2)
   (lambda (w)
     (values 1 0 0 w))))

(past "board-cubicle")

;;; Top of upside down pair.

(define board-reverse-cubicle
  (share-array
   board-reverse-pillar
   (shape 0 2)
   (lambda (w)
     (values 0 0 0 w))))

(past "board-reverse-cubicle")

;;; Piece phase of cubicle.

(define board-piece
  (share-array
   board-cubicle
   (shape)
   (lambda ()
     (values 0))))

(past "board-piece")

;;; Colour phase of the other cubicle that is actually the same cubicle.

(define board-colour
  (share-array
   board-reverse-cubicle
   (shape)
   (lambda ()
     (values 1))))

(past "board-colour")

;;; Put a blue rook at the bottom of the pillar and at the top of the
;;; upside pillar.

(array-set! board-piece 'rook)
(array-set! board-colour 'blue)

(past "array-set! to board-piece and board-colour")

;;; Build the same chess position directly.

(define board-two
  (tabulate-array
   (shape -1 9 -1 9 -1 9 0 2)
   (lambda (t u v w)
     (if (and (= t 4) (= u 3) (= v 3))
         (case w
           ((0) 'rook)
           ((1) 'blue))
         (case w
           ((0) (if (and (< -1 u 8)
                         (< -1 v 8)
                         (< -1 t 8))
                    'crow
                    'jay))
           ((1) (if (and (< -1 u 8)
                         (< -1 v 8)
                         (< -1 t 8))
                    'grey
                    'pink)))))))

(past "board-two")

(or (array-equal? board board-two)
    (error "failed in three dimensional chess"))

(past "board vs board-two")

;;; Permute the dimensions of the chess board in two different ways.
;;; The transpose also exercises matrix multiplication.

(define board-three
  (share-array
   board-two
   (shape 0 2 -1 9 -1 9 -1 9)
   (lambda (w t u v)
     (values t u v w))))

(past "board-three")

(or (array-equal? board-three
                  (transpose board-two 3 0 1 2))
                                    ;; 0 0 0 1
                                    ;; 1 0 0 0
                                    ;; 0 1 0 0
                                    ;; 0 0 1 0
    (error "failed to permute chess board dimensions"))

(past "board-three vs transpose of board-two")

(or (array-equal? (share-array
                   board-two
                   (shape -1 9 0 2 -1 9 -1 9)
                   (lambda (t w u v)
                     (values t u v w)))
                  (transpose board-two 0 3 1 2))
                                    ;; 1 0 0 0
                                    ;; 0 0 0 1
                                    ;; 0 1 0 0
                                    ;; 0 0 1 0
    (error "failed to permute chess board dimensions another way"))

(past "board-two versus transpose of board-two")

;;; Just see that empty share does not crash. No index is valid. Just by
;;; the way. There is nothing to be done with it.

(define board-nothing
  (share-array
   board
   (shape 0 3 1 1 0 3)
   (lambda (t u v)
     (values 0 0 0))))

(or (array-equal? board-nothing (array (array-shape board-nothing)))
    (error "board-nothing failed"))

(past "board-nothing")

;;; ---

(or (array-equal? (tabulate-array (shape 4 8 2 5 0 1) *)
                  (tabulate-array! (shape 4 8 2 5 0 1)
                                   (lambda (v)
                                     (* (vector-ref v 0)
                                        (vector-ref v 1)
                                        (vector-ref v 2)))
                                   (vector * * *)))
    (error "tabulate-array! with vector failed"))

(past "tabulate-array! with vector")

(or (array-equal? (tabulate-array (shape 4 8 2 5 0 1) *)
                  (let ((index (share-array (make-array (shape 0 2 0 3))
                                            (shape 0 3)
                                            (lambda (k) (values 1 k)))))
                    (tabulate-array! (shape 4 8 2 5 0 1)
                                     (lambda (a)
                                       (* (array-ref a 0)
                                          (array-ref a 1)
                                          (array-ref a 2)))
                                     index)))
    (error "tabulate-array! with array failed"))

(past "tabulate-array! with array")

;;; Sum of constants

(or (array-equal?
     (array-map
      +
      (share-array (array (shape) 0) (shape 1 2 1 4) (lambda _ (values)))
      (share-array (array (shape) 1) (shape 1 2 1 4) (lambda _ (values)))
      (share-array (array (shape) 2) (shape 1 2 1 4) (lambda _ (values))))
     (array (shape 1 2 1 4) 3 3 3))
    (error "failed to map constants to their constant sum"))

(past "array-map sum")

;;; Multiplication table

(define four-by-four
  (array (shape 0 4 0 4)
         0 0 0 0
         0 1 2 3
         0 2 4 6
         0 3 6 9))

(past "four-by-four")

(or (array-equal? four-by-four (tabulate-array (shape 0 4 0 4) *))
    (error "failed to tabulate four by four"))

(past "four-by-four vs tabulate-array")

(or (array-equal?
     four-by-four
     (let ((table (make-array (shape 0 4 0 4) 19101)))
       (array-retabulate! table (array-shape table) *)
       table))
    (error "failed to retabulate four by four simply"))

(past "four-by-four vs array-retabulate!")

(or (array-equal?
     four-by-four
     (let ((table (make-array (shape 0 4 0 4) 19101)))
       (array-retabulate!
        table
        (shape 1 2 1 4)
        (lambda (v)
          (* (vector-ref v 0) (vector-ref v 1)))
        (vector - -))
       (array-retabulate!
        table
        (shape 2 4 0 4)
        (lambda (a)
          (* (array-ref a (vector 0)) (array-ref a (vector 1))))
        (make-array (shape 0 2)))
       (array-set! table 0 0 0)
       (array-set! table (vector 0 1) 0)
       (array-set! table (array (shape 0 2) 0 2) 0)
       (shape-for-each
        (shape 0 1 3 4)
        (lambda (v)
          (array-set! table v (vector-ref v 0)))
        (vector - -))
       (let ((arr (share-array
                   table
                   (shape 1 2 0 1 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8)
                   (lambda (r k . _)
                     (values r k)))))
         (array-retabulate! arr (array-shape arr) *))
       table))
    (error "failed to retabulate four by four in a hard way"))

(past "four-by-four vs array-retabulate! on parts")

;;; An argument was missing in a call in arlib when
;;; shape-for-each was called without an index object.

(or (let ((em '()))
      (shape-for-each
       (shape 0 2 -2 0 0 1)
       (lambda (u v w)
         (set! em (cons (list u v w) em))))
      (equal? (reverse em) '((0 -2 0) (0 -1 0) (1 -2 0) (1 -1 0))))
    (error "shape-for-each without index object"))

(past "shape-for-each without index object")
                                 

;;; Exercise share-array/index!

(or (let ((arr (tabulate-array (shape 2 4 3 5 4 7) *)))
      (array-equal? (share-array/index!
                     arr
                     (array-shape arr)
                     (lambda (v) v)
                     (vector * * *))
                    arr))
    (error "share-array/index! with identity and vector failed"))

(past "share-array/index! with identity and vector")

(or (let ((arr (tabulate-array (shape 2 4 3 5 4 7) *))
          (ind (share-array (make-array (shape 0 2 0 3))
                            (shape 0 3)
                            (lambda (k) (values 1 k)))))
      (array-equal? (share-array/index!
                     arr
                     (array-shape arr)
                     (lambda (a) a) ind)
                    arr))
    (error "share-array/index! with identity and array failed"))

(past "share-array/index! with identity and array")

(or (let ((arr (tabulate-array (shape 3 5 4 5 4 7) *))
          (in (vector * *))
          (out (array (shape 0 3) 4 * *)))
      (array-equal? (share-array/index!
                     arr
                     (shape 4 5 4 7)
                     (lambda (in)
                       (array-set! out 1 (vector-ref in 0))
                       (array-set! out 2 (vector-ref in 1))
                       out)
                     in)
                    (share-array
                     arr
                     (shape 4 5 4 7)
                     (lambda (j k)
                       (values 4 j k)))))
    (error "share-array/index! with vector in array out failed"))

(past "share-array/index! with vector in array out")

(or (let ((arr (tabulate-array (shape 3 5 4 5 4 7) *))
          (in (array (shape 0 2) * *))
          (out (vector 4 * *)))
      (array-equal? (share-array/index!
                     arr
                     (shape 4 5 4 7)
                     (lambda (in)
                       (vector-set! out 1 (array-ref in 0))
                       (vector-set! out 2 (array-ref in 1))
                       out)
                     in)
                    (share-array
                     arr
                     (shape 4 5 4 7)
                     (lambda (j k)
                       (values 4 j k)))))
    (error "share-array/index! with array in vector out failed"))

(past "share-array/index! with array in vector out")

(let ((x (array (shape 2 4  3 5  4 5  5 7  6 8)
                10 11 12 13
                20 21 22 23
                30 31 32 33
                40 41 42 43)))
  (or (array-equal? (share-array/origin x 3 3 3 3 3)
                    (array-append 0 (array (shape 3 3
                                                  3 5
                                                  3 4
                                                  3 5
                                                  3 5))
                                  x))
      (error "share-array/origin against empty array-append failed"))
  (or (array-equal? (share-array/origin x 3 3 3 3 3)
                    (array-append 3 (array (shape 3 5
                                                  3 5
                                                  3 4
                                                  3 3
                                                  3 5))
                                  x))
      (error "share-array/origin against empty array-append failed")))

(past "share-array/origin against empty array-append")

(let ((a* (make-array (shape 4 6 7 9 100 101) 'a))
      (b* (make-array (shape 3 6 7 8 200 201) 'b))
      (c* (make-array (shape 0 1 2 4 300 301) 'c)))
  (or (array-equal? (array-append 1 (array-append 0 a* c*) b* b* b*)
                    (apply array (shape 4 7 7 12 100 101)
                           '(a a b b b
                             a a b b b
                             c c b b b)))
      (error "array-append failed")))

(past "array-append")

(let ((a* (make-array (shape 4 6 7 9 100 101) 'a))
      (b* (make-array (shape 3 6 7 8 200 201) 'b))
      (c* (make-array (shape 0 1 2 4 300 301) 'c)))
  (or (array-equal? (array-append 1 a* (transpose c* 1 0 2)
                                  (array-append 0 (transpose b* 1 0 2)
                                                (transpose b* 1 0 2)))
                    (apply array (shape 4 6 7 13 100 101)
                           '(a a c b b b
                             a a c b b b)))
      (error "array-append with transpose failed")))

(past "array-append with transpose")

;;; Check that share-array/index! agrees with share-array.

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? m (share-array m (shape 1 3 1 3) values))
      (error "share-array identity failed"))
  (or (array-equal? m (share-array/index!
                       m (shape 1 3 1 3)
                       (lambda (x) x)
                       (vector * *)))
      (error "share-array/index! identity with vector failed"))
  (or (array-equal? m (share-array/index!
                       m (shape 1 3 1 3)
                       (lambda (x) x)
                       (make-array (shape 0 2))))
      (error "share-array/index! identity with actor failed")))

(past "share-array/index! identity")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape 1 3)
                     (lambda (r)
                       (values r 1)))
                    (share-array/index!
                       m (shape 1 3)
                       (lambda (x)
                         (vector (vector-ref x 0) 1))
                       (vector *)))
      (error "share-array/index! 1-d column failed")))

(past "share-array/index! 1-d column")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape 1 3 1 3)
                     (lambda (r k)
                       (values r 1)))
                    (share-array/index!
                       m (shape 1 3 1 3)
                       (lambda (x)
                         (vector (vector-ref x 0) 1))
                       (vector * *)))
      (error "share-array/index! 2-d column failed")))

(past "share-array/index! 2-d column")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape 1 3)
                     (lambda (k)
                       (values 1 k)))
                    (share-array/index!
                       m (shape 1 3)
                       (lambda (x)
                         (vector 1 (vector-ref x 0)))
                       (vector *)))
      (error "share-array/index! 1-d row failed")))

(past "share-array/index! 1-d row")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape 1 2 1 3)
                     (lambda (r k)
                       (values 1 k)))
                    (share-array/index!
                       m (shape 1 2 1 3)
                       (lambda (x)
                         (vector 1 (vector-ref x 1)))
                       (vector * *)))
      (error "share-array/index! 2-d row failed")))

(past "share-array/index! 2-d row")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape 1 3)
                     (lambda (r)
                       (values r r)))
                    (share-array/index!
                       m (shape 1 3)
                       (lambda (x)
                         (vector (vector-ref x 0) (vector-ref x 0)))
                       (vector *)))
      (error "share-array/index! diagonal failed")))

(past "share-array/index! diagonal")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape)
                     (lambda ()
                       (values 1 2)))
                    (share-array/index!
                       m (shape)
                       (lambda (x)
                         (vector 1 2))
                       (vector)))
      (error "share-array/index! 0-d corner failed")))

(past "share-array/index! 0-d corner")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape 1 2)
                     (lambda (_)
                       (values 1 2)))
                    (share-array/index!
                       m (shape 1 2)
                       (lambda (x)
                         (vector 1 2))
                       (vector *)))
      (error "share-array/index! 1-d corner failed")))

(past "share-array/index! 1-d corner")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array
                     m (shape 1 2 1 2)
                     (lambda (r k)
                       (values 1 2)))
                    (share-array/index!
                       m (shape 1 2 1 2)
                       (lambda (x)
                         (vector 1 2))
                       (vector * *)))
      (error "share-array/index! 2-d corner failed")))

(past "share-array/index! 2-d corner")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array/prefix m 1)
                    (share-array/index!
                     m (shape 1 3)
                     (lambda (x)
                       (vector 1 (vector-ref x 0)))
                     (vector *)))
      (error "share-array/index! with prefix 1 failed")))

(past "share-array/{prefix,index!} 1")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array/prefix m (vector 1))
                    (share-array/index!
                     m (shape 1 3)
                     (lambda (x)
                       (vector 1 (vector-ref x 0)))
                     (vector *)))
      (error "share-array/prefix with vector failed")))

(past "share-array/prefix with vector")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array/prefix m 2)
                    (share-array/index!
                     m (shape 1 3)
                     (lambda (x)
                       (vector 2 (vector-ref x 0)))
                     (vector *)))
      (error "share-array/index! with prefix 2 failed")))

(past "share-array/{prefix,index!} 2")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array/prefix m (array (shape 0 1) 2))
                    (share-array/index!
                     m (shape 1 3)
                     (lambda (x)
                       (vector 2 (vector-ref x 0)))
                     (vector *)))
      (error "share-array/prefix with array failed")))

(past "share-array/prefix with array")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array/prefix m)
                    (share-array/index!
                     m (shape 1 3 1 3)
                     (lambda (x) x)
                     (vector * *)))
      (error "share-array/index! with empty prefix failed")))

(past "share-array/{prefix,index!} e")

(let ((m (array (shape 1 3 1 3) 'a 'b 'c 'd)))
  (or (array-equal? (share-array/prefix m 1 2)
                    (share-array/index!
                     m (shape)
                     (lambda (x)
                       (vector 1 2))
                     (vector)))
      (error "share-array/index! with prefix 1 2 failed")))

(past "share-array/{prefix,index!} 1 2")

;;; Uh oh.

(let* ((hape (tabulate-array
              (shape 0 57 0 2)
              (lambda (r k)
                (case k
                  ((0) r)
                  ((1) (case r
                         ((0)  (+ r 2))
                         ((56) (+ r 4))
                         (else (+ r 1))))))))
       (tape (tabulate-array
              (shape 0 34 0 2)
              (lambda (r k)
                (case k
                  ((0) (+ r 23))
                  ((1) (case r
                         ((33) (+ r 27))
                         (else (+ r 24))))))))
       (long (make-vector 57 *))
       (shot (make-vector 34 *))
       (huge (tabulate-array!
              hape
              (lambda (ix) (vector-ref '#(a b) (vector-ref ix 0)))
              long))
       (tiny0 (share-array/index!
               huge
               tape
               (begin
                 (do ((k 0 (+ k 1)))
                   ((= k 23))
                   (vector-set! long k k))
                 (lambda (ix)
                   (do ((k 23 (+ k 1)))
                     ((= k 57))
                     (vector-set! long k (vector-ref ix (- k 23))))
                   long))
               shot))
       (tiny1 (share-array/index!
               huge
               tape
               (begin
                 (vector-set! long 0 1)
                 (do ((k 1 (+ k 1)))
                   ((= k 23))
                   (vector-set! long k k))
                 (lambda (ix)
                   (do ((k 23 (+ k 1)))
                     ((= k 57))
                     (vector-set! long k (vector-ref ix (- k 23))))
                   long))
               shot)))
  (or (and (equal? (array->vector huge) '#(a a a a b b b b))
           (equal? (array->vector tiny0) '#(a a a a))
           (equal? (array->vector tiny1) '#(b b b b)))
      (error "share-array/index! failed huge or tiny contents"))
  (or (array-equal? huge
                    (share-array/index!
                     (array (shape 4 6) 'a 'b)
                     hape
                     (lambda (ix)
                       (vector-ref '#(#(4) #(5)) (vector-ref ix 0)))
                     long))
      (error "share-array/index! failed huge"))
  (or (array-equal? tiny0
                    (share-array/index!
                     (array (shape 6 7) 'a)
                     tape
                     (lambda (ix) '#(6))
                     shot))
      (error "share-array/index! failed tiny0"))
  (or (array-equal? tiny1
                    (share-array/index!
                     (array (shape 6 7 8 9) 'b)
                     tape
                     (lambda (ix) '#(6 8))
                     shot))
      (error "share-array/index! failed tiny1")))

(past "share-array/index! huge as tiny")

Added srfi/s25/multi-dimensional-arrays.sls.













































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s25 multi-dimensional-arrays)
  (export
    array?
    make-array
    shape
    array
    array-rank
    array-start
    array-end
    array-ref
    array-set!
    share-array)
  (import
    (srfi s25 multi-dimensional-arrays all))
)

Added srfi/s25/multi-dimensional-arrays/all.sls.































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s25 multi-dimensional-arrays all)
  (export
    array:make
    array:array?
    array:vector
    array:index
    array:shape
    array-ref
    array-set!
    array:opt-args
    array:optimize
    array:optimize-empty
    array:coefficients
    array:vector-index
    array:shape-index
    array:empty-shape-index
    array:shape-vector-index
    array:actor-index
    array:0
    array:1
    array:2
    array:3
    array:n
    array:maker
    array:indexer/vector
    array:indexer/array
    array:applier-to-vector
    array:applier-to-actor
    array:applier-to-backing-vector
    array:index/vector
    array:index/array
    array:apply-to-vector
    array:apply-to-actor
    array?
    make-array
    array:make-array
    shape
    array
    array-rank
    array-start
    array-end
    share-array
    array:share/index!
    array:optimize/vector
    array:optimize/actor
    array:shape->vector
    array:size
    array:make-index
    array:good-shape?
    array:good-share?
    array:unchecked-share-depth?
    array:check-indices
    array:check-indices.o
    array:check-index-vector
    array:check-index-actor
    array:good-indices?
    array:good-indices.o?
    array:good-index-vector?
    array:good-index-actor?
    array:good-index?
    array:not-in
    array:list->string
    array:shape-vector->string
    array:thing->string
    array:index-ref
    array:index-set!
    array:index-length
    array:map->string
    array:map-column->string
    array:grok/arguments
    array:grok/index!)
  (import
    (rnrs)
    (rnrs mutable-pairs)
    (rnrs r5rs)
    (srfi s23 error tricks)
    (srfi private include))

  (define-record-type (array-type array:make array:array?)
    (fields (immutable vec array:vector)
            (immutable ind array:index)
            (immutable shp array:shape)))

  (SRFI-23-error->R6RS "(library (srfi s25 multi-dimensional-arrays))"
   (include/resolve ("srfi" "s25") "ix-ctor.scm")
   (include/resolve ("srfi" "s25") "op-ctor.scm")
   (include/resolve ("srfi" "s25") "array.scm"))
)

Added srfi/s25/multi-dimensional-arrays/arlib.sls.

















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s25 multi-dimensional-arrays arlib)
  (export
    array-shape
    array-length
    array-size
    array-equal?
    shape-for-each
    array-for-each-index
    tabulate-array
    tabulate-array!
    array-retabulate!
    array-map
    array-map!
    array->vector
    array->list
    share-array/prefix
    share-row
    share-column
    share-array/origin
    share-array/index!
    array-append
    transpose
    share-nths)
  (import
    (rnrs)
    (rnrs r5rs)
    (srfi s23 error tricks)
    (srfi s25 multi-dimensional-arrays all)
    (srfi private include))

  (SRFI-23-error->R6RS "(library (srfi s25 multi-dimensional-arrays arlib))"
   (include/resolve ("srfi" "s25") "arlib.scm"))
)

Added srfi/s25/op-ctor.scm.











































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
(begin
  (define array:opt-args '(ctor (4)))
  (define (array:optimize f r)
    (case r
      ((0) (let ((n0 (f))) (array:0 n0)))
      ((1) (let ((n0 (f 0))) (array:1 n0 (- (f 1) n0))))
      ((2)
       (let ((n0 (f 0 0)))
         (array:2 n0 (- (f 1 0) n0) (- (f 0 1) n0))))
      ((3)
       (let ((n0 (f 0 0 0)))
         (array:3
           n0
           (- (f 1 0 0) n0)
           (- (f 0 1 0) n0)
           (- (f 0 0 1) n0))))
      (else
       (let ((v
              (do ((k 0 (+ k 1)) (v '() (cons 0 v)))
                  ((= k r) v))))
         (let ((n0 (apply f v)))
           (apply
            array:n
            n0
            (array:coefficients f n0 v v)))))))
  (define (array:optimize-empty r)
    (let ((x (make-vector (+ r 1) 0)))
      (vector-set! x r -1)
      x))
  (define (array:coefficients f n0 vs vp)
    (case vp
      ((()) '())
      (else
       (set-car! vp 1)
       (let ((n (- (apply f vs) n0)))
         (set-car! vp 0)
         (cons n (array:coefficients f n0 vs (cdr vp)))))))
  (define (array:vector-index x ks)
    (do ((sum 0 (+ sum (* (vector-ref x k) (car ks))))
         (ks ks (cdr ks))
         (k 0 (+ k 1)))
        ((null? ks) (+ sum (vector-ref x k)))))
  (define (array:shape-index) '#(2 1 0))
  (define (array:empty-shape-index) '#(0 0 -1))
  (define (array:shape-vector-index x r k)
    (+
     (* (vector-ref x 0) r)
     (* (vector-ref x 1) k)
     (vector-ref x 2)))
  (define (array:actor-index x k)
    (+ (* (vector-ref x 0) k) (vector-ref x 1)))
  (define (array:0 n0) (vector n0))
  (define (array:1 n0 n1) (vector n1 n0))
  (define (array:2 n0 n1 n2) (vector n1 n2 n0))
  (define (array:3 n0 n1 n2 n3) (vector n1 n2 n3 n0))
  (define (array:n n0 n1 n2 n3 n4 . ns)
    (apply vector n1 n2 n3 n4 (append ns (list n0))))
  (define (array:maker r)
    (case r
      ((0) array:0)
      ((1) array:1)
      ((2) array:2)
      ((3) array:3)
      (else array:n)))
  (define array:indexer/vector
    (let ((em
           (vector
             (lambda (x i) (+ (vector-ref x 0)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (vector-ref x 1)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (vector-ref x 2)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (vector-ref x 3)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (* (vector-ref x 3) (vector-ref i 3))
                (vector-ref x 4)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (* (vector-ref x 3) (vector-ref i 3))
                (* (vector-ref x 4) (vector-ref i 4))
                (vector-ref x 5)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (* (vector-ref x 3) (vector-ref i 3))
                (* (vector-ref x 4) (vector-ref i 4))
                (* (vector-ref x 5) (vector-ref i 5))
                (vector-ref x 6)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (* (vector-ref x 3) (vector-ref i 3))
                (* (vector-ref x 4) (vector-ref i 4))
                (* (vector-ref x 5) (vector-ref i 5))
                (* (vector-ref x 6) (vector-ref i 6))
                (vector-ref x 7)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (* (vector-ref x 3) (vector-ref i 3))
                (* (vector-ref x 4) (vector-ref i 4))
                (* (vector-ref x 5) (vector-ref i 5))
                (* (vector-ref x 6) (vector-ref i 6))
                (* (vector-ref x 7) (vector-ref i 7))
                (vector-ref x 8)))
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (* (vector-ref x 3) (vector-ref i 3))
                (* (vector-ref x 4) (vector-ref i 4))
                (* (vector-ref x 5) (vector-ref i 5))
                (* (vector-ref x 6) (vector-ref i 6))
                (* (vector-ref x 7) (vector-ref i 7))
                (* (vector-ref x 8) (vector-ref i 8))
                (vector-ref x 9)))))
          (it
           (lambda (w)
             (lambda (x i)
               (+
                (* (vector-ref x 0) (vector-ref i 0))
                (* (vector-ref x 1) (vector-ref i 1))
                (* (vector-ref x 2) (vector-ref i 2))
                (* (vector-ref x 3) (vector-ref i 3))
                (* (vector-ref x 4) (vector-ref i 4))
                (* (vector-ref x 5) (vector-ref i 5))
                (* (vector-ref x 6) (vector-ref i 6))
                (* (vector-ref x 7) (vector-ref i 7))
                (* (vector-ref x 8) (vector-ref i 8))
                (* (vector-ref x 9) (vector-ref i 9))
                (do ((xi
                      0
                      (+
                       (* (vector-ref x u) (vector-ref i u))
                       xi))
                     (u (- w 1) (- u 1)))
                    ((< u 10) xi))
                (vector-ref x w))))))
      (lambda (r) (if (< r 10) (vector-ref em r) (it r)))))
  (define array:indexer/array
    (let ((em
           (vector
             (lambda (x v i) (+ (vector-ref x 0)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (vector-ref x 1)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (vector-ref x 2)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (vector-ref x 3)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (*
                 (vector-ref x 3)
                 (vector-ref v (array:actor-index i 3)))
                (vector-ref x 4)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (*
                 (vector-ref x 3)
                 (vector-ref v (array:actor-index i 3)))
                (*
                 (vector-ref x 4)
                 (vector-ref v (array:actor-index i 4)))
                (vector-ref x 5)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (*
                 (vector-ref x 3)
                 (vector-ref v (array:actor-index i 3)))
                (*
                 (vector-ref x 4)
                 (vector-ref v (array:actor-index i 4)))
                (*
                 (vector-ref x 5)
                 (vector-ref v (array:actor-index i 5)))
                (vector-ref x 6)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (*
                 (vector-ref x 3)
                 (vector-ref v (array:actor-index i 3)))
                (*
                 (vector-ref x 4)
                 (vector-ref v (array:actor-index i 4)))
                (*
                 (vector-ref x 5)
                 (vector-ref v (array:actor-index i 5)))
                (*
                 (vector-ref x 6)
                 (vector-ref v (array:actor-index i 6)))
                (vector-ref x 7)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (*
                 (vector-ref x 3)
                 (vector-ref v (array:actor-index i 3)))
                (*
                 (vector-ref x 4)
                 (vector-ref v (array:actor-index i 4)))
                (*
                 (vector-ref x 5)
                 (vector-ref v (array:actor-index i 5)))
                (*
                 (vector-ref x 6)
                 (vector-ref v (array:actor-index i 6)))
                (*
                 (vector-ref x 7)
                 (vector-ref v (array:actor-index i 7)))
                (vector-ref x 8)))
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (*
                 (vector-ref x 3)
                 (vector-ref v (array:actor-index i 3)))
                (*
                 (vector-ref x 4)
                 (vector-ref v (array:actor-index i 4)))
                (*
                 (vector-ref x 5)
                 (vector-ref v (array:actor-index i 5)))
                (*
                 (vector-ref x 6)
                 (vector-ref v (array:actor-index i 6)))
                (*
                 (vector-ref x 7)
                 (vector-ref v (array:actor-index i 7)))
                (*
                 (vector-ref x 8)
                 (vector-ref v (array:actor-index i 8)))
                (vector-ref x 9)))))
          (it
           (lambda (w)
             (lambda (x v i)
               (+
                (*
                 (vector-ref x 0)
                 (vector-ref v (array:actor-index i 0)))
                (*
                 (vector-ref x 1)
                 (vector-ref v (array:actor-index i 1)))
                (*
                 (vector-ref x 2)
                 (vector-ref v (array:actor-index i 2)))
                (*
                 (vector-ref x 3)
                 (vector-ref v (array:actor-index i 3)))
                (*
                 (vector-ref x 4)
                 (vector-ref v (array:actor-index i 4)))
                (*
                 (vector-ref x 5)
                 (vector-ref v (array:actor-index i 5)))
                (*
                 (vector-ref x 6)
                 (vector-ref v (array:actor-index i 6)))
                (*
                 (vector-ref x 7)
                 (vector-ref v (array:actor-index i 7)))
                (*
                 (vector-ref x 8)
                 (vector-ref v (array:actor-index i 8)))
                (*
                 (vector-ref x 9)
                 (vector-ref v (array:actor-index i 9)))
                (do ((xi
                      0
                      (+
                       (*
                        (vector-ref x u)
                        (vector-ref
                          v
                          (array:actor-index i u)))
                       xi))
                     (u (- w 1) (- u 1)))
                    ((< u 10) xi))
                (vector-ref x w))))))
      (lambda (r) (if (< r 10) (vector-ref em r) (it r)))))
  (define array:applier-to-vector
    (let ((em
           (vector
             (lambda (p v) (p))
             (lambda (p v) (p (vector-ref v 0)))
             (lambda (p v)
               (p (vector-ref v 0) (vector-ref v 1)))
             (lambda (p v)
               (p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)))
             (lambda (p v)
               (p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)
                (vector-ref v 3)))
             (lambda (p v)
               (p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)
                (vector-ref v 3)
                (vector-ref v 4)))
             (lambda (p v)
               (p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)
                (vector-ref v 3)
                (vector-ref v 4)
                (vector-ref v 5)))
             (lambda (p v)
               (p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)
                (vector-ref v 3)
                (vector-ref v 4)
                (vector-ref v 5)
                (vector-ref v 6)))
             (lambda (p v)
               (p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)
                (vector-ref v 3)
                (vector-ref v 4)
                (vector-ref v 5)
                (vector-ref v 6)
                (vector-ref v 7)))
             (lambda (p v)
               (p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)
                (vector-ref v 3)
                (vector-ref v 4)
                (vector-ref v 5)
                (vector-ref v 6)
                (vector-ref v 7)
                (vector-ref v 8)))))
          (it
           (lambda (r)
             (lambda (p v)
               (apply
                p
                (vector-ref v 0)
                (vector-ref v 1)
                (vector-ref v 2)
                (vector-ref v 3)
                (vector-ref v 4)
                (vector-ref v 5)
                (vector-ref v 6)
                (vector-ref v 7)
                (vector-ref v 8)
                (vector-ref v 9)
                (do ((k r (- k 1))
                     (r
                      '()
                      (cons (vector-ref v (- k 1)) r)))
                    ((= k 10) r)))))))
      (lambda (r) (if (< r 10) (vector-ref em r) (it r)))))
  (define array:applier-to-actor
    (let ((em
           (vector
             (lambda (p a) (p))
             (lambda (p a) (p (array-ref a 0)))
             (lambda (p a)
               (p (array-ref a 0) (array-ref a 1)))
             (lambda (p a)
               (p
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)))
             (lambda (p a)
               (p
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)
                (array-ref a 3)))
             (lambda (p a)
               (p
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)
                (array-ref a 3)
                (array-ref a 4)))
             (lambda (p a)
               (p
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)
                (array-ref a 3)
                (array-ref a 4)
                (array-ref a 5)))
             (lambda (p a)
               (p
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)
                (array-ref a 3)
                (array-ref a 4)
                (array-ref a 5)
                (array-ref a 6)))
             (lambda (p a)
               (p
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)
                (array-ref a 3)
                (array-ref a 4)
                (array-ref a 5)
                (array-ref a 6)
                (array-ref a 7)))
             (lambda (p a)
               (p
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)
                (array-ref a 3)
                (array-ref a 4)
                (array-ref a 5)
                (array-ref a 6)
                (array-ref a 7)
                (array-ref a 8)))))
          (it
           (lambda (r)
             (lambda (p a)
               (apply
                a
                (array-ref a 0)
                (array-ref a 1)
                (array-ref a 2)
                (array-ref a 3)
                (array-ref a 4)
                (array-ref a 5)
                (array-ref a 6)
                (array-ref a 7)
                (array-ref a 8)
                (array-ref a 9)
                (do ((k r (- k 1))
                     (r '() (cons (array-ref a (- k 1)) r)))
                    ((= k 10) r)))))))
      (lambda (r)
        "These are high level, hiding implementation at call site."
        (if (< r 10) (vector-ref em r) (it r)))))
  (define array:applier-to-backing-vector
    (let ((em
           (vector
             (lambda (p ai av) (p))
             (lambda (p ai av)
               (p (vector-ref av (array:actor-index ai 0))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))
                (vector-ref av (array:actor-index ai 3))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))
                (vector-ref av (array:actor-index ai 3))
                (vector-ref av (array:actor-index ai 4))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))
                (vector-ref av (array:actor-index ai 3))
                (vector-ref av (array:actor-index ai 4))
                (vector-ref av (array:actor-index ai 5))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))
                (vector-ref av (array:actor-index ai 3))
                (vector-ref av (array:actor-index ai 4))
                (vector-ref av (array:actor-index ai 5))
                (vector-ref av (array:actor-index ai 6))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))
                (vector-ref av (array:actor-index ai 3))
                (vector-ref av (array:actor-index ai 4))
                (vector-ref av (array:actor-index ai 5))
                (vector-ref av (array:actor-index ai 6))
                (vector-ref av (array:actor-index ai 7))))
             (lambda (p ai av)
               (p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))
                (vector-ref av (array:actor-index ai 3))
                (vector-ref av (array:actor-index ai 4))
                (vector-ref av (array:actor-index ai 5))
                (vector-ref av (array:actor-index ai 6))
                (vector-ref av (array:actor-index ai 7))
                (vector-ref av (array:actor-index ai 8))))))
          (it
           (lambda (r)
             (lambda (p ai av)
               (apply
                p
                (vector-ref av (array:actor-index ai 0))
                (vector-ref av (array:actor-index ai 1))
                (vector-ref av (array:actor-index ai 2))
                (vector-ref av (array:actor-index ai 3))
                (vector-ref av (array:actor-index ai 4))
                (vector-ref av (array:actor-index ai 5))
                (vector-ref av (array:actor-index ai 6))
                (vector-ref av (array:actor-index ai 7))
                (vector-ref av (array:actor-index ai 8))
                (vector-ref av (array:actor-index ai 9))
                (do ((k r (- k 1))
                     (r
                      '()
                      (cons
                       (vector-ref
                         av
                         (array:actor-index ai (- k 1)))
                       r)))
                    ((= k 10) r)))))))
      (lambda (r)
        "These are low level, exposing implementation at call site."
        (if (< r 10) (vector-ref em r) (it r)))))
  (define (array:index/vector r x v)
    ((array:indexer/vector r) x v))
  (define (array:index/array r x av ai)
    ((array:indexer/array r) x av ai))
  (define (array:apply-to-vector r p v)
    ((array:applier-to-vector r) p v))
  (define (array:apply-to-actor r p a)
    ((array:applier-to-actor r) p a)))

Added srfi/s25/test.scm.



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
;;; array test
;;; 2001 Jussi Piitulainen

(define past
  (let ((stones '()))
    (lambda stone
      (if (null? stone)
          (reverse stones)
          (set! stones (cons (apply (lambda (stone) stone) stone) stones))))))

(define (tail n)
  (if (< n (length (past)))
      (list-tail (past) (- (length (past)) n))
      (past)))

;;; Simple tests

(or (and (shape)
         (shape -1 -1)
         (shape -1 0)
         (shape -1 1)
         (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8))
    (error "(shape ...) failed"))

(past "shape")

(or (and (make-array (shape))
         (make-array (shape) *)
         (make-array (shape -1 -1))
         (make-array (shape -1 -1) *)
         (make-array (shape -1 1))
         (make-array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4) *))
    (error "(make-array (shape ...) [o]) failed"))

(past "make-array")

(or (and (array (shape) *)
         (array (shape -1 -1))
         (array (shape -1 1) * *)
         (array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8) *))
    (error "(array (shape ...) ...) failed"))

(past "array")

(or (and (= (array-rank (shape)) 2)
         (= (array-rank (shape -1 -1)) 2)
         (= (array-rank (shape -1 1)) 2)
         (= (array-rank (shape 1 2 3 4 5 6 7 8)) 2))
    (error "(array-rank (shape ...)) failed"))

(past "array-rank of shape")

(or (and (= (array-rank (make-array (shape))) 0)
         (= (array-rank (make-array (shape -1 -1))) 1)
         (= (array-rank (make-array (shape -1 1))) 1)
         (= (array-rank (make-array (shape 1 2 3 4 5 6 7 8))) 4))
    (error "(array-rank (make-array ...)) failed"))

(past "array-rank of make-array")

(or (and (= (array-rank (array (shape) *)) 0)
         (= (array-rank (array (shape -1 -1))) 1)
         (= (array-rank (array (shape -1 1) * *)) 1)
         (= (array-rank (array (shape 1 2 3 4 5 6 7 8) *)) 4))
    (error "(array-rank (array ...)) failed"))

(past "array-rank of array")

(or (and (= (array-start (shape -1 -1) 0) 0)
         (= (array-start (shape -1 -1) 1) 0)
         (= (array-start (shape -1 1) 0) 0)
         (= (array-start (shape -1 1) 1) 0)
         (= (array-start (shape 1 2 3 4 5 6 7 8) 0) 0)
         (= (array-start (shape 1 2 3 4 5 6 7 8) 1) 0))
    (error "(array-start (shape ...)) failed"))

(past "array-start of shape")

(or (and (= (array-end (shape -1 -1) 0) 1)
         (= (array-end (shape -1 -1) 1) 2)
         (= (array-end (shape -1 1) 0) 1)
         (= (array-end (shape -1 1) 1) 2)
         (= (array-end (shape 1 2 3 4 5 6 7 8) 0) 4)
         (= (array-end (shape 1 2 3 4 5 6 7 8) 1) 2))
    (error "(array-end (shape ...)) failed"))

(past "array-end of shape")

(or (and (= (array-start (make-array (shape -1 -1)) 0) -1)
         (= (array-start (make-array (shape -1 1)) 0) -1)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 0) 1)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 1) 3)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 2) 5)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 3) 7))
    (error "(array-start (make-array ...)) failed"))

(past "array-start of make-array")

(or (and (= (array-end (make-array (shape -1 -1)) 0) -1)
         (= (array-end (make-array (shape -1 1)) 0) 1)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 0) 2)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 1) 4)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 2) 6)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 3) 8))
    (error "(array-end (make-array ...)) failed"))

(past "array-end of make-array")

(or (and (= (array-start (array (shape -1 -1)) 0) -1)
         (= (array-start (array (shape -1 1) * *) 0) -1)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 0) 1)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 1) 3)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 2) 5)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 3) 7))
    (error "(array-start (array ...)) failed"))

(past "array-start of array")

(or (and (= (array-end (array (shape -1 -1)) 0) -1)
         (= (array-end (array (shape -1 1) * *) 0) 1)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 0) 2)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 1) 4)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 2) 6)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 3) 8))
    (error "(array-end (array ...)) failed"))

(past "array-end of array")

(or (and (eq? (array-ref (make-array (shape) 'a)) 'a)
         (eq? (array-ref (make-array (shape -1 1) 'b) -1) 'b)
         (eq? (array-ref (make-array (shape -1 1) 'c) 0) 'c)
         (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) 1 3 5 7) 'd))
    (error "array-ref of make-array with arguments failed"))

(past "array-ref of make-array with arguments")

(or (and (eq? (array-ref (make-array (shape) 'a) '#()) 'a)
         (eq? (array-ref (make-array (shape -1 1) 'b) '#(-1)) 'b)
         (eq? (array-ref (make-array (shape -1 1) 'c) '#(0)) 'c)
         (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd)
                         '#(1 3 5 7))
              'd))
    (error "array-ref of make-array with vector failed"))

(past "array-ref of make-array with vector")

(or (and (eq? (array-ref (make-array (shape) 'a)
                         (array (shape 0 0)))
              'a)
         (eq? (array-ref (make-array (shape -1 1) 'b)
                         (array (shape 0 1) -1))
              'b)
         (eq? (array-ref (make-array (shape -1 1) 'c)
                         (array (shape 0 1) 0))
              'c)
         (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd)
                         (array (shape 0 4) 1 3 5 7))
              'd))
    (error "(array-ref of make-array with array failed"))

(past "array-ref of make-array with array")

(or (and (let ((arr (make-array (shape) 'o)))
           (array-set! arr 'a)
           (eq? (array-ref arr) 'a))
         (let ((arr (make-array (shape -1 1) 'o)))
           (array-set! arr -1 'b)
           (array-set! arr 0 'c)
           (and (eq? (array-ref arr -1) 'b)
                (eq? (array-ref arr 0) 'c)))
         (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o)))
           (array-set! arr 1 3 5 7 'd)
           (eq? (array-ref arr 1 3 5 7) 'd)))
    (error "array-set! with arguments failed"))

(past "array-set! of make-array with arguments")

(or (and (let ((arr (make-array (shape) 'o)))
           (array-set! arr '#() 'a)
           (eq? (array-ref arr) 'a))
         (let ((arr (make-array (shape -1 1) 'o)))
           (array-set! arr '#(-1) 'b)
           (array-set! arr '#(0) 'c)
           (and (eq? (array-ref arr -1) 'b)
                (eq? (array-ref arr 0) 'c)))
         (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o)))
           (array-set! arr '#(1 3 5 7) 'd)
           (eq? (array-ref arr 1 3 5 7) 'd)))
    (error "array-set! with vector failed"))

(past "array-set! of make-array with vector")

(or (and (let ((arr (make-array (shape) 'o)))
           (array-set! arr 'a)
           (eq? (array-ref arr) 'a))
         (let ((arr (make-array (shape -1 1) 'o)))
           (array-set! arr (array (shape 0 1) -1) 'b)
           (array-set! arr (array (shape 0 1) 0) 'c)
           (and (eq? (array-ref arr -1) 'b)
                (eq? (array-ref arr 0) 'c)))
         (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o)))
           (array-set! arr (array (shape 0 4) 1 3 5 7) 'd)
           (eq? (array-ref arr 1 3 5 7) 'd)))
    (error "array-set! with arguments failed"))

(past "array-set! of make-array with array")

;;; Share and change:
;;;
;;;  org     brk     swp            box
;;;
;;;   0 1     1 2     5 6
;;; 6 a b   2 a b   3 d c   0 2 4 6 8: e
;;; 7 c d   3 e f   4 f e
;;; 8 e f

(or (let* ((org (array (shape 6 9 0 2) 'a 'b 'c 'd 'e 'f))
           (brk (share-array
                 org
                 (shape 2 4 1 3)
                 (lambda (r k)
                   (values
                    (+ 6 (* 2 (- r 2)))
                    (- k 1)))))
           (swp (share-array
                 org
                 (shape 3 5 5 7)
                 (lambda (r k)
                   (values
                    (+ 7 (- r 3))
                    (- 1 (- k 5))))))
           (box (share-array
                 swp
                 (shape 0 1 2 3 4 5 6 7 8 9)
                 (lambda _ (values 4 6))))
           (org-contents (lambda ()
                           (list (array-ref org 6 0) (array-ref org 6 1)
                                 (array-ref org 7 0) (array-ref org 7 1)
                                 (array-ref org 8 0) (array-ref org 8 1))))
           (brk-contents (lambda ()
                           (list (array-ref brk 2 1) (array-ref brk 2 2)
                                 (array-ref brk 3 1) (array-ref brk 3 2))))
           (swp-contents (lambda ()
                           (list (array-ref swp 3 5) (array-ref swp 3 6)
                                 (array-ref swp 4 5) (array-ref swp 4 6))))
           (box-contents (lambda ()
                           (list (array-ref box 0 2 4 6 8)))))
      (and (equal? (org-contents) '(a b c d e f))
           (equal? (brk-contents) '(a b e f))
           (equal? (swp-contents) '(d c f e))
           (equal? (box-contents) '(e))
           (begin (array-set! org 6 0 'x) #t)
           (equal? (org-contents) '(x b c d e f))
           (equal? (brk-contents) '(x b e f))
           (equal? (swp-contents) '(d c f e))
           (equal? (box-contents) '(e))
           (begin (array-set! brk 3 1 'y) #t)
           (equal? (org-contents) '(x b c d y f))
           (equal? (brk-contents) '(x b y f))
           (equal? (swp-contents) '(d c f y))
           (equal? (box-contents) '(y))
           (begin (array-set! swp 4 5 'z) #t)
           (equal? (org-contents) '(x b c d y z))
           (equal? (brk-contents) '(x b y z))
           (equal? (swp-contents) '(d c z y))
           (equal? (box-contents) '(y))
           (begin (array-set! box 0 2 4 6 8 'e) #t)
           (equal? (org-contents) '(x b c d e z))
           (equal? (brk-contents) '(x b e z))
           (equal? (swp-contents) '(d c z e))
           (equal? (box-contents) '(e))))
    (error "shared change failed"))

(past "shared change")

;;; Check that arrays copy the shape specification

(or (let ((shp (shape 10 12)))
      (let ((arr (make-array shp))
            (ars (array shp * *))
            (art (share-array (make-array shp) shp (lambda (k) k))))
        (array-set! shp 0 0 '?)
        (array-set! shp 0 1 '!)
        (and (= (array-rank shp) 2)
             (= (array-start shp 0) 0)
             (= (array-end shp 0) 1)
             (= (array-start shp 1) 0)
             (= (array-end shp 1) 2)
             (eq? (array-ref shp 0 0) '?)
             (eq? (array-ref shp 0 1) '!)
             (= (array-rank arr) 1)
             (= (array-start arr 0) 10)
             (= (array-end arr 0) 12)
             (= (array-rank ars) 1)
             (= (array-start ars 0) 10)
             (= (array-end ars 0) 12)
             (= (array-rank art) 1)
             (= (array-start art 0) 10)
             (= (array-end art 0) 12))))
    (error "array-set! of shape failed"))

(past "array-set! of shape")

;;; Check that index arrays work even when they share
;;;
;;; arr       ixn
;;;   5  6      0 1
;;; 4 nw ne   0 4 6
;;; 5 sw se   1 5 4

(or (let ((arr (array (shape 4 6 5 7) 'nw 'ne 'sw 'se))
          (ixn (array (shape 0 2 0 2) 4 6 5 4)))
      (let ((col0 (share-array
                   ixn
                   (shape 0 2)
                   (lambda (k)
                     (values k 0))))
            (row0 (share-array
                   ixn
                   (shape 0 2)
                   (lambda (k)
                     (values 0 k))))
            (wor1 (share-array
                   ixn
                   (shape 0 2)
                   (lambda (k)
                     (values 1 (- 1 k)))))
            (cod (share-array
                  ixn
                  (shape 0 2)
                  (lambda (k)
                    (case k
                      ((0) (values 1 0))
                      ((1) (values 0 1))))))
            (box (share-array
                  ixn
                  (shape 0 2)
                  (lambda (k)
                    (values 1 0)))))
        (and (eq? (array-ref arr col0) 'nw)
             (eq? (array-ref arr row0) 'ne)
             (eq? (array-ref arr wor1) 'nw)
             (eq? (array-ref arr cod) 'se)
             (eq? (array-ref arr box) 'sw)
             (begin
               (array-set! arr col0 'ul)
               (array-set! arr row0 'ur)
               (array-set! arr cod 'lr)
               (array-set! arr box 'll)
               #t)
             (eq? (array-ref arr 4 5) 'ul)
             (eq? (array-ref arr 4 6) 'ur)
             (eq? (array-ref arr 5 5) 'll)
             (eq? (array-ref arr 5 6) 'lr)
             (begin
               (array-set! arr wor1 'xx)
               (eq? (array-ref arr 4 5) 'xx)))))
    (error "array access with sharing index array failed"))

(past "array access with sharing index array")

;;; Check that shape arrays work even when they share
;;;
;;; arr             shp       shq       shr       shs
;;;    1  2  3  4      0  1      0  1      0  1      0  1 
;;; 1 10 12 16 20   0 10 12   0 12 20   0 10 10   0 12 12
;;; 2 10 11 12 13   1 10 11   1 11 13   1 11 12   1 12 12
;;;                                     2 12 16
;;;                                     3 13 20

(or (let ((arr (array (shape 1 3 1 5) 10 12 16 20 10 11 12 13)))
      (let ((shp (share-array
                  arr
                  (shape 0 2 0 2)
                  (lambda (r k)
                    (values (+ r 1) (+ k 1)))))
            (shq (share-array
                  arr
                  (shape 0 2 0 2)
                  (lambda (r k)
                    (values (+ r 1) (* 2 (+ 1 k))))))
            (shr (share-array
                  arr
                  (shape 0 4 0 2)
                  (lambda (r k)
                    (values (- 2 k) (+ r 1)))))
            (shs (share-array
                  arr
                  (shape 0 2 0 2)
                  (lambda (r k)
                    (values 2 3)))))
        (and (let ((arr-p (make-array shp)))
               (and (= (array-rank arr-p) 2)
                    (= (array-start arr-p 0) 10)
                    (= (array-end arr-p 0) 12)
                    (= (array-start arr-p 1) 10)
                    (= (array-end arr-p 1) 11)))
             (let ((arr-q (array shq * * * *  * * * *  * * * *  * * * *)))
               (and (= (array-rank arr-q) 2)
                    (= (array-start arr-q 0) 12)
                    (= (array-end arr-q 0) 20)
                    (= (array-start arr-q 1) 11)
                    (= (array-end arr-q 1) 13)))
             (let ((arr-r (share-array
                           (array (shape) *)
                           shr
                           (lambda _ (values)))))
               (and (= (array-rank arr-r) 4)
                    (= (array-start arr-r 0) 10)
                    (= (array-end arr-r 0) 10)
                    (= (array-start arr-r 1) 11)
                    (= (array-end arr-r 1) 12)
                    (= (array-start arr-r 2) 12)
                    (= (array-end arr-r 2) 16)
                    (= (array-start arr-r 3) 13)
                    (= (array-end arr-r 3) 20)))
             (let ((arr-s (make-array shs)))
               (and (= (array-rank arr-s) 2)
                    (= (array-start arr-s 0) 12)
                    (= (array-end arr-s 0) 12)
                    (= (array-start arr-s 1) 12)
                    (= (array-end arr-s 1) 12))))))
    (error "sharing shape array failed"))

(past "sharing shape array")

(let ((super (array (shape 4 7 4 7)
                    1 * *
                    * 2 *
                    * * 3))
      (subshape (share-array
                 (array (shape 0 2 0 3)
                        * 4 *
                        * 7 *)
                 (shape 0 1 0 2)
                 (lambda (r k)
                   (values k 1)))))
  (let ((sub (share-array super subshape (lambda (k) (values k k)))))
    ;(array-equal? subshape (shape 4 7))
    (or (and (= (array-rank subshape) 2)
             (= (array-start subshape 0) 0)
             (= (array-end subshape 0) 1)
             (= (array-start subshape 1) 0)
             (= (array-end subshape 1) 2)
             (= (array-ref subshape 0 0) 4)
             (= (array-ref subshape 0 1) 7))
        (error "sharing subshape failed"))
    ;(array-equal? sub (array (shape 4 7) 1 2 3))
    (or (and (= (array-rank sub) 1)
             (= (array-start sub 0) 4)
             (= (array-end sub 0) 7)
             (= (array-ref sub 4) 1)
             (= (array-ref sub 5) 2)
             (= (array-ref sub 6) 3))
        (error "sharing with sharing subshape failed"))))

(past "sharing with sharing subshape")

Added srfi/s26/cut.scm.

































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
; REFERENCE IMPLEMENTATION FOR SRFI-26 "CUT"
; ==========================================
;
; Sebastian.Egner@philips.com, 5-Jun-2002.
; adapted from the posting by Al Petrofsky <al@petrofsky.org>
; placed in the public domain
;
; The code to handle the variable argument case was originally
; proposed by Michael Sperber and has been adapted to the new
; syntax of the macro using an explicit rest-slot symbol. The
; code to evaluate the non-slots for cute has been proposed by
; Dale Jordan. The code to allow a slot for the procedure position
; and to process the macro using an internal macro is based on 
; a suggestion by Al Petrofsky. The code found below is, with
; exception of this header and some changes in variable names,
; entirely written by Al Petrofsky.
;
; compliance:
;   Scheme R5RS (including macros).
;
; loading this file into Scheme 48 0.57:
;   ,load cut.scm
;
; history of this file:
;   SE,  6-Feb-2002: initial version as 'curry' with ". <>" notation
;   SE, 14-Feb-2002: revised for <...>
;   SE, 27-Feb-2002: revised for 'cut'
;   SE, 03-Jun-2002: revised for proc-slot, cute
;   SE, 04-Jun-2002: rewritten with internal transformer (no "loop" pattern)
;   SE, 05-Jun-2002: replace my code by Al's; substituted "constant" etc.
;     to match the convention in the SRFI-document

; (srfi-26-internal-cut slot-names combination . se)
;   transformer used internally
;     slot-names  : the internal names of the slots
;     combination : procedure being specialized, followed by its arguments
;     se          : slots-or-exprs, the qualifiers of the macro

(define-syntax srfi-26-internal-cut
  (syntax-rules (<> <...>)

    ;; construct fixed- or variable-arity procedure:
    ;;   (begin proc) throws an error if proc is not an <expression>
    ((srfi-26-internal-cut (slot-name ...) (proc arg ...))
     (lambda (slot-name ...) ((begin proc) arg ...)))
    ((srfi-26-internal-cut (slot-name ...) (proc arg ...) <...>)
     (lambda (slot-name ... . rest-slot) (apply proc arg ... rest-slot)))

    ;; process one slot-or-expr
    ((srfi-26-internal-cut (slot-name ...)   (position ...)      <>  . se)
     (srfi-26-internal-cut (slot-name ... x) (position ... x)        . se))
    ((srfi-26-internal-cut (slot-name ...)   (position ...)      nse . se)
     (srfi-26-internal-cut (slot-name ...)   (position ... nse)      . se))))

; (srfi-26-internal-cute slot-names nse-bindings combination . se)
;   transformer used internally
;     slot-names     : the internal names of the slots
;     nse-bindings   : let-style bindings for the non-slot expressions.
;     combination    : procedure being specialized, followed by its arguments
;     se             : slots-or-exprs, the qualifiers of the macro

(define-syntax srfi-26-internal-cute
  (syntax-rules (<> <...>)

    ;; If there are no slot-or-exprs to process, then:
    ;; construct a fixed-arity procedure,
    ((srfi-26-internal-cute
      (slot-name ...) nse-bindings (proc arg ...))
     (let nse-bindings (lambda (slot-name ...) (proc arg ...))))
    ;; or a variable-arity procedure
    ((srfi-26-internal-cute
      (slot-name ...) nse-bindings (proc arg ...) <...>)
     (let nse-bindings (lambda (slot-name ... . x) (apply proc arg ... x))))

    ;; otherwise, process one slot:
    ((srfi-26-internal-cute
      (slot-name ...)         nse-bindings  (position ...)   <>  . se)
     (srfi-26-internal-cute
      (slot-name ... x)       nse-bindings  (position ... x)     . se))
    ;; or one non-slot expression
    ((srfi-26-internal-cute
      slot-names              nse-bindings  (position ...)   nse . se)
     (srfi-26-internal-cute
      slot-names ((x nse) . nse-bindings) (position ... x)       . se))))

; exported syntax

(define-syntax cut
  (syntax-rules ()
    ((cut . slots-or-exprs)
     (srfi-26-internal-cut () () . slots-or-exprs))))

(define-syntax cute
  (syntax-rules ()
    ((cute . slots-or-exprs)
     (srfi-26-internal-cute () () () . slots-or-exprs))))

Added srfi/s26/cut.sls.



























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s26 cut)
  (export cut cute)
  (import (rnrs) (srfi private include))
  
  (include/resolve ("srfi" "s26") "cut.scm")  
)

Added srfi/s27/random-bits.sls.





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s27 random-bits)
  (export random-integer
          random-real
          default-random-source
          make-random-source
          random-source?
          random-source-state-ref
          random-source-state-set!
          random-source-randomize!
          random-source-pseudo-randomize!
          random-source-make-integers
          random-source-make-reals)
  
  (import (rnrs)
          (rnrs r5rs)
          (only (srfi s19 time) time-nanosecond current-time)
          (srfi s23 error tricks)
          (srfi private include)
          )
    
   (SRFI-23-error->R6RS "(library (srfi s27 random-bits))"
    (include/resolve ("srfi" "s27") "random.ss"))
  )

Added srfi/s27/random.ss.

















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
;; R6RS port of the Scheme48 reference implementation of SRFI-27 

; MODULE DEFINITION FOR SRFI-27
; =============================
; 
; Sebastian.Egner@philips.com, Mar-2002, in Scheme 48 0.57

; 1. The core generator is implemented in 'mrg32k3a-a.scm'.
; 2. The generic parts of the interface are in 'mrg32k3a.scm'.
; 3. The non-generic parts (record type, time, error) are here.

; history of this file:
;   SE, 22-Mar-2002: initial version
;   SE, 27-Mar-2002: checked again
;   JS, 06-Dec-2007: R6RS port

(define-record-type :random-source
  (fields state-ref
          state-set!
          randomize!
          pseudo-randomize!
          make-integers
          make-reals))

(define :random-source-make make-:random-source)
(define state-ref :random-source-state-ref)
(define state-set! :random-source-state-set!)
(define randomize! :random-source-randomize!)
(define pseudo-randomize! :random-source-pseudo-randomize!)
(define make-integers :random-source-make-integers)
(define make-reals :random-source-make-reals)

(define (:random-source-current-time)
  (time-nanosecond (current-time)))


;;; mrg32k3a-a.ss

; 54-BIT INTEGER IMPLEMENTATION OF THE "MRG32K3A"-GENERATOR
; =========================================================
;
; Sebastian.Egner@philips.com, Mar-2002.
;
; This file is an implementation of Pierre L'Ecuyer's MRG32k3a
; pseudo random number generator. Please refer to 'mrg32k3a.scm'
; for more information.
;
; compliance:
;   Scheme R5RS with integers covering at least {-2^53..2^53-1}.
;
; history of this file:
;   SE, 18-Mar-2002: initial version
;   SE, 22-Mar-2002: comments adjusted, range added
;   SE, 25-Mar-2002: pack/unpack just return their argument

; the actual generator

(define (mrg32k3a-random-m1 state)
  (let ((x11 (vector-ref state 0))
        (x12 (vector-ref state 1))
        (x13 (vector-ref state 2))
        (x21 (vector-ref state 3))
        (x22 (vector-ref state 4))
        (x23 (vector-ref state 5)))
    (let ((x10 (modulo (- (* 1403580 x12) (* 810728 x13)) 4294967087))
          (x20 (modulo (- (* 527612 x21) (* 1370589 x23)) 4294944443)))
      (vector-set! state 0 x10)
      (vector-set! state 1 x11)
      (vector-set! state 2 x12)
      (vector-set! state 3 x20)
      (vector-set! state 4 x21)
      (vector-set! state 5 x22)
      (modulo (- x10 x20) 4294967087))))

; interface to the generic parts of the generator

(define (mrg32k3a-pack-state unpacked-state)
  unpacked-state)

(define (mrg32k3a-unpack-state state)
  state)

(define (mrg32k3a-random-range) ; m1
  4294967087)

(define (mrg32k3a-random-integer state range) ; rejection method
  (let* ((q (quotient 4294967087 range))
         (qn (* q range)))
    (do ((x (mrg32k3a-random-m1 state) (mrg32k3a-random-m1 state)))
      ((< x qn) (quotient x q)))))

(define (mrg32k3a-random-real state) ; normalization is 1/(m1+1)
  (* 0.0000000002328306549295728 (+ 1.0 (mrg32k3a-random-m1 state))))


;;; mrg32k3a.ss

; GENERIC PART OF MRG32k3a-GENERATOR FOR SRFI-27
; ==============================================
;
; Sebastian.Egner@philips.com, 2002.
;
; This is the generic R5RS-part of the implementation of the MRG32k3a
; generator to be used in SRFI-27. It is based on a separate implementation
; of the core generator (presumably in native code) and on code to
; provide essential functionality not available in R5RS (see below).
;
; compliance:
;   Scheme R5RS with integer covering at least {-2^53..2^53-1}.
;   In addition,
;     SRFI-23: error
;
; history of this file:
;   SE, 22-Mar-2002: refactored from earlier versions
;   SE, 25-Mar-2002: pack/unpack need not allocate
;   SE, 27-Mar-2002: changed interface to core generator
;   SE, 10-Apr-2002: updated spec of mrg32k3a-random-integer

; Generator
; =========
;
; Pierre L'Ecuyer's MRG32k3a generator is a Combined Multiple Recursive 
; Generator. It produces the sequence {(x[1,n] - x[2,n]) mod m1 : n} 
; defined by the two recursive generators
;
;   x[1,n] = (               a12 x[1,n-2] + a13 x[1,n-3]) mod m1,
;   x[2,n] = (a21 x[2,n-1] +                a23 x[2,n-3]) mod m2,
;
; where the constants are
;   m1       = 4294967087 = 2^32 - 209    modulus of 1st component
;   m2       = 4294944443 = 2^32 - 22853  modulus of 2nd component
;   a12      =  1403580                   recursion coefficients
;   a13      =  -810728
;   a21      =   527612
;   a23      = -1370589
;
; The generator passes all tests of G. Marsaglia's Diehard testsuite.
; Its period is (m1^3 - 1)(m2^3 - 1)/2 which is nearly 2^191.
; L'Ecuyer reports: "This generator is well-behaved in all dimensions
; up to at least 45: ..." [with respect to the spectral test, SE].
;
; The period is maximal for all values of the seed as long as the
; state of both recursive generators is not entirely zero.
;
; As the successor state is a linear combination of previous
; states, it is possible to advance the generator by more than one
; iteration by applying a linear transformation. The following
; publication provides detailed information on how to do that:
;
;    [1] P. L'Ecuyer, R. Simard, E. J. Chen, W. D. Kelton:
;        An Object-Oriented Random-Number Package With Many Long 
;        Streams and Substreams. 2001.
;        To appear in Operations Research.
;
; Arithmetics
; ===========
;
; The MRG32k3a generator produces values in {0..2^32-209-1}. All 
; subexpressions of the actual generator fit into {-2^53..2^53-1}. 
; The code below assumes that Scheme's "integer" covers this range.
; In addition, it is assumed that floating point literals can be
; read and there is some arithmetics with inexact numbers.
;
; However, for advancing the state of the generator by more than
; one step at a time, the full range {0..2^32-209-1} is needed.


; Required: Backbone Generator
; ============================
;
; At this point in the code, the following procedures are assumed
; to be defined to execute the core generator:
;
;   (mrg32k3a-pack-state unpacked-state) -> packed-state
;   (mrg32k3a-unpack-state packed-state) -> unpacked-state
;      pack/unpack a state of the generator. The core generator works
;      on packed states, passed as an explicit argument, only. This
;      allows native code implementations to store their state in a
;      suitable form. Unpacked states are #(x10 x11 x12 x20 x21 x22) 
;      with integer x_ij. Pack/unpack need not allocate new objects
;      in case packed and unpacked states are identical.
;
;   (mrg32k3a-random-range) -> m-max
;   (mrg32k3a-random-integer packed-state range) -> x in {0..range-1}
;      advance the state of the generator and return the next random
;      range-limited integer. 
;        Note that the state is not necessarily advanced by just one 
;      step because we use the rejection method to avoid any problems 
;      with distribution anomalies.
;        The range argument must be an exact integer in {1..m-max}.
;      It can be assumed that range is a fixnum if the Scheme system
;      has such a number representation.
;
;   (mrg32k3a-random-real packed-state) -> x in (0,1)
;      advance the state of the generator and return the next random
;      real number between zero and one (both excluded). The type of
;      the result should be a flonum if possible.

; Required: Record Data Type
; ==========================
;
; At this point in the code, the following procedures are assumed
; to be defined to create and access a new record data type:
;
;   (:random-source-make a0 a1 a2 a3 a4 a5) -> s
;     constructs a new random source object s consisting of the 
;     objects a0 .. a5 in this order.
;
;   (:random-source? obj) -> bool
;     tests if a Scheme object is a :random-source.
;
;   (:random-source-state-ref         s) -> a0
;   (:random-source-state-set!        s) -> a1
;   (:random-source-randomize!        s) -> a2
;   (:random-source-pseudo-randomize! s) -> a3
;   (:random-source-make-integers     s) -> a4
;   (:random-source-make-reals        s) -> a5
;     retrieve the values in the fields of the object s.

; Required: Current Time as an Integer
; ====================================
;
; At this point in the code, the following procedure is assumed
; to be defined to obtain a value that is likely to be different
; for each invokation of the Scheme system:
;
;   (:random-source-current-time) -> x
;     an integer that depends on the system clock. It is desired
;     that the integer changes as fast as possible.


; Accessing the State
; ===================

(define (mrg32k3a-state-ref packed-state)
  (cons 'lecuyer-mrg32k3a 
        (vector->list (mrg32k3a-unpack-state packed-state))))

(define (mrg32k3a-state-set external-state)
  
  (define (check-value x m)
    (if (and (integer? x)
             (exact? x)
             (<= 0 x (- m 1)))
        #t
        (error "illegal value" x)))
  
  (if (and (list? external-state)
           (= (length external-state) 7)
           (eq? (car external-state) 'lecuyer-mrg32k3a))
      (let ((s (cdr external-state)))
        (check-value (list-ref s 0) mrg32k3a-m1)
        (check-value (list-ref s 1) mrg32k3a-m1)
        (check-value (list-ref s 2) mrg32k3a-m1)
        (check-value (list-ref s 3) mrg32k3a-m2)
        (check-value (list-ref s 4) mrg32k3a-m2)
        (check-value (list-ref s 5) mrg32k3a-m2)
        (if (or (zero? (+ (list-ref s 0) (list-ref s 1) (list-ref s 2)))
                (zero? (+ (list-ref s 3) (list-ref s 4) (list-ref s 5))))
            (error "illegal degenerate state" external-state))
        (mrg32k3a-pack-state (list->vector s)))
      (error "malformed state" external-state)))


; Pseudo-Randomization
; ====================
;
; Reference [1] above shows how to obtain many long streams and 
; substream from the backbone generator.
;
; The idea is that the generator is a linear operation on the state.
; Hence, we can express this operation as a 3x3-matrix acting on the
; three most recent states. Raising the matrix to the k-th power, we
; obtain the operation to advance the state by k steps at once. The
; virtual streams and substreams are now simply parts of the entire
; periodic sequence (which has period around 2^191).
;
; For the implementation it is necessary to compute with matrices in
; the ring (Z/(m1*m1)*Z)^(3x3). By the Chinese-Remainder Theorem, this
; is isomorphic to ((Z/m1*Z) x (Z/m2*Z))^(3x3). We represent such a pair
; of matrices 
;   [ [[x00 x01 x02],
;      [x10 x11 x12],
;      [x20 x21 x22]], mod m1
;     [[y00 y01 y02],
;      [y10 y11 y12],
;      [y20 y21 y22]]  mod m2]
; as a vector of length 18 of the integers as writen above:
;   #(x00 x01 x02 x10 x11 x12 x20 x21 x22
;     y00 y01 y02 y10 y11 y12 y20 y21 y22)
;
; As the implementation should only use the range {-2^53..2^53-1}, the
; fundamental operation (x*y) mod m, where x, y, m are nearly 2^32, 
; is computed by breaking up x and y as x = x1*w + x0 and y = y1*w + y0 
; where w = 2^16. In this case, all operations fit the range because 
; w^2 mod m is a small number. If proper multiprecision integers are
; available this is not necessary, but pseudo-randomize! is an expected
; to be called only occasionally so we do not provide this implementation.

(define mrg32k3a-m1 4294967087) ; modulus of component 1
(define mrg32k3a-m2 4294944443) ; modulus of component 2

(define mrg32k3a-initial-state ; 0 3 6 9 12 15 of A^16, see below
  '#( 1062452522
      2961816100 
      342112271 
      2854655037 
      3321940838 
      3542344109))

(define mrg32k3a-generators #f) ; computed when needed

(define (mrg32k3a-pseudo-randomize-state i j)
  
  (define (product A B) ; A*B in ((Z/m1*Z) x (Z/m2*Z))^(3x3)
    
    (define w      65536) ; wordsize to split {0..2^32-1}
    (define w-sqr1 209)   ; w^2 mod m1
    (define w-sqr2 22853) ; w^2 mod m2
    
    (define (lc i0 i1 i2 j0 j1 j2 m w-sqr) ; linear combination
      (let ((a0h (quotient (vector-ref A i0) w))
            (a0l (modulo   (vector-ref A i0) w))
            (a1h (quotient (vector-ref A i1) w))
            (a1l (modulo   (vector-ref A i1) w))
            (a2h (quotient (vector-ref A i2) w))
            (a2l (modulo   (vector-ref A i2) w))
            (b0h (quotient (vector-ref B j0) w))
            (b0l (modulo   (vector-ref B j0) w))
            (b1h (quotient (vector-ref B j1) w))
            (b1l (modulo   (vector-ref B j1) w))
            (b2h (quotient (vector-ref B j2) w))
            (b2l (modulo   (vector-ref B j2) w)))
        (modulo
         (+ (* (+ (* a0h b0h) 
                  (* a1h b1h) 
                  (* a2h b2h)) 
               w-sqr)
            (* (+ (* a0h b0l) 
                  (* a0l b0h)
                  (* a1h b1l) 
                  (* a1l b1h)
                  (* a2h b2l) 
                  (* a2l b2h))
               w)
            (* a0l b0l)
            (* a1l b1l)
            (* a2l b2l))
         m)))
    
    (vector
     (lc  0  1  2   0  3  6  mrg32k3a-m1 w-sqr1) ; (A*B)_00 mod m1
     (lc  0  1  2   1  4  7  mrg32k3a-m1 w-sqr1) ; (A*B)_01
     (lc  0  1  2   2  5  8  mrg32k3a-m1 w-sqr1)
     (lc  3  4  5   0  3  6  mrg32k3a-m1 w-sqr1) ; (A*B)_10
     (lc  3  4  5   1  4  7  mrg32k3a-m1 w-sqr1)
     (lc  3  4  5   2  5  8  mrg32k3a-m1 w-sqr1)
     (lc  6  7  8   0  3  6  mrg32k3a-m1 w-sqr1)
     (lc  6  7  8   1  4  7  mrg32k3a-m1 w-sqr1)
     (lc  6  7  8   2  5  8  mrg32k3a-m1 w-sqr1)
     (lc  9 10 11   9 12 15  mrg32k3a-m2 w-sqr2) ; (A*B)_00 mod m2
     (lc  9 10 11  10 13 16  mrg32k3a-m2 w-sqr2)
     (lc  9 10 11  11 14 17  mrg32k3a-m2 w-sqr2)
     (lc 12 13 14   9 12 15  mrg32k3a-m2 w-sqr2)
     (lc 12 13 14  10 13 16  mrg32k3a-m2 w-sqr2)
     (lc 12 13 14  11 14 17  mrg32k3a-m2 w-sqr2)
     (lc 15 16 17   9 12 15  mrg32k3a-m2 w-sqr2)
     (lc 15 16 17  10 13 16  mrg32k3a-m2 w-sqr2)
     (lc 15 16 17  11 14 17  mrg32k3a-m2 w-sqr2)))
  
  (define (power A e) ; A^e
    (cond
      ((zero? e)
       '#(1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1))
      ((= e 1)
       A)
      ((even? e)
       (power (product A A) (quotient e 2)))
      (else
       (product (power A (- e 1)) A))))
  
  (define (power-power A b) ; A^(2^b)
    (if (zero? b)
        A
        (power-power (product A A) (- b 1))))
  
  (define A                        ; the MRG32k3a recursion
    '#(     0 1403580 4294156359
              1       0          0
              0       1          0
              527612       0 4293573854
              1       0          0
              0       1          0))
  
  ; check arguments
  (if (not (and (integer? i) 
                (exact? i)
                (integer? j)
                (exact? j)))
      (error "i j must be exact integer" i j))
  
  ; precompute A^(2^127) and A^(2^76) only once
  
  (if (not mrg32k3a-generators)
      (set! mrg32k3a-generators
            (list (power-power A 127)
                  (power-power A  76)
                  (power A 16))))
  
  ; compute M = A^(16 + i*2^127 + j*2^76)
  (let ((M (product 
            (list-ref mrg32k3a-generators 2)
            (product
             (power (list-ref mrg32k3a-generators 0)
                    (modulo i (expt 2 28)))
             (power (list-ref mrg32k3a-generators 1) 
                    (modulo j (expt 2 28)))))))
    (mrg32k3a-pack-state
     (vector
      (vector-ref M 0)
      (vector-ref M 3)
      (vector-ref M 6)
      (vector-ref M 9)
      (vector-ref M 12)
      (vector-ref M 15)))))

; True Randomization
; ==================
;
; The value obtained from the system time is feed into a very
; simple pseudo random number generator. This in turn is used
; to obtain numbers to randomize the state of the MRG32k3a
; generator, avoiding period degeneration.

(define (mrg32k3a-randomize-state state)
  ;; G. Marsaglia's simple 16-bit generator with carry
  (let* ((m 65536)
         (x (modulo (:random-source-current-time) m)))
    (define (random-m)
      (let ((y (modulo x m)))
        (set! x (+ (* 30903 y) (quotient x m)))
        y))
    (define (random n)			; m < n < m^2
      (modulo (+ (* (random-m) m) (random-m)) n))
    
    ; modify the state
    (let ((m1 mrg32k3a-m1)
          (m2 mrg32k3a-m2)
          (s (mrg32k3a-unpack-state state)))
      (mrg32k3a-pack-state
       (vector
        (+ 1 (modulo (+ (vector-ref s 0) (random (- m1 1))) (- m1 1)))
        (modulo (+ (vector-ref s 1) (random m1)) m1)
        (modulo (+ (vector-ref s 2) (random m1)) m1)
        (+ 1 (modulo (+ (vector-ref s 3) (random (- m2 1))) (- m2 1)))
        (modulo (+ (vector-ref s 4) (random m2)) m2)
        (modulo (+ (vector-ref s 5) (random m2)) m2))))))


; Large Integers
; ==============
;
; To produce large integer random deviates, for n > m-max, we first 
; construct large random numbers in the range {0..m-max^k-1} for some 
; k such that m-max^k >= n and then use the rejection method to choose
; uniformly from the range {0..n-1}.

(define mrg32k3a-m-max
  (mrg32k3a-random-range))

(define (mrg32k3a-random-power state k) ; n = m-max^k, k >= 1
  (if (= k 1)
      (mrg32k3a-random-integer state mrg32k3a-m-max)
      (+ (* (mrg32k3a-random-power state (- k 1)) mrg32k3a-m-max)
         (mrg32k3a-random-integer state mrg32k3a-m-max))))

(define (mrg32k3a-random-large state n) ; n > m-max
  (do ((k 2 (+ k 1))
       (mk (* mrg32k3a-m-max mrg32k3a-m-max) (* mk mrg32k3a-m-max)))
    ((>= mk n)
     (let* ((mk-by-n (quotient mk n))
            (a (* mk-by-n n)))
       (do ((x (mrg32k3a-random-power state k)
               (mrg32k3a-random-power state k)))
         ((< x a) (quotient x mk-by-n)))))))


; Multiple Precision Reals
; ========================
;
; To produce multiple precision reals we produce a large integer value
; and convert it into a real value. This value is then normalized.
; The precision goal is unit <= 1/(m^k + 1), or 1/unit - 1 <= m^k.
; If you know more about the floating point number types of the
; Scheme system, this can be improved.

(define (mrg32k3a-random-real-mp state unit)
  (do ((k 1 (+ k 1))
       (u (- (/ 1 unit) 1) (/ u mrg32k3a-m1)))
    ((<= u 1)
     (/ (exact->inexact (+ (mrg32k3a-random-power state k) 1))
        (exact->inexact (+ (expt mrg32k3a-m-max k) 1))))))


; Provide the Interface as Specified in the SRFI
; ==============================================
;
; An object of type random-source is a record containing the procedures
; as components. The actual state of the generator is stored in the
; binding-time environment of make-random-source.

(define (make-random-source)
  (let ((state (mrg32k3a-pack-state ; make a new copy
                (list->vector (vector->list mrg32k3a-initial-state)))))
    (:random-source-make
     (lambda ()
       (mrg32k3a-state-ref state))
     (lambda (new-state)
       (set! state (mrg32k3a-state-set new-state)))
     (lambda ()
       (set! state (mrg32k3a-randomize-state state)))
     (lambda (i j)
       (set! state (mrg32k3a-pseudo-randomize-state i j)))
     (lambda ()
       (lambda (n)
         (cond
           ((not (and (integer? n) (exact? n) (positive? n)))
            (error "range must be exact positive integer" n))           
           ((<= n mrg32k3a-m-max)
            (mrg32k3a-random-integer state n))
           (else
            (mrg32k3a-random-large state n)))))
     (lambda args
       (cond
         ((null? args)
          (lambda () 
            (mrg32k3a-random-real state)))
         ((null? (cdr args))
          (let ((unit (car args)))
            (cond
              ((not (and (real? unit) (< 0 unit 1)))
               (error "unit must be real in (0,1)" unit))
              ((<= (- (/ 1 unit) 1) mrg32k3a-m1)
               (lambda () 
                 (mrg32k3a-random-real state)))
              (else
               (lambda () 
                 (mrg32k3a-random-real-mp state unit))))))
         (else
          (error "illegal arguments" args)))))))

(define random-source? 
  :random-source?)

(define (random-source-state-ref s)
  ((:random-source-state-ref s)))

(define (random-source-state-set! s state)
  ((:random-source-state-set! s) state))

(define (random-source-randomize! s)
  ((:random-source-randomize! s)))

(define (random-source-pseudo-randomize! s i j)
  ((:random-source-pseudo-randomize! s) i j))

; ---

(define (random-source-make-integers s)
  ((:random-source-make-integers s)))

(define (random-source-make-reals s . unit)
  (apply (:random-source-make-reals s) unit))

; ---

(define default-random-source 
  (make-random-source))

(define random-integer
  (random-source-make-integers default-random-source))

(define random-real
  (random-source-make-reals default-random-source))

Added srfi/s27/readme.







































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
REFERENCE IMPLEMENTATIONS FOR SRFI-27 "Sources of Random Bits"
==============================================================

Sebastian.Egner@philips.com, 10-Apr-2002.

Files
-----

  readme         - this file
  mrg32k3a.scm   - generic parts of P. L' Ecuyer's MRG32k3a PRGN
  mrg32k3a-a.scm - core generator in Scheme integers
  mrg32k3a-b.c   - core generator in C doubles for Scheme 48
  mrg32k3a-c.scm - core generator in Gambit [Scheme] flonums
  srfi-27-a.scm  - Scheme 48 package definition for Scheme-only impl.
  srfi-27-b.scm  - Scheme 48 package definition for C/Scheme impl.
  srfi-27-c.scm  - Gambit definition for Scheme-only impl.
  conftest.scm   - confidence tests for the implementation

Implementations
---------------

The implementation has been factored into three parts.
One part implements the core generator, one part provides
the more generic functionality as specified in the SRFI,
and one part combines the parts and provides the interface
as specified in the SRFI.

a) A Scheme-only implementation for Scheme 48 0.57:
     srfi-27-a.scm
     mrg32k3a-a.scm
     mrg32k3a.scm

   This implementation uses 54-bit Scheme integers for all
   arithmetics of the generator. The result are Scheme integers
   and inexact Scheme numbers when floating point values are
   requested. 

   The implementation is slow but tries to stay away from
   unportable features as much as possible.

b) An implementation in Scheme 48 0.57 and ANSI-C:
     srfi-27-b.scm
     mrg32k3a-b.scm
     mrg32k3a.scm

   This is a more realistic implementation using C's (double)
   datatype for the core generator and 54-bit Scheme integers
   for the more infrequent operations on the state like the
   random-source-pseudo-randomize! operation.

   This implementation is meant as an example for a realistic
   native code implementation of the SRFI. Performance is good.

c) A Scheme-only implementation for Gambit 3.0:
     srfi-27-c.scm
     mrg32k3a-c.scm
     mrg32k3a.scm

   This implementation uses Gambit's 64-bit flonums. It is
   entirely written in Scheme but uses a few special features
   of the Gambit system to tell the compiler.

   This implementation is meant as an example for a realistic
   Scheme implementation using flonums in Scheme and no C-code.
   Performance is good when the code is used in compiled form;
   the implementation has been optimized by Brad Lucier. This
   has resulted in a subtantial performance gain.

Added srfi/s31/rec.sls.









































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s31 rec)
  (export rec)
  (import (rnrs))
  
  ;; Taken directly from the SRFI-31
  (define-syntax rec
    (syntax-rules ()
      [(rec (NAME . VARIABLES) . BODY)
       (letrec ( (NAME (lambda VARIABLES . BODY)) ) NAME)]
      [(rec NAME EXPRESSION)
       (letrec ( (NAME EXPRESSION) ) NAME)]))  
  
)

Added srfi/s37/args-fold.sls.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s37 args-fold)
  (export
    args-fold
    (rename (make-option option))
    option?
    option-names
    option-required-arg?
    option-optional-arg?
    option-processor)
  (import 
    (rnrs)
    (srfi private include))
  
  
  (define-record-type option
    (fields 
      names required-arg? optional-arg? processor)
    (protocol 
      (lambda (c) 
        (lambda (n ra oa p)
          (if (and 
                (and (list? n)
                     (positive? (length n))
                     (for-all (lambda (x) 
                                (or (and (string? x) (positive? (string-length x))) 
                                    (char? x))) 
                              n))
                (boolean? ra)
                (boolean? oa)
                (not (and ra oa))
                (procedure? p))
            (c n ra oa p)
            (assertion-violation 'option "invalid arguments" n ra oa p))))))

  (define args-fold
    (let ([option make-option])
      (include/resolve ("srfi" "%3a37") "srfi-37-reference.scm")
      args-fold))
)

Added srfi/s37/srfi-37-reference.scm.





























































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
;;; args-fold.scm - a program argument processor
;;;
;;; Copyright (c) 2002 Anthony Carrico
;;;
;;; All rights reserved.
;;;
;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:
;;; 1. Redistributions of source code must retain the above copyright
;;;    notice, this list of conditions and the following disclaimer.
;;; 2. Redistributions in binary form must reproduce the above copyright
;;;    notice, this list of conditions and the following disclaimer in the
;;;    documentation and/or other materials provided with the distribution.
;;; 3. The name of the authors may not be used to endorse or promote products
;;;    derived from this software without specific prior written permission.
;;;
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;;; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

;;; NOTE: This implementation uses the following SRFIs:
;;;   "SRFI 9: Defining Record Types"
;;;   "SRFI 11: Syntax for receiving multiple values"
;;;
;;; NOTE: The scsh-utils and Chicken implementations use regular
;;; expressions. These might be easier to read and understand.

#|
(define option #f)
(define option-names #f)
(define option-required-arg? #f)
(define option-optional-arg? #f)
(define option-processor #f)
(define option? #f)

(let ()
  (define-record-type option-type
    ($option names required-arg? optional-arg? processor)
    $option?
    (names $option-names)
    (required-arg? $option-required-arg?)
    (optional-arg? $option-optional-arg?)
    (processor $option-processor))
  (set! option $option)
  (set! option-names $option-names)
  (set! option-required-arg? $option-required-arg?)
  (set! option-optional-arg? $option-optional-arg?)
  (set! option-processor $option-processor)
  (set! option? $option?))
|#

(define args-fold
  (lambda (args
           options
           unrecognized-option-proc
           operand-proc
           . seeds)
    (letrec
        ((find
          (lambda (l ?)
            (cond ((null? l) #f)
                  ((? (car l)) (car l))
                  (else (find (cdr l) ?)))))
         (find-option
          ;; ISSUE: This is a brute force search. Could use a table.
          (lambda (name)
            (find
             options
             (lambda (option)
               (find
                (option-names option)
                (lambda (test-name)
                  (equal? name test-name)))))))
         (scan-short-options
          (lambda (index shorts args seeds)
            (if (= index (string-length shorts))
                (scan-args args seeds)
                (let* ((name (string-ref shorts index))
                       (option (or (find-option name)
                                   (option (list name)
                                           #f
                                           #f
                                           unrecognized-option-proc))))
                  (cond ((and (< (+ index 1) (string-length shorts))
                              (or (option-required-arg? option)
                                  (option-optional-arg? option)))
                         (let-values
                             ((seeds (apply (option-processor option)
                                            option
                                            name
                                            (substring
                                             shorts
                                             (+ index 1)
                                             (string-length shorts))
                                            seeds)))
                           (scan-args args seeds)))
                        ((and (option-required-arg? option)
                              (pair? args))
                         (let-values
                             ((seeds (apply (option-processor option)
                                            option
                                            name
                                            (car args)
                                            seeds)))
                           (scan-args (cdr args) seeds)))
                        (else
                         (let-values
                             ((seeds (apply (option-processor option)
                                            option
                                            name
                                            #f
                                            seeds)))
                           (scan-short-options
                            (+ index 1)
                            shorts
                            args
                            seeds))))))))
         (scan-operands
          (lambda (operands seeds)
            (if (null? operands)
                (apply values seeds)
                (let-values ((seeds (apply operand-proc
                                           (car operands)
                                           seeds)))
                  (scan-operands (cdr operands) seeds)))))
         (scan-args
          (lambda (args seeds)
            (if (null? args)
                (apply values seeds)
                (let ((arg (car args))
                      (args (cdr args)))
                  ;; NOTE: This string matching code would be simpler
                  ;; using a regular expression matcher.
                  (cond
                   (;; (rx bos "--" eos)
                    (string=? "--" arg)
                    ;; End option scanning:
                    (scan-operands args seeds))
                   (;;(rx bos
                    ;;    "--"
                    ;;    (submatch (+ (~ "=")))
                    ;;    "="
                    ;;    (submatch (* any)))
                    (and (> (string-length arg) 4)
                         (char=? #\- (string-ref arg 0))
                         (char=? #\- (string-ref arg 1))
                         (not (char=? #\= (string-ref arg 2)))
                         (let loop ((index 3))
                           (cond ((= index (string-length arg))
                                  #f)
                                 ((char=? #\= (string-ref arg index))
                                  index)
                                 (else
                                  (loop (+ 1 index))))))
                    ;; Found long option with arg:
                    => (lambda (=-index)
                         (let*-values
                             (((name)
                               (substring arg 2 =-index))
                              ((option-arg)
                               (substring arg
                                          (+ =-index 1)
                                          (string-length arg)))
                              ((option)
                               (or (find-option name)
                                   (option (list name)
                                           #t
                                           #f
                                           unrecognized-option-proc)))
                              (seeds
                               (apply (option-processor option)
                                      option
                                      name
                                      option-arg
                                      seeds)))
                           (scan-args args seeds))))
                   (;;(rx bos "--" (submatch (+ any)))
                    (and (> (string-length arg) 3)
                         (char=? #\- (string-ref arg 0))
                         (char=? #\- (string-ref arg 1)))
                    ;; Found long option:
                    (let* ((name (substring arg 2 (string-length arg)))
                           (option (or (find-option name)
                                       (option
                                        (list name)
                                        #f
                                        #f
                                        unrecognized-option-proc))))
                      (if (and (option-required-arg? option)
                               (pair? args))
                          (let-values
                              ((seeds (apply (option-processor option)
                                             option
                                             name
                                             (car args)
                                             seeds)))
                            (scan-args (cdr args) seeds))
                          (let-values
                              ((seeds (apply (option-processor option)
                                             option
                                             name
                                             #f
                                             seeds)))
                            (scan-args args seeds)))))
                   (;; (rx bos "-" (submatch (+ any)))
                    (and (> (string-length arg) 1)
                         (char=? #\- (string-ref arg 0)))
                    ;; Found short options
                    (let ((shorts (substring arg 1 (string-length arg))))
                      (scan-short-options 0 shorts args seeds)))
                   (else
                    (let-values ((seeds (apply operand-proc arg seeds)))
                      (scan-args args seeds)))))))))
      (scan-args args seeds))))

Added srfi/s38/with-shared-structure.chezscheme.sls.





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s38 with-shared-structure)
  (export
    write-with-shared-structure
    (rename (write-with-shared-structure write/ss))
    read-with-shared-structure
    (rename (read-with-shared-structure read/ss)))
  (import
    (rnrs)
    (only (chezscheme) print-graph parameterize))
  
  (define write-with-shared-structure
    (case-lambda 
      [(obj)
       (write-with-shared-structure obj (current-output-port))]
      [(obj port)
       (parameterize ([print-graph #t])
         (write obj port))]
      [(obj port optarg)
       (assertion-violation 'write-with-shared-structure
         "this implementation does not support optarg")]))
  
  (define read-with-shared-structure read)
  
)

Added srfi/s38/with-shared-structure.ikarus.sls.





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s38 with-shared-structure)
  (export
    write-with-shared-structure
    (rename (write-with-shared-structure write/ss))
    read-with-shared-structure
    (rename (read-with-shared-structure read/ss)))
  (import
    (rnrs)
    (only (ikarus) print-graph parameterize))
  
  (define write-with-shared-structure
    (case-lambda 
      [(obj)
       (write-with-shared-structure obj (current-output-port))]
      [(obj port)
       (parameterize ([print-graph #t])
         (write obj port))]
      [(obj port optarg)
       (assertion-violation 'write-with-shared-structure
         "this implementation does not support optarg")]))
  
  (define read-with-shared-structure read)
  
)

Added srfi/s38/with-shared-structure.larceny.sls.



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
;;; SRFI 38: External Representation of Data With Shared Structure
;;;
;;; $Id: %3a38.sls 6149 2009-03-19 02:41:56Z will $
;;;
;;; See <http://srfi.schemers.org/srfi-38/srfi-38.html> for the full document.
;;;
;;; This file contains code that is copyrighted by two separate
;;; SRFI-style copyrights.
;;;
;;; The code for write-with-shared-structure is attributed to
;;; Al Petrovsky and copyrighted by Ray Dillinger.
;;;
;;; All other code was written by William D Clinger.

(library (srfi s38 with-shared-structure)

  (export write-with-shared-structure write/ss
          read-with-shared-structure  read/ss)

  (import (rnrs base)
          (rnrs unicode)
          (rnrs bytevectors)
          (only (rnrs lists) memq)
          (rnrs control)
          (only (rnrs io ports) port? textual-port?)
          (rnrs io simple)
          (rnrs hashtables)
          (rnrs mutable-strings)
          (rnrs mutable-pairs)
          (srfi s99 records procedural)
          (only (srfi s99 records inspection) record?))

;;; Copyright (C) Ray Dillinger 2003. All Rights Reserved. 
;;;
;;; This document and translations of it may be copied and furnished to
;;; others, and derivative works that comment on or otherwise explain it
;;; or assist in its implementation may be prepared, copied, published and
;;; distributed, in whole or in part, without restriction of any kind,
;;; provided that the above copyright notice and this paragraph are
;;; included on all such copies and derivative works. However, this
;;; document itself may not be modified in any way, such as by removing
;;; the copyright notice or references to the Scheme Request For
;;; Implementation process or editors, except as needed for the purpose of
;;; developing SRFIs in which case the procedures for copyrights defined
;;; in the SRFI process must be followed, or as required to translate it
;;; into languages other than English.
;;;
;;; The limited permissions granted above are perpetual and will not be
;;; revoked by the authors or their successors or assigns.
;;;
;;; This document and the information contained herein is provided on an
;;; "AS IS" basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL
;;; WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY
;;; WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY
;;; RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
;;; PARTICULAR PURPOSE.

;;; A printer that shows all sharing of substructures.  Uses the Common
;;; Lisp print-circle notation: #n# refers to a previous substructure
;;; labeled with #n=.   Takes O(n^2) time.

;;; Code attributed to Al* Petrofsky, modified by Dillinger.  
;;;
;;; Modified December 2008 by Will Clinger to use R6RS-style hashtables
;;; and to recognize R6RS data structures.  Now runs in O(n) time if
;;; the hashtable accesses are O(1).

(define (write-with-shared-structure obj . optional-port)

  (define (lookup key state)
    (hashtable-ref state key #f))

  (define (present? key state)
    (hashtable-contains? state key))

  (define (updated-state key val state)
    (hashtable-set! state key val)
    state)

  ;; An object is interesting if it might have a mutable state.
  ;; An interesting object is especially interesting if it has
  ;; a standard external representation (according to SRFI 38)
  ;; that might contain the object itself.  The interesting
  ;; objects described by the R5RS or R6RS are:
  ;;
  ;;     pairs             (especially interesting)
  ;;     vectors           (especially interesting)
  ;;     strings
  ;;     bytevectors
  ;;     records
  ;;     ports
  ;;     hashtables
  ;;
  ;; We treat zero-length vectors, strings, and bytevectors
  ;; as uninteresting because they don't have a mutable state
  ;; and the reference implementation for SRFI 38 also treated
  ;; them as uninteresting.

  (define (interesting? obj)
    (or (pair? obj)
        (and (vector? obj) (not (zero? (vector-length obj))))
        (and (string? obj) (not (zero? (string-length obj))))
        (bytevector? obj)
        (record? obj)
        (port? obj)
        (hashtable? obj)))

  ;; The state has an entry for each interesting part of OBJ.  The
  ;; associated value will be:
  ;;  -- a number if the part has been given one,
  ;;  -- #t if the part will need to be assigned a number but has not been yet,
  ;;  -- #f if the part will not need a number.
  ;; The state also associates a symbol (counter) with the most
  ;; recently assigned number.
  ;; Returns a state with new entries for any parts that had
  ;; numbers assigned.

  (define (write-obj obj state outport)

    (define (write-interesting state)
      (cond ((pair? obj)
             (display "(" outport)
             (let write-cdr ((obj (cdr obj))
                             (state (write-obj (car obj) state outport)))
               (cond ((and (pair? obj)
                           (not (lookup obj state)))
                      (display " " outport)
                      (write-cdr (cdr obj)
                                 (write-obj (car obj) state outport)))
                     ((null? obj)
                      (display ")" outport)
                      state)
                     (else
                      (display " . " outport)
                      (let ((state (write-obj obj state outport)))
                        (display ")" outport)
                        state)))))
            ((vector? obj)
             (display "#(" outport)
             (let ((len (vector-length obj)))
               (let write-vec ((i 1)
                               (state (write-obj (vector-ref obj 0)
                                                 state outport)))
                 (cond ((= i len) (display ")" outport) state)
                       (else (display " " outport)
                             (write-vec (+ i 1)
                                        (write-obj (vector-ref obj i)
                                                   state outport)))))))
            ;; else it's a string or something
            (else (write obj outport) state)))

    (cond ((interesting? obj)
           (let ((val (lookup obj state)))
             (cond ((not val)
                    (write-interesting state))
                   ((number? val) 
                    (begin (display "#" outport)
                           (write val outport)
                           (display "#" outport)
                           state))
                   (else
                    (let* ((n (+ 1 (lookup 'counter state)))
                           (state (updated-state 'counter n state)))
                      (begin (display "#" outport)
                             (write n outport) 
                             (display "=" outport))
                      (write-interesting (updated-state obj n state)))))))
          (else (write obj outport) state)))

  ;; Scan computes the initial value of the state, which maps each
  ;; interesting part of the object to #t if it occurs multiple times,
  ;; #f if only once.

  (define (scan obj state)
    (cond ((not (interesting? obj)) state)
          ((present? obj state)
           (updated-state obj #t state))
          (else
           (let ((state (updated-state obj #f state)))
             (cond ((pair? obj) (scan (car obj) (scan (cdr obj) state)))
                   ((vector? obj)
                    (let ((len (vector-length obj)))
                      (do ((i 0 (+ 1 i))
                           (state state (scan (vector-ref obj i) state)))
                          ((= i len) state))))
                   (else state))))))

  (let* ((state (make-eq-hashtable))
         (state (scan obj state))
         (state (updated-state 'counter 0 state))
         (outport (if (eq? '() optional-port)
                      (current-output-port)
                      (car optional-port))))
    (write-obj obj state outport)
    ;; We don't want to return the big state that write-obj just returned.
    (if #f #f)))


;;; Copyright (C) William D Clinger (2008). All Rights Reserved.
;;; 
;;; Permission is hereby granted, free of charge, to any
;;; person obtaining a copy of this software and associated
;;; documentation files (the "Software"), to deal in the
;;; Software without restriction, including without
;;; limitation the rights to use, copy, modify, merge,
;;; publish, distribute, sublicense, and/or sell copies of
;;; the Software, and to permit persons to whom the Software
;;; is furnished to do so, subject to the following
;;; conditions:
;;; 
;;; The above copyright notice and this permission notice
;;; shall be included in all copies or substantial portions
;;; of the Software.
;;; 
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
;;; ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
;;; TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
;;; PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
;;; SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
;;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
;;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
;;; IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;;; DEALINGS IN THE SOFTWARE.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; read-with-shared-structure
;
; Based on Clinger's reference implementation of get-datum.
;
; The scanner's state machine and the recursive descent parser
; were generated by Will Clinger's LexGen and ParseGen, so the
; parser can be extended or customized by regenerating those
; parts.
;
; LexGen and ParseGen are available at
; http://www.ccs.neu.edu/home/will/Research/SW2006/*.tar.gz
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Fixup objects are used to implement SRFI 38.

(define rtd:fixup-object
  (make-rtd 'fixup-object
            '#((immutable index)
               (mutable ready)
               (mutable value))))

(define make-raw-fixup-object (rtd-constructor rtd:fixup-object))

(define (make-fixup-object index)
  (make-raw-fixup-object index #f #f))

(define fixup-object? (rtd-predicate rtd:fixup-object))

(define fixup-ready? (rtd-accessor rtd:fixup-object 'ready))

(define fixup-index (rtd-accessor rtd:fixup-object 'index))

(define fixup-value (rtd-accessor rtd:fixup-object 'value))

(define (fixup-ready! fixup obj)
  (raw-fixup-value! fixup obj)
  (raw-fixup-ready! fixup #t))

(define raw-fixup-ready! (rtd-mutator rtd:fixup-object 'ready))
(define raw-fixup-value! (rtd-mutator rtd:fixup-object 'value))

; The exported entry point.

(define (read-with-shared-structure . rest)
  (cond ((null? rest)
         (read-with-shared-structure-local (current-input-port)))
        ((and (null? (cdr rest))
              (input-port? (car rest))
              (textual-port? (car rest)))
         (read-with-shared-structure-local (car rest)))
        (else
         (assertion-violation 'read-with-shared-structure
                              "illegal argument(s)" rest))))

(define (read-with-shared-structure-local input-port)

  ; Constants and local variables.

  (let* (; Constants.

         ; initial length of string_accumulator

         (initial_accumulator_length 64)

         ; Encodings of error messages.

         (errLongToken 1)                 ; extremely long token
         (errIncompleteToken 2)      ; any lexical error, really
         (errIllegalHexEscape 3)                 ; illegal \x...
         (errIllegalNamedChar 4)                 ; illegal #\...
         (errIllegalString 5)                   ; illegal string
         (errIllegalSymbol 6)                   ; illegal symbol
         (errNoDelimiter 7)      ; missing delimiter after token
         (errSRFI38 8)                           ; illegal #...#
         (errBug 9)            ; bug in reader, shouldn't happen
         (errLexGenBug 10)                        ; can't happen

         ; Important but unnamed non-Ascii characters.

         (char:nel    (integer->char #x85))
         (char:ls     (integer->char #x2028))

         ; State for one-token buffering in lexical analyzer.

         (kindOfNextToken 'z1)      ; valid iff nextTokenIsReady
         (nextTokenIsReady #f)

         (tokenValue "")  ; string associated with current token

         ; A string buffer for the characters of the current token.
         ; Resized as necessary.

         (string_accumulator (make-string initial_accumulator_length))

         ; Number of characters in string_accumulator.

         (string_accumulator_length 0)

         ; Hook for recording source locations.

         (locationStart #f)

         ; Hash table for SRFI 38, or #f.

         (shared-structures #f)

        )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; Hand-coding scanner0 makes a small but worthwhile difference.
    ;
    ; The most common characters are spaces, parentheses, newlines,
    ; semicolons, and lower case Ascii letters.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
    ; Scanning for the start of a token.

    (define (scanner0)
      (define (loop c)
        (cond ((not (char? c))
               (accept 'eofobj))
              ((or (char=? c #\space)
                   (char=? c #\newline))
               (read-char input-port)
               (loop (peek-char input-port)))
              (else
               (state0 c))))
      (loop (peek-char input-port)))

    ; Consuming a semicolon comment.

    (define (scanner1)
      (define (loop c)
        (cond ((not (char? c))
               (accept 'eofobj))
              ((char=? c #\newline)
               (scanner0))
              (else
               (loop (read-char input-port)))))
      (loop (read-char input-port)))
  
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; LexGen generated the code for the state machine.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  (define (state0 c)
    (case c
      ((#\`) (consumeChar) (accept 'backquote))
      ((#\') (consumeChar) (accept 'quote))
      ((#\]) (consumeChar) (accept 'rbracket))
      ((#\[) (consumeChar) (accept 'lbracket))
      ((#\)) (consumeChar) (accept 'rparen))
      ((#\() (consumeChar) (accept 'lparen))
      ((#\tab #\newline #\vtab #\page #\return #\space)
       (consumeChar)
       (begin
         (set! string_accumulator_length 0)
         (state0 (scanChar))))
      ((#\;) (consumeChar) (state213 (scanChar)))
      ((#\#) (consumeChar) (state212 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state141 (scanChar)))
      ((#\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\g
        #\h
        #\i
        #\j
        #\k
        #\l
        #\m
        #\n
        #\o
        #\p
        #\q
        #\r
        #\s
        #\t
        #\u
        #\v
        #\w
        #\x
        #\y
        #\z
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F
        #\G
        #\H
        #\I
        #\J
        #\K
        #\L
        #\M
        #\N
        #\O
        #\P
        #\Q
        #\R
        #\S
        #\T
        #\U
        #\V
        #\W
        #\X
        #\Y
        #\Z
        #\!
        #\$
        #\%
        #\&
        #\*
        #\/
        #\:
        #\<
        #\=
        #\>
        #\?
        #\^
        #\_
        #\~)
       (consumeChar)
       (state13 (scanChar)))
      ((#\\) (consumeChar) (state12 (scanChar)))
      ((#\-) (consumeChar) (state9 (scanChar)))
      ((#\+) (consumeChar) (state8 (scanChar)))
      ((#\.) (consumeChar) (state7 (scanChar)))
      ((#\") (consumeChar) (state5 (scanChar)))
      ((#\,) (consumeChar) (state1 (scanChar)))
      (else
       (if ((lambda (c)
              (and (char? c)
                   (> (char->integer c) 127)
                   (let ((cat (char-general-category c)))
                     (memq cat
                           '(Lu Ll
                                Lt
                                Lm
                                Lo
                                Mn
                                Nl
                                No
                                Pd
                                Pc
                                Po
                                Sc
                                Sm
                                Sk
                                So
                                Co)))))
            c)
           (begin (consumeChar) (state13 (scanChar)))
           (if (eof-object? c)
               (begin (consumeChar) (accept 'eofobj))
               (if ((lambda (c) (and (char? c) (char-whitespace? c)))
                    c)
                   (begin
                     (consumeChar)
                     (begin
                       (set! string_accumulator_length 0)
                       (state0 (scanChar))))
                   (if ((lambda (c)
                          (and (char? c) (char=? c (integer->char 133))))
                        c)
                       (begin
                         (consumeChar)
                         (begin
                           (set! string_accumulator_length 0)
                           (state0 (scanChar))))
                       (scannerError errIncompleteToken))))))))
  (define (state1 c)
    (case c
      ((#\@) (consumeChar) (accept 'splicing))
      (else (accept 'comma))))
  (define (state2 c)
    (case c
      ((#\") (consumeChar) (accept 'string))
      ((#\newline #\return)
       (consumeChar)
       (state5 (scanChar)))
      ((#\\) (consumeChar) (state4 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state2 (scanChar)))
      (else
       (if (char? c)
           (begin (consumeChar) (state5 (scanChar)))
           (if ((lambda (c)
                  (and (char? c) (char=? c (integer->char 8232))))
                c)
               (begin (consumeChar) (state5 (scanChar)))
               (if ((lambda (c)
                      (and (char? c) (char=? c (integer->char 133))))
                    c)
                   (begin (consumeChar) (state5 (scanChar)))
                   (scannerError errIncompleteToken)))))))
  (define (state3 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state2 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state4 c)
    (case c
      ((#\a
        #\b
        #\t
        #\n
        #\v
        #\f
        #\r
        #\"
        #\\
        #\newline
        #\return
        #\space)
       (consumeChar)
       (state5 (scanChar)))
      ((#\x) (consumeChar) (state3 (scanChar)))
      (else
       (if ((lambda (c)
              (and (char? c) (char=? c (integer->char 8232))))
            c)
           (begin (consumeChar) (state5 (scanChar)))
           (if ((lambda (c)
                  (and (char? c) (char=? c (integer->char 133))))
                c)
               (begin (consumeChar) (state5 (scanChar)))
               (scannerError errIncompleteToken))))))
  (define (state5 c)
    (case c
      ((#\") (consumeChar) (accept 'string))
      ((#\newline #\return)
       (consumeChar)
       (state5 (scanChar)))
      ((#\\) (consumeChar) (state4 (scanChar)))
      (else
       (if (char? c)
           (begin (consumeChar) (state5 (scanChar)))
           (if ((lambda (c)
                  (and (char? c) (char=? c (integer->char 8232))))
                c)
               (begin (consumeChar) (state5 (scanChar)))
               (if ((lambda (c)
                      (and (char? c) (char=? c (integer->char 133))))
                    c)
                   (begin (consumeChar) (state5 (scanChar)))
                   (scannerError errIncompleteToken)))))))
  (define (state6 c)
    (case c
      ((#\.) (consumeChar) (accept 'id))
      (else (scannerError errIncompleteToken))))
  (define (state7 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state130 (scanChar)))
      ((#\.) (consumeChar) (state6 (scanChar)))
      (else (accept 'period))))
  (define (state8 c)
    (case c
      ((#\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state203 (scanChar)))
      ((#\.) (consumeChar) (state149 (scanChar)))
      ((#\n) (consumeChar) (state148 (scanChar)))
      ((#\i) (consumeChar) (state143 (scanChar)))
      (else (accept 'id))))
  (define (state9 c)
    (case c
      ((#\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state203 (scanChar)))
      ((#\.) (consumeChar) (state149 (scanChar)))
      ((#\n) (consumeChar) (state148 (scanChar)))
      ((#\i) (consumeChar) (state143 (scanChar)))
      ((#\>) (consumeChar) (state13 (scanChar)))
      (else (accept 'id))))
  (define (state10 c)
    (case c
      ((#\;) (consumeChar) (state13 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state10 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state11 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state10 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state12 c)
    (case c
      ((#\x) (consumeChar) (state11 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state13 c)
    (case c
      ((#\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\g
        #\h
        #\i
        #\j
        #\k
        #\l
        #\m
        #\n
        #\o
        #\p
        #\q
        #\r
        #\s
        #\t
        #\u
        #\v
        #\w
        #\x
        #\y
        #\z
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F
        #\G
        #\H
        #\I
        #\J
        #\K
        #\L
        #\M
        #\N
        #\O
        #\P
        #\Q
        #\R
        #\S
        #\T
        #\U
        #\V
        #\W
        #\X
        #\Y
        #\Z
        #\!
        #\$
        #\%
        #\&
        #\*
        #\/
        #\:
        #\<
        #\=
        #\>
        #\?
        #\^
        #\_
        #\~
        #\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\+
        #\-
        #\.
        #\@)
       (consumeChar)
       (state13 (scanChar)))
      ((#\\) (consumeChar) (state12 (scanChar)))
      (else
       (if ((lambda (c)
              (and (char? c)
                   (let ((cat (char-general-category c)))
                     (memq cat '(Nd Mc Me)))))
            c)
           (begin (consumeChar) (state13 (scanChar)))
           (if ((lambda (c)
                  (and (char? c)
                       (> (char->integer c) 127)
                       (let ((cat (char-general-category c)))
                         (memq cat
                               '(Lu Ll
                                    Lt
                                    Lm
                                    Lo
                                    Mn
                                    Nl
                                    No
                                    Pd
                                    Pc
                                    Po
                                    Sc
                                    Sm
                                    Sk
                                    So
                                    Co)))))
                c)
               (begin (consumeChar) (state13 (scanChar)))
               (accept 'id))))))
  (define (state14 c)
    (case c
      ((#\#) (consumeChar) (accept 'sharinguse))
      ((#\=) (consumeChar) (accept 'sharingdef))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state14 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state15 c)
    (case c
      ((#\@) (consumeChar) (accept 'unsyntaxsplicing))
      (else (accept 'unsyntax))))
  (define (state16 c)
    (case c
      ((#\() (consumeChar) (accept 'bvecstart))
      (else (scannerError errIncompleteToken))))
  (define (state17 c)
    (case c
      ((#\8) (consumeChar) (state16 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state18 c)
    (case c
      ((#\u) (consumeChar) (state17 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state19 c)
    (case c
      ((#\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\g
        #\h
        #\i
        #\j
        #\k
        #\l
        #\m
        #\n
        #\o
        #\p
        #\q
        #\r
        #\s
        #\t
        #\u
        #\v
        #\w
        #\x
        #\y
        #\z
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F
        #\G
        #\H
        #\I
        #\J
        #\K
        #\L
        #\M
        #\N
        #\O
        #\P
        #\Q
        #\R
        #\S
        #\T
        #\U
        #\V
        #\W
        #\X
        #\Y
        #\Z)
       (consumeChar)
       (state19 (scanChar)))
      (else (accept 'character))))
  (define (state20 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state20 (scanChar)))
      (else (accept 'character))))
  (define (state21 c)
    (case c
      ((#\a #\b #\c #\d #\e #\f #\A #\B #\C #\D #\E #\F)
       (consumeChar)
       (state21 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state20 (scanChar)))
      ((#\g
        #\h
        #\i
        #\j
        #\k
        #\l
        #\m
        #\n
        #\o
        #\p
        #\q
        #\r
        #\s
        #\t
        #\u
        #\v
        #\w
        #\x
        #\y
        #\z
        #\G
        #\H
        #\I
        #\J
        #\K
        #\L
        #\M
        #\N
        #\O
        #\P
        #\Q
        #\R
        #\S
        #\T
        #\U
        #\V
        #\W
        #\X
        #\Y
        #\Z)
       (consumeChar)
       (state19 (scanChar)))
      (else (accept 'character))))
  (define (state22 c)
    (case c
      ((#\x) (consumeChar) (state21 (scanChar)))
      ((#\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\g
        #\h
        #\i
        #\j
        #\k
        #\l
        #\m
        #\n
        #\o
        #\p
        #\q
        #\r
        #\s
        #\t
        #\u
        #\v
        #\w
        #\y
        #\z
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F
        #\G
        #\H
        #\I
        #\J
        #\K
        #\L
        #\M
        #\N
        #\O
        #\P
        #\Q
        #\R
        #\S
        #\T
        #\U
        #\V
        #\W
        #\X
        #\Y
        #\Z)
       (consumeChar)
       (state19 (scanChar)))
      (else
       (if (char? c)
           (begin (consumeChar) (accept 'character))
           (scannerError errIncompleteToken)))))
  (define (state23 c)
    (case c
      ((#\i #\I #\e #\E)
       (consumeChar)
       (state58 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state24 c)
    (case c
      ((#\+ #\-) (consumeChar) (state57 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state33 (scanChar)))
      ((#\#) (consumeChar) (state23 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state25 c)
    (case c
      ((#\i #\I #\e #\E)
       (consumeChar)
       (state88 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state26 c)
    (case c
      ((#\+ #\-) (consumeChar) (state87 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state63 (scanChar)))
      ((#\#) (consumeChar) (state25 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state27 c)
    (case c
      ((#\i #\I #\e #\E)
       (consumeChar)
       (state126 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state28 c)
    (case c
      ((#\+ #\-) (consumeChar) (state125 (scanChar)))
      ((#\0 #\1) (consumeChar) (state93 (scanChar)))
      ((#\#) (consumeChar) (state27 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state29 c)
    (case c
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      ((#\#) (consumeChar) (state29 (scanChar)))
      (else (accept 'number))))
  (define (state30 c)
    (case c
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state30 (scanChar)))
      ((#\#) (consumeChar) (state29 (scanChar)))
      (else (accept 'number))))
  (define (state31 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state30 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state32 c)
    (case c
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      ((#\#) (consumeChar) (state32 (scanChar)))
      ((#\/) (consumeChar) (state31 (scanChar)))
      (else (accept 'number))))
  (define (state33 c)
    (case c
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state33 (scanChar)))
      ((#\#) (consumeChar) (state32 (scanChar)))
      ((#\/) (consumeChar) (state31 (scanChar)))
      (else (accept 'number))))
  (define (state34 c)
    (case c
      ((#\f) (consumeChar) (state38 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state35 c)
    (case c
      ((#\n) (consumeChar) (state34 (scanChar)))
      (else (accept 'number))))
  (define (state36 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      (else (accept 'number))))
  (define (state37 c)
    (case c
      ((#\0) (consumeChar) (state36 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state38 c)
    (case c
      ((#\.) (consumeChar) (state37 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state39 c)
    (case c
      ((#\n) (consumeChar) (state38 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state40 c)
    (case c
      ((#\a) (consumeChar) (state39 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state41 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state108 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state41 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state42 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state41 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state43 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state43 (scanChar)))
      ((#\/) (consumeChar) (state42 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state44 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state44 (scanChar)))
      ((#\#) (consumeChar) (state43 (scanChar)))
      ((#\/) (consumeChar) (state42 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state45 c)
    (case c
      ((#\n) (consumeChar) (state107 (scanChar)))
      ((#\i) (consumeChar) (state102 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state44 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state46 c)
    (case c
      ((#\#) (consumeChar) (state192 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state46 (scanChar)))
      (else (accept 'number))))
  (define (state47 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state46 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state48 c)
    (case c
      ((#\#) (consumeChar) (state48 (scanChar)))
      ((#\/) (consumeChar) (state47 (scanChar)))
      (else (accept 'number))))
  (define (state49 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state49 (scanChar)))
      ((#\#) (consumeChar) (state48 (scanChar)))
      ((#\/) (consumeChar) (state47 (scanChar)))
      (else (accept 'number))))
  (define (state50 c)
    (case c
      ((#\n) (consumeChar) (state183 (scanChar)))
      ((#\i) (consumeChar) (state179 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state49 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state51 c)
    (case c
      ((#\+ #\-) (consumeChar) (state50 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state49 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state52 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state52 (scanChar)))
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      (else (accept 'number))))
  (define (state53 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state53 (scanChar)))
      ((#\#) (consumeChar) (state52 (scanChar)))
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      (else (accept 'number))))
  (define (state54 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state53 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state55 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state55 (scanChar)))
      ((#\/) (consumeChar) (state54 (scanChar)))
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      (else (accept 'number))))
  (define (state56 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state56 (scanChar)))
      ((#\#) (consumeChar) (state55 (scanChar)))
      ((#\/) (consumeChar) (state54 (scanChar)))
      ((#\@) (consumeChar) (state51 (scanChar)))
      ((#\+ #\-) (consumeChar) (state45 (scanChar)))
      (else (accept 'number))))
  (define (state57 c)
    (case c
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state56 (scanChar)))
      ((#\n) (consumeChar) (state40 (scanChar)))
      ((#\i) (consumeChar) (state35 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state58 c)
    (case c
      ((#\+ #\-) (consumeChar) (state57 (scanChar)))
      ((#\0
        #\1
        #\2
        #\3
        #\4
        #\5
        #\6
        #\7
        #\8
        #\9
        #\a
        #\b
        #\c
        #\d
        #\e
        #\f
        #\A
        #\B
        #\C
        #\D
        #\E
        #\F)
       (consumeChar)
       (state33 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state59 c)
    (case c
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      ((#\#) (consumeChar) (state59 (scanChar)))
      (else (accept 'number))))
  (define (state60 c)
    (case c
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state60 (scanChar)))
      ((#\#) (consumeChar) (state59 (scanChar)))
      (else (accept 'number))))
  (define (state61 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state60 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state62 c)
    (case c
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      ((#\#) (consumeChar) (state62 (scanChar)))
      ((#\/) (consumeChar) (state61 (scanChar)))
      (else (accept 'number))))
  (define (state63 c)
    (case c
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state63 (scanChar)))
      ((#\#) (consumeChar) (state62 (scanChar)))
      ((#\/) (consumeChar) (state61 (scanChar)))
      (else (accept 'number))))
  (define (state64 c)
    (case c
      ((#\f) (consumeChar) (state68 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state65 c)
    (case c
      ((#\n) (consumeChar) (state64 (scanChar)))
      (else (accept 'number))))
  (define (state66 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      (else (accept 'number))))
  (define (state67 c)
    (case c
      ((#\0) (consumeChar) (state66 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state68 c)
    (case c
      ((#\.) (consumeChar) (state67 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state69 c)
    (case c
      ((#\n) (consumeChar) (state68 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state70 c)
    (case c
      ((#\a) (consumeChar) (state69 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state71 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state108 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state71 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state72 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state71 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state73 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state73 (scanChar)))
      ((#\/) (consumeChar) (state72 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state74 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state74 (scanChar)))
      ((#\#) (consumeChar) (state73 (scanChar)))
      ((#\/) (consumeChar) (state72 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state75 c)
    (case c
      ((#\n) (consumeChar) (state107 (scanChar)))
      ((#\i) (consumeChar) (state102 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state74 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state76 c)
    (case c
      ((#\#) (consumeChar) (state192 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state76 (scanChar)))
      (else (accept 'number))))
  (define (state77 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state76 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state78 c)
    (case c
      ((#\#) (consumeChar) (state78 (scanChar)))
      ((#\/) (consumeChar) (state77 (scanChar)))
      (else (accept 'number))))
  (define (state79 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state79 (scanChar)))
      ((#\#) (consumeChar) (state78 (scanChar)))
      ((#\/) (consumeChar) (state77 (scanChar)))
      (else (accept 'number))))
  (define (state80 c)
    (case c
      ((#\n) (consumeChar) (state183 (scanChar)))
      ((#\i) (consumeChar) (state179 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state79 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state81 c)
    (case c
      ((#\+ #\-) (consumeChar) (state80 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state79 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state82 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state82 (scanChar)))
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      (else (accept 'number))))
  (define (state83 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state83 (scanChar)))
      ((#\#) (consumeChar) (state82 (scanChar)))
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      (else (accept 'number))))
  (define (state84 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state83 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state85 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state85 (scanChar)))
      ((#\/) (consumeChar) (state84 (scanChar)))
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      (else (accept 'number))))
  (define (state86 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state86 (scanChar)))
      ((#\#) (consumeChar) (state85 (scanChar)))
      ((#\/) (consumeChar) (state84 (scanChar)))
      ((#\@) (consumeChar) (state81 (scanChar)))
      ((#\+ #\-) (consumeChar) (state75 (scanChar)))
      (else (accept 'number))))
  (define (state87 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state86 (scanChar)))
      ((#\n) (consumeChar) (state70 (scanChar)))
      ((#\i) (consumeChar) (state65 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state88 c)
    (case c
      ((#\+ #\-) (consumeChar) (state87 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
       (consumeChar)
       (state63 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state89 c)
    (case c
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      ((#\#) (consumeChar) (state89 (scanChar)))
      (else (accept 'number))))
  (define (state90 c)
    (case c
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      ((#\0 #\1) (consumeChar) (state90 (scanChar)))
      ((#\#) (consumeChar) (state89 (scanChar)))
      (else (accept 'number))))
  (define (state91 c)
    (case c
      ((#\0 #\1) (consumeChar) (state90 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state92 c)
    (case c
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      ((#\#) (consumeChar) (state92 (scanChar)))
      ((#\/) (consumeChar) (state91 (scanChar)))
      (else (accept 'number))))
  (define (state93 c)
    (case c
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      ((#\0 #\1) (consumeChar) (state93 (scanChar)))
      ((#\#) (consumeChar) (state92 (scanChar)))
      ((#\/) (consumeChar) (state91 (scanChar)))
      (else (accept 'number))))
  (define (state94 c)
    (case c
      ((#\f) (consumeChar) (state98 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state95 c)
    (case c
      ((#\n) (consumeChar) (state94 (scanChar)))
      (else (accept 'number))))
  (define (state96 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      (else (accept 'number))))
  (define (state97 c)
    (case c
      ((#\0) (consumeChar) (state96 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state98 c)
    (case c
      ((#\.) (consumeChar) (state97 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state99 c)
    (case c
      ((#\n) (consumeChar) (state98 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state100 c)
    (case c
      ((#\a) (consumeChar) (state99 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state101 c)
    (case c
      ((#\f) (consumeChar) (state105 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state102 c)
    (case c
      ((#\n) (consumeChar) (state101 (scanChar)))
      (else (accept 'number))))
  (define (state103 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      (else (scannerError errIncompleteToken))))
  (define (state104 c)
    (case c
      ((#\0) (consumeChar) (state103 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state105 c)
    (case c
      ((#\.) (consumeChar) (state104 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state106 c)
    (case c
      ((#\n) (consumeChar) (state105 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state107 c)
    (case c
      ((#\a) (consumeChar) (state106 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state108 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state108 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state109 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0 #\1) (consumeChar) (state109 (scanChar)))
      ((#\#) (consumeChar) (state108 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state110 c)
    (case c
      ((#\0 #\1) (consumeChar) (state109 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state111 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state111 (scanChar)))
      ((#\/) (consumeChar) (state110 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state112 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0 #\1) (consumeChar) (state112 (scanChar)))
      ((#\#) (consumeChar) (state111 (scanChar)))
      ((#\/) (consumeChar) (state110 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state113 c)
    (case c
      ((#\0 #\1) (consumeChar) (state112 (scanChar)))
      ((#\n) (consumeChar) (state107 (scanChar)))
      ((#\i) (consumeChar) (state102 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state114 c)
    (case c
      ((#\#) (consumeChar) (state192 (scanChar)))
      ((#\0 #\1) (consumeChar) (state114 (scanChar)))
      (else (accept 'number))))
  (define (state115 c)
    (case c
      ((#\0 #\1) (consumeChar) (state114 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state116 c)
    (case c
      ((#\#) (consumeChar) (state116 (scanChar)))
      ((#\/) (consumeChar) (state115 (scanChar)))
      (else (accept 'number))))
  (define (state117 c)
    (case c
      ((#\0 #\1) (consumeChar) (state117 (scanChar)))
      ((#\#) (consumeChar) (state116 (scanChar)))
      ((#\/) (consumeChar) (state115 (scanChar)))
      (else (accept 'number))))
  (define (state118 c)
    (case c
      ((#\n) (consumeChar) (state183 (scanChar)))
      ((#\i) (consumeChar) (state179 (scanChar)))
      ((#\0 #\1) (consumeChar) (state117 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state119 c)
    (case c
      ((#\+ #\-) (consumeChar) (state118 (scanChar)))
      ((#\0 #\1) (consumeChar) (state117 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state120 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state120 (scanChar)))
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      (else (accept 'number))))
  (define (state121 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0 #\1) (consumeChar) (state121 (scanChar)))
      ((#\#) (consumeChar) (state120 (scanChar)))
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      (else (accept 'number))))
  (define (state122 c)
    (case c
      ((#\0 #\1) (consumeChar) (state121 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state123 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state123 (scanChar)))
      ((#\/) (consumeChar) (state122 (scanChar)))
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      (else (accept 'number))))
  (define (state124 c)
    (case c
      ((#\i) (consumeChar) (accept 'number))
      ((#\0 #\1) (consumeChar) (state124 (scanChar)))
      ((#\#) (consumeChar) (state123 (scanChar)))
      ((#\/) (consumeChar) (state122 (scanChar)))
      ((#\@) (consumeChar) (state119 (scanChar)))
      ((#\+ #\-) (consumeChar) (state113 (scanChar)))
      (else (accept 'number))))
  (define (state125 c)
    (case c
      ((#\0 #\1) (consumeChar) (state124 (scanChar)))
      ((#\n) (consumeChar) (state100 (scanChar)))
      ((#\i) (consumeChar) (state95 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state126 c)
    (case c
      ((#\+ #\-) (consumeChar) (state125 (scanChar)))
      ((#\0 #\1) (consumeChar) (state93 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state127 c)
    (case c
      ((#\d #\D) (consumeChar) (state205 (scanChar)))
      ((#\b #\B) (consumeChar) (state126 (scanChar)))
      ((#\o #\O) (consumeChar) (state88 (scanChar)))
      ((#\x #\X) (consumeChar) (state58 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state128 c)
    (case c
      ((#\+ #\-) (consumeChar) (state204 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state141 (scanChar)))
      ((#\.) (consumeChar) (state129 (scanChar)))
      ((#\#) (consumeChar) (state127 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state129 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state130 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state130 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\#) (consumeChar) (state136 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state135 (scanChar)))
      ((#\|) (consumeChar) (state132 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state130 (scanChar)))
      (else (accept 'number))))
  (define (state131 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state131 (scanChar)))
      (else (accept 'number))))
  (define (state132 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state131 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state133 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state133 (scanChar)))
      ((#\|) (consumeChar) (state132 (scanChar)))
      (else (accept 'number))))
  (define (state134 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state133 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state135 c)
    (case c
      ((#\+ #\-) (consumeChar) (state134 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state133 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state136 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\#) (consumeChar) (state136 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state135 (scanChar)))
      ((#\|) (consumeChar) (state132 (scanChar)))
      (else (accept 'number))))
  (define (state137 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\#) (consumeChar) (state137 (scanChar)))
      (else (accept 'number))))
  (define (state138 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state138 (scanChar)))
      ((#\#) (consumeChar) (state137 (scanChar)))
      (else (accept 'number))))
  (define (state139 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state138 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state140 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\#) (consumeChar) (state140 (scanChar)))
      ((#\/) (consumeChar) (state139 (scanChar)))
      ((#\.) (consumeChar) (state136 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state135 (scanChar)))
      ((#\|) (consumeChar) (state132 (scanChar)))
      (else (accept 'number))))
  (define (state141 c)
    (case c
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state141 (scanChar)))
      ((#\#) (consumeChar) (state140 (scanChar)))
      ((#\/) (consumeChar) (state139 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state135 (scanChar)))
      ((#\|) (consumeChar) (state132 (scanChar)))
      ((#\.) (consumeChar) (state130 (scanChar)))
      (else (accept 'number))))
  (define (state142 c)
    (case c
      ((#\f) (consumeChar) (state146 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state143 c)
    (case c
      ((#\n) (consumeChar) (state142 (scanChar)))
      (else (accept 'number))))
  (define (state144 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      (else (accept 'number))))
  (define (state145 c)
    (case c
      ((#\0) (consumeChar) (state144 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state146 c)
    (case c
      ((#\.) (consumeChar) (state145 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state147 c)
    (case c
      ((#\n) (consumeChar) (state146 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state148 c)
    (case c
      ((#\a) (consumeChar) (state147 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state149 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state150 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state150 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\#) (consumeChar) (state156 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state155 (scanChar)))
      ((#\|) (consumeChar) (state152 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state150 (scanChar)))
      (else (accept 'number))))
  (define (state151 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state151 (scanChar)))
      (else (accept 'number))))
  (define (state152 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state151 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state153 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state153 (scanChar)))
      ((#\|) (consumeChar) (state152 (scanChar)))
      (else (accept 'number))))
  (define (state154 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state153 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state155 c)
    (case c
      ((#\+ #\-) (consumeChar) (state154 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state153 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state156 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\#) (consumeChar) (state156 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state155 (scanChar)))
      ((#\|) (consumeChar) (state152 (scanChar)))
      (else (accept 'number))))
  (define (state157 c)
    (case c
      ((#\f) (consumeChar) (state161 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state158 c)
    (case c
      ((#\n) (consumeChar) (state157 (scanChar)))
      (else (accept 'number))))
  (define (state159 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      (else (scannerError errIncompleteToken))))
  (define (state160 c)
    (case c
      ((#\0) (consumeChar) (state159 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state161 c)
    (case c
      ((#\.) (consumeChar) (state160 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state162 c)
    (case c
      ((#\n) (consumeChar) (state161 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state163 c)
    (case c
      ((#\a) (consumeChar) (state162 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state164 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state165 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state165 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state171 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state170 (scanChar)))
      ((#\|) (consumeChar) (state167 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state165 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state166 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state166 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state167 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state166 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state168 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state168 (scanChar)))
      ((#\|) (consumeChar) (state167 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state169 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state168 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state170 c)
    (case c
      ((#\+ #\-) (consumeChar) (state169 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state168 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state171 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state171 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state170 (scanChar)))
      ((#\|) (consumeChar) (state167 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state172 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state172 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state173 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state173 (scanChar)))
      ((#\#) (consumeChar) (state172 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state174 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state173 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state175 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state175 (scanChar)))
      ((#\/) (consumeChar) (state174 (scanChar)))
      ((#\.) (consumeChar) (state171 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state170 (scanChar)))
      ((#\|) (consumeChar) (state167 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state176 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state176 (scanChar)))
      ((#\#) (consumeChar) (state175 (scanChar)))
      ((#\/) (consumeChar) (state174 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state170 (scanChar)))
      ((#\|) (consumeChar) (state167 (scanChar)))
      ((#\.) (consumeChar) (state165 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state177 c)
    (case c
      ((#\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state176 (scanChar)))
      ((#\.) (consumeChar) (state164 (scanChar)))
      ((#\n) (consumeChar) (state163 (scanChar)))
      ((#\i) (consumeChar) (state158 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state178 c)
    (case c
      ((#\f) (consumeChar) (state181 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state179 c)
    (case c
      ((#\n) (consumeChar) (state178 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state180 c)
    (case c
      ((#\0) (consumeChar) (accept 'number))
      (else (scannerError errIncompleteToken))))
  (define (state181 c)
    (case c
      ((#\.) (consumeChar) (state180 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state182 c)
    (case c
      ((#\n) (consumeChar) (state181 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state183 c)
    (case c
      ((#\a) (consumeChar) (state182 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state184 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state185 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state185 c)
    (case c
      ((#\#) (consumeChar) (state191 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state190 (scanChar)))
      ((#\|) (consumeChar) (state187 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state185 (scanChar)))
      (else (accept 'number))))
  (define (state186 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state186 (scanChar)))
      (else (accept 'number))))
  (define (state187 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state186 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state188 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state188 (scanChar)))
      ((#\|) (consumeChar) (state187 (scanChar)))
      (else (accept 'number))))
  (define (state189 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state188 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state190 c)
    (case c
      ((#\+ #\-) (consumeChar) (state189 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state188 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state191 c)
    (case c
      ((#\#) (consumeChar) (state191 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state190 (scanChar)))
      ((#\|) (consumeChar) (state187 (scanChar)))
      (else (accept 'number))))
  (define (state192 c)
    (case c
      ((#\#) (consumeChar) (state192 (scanChar)))
      (else (accept 'number))))
  (define (state193 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state193 (scanChar)))
      ((#\#) (consumeChar) (state192 (scanChar)))
      (else (accept 'number))))
  (define (state194 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state193 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state195 c)
    (case c
      ((#\#) (consumeChar) (state195 (scanChar)))
      ((#\/) (consumeChar) (state194 (scanChar)))
      ((#\.) (consumeChar) (state191 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state190 (scanChar)))
      ((#\|) (consumeChar) (state187 (scanChar)))
      (else (accept 'number))))
  (define (state196 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state196 (scanChar)))
      ((#\#) (consumeChar) (state195 (scanChar)))
      ((#\/) (consumeChar) (state194 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state190 (scanChar)))
      ((#\|) (consumeChar) (state187 (scanChar)))
      ((#\.) (consumeChar) (state185 (scanChar)))
      (else (accept 'number))))
  (define (state197 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state196 (scanChar)))
      ((#\.) (consumeChar) (state184 (scanChar)))
      ((#\n) (consumeChar) (state183 (scanChar)))
      ((#\i) (consumeChar) (state179 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state198 c)
    (case c
      ((#\+ #\-) (consumeChar) (state197 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state196 (scanChar)))
      ((#\.) (consumeChar) (state184 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state199 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state199 (scanChar)))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      (else (accept 'number))))
  (define (state200 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state200 (scanChar)))
      ((#\#) (consumeChar) (state199 (scanChar)))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      (else (accept 'number))))
  (define (state201 c)
    (case c
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state200 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state202 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\#) (consumeChar) (state202 (scanChar)))
      ((#\/) (consumeChar) (state201 (scanChar)))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\.) (consumeChar) (state156 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state155 (scanChar)))
      ((#\|) (consumeChar) (state152 (scanChar)))
      (else (accept 'number))))
  (define (state203 c)
    (case c
      ((#\i #\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state203 (scanChar)))
      ((#\#) (consumeChar) (state202 (scanChar)))
      ((#\/) (consumeChar) (state201 (scanChar)))
      ((#\@) (consumeChar) (state198 (scanChar)))
      ((#\+ #\-) (consumeChar) (state177 (scanChar)))
      ((#\e #\E #\s #\S #\f #\F #\d #\D #\l #\L)
       (consumeChar)
       (state155 (scanChar)))
      ((#\|) (consumeChar) (state152 (scanChar)))
      ((#\.) (consumeChar) (state150 (scanChar)))
      (else (accept 'number))))
  (define (state204 c)
    (case c
      ((#\I) (consumeChar) (accept 'number))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state203 (scanChar)))
      ((#\.) (consumeChar) (state149 (scanChar)))
      ((#\n) (consumeChar) (state148 (scanChar)))
      ((#\i) (consumeChar) (state143 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state205 c)
    (case c
      ((#\+ #\-) (consumeChar) (state204 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state141 (scanChar)))
      ((#\.) (consumeChar) (state129 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state206 c)
    (case c
      ((#\i #\I #\e #\E)
       (consumeChar)
       (state205 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state207 c)
    (case c
      ((#\#) (consumeChar) (state206 (scanChar)))
      ((#\+ #\-) (consumeChar) (state204 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state141 (scanChar)))
      ((#\.) (consumeChar) (state129 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state208 c)
    (case c
      ((#\s) (consumeChar) (accept 'miscflag))
      (else (scannerError errIncompleteToken))))
  (define (state209 c)
    (case c
      ((#\r) (consumeChar) (state208 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state210 c)
    (case c
      ((#\6) (consumeChar) (state209 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state211 c)
    (case c
      ((#\r) (consumeChar) (state210 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state212 c)
    (case c
      ((#\`) (consumeChar) (accept 'quasisyntax))
      ((#\') (consumeChar) (accept 'syntax))
      ((#\() (consumeChar) (accept 'vecstart))
      ((#\t #\T #\f #\F)
       (consumeChar)
       (accept 'boolean))
      ((#\;) (consumeChar) (accept 'commentdatum))
      ((#\|) (consumeChar) (accept 'comment))
      ((#\!) (consumeChar) (state211 (scanChar)))
      ((#\d #\D) (consumeChar) (state207 (scanChar)))
      ((#\i #\I #\e #\E)
       (consumeChar)
       (state128 (scanChar)))
      ((#\b #\B) (consumeChar) (state28 (scanChar)))
      ((#\o #\O) (consumeChar) (state26 (scanChar)))
      ((#\x #\X) (consumeChar) (state24 (scanChar)))
      ((#\\) (consumeChar) (state22 (scanChar)))
      ((#\v) (consumeChar) (state18 (scanChar)))
      ((#\,) (consumeChar) (state15 (scanChar)))
      ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
       (consumeChar)
       (state14 (scanChar)))
      (else (scannerError errIncompleteToken))))
  (define (state213 c)
    (case c
      (else
       (if ((lambda (c)
              (and (char? c)
                   (not (char=? c (integer->char 10)))))
            c)
           (begin (consumeChar) (state213 (scanChar)))
           (begin
             (set! string_accumulator_length 0)
             (state0 (scanChar)))))))
  (define (state214 c)
    (case c
      (else
       (begin
         (set! string_accumulator_length 0)
         (state0 (scanChar))))))
  (define (state215 c)
    (case c (else (accept 'comment))))
  (define (state216 c)
    (case c (else (accept 'commentdatum))))
  (define (state217 c)
    (case c (else (accept 'miscflag))))
  (define (state218 c)
    (case c (else (accept 'boolean))))
  (define (state219 c)
    (case c (else (accept 'number))))
  (define (state220 c)
    (case c (else (accept 'character))))
  (define (state221 c)
    (case c (else (accept 'vecstart))))
  (define (state222 c)
    (case c (else (accept 'bvecstart))))
  (define (state223 c)
    (case c (else (accept 'syntax))))
  (define (state224 c)
    (case c (else (accept 'quasisyntax))))
  (define (state225 c)
    (case c (else (accept 'unsyntaxsplicing))))
  (define (state226 c)
    (case c (else (accept 'sharingdef))))
  (define (state227 c)
    (case c (else (accept 'sharinguse))))
  (define (state228 c)
    (case c (else (accept 'eofobj))))
  (define (state229 c)
    (case c (else (accept 'id))))
  (define (state230 c)
    (case c (else (accept 'string))))
  (define (state231 c)
    (case c (else (accept 'lparen))))
  (define (state232 c)
    (case c (else (accept 'rparen))))
  (define (state233 c)
    (case c (else (accept 'lbracket))))
  (define (state234 c)
    (case c (else (accept 'rbracket))))
  (define (state235 c)
    (case c (else (accept 'quote))))
  (define (state236 c)
    (case c (else (accept 'backquote))))
  (define (state237 c)
    (case c (else (accept 'splicing))))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; End of state machine generated by LexGen.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; ParseGen generated the code for the strong LL(1) parser.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
(define (parse-outermost-datum)
  (case (next-token)
    ((unsyntaxsplicing
       unsyntax
       quasisyntax
       syntax
       splicing
       comma
       backquote
       quote
       lbracket
       lparen
       vecstart
       bvecstart
       miscflag
       id
       string
       character
       number
       boolean
       sharingdef
       sharinguse)
     (let ((ast1 (parse-datum))) (identity ast1)))
    ((eofobj) (begin (consume-token!) (makeEOF)))
    (else
     (parse-error
       '<outermost-datum>
       '(backquote
          boolean
          bvecstart
          character
          comma
          eofobj
          id
          lbracket
          lparen
          miscflag
          number
          quasisyntax
          quote
          sharingdef
          sharinguse
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-datum)
  (case (next-token)
    ((sharinguse)
     (let ((ast1 (parse-sharinguse)))
       (makeSharingUse ast1)))
    ((sharingdef)
     (let ((ast1 (parse-sharingdef)))
       (let ((ast2 (parse-udatum)))
         (makeSharingDef ast1 ast2))))
    ((boolean
       number
       character
       string
       id
       miscflag
       bvecstart
       vecstart
       lparen
       lbracket
       quote
       backquote
       comma
       splicing
       syntax
       quasisyntax
       unsyntax
       unsyntaxsplicing)
     (let ((ast1 (parse-udatum))) (identity ast1)))
    (else
     (parse-error
       '<datum>
       '(backquote
          boolean
          bvecstart
          character
          comma
          id
          lbracket
          lparen
          miscflag
          number
          quasisyntax
          quote
          sharingdef
          sharinguse
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-udatum)
  (case (next-token)
    ((unsyntaxsplicing
       unsyntax
       quasisyntax
       syntax
       splicing
       comma
       backquote
       quote
       lbracket
       lparen
       vecstart
       bvecstart)
     (let ((ast1 (parse-location)))
       (let ((ast2 (parse-structured)))
         (makeStructured ast1 ast2))))
    ((miscflag) (begin (consume-token!) (makeFlag)))
    ((id) (begin (consume-token!) (makeSym)))
    ((string) (begin (consume-token!) (makeString)))
    ((character) (begin (consume-token!) (makeChar)))
    ((number) (begin (consume-token!) (makeNum)))
    ((boolean) (begin (consume-token!) (makeBool)))
    (else
     (parse-error
       '<udatum>
       '(backquote
          boolean
          bvecstart
          character
          comma
          id
          lbracket
          lparen
          miscflag
          number
          quasisyntax
          quote
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-structured)
  (case (next-token)
    ((bvecstart)
     (let ((ast1 (parse-bytevector))) (identity ast1)))
    ((vecstart)
     (let ((ast1 (parse-vector))) (identity ast1)))
    ((lparen
       lbracket
       quote
       backquote
       comma
       splicing
       syntax
       quasisyntax
       unsyntax
       unsyntaxsplicing)
     (let ((ast1 (parse-list))) (identity ast1)))
    (else
     (parse-error
       '<structured>
       '(backquote
          bvecstart
          comma
          lbracket
          lparen
          quasisyntax
          quote
          splicing
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-string)
  (case (next-token)
    ((string) (begin (consume-token!) (makeString)))
    (else (parse-error '<string> '(string)))))

(define (parse-symbol)
  (case (next-token)
    ((id) (begin (consume-token!) (makeSym)))
    (else (parse-error '<symbol> '(id)))))

(define (parse-list)
  (case (next-token)
    ((unsyntaxsplicing
       unsyntax
       quasisyntax
       syntax
       splicing
       comma
       backquote
       quote)
     (let ((ast1 (parse-abbreviation)))
       (identity ast1)))
    ((lbracket)
     (begin
       (consume-token!)
       (let ((ast1 (parse-blst2))) (identity ast1))))
    ((lparen)
     (begin
       (consume-token!)
       (let ((ast1 (parse-list2))) (identity ast1))))
    (else
     (parse-error
       '<list>
       '(backquote
          comma
          lbracket
          lparen
          quasisyntax
          quote
          splicing
          syntax
          unsyntax
          unsyntaxsplicing)))))

(define (parse-list2)
  (case (next-token)
    ((unsyntaxsplicing
       unsyntax
       quasisyntax
       syntax
       splicing
       comma
       backquote
       quote
       lbracket
       lparen
       vecstart
       bvecstart
       miscflag
       id
       string
       character
       number
       boolean
       sharingdef
       sharinguse)
     (let ((ast1 (parse-datum)))
       (let ((ast2 (parse-list3))) (cons ast1 ast2))))
    ((rparen) (begin (consume-token!) (emptyList)))
    (else
     (parse-error
       '<list2>
       '(backquote
          boolean
          bvecstart
          character
          comma
          id
          lbracket
          lparen
          miscflag
          number
          quasisyntax
          quote
          rparen
          sharingdef
          sharinguse
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-list3)
  (case (next-token)
    ((rparen
       period
       sharinguse
       sharingdef
       boolean
       number
       character
       string
       id
       miscflag
       bvecstart
       vecstart
       lparen
       lbracket
       quote
       backquote
       comma
       splicing
       syntax
       quasisyntax
       unsyntax
       unsyntaxsplicing)
     (let ((ast1 (parse-data)))
       (let ((ast2 (parse-list4)))
         (pseudoAppend ast1 ast2))))
    (else
     (parse-error
       '<list3>
       '(backquote
          boolean
          bvecstart
          character
          comma
          id
          lbracket
          lparen
          miscflag
          number
          period
          quasisyntax
          quote
          rparen
          sharingdef
          sharinguse
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-list4)
  (case (next-token)
    ((period)
     (begin
       (consume-token!)
       (let ((ast1 (parse-datum)))
         (if (eq? (next-token) 'rparen)
             (begin (consume-token!) (identity ast1))
             (parse-error '<list4> '(rparen))))))
    ((rparen) (begin (consume-token!) (emptyList)))
    (else (parse-error '<list4> '(period rparen)))))

(define (parse-blst2)
  (case (next-token)
    ((unsyntaxsplicing
       unsyntax
       quasisyntax
       syntax
       splicing
       comma
       backquote
       quote
       lbracket
       lparen
       vecstart
       bvecstart
       miscflag
       id
       string
       character
       number
       boolean
       sharingdef
       sharinguse)
     (let ((ast1 (parse-datum)))
       (let ((ast2 (parse-blst3))) (cons ast1 ast2))))
    ((rbracket) (begin (consume-token!) (emptyList)))
    (else
     (parse-error
       '<blst2>
       '(backquote
          boolean
          bvecstart
          character
          comma
          id
          lbracket
          lparen
          miscflag
          number
          quasisyntax
          quote
          rbracket
          sharingdef
          sharinguse
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-blst3)
  (case (next-token)
    ((rbracket
       period
       sharinguse
       sharingdef
       boolean
       number
       character
       string
       id
       miscflag
       bvecstart
       vecstart
       lparen
       lbracket
       quote
       backquote
       comma
       splicing
       syntax
       quasisyntax
       unsyntax
       unsyntaxsplicing)
     (let ((ast1 (parse-data)))
       (let ((ast2 (parse-blst4)))
         (pseudoAppend ast1 ast2))))
    (else
     (parse-error
       '<blst3>
       '(backquote
          boolean
          bvecstart
          character
          comma
          id
          lbracket
          lparen
          miscflag
          number
          period
          quasisyntax
          quote
          rbracket
          sharingdef
          sharinguse
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-blst4)
  (case (next-token)
    ((period)
     (begin
       (consume-token!)
       (let ((ast1 (parse-datum)))
         (if (eq? (next-token) 'rbracket)
             (begin (consume-token!) (identity ast1))
             (parse-error '<blst4> '(rbracket))))))
    ((rbracket) (begin (consume-token!) (emptyList)))
    (else (parse-error '<blst4> '(period rbracket)))))

(define (parse-abbreviation)
  (case (next-token)
    ((quote backquote
            comma
            splicing
            syntax
            quasisyntax
            unsyntax
            unsyntaxsplicing)
     (let ((ast1 (parse-abbrev-prefix)))
       (let ((ast2 (parse-datum))) (list ast1 ast2))))
    (else
     (parse-error
       '<abbreviation>
       '(backquote
          comma
          quasisyntax
          quote
          splicing
          syntax
          unsyntax
          unsyntaxsplicing)))))

(define (parse-abbrev-prefix)
  (case (next-token)
    ((unsyntaxsplicing)
     (begin (consume-token!) (symUnsyntax-splicing)))
    ((unsyntax)
     (begin (consume-token!) (symUnsyntax)))
    ((quasisyntax)
     (begin (consume-token!) (symQuasisyntax)))
    ((syntax) (begin (consume-token!) (symSyntax)))
    ((splicing)
     (begin (consume-token!) (symSplicing)))
    ((comma) (begin (consume-token!) (symUnquote)))
    ((backquote)
     (begin (consume-token!) (symBackquote)))
    ((quote) (begin (consume-token!) (symQuote)))
    (else
     (parse-error
       '<abbrev-prefix>
       '(backquote
          comma
          quasisyntax
          quote
          splicing
          syntax
          unsyntax
          unsyntaxsplicing)))))

(define (parse-vector)
  (case (next-token)
    ((vecstart)
     (begin
       (consume-token!)
       (let ((ast1 (parse-data)))
         (if (eq? (next-token) 'rparen)
             (begin (consume-token!) (list2vector ast1))
             (parse-error '<vector> '(rparen))))))
    (else (parse-error '<vector> '(vecstart)))))

(define (parse-bytevector)
  (case (next-token)
    ((bvecstart)
     (begin
       (consume-token!)
       (let ((ast1 (parse-octets)))
         (if (eq? (next-token) 'rparen)
             (begin (consume-token!) (list2bytevector ast1))
             (parse-error '<bytevector> '(rparen))))))
    (else (parse-error '<bytevector> '(bvecstart)))))

(define (parse-data)
  (case (next-token)
    ((unsyntaxsplicing
       unsyntax
       quasisyntax
       syntax
       splicing
       comma
       backquote
       quote
       lbracket
       lparen
       vecstart
       bvecstart
       miscflag
       id
       string
       character
       number
       boolean
       sharingdef
       sharinguse)
     (let ((ast1 (parse-datum)))
       (let ((ast2 (parse-data))) (cons ast1 ast2))))
    ((rparen period rbracket) (emptyList))
    (else
     (parse-error
       '<data>
       '(backquote
          boolean
          bvecstart
          character
          comma
          id
          lbracket
          lparen
          miscflag
          number
          period
          quasisyntax
          quote
          rbracket
          rparen
          sharingdef
          sharinguse
          splicing
          string
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-octets)
  (case (next-token)
    ((number)
     (let ((ast1 (parse-octet)))
       (let ((ast2 (parse-octets))) (cons ast1 ast2))))
    ((rparen) (emptyList))
    (else (parse-error '<octets> '(number rparen)))))

(define (parse-octet)
  (case (next-token)
    ((number) (begin (consume-token!) (makeOctet)))
    (else (parse-error '<octet> '(number)))))

(define (parse-location)
  (case (next-token)
    ((unsyntaxsplicing
       unsyntax
       quasisyntax
       syntax
       splicing
       comma
       backquote
       quote
       lbracket
       lparen
       vecstart
       bvecstart)
     (sourceLocation))
    (else
     (parse-error
       '<location>
       '(backquote
          bvecstart
          comma
          lbracket
          lparen
          quasisyntax
          quote
          splicing
          syntax
          unsyntax
          unsyntaxsplicing
          vecstart)))))

(define (parse-sharingdef)
  (case (next-token)
    ((sharingdef)
     (begin (consume-token!) (sharingDef)))
    (else (parse-error '<sharingdef> '(sharingdef)))))

(define (parse-sharinguse)
  (case (next-token)
    ((sharinguse)
     (begin (consume-token!) (sharingUse)))
    (else (parse-error '<sharinguse> '(sharinguse)))))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; End of LL(1) parser generated by ParseGen.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; Lexical analyzer.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
    ; next-token and consume-token! are called by the parser.
  
    ; Returns the current token.
  
    (define (next-token)
      (if nextTokenIsReady
          kindOfNextToken
          (begin (set! string_accumulator_length 0)
                 (scanner0))))
  
    ; Consumes the current token.
  
    (define (consume-token!)
      (set! nextTokenIsReady #f))
  
    ; Called by the lexical analyzer's state machine.
  
    (define (scannerError msg)
      (define msgtxt
        (cond ((= msg errLongToken)
               "Amazingly long token")
              ((= msg errIncompleteToken)
               "Incomplete or illegal token")
              ((= msg errIllegalHexEscape)
               "Illegal hex escape")
              ((= msg errIllegalNamedChar)
               "Illegal character syntax")
              ((= msg errIllegalString)
               "Illegal string syntax")
              ((= msg errIllegalSymbol)
               "Illegal symbol syntax")
              ((= msg errSRFI38)
               "Illegal SRFI 38 syntax")
              ((= msg errNoDelimiter)
               "Missing delimiter")
              ((= msg errLexGenBug)
               "Bug in lexical analyzer (generated)")
              (else "Bug in lexical analyzer")))
      (let* ((c (scanChar))
             (next (if (char? c) (string c) ""))
             (msgtxt (string-append msgtxt
                                    ": "
                                    (substring string_accumulator
                                               0
                                               string_accumulator_length)
                                    next)))

        ; must avoid infinite loop on current input port

        (consumeChar)
        (error 'get-datum
               (string-append "Lexical Error: " msgtxt " ")
               input-port))
      (next-token))
  
    ; Accepts a token of the given kind, returning that kind.
    ;
    ; For some kinds of tokens, a value for the token must also
    ; be recorded in tokenValue.  Most of those tokens must be
    ; followed by a delimiter.
    ;
    ; Some magical tokens require special processing.
  
    (define (accept t)
      (case t

       ((comment)
        ; The token is #|, which starts a nested comment.
        (scan-nested-comment)
        (next-token))

       ((commentdatum)
        ; The token is #; so parse and ignore the next datum.
        (parse-datum)
        (next-token))

       ((id boolean number character string miscflag period
         sharingdef sharinguse)

        (set! tokenValue
              (substring string_accumulator
                         0 string_accumulator_length))

        (cond ((and (eq? t 'miscflag)
                    (string=? tokenValue "#!r6rs"))
               (next-token))

              ((or (delimiter? (scanChar))
                   (eq? t 'string)
                   (eq? t 'sharingdef)                                ; SRFI 38
                   (eq? t 'sharinguse))                               ; SRFI 38
               (set! kindOfNextToken t)
               (set! nextTokenIsReady #t)
               t)

              (else
               (scannerError errNoDelimiter))))

       (else
        (set! kindOfNextToken t)
        (set! nextTokenIsReady #t)
        t)))

    ; Having seen a #| token, scans and discards the entire comment.

    (define (scan-nested-comment)
      (define (loop depth)
        (let ((c (scanChar)))
          (cond ((= depth 0) #t)
                ((eof-object? c)
                 (scannerError errIncompleteToken))
                ((char=? c #\#)
                 (consumeChar)
                 (if (char=? (scanChar) #\|)
                     (begin (consumeChar) (loop (+ depth 1)))
                     (loop depth)))
                ((char=? c #\|)
                 (consumeChar)
                 (if (char=? (scanChar) #\#)
                     (begin (consumeChar) (loop (- depth 1)))
                     (loop depth)))
                (else
                 (consumeChar)
                 (loop depth)))))
      (loop 1))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; Character i/o, so to speak.
    ; Uses the input-port as input.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
    (define (scanChar)
      (peek-char input-port))

    ; Consumes the current character.  Returns unspecified values.
  
    (define (consumeChar)
      (if (< string_accumulator_length (string-length string_accumulator))
          (let ((c (read-char input-port)))
            (if (char? c)
                (begin
                 (string-set! string_accumulator
                              string_accumulator_length
                              c)
                 (set! string_accumulator_length
                       (+ string_accumulator_length 1)))))
          (begin (expand-accumulator) (consumeChar))))

    ; Doubles the size of string_accumulator while
    ; preserving its contents.

    (define (expand-accumulator)
      (let* ((n (string-length string_accumulator))
             (new (make-string (* 2 n))))
        (do ((i 0 (+ i 1)))
            ((= i n))
          (string-set! new i (string-ref string_accumulator i)))
        (set! string_accumulator new)))

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; Miscellaneous utility routines.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ; Determines whether its argument is a <delimiter>.

    (define (delimiter? c)
      (case c
       ((#\( #\) #\[ #\] #\" #\; #\#)
        #t)
       (else
        (or (not (char? c))
            (char-whitespace? c)))))         

    ; Given the integer parsed from a hex escape,
    ; returns the corresponding Unicode character.

    (define (checked-integer->char n)
      (if (or (< n #xd800)
              (<= #xe000 n #x10ffff))
          (integer->char n)
          (scannerError errIllegalHexEscape)))

    ; Given a string and the index at the beginning of nonempty
    ; sequence of hexadecimal characters followed by a semicolon,
    ; returns two values:
    ;     the numerical value of the hex characters
    ;     the index following the semicolon

    (define (hex-escape s i)
      (let ((n (string-length s)))
        (define (loop i val)
          (if (>= i n)
              (scannerError errIllegalHexEscape)
              (let ((c (string-ref s i)))
                (case c
                 ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
                  (loop (+ i 1)
                        (+ (* 16 val)
                           (- (char->integer c) (char->integer #\0)))))
                 ((#\a #\b #\c #\d #\e #\f)
                  (loop (+ i 1)
                        (+ (* 16 val)
                           10
                           (- (char->integer c) (char->integer #\a)))))
                 ((#\A #\B #\C #\D #\E #\F)
                  (loop (+ i 1)
                        (+ (* 16 val)
                           10
                           (- (char->integer c) (char->integer #\A)))))
                 ((#\;)
                  (values val (+ i 1)))
                 (else (scannerError errIllegalHexEscape))))))
        (loop i 0)))
  
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; Action procedures called by the parser.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    ; Hook for recording source locations.
    ; Called by some action routines.

    (define (record-source-location x loc) x)

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    (define (emptyList) '())
  
    (define (identity x) x)

    (define (list2bytevector octets) (u8-list->bytevector octets))

    (define (list2vector vals) (list->vector vals))
  
    (define (makeBool)
      (let ((x (case (string-ref tokenValue 1)
                ((#\t #\T) #t)
                ((#\f #\F) #f)
                (else (scannerError errBug)))))
        (record-source-location x locationStart)))
  
    (define (makeChar)
      (let* ((n (string-length tokenValue))
             (x (cond ((= n 3)
                       (string-ref tokenValue 2))
                      ((char=? #\x (string-ref tokenValue 2))
                       (checked-integer->char
                        (string->number (substring tokenValue 3 n) 16)))
                      (else
                       (let* ((s (substring tokenValue 2 n))
                              (sym (string->symbol s)))
                         (case sym
                          ((nul)               #\nul)
                          ((alarm)             #\alarm)
                          ((backspace)         #\backspace)
                          ((tab)               #\tab)
                          ((linefeed newline)  #\linefeed)
                          ((vtab)              #\vtab)
                          ((page)              #\page)
                          ((return)            #\return)
                          ((esc)               #\esc)
                          ((space)             #\space)
                          ((delete)            #\delete)
                          (else
                           (scannerError errIllegalNamedChar))))))))
        (record-source-location x locationStart)))

    (define (makeEOF) (eof-object))

    (define (makeFlag)

      ; The draft R6RS allows implementation-specific extensions
      ; of the form #!..., which are processed here.
      ; Note that the #!r6rs flag is a comment, handled by accept,
      ; so that flag will never be seen here.

      (accept 'miscflag)
      (parse-error '<miscflag> '(miscflag)))
  
    (define (makeNum)
      (let ((x (string->number tokenValue)))
        (if x
            (record-source-location x locationStart)
            (begin (accept 'number)
                   (parse-error '<number> '(number))))))
  
    (define (makeOctet)
      (let ((n (string->number tokenValue)))
        (if (and (exact? n) (integer? n) (<= 0 n 255))
            (record-source-location n locationStart)
            (begin (accept 'octet)
                   (parse-error '<octet> '(octet))))))
  
    (define (makeString)

      ; Must strip off outer double quotes and deal with escapes.
      ;
      ; i is the next index into tokenValue
      ; n is the exclusive upper bound for i
      ; newstring is a string that might become the result
      ; j is the next index into newstring

      (define (loop i n newstring j)
        (if (>= i n)
            (if (= j (string-length newstring))
                newstring
                (substring newstring 0 j))
            (let ((c (string-ref tokenValue i)))
              (cond ((or (char=? c #\return)
                         (char=? c #\linefeed)
                         (char=? c char:nel)
                         (char=? c char:ls))
                     (string-set! newstring j #\linefeed)
                     (let* ((i+1 (+ i 1))
                            (i+1 (if (and (char=? c #\return)
                                          (< i+1 n))
                                     (let ((c2 (string-ref tokenValue i+1)))
                                       (if (or (char=? c2 #\linefeed)
                                               (char=? c2 char:nel))
                                           (+ i 2)
                                           i+1))
                                     i+1)))
                       (loop i+1 n newstring (+ j 1))))
                    ((char=? c #\\)
                     (if (< (+ i 1) n)
                         (let ((c2 (string-ref tokenValue (+ i 1))))
                           (case c2
                            ((#\a)
                             (string-set! newstring j #\alarm)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\b)
                             (string-set! newstring j #\backspace)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\t)
                             (string-set! newstring j #\tab)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\n)
                             (string-set! newstring j #\linefeed)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\v)
                             (string-set! newstring j #\vtab)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\f)
                             (string-set! newstring j #\page)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\r)
                             (string-set! newstring j #\return)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\" #\\)
                             (string-set! newstring j c2)
                             (loop (+ i 2) n newstring (+ j 1)))
                            ((#\x)
                             (call-with-values
                              (lambda () (hex-escape tokenValue (+ i 2)))
                              (lambda (sv i)
                                (string-set! newstring
                                             j
                                             (checked-integer->char sv))
                                (loop i n newstring (+ j 1)))))
                            (else
                             (ignore-escaped-line-ending (+ i 1)
                                                         n newstring j #f))))
                     (scannerError errIllegalString)))
                    (else
                     (string-set! newstring j c)
                     (loop (+ i 1) n newstring (+ j 1)))))))

      ; Ignores <intraline whitespace>* <line ending> <intraline whitespace>*
      ; after? is true iff the <line ending> has already been ignored.
      ; The other arguments are the same as for loop above.

      (define (ignore-escaped-line-ending i n newstring j after?)
        (cond ((< i n)
               (let ((c (string-ref tokenValue i)))
                 (cond ((or (char=? c #\tab)
                            (eq? 'Zs (char-general-category c)))
                        (ignore-escaped-line-ending (+ i 1)
                                                    n newstring j after?))
                       (after?
                        (loop i n newstring j))
                       ((or (char=? c #\return)
                            (char=? c #\linefeed)
                            (char=? c char:nel)
                            (char=? c char:ls))
                        (let* ((i+1 (+ i 1))
                               (i+1 (if (and (char=? c #\return)
                                             (< i+1 n))
                                        (let ((c2 (string-ref
                                                   tokenValue i+1)))
                                          (if (or (char=? c2 #\linefeed)
                                                  (char=? c2 char:nel))
                                              (+ i 2)
                                              i+1))
                                        i+1)))
                          (ignore-escaped-line-ending i+1 n newstring j #t)))
                       (else
                        (scannerError errIllegalString)))))
              (after?
               (loop i n newstring j))
              (else
               (scannerError errIllegalString))))

      (let* ((n (string-length tokenValue))
             (s (loop 1 (- n 1) (make-string (- n 2)) 0)))
        (record-source-location s locationStart)))

    (define (makeStructured loc0 x)
      (record-source-location x loc0))

    (define (makeSym)
      (let ((n (string-length tokenValue)))
        (define (return sym)
          sym)
        (define (loop i)
          (if (= i n)
              (return (string->symbol tokenValue))
              (let ((c (string-ref tokenValue i)))
                (cond ((or (char=? c #\\)
                           (char=? c #\#))
                       (slow-loop i
                                  (reverse
                                   (string->list (substring tokenValue 0 i)))))
                      (else
                       (loop (+ i 1)))))))
        (define (slow-loop i chars)
          (if (= i n)
              (return (string->symbol (list->string (reverse chars))))
              (let ((c (string-ref tokenValue i)))
                (cond ((char=? c #\\)
                       (cond ((and (< (+ i 1) n)
                                   (char=? (string-ref tokenValue (+ i 1))
                                           #\x))
                              (call-with-values
                               (lambda () (hex-escape tokenValue (+ i 2)))
                               (lambda (sv i)
                                 (slow-loop i
                                            (cons (checked-integer->char sv)
                                                  chars)))))
                             (else
                              (scannerError errIllegalSymbol))))
                      (else (slow-loop (+ i 1) (cons c chars)))))))
        (loop 0)))

    ; Like append, but allows the last argument to be a non-list.
  
    (define (pseudoAppend vals terminus)
      (if (null? vals)
          terminus
          (cons (car vals)
                (pseudoAppend (cdr vals) terminus))))

    ; Hook for associating source locations with tokens.

    (define (sourceLocation) 0)

    (define (symBackquote) 'quasiquote)
    (define (symQuasisyntax) 'quasisyntax)
    (define (symQuote) 'quote)
    (define (symSplicing) 'unquote-splicing)
    (define (symSyntax) 'syntax)
    (define (symUnquote) 'unquote)
    (define (symUnsyntax) 'unsyntax)
    (define (symUnsyntax-splicing) 'unsyntax-splicing)
  
    ; Action routines for SRFI 38.
    ;
    ; The shared-structures hashtable defines a mapping from
    ; indexes to fixup objects.
    ;
    ; A fixup object is a record with two mutable fields:
    ;     ready: #t if the object field is ready, else #f
    ;     value: if ready, the object that will replace the
    ;            fixup object during a post-pass

    (define (sharingDef)
      (let* ((index
              (string->number
               (substring tokenValue 1 (- (string-length tokenValue) 1))))
             (fixup (make-fixup-object index)))
        (if (not shared-structures)
            (set! shared-structures
                  (make-hashtable values =)))
        (hashtable-set! shared-structures index fixup)
        fixup))

    (define (sharingUse)
      (let* ((index
              (string->number
               (substring tokenValue 1 (- (string-length tokenValue) 1)))))
        (if (not shared-structures)
            (scannerError errSRFI38))
        (let ((fixup (hashtable-ref shared-structures index #f)))
          (if (not fixup)
              (scannerError errSRFI38))
          fixup)))

    (define (makeSharingDef fixup datum)
      (fixup-ready! fixup datum)
      datum)

    (define (makeSharingUse fixup)
      fixup)

    ;; After everything has been read, a second pass prepares
    ;; and then executes the side effects needed to recreate
    ;; the shared structure.

    (define (srfi38-postpass x)
      (let ((fixups '()))

        (define (add-fixup! fixup-object kind . rest)
          (if (fixup-ready? fixup-object)
              (set! fixups
                    (cons (cons (fixup-value fixup-object)
                                (cons kind rest))
                          fixups))
              (assertion-violation 'read-with-shared-structure
                                   "undefined index"
                                   (fixup-index fixup-object))))

        (define (postpass x)
          (cond ((pair? x)
                 (if (fixup-object? (car x))
                     (add-fixup! (car x) 'set-car! x)
                     (postpass (car x)))
                 (if (fixup-object? (cdr x))
                     (add-fixup! (cdr x) 'set-cdr! x)
                     (postpass (cdr x))))
                ((vector? x)
                 (do ((n (vector-length x))
                      (i 0 (+ i 1)))
                     ((= i n))
                   (let ((y (vector-ref x i)))
                     (if (fixup-object? y)
                         (add-fixup! y 'vector-set! x i)
                         (postpass y)))))
                (else #f)))

        (define (fixup! fixup)
          (let ((value (car fixup))
                (kind (cadr fixup))
                (container (caddr fixup))
                (rest (cdddr fixup)))
            (case kind
             ((set-car!)
              (set-car! container value))
             ((set-cdr!)
              (set-cdr! container value))
             ((vector-set!)
              (vector-set! container (car rest) value))
             (else
              (assert #f)))))

      (if shared-structures
          (begin (postpass x)
                 (for-each fixup! fixups)
                 (if (fixup-object? x)
                     (fixup-value x)
                     x))
          x)))


    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;
    ; Error procedure called by the parser.
    ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
    (define (parse-error nonterminal expected-terminals)
      (let* ((culprit (next-token))
             (culprit-as-string (symbol->string culprit))
             (culprit-as-string
              (if (memq culprit expected-terminals)
                  (string-append "illegal " culprit-as-string)
                  culprit-as-string))
             (msg (string-append
                   "Syntax error while parsing "
                   (symbol->string nonterminal)
                   (string #\newline)
                   "  Encountered "
                   culprit-as-string
                   " while expecting "
                   (case nonterminal
                    ((<datum> <outermost-datum> <data>)
                     "a datum")
                    (else
                     (string-append
                      (string #\newline)
                      "  "
                      (apply string-append
                             (map (lambda (terminal)
                                    (string-append " "
                                                   (symbol->string terminal)))
                                  expected-terminals)))))
                   (string #\newline))))
        (error 'get-datum msg input-port)))

    ; The list of tokens that can start a datum in R6RS mode.

    (define datum-starters
      '(backquote
        boolean
        bvecstart
        character
        comma
        id
        lbracket
        lparen
        miscflag
        number
        quasisyntax
        quote
        splicing
        string
        syntax
        unsyntax
        unsyntaxsplicing
        vecstart))
  
    (srfi38-postpass (parse-outermost-datum))))

  (define write/ss write-with-shared-structure)
  (define read/ss read-with-shared-structure)
)

Added srfi/s38/with-shared-structure.ypsilon.sls.































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s38 with-shared-structure)
  (export
    write-with-shared-structure
    (rename (write-with-shared-structure write/ss))
    read-with-shared-structure
    (rename (read-with-shared-structure read/ss)))
  (import
    (only (core) write-with-shared-structure read-with-shared-structure))
)

Added srfi/s39/parameters.ikarus.sls.



























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s39 parameters)
  (export 
    make-parameter 
    parameterize)
  (import 
    (only (ikarus) make-parameter parameterize))
)

Added srfi/s39/parameters.mzscheme.sls.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s39 parameters)
  (export 
    make-parameter 
    parameterize)
  (import 
    (only (scheme base) make-parameter parameterize))
)

Added srfi/s39/parameters.sls.







































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

;; Fall-back library in case the host Scheme system does not provide SRFI-39.

#!r6rs
(library (srfi s39 parameters)
  (export
    make-parameter 
    parameterize)
  (import
    (rnrs))

  (define make-parameter
    (case-lambda
      [(val) (make-parameter val values)]
      [(val guard)
       (unless (procedure? guard)
         (assertion-violation 'make-parameter "not a procedure" guard))
       (let ([p (case-lambda
                  [() val]
                  [(x) (set! val (guard x))])])
         (p val)
         p)]))
      
  (define-syntax parameterize
    ;; Derived from Ikarus's implementation of parameterize.
    (lambda (stx)
      (syntax-case stx ()
        [(_ () b0 b ...)
         #'(let () b0 b ...)]
        [(_ ([p e] ...) b0 b ...)
         (with-syntax ([(tp ...) (generate-temporaries #'(p ...))]
                       [(te ...) (generate-temporaries #'(e ...))])
           #'(let ([tp p] ...
                   [te e] ...)
               (let ([swap (lambda ()
                             (let ([t (tp)])
                               (tp te)
                               (set! te t))
                             ...)])
                 (dynamic-wind
                  swap
                  (lambda () b0 b ...)
                  swap))))])))  

)

Added srfi/s39/parameters.ypsilon.sls.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s39 parameters)
  (export 
    make-parameter parameterize)
  (import 
    (only (core) make-parameter parameterize))
)

Added srfi/s41/streams.sls.





































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!r6rs 
;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved.
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation files
;;; (the "Software"), to deal in the Software without restriction,
;;; including without limitation the rights to use, copy, modify, merge,
;;; publish, distribute, sublicense, and/or sell copies of the Software,
;;; and to permit persons to whom the Software is furnished to do so,
;;; subject to the following conditions:
;;; 
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.
;;; 
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
;;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
;;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
;;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;;; SOFTWARE.

(library (srfi s41 streams)

  (export stream-null stream-cons stream? stream-null? stream-pair? stream-car
          stream-cdr stream-lambda define-stream list->stream port->stream stream
          stream->list stream-append stream-concat stream-constant stream-drop
          stream-drop-while stream-filter stream-fold stream-for-each stream-from
          stream-iterate stream-length stream-let stream-map stream-match
          stream-of stream-range stream-ref stream-reverse stream-scan stream-take
          stream-take-while stream-unfold stream-unfolds stream-zip)

  (import (srfi s41 streams primitive)
          (srfi s41 streams derived)))

Added srfi/s41/streams/derived.sls.





























































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
#!r6rs 
;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved.
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation files
;;; (the "Software"), to deal in the Software without restriction,
;;; including without limitation the rights to use, copy, modify, merge,
;;; publish, distribute, sublicense, and/or sell copies of the Software,
;;; and to permit persons to whom the Software is furnished to do so,
;;; subject to the following conditions:
;;; 
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.
;;; 
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
;;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
;;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
;;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;;; SOFTWARE.

(library (srfi s41 streams derived)

  (export stream-null stream-cons stream? stream-null? stream-pair? stream-car
          stream-cdr stream-lambda define-stream list->stream port->stream stream
          stream->list stream-append stream-concat stream-constant stream-drop
          stream-drop-while stream-filter stream-fold stream-for-each stream-from
          stream-iterate stream-length stream-let stream-map stream-match
          stream-of stream-range stream-ref stream-reverse stream-scan stream-take
          stream-take-while stream-unfold stream-unfolds stream-zip)

  (import (rnrs) (srfi s41 streams primitive))

  (define-syntax define-stream
    (syntax-rules ()
      ((define-stream (name . formal) body0 body1 ...)
        (define name (stream-lambda formal body0 body1 ...)))))

  (define (list->stream objs)
    (define list->stream
      (stream-lambda (objs)
        (if (null? objs)
            stream-null
            (stream-cons (car objs) (list->stream (cdr objs))))))
    (if (not (list? objs))
        (error 'list->stream "non-list argument")
        (list->stream objs)))

  (define (port->stream . port)
    (define port->stream
      (stream-lambda (p)
        (let ((c (read-char p)))
          (if (eof-object? c)
              stream-null
              (stream-cons c (port->stream p))))))
    (let ((p (if (null? port) (current-input-port) (car port))))
      (if (not (input-port? p))
          (error 'port->stream "non-input-port argument")
          (port->stream p))))

  (define-syntax stream
    (syntax-rules ()
      ((stream) stream-null)
      ((stream x y ...) (stream-cons x (stream y ...)))))

  (define (stream->list . args)
    (let ((n (if (= 1 (length args)) #f (car args)))
          (strm (if (= 1 (length args)) (car args) (cadr args))))
      (cond ((not (stream? strm)) (error 'stream->list "non-stream argument"))
            ((and n (not (integer? n))) (error 'stream->list "non-integer count"))
            ((and n (negative? n)) (error 'stream->list "negative count"))
            (else (let loop ((n (if n n -1)) (strm strm))
                    (if (or (zero? n) (stream-null? strm))
                        '()
                        (cons (stream-car strm) (loop (- n 1) (stream-cdr strm)))))))))

  (define (stream-append . strms)
    (define stream-append
      (stream-lambda (strms)
        (cond ((null? (cdr strms)) (car strms))
              ((stream-null? (car strms)) (stream-append (cdr strms)))
              (else (stream-cons (stream-car (car strms))
                                 (stream-append (cons (stream-cdr (car strms)) (cdr strms))))))))
    (cond ((null? strms) stream-null)
          ((exists (lambda (x) (not (stream? x))) strms)
            (error 'stream-append "non-stream argument"))
          (else (stream-append strms))))

  (define (stream-concat strms)
    (define stream-concat
      (stream-lambda (strms)
        (cond ((stream-null? strms) stream-null)
              ((not (stream? (stream-car strms)))
                (error 'stream-concat "non-stream object in input stream"))
              ((stream-null? (stream-car strms))
                (stream-concat (stream-cdr strms)))
              (else (stream-cons
                      (stream-car (stream-car strms))
                      (stream-concat
                        (stream-cons (stream-cdr (stream-car strms)) (stream-cdr strms))))))))
    (if (not (stream? strms))
        (error 'stream-concat "non-stream argument")
        (stream-concat strms)))

  (define stream-constant
    (stream-lambda objs
      (cond ((null? objs) stream-null)
            ((null? (cdr objs)) (stream-cons (car objs) (stream-constant (car objs))))
            (else (stream-cons (car objs)
                               (apply stream-constant (append (cdr objs) (list (car objs)))))))))

  (define (stream-drop n strm)
    (define stream-drop
      (stream-lambda (n strm)
        (if (or (zero? n) (stream-null? strm))
            strm
            (stream-drop (- n 1) (stream-cdr strm)))))
    (cond ((not (integer? n)) (error 'stream-drop "non-integer argument"))
          ((negative? n) (error 'stream-drop "negative argument"))
          ((not (stream? strm)) (error 'stream-drop "non-stream argument"))
          (else (stream-drop n strm))))

  (define (stream-drop-while pred? strm)
    (define stream-drop-while
      (stream-lambda (strm)
        (if (and (stream-pair? strm) (pred? (stream-car strm)))
            (stream-drop-while (stream-cdr strm))
            strm)))
    (cond ((not (procedure? pred?)) (error 'stream-drop-while "non-procedural argument"))
          ((not (stream? strm)) (error 'stream-drop-while "non-stream argument"))
          (else (stream-drop-while strm))))

  (define (stream-filter pred? strm)
    (define stream-filter
      (stream-lambda (strm)
        (cond ((stream-null? strm) stream-null)
              ((pred? (stream-car strm))
                (stream-cons (stream-car strm) (stream-filter (stream-cdr strm))))
              (else (stream-filter (stream-cdr strm))))))
    (cond ((not (procedure? pred?)) (error 'stream-filter "non-procedural argument"))
          ((not (stream? strm)) (error 'stream-filter "non-stream argument"))
          (else (stream-filter strm))))

  (define (stream-fold proc base strm)
    (cond ((not (procedure? proc)) (error 'stream-fold "non-procedural argument"))
          ((not (stream? strm)) (error 'stream-fold "non-stream argument"))
          (else (let loop ((base base) (strm strm))
                  (if (stream-null? strm)
                      base
                      (loop (proc base (stream-car strm)) (stream-cdr strm)))))))

  (define (stream-for-each proc . strms)
    (define (stream-for-each strms)
      (if (not (exists stream-null? strms))
          (begin (apply proc (map stream-car strms))
                 (stream-for-each (map stream-cdr strms)))))
    (cond ((not (procedure? proc)) (error 'stream-for-each "non-procedural argument"))
          ((null? strms) (error 'stream-for-each "no stream arguments"))
          ((exists (lambda (x) (not (stream? x))) strms)
            (error 'stream-for-each "non-stream argument"))
          (else (stream-for-each strms))))

  (define (stream-from first . step)
    (define stream-from
      (stream-lambda (first delta)
        (stream-cons first (stream-from (+ first delta) delta))))
    (let ((delta (if (null? step) 1 (car step))))
      (cond ((not (number? first)) (error 'stream-from "non-numeric starting number"))
            ((not (number? delta)) (error 'stream-from "non-numeric step size"))
            (else (stream-from first delta)))))

  (define (stream-iterate proc base)
    (define stream-iterate
      (stream-lambda (base)
        (stream-cons base (stream-iterate (proc base)))))
    (if (not (procedure? proc))
        (error 'stream-iterate "non-procedural argument")
        (stream-iterate base)))

  (define (stream-length strm)
    (if (not (stream? strm))
        (error 'stream-length "non-stream argument")
        (let loop ((len 0) (strm strm))
          (if (stream-null? strm)
              len
              (loop (+ len 1) (stream-cdr strm))))))

  (define-syntax stream-let
    (syntax-rules ()
      ((stream-let tag ((name val) ...) body1 body2 ...)
       ((letrec ((tag (stream-lambda (name ...) body1 body2 ...))) tag) val ...))))

  (define (stream-map proc . strms)
    (define stream-map
      (stream-lambda (strms)
        (if (exists stream-null? strms)
            stream-null
            (stream-cons (apply proc (map stream-car strms))
                         (stream-map (map stream-cdr strms))))))
    (cond ((not (procedure? proc)) (error 'stream-map "non-procedural argument"))
          ((null? strms) (error 'stream-map "no stream arguments"))
          ((exists (lambda (x) (not (stream? x))) strms)
            (error 'stream-map "non-stream argument"))
          (else (stream-map strms))))

  (define-syntax stream-match
    (syntax-rules ()
      ((stream-match strm-expr clause ...)
        (let ((strm strm-expr))
          (cond
            ((not (stream? strm)) (error 'stream-match "non-stream argument"))
            ((stream-match-test strm clause) => car) ...
            (else (error 'stream-match "pattern failure")))))))

  (define-syntax stream-match-test
    (syntax-rules ()
      ((stream-match-test strm (pattern fender expr))
        (stream-match-pattern strm pattern () (and fender (list expr))))
      ((stream-match-test strm (pattern expr))
        (stream-match-pattern strm pattern () (list expr)))))

  (define-syntax stream-match-pattern 
    (lambda (x)
      (define (wildcard? x)
        (and (identifier? x)
             (free-identifier=? x (syntax _))))
      (syntax-case x () 
        ((stream-match-pattern strm () (binding ...) body)
          (syntax (and (stream-null? strm) (let (binding ...) body))))
        ((stream-match-pattern strm (w? . rest) (binding ...) body)
          (wildcard? #'w?) 
          (syntax (and (stream-pair? strm)
                       (let ((strm (stream-cdr strm)))
                         (stream-match-pattern strm rest (binding ...) body)))))
        ((stream-match-pattern strm (var . rest) (binding ...) body)
          (syntax (and (stream-pair? strm)
                       (let ((temp (stream-car strm)) (strm (stream-cdr strm))) 
                         (stream-match-pattern strm rest ((var temp) binding ...) body)))))
        ((stream-match-pattern strm w? (binding ...) body)
          (wildcard? #'w?)
          (syntax (let (binding ...) body)))
        ((stream-match-pattern strm var (binding ...) body) 
          (syntax (let ((var strm) binding ...) body))))))

  (define-syntax stream-of
    (syntax-rules ()
      ((_ expr rest ...)
        (stream-of-aux expr stream-null rest ...))))

  (define-syntax stream-of-aux
    (syntax-rules (in is)
      ((stream-of-aux expr base)
        (stream-cons expr base))
      ((stream-of-aux expr base (var in stream) rest ...)
        (stream-let loop ((strm stream))
          (if (stream-null? strm)
              base
              (let ((var (stream-car strm)))
                (stream-of-aux expr (loop (stream-cdr strm)) rest ...)))))
      ((stream-of-aux expr base (var is exp) rest ...)
        (let ((var exp)) (stream-of-aux expr base rest ...)))
      ((stream-of-aux expr base pred? rest ...)
        (if pred? (stream-of-aux expr base rest ...) base))))

  (define (stream-range first past . step)
    (define stream-range
      (stream-lambda (first past delta lt?)
        (if (lt? first past)
            (stream-cons first (stream-range (+ first delta) past delta lt?))
            stream-null)))
    (cond ((not (number? first)) (error 'stream-range "non-numeric starting number"))
          ((not (number? past)) (error 'stream-range "non-numeric ending number"))
          (else (let ((delta (cond ((pair? step) (car step)) ((< first past) 1) (else -1))))
                  (if (not (number? delta))
                      (error 'stream-range "non-numeric step size")
                      (let ((lt? (if (< 0 delta) < >)))
                        (stream-range first past delta lt?)))))))

  (define (stream-ref strm n)
    (cond ((not (stream? strm)) (error 'stream-ref "non-stream argument"))
          ((not (integer? n)) (error 'stream-ref "non-integer argument"))
          ((negative? n) (error 'stream-ref "negative argument"))
          (else (let loop ((strm strm) (n n))
                  (cond ((stream-null? strm) (error 'stream-ref "beyond end of stream"))
                        ((zero? n) (stream-car strm))
                        (else (loop (stream-cdr strm) (- n 1))))))))

  (define (stream-reverse strm)
    (define stream-reverse
      (stream-lambda (strm rev)
        (if (stream-null? strm)
            rev
            (stream-reverse (stream-cdr strm) (stream-cons (stream-car strm) rev)))))
    (if (not (stream? strm))
        (error 'stream-reverse "non-stream argument")
        (stream-reverse strm stream-null)))

  (define (stream-scan proc base strm)
    (define stream-scan
      (stream-lambda (base strm)
        (if (stream-null? strm)
            (stream base)
            (stream-cons base (stream-scan (proc base (stream-car strm)) (stream-cdr strm))))))
    (cond ((not (procedure? proc)) (error 'stream-scan "non-procedural argument"))
          ((not (stream? strm)) (error 'stream-scan "non-stream argument"))
          (else (stream-scan base strm))))

  (define (stream-take n strm)
    (define stream-take
      (stream-lambda (n strm)
        (if (or (stream-null? strm) (zero? n))
            stream-null
            (stream-cons (stream-car strm) (stream-take (- n 1) (stream-cdr strm))))))
    (cond ((not (stream? strm)) (error 'stream-take "non-stream argument"))
          ((not (integer? n)) (error 'stream-take "non-integer argument"))
          ((negative? n) (error 'stream-take "negative argument"))
          (else (stream-take n strm))))

  (define (stream-take-while pred? strm)
    (define stream-take-while
      (stream-lambda (strm)
        (cond ((stream-null? strm) stream-null)
              ((pred? (stream-car strm))
                (stream-cons (stream-car strm) (stream-take-while (stream-cdr strm))))
              (else stream-null))))
    (cond ((not (stream? strm)) (error 'stream-take-while "non-stream argument"))
          ((not (procedure? pred?)) (error 'stream-take-while "non-procedural argument"))
          (else (stream-take-while strm))))

  (define (stream-unfold mapper pred? generator base)
    (define stream-unfold
      (stream-lambda (base)
        (if (pred? base)
            (stream-cons (mapper base) (stream-unfold (generator base)))
            stream-null)))
    (cond ((not (procedure? mapper)) (error 'stream-unfold "non-procedural mapper"))
          ((not (procedure? pred?)) (error 'stream-unfold "non-procedural pred?"))
          ((not (procedure? generator)) (error 'stream-unfold "non-procedural generator"))
          (else (stream-unfold base))))

  (define (stream-unfolds gen seed)
    (define (len-values gen seed)
      (call-with-values
        (lambda () (gen seed))
        (lambda vs (- (length vs) 1))))
    (define unfold-result-stream
      (stream-lambda (gen seed)
        (call-with-values
          (lambda () (gen seed))
          (lambda (next . results)
            (stream-cons results (unfold-result-stream gen next))))))
    (define result-stream->output-stream
      (stream-lambda (result-stream i)
        (let ((result (list-ref (stream-car result-stream) (- i 1))))
          (cond ((pair? result)
                  (stream-cons
                    (car result)
                    (result-stream->output-stream (stream-cdr result-stream) i)))
                ((not result)
                  (result-stream->output-stream (stream-cdr result-stream) i))
                ((null? result) stream-null)
                (else (error 'stream-unfolds "can't happen"))))))
    (define (result-stream->output-streams result-stream)
      (let loop ((i (len-values gen seed)) (outputs '()))
        (if (zero? i)
            (apply values outputs)
            (loop (- i 1) (cons (result-stream->output-stream result-stream i) outputs)))))
    (if (not (procedure? gen))
        (error 'stream-unfolds "non-procedural argument")
        (result-stream->output-streams (unfold-result-stream gen seed))))

  (define (stream-zip . strms)
    (define stream-zip
      (stream-lambda (strms)
        (if (exists stream-null? strms)
            stream-null
            (stream-cons (map stream-car strms) (stream-zip (map stream-cdr strms))))))
    (cond ((null? strms) (error 'stream-zip "no stream arguments"))
          ((exists (lambda (x) (not (stream? x))) strms)
            (error 'stream-zip "non-stream argument"))
          (else (stream-zip strms)))))

Added srfi/s41/streams/primitive.sls.























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!r6rs 
;;; Copyright (C) Philip L. Bewig (2007). All Rights Reserved.
;;; Permission is hereby granted, free of charge, to any person
;;; obtaining a copy of this software and associated documentation files
;;; (the "Software"), to deal in the Software without restriction,
;;; including without limitation the rights to use, copy, modify, merge,
;;; publish, distribute, sublicense, and/or sell copies of the Software,
;;; and to permit persons to whom the Software is furnished to do so,
;;; subject to the following conditions:
;;; 
;;; The above copyright notice and this permission notice shall be
;;; included in all copies or substantial portions of the Software.
;;; 
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
;;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
;;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
;;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;;; SOFTWARE.

(library (srfi s41 streams primitive)

  (export stream-null stream-cons stream? stream-null? stream-pair?
          stream-car stream-cdr stream-lambda)

  (import (rnrs) (rnrs mutable-pairs))

  (define-record-type (stream-type make-stream stream?)
    (fields (mutable box stream-promise stream-promise!)))

  (define-syntax stream-lazy
    (syntax-rules ()
      ((lazy expr)
        (make-stream
          (cons 'lazy (lambda () expr))))))

  (define (stream-eager expr)
    (make-stream
      (cons 'eager expr)))

  (define-syntax stream-delay
    (syntax-rules ()
      ((stream-delay expr)
        (stream-lazy (stream-eager expr)))))

  (define (stream-force promise)
    (let ((content (stream-promise promise)))
      (case (car content)
        ((eager) (cdr content))
        ((lazy)  (let* ((promise* ((cdr content)))
                        (content  (stream-promise promise)))
                   (if (not (eqv? (car content) 'eager))
                       (begin (set-car! content (car (stream-promise promise*)))
                              (set-cdr! content (cdr (stream-promise promise*)))
                              (stream-promise! promise* content)))
                   (stream-force promise))))))

  (define stream-null (stream-delay (cons 'stream 'null)))

  (define-record-type (stream-pare-type make-stream-pare stream-pare?)
    (fields (immutable kar stream-kar) (immutable kdr stream-kdr)))

  (define (stream-pair? obj)
    (and (stream? obj) (stream-pare? (stream-force obj))))

  (define (stream-null? obj)
    (and (stream? obj)
         (eqv? (stream-force obj)
               (stream-force stream-null))))

  (define-syntax stream-cons
    (syntax-rules ()
      ((stream-cons obj strm)
        (stream-delay (make-stream-pare (stream-delay obj) (stream-lazy strm))))))

  (define (stream-car strm)
    (cond ((not (stream? strm)) (error 'stream-car "non-stream"))
          ((stream-null? strm) (error 'stream-car "null stream"))
          (else (stream-force (stream-kar (stream-force strm))))))

  (define (stream-cdr strm)
    (cond ((not (stream? strm)) (error 'stream-cdr "non-stream"))
          ((stream-null? strm) (error 'stream-cdr "null stream"))
          (else (stream-kdr (stream-force strm)))))

  (define-syntax stream-lambda
    (syntax-rules ()
      ((stream-lambda formals body0 body1 ...)
        (lambda formals (stream-lazy (let () body0 body1 ...)))))))

Added srfi/s41/test.sps.

















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
; #!/usr/bin/env scheme-script

; Copyright (C) 2007 by Philip L. Bewig of Saint Louis, Missouri, USA.
; All rights reserved.  Permission is hereby granted, free of charge,
; to any person obtaining a copy of
; this software and associated documentation files (the "Software"),
; to deal in the Software without restriction, including without
; limitation the rights to use, copy, modify, merge, publish,
; distribute, sublicense, and/or sell copies of the Software, and to
; permit persons to whom the Software is furnished to do so, subject
; to the following conditions: The above copyright notice and this
; permission notice shall be included in all copies or substantial
; portions of the Software.  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT
; WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
; TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
; PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
; COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
; OR OTHER DEALINGS IN THE SOFTWARE.

(import (surfage s41 streams)
        (except (rnrs) assert)
        (rnrs r5rs))

(define (add1 n) (+ n 1))

(define (lsec proc . args)
  (lambda x (apply proc (append args x))))

(define (rsec proc . args)
  (lambda x (apply proc (reverse (append (reverse args) (reverse x))))))

(define-stream (qsort lt? strm)
  (if (stream-null? strm)
      stream-null
      (let ((x (stream-car strm))
            (xs (stream-cdr strm)))
        (stream-append
          (qsort lt?
            (stream-filter
              (lambda (u) (lt? u x))
              xs))
          (stream x)
          (qsort lt?
            (stream-filter
              (lambda (u) (not (lt? u x)))
              xs))))))

(define-stream (isort lt? strm)
    (define-stream (insert strm x)
      (stream-match strm
        (() (stream x))
        ((y . ys)
          (if (lt? y x)
              (stream-cons y (insert ys x))
              (stream-cons x strm)))))
    (stream-fold insert stream-null strm))

(define-stream (stream-merge lt? . strms)
  (define-stream (merge xx yy)
    (stream-match xx (() yy) ((x . xs)
      (stream-match yy (() xx) ((y . ys)
        (if (lt? y x)
            (stream-cons y (merge xx ys))
            (stream-cons x (merge xs yy))))))))
  (stream-let loop ((strms strms))
    (cond ((null? strms) stream-null)
          ((null? (cdr strms)) (car strms))
          (else (merge (car strms)
                       (apply stream-merge lt?
                         (cdr strms)))))))

(define-stream (msort lt? strm)
  (let* ((n (quotient (stream-length strm) 2))
         (ts (stream-take n strm))
         (ds (stream-drop n strm)))
    (if (zero? n)
        strm
        (stream-merge lt?
          (msort < ts) (msort < ds)))))

(define-stream (stream-unique eql? strm)
  (if (stream-null? strm)
      stream-null
      (stream-cons (stream-car strm)
        (stream-unique eql?
          (stream-drop-while
            (lambda (x)
              (eql? (stream-car strm) x))
            strm)))))

(define nats
  (stream-cons 0
    (stream-map add1 nats)))

(define hamming
  (stream-unique =
    (stream-cons 1
      (stream-merge <
        (stream-map (lsec * 2) hamming)
        (stream-merge <
          (stream-map (lsec * 3) hamming)
          (stream-map (lsec * 5) hamming))))))

(define primes (let ()
  (define-stream (next base mult strm)
    (let ((first (stream-car strm))
          (rest (stream-cdr strm)))
      (cond ((< first mult)
              (stream-cons first
                (next base mult rest)))
            ((< mult first)
              (next base (+ base mult) strm))
            (else (next base
                    (+ base mult) rest)))))
  (define-stream (sift base strm)
    (next base (+ base base) strm))
  (define-stream (sieve strm)
    (let ((first (stream-car strm))
          (rest (stream-cdr strm)))
      (stream-cons first
        (sieve (sift first rest)))))
  (sieve (stream-from 2))))

(define (handle-assertion thunk expr result)
  (cond
    [(string? result) 
     ;;; error case
     (call/cc
       (lambda (k) 
         ;;; ignoring result string
         (with-exception-handler k thunk)
         (error 'test "did not fail" expr)))]
    [else
     (unless (equal? result (thunk)) 
       (error 'test "failed" expr))]))

(define-syntax assert
  (syntax-rules ()
    [(_ expr result) 
     (handle-assertion (lambda () expr) 'expr result)]))

(define (unit-test)
  
  (define strm123 (stream 1 2 3))

  ; stream-null
  (assert (stream? stream-null) #t)
  (assert (stream-null? stream-null) #t)
  (assert (stream-pair? stream-null) #f)
  
  ; stream-cons
  (assert (stream? (stream-cons 1 stream-null)) #t)
  (assert (stream-null? (stream-cons 1 stream-null)) #f)
  (assert (stream-pair? (stream-cons 1 stream-null)) #t)
  
  ; stream?
  (assert (stream? stream-null) #t)
  (assert (stream? (stream-cons 1 stream-null)) #t)
  (assert (stream? "four") #f)
  
  ; stream-null?
  (assert (stream-null? stream-null) #t)
  (assert (stream-null? (stream-cons 1 stream-null)) #f)
  (assert (stream-null? "four") #f)
  
  ; stream-pair?
  (assert (stream-pair? stream-null) #f)
  (assert (stream-pair? (stream-cons 1 stream-null)) #t)
  (assert (stream-pair? "four") #f)
  
  ; stream-car
  (assert (stream-car "four") "stream-car: non-stream")
  (assert (stream-car stream-null) "stream-car: null stream")
  (assert (stream-car strm123) 1)
  
  ; stream-cdr
  (assert (stream-cdr "four") "stream-cdr: non-stream")
  (assert (stream-cdr stream-null) "stream-cdr: null stream")
  (assert (stream-car (stream-cdr strm123)) 2)
  
  ; stream-lambda
  (assert
    (stream->list
      (letrec ((double
        (stream-lambda (strm)
          (if (stream-null? strm)
              stream-null
              (stream-cons
                (* 2 (stream-car strm))
                (double (stream-cdr strm)))))))
        (double strm123)))
    '(2 4 6))
  
  ; define-stream
  (assert
    (stream->list
      (let ()
        (define-stream (double strm)
          (if (stream-null? strm)
              stream-null
              (stream-cons
                (* 2 (stream-car strm))
                (double (stream-cdr strm)))))
        (double strm123)))
    '(2 4 6))
  
  ; list->stream
  (assert (list->stream "four") "list->stream: non-list argument")
  (assert (stream->list (list->stream '())) '())
  (assert (stream->list (list->stream '(1 2 3))) '(1 2 3))
  
  ; port->stream
  ;; (let* ((p (open-input-file "streams.ss"))
  ;;        (s (port->stream p)))
  ;;   (assert (port->stream "four") "port->stream: non-input-port argument")
  ;;   (assert (string=? (list->string (stream->list 11 s)) "; Copyright") #t)
  ;;   (close-input-port p))

  ; stream
  (assert (stream->list (stream)) '())
  (assert (stream->list (stream 1)) '(1))
  (assert (stream->list (stream 1 2 3)) '(1 2 3))
  
  ; stream->list
  (assert (stream->list '()) "stream->list: non-stream argument")
  (assert (stream->list "four" strm123) "stream->list: non-integer count")
  (assert (stream->list -1 strm123) "stream->list: negative count")
  (assert (stream->list (stream)) '())
  (assert (stream->list strm123) '(1 2 3))
  (assert (stream->list 5 strm123) '(1 2 3))
  (assert (stream->list 3 (stream-from 1)) '(1 2 3))
  
  ; stream-append
  (assert (stream-append "four") "stream-append: non-stream argument")
  (assert (stream->list (stream-append strm123)) '(1 2 3))
  (assert (stream->list (stream-append strm123 strm123)) '(1 2 3 1 2 3))
  (assert (stream->list (stream-append strm123 strm123 strm123)) '(1 2 3 1 2 3 1 2 3))
  (assert (stream->list (stream-append strm123 stream-null)) '(1 2 3))
  (assert (stream->list (stream-append stream-null strm123)) '(1 2 3))
  
  ; stream-concat
  (assert (stream-concat "four") "stream-concat: non-stream argument")
  (assert (stream->list (stream-concat (stream strm123))) '(1 2 3))
  (assert (stream->list (stream-concat (stream strm123 strm123))) '(1 2 3 1 2 3))
  
  ; stream-constant
  (assert (stream-ref (stream-constant 1) 100) 1)
  (assert (stream-ref (stream-constant 1 2) 100) 1)
  (assert (stream-ref (stream-constant 1 2 3) 3) 1)
  
  ; stream-drop
  (assert (stream-drop "four" strm123) "stream-drop: non-integer argument")
  (assert (stream-drop -1 strm123) "stream-drop: negative argument")
  (assert (stream-drop 2 "four") "stream-drop: non-stream argument")
  (assert (stream->list (stream-drop 0 stream-null)) '())
  (assert (stream->list (stream-drop 0 strm123)) '(1 2 3))
  (assert (stream->list (stream-drop 1 strm123)) '(2 3))
  (assert (stream->list (stream-drop 5 strm123)) '())
  
  ; stream-drop-while
  (assert (stream-drop-while "four" strm123) "stream-drop-while: non-procedural argument")
  (assert (stream-drop-while odd? "four") "stream-drop-while: non-stream argument")
  (assert (stream->list (stream-drop-while odd? stream-null)) '())
  (assert (stream->list (stream-drop-while odd? strm123)) '(2 3))
  (assert (stream->list (stream-drop-while even? strm123)) '(1 2 3))
  (assert (stream->list (stream-drop-while positive? strm123)) '())
  (assert (stream->list (stream-drop-while negative? strm123)) '(1 2 3))
  
  ; stream-filter
  (assert (stream-filter "four" strm123) "stream-filter: non-procedural argument")
  (assert (stream-filter odd? '()) "stream-filter: non-stream argument")
  (assert (stream-null? (stream-filter odd? (stream))) #t)
  (assert (stream->list (stream-filter odd? strm123)) '(1 3))
  (assert (stream->list (stream-filter even? strm123)) '(2))
  (assert (stream->list (stream-filter positive? strm123)) '(1 2 3))
  (assert (stream->list (stream-filter negative? strm123)) '())
  (let loop ((n 10))
    (assert (odd? (stream-ref (stream-filter odd? (stream-from 0)) n)) #t)
    (if (positive? n) (loop (- n 1))))
  (let loop ((n 10))
    (assert (even? (stream-ref (stream-filter odd? (stream-from 0)) n)) #f)
    (if (positive? n) (loop (- n 1))))
  
  ; stream-fold
  (assert (stream-fold "four" 0 strm123) "stream-fold: non-procedural argument")
  (assert (stream-fold + 0 '()) "stream-fold: non-stream argument")
  (assert (stream-fold + 0 strm123) 6)
  
  ; stream-for-each
  (assert (stream-for-each "four" strm123) "stream-for-each: non-procedural argument")
  (assert (stream-for-each display) "stream-for-each: no stream arguments")
  (assert (stream-for-each display "four") "stream-for-each: non-stream argument")
  (assert (let ((sum 0)) (stream-for-each (lambda (x) (set! sum (+ sum x))) strm123) sum) 6)

  ; stream-from
  (assert (stream-from "four") "stream-from: non-numeric starting number")
  (assert (stream-from 1 "four") "stream-from: non-numeric step size")
  (assert (stream-ref (stream-from 0) 100) 100)
  (assert (stream-ref (stream-from 1 2) 100) 201)
  (assert (stream-ref (stream-from 0 -1) 100) -100)
  
  ; stream-iterate
  (assert (stream-iterate "four" 0) "stream-iterate: non-procedural argument")
  
  (assert (stream->list 3 (stream-iterate (lsec + 1) 1)) '(1 2 3))
  
  ; stream-length
  (assert (stream-length "four") "stream-length: non-stream argument")
  (assert (stream-length (stream)) 0)
  (assert (stream-length strm123) 3)
  
  ; stream-let
  (assert (stream->list
            (stream-let loop ((strm strm123))
              (if (stream-null? strm)
                  stream-null
                  (stream-cons
                    (* 2 (stream-car strm))
                    (loop (stream-cdr strm))))))
          '(2 4 6))
  
  ; stream-map
  (assert (stream-map "four" strm123) "stream-map: non-procedural argument")
  (assert (stream-map odd?) "stream-map: no stream arguments")
  (assert (stream-map odd? "four") "stream-map: non-stream argument")
  (assert (stream->list (stream-map - strm123)) '(-1 -2 -3))
  (assert (stream->list (stream-map + strm123 strm123)) '(2 4 6))
  (assert (stream->list (stream-map + strm123 (stream-from 1))) '(2 4 6))
  (assert (stream->list (stream-map + (stream-from 1) strm123)) '(2 4 6))
  (assert (stream->list (stream-map + strm123 strm123 strm123)) '(3 6 9))
  
  ; stream-match
  (assert (stream-match '(1 2 3) (_ 'ok)) "stream-match: non-stream argument")
  (assert (stream-match strm123 (() 42)) "stream-match: pattern failure")
  (assert (stream-match stream-null (() 'ok)) 'ok)
  (assert (stream-match strm123 (() 'no) (else 'ok)) 'ok)
  (assert (stream-match (stream 1) (() 'no) ((a) a)) 1)
  (assert (stream-match (stream 1) (() 'no) ((_) 'ok)) 'ok)
  (assert (stream-match strm123 ((a b c) (list a b c))) '(1 2 3))
  (assert (stream-match strm123 ((a . _) a)) 1)
  (assert (stream-match strm123 ((a b . _) (list a b))) '(1 2))
  (assert (stream-match strm123 ((a b . c) (list a b (stream-car c)))) '(1 2 3))
  (assert (stream-match strm123 (s (stream->list s))) '(1 2 3))
  (assert (stream-match strm123 ((a . _) (= a 1) 'ok)) 'ok)
  (assert (stream-match strm123 ((a . _) (= a 2) 'yes) (_ 'no)) 'no)
  (assert (stream-match strm123 ((a b c) (= a b) 'yes) (_ 'no)) 'no)
  (assert (stream-match (stream 1 1 2) ((a b c) (= a b) 'yes) (_ 'no)) 'yes)
  
  ; stream-of
  (assert (stream->list
            (stream-of (+ y 6)
              (x in (stream-range 1 6))
              (odd? x)
              (y is (* x x)))) '(7 15 31))
  (assert (stream->list
            (stream-of (* x y)
              (x in (stream-range 1 4))
              (y in (stream-range 1 5))))
          '(1 2 3 4 2 4 6 8 3 6 9 12))
  (assert (stream-car (stream-of 1)) 1)

  ; stream-range
  (assert (stream-range "four" 0) "stream-range: non-numeric starting number")
  (assert (stream-range 0 "four") "stream-range: non-numeric ending number")
  (assert (stream-range 1 2 "three") "stream-range: non-numeric step size")
  (assert (stream->list (stream-range 0 5)) '(0 1 2 3 4))
  (assert (stream->list (stream-range 5 0)) '(5 4 3 2 1))
  (assert (stream->list (stream-range 0 5 2)) '(0 2 4))
  (assert (stream->list (stream-range 5 0 -2)) '(5 3 1))
  (assert (stream->list (stream-range 0 1 -1)) '())
  
  ; stream-ref
  (assert (stream-ref '() 4) "stream-ref: non-stream argument")
  (assert (stream-ref nats 3.5) "stream-ref: non-integer argument")
  (assert (stream-ref nats -3) "stream-ref: negative argument")
  (assert (stream-ref strm123 5) "stream-ref: beyond end of stream")
  (assert (stream-ref strm123 0) 1)
  (assert (stream-ref strm123 1) 2)
  (assert (stream-ref strm123 2) 3)
  
  ; stream-reverse
  (assert (stream-reverse '()) "stream-reverse: non-stream argument")
  (assert (stream->list (stream-reverse (stream))) '())
  (assert (stream->list (stream-reverse strm123)) '(3 2 1))
  
  ; stream-scan
  (assert (stream-scan "four" 0 strm123) "stream-scan: non-procedural argument")
  (assert (stream-scan + 0 '()) "stream-scan: non-stream argument")
  (assert (stream->list (stream-scan + 0 strm123)) '(0 1 3 6))
  
  ; stream-take
  (assert (stream-take 5 "four") "stream-take: non-stream argument")
  (assert (stream-take "four" strm123) "stream-take: non-integer argument")
  (assert (stream-take -4 strm123) "stream-take: negative argument")
  (assert (stream->list (stream-take 5 stream-null)) '())
  (assert (stream->list (stream-take 0 stream-null)) '())
  (assert (stream->list (stream-take 0 strm123)) '())
  (assert (stream->list (stream-take 2 strm123)) '(1 2))
  (assert (stream->list (stream-take 3 strm123)) '(1 2 3))
  (assert (stream->list (stream-take 5 strm123)) '(1 2 3))
  
  ; stream-take-while
  (assert (stream-take-while odd? "four") "stream-take-while: non-stream argument")
  (assert (stream-take-while "four" strm123) "stream-take-while: non-procedural argument")
  (assert (stream->list (stream-take-while odd? strm123)) '(1))
  (assert (stream->list (stream-take-while even? strm123)) '())
  (assert (stream->list (stream-take-while positive? strm123)) '(1 2 3))
  (assert (stream->list (stream-take-while negative? strm123)) '())
  
  ; stream-unfold
  (assert (stream-unfold "four" odd? + 0) "stream-unfold: non-procedural mapper")
  (assert (stream-unfold + "four" + 0) "stream-unfold: non-procedural pred?")
  (assert (stream-unfold + odd? "four" 0) "stream-unfold: non-procedural generator")

  (assert (stream->list (stream-unfold (rsec expt 2) (rsec < 10) (rsec + 1) 0))
           '(0 1 4 9 16 25 36 49 64 81))
  
  ; stream-unfolds
  (assert
    (stream->list
      (stream-unfolds
        (lambda (x)
          (let ((n (car x)) (s (cdr x)))
            (if (zero? n)
                (values 'dummy '())
                (values
                  (cons (- n 1) (stream-cdr s))
                  (list (stream-car s))))))
        (cons 5 (stream-from 0))))
      '(0 1 2 3 4))
  
  ; stream-zip
  (assert (stream-zip) "stream-zip: no stream arguments")
  (assert (stream-zip "four") "stream-zip: non-stream argument")
  (assert (stream-zip strm123 "four") "stream-zip: non-stream argument")
  (assert (stream->list (stream-zip strm123 stream-null)) '())
  (assert (stream->list (stream-zip strm123)) '((1) (2) (3)))
  (assert (stream->list (stream-zip strm123 strm123)) '((1 1) (2 2) (3 3)))
  (assert (stream->list (stream-zip strm123 (stream-from 1))) '((1 1) (2 2) (3 3)))
  (assert (stream->list (stream-zip strm123 strm123 strm123)) '((1 1 1) (2 2 2) (3 3 3)))
  
  ; other tests

  (assert
    (stream-car
      (stream-reverse
        (stream-take-while
          (rsec < 1000)
          primes)))
    997)
  
  (assert
    (equal?
      (stream->list (qsort < (stream 3 1 5 2 4)))
      (stream->list (isort < (stream 2 5 1 4 3))))
    #t)
   
  (assert
    (equal?
      (stream->list (msort < (stream 3 1 5 2 4)))
      (stream->list (isort < (stream 2 5 1 4 3))))
    #t)
   
  ; http://www.research.att.com/~njas/sequences/A051037
  (assert (stream-ref hamming 999) 51200000)
  
)


(unit-test)

Added srfi/s42/_eager-comprehensions-a.sls.



























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s42 eager-comprehensions)
  (export
    do-ec list-ec append-ec string-ec string-append-ec vector-ec 
    vector-of-length-ec sum-ec product-ec min-ec max-ec any?-ec 
    every?-ec first-ec last-ec fold-ec fold3-ec 
    : :list :string :vector :integers :range :real-range :char-range 
    :port :dispatched :do :let :parallel :while :until
    :-dispatch-ref :-dispatch-set! make-initial-:-dispatch 
    dispatch-union :generator-proc)
  (import
    (rnrs)
    (rnrs r5rs)
    (srfi s39 parameters)
    (srfi s23 error tricks)
    (srfi private include))
  
  ;; (SRFI-23-error->R6RS "(library (srfi s42 eager-comprehensions))"
  ;;  (include/resolve ("srfi" "%3a42") "ec.scm"))

; <PLAINTEXT>
; Eager Comprehensions in [outer..inner|expr]-Convention
; ======================================================
;
; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005
; Scheme R5RS (incl. macros), SRFI-23 (error).
;
; Modified by Derick Eddington to be able to be included into an R6RS library.
; 
; Loading the implementation into Scheme48 0.57:
;   ,open srfi-23
;   ,load ec.scm
;
; Loading the implementation into PLT/DrScheme 202:
;   ; File > Open ... "ec.scm", click Execute
;
; Loading the implementation into SCM 5d7:
;   (require 'macro) (require 'record) 
;   (load "ec.scm")
;
; Implementation comments:
;   * All local (not exported) identifiers are named ec-<something>.
;   * This implementation focuses on portability, performance, 
;     readability, and simplicity roughly in this order. Design
;     decisions related to performance are taken for Scheme48.
;   * Alternative implementations, Comments and Warnings are 
;     mentioned after the definition with a heading.


; ==========================================================================
; The fundamental comprehension do-ec
; ==========================================================================
;
; All eager comprehensions are reduced into do-ec and
; all generators are reduced to :do. 
;
; We use the following short names for syntactic variables
;   q    - qualifier
;   cc   - current continuation, thing to call at the end;
;          the CPS is (m (cc ...) arg ...) -> (cc ... expr ...)
;   cmd  - an expression being evaluated for its side-effects
;   expr - an expression
;   gen  - a generator of an eager comprehension
;   ob   - outer binding
;   oc   - outer command
;   lb   - loop binding
;   ne1? - not-end1? (before the payload)
;   ib   - inner binding
;   ic   - inner command
;   ne2? - not-end2? (after the payload)
;   ls   - loop step
;   etc  - more arguments of mixed type


; (do-ec q ... cmd)
;   handles nested, if/not/and/or, begin, :let, and calls generator 
;   macros in CPS to transform them into fully decorated :do.
;   The code generation for a :do is delegated to do-ec:do.

(define-syntax do-ec
  (syntax-rules (nested if not and or begin :do let)

    ; explicit nesting -> implicit nesting
    ((do-ec (nested q ...) etc ...)
     (do-ec q ... etc ...) )

    ; implicit nesting -> fold do-ec
    ((do-ec q1 q2 etc1 etc ...)
     (do-ec q1 (do-ec q2 etc1 etc ...)) )

    ; no qualifiers at all -> evaluate cmd once
    ((do-ec cmd)
     (begin cmd (if #f #f)) )

; now (do-ec q cmd) remains

    ; filter -> make conditional
    ((do-ec (if test) cmd)
     (if test (do-ec cmd)) )
    ((do-ec (not test) cmd)
     (if (not test) (do-ec cmd)) )
    ((do-ec (and test ...) cmd)
     (if (and test ...) (do-ec cmd)) )
    ((do-ec (or test ...) cmd)
     (if (or test ...) (do-ec cmd)) )

    ; begin -> make a sequence
    ((do-ec (begin etc ...) cmd)
     (begin etc ... (do-ec cmd)) )

    ; fully decorated :do-generator -> delegate to do-ec:do
    ((do-ec (:do olet lbs ne1? ilet ne2? lss) cmd)
     (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) )

; anything else -> call generator-macro in CPS; reentry at (*)

    ((do-ec (g arg1 arg ...) cmd)
     (g (do-ec:do cmd) arg1 arg ...) )))


; (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss))
;   generates code for a single fully decorated :do-generator
;   with cmd as payload, taking care of special cases.

(define-syntax do-ec:do
  (syntax-rules (:do let)

    ; reentry point (*) -> generate code
    ((do-ec:do cmd 
               (:do (let obs oc ...) 
                    lbs 
                    ne1? 
                    (let ibs ic ...) 
                    ne2? 
                    (ls ...) ))
     (ec-simplify
       (let obs
         oc ...
         (let loop lbs
           (ec-simplify
             (if ne1?
                 (ec-simplify
                   (let ibs
                      ic ...
                      cmd
                      (ec-simplify
                        (if ne2?
                            (loop ls ...) )))))))))) ))

    
; (ec-simplify <expression>)
;   generates potentially more efficient code for <expression>.
;   The macro handles if, (begin <command>*), and (let () <command>*)
;   and takes care of special cases.

(define-syntax ec-simplify
  (syntax-rules (if not let begin)

; one- and two-sided if

    ; literal <test>
    ((ec-simplify (if #t consequent))
     consequent )
    ((ec-simplify (if #f consequent))
     (if #f #f) )
    ((ec-simplify (if #t consequent alternate))
     consequent )
    ((ec-simplify (if #f consequent alternate))
     alternate )

    ; (not (not <test>))
    ((ec-simplify (if (not (not test)) consequent))
     (ec-simplify (if test consequent)) )
    ((ec-simplify (if (not (not test)) consequent alternate))
     (ec-simplify (if test consequent alternate)) )

; (let () <command>*) 

    ; empty <binding spec>*
    ((ec-simplify (let () command ...))
     (ec-simplify (begin command ...)) )

; begin 

    ; flatten use helper (ec-simplify 1 done to-do)
    ((ec-simplify (begin command ...))
     (ec-simplify 1 () (command ...)) )
    ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...))
     (ec-simplify 1 done (to-do1 ... to-do2 ...)) )
    ((ec-simplify 1 (done ...) (to-do1 to-do ...))
     (ec-simplify 1 (done ... to-do1) (to-do ...)) )

    ; exit helper
    ((ec-simplify 1 () ())
     (if #f #f) )
    ((ec-simplify 1 (command) ())
     command )
    ((ec-simplify 1 (command1 command ...) ())
     (begin command1 command ...) )

; anything else

    ((ec-simplify expression)
     expression )))


; ==========================================================================
; The special generators :do, :let, :parallel, :while, and :until
; ==========================================================================

(define-syntax :do
  (syntax-rules ()

    ; full decorated -> continue with cc, reentry at (*)
    ((:do (cc ...) olet lbs ne1? ilet ne2? lss)
     (cc ... (:do olet lbs ne1? ilet ne2? lss)) )

    ; short form -> fill in default values
    ((:do cc lbs ne1? lss)
     (:do cc (let ()) lbs ne1? (let ()) #t lss) )))
    

(define-syntax :let
  (syntax-rules (index)
    ((:let cc var (index i) expression)
     (:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) )
    ((:let cc var expression)
     (:do cc (let ((var expression))) () #t (let ()) #f ()) )))


(define-syntax :parallel
  (syntax-rules (:do)
    ((:parallel cc)
     cc )
    ((:parallel cc (g arg1 arg ...) gen ...)
     (g (:parallel-1 cc (gen ...)) arg1 arg ...) )))

; (:parallel-1 cc (to-do ...) result [ next ] )
;    iterates over to-do by converting the first generator into 
;    the :do-generator next and merging next into result.

(define-syntax :parallel-1  ; used as 
  (syntax-rules (:do let)

    ; process next element of to-do, reentry at (**)
    ((:parallel-1 cc ((g arg1 arg ...) gen ...) result)
     (g (:parallel-1 cc (gen ...) result) arg1 arg ...) )

    ; reentry point (**) -> merge next into result
    ((:parallel-1 
       cc 
       gens 
       (:do (let (ob1 ...) oc1 ...) 
            (lb1 ...) 
            ne1?1 
            (let (ib1 ...) ic1 ...) 
            ne2?1 
            (ls1 ...) )
       (:do (let (ob2 ...) oc2 ...) 
            (lb2 ...) 
            ne1?2 
            (let (ib2 ...) ic2 ...) 
            ne2?2 
            (ls2 ...) ))
     (:parallel-1 
       cc 
       gens 
       (:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...) 
            (lb1 ... lb2 ...) 
            (and ne1?1 ne1?2) 
            (let (ib1 ... ib2 ...) ic1 ... ic2 ...) 
            (and ne2?1 ne2?2) 
            (ls1 ... ls2 ...) )))

    ; no more gens -> continue with cc, reentry at (*)
    ((:parallel-1 (cc ...) () result)
     (cc ... result) )))

(define-syntax :while
  (syntax-rules ()
    ((:while cc (g arg1 arg ...) test)
     (g (:while-1 cc test) arg1 arg ...) )))

; (:while-1 cc test (:do ...))
;    modifies the fully decorated :do-generator such that it
;    runs while test is a true value. 
;       The original implementation just replaced ne1? by
;    (and ne1? test) as follows:
;
;      (define-syntax :while-1
;        (syntax-rules (:do)
;          ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss))
;           (:do cc olet lbs (and ne1? test) ilet ne2? lss) )))
;
;    Unfortunately, this code is wrong because ne1? may depend
;    in the inner bindings introduced in ilet, but ne1? is evaluated
;    outside of the inner bindings. (Refer to the specification of
;    :do to see the structure.) 
;       The problem manifests itself (as sunnan@handgranat.org 
;    observed) when the :list-generator is modified:
; 
;      (do-ec (:while (:list x '(1 2)) (= x 1)) (display x)).
;
;    In order to generate proper code, we introduce temporary
;    variables saving the values of the inner bindings. The inner
;    bindings are executed in a new ne1?, which also evaluates ne1?
;    outside the scope of the inner bindings, then the inner commands
;    are executed (possibly changing the variables), and then the
;    values of the inner bindings are saved and (and ne1? test) is
;    returned. In the new ilet, the inner variables are bound and
;    initialized and their values are restored. So we construct:
;
;     (let (ob .. (ib-tmp #f) ...)
;       oc ...
;       (let loop (lb ...)
;         (if (let (ne1?-value ne1?)
;               (let ((ib-var ib-rhs) ...)
;                 ic ...
;                 (set! ib-tmp ib-var) ...)
;               (and ne1?-value test))
;             (let ((ib-var ib-tmp) ...)
;               /payload/
;               (if ne2?
;                   (loop ls ...) )))))

(define-syntax :while-1
  (syntax-rules (:do let)
    ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss))
     (:while-2 cc test () () () (:do olet lbs ne1? ilet ne2? lss)))))

(define-syntax :while-2
  (syntax-rules (:do let)
    ((:while-2 cc 
               test 
               (ib-let     ...)
               (ib-save    ...)
               (ib-restore ...)
               (:do olet 
                    lbs 
                    ne1? 
                    (let ((ib-var ib-rhs) ib ...) ic ...)
                    ne2? 
                    lss))
     (:while-2 cc 
               test 
               (ib-let     ... (ib-tmp #f))
               (ib-save    ... (ib-var ib-rhs))
               (ib-restore ... (ib-var ib-tmp))
               (:do olet 
                    lbs 
                    ne1? 
                    (let (ib ...) ic ... (set! ib-tmp ib-var)) 
                    ne2? 
                    lss)))
    ((:while-2 cc
               test
               (ib-let     ...)
               (ib-save    ...)
               (ib-restore ...)
               (:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss))
     (:do cc
          (let (ob ... ib-let ...) oc ...)
          lbs
          (let ((ne1?-value ne1?))
            (let (ib-save ...)
                ic ...
                (and ne1?-value test)))
          (let (ib-restore ...))
          ne2?
          lss))))


(define-syntax :until
  (syntax-rules ()
    ((:until cc (g arg1 arg ...) test)
     (g (:until-1 cc test) arg1 arg ...) )))

(define-syntax :until-1
  (syntax-rules (:do)
    ((:until-1 cc test (:do olet lbs ne1? ilet ne2? lss))
     (:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) )))


; ==========================================================================
; The typed generators :list :string :vector etc.
; ==========================================================================

(define-syntax :list
  (syntax-rules (index)
    ((:list cc var (index i) arg ...)
     (:parallel cc (:list var arg ...) (:integers i)) )
    ((:list cc var arg1 arg2 arg ...)
     (:list cc var (append arg1 arg2 arg ...)) )
    ((:list cc var arg)
     (:do cc
          (let ())
          ((t arg))
          (not (null? t))
          (let ((var (car t))))
          #t
          ((cdr t)) ))))


(define-syntax :string
  (syntax-rules (index)
    ((:string cc var (index i) arg)
     (:do cc
          (let ((str arg) (len 0)) 
            (set! len (string-length str)))
          ((i 0))
          (< i len)
          (let ((var (string-ref str i))))
          #t
          ((+ i 1)) ))
    ((:string cc var (index i) arg1 arg2 arg ...)
     (:string cc var (index i) (string-append arg1 arg2 arg ...)) )
    ((:string cc var arg1 arg ...)
     (:string cc var (index i) arg1 arg ...) )))

; Alternative: An implementation in the style of :vector can also
;   be used for :string. However, it is less interesting as the
;   overhead of string-append is much less than for 'vector-append'.


(define-syntax :vector
  (syntax-rules (index)
    ((:vector cc var arg)
     (:vector cc var (index i) arg) )
    ((:vector cc var (index i) arg)
     (:do cc
          (let ((vec arg) (len 0)) 
            (set! len (vector-length vec)))
          ((i 0))
          (< i len)
          (let ((var (vector-ref vec i))))
          #t
          ((+ i 1)) ))

    ((:vector cc var (index i) arg1 arg2 arg ...)
     (:parallel cc (:vector cc var arg1 arg2 arg ...) (:integers i)) )
    ((:vector cc var arg1 arg2 arg ...)
     (:do cc
          (let ((vec #f)
                (len 0)
                (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) ))
          ((k 0))
          (if (< k len)
              #t
              (if (null? vecs)
                  #f
                  (begin (set! vec (car vecs))
                         (set! vecs (cdr vecs))
                         (set! len (vector-length vec))
                         (set! k 0)
                         #t )))
          (let ((var (vector-ref vec k))))
          #t
          ((+ k 1)) ))))

(define (ec-:vector-filter vecs)
  (if (null? vecs)
      '()
      (if (zero? (vector-length (car vecs)))
          (ec-:vector-filter (cdr vecs))
          (cons (car vecs) (ec-:vector-filter (cdr vecs))) )))

; Alternative: A simpler implementation for :vector uses vector->list
;   append and :list in the multi-argument case. Please refer to the
;   'design.scm' for more details.


(define-syntax :integers
  (syntax-rules (index)
    ((:integers cc var (index i))
     (:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) )
    ((:integers cc var)
     (:do cc ((var 0)) #t ((+ var 1))) )))


(define-syntax :range
  (syntax-rules (index)

    ; handle index variable and add optional args
    ((:range cc var (index i) arg1 arg ...)
     (:parallel cc (:range var arg1 arg ...) (:integers i)) )
    ((:range cc var arg1)
     (:range cc var 0 arg1 1) )
    ((:range cc var arg1 arg2)
     (:range cc var arg1 arg2 1) )

; special cases (partially evaluated by hand from general case)

    ((:range cc var 0 arg2 1)
     (:do cc
          (let ((b arg2))
            (if (not (and (integer? b) (exact? b)))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" 0 b 1 )))
          ((var 0))
          (< var b)
          (let ())
          #t
          ((+ var 1)) ))

    ((:range cc var 0 arg2 -1)
     (:do cc
          (let ((b arg2))
            (if (not (and (integer? b) (exact? b)))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" 0 b 1 )))
          ((var 0))
          (> var b)
          (let ())
          #t
          ((- var 1)) ))

    ((:range cc var arg1 arg2 1)
     (:do cc
          (let ((a arg1) (b arg2))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b 1 )) )
          ((var a))
          (< var b)
          (let ())
          #t
          ((+ var 1)) ))

    ((:range cc var arg1 arg2 -1)
     (:do cc
          (let ((a arg1) (b arg2) (s -1) (stop 0))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b -1 )) )
          ((var a))
          (> var b)
          (let ())
          #t
          ((- var 1)) ))

; the general case

    ((:range cc var arg1 arg2 arg3)
     (:do cc
          (let ((a arg1) (b arg2) (s arg3) (stop 0))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b)
                          (integer? s) (exact? s) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b s ))
            (if (zero? s)
                (error "step size must not be zero in :range") )
            (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) )
          ((var a))
          (not (= var stop))
          (let ())
          #t
          ((+ var s)) ))))

; Comment: The macro :range inserts some code to make sure the values
;   are exact integers. This overhead has proven very helpful for 
;   saving users from themselves.


(define-syntax :real-range
  (syntax-rules (index)

    ; add optional args and index variable
    ((:real-range cc var arg1)
     (:real-range cc var (index i) 0 arg1 1) )
    ((:real-range cc var (index i) arg1)
     (:real-range cc var (index i) 0 arg1 1) )
    ((:real-range cc var arg1 arg2)
     (:real-range cc var (index i) arg1 arg2 1) )
    ((:real-range cc var (index i) arg1 arg2)
     (:real-range cc var (index i) arg1 arg2 1) )
    ((:real-range cc var arg1 arg2 arg3)
     (:real-range cc var (index i) arg1 arg2 arg3) )

    ; the fully qualified case
    ((:real-range cc var (index i) arg1 arg2 arg3)
     (:do cc
          (let ((a arg1) (b arg2) (s arg3) (istop 0))
            (if (not (and (real? a) (real? b) (real? s)))
                (error "arguments of :real-range are not real" a b s) )
            (if (and (exact? a) (or (not (exact? b)) (not (exact? s))))
                (set! a (exact->inexact a)) )
            (set! istop (/ (- b a) s)) )
          ((i 0))
          (< i istop)
          (let ((var (+ a (* s i)))))
          #t
          ((+ i 1)) ))))

; Comment: The macro :real-range adapts the exactness of the start
;   value in case any of the other values is inexact. This is a
;   precaution to avoid (list-ec (: x 0 3.0) x) => '(0 1.0 2.0).

    
(define-syntax :char-range
  (syntax-rules (index)
    ((:char-range cc var (index i) arg1 arg2)
     (:parallel cc (:char-range var arg1 arg2) (:integers i)) )
    ((:char-range cc var arg1 arg2)
     (:do cc
          (let ((imax (char->integer arg2))))
          ((i (char->integer arg1)))
          (<= i imax)
          (let ((var (integer->char i))))
          #t
          ((+ i 1)) ))))

; Warning: There is no R5RS-way to implement the :char-range generator 
;   because the integers obtained by char->integer are not necessarily 
;   consecutive. We simply assume this anyhow for illustration.


(define-syntax :port
  (syntax-rules (index)
    ((:port cc var (index i) arg1 arg ...)
     (:parallel cc (:port var arg1 arg ...) (:integers i)) )
    ((:port cc var arg)
     (:port cc var arg read) )
    ((:port cc var arg1 arg2)
     (:do cc
          (let ((port arg1) (read-proc arg2)))
          ((var (read-proc port)))
          (not (eof-object? var))
          (let ())
          #t
          ((read-proc port)) ))))


; ==========================================================================
; The typed generator :dispatched and utilities for constructing dispatchers
; ==========================================================================

(define-syntax :dispatched
  (syntax-rules (index)
    ((:dispatched cc var (index i) dispatch arg1 arg ...)
     (:parallel cc 
                (:integers i)
                (:dispatched var dispatch arg1 arg ...) ))
    ((:dispatched cc var dispatch arg1 arg ...)
     (:do cc
          (let ((d dispatch) 
                (args (list arg1 arg ...)) 
                (g #f) 
                (empty (list #f)) )
            (set! g (d args))
            (if (not (procedure? g))
                (error "unrecognized arguments in dispatching" 
                       args 
                       (d '()) )))
          ((var (g empty)))
          (not (eq? var empty))
          (let ())
          #t
          ((g empty)) ))))

; Comment: The unique object empty is created as a newly allocated
;   non-empty list. It is compared using eq? which distinguishes
;   the object from any other object, according to R5RS 6.1.


(define-syntax :generator-proc
  (syntax-rules (:do let)

    ; call g with a variable, reentry at (**)
    ((:generator-proc (g arg ...))
     (g (:generator-proc var) var arg ...) )

    ; reentry point (**) -> make the code from a single :do
    ((:generator-proc
       var 
       (:do (let obs oc ...) 
            ((lv li) ...) 
            ne1? 
            (let ((i v) ...) ic ...) 
            ne2? 
            (ls ...)) )
     (ec-simplify 
      (let obs
          oc ...
          (let ((lv li) ... (ne2 #t))
            (ec-simplify
             (let ((i #f) ...) ; v not yet valid
               (lambda (empty)
                 (if (and ne1? ne2)
                     (ec-simplify
                      (begin 
                        (set! i v) ...
                        ic ...
                        (let ((value var))
                          (ec-simplify
                           (if ne2?
                               (ec-simplify 
                                (begin (set! lv ls) ...) )
                               (set! ne2 #f) ))
                          value )))
                     empty ))))))))

    ; silence warnings of some macro expanders
    ((:generator-proc var)
     (error "illegal macro call") )))


(define (dispatch-union d1 d2)
  (lambda (args)
    (let ((g1 (d1 args)) (g2 (d2 args)))
      (if g1
          (if g2 
              (if (null? args)
                  (append (if (list? g1) g1 (list g1)) 
                          (if (list? g2) g2 (list g2)) )
                  (error "dispatching conflict" args (d1 '()) (d2 '())) )
              g1 )
          (if g2 g2 #f) ))))


; ==========================================================================
; The dispatching generator :
; ==========================================================================

(define (make-initial-:-dispatch)
  (lambda (args)
    (case (length args)
      ((0) 'SRFI42)
      ((1) (let ((a1 (car args)))
             (cond
              ((list? a1)
               (:generator-proc (:list a1)) )
              ((string? a1)
               (:generator-proc (:string a1)) )
              ((vector? a1)
               (:generator-proc (:vector a1)) )
              ((and (integer? a1) (exact? a1))
               (:generator-proc (:range a1)) )
              ((real? a1)
               (:generator-proc (:real-range a1)) )
              ((input-port? a1)
               (:generator-proc (:port a1)) )
              (else
               #f ))))
      ((2) (let ((a1 (car args)) (a2 (cadr args)))
             (cond
              ((and (list? a1) (list? a2))
               (:generator-proc (:list a1 a2)) )
              ((and (string? a1) (string? a2))
               (:generator-proc (:string a1 a2)) )
              ((and (vector? a1) (vector? a2))
               (:generator-proc (:vector a1 a2)) )
              ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2))
               (:generator-proc (:range a1 a2)) )
              ((and (real? a1) (real? a2))
               (:generator-proc (:real-range a1 a2)) )
              ((and (char? a1) (char? a2))
               (:generator-proc (:char-range a1 a2)) )
              ((and (input-port? a1) (procedure? a2))
               (:generator-proc (:port a1 a2)) )
              (else
               #f ))))
      ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args)))
             (cond
              ((and (list? a1) (list? a2) (list? a3))
               (:generator-proc (:list a1 a2 a3)) )
              ((and (string? a1) (string? a2) (string? a3))
               (:generator-proc (:string a1 a2 a3)) )
              ((and (vector? a1) (vector? a2) (vector? a3))
               (:generator-proc (:vector a1 a2 a3)) )
              ((and (integer? a1) (exact? a1) 
                    (integer? a2) (exact? a2)
                    (integer? a3) (exact? a3))
               (:generator-proc (:range a1 a2 a3)) )
              ((and (real? a1) (real? a2) (real? a3))
               (:generator-proc (:real-range a1 a2 a3)) )
              (else
               #f ))))
      (else
       (letrec ((every? 
                 (lambda (pred args)
                   (if (null? args)
                       #t
                       (and (pred (car args))
                            (every? pred (cdr args)) )))))
         (cond
          ((every? list? args)
           (:generator-proc (:list (apply append args))) )
          ((every? string? args)
           (:generator-proc (:string (apply string-append args))) )
          ((every? vector? args)
           (:generator-proc (:list (apply append (map vector->list args)))) )
          (else
           #f )))))))

(define :-dispatch 
  (make-parameter (make-initial-:-dispatch)
                  (lambda (x) (if (procedure? x) x (error "not a procedure" x)))))

(define (:-dispatch-ref)
  (:-dispatch))

(define (:-dispatch-set! dispatch)
  (:-dispatch dispatch))

(define-syntax :
  (syntax-rules (index)
    ((: cc var (index i) arg1 arg ...)
     (:dispatched cc var (index i) (:-dispatch) arg1 arg ...) )
    ((: cc var arg1 arg ...)
     (:dispatched cc var (:-dispatch) arg1 arg ...) )))


; ==========================================================================
; The utility comprehensions fold-ec, fold3-ec
; ==========================================================================

(define-syntax fold3-ec
  (syntax-rules (nested)
    ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
     (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
    ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
     (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
    ((fold3-ec x0 expression f1 f2)
     (fold3-ec x0 (nested) expression f1 f2) )

    ((fold3-ec x0 qualifier expression f1 f2)
     (let ((result #f) (empty #t))
       (do-ec qualifier
              (let ((value expression)) ; don't duplicate
                (if empty
                    (begin (set! result (f1 value))
                           (set! empty #f) )
                    (set! result (f2 value result)) )))
       (if empty x0 result) ))))


(define-syntax fold-ec
  (syntax-rules (nested)
    ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...)
     (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) )
    ((fold-ec x0 q1 q2 etc1 etc2 etc ...)
     (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) )
    ((fold-ec x0 expression f2)
     (fold-ec x0 (nested) expression f2) )

    ((fold-ec x0 qualifier expression f2)
     (let ((result x0))
       (do-ec qualifier (set! result (f2 expression result)))
       result ))))


; ==========================================================================
; The comprehensions list-ec string-ec vector-ec etc.
; ==========================================================================

(define-syntax list-ec
  (syntax-rules ()
    ((list-ec etc1 etc ...)
     (reverse (fold-ec '() etc1 etc ... cons)) )))

; Alternative: Reverse can safely be replaced by reverse! if you have it.
;
; Alternative: It is possible to construct the result in the correct order
;   using set-cdr! to add at the tail. This removes the overhead of copying
;   at the end, at the cost of more book-keeping.


(define-syntax append-ec
  (syntax-rules ()
    ((append-ec etc1 etc ...)
     (apply append (list-ec etc1 etc ...)) )))

(define-syntax string-ec
  (syntax-rules ()
    ((string-ec etc1 etc ...)
     (list->string (list-ec etc1 etc ...)) )))

; Alternative: For very long strings, the intermediate list may be a
;   problem. A more space-aware implementation collect the characters 
;   in an intermediate list and when this list becomes too large it is
;   converted into an intermediate string. At the end, the intermediate
;   strings are concatenated with string-append.


(define-syntax string-append-ec
  (syntax-rules ()
    ((string-append-ec etc1 etc ...)
     (apply string-append (list-ec etc1 etc ...)) )))

(define-syntax vector-ec
  (syntax-rules ()
    ((vector-ec etc1 etc ...)
     (list->vector (list-ec etc1 etc ...)) )))

; Comment: A similar approach as for string-ec can be used for vector-ec.
;   However, the space overhead for the intermediate list is much lower
;   than for string-ec and as there is no vector-append, the intermediate
;   vectors must be copied explicitly.

(define-syntax vector-of-length-ec
  (syntax-rules (nested)
    ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...)
     (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) )
    ((vector-of-length-ec k q1 q2             etc1 etc ...)
     (vector-of-length-ec k (nested q1 q2)    etc1 etc ...) )
    ((vector-of-length-ec k expression)
     (vector-of-length-ec k (nested) expression) )

    ((vector-of-length-ec k qualifier expression)
     (let ((len k))
       (let ((vec (make-vector len))
             (i 0) )
         (do-ec qualifier
                (if (< i len)
                    (begin (vector-set! vec i expression)
                           (set! i (+ i 1)) )
                    (error "vector is too short for the comprehension") ))
         (if (= i len)
             vec
             (error "vector is too long for the comprehension") ))))))


(define-syntax sum-ec
  (syntax-rules ()
    ((sum-ec etc1 etc ...)
     (fold-ec (+) etc1 etc ... +) )))

(define-syntax product-ec
  (syntax-rules ()
    ((product-ec etc1 etc ...)
     (fold-ec (*) etc1 etc ... *) )))

(define-syntax min-ec
  (syntax-rules ()
    ((min-ec etc1 etc ...)
     (fold3-ec (min) etc1 etc ... min min) )))

(define-syntax max-ec
  (syntax-rules ()
    ((max-ec etc1 etc ...)
     (fold3-ec (max) etc1 etc ... max max) )))

(define-syntax last-ec
  (syntax-rules (nested)
    ((last-ec default (nested q1 ...) q etc1 etc ...)
     (last-ec default (nested q1 ... q) etc1 etc ...) )
    ((last-ec default q1 q2             etc1 etc ...)
     (last-ec default (nested q1 q2)    etc1 etc ...) )
    ((last-ec default expression)
     (last-ec default (nested) expression) )

    ((last-ec default qualifier expression)
     (let ((result default))
       (do-ec qualifier (set! result expression))
       result ))))


; ==========================================================================
; The fundamental early-stopping comprehension first-ec
; ==========================================================================

(define-syntax first-ec
  (syntax-rules (nested)
    ((first-ec default (nested q1 ...) q etc1 etc ...)
     (first-ec default (nested q1 ... q) etc1 etc ...) )
    ((first-ec default q1 q2             etc1 etc ...)
     (first-ec default (nested q1 q2)    etc1 etc ...) )
    ((first-ec default expression)
     (first-ec default (nested) expression) )

    ((first-ec default qualifier expression)
     (let ((result default) (stop #f))
       (ec-guarded-do-ec 
         stop 
         (nested qualifier)
         (begin (set! result expression)
                (set! stop #t) ))
       result ))))

; (ec-guarded-do-ec stop (nested q ...) cmd)
;   constructs (do-ec q ... cmd) where the generators gen in q ... are
;   replaced by (:until gen stop).

(define-syntax ec-guarded-do-ec
  (syntax-rules (nested if not and or begin)

    ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd)
     (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) )

    ((ec-guarded-do-ec stop (nested (if test) q ...) cmd)
     (if test (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (not test) q ...) cmd)
     (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd)
     (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd)
     (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd)
     (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested gen q ...) cmd)
     (do-ec 
       (:until gen stop) 
       (ec-guarded-do-ec stop (nested q ...) cmd) ))

    ((ec-guarded-do-ec stop (nested) cmd)
     (do-ec cmd) )))

; Alternative: Instead of modifying the generator with :until, it is
;   possible to use call-with-current-continuation:
;
;   (define-synatx first-ec 
;     ...same as above...
;     ((first-ec default qualifier expression)
;      (call-with-current-continuation 
;       (lambda (cc)
;        (do-ec qualifier (cc expression))
;        default ))) ))
;
;   This is much simpler but not necessarily as efficient.


; ==========================================================================
; The early-stopping comprehensions any?-ec every?-ec
; ==========================================================================

(define-syntax any?-ec
  (syntax-rules (nested)
    ((any?-ec (nested q1 ...) q etc1 etc ...)
     (any?-ec (nested q1 ... q) etc1 etc ...) )
    ((any?-ec q1 q2             etc1 etc ...)
     (any?-ec (nested q1 q2)    etc1 etc ...) )
    ((any?-ec expression)
     (any?-ec (nested) expression) )

    ((any?-ec qualifier expression)
     (first-ec #f qualifier (if expression) #t) )))

(define-syntax every?-ec
  (syntax-rules (nested)
    ((every?-ec (nested q1 ...) q etc1 etc ...)
     (every?-ec (nested q1 ... q) etc1 etc ...) )
    ((every?-ec q1 q2             etc1 etc ...)
     (every?-ec (nested q1 q2)    etc1 etc ...) )
    ((every?-ec expression)
     (every?-ec (nested) expression) )

    ((every?-ec qualifier expression)
     (first-ec #t qualifier (if (not expression)) #f) )))

  

  
)

Added srfi/s42/_eager-comprehensions.sls.





















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s42 eager-comprehensions)
  (export
    do-ec list-ec append-ec string-ec string-append-ec vector-ec 
    vector-of-length-ec sum-ec product-ec min-ec max-ec any?-ec 
    every?-ec first-ec last-ec fold-ec fold3-ec 
    : :list :string :vector :integers :range :real-range :char-range 
    :port :dispatched :do :let :parallel :while :until
    :-dispatch-ref :-dispatch-set! make-initial-:-dispatch 
    dispatch-union :generator-proc)
  (import
    (rnrs)
    (rnrs r5rs)
    (srfi s39 parameters)
    (srfi s23 error tricks)
    (srfi private include))
  
  (SRFI-23-error->R6RS "(library (srfi s42 eager-comprehensions))"
   (include/resolve ("srfi" "%3a42") "ec.scm"))  
)

Added srfi/s42/design.scm.







































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
; <PLAINTEXT>
; Design Alternatives for Eager Comprehensions
; ============================================
;
; sebastian.egner@philips.com, Eindhoven, The Netherlands, Feb-2003.
; Scheme R5RS (incl. macros), SRFI-23 (error).
;
; This file contains implementation alternatives for eager comprehensions
; and examples to find out which is better suited for a particular system.
;
; Loading the alternatives in Scheme48 (version 0.57):
;   ,open srfi-23
;   ,load ec.scm
;   ,load design.scm
;
; Loading the alternatives in PLT/DrScheme (version 202): 
;   ; open "ec.scm", click Execute
;   (load "design.scm")
;
; Loading the alternatives in SCM (version 5d7):
;   ; invoke SCM with -v on the command line
;   (require 'macro) (require 'record)
;   (load "ec.scm")
;   (load "design.scm")


; =======================================================================
; list-ec
; =======================================================================

; list-ec1
;   uses reverse and fold-ec in the obvious way.
;     + one-liner
;     + reverse could allocate result contiguous
;     - resulting list is allocated twice (unless reverse! is used)

(define-syntax list-ec1
  (syntax-rules ()
    ((list-ec etc1 etc ...)
     (reverse (fold-ec '() etc1 etc ... cons)) )))


; list-ec2
;   uses set-cdr! appending at the end of the result.
;     + no copying of the result
;     - more book-keeping in the inner loop

(define-syntax list-ec2
  (syntax-rules (nested)
    ((list-ec2 (nested q1 ...) q etc1 etc ...)
     (list-ec2 (nested q1 ... q) etc1 etc ...) )
    ((list-ec2 q1 q2             etc1 etc ...)
     (list-ec2 (nested q1 q2)    etc1 etc ...) )
    ((list-ec2 expression)
     (list-ec2 (nested) expression) )

    ((list-ec2 qualifier expression)
     (let ((result #f) (tail (list #f)))
       (set! result tail)
       (do-ec qualifier
              (begin (set-cdr! tail (list expression))
                     (set! tail (cdr tail)) ))
       (cdr result) ))))


; comparison
;   * The trade-off is book-keeping overhead vs. allocation pressure.
;     The difference in the inner loop is (set-cdr!, list, set!) vs.
;     (set!, cons), the difference 
;   * Scheme48 0.57: list-ec1 seems 5% percent faster than list-ec2.
;   * PLT 202: list-ec1 seems 25% faster than than list-ec2.

(define (perf-list-ec1 iterations n)
  (do-ec (:range i iterations) 
         (list-ec1 (:range k n) k) ))

(define (perf-list-ec2 iterations n)
  (do-ec (:range i iterations) 
         (list-ec2 (:range k n) k) ))

; try:
;   (perf-list-ec1 100000 10)
;   (perf-list-ec2 100000 10)
;   (perf-list-ec1 100 10000)
;   (perf-list-ec2 100 10000)


; =======================================================================
; string-ec
; =======================================================================

; string-ec1
;   uses list->string and list-ec in the obvious way.
;     + one-liner
;     - intermediate list is much bigger than result
;     - inherits overhead of list-ec

(define-syntax string-ec1
  (syntax-rules ()
    ((string-ec1 etc1 etc ...)
     (list->string (list-ec etc1 etc ...)) )))


; string-ec2
;   uses string-append on pieces of the result of limited length;
;   pieces are constructed with the method of string-ec1
;     + space-efficient for long results
;     - overhead for short result
;     + potentially very efficient in native code
;     - more complicated book-keeping

(define-syntax string-ec2
  (syntax-rules ()
    ((string-ec2 (nested q1 ...) q etc1 etc ...)
     (string-ec2 (nested q1 ... q) etc1 etc ...) )
    ((string-ec2 q1 q2             etc1 etc ...)
     (string-ec2 (nested q1 q2)    etc1 etc ...) )
    ((string-ec2 expression)
     (string-ec2 (nested) expression) )

    ((string-ec2 qualifier expression)
     (let ((result '()) (piece '()) (len 0) (max-len 1000))
       (do-ec qualifier
              (begin 
                (set! piece (cons expression piece))
                (set! len (+ len 1))
                (if (= len max-len)
                    (begin 
                      (set! result (cons (list->string piece) result))
                      (set! piece '())
                      (set! len 0) ))))
       (apply string-append 
              (reverse (cons (list->string piece) result)) )))))


; comparison
;   * The main question is whether the space overhead for an intermediate
;     list is acceptable. If not, string-ec1 is no option.
;   * If string-ec2 is used, the question is how to adjust max-len. It
;     can either be used as an emergency brake for very long intermediate
;     lists or it can be used to keep the total overhead limited.
;   * Scheme48 0.57: string-ec1 is 25% faster than string-ec2 for short 
;       results and still 15% faster for strings of length 10^5. However, 
;       at 10^6 (for my test configuration) string-ec1 has 'heap overflow',
;       whereas string-ec2 has no problem.
;   * PLT 202: string-ec1 is 50%..70% faster than string-ec2, both for 
;       short and for long strings.

(define (perf-string-ec1 iterations n)
  (do-ec (:range i iterations) 
         (string-ec1 (:range k n) #\a) ))

(define (perf-string-ec2 iterations n)
  (do-ec (:range i iterations) 
         (string-ec2 (:range k n) #\a) ))

; try:
;   (perf-string-ec1 100000 10)
;   (perf-string-ec2 100000 10)
;   (perf-string-ec1 10 100000)
;   (perf-string-ec2 10 100000)
;   (perf-string-ec1 1 1000000)
;   (perf-string-ec2 1 1000000)


; =======================================================================
; first-ec
; =======================================================================

; first-ec1
;   uses a non-local exit constructed by call-with-current-continuation.
;     - stack-based Schemes have problems implementing this efficiently
;     + simple, straight-forward, schemeish

(define-syntax first-ec1
  (syntax-rules (nested)
    ((first-ec1 default (nested q1 ...) q etc1 etc ...)
     (first-ec1 default (nested q1 ... q) etc1 etc ...) )
    ((first-ec1 default q1 q2             etc1 etc ...)
     (first-ec1 default (nested q1 q2)    etc1 etc ...) )
    ((first-ec1 default expression)
     (first-ec1 default (nested) expression) )

    ((first-ec1 default qualifier expression)
     (call-with-current-continuation 
      (lambda (cc)
        (do-ec qualifier (cc expression))
        default )))))


; first-ec2
;   uses :until to add an early termination to each generator.
;     + as fast as it gets
;     - copies part of the functionality of do-ec 

(define-syntax first-ec2
  (syntax-rules (nested)
    ((first-ec2 default (nested q1 ...) q etc1 etc ...)
     (first-ec2 default (nested q1 ... q) etc1 etc ...) )
    ((first-ec2 default q1 q2             etc1 etc ...)
     (first-ec2 default (nested q1 q2)    etc1 etc ...) )
    ((first-ec2 default expression)
     (first-ec2 default (nested) expression) )

    ((first-ec2 default qualifier expression)
     (let ((result default) (stop #f))
       (ec-guarded-do-ec 
         stop 
         (nested qualifier)
         (begin (set! result expression)
                (set! stop #t) ))
       result ))))

; (ec-guarded-do-ec stop (nested q ...) cmd)
;   constructs (do-ec q ... cmd) where the generators gen in q ... are
;   replaced by (:until gen stop).

(define-syntax ec-guarded-do-ec
  (syntax-rules (nested if not and or begin)

    ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd)
     (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) )

    ((ec-guarded-do-ec stop (nested (if test) q ...) cmd)
     (if test (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (not test) q ...) cmd)
     (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd)
     (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd)
     (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd)
     (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested gen q ...) cmd)
     (do-ec 
       (:until gen stop) 
       (ec-guarded-do-ec stop (nested q ...) cmd) ))

    ((ec-guarded-do-ec stop (nested) cmd)
     (do-ec cmd) )))


; comparison
;   * The main question is whether call/cc is efficient here.
;     If it is not, first-ec1 is not an option.
;   * We simply run a loop terminating after a few iterations and 
;     measure the time it takes in total.
;   * Scheme48 0.57: first-ec2 seems to be about 15% faster than first-ec1.
;   * PLT 202: first-ec2 seems to be about factor 4 faster than first-ec1.

(define (perf-first-ec1 iterations)
  (do-ec (:range i iterations) 
         (first-ec1 0 (:range x 10) (if (= x 5)) #t) ))

(define (perf-first-ec2 iterations)
  (do-ec (:range i iterations) 
         (first-ec2 0 (:range x 10) (if (= x 5)) #t) ))

; try:
;   (perf-first-ec1 100000)
;   (perf-first-ec2 100000)


; =======================================================================
; :vector
; =======================================================================


; :vector
;    uses vector->list, append and :list for the multi-argument case.
;      + one-liner
;      - the enumerated sequence is copied
;      - the intermediate list is larger than the arguments

(define-syntax :vector1
  (syntax-rules (index)
    ((:vector1 cc var (index i) arg)
     (:do cc
          (let ((vec arg) (len 0)) 
            (set! len (vector-length vec)))
          ((i 0))
          (< i len)
          (let ((var (vector-ref vec i))))
          #t
          ((+ i 1)) ))
    ((:vector1 cc var (index i) arg1 arg2 arg ...)
     (:list cc 
            var 
            (index i) 
            (apply append (map vector->list (list arg1 arg2 arg ...))) ))
    ((:vector1 cc var arg1 arg ...)
     (:vector1 cc var (index i) arg1 arg ...) )))


; :vector2
;    runs through a list of vectors with nested loops for multi-arg.
;      + no space overhead
;      + no copying of the arguments
;      - more complicated book-keeping

(define-syntax :vector2
  (syntax-rules (index)
    ((:vector2 cc var arg)
     (:vector2 cc var (index i) arg) )
    ((:vector2 cc var (index i) arg)
     (:do cc
          (let ((vec arg) (len 0)) 
            (set! len (vector-length vec)))
          ((i 0))
          (< i len)
          (let ((var (vector-ref vec i))))
          #t
          ((+ i 1)) ))

    ((:vector2 cc var (index i) arg1 arg2 arg ...)
     (:parallel cc (:vector2 cc var arg1 arg2 arg ...) (:integers i)) )
    ((:vector2 cc var arg1 arg2 arg ...)
     (:do cc
          (let ((vec #f)
                (len 0)
                (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) ))
          ((k 0))
          (if (< k len)
              #t
              (if (null? vecs)
                  #f
                  (begin (set! vec (car vecs))
                         (set! vecs (cdr vecs))
                         (set! len (vector-length vec))
                         (set! k 0)
                         #t )))
          (let ((var (vector-ref vec k))))
          #t
          ((+ k 1)) ))))

(define (ec-:vector-filter vecs)
  (if (null? vecs)
      '()
      (if (zero? (vector-length (car vecs)))
          (ec-:vector-filter (cdr vecs))
          (cons (car vecs) (ec-:vector-filter (cdr vecs))) )))

; comparison
;   * The trade-off is book-keeping overhead vs. allocation overhead.
;   * Scheme48 0.57: For short vectors :vector1 is 20% faster than 
;       :vector2. For long vectors (10^4) :vector2 is factor 2.8 
;       times faster. Break-even around n = 2.
;   * PLT 202: For short vectors, :vector1 is factor 2 faster than
;       :vector2, for long vectors (10^4) factor 1.6 slower.
;       Break-even is around n = 3.

(define (perf-:vector1 iterations n)
  (do-ec 
   (:let v (vector-of-length-ec n (:range i n) i))
   (:range i iterations) 
   (do-ec (:vector1 x v v v v v v v v v v) x) ))

(define (perf-:vector2 iterations n)
  (do-ec 
   (:let v (vector-of-length-ec n (:range i n) i))
   (:range i iterations) 
   (do-ec (:vector2 x v v v v v v v v v v) x) ))

; try:
;   (perf-:vector1 100000 1)
;   (perf-:vector2 100000 1)
;   (perf-:vector1 100 10000)
;   (perf-:vector2 100 10000)

Added srfi/s42/eager-comprehensions.sls.



























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s42 eager-comprehensions)
  (export
    do-ec list-ec append-ec string-ec string-append-ec vector-ec 
    vector-of-length-ec sum-ec product-ec min-ec max-ec any?-ec 
    every?-ec first-ec last-ec fold-ec fold3-ec 
    : :list :string :vector :integers :range :real-range :char-range 
    :port :dispatched :do :let :parallel :while :until
    :-dispatch-ref :-dispatch-set! make-initial-:-dispatch 
    dispatch-union :generator-proc)
  (import
    (except (rnrs) error)
    (rnrs r5rs)
    (srfi s39 parameters)
    ;; (srfi s23 error tricks)
    (srfi s23 error)
    (srfi private include))
  
  ;; (SRFI-23-error->R6RS "(library (srfi s42 eager-comprehensions))"
  ;;   (include/resolve ("srfi" "s42") "ec.scm"))

; <PLAINTEXT>
; Eager Comprehensions in [outer..inner|expr]-Convention
; ======================================================
;
; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005
; Scheme R5RS (incl. macros), SRFI-23 (error).
;
; Modified by Derick Eddington to be able to be included into an R6RS library.
; 
; Loading the implementation into Scheme48 0.57:
;   ,open srfi-23
;   ,load ec.scm
;
; Loading the implementation into PLT/DrScheme 202:
;   ; File > Open ... "ec.scm", click Execute
;
; Loading the implementation into SCM 5d7:
;   (require 'macro) (require 'record) 
;   (load "ec.scm")
;
; Implementation comments:
;   * All local (not exported) identifiers are named ec-<something>.
;   * This implementation focuses on portability, performance, 
;     readability, and simplicity roughly in this order. Design
;     decisions related to performance are taken for Scheme48.
;   * Alternative implementations, Comments and Warnings are 
;     mentioned after the definition with a heading.


; ==========================================================================
; The fundamental comprehension do-ec
; ==========================================================================
;
; All eager comprehensions are reduced into do-ec and
; all generators are reduced to :do. 
;
; We use the following short names for syntactic variables
;   q    - qualifier
;   cc   - current continuation, thing to call at the end;
;          the CPS is (m (cc ...) arg ...) -> (cc ... expr ...)
;   cmd  - an expression being evaluated for its side-effects
;   expr - an expression
;   gen  - a generator of an eager comprehension
;   ob   - outer binding
;   oc   - outer command
;   lb   - loop binding
;   ne1? - not-end1? (before the payload)
;   ib   - inner binding
;   ic   - inner command
;   ne2? - not-end2? (after the payload)
;   ls   - loop step
;   etc  - more arguments of mixed type


; (do-ec q ... cmd)
;   handles nested, if/not/and/or, begin, :let, and calls generator 
;   macros in CPS to transform them into fully decorated :do.
;   The code generation for a :do is delegated to do-ec:do.

(define-syntax do-ec
  (syntax-rules (nested if not and or begin :do let)

    ; explicit nesting -> implicit nesting
    ((do-ec (nested q ...) etc ...)
     (do-ec q ... etc ...) )

    ; implicit nesting -> fold do-ec
    ((do-ec q1 q2 etc1 etc ...)
     (do-ec q1 (do-ec q2 etc1 etc ...)) )

    ; no qualifiers at all -> evaluate cmd once
    ((do-ec cmd)
     (begin cmd (if #f #f)) )

; now (do-ec q cmd) remains

    ; filter -> make conditional
    ((do-ec (if test) cmd)
     (if test (do-ec cmd)) )
    ((do-ec (not test) cmd)
     (if (not test) (do-ec cmd)) )
    ((do-ec (and test ...) cmd)
     (if (and test ...) (do-ec cmd)) )
    ((do-ec (or test ...) cmd)
     (if (or test ...) (do-ec cmd)) )

    ; begin -> make a sequence
    ((do-ec (begin etc ...) cmd)
     (begin etc ... (do-ec cmd)) )

    ; fully decorated :do-generator -> delegate to do-ec:do
    ((do-ec (:do olet lbs ne1? ilet ne2? lss) cmd)
     (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) )

; anything else -> call generator-macro in CPS; reentry at (*)

    ((do-ec (g arg1 arg ...) cmd)
     (g (do-ec:do cmd) arg1 arg ...) )))


; (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss))
;   generates code for a single fully decorated :do-generator
;   with cmd as payload, taking care of special cases.

(define-syntax do-ec:do
  (syntax-rules (:do let)

    ; reentry point (*) -> generate code
    ((do-ec:do cmd 
               (:do (let obs oc ...) 
                    lbs 
                    ne1? 
                    (let ibs ic ...) 
                    ne2? 
                    (ls ...) ))
     (ec-simplify
       (let obs
         oc ...
         (let loop lbs
           (ec-simplify
             (if ne1?
                 (ec-simplify
                   (let ibs
                      ic ...
                      cmd
                      (ec-simplify
                        (if ne2?
                            (loop ls ...) )))))))))) ))

    
; (ec-simplify <expression>)
;   generates potentially more efficient code for <expression>.
;   The macro handles if, (begin <command>*), and (let () <command>*)
;   and takes care of special cases.

(define-syntax ec-simplify
  (syntax-rules (if not let begin)

; one- and two-sided if

    ; literal <test>
    ((ec-simplify (if #t consequent))
     consequent )
    ((ec-simplify (if #f consequent))
     (if #f #f) )
    ((ec-simplify (if #t consequent alternate))
     consequent )
    ((ec-simplify (if #f consequent alternate))
     alternate )

    ; (not (not <test>))
    ((ec-simplify (if (not (not test)) consequent))
     (ec-simplify (if test consequent)) )
    ((ec-simplify (if (not (not test)) consequent alternate))
     (ec-simplify (if test consequent alternate)) )

; (let () <command>*) 

    ; empty <binding spec>*
    ((ec-simplify (let () command ...))
     (ec-simplify (begin command ...)) )

; begin 

    ; flatten use helper (ec-simplify 1 done to-do)
    ((ec-simplify (begin command ...))
     (ec-simplify 1 () (command ...)) )
    ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...))
     (ec-simplify 1 done (to-do1 ... to-do2 ...)) )
    ((ec-simplify 1 (done ...) (to-do1 to-do ...))
     (ec-simplify 1 (done ... to-do1) (to-do ...)) )

    ; exit helper
    ((ec-simplify 1 () ())
     (if #f #f) )
    ((ec-simplify 1 (command) ())
     command )
    ((ec-simplify 1 (command1 command ...) ())
     (begin command1 command ...) )

; anything else

    ((ec-simplify expression)
     expression )))


; ==========================================================================
; The special generators :do, :let, :parallel, :while, and :until
; ==========================================================================

(define-syntax :do
  (syntax-rules ()

    ; full decorated -> continue with cc, reentry at (*)
    ((:do (cc ...) olet lbs ne1? ilet ne2? lss)
     (cc ... (:do olet lbs ne1? ilet ne2? lss)) )

    ; short form -> fill in default values
    ((:do cc lbs ne1? lss)
     (:do cc (let ()) lbs ne1? (let ()) #t lss) )))
    

(define-syntax :let
  (syntax-rules (index)
    ((:let cc var (index i) expression)
     (:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) )
    ((:let cc var expression)
     (:do cc (let ((var expression))) () #t (let ()) #f ()) )))


(define-syntax :parallel
  (syntax-rules (:do)
    ((:parallel cc)
     cc )
    ((:parallel cc (g arg1 arg ...) gen ...)
     (g (:parallel-1 cc (gen ...)) arg1 arg ...) )))

; (:parallel-1 cc (to-do ...) result [ next ] )
;    iterates over to-do by converting the first generator into 
;    the :do-generator next and merging next into result.

(define-syntax :parallel-1  ; used as 
  (syntax-rules (:do let)

    ; process next element of to-do, reentry at (**)
    ((:parallel-1 cc ((g arg1 arg ...) gen ...) result)
     (g (:parallel-1 cc (gen ...) result) arg1 arg ...) )

    ; reentry point (**) -> merge next into result
    ((:parallel-1 
       cc 
       gens 
       (:do (let (ob1 ...) oc1 ...) 
            (lb1 ...) 
            ne1?1 
            (let (ib1 ...) ic1 ...) 
            ne2?1 
            (ls1 ...) )
       (:do (let (ob2 ...) oc2 ...) 
            (lb2 ...) 
            ne1?2 
            (let (ib2 ...) ic2 ...) 
            ne2?2 
            (ls2 ...) ))
     (:parallel-1 
       cc 
       gens 
       (:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...) 
            (lb1 ... lb2 ...) 
            (and ne1?1 ne1?2) 
            (let (ib1 ... ib2 ...) ic1 ... ic2 ...) 
            (and ne2?1 ne2?2) 
            (ls1 ... ls2 ...) )))

    ; no more gens -> continue with cc, reentry at (*)
    ((:parallel-1 (cc ...) () result)
     (cc ... result) )))

(define-syntax :while
  (syntax-rules ()
    ((:while cc (g arg1 arg ...) test)
     (g (:while-1 cc test) arg1 arg ...) )))

; (:while-1 cc test (:do ...))
;    modifies the fully decorated :do-generator such that it
;    runs while test is a true value. 
;       The original implementation just replaced ne1? by
;    (and ne1? test) as follows:
;
;      (define-syntax :while-1
;        (syntax-rules (:do)
;          ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss))
;           (:do cc olet lbs (and ne1? test) ilet ne2? lss) )))
;
;    Unfortunately, this code is wrong because ne1? may depend
;    in the inner bindings introduced in ilet, but ne1? is evaluated
;    outside of the inner bindings. (Refer to the specification of
;    :do to see the structure.) 
;       The problem manifests itself (as sunnan@handgranat.org 
;    observed) when the :list-generator is modified:
; 
;      (do-ec (:while (:list x '(1 2)) (= x 1)) (display x)).
;
;    In order to generate proper code, we introduce temporary
;    variables saving the values of the inner bindings. The inner
;    bindings are executed in a new ne1?, which also evaluates ne1?
;    outside the scope of the inner bindings, then the inner commands
;    are executed (possibly changing the variables), and then the
;    values of the inner bindings are saved and (and ne1? test) is
;    returned. In the new ilet, the inner variables are bound and
;    initialized and their values are restored. So we construct:
;
;     (let (ob .. (ib-tmp #f) ...)
;       oc ...
;       (let loop (lb ...)
;         (if (let (ne1?-value ne1?)
;               (let ((ib-var ib-rhs) ...)
;                 ic ...
;                 (set! ib-tmp ib-var) ...)
;               (and ne1?-value test))
;             (let ((ib-var ib-tmp) ...)
;               /payload/
;               (if ne2?
;                   (loop ls ...) )))))

(define-syntax :while-1
  (syntax-rules (:do let)
    ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss))
     (:while-2 cc test () () () (:do olet lbs ne1? ilet ne2? lss)))))

(define-syntax :while-2
  (syntax-rules (:do let)
    ((:while-2 cc 
               test 
               (ib-let     ...)
               (ib-save    ...)
               (ib-restore ...)
               (:do olet 
                    lbs 
                    ne1? 
                    (let ((ib-var ib-rhs) ib ...) ic ...)
                    ne2? 
                    lss))
     (:while-2 cc 
               test 
               (ib-let     ... (ib-tmp #f))
               (ib-save    ... (ib-var ib-rhs))
               (ib-restore ... (ib-var ib-tmp))
               (:do olet 
                    lbs 
                    ne1? 
                    (let (ib ...) ic ... (set! ib-tmp ib-var)) 
                    ne2? 
                    lss)))
    ((:while-2 cc
               test
               (ib-let     ...)
               (ib-save    ...)
               (ib-restore ...)
               (:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss))
     (:do cc
          (let (ob ... ib-let ...) oc ...)
          lbs
          (let ((ne1?-value ne1?))
            (let (ib-save ...)
                ic ...
                (and ne1?-value test)))
          (let (ib-restore ...))
          ne2?
          lss))))


(define-syntax :until
  (syntax-rules ()
    ((:until cc (g arg1 arg ...) test)
     (g (:until-1 cc test) arg1 arg ...) )))

(define-syntax :until-1
  (syntax-rules (:do)
    ((:until-1 cc test (:do olet lbs ne1? ilet ne2? lss))
     (:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) )))


; ==========================================================================
; The typed generators :list :string :vector etc.
; ==========================================================================

(define-syntax :list
  (syntax-rules (index)
    ((:list cc var (index i) arg ...)
     (:parallel cc (:list var arg ...) (:integers i)) )
    ((:list cc var arg1 arg2 arg ...)
     (:list cc var (append arg1 arg2 arg ...)) )
    ((:list cc var arg)
     (:do cc
          (let ())
          ((t arg))
          (not (null? t))
          (let ((var (car t))))
          #t
          ((cdr t)) ))))


(define-syntax :string
  (syntax-rules (index)
    ((:string cc var (index i) arg)
     (:do cc
          (let ((str arg) (len 0)) 
            (set! len (string-length str)))
          ((i 0))
          (< i len)
          (let ((var (string-ref str i))))
          #t
          ((+ i 1)) ))
    ((:string cc var (index i) arg1 arg2 arg ...)
     (:string cc var (index i) (string-append arg1 arg2 arg ...)) )
    ((:string cc var arg1 arg ...)
     (:string cc var (index i) arg1 arg ...) )))

; Alternative: An implementation in the style of :vector can also
;   be used for :string. However, it is less interesting as the
;   overhead of string-append is much less than for 'vector-append'.


(define-syntax :vector
  (syntax-rules (index)
    ((:vector cc var arg)
     (:vector cc var (index i) arg) )
    ((:vector cc var (index i) arg)
     (:do cc
          (let ((vec arg) (len 0)) 
            (set! len (vector-length vec)))
          ((i 0))
          (< i len)
          (let ((var (vector-ref vec i))))
          #t
          ((+ i 1)) ))

    ((:vector cc var (index i) arg1 arg2 arg ...)
     (:parallel cc (:vector cc var arg1 arg2 arg ...) (:integers i)) )
    ((:vector cc var arg1 arg2 arg ...)
     (:do cc
          (let ((vec #f)
                (len 0)
                (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) ))
          ((k 0))
          (if (< k len)
              #t
              (if (null? vecs)
                  #f
                  (begin (set! vec (car vecs))
                         (set! vecs (cdr vecs))
                         (set! len (vector-length vec))
                         (set! k 0)
                         #t )))
          (let ((var (vector-ref vec k))))
          #t
          ((+ k 1)) ))))

(define (ec-:vector-filter vecs)
  (if (null? vecs)
      '()
      (if (zero? (vector-length (car vecs)))
          (ec-:vector-filter (cdr vecs))
          (cons (car vecs) (ec-:vector-filter (cdr vecs))) )))

; Alternative: A simpler implementation for :vector uses vector->list
;   append and :list in the multi-argument case. Please refer to the
;   'design.scm' for more details.


(define-syntax :integers
  (syntax-rules (index)
    ((:integers cc var (index i))
     (:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) )
    ((:integers cc var)
     (:do cc ((var 0)) #t ((+ var 1))) )))


(define-syntax :range
  (syntax-rules (index)

    ; handle index variable and add optional args
    ((:range cc var (index i) arg1 arg ...)
     (:parallel cc (:range var arg1 arg ...) (:integers i)) )
    ((:range cc var arg1)
     (:range cc var 0 arg1 1) )
    ((:range cc var arg1 arg2)
     (:range cc var arg1 arg2 1) )

; special cases (partially evaluated by hand from general case)

    ((:range cc var 0 arg2 1)
     (:do cc
          (let ((b arg2))
            (if (not (and (integer? b) (exact? b)))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" 0 b 1 )))
          ((var 0))
          (< var b)
          (let ())
          #t
          ((+ var 1)) ))

    ((:range cc var 0 arg2 -1)
     (:do cc
          (let ((b arg2))
            (if (not (and (integer? b) (exact? b)))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" 0 b 1 )))
          ((var 0))
          (> var b)
          (let ())
          #t
          ((- var 1)) ))

    ((:range cc var arg1 arg2 1)
     (:do cc
          (let ((a arg1) (b arg2))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b 1 )) )
          ((var a))
          (< var b)
          (let ())
          #t
          ((+ var 1)) ))

    ((:range cc var arg1 arg2 -1)
     (:do cc
          (let ((a arg1) (b arg2) (s -1) (stop 0))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b -1 )) )
          ((var a))
          (> var b)
          (let ())
          #t
          ((- var 1)) ))

; the general case

    ((:range cc var arg1 arg2 arg3)
     (:do cc
          (let ((a arg1) (b arg2) (s arg3) (stop 0))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b)
                          (integer? s) (exact? s) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b s ))
            (if (zero? s)
                (error "step size must not be zero in :range") )
            (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) )
          ((var a))
          (not (= var stop))
          (let ())
          #t
          ((+ var s)) ))))

; Comment: The macro :range inserts some code to make sure the values
;   are exact integers. This overhead has proven very helpful for 
;   saving users from themselves.


(define-syntax :real-range
  (syntax-rules (index)

    ; add optional args and index variable
    ((:real-range cc var arg1)
     (:real-range cc var (index i) 0 arg1 1) )
    ((:real-range cc var (index i) arg1)
     (:real-range cc var (index i) 0 arg1 1) )
    ((:real-range cc var arg1 arg2)
     (:real-range cc var (index i) arg1 arg2 1) )
    ((:real-range cc var (index i) arg1 arg2)
     (:real-range cc var (index i) arg1 arg2 1) )
    ((:real-range cc var arg1 arg2 arg3)
     (:real-range cc var (index i) arg1 arg2 arg3) )

    ; the fully qualified case
    ((:real-range cc var (index i) arg1 arg2 arg3)
     (:do cc
          (let ((a arg1) (b arg2) (s arg3) (istop 0))
            (if (not (and (real? a) (real? b) (real? s)))
                (error "arguments of :real-range are not real" a b s) )
            (if (and (exact? a) (or (not (exact? b)) (not (exact? s))))
                (set! a (exact->inexact a)) )
            (set! istop (/ (- b a) s)) )
          ((i 0))
          (< i istop)
          (let ((var (+ a (* s i)))))
          #t
          ((+ i 1)) ))))

; Comment: The macro :real-range adapts the exactness of the start
;   value in case any of the other values is inexact. This is a
;   precaution to avoid (list-ec (: x 0 3.0) x) => '(0 1.0 2.0).

    
(define-syntax :char-range
  (syntax-rules (index)
    ((:char-range cc var (index i) arg1 arg2)
     (:parallel cc (:char-range var arg1 arg2) (:integers i)) )
    ((:char-range cc var arg1 arg2)
     (:do cc
          (let ((imax (char->integer arg2))))
          ((i (char->integer arg1)))
          (<= i imax)
          (let ((var (integer->char i))))
          #t
          ((+ i 1)) ))))

; Warning: There is no R5RS-way to implement the :char-range generator 
;   because the integers obtained by char->integer are not necessarily 
;   consecutive. We simply assume this anyhow for illustration.


(define-syntax :port
  (syntax-rules (index)
    ((:port cc var (index i) arg1 arg ...)
     (:parallel cc (:port var arg1 arg ...) (:integers i)) )
    ((:port cc var arg)
     (:port cc var arg read) )
    ((:port cc var arg1 arg2)
     (:do cc
          (let ((port arg1) (read-proc arg2)))
          ((var (read-proc port)))
          (not (eof-object? var))
          (let ())
          #t
          ((read-proc port)) ))))


; ==========================================================================
; The typed generator :dispatched and utilities for constructing dispatchers
; ==========================================================================

(define-syntax :dispatched
  (syntax-rules (index)
    ((:dispatched cc var (index i) dispatch arg1 arg ...)
     (:parallel cc 
                (:integers i)
                (:dispatched var dispatch arg1 arg ...) ))
    ((:dispatched cc var dispatch arg1 arg ...)
     (:do cc
          (let ((d dispatch) 
                (args (list arg1 arg ...)) 
                (g #f) 
                (empty (list #f)) )
            (set! g (d args))
            (if (not (procedure? g))
                (error "unrecognized arguments in dispatching" 
                       args 
                       (d '()) )))
          ((var (g empty)))
          (not (eq? var empty))
          (let ())
          #t
          ((g empty)) ))))

; Comment: The unique object empty is created as a newly allocated
;   non-empty list. It is compared using eq? which distinguishes
;   the object from any other object, according to R5RS 6.1.


(define-syntax :generator-proc
  (syntax-rules (:do let)

    ; call g with a variable, reentry at (**)
    ((:generator-proc (g arg ...))
     (g (:generator-proc var) var arg ...) )

    ; reentry point (**) -> make the code from a single :do
    ((:generator-proc
       var 
       (:do (let obs oc ...) 
            ((lv li) ...) 
            ne1? 
            (let ((i v) ...) ic ...) 
            ne2? 
            (ls ...)) )
     (ec-simplify 
      (let obs
          oc ...
          (let ((lv li) ... (ne2 #t))
            (ec-simplify
             (let ((i #f) ...) ; v not yet valid
               (lambda (empty)
                 (if (and ne1? ne2)
                     (ec-simplify
                      (begin 
                        (set! i v) ...
                        ic ...
                        (let ((value var))
                          (ec-simplify
                           (if ne2?
                               (ec-simplify 
                                (begin (set! lv ls) ...) )
                               (set! ne2 #f) ))
                          value )))
                     empty ))))))))

    ; silence warnings of some macro expanders
    ((:generator-proc var)
     (error "illegal macro call") )))


(define (dispatch-union d1 d2)
  (lambda (args)
    (let ((g1 (d1 args)) (g2 (d2 args)))
      (if g1
          (if g2 
              (if (null? args)
                  (append (if (list? g1) g1 (list g1)) 
                          (if (list? g2) g2 (list g2)) )
                  (error "dispatching conflict" args (d1 '()) (d2 '())) )
              g1 )
          (if g2 g2 #f) ))))


; ==========================================================================
; The dispatching generator :
; ==========================================================================

(define (make-initial-:-dispatch)
  (lambda (args)
    (case (length args)
      ((0) 'SRFI42)
      ((1) (let ((a1 (car args)))
             (cond
              ((list? a1)
               (:generator-proc (:list a1)) )
              ((string? a1)
               (:generator-proc (:string a1)) )
              ((vector? a1)
               (:generator-proc (:vector a1)) )
              ((and (integer? a1) (exact? a1))
               (:generator-proc (:range a1)) )
              ((real? a1)
               (:generator-proc (:real-range a1)) )
              ((input-port? a1)
               (:generator-proc (:port a1)) )
              (else
               #f ))))
      ((2) (let ((a1 (car args)) (a2 (cadr args)))
             (cond
              ((and (list? a1) (list? a2))
               (:generator-proc (:list a1 a2)) )
              ((and (string? a1) (string? a2))
               (:generator-proc (:string a1 a2)) )
              ((and (vector? a1) (vector? a2))
               (:generator-proc (:vector a1 a2)) )
              ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2))
               (:generator-proc (:range a1 a2)) )
              ((and (real? a1) (real? a2))
               (:generator-proc (:real-range a1 a2)) )
              ((and (char? a1) (char? a2))
               (:generator-proc (:char-range a1 a2)) )
              ((and (input-port? a1) (procedure? a2))
               (:generator-proc (:port a1 a2)) )
              (else
               #f ))))
      ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args)))
             (cond
              ((and (list? a1) (list? a2) (list? a3))
               (:generator-proc (:list a1 a2 a3)) )
              ((and (string? a1) (string? a2) (string? a3))
               (:generator-proc (:string a1 a2 a3)) )
              ((and (vector? a1) (vector? a2) (vector? a3))
               (:generator-proc (:vector a1 a2 a3)) )
              ((and (integer? a1) (exact? a1) 
                    (integer? a2) (exact? a2)
                    (integer? a3) (exact? a3))
               (:generator-proc (:range a1 a2 a3)) )
              ((and (real? a1) (real? a2) (real? a3))
               (:generator-proc (:real-range a1 a2 a3)) )
              (else
               #f ))))
      (else
       (letrec ((every? 
                 (lambda (pred args)
                   (if (null? args)
                       #t
                       (and (pred (car args))
                            (every? pred (cdr args)) )))))
         (cond
          ((every? list? args)
           (:generator-proc (:list (apply append args))) )
          ((every? string? args)
           (:generator-proc (:string (apply string-append args))) )
          ((every? vector? args)
           (:generator-proc (:list (apply append (map vector->list args)))) )
          (else
           #f )))))))

(define :-dispatch 
  (make-parameter (make-initial-:-dispatch)
                  (lambda (x) (if (procedure? x) x (error "not a procedure" x)))))

(define (:-dispatch-ref)
  (:-dispatch))

(define (:-dispatch-set! dispatch)
  (:-dispatch dispatch))

(define-syntax :
  (syntax-rules (index)
    ((: cc var (index i) arg1 arg ...)
     (:dispatched cc var (index i) (:-dispatch) arg1 arg ...) )
    ((: cc var arg1 arg ...)
     (:dispatched cc var (:-dispatch) arg1 arg ...) )))


; ==========================================================================
; The utility comprehensions fold-ec, fold3-ec
; ==========================================================================

(define-syntax fold3-ec
  (syntax-rules (nested)
    ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
     (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
    ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
     (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
    ((fold3-ec x0 expression f1 f2)
     (fold3-ec x0 (nested) expression f1 f2) )

    ((fold3-ec x0 qualifier expression f1 f2)
     (let ((result #f) (empty #t))
       (do-ec qualifier
              (let ((value expression)) ; don't duplicate
                (if empty
                    (begin (set! result (f1 value))
                           (set! empty #f) )
                    (set! result (f2 value result)) )))
       (if empty x0 result) ))))


(define-syntax fold-ec
  (syntax-rules (nested)
    ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...)
     (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) )
    ((fold-ec x0 q1 q2 etc1 etc2 etc ...)
     (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) )
    ((fold-ec x0 expression f2)
     (fold-ec x0 (nested) expression f2) )

    ((fold-ec x0 qualifier expression f2)
     (let ((result x0))
       (do-ec qualifier (set! result (f2 expression result)))
       result ))))


; ==========================================================================
; The comprehensions list-ec string-ec vector-ec etc.
; ==========================================================================

(define-syntax list-ec
  (syntax-rules ()
    ((list-ec etc1 etc ...)
     (reverse (fold-ec '() etc1 etc ... cons)) )))

; Alternative: Reverse can safely be replaced by reverse! if you have it.
;
; Alternative: It is possible to construct the result in the correct order
;   using set-cdr! to add at the tail. This removes the overhead of copying
;   at the end, at the cost of more book-keeping.


(define-syntax append-ec
  (syntax-rules ()
    ((append-ec etc1 etc ...)
     (apply append (list-ec etc1 etc ...)) )))

(define-syntax string-ec
  (syntax-rules ()
    ((string-ec etc1 etc ...)
     (list->string (list-ec etc1 etc ...)) )))

; Alternative: For very long strings, the intermediate list may be a
;   problem. A more space-aware implementation collect the characters 
;   in an intermediate list and when this list becomes too large it is
;   converted into an intermediate string. At the end, the intermediate
;   strings are concatenated with string-append.


(define-syntax string-append-ec
  (syntax-rules ()
    ((string-append-ec etc1 etc ...)
     (apply string-append (list-ec etc1 etc ...)) )))

(define-syntax vector-ec
  (syntax-rules ()
    ((vector-ec etc1 etc ...)
     (list->vector (list-ec etc1 etc ...)) )))

; Comment: A similar approach as for string-ec can be used for vector-ec.
;   However, the space overhead for the intermediate list is much lower
;   than for string-ec and as there is no vector-append, the intermediate
;   vectors must be copied explicitly.

(define-syntax vector-of-length-ec
  (syntax-rules (nested)
    ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...)
     (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) )
    ((vector-of-length-ec k q1 q2             etc1 etc ...)
     (vector-of-length-ec k (nested q1 q2)    etc1 etc ...) )
    ((vector-of-length-ec k expression)
     (vector-of-length-ec k (nested) expression) )

    ((vector-of-length-ec k qualifier expression)
     (let ((len k))
       (let ((vec (make-vector len))
             (i 0) )
         (do-ec qualifier
                (if (< i len)
                    (begin (vector-set! vec i expression)
                           (set! i (+ i 1)) )
                    (error "vector is too short for the comprehension") ))
         (if (= i len)
             vec
             (error "vector is too long for the comprehension") ))))))


(define-syntax sum-ec
  (syntax-rules ()
    ((sum-ec etc1 etc ...)
     (fold-ec (+) etc1 etc ... +) )))

(define-syntax product-ec
  (syntax-rules ()
    ((product-ec etc1 etc ...)
     (fold-ec (*) etc1 etc ... *) )))

(define-syntax min-ec
  (syntax-rules ()
    ((min-ec etc1 etc ...)
     (fold3-ec (min) etc1 etc ... min min) )))

(define-syntax max-ec
  (syntax-rules ()
    ((max-ec etc1 etc ...)
     (fold3-ec (max) etc1 etc ... max max) )))

(define-syntax last-ec
  (syntax-rules (nested)
    ((last-ec default (nested q1 ...) q etc1 etc ...)
     (last-ec default (nested q1 ... q) etc1 etc ...) )
    ((last-ec default q1 q2             etc1 etc ...)
     (last-ec default (nested q1 q2)    etc1 etc ...) )
    ((last-ec default expression)
     (last-ec default (nested) expression) )

    ((last-ec default qualifier expression)
     (let ((result default))
       (do-ec qualifier (set! result expression))
       result ))))


; ==========================================================================
; The fundamental early-stopping comprehension first-ec
; ==========================================================================

(define-syntax first-ec
  (syntax-rules (nested)
    ((first-ec default (nested q1 ...) q etc1 etc ...)
     (first-ec default (nested q1 ... q) etc1 etc ...) )
    ((first-ec default q1 q2             etc1 etc ...)
     (first-ec default (nested q1 q2)    etc1 etc ...) )
    ((first-ec default expression)
     (first-ec default (nested) expression) )

    ((first-ec default qualifier expression)
     (let ((result default) (stop #f))
       (ec-guarded-do-ec 
         stop 
         (nested qualifier)
         (begin (set! result expression)
                (set! stop #t) ))
       result ))))

; (ec-guarded-do-ec stop (nested q ...) cmd)
;   constructs (do-ec q ... cmd) where the generators gen in q ... are
;   replaced by (:until gen stop).

(define-syntax ec-guarded-do-ec
  (syntax-rules (nested if not and or begin)

    ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd)
     (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) )

    ((ec-guarded-do-ec stop (nested (if test) q ...) cmd)
     (if test (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (not test) q ...) cmd)
     (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd)
     (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd)
     (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd)
     (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested gen q ...) cmd)
     (do-ec 
       (:until gen stop) 
       (ec-guarded-do-ec stop (nested q ...) cmd) ))

    ((ec-guarded-do-ec stop (nested) cmd)
     (do-ec cmd) )))

; Alternative: Instead of modifying the generator with :until, it is
;   possible to use call-with-current-continuation:
;
;   (define-synatx first-ec 
;     ...same as above...
;     ((first-ec default qualifier expression)
;      (call-with-current-continuation 
;       (lambda (cc)
;        (do-ec qualifier (cc expression))
;        default ))) ))
;
;   This is much simpler but not necessarily as efficient.


; ==========================================================================
; The early-stopping comprehensions any?-ec every?-ec
; ==========================================================================

(define-syntax any?-ec
  (syntax-rules (nested)
    ((any?-ec (nested q1 ...) q etc1 etc ...)
     (any?-ec (nested q1 ... q) etc1 etc ...) )
    ((any?-ec q1 q2             etc1 etc ...)
     (any?-ec (nested q1 q2)    etc1 etc ...) )
    ((any?-ec expression)
     (any?-ec (nested) expression) )

    ((any?-ec qualifier expression)
     (first-ec #f qualifier (if expression) #t) )))

(define-syntax every?-ec
  (syntax-rules (nested)
    ((every?-ec (nested q1 ...) q etc1 etc ...)
     (every?-ec (nested q1 ... q) etc1 etc ...) )
    ((every?-ec q1 q2             etc1 etc ...)
     (every?-ec (nested q1 q2)    etc1 etc ...) )
    ((every?-ec expression)
     (every?-ec (nested) expression) )

    ((every?-ec qualifier expression)
     (first-ec #t qualifier (if (not expression)) #f) )))

  

)

Added srfi/s42/ec.scm.































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
; <PLAINTEXT>
; Eager Comprehensions in [outer..inner|expr]-Convention
; ======================================================
;
; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005
; Scheme R5RS (incl. macros), SRFI-23 (error).
;
; Modified by Derick Eddington to be able to be included into an R6RS library.
; 
; Loading the implementation into Scheme48 0.57:
;   ,open srfi-23
;   ,load ec.scm
;
; Loading the implementation into PLT/DrScheme 202:
;   ; File > Open ... "ec.scm", click Execute
;
; Loading the implementation into SCM 5d7:
;   (require 'macro) (require 'record) 
;   (load "ec.scm")
;
; Implementation comments:
;   * All local (not exported) identifiers are named ec-<something>.
;   * This implementation focuses on portability, performance, 
;     readability, and simplicity roughly in this order. Design
;     decisions related to performance are taken for Scheme48.
;   * Alternative implementations, Comments and Warnings are 
;     mentioned after the definition with a heading.


; ==========================================================================
; The fundamental comprehension do-ec
; ==========================================================================
;
; All eager comprehensions are reduced into do-ec and
; all generators are reduced to :do. 
;
; We use the following short names for syntactic variables
;   q    - qualifier
;   cc   - current continuation, thing to call at the end;
;          the CPS is (m (cc ...) arg ...) -> (cc ... expr ...)
;   cmd  - an expression being evaluated for its side-effects
;   expr - an expression
;   gen  - a generator of an eager comprehension
;   ob   - outer binding
;   oc   - outer command
;   lb   - loop binding
;   ne1? - not-end1? (before the payload)
;   ib   - inner binding
;   ic   - inner command
;   ne2? - not-end2? (after the payload)
;   ls   - loop step
;   etc  - more arguments of mixed type


; (do-ec q ... cmd)
;   handles nested, if/not/and/or, begin, :let, and calls generator 
;   macros in CPS to transform them into fully decorated :do.
;   The code generation for a :do is delegated to do-ec:do.

(define-syntax do-ec
  (syntax-rules (nested if not and or begin :do let)

    ; explicit nesting -> implicit nesting
    ((do-ec (nested q ...) etc ...)
     (do-ec q ... etc ...) )

    ; implicit nesting -> fold do-ec
    ((do-ec q1 q2 etc1 etc ...)
     (do-ec q1 (do-ec q2 etc1 etc ...)) )

    ; no qualifiers at all -> evaluate cmd once
    ((do-ec cmd)
     (begin cmd (if #f #f)) )

; now (do-ec q cmd) remains

    ; filter -> make conditional
    ((do-ec (if test) cmd)
     (if test (do-ec cmd)) )
    ((do-ec (not test) cmd)
     (if (not test) (do-ec cmd)) )
    ((do-ec (and test ...) cmd)
     (if (and test ...) (do-ec cmd)) )
    ((do-ec (or test ...) cmd)
     (if (or test ...) (do-ec cmd)) )

    ; begin -> make a sequence
    ((do-ec (begin etc ...) cmd)
     (begin etc ... (do-ec cmd)) )

    ; fully decorated :do-generator -> delegate to do-ec:do
    ((do-ec (:do olet lbs ne1? ilet ne2? lss) cmd)
     (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss)) )

; anything else -> call generator-macro in CPS; reentry at (*)

    ((do-ec (g arg1 arg ...) cmd)
     (g (do-ec:do cmd) arg1 arg ...) )))


; (do-ec:do cmd (:do olet lbs ne1? ilet ne2? lss))
;   generates code for a single fully decorated :do-generator
;   with cmd as payload, taking care of special cases.

(define-syntax do-ec:do
  (syntax-rules (:do let)

    ; reentry point (*) -> generate code
    ((do-ec:do cmd 
               (:do (let obs oc ...) 
                    lbs 
                    ne1? 
                    (let ibs ic ...) 
                    ne2? 
                    (ls ...) ))
     (ec-simplify
       (let obs
         oc ...
         (let loop lbs
           (ec-simplify
             (if ne1?
                 (ec-simplify
                   (let ibs
                      ic ...
                      cmd
                      (ec-simplify
                        (if ne2?
                            (loop ls ...) )))))))))) ))

    
; (ec-simplify <expression>)
;   generates potentially more efficient code for <expression>.
;   The macro handles if, (begin <command>*), and (let () <command>*)
;   and takes care of special cases.

(define-syntax ec-simplify
  (syntax-rules (if not let begin)

; one- and two-sided if

    ; literal <test>
    ((ec-simplify (if #t consequent))
     consequent )
    ((ec-simplify (if #f consequent))
     (if #f #f) )
    ((ec-simplify (if #t consequent alternate))
     consequent )
    ((ec-simplify (if #f consequent alternate))
     alternate )

    ; (not (not <test>))
    ((ec-simplify (if (not (not test)) consequent))
     (ec-simplify (if test consequent)) )
    ((ec-simplify (if (not (not test)) consequent alternate))
     (ec-simplify (if test consequent alternate)) )

; (let () <command>*) 

    ; empty <binding spec>*
    ((ec-simplify (let () command ...))
     (ec-simplify (begin command ...)) )

; begin 

    ; flatten use helper (ec-simplify 1 done to-do)
    ((ec-simplify (begin command ...))
     (ec-simplify 1 () (command ...)) )
    ((ec-simplify 1 done ((begin to-do1 ...) to-do2 ...))
     (ec-simplify 1 done (to-do1 ... to-do2 ...)) )
    ((ec-simplify 1 (done ...) (to-do1 to-do ...))
     (ec-simplify 1 (done ... to-do1) (to-do ...)) )

    ; exit helper
    ((ec-simplify 1 () ())
     (if #f #f) )
    ((ec-simplify 1 (command) ())
     command )
    ((ec-simplify 1 (command1 command ...) ())
     (begin command1 command ...) )

; anything else

    ((ec-simplify expression)
     expression )))


; ==========================================================================
; The special generators :do, :let, :parallel, :while, and :until
; ==========================================================================

(define-syntax :do
  (syntax-rules ()

    ; full decorated -> continue with cc, reentry at (*)
    ((:do (cc ...) olet lbs ne1? ilet ne2? lss)
     (cc ... (:do olet lbs ne1? ilet ne2? lss)) )

    ; short form -> fill in default values
    ((:do cc lbs ne1? lss)
     (:do cc (let ()) lbs ne1? (let ()) #t lss) )))
    

(define-syntax :let
  (syntax-rules (index)
    ((:let cc var (index i) expression)
     (:do cc (let ((var expression) (i 0))) () #t (let ()) #f ()) )
    ((:let cc var expression)
     (:do cc (let ((var expression))) () #t (let ()) #f ()) )))


(define-syntax :parallel
  (syntax-rules (:do)
    ((:parallel cc)
     cc )
    ((:parallel cc (g arg1 arg ...) gen ...)
     (g (:parallel-1 cc (gen ...)) arg1 arg ...) )))

; (:parallel-1 cc (to-do ...) result [ next ] )
;    iterates over to-do by converting the first generator into 
;    the :do-generator next and merging next into result.

(define-syntax :parallel-1  ; used as 
  (syntax-rules (:do let)

    ; process next element of to-do, reentry at (**)
    ((:parallel-1 cc ((g arg1 arg ...) gen ...) result)
     (g (:parallel-1 cc (gen ...) result) arg1 arg ...) )

    ; reentry point (**) -> merge next into result
    ((:parallel-1 
       cc 
       gens 
       (:do (let (ob1 ...) oc1 ...) 
            (lb1 ...) 
            ne1?1 
            (let (ib1 ...) ic1 ...) 
            ne2?1 
            (ls1 ...) )
       (:do (let (ob2 ...) oc2 ...) 
            (lb2 ...) 
            ne1?2 
            (let (ib2 ...) ic2 ...) 
            ne2?2 
            (ls2 ...) ))
     (:parallel-1 
       cc 
       gens 
       (:do (let (ob1 ... ob2 ...) oc1 ... oc2 ...) 
            (lb1 ... lb2 ...) 
            (and ne1?1 ne1?2) 
            (let (ib1 ... ib2 ...) ic1 ... ic2 ...) 
            (and ne2?1 ne2?2) 
            (ls1 ... ls2 ...) )))

    ; no more gens -> continue with cc, reentry at (*)
    ((:parallel-1 (cc ...) () result)
     (cc ... result) )))

(define-syntax :while
  (syntax-rules ()
    ((:while cc (g arg1 arg ...) test)
     (g (:while-1 cc test) arg1 arg ...) )))

; (:while-1 cc test (:do ...))
;    modifies the fully decorated :do-generator such that it
;    runs while test is a true value. 
;       The original implementation just replaced ne1? by
;    (and ne1? test) as follows:
;
;      (define-syntax :while-1
;        (syntax-rules (:do)
;          ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss))
;           (:do cc olet lbs (and ne1? test) ilet ne2? lss) )))
;
;    Unfortunately, this code is wrong because ne1? may depend
;    in the inner bindings introduced in ilet, but ne1? is evaluated
;    outside of the inner bindings. (Refer to the specification of
;    :do to see the structure.) 
;       The problem manifests itself (as sunnan@handgranat.org 
;    observed) when the :list-generator is modified:
; 
;      (do-ec (:while (:list x '(1 2)) (= x 1)) (display x)).
;
;    In order to generate proper code, we introduce temporary
;    variables saving the values of the inner bindings. The inner
;    bindings are executed in a new ne1?, which also evaluates ne1?
;    outside the scope of the inner bindings, then the inner commands
;    are executed (possibly changing the variables), and then the
;    values of the inner bindings are saved and (and ne1? test) is
;    returned. In the new ilet, the inner variables are bound and
;    initialized and their values are restored. So we construct:
;
;     (let (ob .. (ib-tmp #f) ...)
;       oc ...
;       (let loop (lb ...)
;         (if (let (ne1?-value ne1?)
;               (let ((ib-var ib-rhs) ...)
;                 ic ...
;                 (set! ib-tmp ib-var) ...)
;               (and ne1?-value test))
;             (let ((ib-var ib-tmp) ...)
;               /payload/
;               (if ne2?
;                   (loop ls ...) )))))

(define-syntax :while-1
  (syntax-rules (:do let)
    ((:while-1 cc test (:do olet lbs ne1? ilet ne2? lss))
     (:while-2 cc test () () () (:do olet lbs ne1? ilet ne2? lss)))))

(define-syntax :while-2
  (syntax-rules (:do let)
    ((:while-2 cc 
               test 
               (ib-let     ...)
               (ib-save    ...)
               (ib-restore ...)
               (:do olet 
                    lbs 
                    ne1? 
                    (let ((ib-var ib-rhs) ib ...) ic ...)
                    ne2? 
                    lss))
     (:while-2 cc 
               test 
               (ib-let     ... (ib-tmp #f))
               (ib-save    ... (ib-var ib-rhs))
               (ib-restore ... (ib-var ib-tmp))
               (:do olet 
                    lbs 
                    ne1? 
                    (let (ib ...) ic ... (set! ib-tmp ib-var)) 
                    ne2? 
                    lss)))
    ((:while-2 cc
               test
               (ib-let     ...)
               (ib-save    ...)
               (ib-restore ...)
               (:do (let (ob ...) oc ...) lbs ne1? (let () ic ...) ne2? lss))
     (:do cc
          (let (ob ... ib-let ...) oc ...)
          lbs
          (let ((ne1?-value ne1?))
            (let (ib-save ...)
                ic ...
                (and ne1?-value test)))
          (let (ib-restore ...))
          ne2?
          lss))))


(define-syntax :until
  (syntax-rules ()
    ((:until cc (g arg1 arg ...) test)
     (g (:until-1 cc test) arg1 arg ...) )))

(define-syntax :until-1
  (syntax-rules (:do)
    ((:until-1 cc test (:do olet lbs ne1? ilet ne2? lss))
     (:do cc olet lbs ne1? ilet (and ne2? (not test)) lss) )))


; ==========================================================================
; The typed generators :list :string :vector etc.
; ==========================================================================

(define-syntax :list
  (syntax-rules (index)
    ((:list cc var (index i) arg ...)
     (:parallel cc (:list var arg ...) (:integers i)) )
    ((:list cc var arg1 arg2 arg ...)
     (:list cc var (append arg1 arg2 arg ...)) )
    ((:list cc var arg)
     (:do cc
          (let ())
          ((t arg))
          (not (null? t))
          (let ((var (car t))))
          #t
          ((cdr t)) ))))


(define-syntax :string
  (syntax-rules (index)
    ((:string cc var (index i) arg)
     (:do cc
          (let ((str arg) (len 0)) 
            (set! len (string-length str)))
          ((i 0))
          (< i len)
          (let ((var (string-ref str i))))
          #t
          ((+ i 1)) ))
    ((:string cc var (index i) arg1 arg2 arg ...)
     (:string cc var (index i) (string-append arg1 arg2 arg ...)) )
    ((:string cc var arg1 arg ...)
     (:string cc var (index i) arg1 arg ...) )))

; Alternative: An implementation in the style of :vector can also
;   be used for :string. However, it is less interesting as the
;   overhead of string-append is much less than for 'vector-append'.


(define-syntax :vector
  (syntax-rules (index)
    ((:vector cc var arg)
     (:vector cc var (index i) arg) )
    ((:vector cc var (index i) arg)
     (:do cc
          (let ((vec arg) (len 0)) 
            (set! len (vector-length vec)))
          ((i 0))
          (< i len)
          (let ((var (vector-ref vec i))))
          #t
          ((+ i 1)) ))

    ((:vector cc var (index i) arg1 arg2 arg ...)
     (:parallel cc (:vector cc var arg1 arg2 arg ...) (:integers i)) )
    ((:vector cc var arg1 arg2 arg ...)
     (:do cc
          (let ((vec #f)
                (len 0)
                (vecs (ec-:vector-filter (list arg1 arg2 arg ...))) ))
          ((k 0))
          (if (< k len)
              #t
              (if (null? vecs)
                  #f
                  (begin (set! vec (car vecs))
                         (set! vecs (cdr vecs))
                         (set! len (vector-length vec))
                         (set! k 0)
                         #t )))
          (let ((var (vector-ref vec k))))
          #t
          ((+ k 1)) ))))

(define (ec-:vector-filter vecs)
  (if (null? vecs)
      '()
      (if (zero? (vector-length (car vecs)))
          (ec-:vector-filter (cdr vecs))
          (cons (car vecs) (ec-:vector-filter (cdr vecs))) )))

; Alternative: A simpler implementation for :vector uses vector->list
;   append and :list in the multi-argument case. Please refer to the
;   'design.scm' for more details.


(define-syntax :integers
  (syntax-rules (index)
    ((:integers cc var (index i))
     (:do cc ((var 0) (i 0)) #t ((+ var 1) (+ i 1))) )
    ((:integers cc var)
     (:do cc ((var 0)) #t ((+ var 1))) )))


(define-syntax :range
  (syntax-rules (index)

    ; handle index variable and add optional args
    ((:range cc var (index i) arg1 arg ...)
     (:parallel cc (:range var arg1 arg ...) (:integers i)) )
    ((:range cc var arg1)
     (:range cc var 0 arg1 1) )
    ((:range cc var arg1 arg2)
     (:range cc var arg1 arg2 1) )

; special cases (partially evaluated by hand from general case)

    ((:range cc var 0 arg2 1)
     (:do cc
          (let ((b arg2))
            (if (not (and (integer? b) (exact? b)))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" 0 b 1 )))
          ((var 0))
          (< var b)
          (let ())
          #t
          ((+ var 1)) ))

    ((:range cc var 0 arg2 -1)
     (:do cc
          (let ((b arg2))
            (if (not (and (integer? b) (exact? b)))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" 0 b 1 )))
          ((var 0))
          (> var b)
          (let ())
          #t
          ((- var 1)) ))

    ((:range cc var arg1 arg2 1)
     (:do cc
          (let ((a arg1) (b arg2))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b 1 )) )
          ((var a))
          (< var b)
          (let ())
          #t
          ((+ var 1)) ))

    ((:range cc var arg1 arg2 -1)
     (:do cc
          (let ((a arg1) (b arg2) (s -1) (stop 0))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b -1 )) )
          ((var a))
          (> var b)
          (let ())
          #t
          ((- var 1)) ))

; the general case

    ((:range cc var arg1 arg2 arg3)
     (:do cc
          (let ((a arg1) (b arg2) (s arg3) (stop 0))
            (if (not (and (integer? a) (exact? a)
                          (integer? b) (exact? b)
                          (integer? s) (exact? s) ))
                (error 
                   "arguments of :range are not exact integer "
                   "(use :real-range?)" a b s ))
            (if (zero? s)
                (error "step size must not be zero in :range") )
            (set! stop (+ a (* (max 0 (ceiling (/ (- b a) s))) s))) )
          ((var a))
          (not (= var stop))
          (let ())
          #t
          ((+ var s)) ))))

; Comment: The macro :range inserts some code to make sure the values
;   are exact integers. This overhead has proven very helpful for 
;   saving users from themselves.


(define-syntax :real-range
  (syntax-rules (index)

    ; add optional args and index variable
    ((:real-range cc var arg1)
     (:real-range cc var (index i) 0 arg1 1) )
    ((:real-range cc var (index i) arg1)
     (:real-range cc var (index i) 0 arg1 1) )
    ((:real-range cc var arg1 arg2)
     (:real-range cc var (index i) arg1 arg2 1) )
    ((:real-range cc var (index i) arg1 arg2)
     (:real-range cc var (index i) arg1 arg2 1) )
    ((:real-range cc var arg1 arg2 arg3)
     (:real-range cc var (index i) arg1 arg2 arg3) )

    ; the fully qualified case
    ((:real-range cc var (index i) arg1 arg2 arg3)
     (:do cc
          (let ((a arg1) (b arg2) (s arg3) (istop 0))
            (if (not (and (real? a) (real? b) (real? s)))
                (error "arguments of :real-range are not real" a b s) )
            (if (and (exact? a) (or (not (exact? b)) (not (exact? s))))
                (set! a (exact->inexact a)) )
            (set! istop (/ (- b a) s)) )
          ((i 0))
          (< i istop)
          (let ((var (+ a (* s i)))))
          #t
          ((+ i 1)) ))))

; Comment: The macro :real-range adapts the exactness of the start
;   value in case any of the other values is inexact. This is a
;   precaution to avoid (list-ec (: x 0 3.0) x) => '(0 1.0 2.0).

    
(define-syntax :char-range
  (syntax-rules (index)
    ((:char-range cc var (index i) arg1 arg2)
     (:parallel cc (:char-range var arg1 arg2) (:integers i)) )
    ((:char-range cc var arg1 arg2)
     (:do cc
          (let ((imax (char->integer arg2))))
          ((i (char->integer arg1)))
          (<= i imax)
          (let ((var (integer->char i))))
          #t
          ((+ i 1)) ))))

; Warning: There is no R5RS-way to implement the :char-range generator 
;   because the integers obtained by char->integer are not necessarily 
;   consecutive. We simply assume this anyhow for illustration.


(define-syntax :port
  (syntax-rules (index)
    ((:port cc var (index i) arg1 arg ...)
     (:parallel cc (:port var arg1 arg ...) (:integers i)) )
    ((:port cc var arg)
     (:port cc var arg read) )
    ((:port cc var arg1 arg2)
     (:do cc
          (let ((port arg1) (read-proc arg2)))
          ((var (read-proc port)))
          (not (eof-object? var))
          (let ())
          #t
          ((read-proc port)) ))))


; ==========================================================================
; The typed generator :dispatched and utilities for constructing dispatchers
; ==========================================================================

(define-syntax :dispatched
  (syntax-rules (index)
    ((:dispatched cc var (index i) dispatch arg1 arg ...)
     (:parallel cc 
                (:integers i)
                (:dispatched var dispatch arg1 arg ...) ))
    ((:dispatched cc var dispatch arg1 arg ...)
     (:do cc
          (let ((d dispatch) 
                (args (list arg1 arg ...)) 
                (g #f) 
                (empty (list #f)) )
            (set! g (d args))
            (if (not (procedure? g))
                (error "unrecognized arguments in dispatching" 
                       args 
                       (d '()) )))
          ((var (g empty)))
          (not (eq? var empty))
          (let ())
          #t
          ((g empty)) ))))

; Comment: The unique object empty is created as a newly allocated
;   non-empty list. It is compared using eq? which distinguishes
;   the object from any other object, according to R5RS 6.1.


(define-syntax :generator-proc
  (syntax-rules (:do let)

    ; call g with a variable, reentry at (**)
    ((:generator-proc (g arg ...))
     (g (:generator-proc var) var arg ...) )

    ; reentry point (**) -> make the code from a single :do
    ((:generator-proc
       var 
       (:do (let obs oc ...) 
            ((lv li) ...) 
            ne1? 
            (let ((i v) ...) ic ...) 
            ne2? 
            (ls ...)) )
     (ec-simplify 
      (let obs
          oc ...
          (let ((lv li) ... (ne2 #t))
            (ec-simplify
             (let ((i #f) ...) ; v not yet valid
               (lambda (empty)
                 (if (and ne1? ne2)
                     (ec-simplify
                      (begin 
                        (set! i v) ...
                        ic ...
                        (let ((value var))
                          (ec-simplify
                           (if ne2?
                               (ec-simplify 
                                (begin (set! lv ls) ...) )
                               (set! ne2 #f) ))
                          value )))
                     empty ))))))))

    ; silence warnings of some macro expanders
    ((:generator-proc var)
     (error "illegal macro call") )))


(define (dispatch-union d1 d2)
  (lambda (args)
    (let ((g1 (d1 args)) (g2 (d2 args)))
      (if g1
          (if g2 
              (if (null? args)
                  (append (if (list? g1) g1 (list g1)) 
                          (if (list? g2) g2 (list g2)) )
                  (error "dispatching conflict" args (d1 '()) (d2 '())) )
              g1 )
          (if g2 g2 #f) ))))


; ==========================================================================
; The dispatching generator :
; ==========================================================================

(define (make-initial-:-dispatch)
  (lambda (args)
    (case (length args)
      ((0) 'SRFI42)
      ((1) (let ((a1 (car args)))
             (cond
              ((list? a1)
               (:generator-proc (:list a1)) )
              ((string? a1)
               (:generator-proc (:string a1)) )
              ((vector? a1)
               (:generator-proc (:vector a1)) )
              ((and (integer? a1) (exact? a1))
               (:generator-proc (:range a1)) )
              ((real? a1)
               (:generator-proc (:real-range a1)) )
              ((input-port? a1)
               (:generator-proc (:port a1)) )
              (else
               #f ))))
      ((2) (let ((a1 (car args)) (a2 (cadr args)))
             (cond
              ((and (list? a1) (list? a2))
               (:generator-proc (:list a1 a2)) )
              ((and (string? a1) (string? a2))
               (:generator-proc (:string a1 a2)) )
              ((and (vector? a1) (vector? a2))
               (:generator-proc (:vector a1 a2)) )
              ((and (integer? a1) (exact? a1) (integer? a2) (exact? a2))
               (:generator-proc (:range a1 a2)) )
              ((and (real? a1) (real? a2))
               (:generator-proc (:real-range a1 a2)) )
              ((and (char? a1) (char? a2))
               (:generator-proc (:char-range a1 a2)) )
              ((and (input-port? a1) (procedure? a2))
               (:generator-proc (:port a1 a2)) )
              (else
               #f ))))
      ((3) (let ((a1 (car args)) (a2 (cadr args)) (a3 (caddr args)))
             (cond
              ((and (list? a1) (list? a2) (list? a3))
               (:generator-proc (:list a1 a2 a3)) )
              ((and (string? a1) (string? a2) (string? a3))
               (:generator-proc (:string a1 a2 a3)) )
              ((and (vector? a1) (vector? a2) (vector? a3))
               (:generator-proc (:vector a1 a2 a3)) )
              ((and (integer? a1) (exact? a1) 
                    (integer? a2) (exact? a2)
                    (integer? a3) (exact? a3))
               (:generator-proc (:range a1 a2 a3)) )
              ((and (real? a1) (real? a2) (real? a3))
               (:generator-proc (:real-range a1 a2 a3)) )
              (else
               #f ))))
      (else
       (letrec ((every? 
                 (lambda (pred args)
                   (if (null? args)
                       #t
                       (and (pred (car args))
                            (every? pred (cdr args)) )))))
         (cond
          ((every? list? args)
           (:generator-proc (:list (apply append args))) )
          ((every? string? args)
           (:generator-proc (:string (apply string-append args))) )
          ((every? vector? args)
           (:generator-proc (:list (apply append (map vector->list args)))) )
          (else
           #f )))))))

(define :-dispatch 
  (make-parameter (make-initial-:-dispatch)
                  (lambda (x) (if (procedure? x) x (error "not a procedure" x)))))

(define (:-dispatch-ref)
  (:-dispatch))

(define (:-dispatch-set! dispatch)
  (:-dispatch dispatch))

(define-syntax :
  (syntax-rules (index)
    ((: cc var (index i) arg1 arg ...)
     (:dispatched cc var (index i) (:-dispatch) arg1 arg ...) )
    ((: cc var arg1 arg ...)
     (:dispatched cc var (:-dispatch) arg1 arg ...) )))


; ==========================================================================
; The utility comprehensions fold-ec, fold3-ec
; ==========================================================================

(define-syntax fold3-ec
  (syntax-rules (nested)
    ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
     (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
    ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
     (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
    ((fold3-ec x0 expression f1 f2)
     (fold3-ec x0 (nested) expression f1 f2) )

    ((fold3-ec x0 qualifier expression f1 f2)
     (let ((result #f) (empty #t))
       (do-ec qualifier
              (let ((value expression)) ; don't duplicate
                (if empty
                    (begin (set! result (f1 value))
                           (set! empty #f) )
                    (set! result (f2 value result)) )))
       (if empty x0 result) ))))


(define-syntax fold-ec
  (syntax-rules (nested)
    ((fold-ec x0 (nested q1 ...) q etc1 etc2 etc ...)
     (fold-ec x0 (nested q1 ... q) etc1 etc2 etc ...) )
    ((fold-ec x0 q1 q2 etc1 etc2 etc ...)
     (fold-ec x0 (nested q1 q2) etc1 etc2 etc ...) )
    ((fold-ec x0 expression f2)
     (fold-ec x0 (nested) expression f2) )

    ((fold-ec x0 qualifier expression f2)
     (let ((result x0))
       (do-ec qualifier (set! result (f2 expression result)))
       result ))))


; ==========================================================================
; The comprehensions list-ec string-ec vector-ec etc.
; ==========================================================================

(define-syntax list-ec
  (syntax-rules ()
    ((list-ec etc1 etc ...)
     (reverse (fold-ec '() etc1 etc ... cons)) )))

; Alternative: Reverse can safely be replaced by reverse! if you have it.
;
; Alternative: It is possible to construct the result in the correct order
;   using set-cdr! to add at the tail. This removes the overhead of copying
;   at the end, at the cost of more book-keeping.


(define-syntax append-ec
  (syntax-rules ()
    ((append-ec etc1 etc ...)
     (apply append (list-ec etc1 etc ...)) )))

(define-syntax string-ec
  (syntax-rules ()
    ((string-ec etc1 etc ...)
     (list->string (list-ec etc1 etc ...)) )))

; Alternative: For very long strings, the intermediate list may be a
;   problem. A more space-aware implementation collect the characters 
;   in an intermediate list and when this list becomes too large it is
;   converted into an intermediate string. At the end, the intermediate
;   strings are concatenated with string-append.


(define-syntax string-append-ec
  (syntax-rules ()
    ((string-append-ec etc1 etc ...)
     (apply string-append (list-ec etc1 etc ...)) )))

(define-syntax vector-ec
  (syntax-rules ()
    ((vector-ec etc1 etc ...)
     (list->vector (list-ec etc1 etc ...)) )))

; Comment: A similar approach as for string-ec can be used for vector-ec.
;   However, the space overhead for the intermediate list is much lower
;   than for string-ec and as there is no vector-append, the intermediate
;   vectors must be copied explicitly.

(define-syntax vector-of-length-ec
  (syntax-rules (nested)
    ((vector-of-length-ec k (nested q1 ...) q etc1 etc ...)
     (vector-of-length-ec k (nested q1 ... q) etc1 etc ...) )
    ((vector-of-length-ec k q1 q2             etc1 etc ...)
     (vector-of-length-ec k (nested q1 q2)    etc1 etc ...) )
    ((vector-of-length-ec k expression)
     (vector-of-length-ec k (nested) expression) )

    ((vector-of-length-ec k qualifier expression)
     (let ((len k))
       (let ((vec (make-vector len))
             (i 0) )
         (do-ec qualifier
                (if (< i len)
                    (begin (vector-set! vec i expression)
                           (set! i (+ i 1)) )
                    (error "vector is too short for the comprehension") ))
         (if (= i len)
             vec
             (error "vector is too long for the comprehension") ))))))


(define-syntax sum-ec
  (syntax-rules ()
    ((sum-ec etc1 etc ...)
     (fold-ec (+) etc1 etc ... +) )))

(define-syntax product-ec
  (syntax-rules ()
    ((product-ec etc1 etc ...)
     (fold-ec (*) etc1 etc ... *) )))

(define-syntax min-ec
  (syntax-rules ()
    ((min-ec etc1 etc ...)
     (fold3-ec (min) etc1 etc ... min min) )))

(define-syntax max-ec
  (syntax-rules ()
    ((max-ec etc1 etc ...)
     (fold3-ec (max) etc1 etc ... max max) )))

(define-syntax last-ec
  (syntax-rules (nested)
    ((last-ec default (nested q1 ...) q etc1 etc ...)
     (last-ec default (nested q1 ... q) etc1 etc ...) )
    ((last-ec default q1 q2             etc1 etc ...)
     (last-ec default (nested q1 q2)    etc1 etc ...) )
    ((last-ec default expression)
     (last-ec default (nested) expression) )

    ((last-ec default qualifier expression)
     (let ((result default))
       (do-ec qualifier (set! result expression))
       result ))))


; ==========================================================================
; The fundamental early-stopping comprehension first-ec
; ==========================================================================

(define-syntax first-ec
  (syntax-rules (nested)
    ((first-ec default (nested q1 ...) q etc1 etc ...)
     (first-ec default (nested q1 ... q) etc1 etc ...) )
    ((first-ec default q1 q2             etc1 etc ...)
     (first-ec default (nested q1 q2)    etc1 etc ...) )
    ((first-ec default expression)
     (first-ec default (nested) expression) )

    ((first-ec default qualifier expression)
     (let ((result default) (stop #f))
       (ec-guarded-do-ec 
         stop 
         (nested qualifier)
         (begin (set! result expression)
                (set! stop #t) ))
       result ))))

; (ec-guarded-do-ec stop (nested q ...) cmd)
;   constructs (do-ec q ... cmd) where the generators gen in q ... are
;   replaced by (:until gen stop).

(define-syntax ec-guarded-do-ec
  (syntax-rules (nested if not and or begin)

    ((ec-guarded-do-ec stop (nested (nested q1 ...) q2 ...) cmd)
     (ec-guarded-do-ec stop (nested q1 ... q2 ...) cmd) )

    ((ec-guarded-do-ec stop (nested (if test) q ...) cmd)
     (if test (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (not test) q ...) cmd)
     (if (not test) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (and test ...) q ...) cmd)
     (if (and test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )
    ((ec-guarded-do-ec stop (nested (or test ...) q ...) cmd)
     (if (or test ...) (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested (begin etc ...) q ...) cmd)
     (begin etc ... (ec-guarded-do-ec stop (nested q ...) cmd)) )

    ((ec-guarded-do-ec stop (nested gen q ...) cmd)
     (do-ec 
       (:until gen stop) 
       (ec-guarded-do-ec stop (nested q ...) cmd) ))

    ((ec-guarded-do-ec stop (nested) cmd)
     (do-ec cmd) )))

; Alternative: Instead of modifying the generator with :until, it is
;   possible to use call-with-current-continuation:
;
;   (define-synatx first-ec 
;     ...same as above...
;     ((first-ec default qualifier expression)
;      (call-with-current-continuation 
;       (lambda (cc)
;        (do-ec qualifier (cc expression))
;        default ))) ))
;
;   This is much simpler but not necessarily as efficient.


; ==========================================================================
; The early-stopping comprehensions any?-ec every?-ec
; ==========================================================================

(define-syntax any?-ec
  (syntax-rules (nested)
    ((any?-ec (nested q1 ...) q etc1 etc ...)
     (any?-ec (nested q1 ... q) etc1 etc ...) )
    ((any?-ec q1 q2             etc1 etc ...)
     (any?-ec (nested q1 q2)    etc1 etc ...) )
    ((any?-ec expression)
     (any?-ec (nested) expression) )

    ((any?-ec qualifier expression)
     (first-ec #f qualifier (if expression) #t) )))

(define-syntax every?-ec
  (syntax-rules (nested)
    ((every?-ec (nested q1 ...) q etc1 etc ...)
     (every?-ec (nested q1 ... q) etc1 etc ...) )
    ((every?-ec q1 q2             etc1 etc ...)
     (every?-ec (nested q1 q2)    etc1 etc ...) )
    ((every?-ec expression)
     (every?-ec (nested) expression) )

    ((every?-ec qualifier expression)
     (first-ec #t qualifier (if (not expression)) #f) )))

Added srfi/s42/extension.scm.









































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
; <PLAINTEXT>
; Examples for Application Specific Extensions of Eager Comprehensions
; ====================================================================
;
; sebastian.egner@philips.com, Eindhoven, The Netherlands, Feb-2003.
; Scheme R5RS (incl. macros), SRFI-23 (error).
; 
; Running the extensions in Scheme48 (version 0.57):
;   ; load "examples.scm" as described there
;   ,load extension.scm
;
; Running the extensions in PLT (version 202):
;   ; load "examples.scm" as described there
;   (load "extension.scm")
;
; Running the extensions in SCM (version 5d7):
;   ; load "examples.scm" as described there
;   (load "extension.scm")

; reset SRFI

(set! :-dispatch (make-initial-:-dispatch))

(define my-check-correct 0)
(define my-check-wrong   0)


; ==========================================================================
; Extending the predefined dispatching generator
; ==========================================================================

; example from SRFI document (for :dispatch)

(define (example-dispatch args)
  (cond
   ((null? args)
    'example )
   ((and (= (length args) 1) (symbol? (car args)) )
    (:generator-proc (:string (symbol->string (car args)))) )
   (else
    #f )))

(:-dispatch-set! (dispatch-union (:-dispatch-ref) example-dispatch))

; run the example

(my-check (list-ec (: c 'abc) c) => '(#\a #\b #\c))


; ==========================================================================
; Adding an application specific dispatching generator
; ==========================================================================

; example from SRFI document (for :dispatch)

(define (:my-dispatch args)
  (case (length args)
    ((0) 'example)
    ((1) (let ((a1 (car args)))
           (cond
            ((list? a1)
             (:generator-proc (:list a1)) )
            ((string? a1)
             (:generator-proc (:string a1)) )
;           ...more unary cases...
            (else
             #f ))))
    ((2) (let ((a1 (car args)) (a2 (cadr args)))
           (cond
            ((and (list? a1) (list? a2))
             (:generator-proc (:list a1 a2)) )
;           ...more binary cases...
            (else
             #f ))))
;   ...more arity cases...
    (else
     (cond
      ((every?-ec (:list a args) (list? a))
       (:generator-proc (:list (apply append args))) )
;     ...more large variable arity cases...
      (else
       #f )))))

(define-syntax :my
  (syntax-rules (index)
    ((:my cc var (index i) arg1 arg ...)
     (:dispatched cc var (index i) :my-dispatch arg1 arg ...) )
    ((:my cc var arg1 arg ...)
     (:dispatched cc var :my-dispatch arg1 arg ...) )))

; run the example

(my-check (list-ec (:my x "abc") x) => '(#\a #\b #\c))

(my-check (list-ec (:my x '(1) '(2) '(3)) x) => '(1 2 3))

(my-check 
  (list-ec (:my x (index i) "abc") (list x i)) 
  => '((#\a 0) (#\b 1) (#\c 2)) )


; ==========================================================================
; Adding an application specific typed generator
; ==========================================================================

; example from SRFI document

(define-syntax :mygen
  (syntax-rules ()
    ((:mygen cc var arg)
     (:list cc var (reverse arg)) )))

; run the example

(my-check (list-ec (:mygen x '(1 2 3)) x) => '(3 2 1))


; ==========================================================================
; Adding application specific comprehensions
; ==========================================================================

; example from SRFI document

(define-syntax new-list-ec
  (syntax-rules ()
    ((new-list-ec etc1 etc ...)
     (reverse (fold-ec '() etc1 etc ... cons)) )))

(define-syntax new-min-ec
  (syntax-rules ()
    ((new-min-ec etc1 etc ...)
     (fold3-ec (min) etc1 etc ... min min) )))

(define-syntax new-fold3-ec
  (syntax-rules (nested)
    ((new-fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
     (new-fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
    ((new-fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
     (new-fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
    ((new-fold3-ec x0 expression f1 f2)
     (new-fold3-ec x0 (nested) expression f1 f2) )

    ((new-fold3-ec x0 qualifier expression f1 f2)
     (let ((result #f) (empty #t))
       (do-ec qualifier
              (let ((value expression)) ; don't duplicate
                (if empty
                    (begin (set! result (f1 value))
                           (set! empty #f) )
                    (set! result (f2 value result)) )))
       (if empty x0 result) ))))

; run the example

(my-check (new-list-ec (: i 5) i) => '(0 1 2 3 4))

(my-check (new-min-ec (: i 5) i) => 0)

(my-check 
 (let ((f1 (lambda (x) (list 'f1 x)))
       (f2 (lambda (x result) (list 'f2 x result))) )
   (new-fold3-ec (error "bad") (: i 5) i f1 f2) )
 => '(f2 4 (f2 3 (f2 2 (f2 1 (f1 0))))) )


; ==========================================================================
; Summary
; ==========================================================================

(begin
  (newline)
  (newline)
  (display "correct examples : ")
  (display my-check-correct)
  (newline)
  (display "wrong examples   : ")
  (display my-check-wrong)
  (newline)
  (newline) )

Added srfi/s42/timing.scm.









































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
; <PLAINTEXT>
; Timing for Eager Comprehensions in [outer..inner|expr]-Convention
; =================================================================
;
; sebastian.egner@philips.com, Eindhoven, The Netherlands, Feb-2003.
; Scheme R5RS (incl. macros), SRFI-23 (error).
; 
; Running the examples in Scheme48 (version 0.57):
;   ,open srfi-23
;   ,load ec.scm
;   ,load timing.scm
;
; Running the examples in PLT/DrScheme (version 202): 
;   ; open "ec.scm", click Execute
;   (load "timing.scm")
;
; Running the examples in SCM (version 5d7):
;   ; invoke SCM with -v on the command line
;   (require 'macro) (require 'record)
;   (load "ec.scm")
;   (load "timing.scm")


; =======================================================================
; Basic loops
; =======================================================================
;
; We measure execution times for (:range var n) and for (: var n),
; both as an outer loop (to measure iteration speed) or as an inner 
; loop (to measure start up overhead). For comparison the same is
; measured for a hand-coded DO-loop.

(define (perf0 n) ; reference for loop duration
  (do ((i 0 (+ i 1)))
      ((= i n))
    i))

(define (perf1 n)
  (do-ec (:range i n) i))

(define (perf2 n)
  (do-ec (: i n) i))

(define (perf0s n) ; reference for startup delay
  (do-ec (:range i n)
         (do ((i 0 (+ i 1)))
             ((= i 1))
           i)))

(define (perf1s n)
  (do-ec (:range i n) (:range j 1) i))

(define (perf2s n)
  (do-ec (:range i n) (: j 1) i))

(define n-perf 
  10000000)


; Scheme48 0.57 on HP 9000/800 server running HP-UX
; -------------------------------------------------
;
; ,time (perf0  n-perf)   19.3   ; built-in do
; ,time (perf1  n-perf)   17.0   ; faster than built-in do (?)
; ,time (perf2  n-perf)   40.4   ; due to calling the generator as procedure
;
; ,time (perf0s n-perf)   57.5   ; built-in do in the inner loop
; ,time (perf1s n-perf)   78.5   ; due to checking exact? integer?
; ,time (perf2s n-perf)  274.0   ; due to dispatch mechanism
;
; [All times are CPU time in seconds for n-perf iterations.]


; PLT 202 on Pentium III Mobile, 1 GHz, 1 GB RAM, Windows 2k
; ----------------------------------------------------------
;
; (time (perf0  n-perf))   11.1
; (time (perf1  n-perf))    7.8
; (time (perf2  n-perf))   18.1
;
; (time (perf0s n-perf))   35.2
; (time (perf1s n-perf))   42.9
; (time (perf2s n-perf))  147.8
; 
; [All times are CPU time in seconds for n-perf iterations.]


; SCM 5d7 on Pentium III Mobile, 1 GHz, 1 GB RAM, Windows 2k
; ----------------------------------------------------------
;
; (perf0  n-perf)   29.1
; (perf1  n-perf)   30.0
; (perf2  n-perf)   45.5
;
; (perf0s n-perf)   79.2
; (perf1s n-perf)  448.6
; (perf2s n-perf)  756.2
; 
; [All times are CPU time in seconds for n-perf iterations.]

Added srfi/s43/_vectors-a.sls.





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s43 vectors)
  (export
    ;;; * Constructors
    make-vector vector
    vector-unfold         vector-unfold-right
    vector-copy
    vector-reverse-copy
    vector-append         vector-concatenate
    ;;; * Predicates
    vector?
    vector-empty?
    vector=
    ;;; * Selectors
    vector-ref
    vector-length
    ;;; * Iteration
    vector-fold           vector-fold-right
    vector-map            vector-map!
    vector-for-each
    vector-count
    ;;; * Searching
    vector-index          vector-skip
    vector-index-right    vector-skip-right
    vector-binary-search  vector-any    vector-every
    ;;; * Mutators
    vector-set!
    vector-swap!
    ;; (rename (my:vector-fill! vector-fill!))
    vector-fill!
    vector-reverse!
    vector-copy!          vector-reverse-copy!
    ;;; * Conversion
    ;; (rename (my:vector->list vector->list))
    vector->list reverse-vector->list
    ;; (rename (my:list->vector list->vector))
    list->vector
    reverse-list->vector )
  (import
    (except (rnrs) vector-map vector-for-each vector-fill! vector->list
            list->vector)
    (rnrs r5rs)
    ;; (srfi s23 error tricks)
    (srfi s8 receive)
    ;; (for (srfi private vanish) expand)
    ;; (srfi private include)

    )

  ;; I do these let-syntax tricks so the original vector-lib.scm file does
  ;; not have to be modified at all.
  ;; (let-syntax
  ;;     ((define
  ;;       (let ((vd (vanish-define define
  ;;                  (make-vector vector vector? vector-ref vector-set! vector-length))))
  ;;         (lambda (stx)
  ;;           (define (rename? id)
  ;;             (memp (lambda (x) (free-identifier=? id x))
  ;;                   (list #'vector-fill! #'vector->list #'list->vector)))
  ;;           (define (rename id)
  ;;             (datum->syntax id
  ;;              (string->symbol
  ;;               (string-append "my:" (symbol->string (syntax->datum id))))))
  ;;           (syntax-case stx ()
  ;;             ((_ name . r)
  ;;              (and (identifier? #'name)
  ;;                   (rename? #'name))
  ;;              #`(define #,(rename #'name) . r))
  ;;             (_ (vd stx))))))
  ;;      (define-syntax
  ;;       (vanish-define define-syntax
  ;;        (receive))))
  ;;   (SRFI-23-error->R6RS "(library (srfi s43 vectors))"
  ;;    (include/resolve ("srfi" "s43") "vector-lib.scm")))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;; SRFI 43: Vector library                           -*- Scheme -*-
;;;
;;; Taylor Campbell wrote this code; he places it in the public domain.
;;; Will Clinger [wdc] made some corrections, also in the public domain.

;;; --------------------
;;; Exported procedure index
;;;
;;; * Constructors
;;; make-vector vector
;;; vector-unfold                   vector-unfold-right
;;; vector-copy                     vector-reverse-copy
;;; vector-append                   vector-concatenate
;;;
;;; * Predicates
;;; vector?
;;; vector-empty?
;;; vector=
;;;
;;; * Selectors
;;; vector-ref
;;; vector-length
;;;
;;; * Iteration
;;; vector-fold                     vector-fold-right
;;; vector-map                      vector-map!
;;; vector-for-each
;;; vector-count
;;;
;;; * Searching
;;; vector-index                    vector-skip
;;; vector-index-right              vector-skip-right
;;; vector-binary-search
;;; vector-any                      vector-every
;;;
;;; * Mutators
;;; vector-set!
;;; vector-swap!
;;; vector-fill!
;;; vector-reverse!
;;; vector-copy!                    vector-reverse-copy!
;;; vector-reverse!
;;;
;;; * Conversion
;;; vector->list                    reverse-vector->list
;;; list->vector                    reverse-list->vector

 

;;; --------------------
;;; Commentary on efficiency of the code

;;; This code is somewhat tuned for efficiency.  There are several
;;; internal routines that can be optimized greatly to greatly improve
;;; the performance of much of the library.  These internal procedures
;;; are already carefully tuned for performance, and lambda-lifted by
;;; hand.  Some other routines are lambda-lifted by hand, but only the
;;; loops are lambda-lifted, and only if some routine has two possible
;;; loops -- a fast path and an n-ary case --, whereas _all_ of the
;;; internal routines' loops are lambda-lifted so as to never cons a
;;; closure in their body (VECTOR-PARSE-START+END doesn't have a loop),
;;; even in Scheme systems that perform no loop optimization (which is
;;; most of them, unfortunately).
;;;
;;; Fast paths are provided for common cases in most of the loops in
;;; this library.
;;;
;;; All calls to primitive vector operations are protected by a prior
;;; type check; they can be safely converted to use unsafe equivalents
;;; of the operations, if available.  Ideally, the compiler should be
;;; able to determine this, but the state of Scheme compilers today is
;;; not a happy one.
;;;
;;; Efficiency of the actual algorithms is a rather mundane point to
;;; mention; vector operations are rarely beyond being straightforward.

 

;;; --------------------
;;; Utilities

;;; SRFI 8, too trivial to put in the dependencies list.
;; (define-syntax receive
;;   (syntax-rules ()
;;     ((receive ?formals ?producer ?body1 ?body2 ...)
;;      (call-with-values (lambda () ?producer)
;;        (lambda ?formals ?body1 ?body2 ...)))))

;;; Not the best LET*-OPTIONALS, but not the worst, either.  Use Olin's
;;; if it's available to you.
(define-syntax let*-optionals
  (syntax-rules ()
    ((let*-optionals (?x ...) ((?var ?default) ...) ?body1 ?body2 ...)
     (let ((args (?x ...)))
       (let*-optionals args ((?var ?default) ...) ?body1 ?body2 ...)))
    ((let*-optionals ?args ((?var ?default) ...) ?body1 ?body2 ...)
     (let*-optionals:aux ?args ?args ((?var ?default) ...)
       ?body1 ?body2 ...))))

(define-syntax let*-optionals:aux
  (syntax-rules ()
    ((aux ?orig-args-var ?args-var () ?body1 ?body2 ...)
     (if (null? ?args-var)
         (let () ?body1 ?body2 ...)
         (error "too many arguments" (length ?orig-args-var)
                ?orig-args-var)))
    ((aux ?orig-args-var ?args-var
         ((?var ?default) ?more ...)
       ?body1 ?body2 ...)
     (if (null? ?args-var)
         (let* ((?var ?default) ?more ...) ?body1 ?body2 ...)
         (let ((?var (car ?args-var))
               (new-args (cdr ?args-var)))
           (let*-optionals:aux ?orig-args-var new-args
               (?more ...)
             ?body1 ?body2 ...))))))

(define (nonneg-int? x)
  (and (integer? x)
       (not (negative? x))))

(define (between? x y z)
  (and (<  x y)
       (<= y z)))

(define (unspecified-value) (if #f #f))

;++ This should be implemented more efficiently.  It shouldn't cons a
;++ closure, and the cons cells used in the loops when using this could
;++ be reused.
(define (vectors-ref vectors i)
  (map (lambda (v) (vector-ref v i)) vectors))

 

;;; --------------------
;;; Error checking

;;; Error signalling (not checking) is done in a way that tries to be
;;; as helpful to the person who gets the debugging prompt as possible.
;;; That said, error _checking_ tries to be as unredundant as possible.

;;; I don't use any sort of general condition mechanism; I use simply
;;; SRFI 23's ERROR, even in cases where it might be better to use such
;;; a general condition mechanism.  Fix that when porting this to a
;;; Scheme implementation that has its own condition system.

;;; In argument checks, upon receiving an invalid argument, the checker
;;; procedure recursively calls itself, but in one of the arguments to
;;; itself is a call to ERROR; this mechanism is used in the hopes that
;;; the user may be thrown into a debugger prompt, proceed with another
;;; value, and let it be checked again.

;;; Type checking is pretty basic, but easily factored out and replaced
;;; with whatever your implementation's preferred type checking method
;;; is.  I doubt there will be many other methods of index checking,
;;; though the index checkers might be better implemented natively.

;;; (CHECK-TYPE <type-predicate?> <value> <callee>) -> value
;;;   Ensure that VALUE satisfies TYPE-PREDICATE?; if not, signal an
;;;   error stating that VALUE did not satisfy TYPE-PREDICATE?, showing
;;;   that this happened while calling CALLEE.  Return VALUE if no
;;;   error was signalled.
(define (check-type pred? value callee)
  (if (pred? value)
      value
      ;; Recur: when (or if) the user gets a debugger prompt, he can
      ;; proceed where the call to ERROR was with the correct value.
      (check-type pred?
                  (error "erroneous value"
                         (list pred? value)
                         `(while calling ,callee))
                  callee)))

;;; (CHECK-INDEX <vector> <index> <callee>) -> index
;;;   Ensure that INDEX is a valid index into VECTOR; if not, signal an
;;;   error stating that it is not and that this happened in a call to
;;;   CALLEE.  Return INDEX when it is valid.  (Note that this does NOT
;;;   check that VECTOR is indeed a vector.)
(define (check-index vec index callee)
  (let ((index (check-type integer? index callee)))
    (cond ((< index 0)
           (check-index vec
                        (error "vector index too low"
                               index
                               `(into vector ,vec)
                               `(while calling ,callee))
                        callee))
          ((>= index (vector-length vec))
           (check-index vec
                        (error "vector index too high"
                               index
                               `(into vector ,vec)
                               `(while calling ,callee))
                        callee))
          (else index))))

;;; (CHECK-INDICES <vector>
;;;                <start> <start-name>
;;;                <end> <end-name>
;;;                <caller>) -> [start end]
;;;   Ensure that START and END are valid bounds of a range within
;;;   VECTOR; if not, signal an error stating that they are not, with
;;;   the message being informative about what the argument names were
;;;   called -- by using START-NAME & END-NAME --, and that it occurred
;;;   while calling CALLEE.  Also ensure that VEC is in fact a vector.
;;;   Returns no useful value.
(define (check-indices vec start start-name end end-name callee)
  (let ((lose (lambda things
                (apply error "vector range out of bounds"
                       (append things
                               `(vector was ,vec)
                               `(,start-name was ,start)
                               `(,end-name was ,end)
                               `(while calling ,callee)))))
        (start (check-type integer? start callee))
        (end   (check-type integer? end   callee)))
    (cond ((> start end)
           ;; I'm not sure how well this will work.  The intent is that
           ;; the programmer tells the debugger to proceed with both a
           ;; new START & a new END by returning multiple values
           ;; somewhere.
           (receive (new-start new-end)
                    (lose `(,end-name < ,start-name))
             (check-indices vec
                            new-start start-name
                            new-end end-name
                            callee)))
          ((< start 0)
           (check-indices vec
                          (lose `(,start-name < 0))
                          start-name
                          end end-name
                          callee))
          ((>= start (vector-length vec))
           (check-indices vec
                          (lose `(,start-name > len)
                                `(len was ,(vector-length vec)))
                          start-name
                          end end-name
                          callee))
          ((> end (vector-length vec))
           (check-indices vec
                          start start-name
                          (lose `(,end-name > len)
                                `(len was ,(vector-length vec)))
                          end-name
                          callee))
          (else
           (values start end)))))

 

;;; --------------------
;;; Internal routines

;;; These should all be integrated, native, or otherwise optimized --
;;; they're used a _lot_ --.  All of the loops and LETs inside loops
;;; are lambda-lifted by hand, just so as not to cons closures in the
;;; loops.  (If your compiler can do better than that if they're not
;;; lambda-lifted, then lambda-drop (?) them.)

;;; (VECTOR-PARSE-START+END <vector> <arguments>
;;;                         <start-name> <end-name>
;;;                         <callee>)
;;;       -> [start end]
;;;   Return two values, composing a valid range within VECTOR, as
;;;   extracted from ARGUMENTS or defaulted from VECTOR -- 0 for START
;;;   and the length of VECTOR for END --; START-NAME and END-NAME are
;;;   purely for error checking.
(define (vector-parse-start+end vec args start-name end-name callee)
  (let ((len (vector-length vec)))
    (cond ((null? args)
           (values 0 len))
          ((null? (cdr args))
           (check-indices vec
                          (car args) start-name
                          len end-name
                          callee))
          ((null? (cddr args))
           (check-indices vec
                          (car  args) start-name
                          (cadr args) end-name
                          callee))
          (else
           (error "too many arguments"
                  `(extra args were ,(cddr args))
                  `(while calling ,callee))))))

(define-syntax let-vector-start+end
  (syntax-rules ()
    ((let-vector-start+end ?callee ?vec ?args (?start ?end)
       ?body1 ?body2 ...)
     (let ((?vec (check-type vector? ?vec ?callee)))
       (receive (?start ?end)
                (vector-parse-start+end ?vec ?args '?start '?end
                                        ?callee)
         ?body1 ?body2 ...)))))

;;; (%SMALLEST-LENGTH <vector-list> <default-length> <callee>)
;;;       -> exact, nonnegative integer
;;;   Compute the smallest length of VECTOR-LIST.  DEFAULT-LENGTH is
;;;   the length that is returned if VECTOR-LIST is empty.  Common use
;;;   of this is in n-ary vector routines:
;;;     (define (f vec . vectors)
;;;       (let ((vec (check-type vector? vec f)))
;;;         ...(%smallest-length vectors (vector-length vec) f)...))
;;;   %SMALLEST-LENGTH takes care of the type checking -- which is what
;;;   the CALLEE argument is for --; thus, the design is tuned for
;;;   avoiding redundant type checks.
(define %smallest-length
  (letrec ((loop (lambda (vector-list length callee)
                   (if (null? vector-list)
                       length
                       (loop (cdr vector-list)
                             (min (vector-length
                                   (check-type vector?
                                               (car vector-list)
                                               callee))
                                  length)
                             callee)))))
    loop))

;;; (%VECTOR-COPY! <target> <tstart> <source> <sstart> <send>)
;;;   Copy elements at locations SSTART to SEND from SOURCE to TARGET,
;;;   starting at TSTART in TARGET.
;;;
;;; Optimize this!  Probably with some combination of:
;;;   - Force it to be integrated.
;;;   - Let it use unsafe vector element dereferencing routines: bounds
;;;     checking already happens outside of it.  (Or use a compiler
;;;     that figures this out, but Olin Shivers' PhD thesis seems to
;;;     have been largely ignored in actual implementations...)
;;;   - Implement it natively as a VM primitive: the VM can undoubtedly
;;;     perform much faster than it can make Scheme perform, even with
;;;     bounds checking.
;;;   - Implement it in assembly: you _want_ the fine control that
;;;     assembly can give you for this.
;;; I already lambda-lift it by hand, but you should be able to make it
;;; even better than that.
(define %vector-copy!
  (letrec ((loop/l->r (lambda (target source send i j)
                        (cond ((< i send)
                               (vector-set! target j
                                            (vector-ref source i))
                               (loop/l->r target source send
                                          (+ i 1) (+ j 1))))))
           (loop/r->l (lambda (target source sstart i j)
                        (cond ((>= i sstart)
                               (vector-set! target j
                                            (vector-ref source i))
                               (loop/r->l target source sstart
                                          (- i 1) (- j 1)))))))
    (lambda (target tstart source sstart send)
      (if (> sstart tstart)             ; Make sure we don't copy over
                                        ;   ourselves.
          (loop/l->r target source send sstart tstart)
          (loop/r->l target source sstart (- send 1)
                     (+ -1 tstart send (- sstart)))))))

;;; (%VECTOR-REVERSE-COPY! <target> <tstart> <source> <sstart> <send>)
;;;   Copy elements from SSTART to SEND from SOURCE to TARGET, in the
;;;   reverse order.
(define %vector-reverse-copy!
  (letrec ((loop (lambda (target source sstart i j)
                   (cond ((>= i sstart)
                          (vector-set! target j (vector-ref source i))
                          (loop target source sstart
                                (- i 1)
                                (+ j 1)))))))
    (lambda (target tstart source sstart send)
      (loop target source sstart
            (- send 1)
            tstart))))

;;; (%VECTOR-REVERSE! <vector>)
(define %vector-reverse!
  (letrec ((loop (lambda (vec i j)
                   (cond ((<= i j)
                          (let ((v (vector-ref vec i)))
                            (vector-set! vec i (vector-ref vec j))
                            (vector-set! vec j v)
                            (loop vec (+ i 1) (- j 1))))))))
    (lambda (vec start end)
      (loop vec start (- end 1)))))

;;; (%VECTOR-FOLD1 <kons> <knil> <vector>) -> knil'
;;;     (KONS <index> <knil> <elt>) -> knil'
(define %vector-fold1
  (letrec ((loop (lambda (kons knil len vec i)
                   (if (= i len)
                       knil
                       (loop kons
                             (kons i knil (vector-ref vec i))
                             len vec (+ i 1))))))
    (lambda (kons knil len vec)
      (loop kons knil len vec 0))))

;;; (%VECTOR-FOLD2+ <kons> <knil> <vector> ...) -> knil'
;;;     (KONS <index> <knil> <elt> ...) -> knil'
(define %vector-fold2+
  (letrec ((loop (lambda (kons knil len vectors i)
                   (if (= i len)
                       knil
                       (loop kons
                             (apply kons i knil
                                    (vectors-ref vectors i))
                             len vectors (+ i 1))))))
    (lambda (kons knil len vectors)
      (loop kons knil len vectors 0))))

;;; (%VECTOR-MAP! <f> <target> <length> <vector>) -> target
;;;     (F <index> <elt>) -> elt'
(define %vector-map1!
  (letrec ((loop (lambda (f target vec i)
                   (if (zero? i)
                       target
                       (let ((j (- i 1)))
                         (vector-set! target j
                                      (f j (vector-ref vec j)))
                         (loop f target vec j))))))
    (lambda (f target vec len)
      (loop f target vec len))))

;;; (%VECTOR-MAP2+! <f> <target> <vectors> <len>) -> target
;;;     (F <index> <elt> ...) -> elt'
(define %vector-map2+!
  (letrec ((loop (lambda (f target vectors i)
                   (if (zero? i)
                       target
                       (let ((j (- i 1)))
                         (vector-set! target j
                           (apply f j (vectors-ref vectors j)))
                         (loop f target vectors j))))))
    (lambda (f target vectors len)
      (loop f target vectors len))))

 

;;;;;;;;;;;;;;;;;;;;;;;; ***** vector-lib ***** ;;;;;;;;;;;;;;;;;;;;;;;

;;; --------------------
;;; Constructors

;;; (MAKE-VECTOR <size> [<fill>]) -> vector
;;;   [R5RS] Create a vector of length LENGTH.  If FILL is present,
;;;   initialize each slot in the vector with it; if not, the vector's
;;;   initial contents are unspecified.
;; (define make-vector make-vector)

;;; (VECTOR <elt> ...) -> vector
;;;   [R5RS] Create a vector containing ELEMENT ..., in order.
;; (define vector vector)

;;; This ought to be able to be implemented much more efficiently -- if
;;; we have the number of arguments available to us, we can create the
;;; vector without using LENGTH to determine the number of elements it
;;; should have.
;(define (vector . elements) (list->vector elements))

;;; (VECTOR-UNFOLD <f> <length> <initial-seed> ...) -> vector
;;;     (F <index> <seed> ...) -> [elt seed' ...]
;;;   The fundamental vector constructor.  Creates a vector whose
;;;   length is LENGTH and iterates across each index K between 0 and
;;;   LENGTH, applying F at each iteration to the current index and the
;;;   current seeds to receive N+1 values: first, the element to put in
;;;   the Kth slot and then N new seeds for the next iteration.
(define vector-unfold
  (letrec ((tabulate!                   ; Special zero-seed case.
            (lambda (f vec i len)
              (cond ((< i len)
                     (vector-set! vec i (f i))
                     (tabulate! f vec (+ i 1) len)))))
           (unfold1!                    ; Fast path for one seed.
            (lambda (f vec i len seed)
              (if (< i len)
                  (receive (elt new-seed)
                           (f i seed)
                    (vector-set! vec i elt)
                    (unfold1! f vec (+ i 1) len new-seed)))))
           (unfold2+!                   ; Slower variant for N seeds.
            (lambda (f vec i len seeds)
              (if (< i len)
                  (receive (elt . new-seeds)
                           (apply f i seeds)
                    (vector-set! vec i elt)
                    (unfold2+! f vec (+ i 1) len new-seeds))))))
    (lambda (f len . initial-seeds)
      (let ((f   (check-type procedure?  f   vector-unfold))
            (len (check-type nonneg-int? len vector-unfold)))
        (let ((vec (make-vector len)))
          (cond ((null? initial-seeds)
                 (tabulate! f vec 0 len))
                ((null? (cdr initial-seeds))
                 (unfold1! f vec 0 len (car initial-seeds)))
                (else
                 (unfold2+! f vec 0 len initial-seeds)))
          vec)))))

;;; (VECTOR-UNFOLD-RIGHT <f> <length> <initial-seed> ...) -> vector
;;;     (F <seed> ...) -> [seed' ...]
;;;   Like VECTOR-UNFOLD, but it generates elements from LENGTH to 0
;;;   (still exclusive with  LENGTH and inclusive with 0), not 0 to
;;;   LENGTH as with VECTOR-UNFOLD.
(define vector-unfold-right
  (letrec ((tabulate!
            (lambda (f vec i)
              (cond ((>= i 0)
                     (vector-set! vec i (f i))
                     (tabulate! f vec (- i 1))))))
           (unfold1!
            (lambda (f vec i seed)
              (if (>= i 0)
                  (receive (elt new-seed)
                           (f i seed)
                    (vector-set! vec i elt)
                    (unfold1! f vec (- i 1) new-seed)))))
           (unfold2+!
            (lambda (f vec i seeds)
              (if (>= i 0)
                  (receive (elt . new-seeds)
                           (apply f i seeds)
                    (vector-set! vec i elt)
                    (unfold2+! f vec (- i 1) new-seeds))))))
    (lambda (f len . initial-seeds)
      (let ((f   (check-type procedure?  f   vector-unfold-right))
            (len (check-type nonneg-int? len vector-unfold-right)))
        (let ((vec (make-vector len))
              (i (- len 1)))
          (cond ((null? initial-seeds)
                 (tabulate! f vec i))
                ((null? (cdr initial-seeds))
                 (unfold1!  f vec i (car initial-seeds)))
                (else
                 (unfold2+! f vec i initial-seeds)))
          vec)))))

;;; (VECTOR-COPY <vector> [<start> <end> <fill>]) -> vector
;;;   Create a newly allocated vector containing the elements from the
;;;   range [START,END) in VECTOR.  START defaults to 0; END defaults
;;;   to the length of VECTOR.  END may be greater than the length of
;;;   VECTOR, in which case the vector is enlarged; if FILL is passed,
;;;   the new locations from which there is no respective element in
;;;   VECTOR are filled with FILL.

(define (vector-copy vec . args)
  (let ((vec (check-type vector? vec vector-copy)))
    ;; We can't use LET-VECTOR-START+END, because we have one more
    ;; argument, and we want finer control, too.
    ;;
    ;; Olin's implementation of LET*-OPTIONALS would prove useful here:
    ;; the built-in argument-checks-as-you-go-along produces almost
    ;; _exactly_ the same code as VECTOR-COPY:PARSE-ARGS.
    (receive (start end fill)
             (vector-copy:parse-args vec args)
      (let ((new-vector (make-vector (- end start) fill)))
        (%vector-copy! new-vector 0
                       vec        start
                       (if (> end (vector-length vec))
                           (vector-length vec)
                           end))
        new-vector))))

;;; Auxiliary for VECTOR-COPY.
;;; [wdc] Corrected to allow 0 <= start <= (vector-length vec).
(define (vector-copy:parse-args vec args)
  (define (parse-args start end n fill)
    (let ((start (check-type nonneg-int? start vector-copy))
          (end   (check-type nonneg-int? end vector-copy)))
      (cond ((and (<= 0 start end)
                  (<= start n))
             (values start end fill))
            (else
             (error "illegal arguments"
                    `(while calling ,vector-copy)
                    `(start was ,start)
                    `(end was ,end)
                    `(vector was ,vec))))))
  (let ((n (vector-length vec)))
    (cond ((null? args)
           (parse-args 0 n n (unspecified-value)))
          ((null? (cdr args))
           (parse-args (car args) n n (unspecified-value)))
          ((null? (cddr args))
           (parse-args (car args) (cadr args) n (unspecified-value)))
          ((null? (cdddr args))
           (parse-args (car args) (cadr args) n (caddr args)))
          (else
           (error "too many arguments"
                  vector-copy
                  (cdddr args))))))

;;; (VECTOR-REVERSE-COPY <vector> [<start> <end>]) -> vector
;;;   Create a newly allocated vector whose elements are the reversed
;;;   sequence of elements between START and END in VECTOR.  START's
;;;   default is 0; END's default is the length of VECTOR.
(define (vector-reverse-copy vec . maybe-start+end)
  (let-vector-start+end vector-reverse-copy vec maybe-start+end
                        (start end)
    (let ((new (make-vector (- end start))))
      (%vector-reverse-copy! new 0 vec start end)
      new)))

;;; (VECTOR-APPEND <vector> ...) -> vector
;;;   Append VECTOR ... into a newly allocated vector and return that
;;;   new vector.
(define (vector-append . vectors)
  (vector-concatenate:aux vectors vector-append))

;;; (VECTOR-CONCATENATE <vector-list>) -> vector
;;;   Concatenate the vectors in VECTOR-LIST.  This is equivalent to
;;;     (apply vector-append VECTOR-LIST)
;;;   but VECTOR-APPEND tends to be implemented in terms of
;;;   VECTOR-CONCATENATE, and some Schemes bork when the list to apply
;;;   a function to is too long.
;;;
;;; Actually, they're both implemented in terms of an internal routine.
(define (vector-concatenate vector-list)
  (vector-concatenate:aux vector-list vector-concatenate))

;;; Auxiliary for VECTOR-APPEND and VECTOR-CONCATENATE
(define vector-concatenate:aux
  (letrec ((compute-length
            (lambda (vectors len callee)
              (if (null? vectors)
                  len
                  (let ((vec (check-type vector? (car vectors)
                                         callee)))
                    (compute-length (cdr vectors)
                                    (+ (vector-length vec) len)
                                    callee)))))
           (concatenate!
            (lambda (vectors target to)
              (if (null? vectors)
                  target
                  (let* ((vec1 (car vectors))
                         (len (vector-length vec1)))
                    (%vector-copy! target to vec1 0 len)
                    (concatenate! (cdr vectors) target
                                  (+ to len)))))))
    (lambda (vectors callee)
      (cond ((null? vectors)            ;+++
             (make-vector 0))
            ((null? (cdr vectors))      ;+++
             ;; Blech, we still have to allocate a new one.
             (let* ((vec (check-type vector? (car vectors) callee))
                    (len (vector-length vec))
                    (new (make-vector len)))
               (%vector-copy! new 0 vec 0 len)
               new))
            (else
             (let ((new-vector
                    (make-vector (compute-length vectors 0 callee))))
               (concatenate! vectors new-vector 0)
               new-vector))))))

 

;;; --------------------
;;; Predicates

;;; (VECTOR? <value>) -> boolean
;;;   [R5RS] Return #T if VALUE is a vector and #F if not.
;; (define vector? vector?)

;;; (VECTOR-EMPTY? <vector>) -> boolean
;;;   Return #T if VECTOR has zero elements in it, i.e. VECTOR's length
;;;   is 0, and #F if not.
(define (vector-empty? vec)
  (let ((vec (check-type vector? vec vector-empty?)))
    (zero? (vector-length vec))))

;;; (VECTOR= <elt=?> <vector> ...) -> boolean
;;;     (ELT=? <value> <value>) -> boolean
;;;   Determine vector equality generalized across element comparators.
;;;   Vectors A and B are equal iff their lengths are the same and for
;;;   each respective elements E_a and E_b (element=? E_a E_b) returns
;;;   a true value.  ELT=? is always applied to two arguments.  Element
;;;   comparison must be consistent wtih EQ?; that is, if (eq? E_a E_b)
;;;   results in a true value, then (ELEMENT=? E_a E_b) must result in a
;;;   true value.  This may be exploited to avoid multiple unnecessary
;;;   element comparisons.  (This implementation does, but does not deal
;;;   with the situation that ELEMENT=? is EQ? to avoid more unnecessary
;;;   comparisons, but I believe this optimization is probably fairly
;;;   insignificant.)
;;;   
;;;   If the number of vector arguments is zero or one, then #T is
;;;   automatically returned.  If there are N vector arguments,
;;;   VECTOR_1 VECTOR_2 ... VECTOR_N, then VECTOR_1 & VECTOR_2 are
;;;   compared; if they are equal, the vectors VECTOR_2 ... VECTOR_N
;;;   are compared.  The precise order in which ELT=? is applied is not
;;;   specified.
(define (vector= elt=? . vectors)
  (let ((elt=? (check-type procedure? elt=? vector=)))
    (cond ((null? vectors)
           #t)
          ((null? (cdr vectors))
           (check-type vector? (car vectors) vector=)
           #t)
          (else
           (let loop ((vecs vectors))
             (let ((vec1 (check-type vector? (car vecs) vector=))
                   (vec2+ (cdr vecs)))
               (or (null? vec2+)
                   (and (binary-vector= elt=? vec1 (car vec2+))
                        (loop vec2+)))))))))
(define (binary-vector= elt=? vector-a vector-b)
  (or (eq? vector-a vector-b)           ;+++
      (let ((length-a (vector-length vector-a))
            (length-b (vector-length vector-b)))
        (letrec ((loop (lambda (i)
                         (or (= i length-a)
                             (and (< i length-b)
                                  (test (vector-ref vector-a i)
                                        (vector-ref vector-b i)
                                        i)))))
                 (test (lambda (elt-a elt-b i)
                         (and (or (eq? elt-a elt-b) ;+++
                                  (elt=? elt-a elt-b))
                              (loop (+ i 1))))))
          (and (= length-a length-b)
               (loop 0))))))

 

;;; --------------------
;;; Selectors

;;; (VECTOR-REF <vector> <index>) -> value
;;;   [R5RS] Return the value that the location in VECTOR at INDEX is
;;;   mapped to in the store.
;; (define vector-ref vector-ref)

;;; (VECTOR-LENGTH <vector>) -> exact, nonnegative integer
;;;   [R5RS] Return the length of VECTOR.
;; (define vector-length vector-length)

 

;;; --------------------
;;; Iteration

;;; (VECTOR-FOLD <kons> <initial-knil> <vector> ...) -> knil
;;;     (KONS <knil> <elt> ...) -> knil' ; N vectors -> N+1 args
;;;   The fundamental vector iterator.  KONS is iterated over each
;;;   index in all of the vectors in parallel, stopping at the end of
;;;   the shortest; KONS is applied to an argument list of (list I
;;;   STATE (vector-ref VEC I) ...), where STATE is the current state
;;;   value -- the state value begins with KNIL and becomes whatever
;;;   KONS returned at the respective iteration --, and I is the
;;;   current index in the iteration.  The iteration is strictly left-
;;;   to-right.
;;;     (vector-fold KONS KNIL (vector E_1 E_2 ... E_N))
;;;       <=>
;;;     (KONS (... (KONS (KONS KNIL E_1) E_2) ... E_N-1) E_N)
(define (vector-fold kons knil vec . vectors)
  (let ((kons (check-type procedure? kons vector-fold))
        (vec  (check-type vector?    vec  vector-fold)))
    (if (null? vectors)
        (%vector-fold1 kons knil (vector-length vec) vec)
        (%vector-fold2+ kons knil
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-fold)
                        (cons vec vectors)))))

;;; (VECTOR-FOLD-RIGHT <kons> <initial-knil> <vector> ...) -> knil
;;;     (KONS <knil> <elt> ...) -> knil' ; N vectors => N+1 args
;;;   The fundamental vector recursor.  Iterates in parallel across
;;;   VECTOR ... right to left, applying KONS to the elements and the
;;;   current state value; the state value becomes what KONS returns
;;;   at each next iteration.  KNIL is the initial state value.
;;;     (vector-fold-right KONS KNIL (vector E_1 E_2 ... E_N))
;;;       <=>
;;;     (KONS (... (KONS (KONS KNIL E_N) E_N-1) ... E_2) E_1)
;;;
;;; Not implemented in terms of a more primitive operations that might
;;; called %VECTOR-FOLD-RIGHT due to the fact that it wouldn't be very
;;; useful elsewhere.
(define vector-fold-right
  (letrec ((loop1 (lambda (kons knil vec i)
                    (if (negative? i)
                        knil
                        (loop1 kons (kons i knil (vector-ref vec i))
                               vec
                               (- i 1)))))
           (loop2+ (lambda (kons knil vectors i)
                     (if (negative? i)
                         knil
                         (loop2+ kons
                                 (apply kons i knil
                                        (vectors-ref vectors i))
                                 vectors
                                 (- i 1))))))
    (lambda (kons knil vec . vectors)
      (let ((kons (check-type procedure? kons vector-fold-right))
            (vec  (check-type vector?    vec  vector-fold-right)))
        (if (null? vectors)
            (loop1  kons knil vec (- (vector-length vec) 1))
            (loop2+ kons knil (cons vec vectors)
                    (- (%smallest-length vectors
                                         (vector-length vec)
                                         vector-fold-right)
                       1)))))))

;;; (VECTOR-MAP <f> <vector> ...) -> vector
;;;     (F <elt> ...) -> value ; N vectors -> N args
;;;   Constructs a new vector of the shortest length of the vector
;;;   arguments.  Each element at index I of the new vector is mapped
;;;   from the old vectors by (F I (vector-ref VECTOR I) ...).  The
;;;   dynamic order of application of F is unspecified.
(define (vector-map f vec . vectors)
  (let ((f   (check-type procedure? f   vector-map))
        (vec (check-type vector?    vec vector-map)))
    (if (null? vectors)
        (let ((len (vector-length vec)))
          (%vector-map1! f (make-vector len) vec len))
        (let ((len (%smallest-length vectors
                                     (vector-length vec)
                                     vector-map)))
          (%vector-map2+! f (make-vector len) (cons vec vectors)
                          len)))))

;;; (VECTOR-MAP! <f> <vector> ...) -> unspecified
;;;     (F <elt> ...) -> element' ; N vectors -> N args
;;;   Similar to VECTOR-MAP, but rather than mapping the new elements
;;;   into a new vector, the new mapped elements are destructively
;;;   inserted into the first vector.  Again, the dynamic order of
;;;   application of F is unspecified, so it is dangerous for F to
;;;   manipulate the first VECTOR.
(define (vector-map! f vec . vectors)
  (let ((f   (check-type procedure? f   vector-map!))
        (vec (check-type vector?    vec vector-map!)))
    (if (null? vectors)
        (%vector-map1!  f vec vec (vector-length vec))
        (%vector-map2+! f vec (cons vec vectors)
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-map!)))
    (unspecified-value)))

;;; (VECTOR-FOR-EACH <f> <vector> ...) -> unspecified
;;;     (F <elt> ...) ; N vectors -> N args
;;;   Simple vector iterator: applies F to each index in the range [0,
;;;   LENGTH), where LENGTH is the length of the smallest vector
;;;   argument passed, and the respective element at that index.  In
;;;   contrast with VECTOR-MAP, F is reliably applied to each
;;;   subsequent elements, starting at index 0 from left to right, in
;;;   the vectors.
(define vector-for-each
  (letrec ((for-each1
            (lambda (f vec i len)
              (cond ((< i len)
                     (f i (vector-ref vec i))
                     (for-each1 f vec (+ i 1) len)))))
           (for-each2+
            (lambda (f vecs i len)
              (cond ((< i len)
                     (apply f i (vectors-ref vecs i))
                     (for-each2+ f vecs (+ i 1) len))))))
    (lambda (f vec . vectors)
      (let ((f   (check-type procedure? f   vector-for-each))
            (vec (check-type vector?    vec vector-for-each)))
        (if (null? vectors)
            (for-each1 f vec 0 (vector-length vec))
            (for-each2+ f (cons vec vectors) 0
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-for-each)))))))

;;; (VECTOR-COUNT <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer
;;;     (PREDICATE? <index> <value> ...) ; N vectors -> N+1 args
;;;   PREDICATE? is applied element-wise to the elements of VECTOR ...,
;;;   and a count is tallied of the number of elements for which a
;;;   true value is produced by PREDICATE?.  This count is returned.
(define (vector-count pred? vec . vectors)
  (let ((pred? (check-type procedure? pred? vector-count))
        (vec   (check-type vector?    vec   vector-count)))
    (if (null? vectors)
        (%vector-fold1 (lambda (index count elt)
                         (if (pred? index elt)
                             (+ count 1)
                             count))
                       0
                       (vector-length vec)
                       vec)
        (%vector-fold2+ (lambda (index count . elts)
                          (if (apply pred? index elts)
                              (+ count 1)
                              count))
                        0
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-count)
                        (cons vec vectors)))))

 

;;; --------------------
;;; Searching

;;; (VECTOR-INDEX <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   Search left-to-right across VECTOR ... in parallel, returning the
;;;   index of the first set of values VALUE ... such that (PREDICATE?
;;;   VALUE ...) returns a true value; if no such set of elements is
;;;   reached, return #F.
(define (vector-index pred? vec . vectors)
  (vector-index/skip pred? vec vectors vector-index))

;;; (VECTOR-SKIP <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   (vector-index (lambda elts (not (apply PREDICATE? elts)))
;;;                 VECTOR ...)
;;;   Like VECTOR-INDEX, but find the index of the first set of values
;;;   that do _not_ satisfy PREDICATE?.
(define (vector-skip pred? vec . vectors)
  (vector-index/skip (lambda elts (not (apply pred? elts)))
                     vec vectors
                     vector-skip))

;;; Auxiliary for VECTOR-INDEX & VECTOR-SKIP
(define vector-index/skip
  (letrec ((loop1  (lambda (pred? vec len i)
                     (cond ((= i len) #f)
                           ((pred? (vector-ref vec i)) i)
                           (else (loop1 pred? vec len (+ i 1))))))
           (loop2+ (lambda (pred? vectors len i)
                     (cond ((= i len) #f)
                           ((apply pred? (vectors-ref vectors i)) i)
                           (else (loop2+ pred? vectors len
                                         (+ i 1)))))))
    (lambda (pred? vec vectors callee)
      (let ((pred? (check-type procedure? pred? callee))
            (vec   (check-type vector?    vec   callee)))
        (if (null? vectors)
            (loop1 pred? vec (vector-length vec) 0)
            (loop2+ pred? (cons vec vectors)
                    (%smallest-length vectors
                                      (vector-length vec)
                                      callee)
                    0))))))

;;; (VECTOR-INDEX-RIGHT <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   Right-to-left variant of VECTOR-INDEX.
(define (vector-index-right pred? vec . vectors)
  (vector-index/skip-right pred? vec vectors vector-index-right))

;;; (VECTOR-SKIP-RIGHT <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   Right-to-left variant of VECTOR-SKIP.
(define (vector-skip-right pred? vec . vectors)
  (vector-index/skip-right (lambda elts (not (apply pred? elts)))
                           vec vectors
                           vector-index-right))

(define vector-index/skip-right
  (letrec ((loop1  (lambda (pred? vec i)
                     (cond ((negative? i) #f)
                           ((pred? (vector-ref vec i)) i)
                           (else (loop1 pred? vec (- i 1))))))
           (loop2+ (lambda (pred? vectors i)
                     (cond ((negative? i) #f)
                           ((apply pred? (vectors-ref vectors i)) i)
                           (else (loop2+ pred? vectors (- i 1)))))))
    (lambda (pred? vec vectors callee)
      (let ((pred? (check-type procedure? pred? callee))
            (vec   (check-type vector?    vec   callee)))
        (if (null? vectors)
            (loop1 pred? vec (- (vector-length vec) 1))
            (loop2+ pred? (cons vec vectors)
                    (- (%smallest-length vectors
                                         (vector-length vec)
                                         callee)
                       1)))))))

;;; (VECTOR-BINARY-SEARCH <vector> <value> <cmp> [<start> <end>])
;;;       -> exact, nonnegative integer or #F
;;;     (CMP <value1> <value2>) -> integer
;;;       positive -> VALUE1 > VALUE2
;;;       zero     -> VALUE1 = VALUE2
;;;       negative -> VALUE1 < VALUE2
;;;   Perform a binary search through VECTOR for VALUE, comparing each
;;;   element to VALUE with CMP.
(define (vector-binary-search vec value cmp . maybe-start+end)
  (let ((cmp (check-type procedure? cmp vector-binary-search)))
    (let-vector-start+end vector-binary-search vec maybe-start+end
                          (start end)
      (let loop ((start start) (end end) (j #f))
        (let ((i (quotient (+ start end) 2)))
          (if (or (= start end) (and j (= i j)))
              #f
              (let ((comparison
                     (check-type integer?
                                 (cmp (vector-ref vec i) value)
                                 `(,cmp for ,vector-binary-search))))
                (cond ((zero?     comparison) i)
                      ((positive? comparison) (loop start i i))
                      (else                   (loop i end i))))))))))

;;; (VECTOR-ANY <pred?> <vector> ...) -> value
;;;   Apply PRED? to each parallel element in each VECTOR ...; if PRED?
;;;   should ever return a true value, immediately stop and return that
;;;   value; otherwise, when the shortest vector runs out, return #F.
;;;   The iteration and order of application of PRED? across elements
;;;   is of the vectors is strictly left-to-right.
(define vector-any
  (letrec ((loop1 (lambda (pred? vec i len len-1)
                    (and (not (= i len))
                         (if (= i len-1)
                             (pred? (vector-ref vec i))
                             (or (pred? (vector-ref vec i))
                                 (loop1 pred? vec (+ i 1)
                                        len len-1))))))
           (loop2+ (lambda (pred? vectors i len len-1)
                     (and (not (= i len))
                          (if (= i len-1)
                              (apply pred? (vectors-ref vectors i))
                              (or (apply pred? (vectors-ref vectors i))
                                  (loop2+ pred? vectors (+ i 1)
                                         len len-1)))))))
    (lambda (pred? vec . vectors)
      (let ((pred? (check-type procedure? pred? vector-any))
            (vec   (check-type vector?    vec   vector-any)))
        (if (null? vectors)
            (let ((len (vector-length vec)))
              (loop1 pred? vec 0 len (- len 1)))
            (let ((len (%smallest-length vectors
                                         (vector-length vec)
                                         vector-any)))
              (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))

;;; (VECTOR-EVERY <pred?> <vector> ...) -> value
;;;   Apply PRED? to each parallel value in each VECTOR ...; if PRED?
;;;   should ever return #F, immediately stop and return #F; otherwise,
;;;   if PRED? should return a true value for each element, stopping at
;;;   the end of the shortest vector, return the last value that PRED?
;;;   returned.  In the case that there is an empty vector, return #T.
;;;   The iteration and order of application of PRED? across elements
;;;   is of the vectors is strictly left-to-right.
(define vector-every
  (letrec ((loop1 (lambda (pred? vec i len len-1)
                    (or (= i len)
                        (if (= i len-1)
                            (pred? (vector-ref vec i))
                            (and (pred? (vector-ref vec i))
                                 (loop1 pred? vec (+ i 1)
                                        len len-1))))))
           (loop2+ (lambda (pred? vectors i len len-1)
                     (or (= i len)
                         (if (= i len-1)
                             (apply pred? (vectors-ref vectors i))
                             (and (apply pred? (vectors-ref vectors i))
                                  (loop2+ pred? vectors (+ i 1)
                                          len len-1)))))))
    (lambda (pred? vec . vectors)
      (let ((pred? (check-type procedure? pred? vector-every))
            (vec   (check-type vector?    vec   vector-every)))
        (if (null? vectors)
            (let ((len (vector-length vec)))
              (loop1 pred? vec 0 len (- len 1)))
            (let ((len (%smallest-length vectors
                                         (vector-length vec)
                                         vector-every)))
              (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))

 

;;; --------------------
;;; Mutators

;;; (VECTOR-SET! <vector> <index> <value>) -> unspecified
;;;   [R5RS] Assign the location at INDEX in VECTOR to VALUE.
;; (define vector-set! vector-set!)

;;; (VECTOR-SWAP! <vector> <index1> <index2>) -> unspecified
;;;   Swap the values in the locations at INDEX1 and INDEX2.
(define (vector-swap! vec i j)
  (let ((vec (check-type vector? vec vector-swap!)))
    (let ((i (check-index vec i vector-swap!))
          (j (check-index vec j vector-swap!)))
      (let ((x (vector-ref vec i)))
        (vector-set! vec i (vector-ref vec j))
        (vector-set! vec j x)))))

;;; (VECTOR-FILL! <vector> <value> [<start> <end>]) -> unspecified
;;;   [R5RS+] Fill the locations in VECTOR between START, whose default
;;;   is 0, and END, whose default is the length of VECTOR, with VALUE.
;;;
;;; This one can probably be made really fast natively.
(define vector-fill!
  (let ((%vector-fill! vector-fill!))   ; Take the native one, under
                                        ;   the assumption that it's
                                        ;   faster, so we can use it if
                                        ;   there are no optional
                                        ;   arguments.
    (lambda (vec value . maybe-start+end)
      (if (null? maybe-start+end)
          (%vector-fill! vec value)     ;+++
          (let-vector-start+end vector-fill! vec maybe-start+end
                                (start end)
            (do ((i start (+ i 1)))
                ((= i end))
              (vector-set! vec i value)))))))

;;; (VECTOR-COPY! <target> <tstart> <source> [<sstart> <send>])
;;;       -> unspecified
;;;   Copy the values in the locations in [SSTART,SEND) from SOURCE to
;;;   to TARGET, starting at TSTART in TARGET.
;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
(define (vector-copy! target tstart source . maybe-sstart+send)
  (define (doit! sstart send source-length)
    (let ((tstart (check-type nonneg-int? tstart vector-copy!))
          (sstart (check-type nonneg-int? sstart vector-copy!))
          (send   (check-type nonneg-int? send vector-copy!)))
      (cond ((and (<= 0 sstart send source-length)
                  (<= (+ tstart (- send sstart)) (vector-length target)))
             (%vector-copy! target tstart source sstart send))
            (else
             (error "illegal arguments"
                    `(while calling ,vector-copy!)
                    `(target was ,target)
                    `(target-length was ,(vector-length target))
                    `(tstart was ,tstart)
                    `(source was ,source)
                    `(source-length was ,source-length)
                    `(sstart was ,sstart)
                    `(send   was ,send))))))
  (let ((n (vector-length source)))
    (cond ((null? maybe-sstart+send)
           (doit! 0 n n))
          ((null? (cdr maybe-sstart+send))
           (doit! (car maybe-sstart+send) n n))
          ((null? (cddr maybe-sstart+send))
           (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
          (else
           (error "too many arguments"
                  vector-copy!
                  (cddr maybe-sstart+send))))))

;;; (VECTOR-REVERSE-COPY! <target> <tstart> <source> [<sstart> <send>])
;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
(define (vector-reverse-copy! target tstart source . maybe-sstart+send)
  (define (doit! sstart send source-length)
    (let ((tstart (check-type nonneg-int? tstart vector-reverse-copy!))
          (sstart (check-type nonneg-int? sstart vector-reverse-copy!))
          (send   (check-type nonneg-int? send vector-reverse-copy!)))
      (cond ((and (eq? target source)
                  (or (between? sstart tstart send)
                      (between? tstart sstart
                                (+ tstart (- send sstart)))))
               (error "vector range for self-copying overlaps"
                      vector-reverse-copy!
                      `(vector was ,target)
                      `(tstart was ,tstart)
                      `(sstart was ,sstart)
                      `(send   was ,send)))
            ((and (<= 0 sstart send source-length)
                  (<= (+ tstart (- send sstart)) (vector-length target)))
             (%vector-reverse-copy! target tstart source sstart send))
            (else
             (error "illegal arguments"
                    `(while calling ,vector-reverse-copy!)
                    `(target was ,target)
                    `(target-length was ,(vector-length target))
                    `(tstart was ,tstart)
                    `(source was ,source)
                    `(source-length was ,source-length)
                    `(sstart was ,sstart)
                    `(send   was ,send))))))
  (let ((n (vector-length source)))
    (cond ((null? maybe-sstart+send)
           (doit! 0 n n))
          ((null? (cdr maybe-sstart+send))
           (doit! (car maybe-sstart+send) n n))
          ((null? (cddr maybe-sstart+send))
           (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
          (else
           (error "too many arguments"
                  vector-reverse-copy!
                  (cddr maybe-sstart+send))))))

;;; (VECTOR-REVERSE! <vector> [<start> <end>]) -> unspecified
;;;   Destructively reverse the contents of the sequence of locations
;;;   in VECTOR between START, whose default is 0, and END, whose
;;;   default is the length of VECTOR.
(define (vector-reverse! vec . start+end)
  (let-vector-start+end vector-reverse! vec start+end
                        (start end)
    (%vector-reverse! vec start end)))

 

;;; --------------------
;;; Conversion

;;; (VECTOR->LIST <vector> [<start> <end>]) -> list
;;;   [R5RS+] Produce a list containing the elements in the locations
;;;   between START, whose default is 0, and END, whose default is the
;;;   length of VECTOR, from VECTOR.
(define vector->list
  (let ((%vector->list vector->list))
    (lambda (vec . maybe-start+end)
      (if (null? maybe-start+end)       ; Oughta use CASE-LAMBDA.
          (%vector->list vec)           ;+++
          (let-vector-start+end vector->list vec maybe-start+end
                                (start end)
            ;(unfold (lambda (i)        ; No SRFI 1.
            ;          (< i start))
            ;        (lambda (i) (vector-ref vec i))
            ;        (lambda (i) (- i 1))
            ;        (- end 1))
            (do ((i (- end 1) (- i 1))
                 (result '() (cons (vector-ref vec i) result)))
                ((< i start) result)))))))

;;; (REVERSE-VECTOR->LIST <vector> [<start> <end>]) -> list
;;;   Produce a list containing the elements in the locations between
;;;   START, whose default is 0, and END, whose default is the length
;;;   of VECTOR, from VECTOR, in reverse order.
(define (reverse-vector->list vec . maybe-start+end)
  (let-vector-start+end reverse-vector->list vec maybe-start+end
                        (start end)
    ;(unfold (lambda (i) (= i end))     ; No SRFI 1.
    ;        (lambda (i) (vector-ref vec i))
    ;        (lambda (i) (+ i 1))
    ;        start)
    (do ((i start (+ i 1))
         (result '() (cons (vector-ref vec i) result)))
        ((= i end) result))))

;;; (LIST->VECTOR <list> [<start> <end>]) -> vector
;;;   [R5RS+] Produce a vector containing the elements in LIST, which
;;;   must be a proper list, between START, whose default is 0, & END,
;;;   whose default is the length of LIST.  It is suggested that if the
;;;   length of LIST is known in advance, the START and END arguments
;;;   be passed, so that LIST->VECTOR need not call LENGTH to determine
;;;   the the length.
;;;
;;; This implementation diverges on circular lists, unless LENGTH fails
;;; and causes - to fail as well.  Given a LENGTH* that computes the
;;; length of a list's cycle, this wouldn't diverge, and would work
;;; great for circular lists.
(define list->vector
  (let ((%list->vector list->vector))
    (lambda (lst . maybe-start+end)
      ;; Checking the type of a proper list is expensive, so we do it
      ;; amortizedly, or let %LIST->VECTOR or LIST-TAIL do it.
      (if (null? maybe-start+end)       ; Oughta use CASE-LAMBDA.
          (%list->vector lst)           ;+++
          ;; We can't use LET-VECTOR-START+END, because we're using the
          ;; bounds of a _list_, not a vector.
          (let*-optionals maybe-start+end
              ((start 0)
               (end (length lst)))      ; Ugh -- LENGTH
            (let ((start (check-type nonneg-int? start list->vector))
                  (end   (check-type nonneg-int? end   list->vector)))
              ((lambda (f)
                 (vector-unfold f (- end start) (list-tail lst start)))
               (lambda (index l)
                 (cond ((null? l)
                        (error "list was too short"
                               `(list was ,lst)
                               `(attempted end was ,end)
                               `(while calling ,list->vector)))
                       ((pair? l)
                        (values (car l) (cdr l)))
                       (else
                        ;; Make this look as much like what CHECK-TYPE
                        ;; would report as possible.
                        (error "erroneous value"
                               ;; We want SRFI 1's PROPER-LIST?, but it
                               ;; would be a waste to link all of SRFI
                               ;; 1 to this module for only the single
                               ;; function PROPER-LIST?.
                               (list list? lst)
                               `(while calling
                                 ,list->vector))))))))))))

;;; (REVERSE-LIST->VECTOR <list> [<start> <end>]) -> vector
;;;   Produce a vector containing the elements in LIST, which must be a
;;;   proper list, between START, whose default is 0, and END, whose
;;;   default is the length of LIST, in reverse order.  It is suggested
;;;   that if the length of LIST is known in advance, the START and END
;;;   arguments be passed, so that REVERSE-LIST->VECTOR need not call
;;;   LENGTH to determine the the length.
;;;
;;; This also diverges on circular lists unless, again, LENGTH returns
;;; something that makes - bork.
(define (reverse-list->vector lst . maybe-start+end)
  (let*-optionals maybe-start+end
      ((start 0)
       (end (length lst)))              ; Ugh -- LENGTH
    (let ((start (check-type nonneg-int? start reverse-list->vector))
          (end   (check-type nonneg-int? end   reverse-list->vector)))
      ((lambda (f)
         (vector-unfold-right f (- end start) (list-tail lst start)))
       (lambda (index l)
         (cond ((null? l)
                (error "list too short"
                       `(list was ,lst)
                       `(attempted end was ,end)
                       `(while calling ,reverse-list->vector)))
               ((pair? l)
                (values (car l) (cdr l)))
               (else
                (error "erroneous value"
                       (list list? lst)
                       `(while calling ,reverse-list->vector)))))))))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

)

Added srfi/s43/_vectors.sls.



















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s43 vectors)
  (export
    ;;; * Constructors
    make-vector vector
    vector-unfold         vector-unfold-right
    vector-copy           vector-reverse-copy
    vector-append         vector-concatenate
    ;;; * Predicates
    vector?
    vector-empty?
    vector=
    ;;; * Selectors
    vector-ref
    vector-length
    ;;; * Iteration
    vector-fold           vector-fold-right
    vector-map            vector-map!
    vector-for-each
    vector-count
    ;;; * Searching
    vector-index          vector-skip
    vector-index-right    vector-skip-right
    vector-binary-search  vector-any    vector-every
    ;;; * Mutators
    vector-set!
    vector-swap!
    (rename (my:vector-fill! vector-fill!))
    vector-reverse!
    vector-copy!          vector-reverse-copy!
    ;;; * Conversion
    (rename (my:vector->list vector->list))          reverse-vector->list
    (rename (my:list->vector list->vector))          reverse-list->vector )
  (import
    (except (rnrs) vector-map vector-for-each)
    (rnrs r5rs)
    (srfi s23 error tricks)
    (srfi s8 receive)
    (for (srfi private vanish) expand)
    (srfi private include))

  ;; I do these let-syntax tricks so the original vector-lib.scm file does
  ;; not have to be modified at all.
  (let-syntax
      ((define
        (let ((vd (vanish-define define
                   (make-vector vector vector? vector-ref vector-set! vector-length))))
          (lambda (stx)
            (define (rename? id)
              (memp (lambda (x) (free-identifier=? id x))
                    (list #'vector-fill! #'vector->list #'list->vector)))
            (define (rename id)
              (datum->syntax id
               (string->symbol
                (string-append "my:" (symbol->string (syntax->datum id))))))
            (syntax-case stx ()
              ((_ name . r)
               (and (identifier? #'name)
                    (rename? #'name))
               #`(define #,(rename #'name) . r))
              (_ (vd stx))))))
       (define-syntax
        (vanish-define define-syntax
         (receive))))
    (SRFI-23-error->R6RS "(library (srfi s43 vectors))"
     (include/resolve ("srfi" "s43") "vector-lib.scm")))
)

Added srfi/s43/vector-lib.scm.









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
;;;;;; SRFI 43: Vector library                           -*- Scheme -*-
;;;
;;; $Id: vector-lib.scm,v 1.7 2009/03/29 09:46:03 sperber Exp $
;;;
;;; Taylor Campbell wrote this code; he places it in the public domain.
;;; Will Clinger [wdc] made some corrections, also in the public domain.

;;; --------------------
;;; Exported procedure index
;;;
;;; * Constructors
;;; make-vector vector
;;; vector-unfold                   vector-unfold-right
;;; vector-copy                     vector-reverse-copy
;;; vector-append                   vector-concatenate
;;;
;;; * Predicates
;;; vector?
;;; vector-empty?
;;; vector=
;;;
;;; * Selectors
;;; vector-ref
;;; vector-length
;;;
;;; * Iteration
;;; vector-fold                     vector-fold-right
;;; vector-map                      vector-map!
;;; vector-for-each
;;; vector-count
;;;
;;; * Searching
;;; vector-index                    vector-skip
;;; vector-index-right              vector-skip-right
;;; vector-binary-search
;;; vector-any                      vector-every
;;;
;;; * Mutators
;;; vector-set!
;;; vector-swap!
;;; vector-fill!
;;; vector-reverse!
;;; vector-copy!                    vector-reverse-copy!
;;; vector-reverse!
;;;
;;; * Conversion
;;; vector->list                    reverse-vector->list
;;; list->vector                    reverse-list->vector

 

;;; --------------------
;;; Commentary on efficiency of the code

;;; This code is somewhat tuned for efficiency.  There are several
;;; internal routines that can be optimized greatly to greatly improve
;;; the performance of much of the library.  These internal procedures
;;; are already carefully tuned for performance, and lambda-lifted by
;;; hand.  Some other routines are lambda-lifted by hand, but only the
;;; loops are lambda-lifted, and only if some routine has two possible
;;; loops -- a fast path and an n-ary case --, whereas _all_ of the
;;; internal routines' loops are lambda-lifted so as to never cons a
;;; closure in their body (VECTOR-PARSE-START+END doesn't have a loop),
;;; even in Scheme systems that perform no loop optimization (which is
;;; most of them, unfortunately).
;;;
;;; Fast paths are provided for common cases in most of the loops in
;;; this library.
;;;
;;; All calls to primitive vector operations are protected by a prior
;;; type check; they can be safely converted to use unsafe equivalents
;;; of the operations, if available.  Ideally, the compiler should be
;;; able to determine this, but the state of Scheme compilers today is
;;; not a happy one.
;;;
;;; Efficiency of the actual algorithms is a rather mundane point to
;;; mention; vector operations are rarely beyond being straightforward.

 

;;; --------------------
;;; Utilities

;;; SRFI 8, too trivial to put in the dependencies list.
(define-syntax receive
  (syntax-rules ()
    ((receive ?formals ?producer ?body1 ?body2 ...)
     (call-with-values (lambda () ?producer)
       (lambda ?formals ?body1 ?body2 ...)))))

;;; Not the best LET*-OPTIONALS, but not the worst, either.  Use Olin's
;;; if it's available to you.
(define-syntax let*-optionals
  (syntax-rules ()
    ((let*-optionals (?x ...) ((?var ?default) ...) ?body1 ?body2 ...)
     (let ((args (?x ...)))
       (let*-optionals args ((?var ?default) ...) ?body1 ?body2 ...)))
    ((let*-optionals ?args ((?var ?default) ...) ?body1 ?body2 ...)
     (let*-optionals:aux ?args ?args ((?var ?default) ...)
       ?body1 ?body2 ...))))

(define-syntax let*-optionals:aux
  (syntax-rules ()
    ((aux ?orig-args-var ?args-var () ?body1 ?body2 ...)
     (if (null? ?args-var)
         (let () ?body1 ?body2 ...)
         (error "too many arguments" (length ?orig-args-var)
                ?orig-args-var)))
    ((aux ?orig-args-var ?args-var
         ((?var ?default) ?more ...)
       ?body1 ?body2 ...)
     (if (null? ?args-var)
         (let* ((?var ?default) ?more ...) ?body1 ?body2 ...)
         (let ((?var (car ?args-var))
               (new-args (cdr ?args-var)))
           (let*-optionals:aux ?orig-args-var new-args
               (?more ...)
             ?body1 ?body2 ...))))))

(define (nonneg-int? x)
  (and (integer? x)
       (not (negative? x))))

(define (between? x y z)
  (and (<  x y)
       (<= y z)))

(define (unspecified-value) (if #f #f))

;++ This should be implemented more efficiently.  It shouldn't cons a
;++ closure, and the cons cells used in the loops when using this could
;++ be reused.
(define (vectors-ref vectors i)
  (map (lambda (v) (vector-ref v i)) vectors))

 

;;; --------------------
;;; Error checking

;;; Error signalling (not checking) is done in a way that tries to be
;;; as helpful to the person who gets the debugging prompt as possible.
;;; That said, error _checking_ tries to be as unredundant as possible.

;;; I don't use any sort of general condition mechanism; I use simply
;;; SRFI 23's ERROR, even in cases where it might be better to use such
;;; a general condition mechanism.  Fix that when porting this to a
;;; Scheme implementation that has its own condition system.

;;; In argument checks, upon receiving an invalid argument, the checker
;;; procedure recursively calls itself, but in one of the arguments to
;;; itself is a call to ERROR; this mechanism is used in the hopes that
;;; the user may be thrown into a debugger prompt, proceed with another
;;; value, and let it be checked again.

;;; Type checking is pretty basic, but easily factored out and replaced
;;; with whatever your implementation's preferred type checking method
;;; is.  I doubt there will be many other methods of index checking,
;;; though the index checkers might be better implemented natively.

;;; (CHECK-TYPE <type-predicate?> <value> <callee>) -> value
;;;   Ensure that VALUE satisfies TYPE-PREDICATE?; if not, signal an
;;;   error stating that VALUE did not satisfy TYPE-PREDICATE?, showing
;;;   that this happened while calling CALLEE.  Return VALUE if no
;;;   error was signalled.
(define (check-type pred? value callee)
  (if (pred? value)
      value
      ;; Recur: when (or if) the user gets a debugger prompt, he can
      ;; proceed where the call to ERROR was with the correct value.
      (check-type pred?
                  (error "erroneous value"
                         (list pred? value)
                         `(while calling ,callee))
                  callee)))

;;; (CHECK-INDEX <vector> <index> <callee>) -> index
;;;   Ensure that INDEX is a valid index into VECTOR; if not, signal an
;;;   error stating that it is not and that this happened in a call to
;;;   CALLEE.  Return INDEX when it is valid.  (Note that this does NOT
;;;   check that VECTOR is indeed a vector.)
(define (check-index vec index callee)
  (let ((index (check-type integer? index callee)))
    (cond ((< index 0)
           (check-index vec
                        (error "vector index too low"
                               index
                               `(into vector ,vec)
                               `(while calling ,callee))
                        callee))
          ((>= index (vector-length vec))
           (check-index vec
                        (error "vector index too high"
                               index
                               `(into vector ,vec)
                               `(while calling ,callee))
                        callee))
          (else index))))

;;; (CHECK-INDICES <vector>
;;;                <start> <start-name>
;;;                <end> <end-name>
;;;                <caller>) -> [start end]
;;;   Ensure that START and END are valid bounds of a range within
;;;   VECTOR; if not, signal an error stating that they are not, with
;;;   the message being informative about what the argument names were
;;;   called -- by using START-NAME & END-NAME --, and that it occurred
;;;   while calling CALLEE.  Also ensure that VEC is in fact a vector.
;;;   Returns no useful value.
(define (check-indices vec start start-name end end-name callee)
  (let ((lose (lambda things
                (apply error "vector range out of bounds"
                       (append things
                               `(vector was ,vec)
                               `(,start-name was ,start)
                               `(,end-name was ,end)
                               `(while calling ,callee)))))
        (start (check-type integer? start callee))
        (end   (check-type integer? end   callee)))
    (cond ((> start end)
           ;; I'm not sure how well this will work.  The intent is that
           ;; the programmer tells the debugger to proceed with both a
           ;; new START & a new END by returning multiple values
           ;; somewhere.
           (receive (new-start new-end)
                    (lose `(,end-name < ,start-name))
             (check-indices vec
                            new-start start-name
                            new-end end-name
                            callee)))
          ((< start 0)
           (check-indices vec
                          (lose `(,start-name < 0))
                          start-name
                          end end-name
                          callee))
          ((>= start (vector-length vec))
           (check-indices vec
                          (lose `(,start-name > len)
                                `(len was ,(vector-length vec)))
                          start-name
                          end end-name
                          callee))
          ((> end (vector-length vec))
           (check-indices vec
                          start start-name
                          (lose `(,end-name > len)
                                `(len was ,(vector-length vec)))
                          end-name
                          callee))
          (else
           (values start end)))))

 

;;; --------------------
;;; Internal routines

;;; These should all be integrated, native, or otherwise optimized --
;;; they're used a _lot_ --.  All of the loops and LETs inside loops
;;; are lambda-lifted by hand, just so as not to cons closures in the
;;; loops.  (If your compiler can do better than that if they're not
;;; lambda-lifted, then lambda-drop (?) them.)

;;; (VECTOR-PARSE-START+END <vector> <arguments>
;;;                         <start-name> <end-name>
;;;                         <callee>)
;;;       -> [start end]
;;;   Return two values, composing a valid range within VECTOR, as
;;;   extracted from ARGUMENTS or defaulted from VECTOR -- 0 for START
;;;   and the length of VECTOR for END --; START-NAME and END-NAME are
;;;   purely for error checking.
(define (vector-parse-start+end vec args start-name end-name callee)
  (let ((len (vector-length vec)))
    (cond ((null? args)
           (values 0 len))
          ((null? (cdr args))
           (check-indices vec
                          (car args) start-name
                          len end-name
                          callee))
          ((null? (cddr args))
           (check-indices vec
                          (car  args) start-name
                          (cadr args) end-name
                          callee))
          (else
           (error "too many arguments"
                  `(extra args were ,(cddr args))
                  `(while calling ,callee))))))

(define-syntax let-vector-start+end
  (syntax-rules ()
    ((let-vector-start+end ?callee ?vec ?args (?start ?end)
       ?body1 ?body2 ...)
     (let ((?vec (check-type vector? ?vec ?callee)))
       (receive (?start ?end)
                (vector-parse-start+end ?vec ?args '?start '?end
                                        ?callee)
         ?body1 ?body2 ...)))))

;;; (%SMALLEST-LENGTH <vector-list> <default-length> <callee>)
;;;       -> exact, nonnegative integer
;;;   Compute the smallest length of VECTOR-LIST.  DEFAULT-LENGTH is
;;;   the length that is returned if VECTOR-LIST is empty.  Common use
;;;   of this is in n-ary vector routines:
;;;     (define (f vec . vectors)
;;;       (let ((vec (check-type vector? vec f)))
;;;         ...(%smallest-length vectors (vector-length vec) f)...))
;;;   %SMALLEST-LENGTH takes care of the type checking -- which is what
;;;   the CALLEE argument is for --; thus, the design is tuned for
;;;   avoiding redundant type checks.
(define %smallest-length
  (letrec ((loop (lambda (vector-list length callee)
                   (if (null? vector-list)
                       length
                       (loop (cdr vector-list)
                             (min (vector-length
                                   (check-type vector?
                                               (car vector-list)
                                               callee))
                                  length)
                             callee)))))
    loop))

;;; (%VECTOR-COPY! <target> <tstart> <source> <sstart> <send>)
;;;   Copy elements at locations SSTART to SEND from SOURCE to TARGET,
;;;   starting at TSTART in TARGET.
;;;
;;; Optimize this!  Probably with some combination of:
;;;   - Force it to be integrated.
;;;   - Let it use unsafe vector element dereferencing routines: bounds
;;;     checking already happens outside of it.  (Or use a compiler
;;;     that figures this out, but Olin Shivers' PhD thesis seems to
;;;     have been largely ignored in actual implementations...)
;;;   - Implement it natively as a VM primitive: the VM can undoubtedly
;;;     perform much faster than it can make Scheme perform, even with
;;;     bounds checking.
;;;   - Implement it in assembly: you _want_ the fine control that
;;;     assembly can give you for this.
;;; I already lambda-lift it by hand, but you should be able to make it
;;; even better than that.
(define %vector-copy!
  (letrec ((loop/l->r (lambda (target source send i j)
                        (cond ((< i send)
                               (vector-set! target j
                                            (vector-ref source i))
                               (loop/l->r target source send
                                          (+ i 1) (+ j 1))))))
           (loop/r->l (lambda (target source sstart i j)
                        (cond ((>= i sstart)
                               (vector-set! target j
                                            (vector-ref source i))
                               (loop/r->l target source sstart
                                          (- i 1) (- j 1)))))))
    (lambda (target tstart source sstart send)
      (if (> sstart tstart)             ; Make sure we don't copy over
                                        ;   ourselves.
          (loop/l->r target source send sstart tstart)
          (loop/r->l target source sstart (- send 1)
                     (+ -1 tstart send (- sstart)))))))

;;; (%VECTOR-REVERSE-COPY! <target> <tstart> <source> <sstart> <send>)
;;;   Copy elements from SSTART to SEND from SOURCE to TARGET, in the
;;;   reverse order.
(define %vector-reverse-copy!
  (letrec ((loop (lambda (target source sstart i j)
                   (cond ((>= i sstart)
                          (vector-set! target j (vector-ref source i))
                          (loop target source sstart
                                (- i 1)
                                (+ j 1)))))))
    (lambda (target tstart source sstart send)
      (loop target source sstart
            (- send 1)
            tstart))))

;;; (%VECTOR-REVERSE! <vector>)
(define %vector-reverse!
  (letrec ((loop (lambda (vec i j)
                   (cond ((<= i j)
                          (let ((v (vector-ref vec i)))
                            (vector-set! vec i (vector-ref vec j))
                            (vector-set! vec j v)
                            (loop vec (+ i 1) (- j 1))))))))
    (lambda (vec start end)
      (loop vec start (- end 1)))))

;;; (%VECTOR-FOLD1 <kons> <knil> <vector>) -> knil'
;;;     (KONS <index> <knil> <elt>) -> knil'
(define %vector-fold1
  (letrec ((loop (lambda (kons knil len vec i)
                   (if (= i len)
                       knil
                       (loop kons
                             (kons i knil (vector-ref vec i))
                             len vec (+ i 1))))))
    (lambda (kons knil len vec)
      (loop kons knil len vec 0))))

;;; (%VECTOR-FOLD2+ <kons> <knil> <vector> ...) -> knil'
;;;     (KONS <index> <knil> <elt> ...) -> knil'
(define %vector-fold2+
  (letrec ((loop (lambda (kons knil len vectors i)
                   (if (= i len)
                       knil
                       (loop kons
                             (apply kons i knil
                                    (vectors-ref vectors i))
                             len vectors (+ i 1))))))
    (lambda (kons knil len vectors)
      (loop kons knil len vectors 0))))

;;; (%VECTOR-MAP! <f> <target> <length> <vector>) -> target
;;;     (F <index> <elt>) -> elt'
(define %vector-map1!
  (letrec ((loop (lambda (f target vec i)
                   (if (zero? i)
                       target
                       (let ((j (- i 1)))
                         (vector-set! target j
                                      (f j (vector-ref vec j)))
                         (loop f target vec j))))))
    (lambda (f target vec len)
      (loop f target vec len))))

;;; (%VECTOR-MAP2+! <f> <target> <vectors> <len>) -> target
;;;     (F <index> <elt> ...) -> elt'
(define %vector-map2+!
  (letrec ((loop (lambda (f target vectors i)
                   (if (zero? i)
                       target
                       (let ((j (- i 1)))
                         (vector-set! target j
                           (apply f j (vectors-ref vectors j)))
                         (loop f target vectors j))))))
    (lambda (f target vectors len)
      (loop f target vectors len))))

 

;;;;;;;;;;;;;;;;;;;;;;;; ***** vector-lib ***** ;;;;;;;;;;;;;;;;;;;;;;;

;;; --------------------
;;; Constructors

;;; (MAKE-VECTOR <size> [<fill>]) -> vector
;;;   [R5RS] Create a vector of length LENGTH.  If FILL is present,
;;;   initialize each slot in the vector with it; if not, the vector's
;;;   initial contents are unspecified.
(define make-vector make-vector)

;;; (VECTOR <elt> ...) -> vector
;;;   [R5RS] Create a vector containing ELEMENT ..., in order.
(define vector vector)

;;; This ought to be able to be implemented much more efficiently -- if
;;; we have the number of arguments available to us, we can create the
;;; vector without using LENGTH to determine the number of elements it
;;; should have.
;(define (vector . elements) (list->vector elements))

;;; (VECTOR-UNFOLD <f> <length> <initial-seed> ...) -> vector
;;;     (F <index> <seed> ...) -> [elt seed' ...]
;;;   The fundamental vector constructor.  Creates a vector whose
;;;   length is LENGTH and iterates across each index K between 0 and
;;;   LENGTH, applying F at each iteration to the current index and the
;;;   current seeds to receive N+1 values: first, the element to put in
;;;   the Kth slot and then N new seeds for the next iteration.
(define vector-unfold
  (letrec ((tabulate!                   ; Special zero-seed case.
            (lambda (f vec i len)
              (cond ((< i len)
                     (vector-set! vec i (f i))
                     (tabulate! f vec (+ i 1) len)))))
           (unfold1!                    ; Fast path for one seed.
            (lambda (f vec i len seed)
              (if (< i len)
                  (receive (elt new-seed)
                           (f i seed)
                    (vector-set! vec i elt)
                    (unfold1! f vec (+ i 1) len new-seed)))))
           (unfold2+!                   ; Slower variant for N seeds.
            (lambda (f vec i len seeds)
              (if (< i len)
                  (receive (elt . new-seeds)
                           (apply f i seeds)
                    (vector-set! vec i elt)
                    (unfold2+! f vec (+ i 1) len new-seeds))))))
    (lambda (f len . initial-seeds)
      (let ((f   (check-type procedure?  f   vector-unfold))
            (len (check-type nonneg-int? len vector-unfold)))
        (let ((vec (make-vector len)))
          (cond ((null? initial-seeds)
                 (tabulate! f vec 0 len))
                ((null? (cdr initial-seeds))
                 (unfold1! f vec 0 len (car initial-seeds)))
                (else
                 (unfold2+! f vec 0 len initial-seeds)))
          vec)))))

;;; (VECTOR-UNFOLD-RIGHT <f> <length> <initial-seed> ...) -> vector
;;;     (F <seed> ...) -> [seed' ...]
;;;   Like VECTOR-UNFOLD, but it generates elements from LENGTH to 0
;;;   (still exclusive with  LENGTH and inclusive with 0), not 0 to
;;;   LENGTH as with VECTOR-UNFOLD.
(define vector-unfold-right
  (letrec ((tabulate!
            (lambda (f vec i)
              (cond ((>= i 0)
                     (vector-set! vec i (f i))
                     (tabulate! f vec (- i 1))))))
           (unfold1!
            (lambda (f vec i seed)
              (if (>= i 0)
                  (receive (elt new-seed)
                           (f i seed)
                    (vector-set! vec i elt)
                    (unfold1! f vec (- i 1) new-seed)))))
           (unfold2+!
            (lambda (f vec i seeds)
              (if (>= i 0)
                  (receive (elt . new-seeds)
                           (apply f i seeds)
                    (vector-set! vec i elt)
                    (unfold2+! f vec (- i 1) new-seeds))))))
    (lambda (f len . initial-seeds)
      (let ((f   (check-type procedure?  f   vector-unfold-right))
            (len (check-type nonneg-int? len vector-unfold-right)))
        (let ((vec (make-vector len))
              (i (- len 1)))
          (cond ((null? initial-seeds)
                 (tabulate! f vec i))
                ((null? (cdr initial-seeds))
                 (unfold1!  f vec i (car initial-seeds)))
                (else
                 (unfold2+! f vec i initial-seeds)))
          vec)))))

;;; (VECTOR-COPY <vector> [<start> <end> <fill>]) -> vector
;;;   Create a newly allocated vector containing the elements from the
;;;   range [START,END) in VECTOR.  START defaults to 0; END defaults
;;;   to the length of VECTOR.  END may be greater than the length of
;;;   VECTOR, in which case the vector is enlarged; if FILL is passed,
;;;   the new locations from which there is no respective element in
;;;   VECTOR are filled with FILL.
(define (vector-copy vec . args)
  (let ((vec (check-type vector? vec vector-copy)))
    ;; We can't use LET-VECTOR-START+END, because we have one more
    ;; argument, and we want finer control, too.
    ;;
    ;; Olin's implementation of LET*-OPTIONALS would prove useful here:
    ;; the built-in argument-checks-as-you-go-along produces almost
    ;; _exactly_ the same code as VECTOR-COPY:PARSE-ARGS.
    (receive (start end fill)
             (vector-copy:parse-args vec args)
      (let ((new-vector (make-vector (- end start) fill)))
        (%vector-copy! new-vector 0
                       vec        start
                       (if (> end (vector-length vec))
                           (vector-length vec)
                           end))
        new-vector))))

;;; Auxiliary for VECTOR-COPY.
;;; [wdc] Corrected to allow 0 <= start <= (vector-length vec).
(define (vector-copy:parse-args vec args)
  (define (parse-args start end n fill)
    (let ((start (check-type nonneg-int? start vector-copy))
          (end   (check-type nonneg-int? end vector-copy)))
      (cond ((and (<= 0 start end)
                  (<= start n))
             (values start end fill))
            (else
             (error "illegal arguments"
                    `(while calling ,vector-copy)
                    `(start was ,start)
                    `(end was ,end)
                    `(vector was ,vec))))))
  (let ((n (vector-length vec)))
    (cond ((null? args)
           (parse-args 0 n n (unspecified-value)))
          ((null? (cdr args))
           (parse-args (car args) n n (unspecified-value)))
          ((null? (cddr args))
           (parse-args (car args) (cadr args) n (unspecified-value)))
          ((null? (cdddr args))
           (parse-args (car args) (cadr args) n (caddr args)))
          (else
           (error "too many arguments"
                  vector-copy
                  (cdddr args))))))

;;; (VECTOR-REVERSE-COPY <vector> [<start> <end>]) -> vector
;;;   Create a newly allocated vector whose elements are the reversed
;;;   sequence of elements between START and END in VECTOR.  START's
;;;   default is 0; END's default is the length of VECTOR.
(define (vector-reverse-copy vec . maybe-start+end)
  (let-vector-start+end vector-reverse-copy vec maybe-start+end
                        (start end)
    (let ((new (make-vector (- end start))))
      (%vector-reverse-copy! new 0 vec start end)
      new)))

;;; (VECTOR-APPEND <vector> ...) -> vector
;;;   Append VECTOR ... into a newly allocated vector and return that
;;;   new vector.
(define (vector-append . vectors)
  (vector-concatenate:aux vectors vector-append))

;;; (VECTOR-CONCATENATE <vector-list>) -> vector
;;;   Concatenate the vectors in VECTOR-LIST.  This is equivalent to
;;;     (apply vector-append VECTOR-LIST)
;;;   but VECTOR-APPEND tends to be implemented in terms of
;;;   VECTOR-CONCATENATE, and some Schemes bork when the list to apply
;;;   a function to is too long.
;;;
;;; Actually, they're both implemented in terms of an internal routine.
(define (vector-concatenate vector-list)
  (vector-concatenate:aux vector-list vector-concatenate))

;;; Auxiliary for VECTOR-APPEND and VECTOR-CONCATENATE
(define vector-concatenate:aux
  (letrec ((compute-length
            (lambda (vectors len callee)
              (if (null? vectors)
                  len
                  (let ((vec (check-type vector? (car vectors)
                                         callee)))
                    (compute-length (cdr vectors)
                                    (+ (vector-length vec) len)
                                    callee)))))
           (concatenate!
            (lambda (vectors target to)
              (if (null? vectors)
                  target
                  (let* ((vec1 (car vectors))
                         (len (vector-length vec1)))
                    (%vector-copy! target to vec1 0 len)
                    (concatenate! (cdr vectors) target
                                  (+ to len)))))))
    (lambda (vectors callee)
      (cond ((null? vectors)            ;+++
             (make-vector 0))
            ((null? (cdr vectors))      ;+++
             ;; Blech, we still have to allocate a new one.
             (let* ((vec (check-type vector? (car vectors) callee))
                    (len (vector-length vec))
                    (new (make-vector len)))
               (%vector-copy! new 0 vec 0 len)
               new))
            (else
             (let ((new-vector
                    (make-vector (compute-length vectors 0 callee))))
               (concatenate! vectors new-vector 0)
               new-vector))))))

 

;;; --------------------
;;; Predicates

;;; (VECTOR? <value>) -> boolean
;;;   [R5RS] Return #T if VALUE is a vector and #F if not.
(define vector? vector?)

;;; (VECTOR-EMPTY? <vector>) -> boolean
;;;   Return #T if VECTOR has zero elements in it, i.e. VECTOR's length
;;;   is 0, and #F if not.
(define (vector-empty? vec)
  (let ((vec (check-type vector? vec vector-empty?)))
    (zero? (vector-length vec))))

;;; (VECTOR= <elt=?> <vector> ...) -> boolean
;;;     (ELT=? <value> <value>) -> boolean
;;;   Determine vector equality generalized across element comparators.
;;;   Vectors A and B are equal iff their lengths are the same and for
;;;   each respective elements E_a and E_b (element=? E_a E_b) returns
;;;   a true value.  ELT=? is always applied to two arguments.  Element
;;;   comparison must be consistent wtih EQ?; that is, if (eq? E_a E_b)
;;;   results in a true value, then (ELEMENT=? E_a E_b) must result in a
;;;   true value.  This may be exploited to avoid multiple unnecessary
;;;   element comparisons.  (This implementation does, but does not deal
;;;   with the situation that ELEMENT=? is EQ? to avoid more unnecessary
;;;   comparisons, but I believe this optimization is probably fairly
;;;   insignificant.)
;;;   
;;;   If the number of vector arguments is zero or one, then #T is
;;;   automatically returned.  If there are N vector arguments,
;;;   VECTOR_1 VECTOR_2 ... VECTOR_N, then VECTOR_1 & VECTOR_2 are
;;;   compared; if they are equal, the vectors VECTOR_2 ... VECTOR_N
;;;   are compared.  The precise order in which ELT=? is applied is not
;;;   specified.
(define (vector= elt=? . vectors)
  (let ((elt=? (check-type procedure? elt=? vector=)))
    (cond ((null? vectors)
           #t)
          ((null? (cdr vectors))
           (check-type vector? (car vectors) vector=)
           #t)
          (else
           (let loop ((vecs vectors))
             (let ((vec1 (check-type vector? (car vecs) vector=))
                   (vec2+ (cdr vecs)))
               (or (null? vec2+)
                   (and (binary-vector= elt=? vec1 (car vec2+))
                        (loop vec2+)))))))))
(define (binary-vector= elt=? vector-a vector-b)
  (or (eq? vector-a vector-b)           ;+++
      (let ((length-a (vector-length vector-a))
            (length-b (vector-length vector-b)))
        (letrec ((loop (lambda (i)
                         (or (= i length-a)
                             (and (< i length-b)
                                  (test (vector-ref vector-a i)
                                        (vector-ref vector-b i)
                                        i)))))
                 (test (lambda (elt-a elt-b i)
                         (and (or (eq? elt-a elt-b) ;+++
                                  (elt=? elt-a elt-b))
                              (loop (+ i 1))))))
          (and (= length-a length-b)
               (loop 0))))))

 

;;; --------------------
;;; Selectors

;;; (VECTOR-REF <vector> <index>) -> value
;;;   [R5RS] Return the value that the location in VECTOR at INDEX is
;;;   mapped to in the store.
(define vector-ref vector-ref)

;;; (VECTOR-LENGTH <vector>) -> exact, nonnegative integer
;;;   [R5RS] Return the length of VECTOR.
(define vector-length vector-length)

 

;;; --------------------
;;; Iteration

;;; (VECTOR-FOLD <kons> <initial-knil> <vector> ...) -> knil
;;;     (KONS <knil> <elt> ...) -> knil' ; N vectors -> N+1 args
;;;   The fundamental vector iterator.  KONS is iterated over each
;;;   index in all of the vectors in parallel, stopping at the end of
;;;   the shortest; KONS is applied to an argument list of (list I
;;;   STATE (vector-ref VEC I) ...), where STATE is the current state
;;;   value -- the state value begins with KNIL and becomes whatever
;;;   KONS returned at the respective iteration --, and I is the
;;;   current index in the iteration.  The iteration is strictly left-
;;;   to-right.
;;;     (vector-fold KONS KNIL (vector E_1 E_2 ... E_N))
;;;       <=>
;;;     (KONS (... (KONS (KONS KNIL E_1) E_2) ... E_N-1) E_N)
(define (vector-fold kons knil vec . vectors)
  (let ((kons (check-type procedure? kons vector-fold))
        (vec  (check-type vector?    vec  vector-fold)))
    (if (null? vectors)
        (%vector-fold1 kons knil (vector-length vec) vec)
        (%vector-fold2+ kons knil
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-fold)
                        (cons vec vectors)))))

;;; (VECTOR-FOLD-RIGHT <kons> <initial-knil> <vector> ...) -> knil
;;;     (KONS <knil> <elt> ...) -> knil' ; N vectors => N+1 args
;;;   The fundamental vector recursor.  Iterates in parallel across
;;;   VECTOR ... right to left, applying KONS to the elements and the
;;;   current state value; the state value becomes what KONS returns
;;;   at each next iteration.  KNIL is the initial state value.
;;;     (vector-fold-right KONS KNIL (vector E_1 E_2 ... E_N))
;;;       <=>
;;;     (KONS (... (KONS (KONS KNIL E_N) E_N-1) ... E_2) E_1)
;;;
;;; Not implemented in terms of a more primitive operations that might
;;; called %VECTOR-FOLD-RIGHT due to the fact that it wouldn't be very
;;; useful elsewhere.
(define vector-fold-right
  (letrec ((loop1 (lambda (kons knil vec i)
                    (if (negative? i)
                        knil
                        (loop1 kons (kons i knil (vector-ref vec i))
                               vec
                               (- i 1)))))
           (loop2+ (lambda (kons knil vectors i)
                     (if (negative? i)
                         knil
                         (loop2+ kons
                                 (apply kons i knil
                                        (vectors-ref vectors i))
                                 vectors
                                 (- i 1))))))
    (lambda (kons knil vec . vectors)
      (let ((kons (check-type procedure? kons vector-fold-right))
            (vec  (check-type vector?    vec  vector-fold-right)))
        (if (null? vectors)
            (loop1  kons knil vec (- (vector-length vec) 1))
            (loop2+ kons knil (cons vec vectors)
                    (- (%smallest-length vectors
                                         (vector-length vec)
                                         vector-fold-right)
                       1)))))))

;;; (VECTOR-MAP <f> <vector> ...) -> vector
;;;     (F <elt> ...) -> value ; N vectors -> N args
;;;   Constructs a new vector of the shortest length of the vector
;;;   arguments.  Each element at index I of the new vector is mapped
;;;   from the old vectors by (F I (vector-ref VECTOR I) ...).  The
;;;   dynamic order of application of F is unspecified.
(define (vector-map f vec . vectors)
  (let ((f   (check-type procedure? f   vector-map))
        (vec (check-type vector?    vec vector-map)))
    (if (null? vectors)
        (let ((len (vector-length vec)))
          (%vector-map1! f (make-vector len) vec len))
        (let ((len (%smallest-length vectors
                                     (vector-length vec)
                                     vector-map)))
          (%vector-map2+! f (make-vector len) (cons vec vectors)
                          len)))))

;;; (VECTOR-MAP! <f> <vector> ...) -> unspecified
;;;     (F <elt> ...) -> element' ; N vectors -> N args
;;;   Similar to VECTOR-MAP, but rather than mapping the new elements
;;;   into a new vector, the new mapped elements are destructively
;;;   inserted into the first vector.  Again, the dynamic order of
;;;   application of F is unspecified, so it is dangerous for F to
;;;   manipulate the first VECTOR.
(define (vector-map! f vec . vectors)
  (let ((f   (check-type procedure? f   vector-map!))
        (vec (check-type vector?    vec vector-map!)))
    (if (null? vectors)
        (%vector-map1!  f vec vec (vector-length vec))
        (%vector-map2+! f vec (cons vec vectors)
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-map!)))
    (unspecified-value)))

;;; (VECTOR-FOR-EACH <f> <vector> ...) -> unspecified
;;;     (F <elt> ...) ; N vectors -> N args
;;;   Simple vector iterator: applies F to each index in the range [0,
;;;   LENGTH), where LENGTH is the length of the smallest vector
;;;   argument passed, and the respective element at that index.  In
;;;   contrast with VECTOR-MAP, F is reliably applied to each
;;;   subsequent elements, starting at index 0 from left to right, in
;;;   the vectors.
(define vector-for-each
  (letrec ((for-each1
            (lambda (f vec i len)
              (cond ((< i len)
                     (f i (vector-ref vec i))
                     (for-each1 f vec (+ i 1) len)))))
           (for-each2+
            (lambda (f vecs i len)
              (cond ((< i len)
                     (apply f i (vectors-ref vecs i))
                     (for-each2+ f vecs (+ i 1) len))))))
    (lambda (f vec . vectors)
      (let ((f   (check-type procedure? f   vector-for-each))
            (vec (check-type vector?    vec vector-for-each)))
        (if (null? vectors)
            (for-each1 f vec 0 (vector-length vec))
            (for-each2+ f (cons vec vectors) 0
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-for-each)))))))

;;; (VECTOR-COUNT <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer
;;;     (PREDICATE? <index> <value> ...) ; N vectors -> N+1 args
;;;   PREDICATE? is applied element-wise to the elements of VECTOR ...,
;;;   and a count is tallied of the number of elements for which a
;;;   true value is produced by PREDICATE?.  This count is returned.
(define (vector-count pred? vec . vectors)
  (let ((pred? (check-type procedure? pred? vector-count))
        (vec   (check-type vector?    vec   vector-count)))
    (if (null? vectors)
        (%vector-fold1 (lambda (index count elt)
                         (if (pred? index elt)
                             (+ count 1)
                             count))
                       0
                       (vector-length vec)
                       vec)
        (%vector-fold2+ (lambda (index count . elts)
                          (if (apply pred? index elts)
                              (+ count 1)
                              count))
                        0
                        (%smallest-length vectors
                                          (vector-length vec)
                                          vector-count)
                        (cons vec vectors)))))

 

;;; --------------------
;;; Searching

;;; (VECTOR-INDEX <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   Search left-to-right across VECTOR ... in parallel, returning the
;;;   index of the first set of values VALUE ... such that (PREDICATE?
;;;   VALUE ...) returns a true value; if no such set of elements is
;;;   reached, return #F.
(define (vector-index pred? vec . vectors)
  (vector-index/skip pred? vec vectors vector-index))

;;; (VECTOR-SKIP <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   (vector-index (lambda elts (not (apply PREDICATE? elts)))
;;;                 VECTOR ...)
;;;   Like VECTOR-INDEX, but find the index of the first set of values
;;;   that do _not_ satisfy PREDICATE?.
(define (vector-skip pred? vec . vectors)
  (vector-index/skip (lambda elts (not (apply pred? elts)))
                     vec vectors
                     vector-skip))

;;; Auxiliary for VECTOR-INDEX & VECTOR-SKIP
(define vector-index/skip
  (letrec ((loop1  (lambda (pred? vec len i)
                     (cond ((= i len) #f)
                           ((pred? (vector-ref vec i)) i)
                           (else (loop1 pred? vec len (+ i 1))))))
           (loop2+ (lambda (pred? vectors len i)
                     (cond ((= i len) #f)
                           ((apply pred? (vectors-ref vectors i)) i)
                           (else (loop2+ pred? vectors len
                                         (+ i 1)))))))
    (lambda (pred? vec vectors callee)
      (let ((pred? (check-type procedure? pred? callee))
            (vec   (check-type vector?    vec   callee)))
        (if (null? vectors)
            (loop1 pred? vec (vector-length vec) 0)
            (loop2+ pred? (cons vec vectors)
                    (%smallest-length vectors
                                      (vector-length vec)
                                      callee)
                    0))))))

;;; (VECTOR-INDEX-RIGHT <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   Right-to-left variant of VECTOR-INDEX.
(define (vector-index-right pred? vec . vectors)
  (vector-index/skip-right pred? vec vectors vector-index-right))

;;; (VECTOR-SKIP-RIGHT <predicate?> <vector> ...)
;;;       -> exact, nonnegative integer or #F
;;;     (PREDICATE? <elt> ...) -> boolean ; N vectors -> N args
;;;   Right-to-left variant of VECTOR-SKIP.
(define (vector-skip-right pred? vec . vectors)
  (vector-index/skip-right (lambda elts (not (apply pred? elts)))
                           vec vectors
                           vector-index-right))

(define vector-index/skip-right
  (letrec ((loop1  (lambda (pred? vec i)
                     (cond ((negative? i) #f)
                           ((pred? (vector-ref vec i)) i)
                           (else (loop1 pred? vec (- i 1))))))
           (loop2+ (lambda (pred? vectors i)
                     (cond ((negative? i) #f)
                           ((apply pred? (vectors-ref vectors i)) i)
                           (else (loop2+ pred? vectors (- i 1)))))))
    (lambda (pred? vec vectors callee)
      (let ((pred? (check-type procedure? pred? callee))
            (vec   (check-type vector?    vec   callee)))
        (if (null? vectors)
            (loop1 pred? vec (- (vector-length vec) 1))
            (loop2+ pred? (cons vec vectors)
                    (- (%smallest-length vectors
                                         (vector-length vec)
                                         callee)
                       1)))))))

;;; (VECTOR-BINARY-SEARCH <vector> <value> <cmp> [<start> <end>])
;;;       -> exact, nonnegative integer or #F
;;;     (CMP <value1> <value2>) -> integer
;;;       positive -> VALUE1 > VALUE2
;;;       zero     -> VALUE1 = VALUE2
;;;       negative -> VALUE1 < VALUE2
;;;   Perform a binary search through VECTOR for VALUE, comparing each
;;;   element to VALUE with CMP.
(define (vector-binary-search vec value cmp . maybe-start+end)
  (let ((cmp (check-type procedure? cmp vector-binary-search)))
    (let-vector-start+end vector-binary-search vec maybe-start+end
                          (start end)
      (let loop ((start start) (end end) (j #f))
        (let ((i (quotient (+ start end) 2)))
          (if (or (= start end) (and j (= i j)))
              #f
              (let ((comparison
                     (check-type integer?
                                 (cmp (vector-ref vec i) value)
                                 `(,cmp for ,vector-binary-search))))
                (cond ((zero?     comparison) i)
                      ((positive? comparison) (loop start i i))
                      (else                   (loop i end i))))))))))

;;; (VECTOR-ANY <pred?> <vector> ...) -> value
;;;   Apply PRED? to each parallel element in each VECTOR ...; if PRED?
;;;   should ever return a true value, immediately stop and return that
;;;   value; otherwise, when the shortest vector runs out, return #F.
;;;   The iteration and order of application of PRED? across elements
;;;   is of the vectors is strictly left-to-right.
(define vector-any
  (letrec ((loop1 (lambda (pred? vec i len len-1)
                    (and (not (= i len))
                         (if (= i len-1)
                             (pred? (vector-ref vec i))
                             (or (pred? (vector-ref vec i))
                                 (loop1 pred? vec (+ i 1)
                                        len len-1))))))
           (loop2+ (lambda (pred? vectors i len len-1)
                     (and (not (= i len))
                          (if (= i len-1)
                              (apply pred? (vectors-ref vectors i))
                              (or (apply pred? (vectors-ref vectors i))
                                  (loop2+ pred? vectors (+ i 1)
                                         len len-1)))))))
    (lambda (pred? vec . vectors)
      (let ((pred? (check-type procedure? pred? vector-any))
            (vec   (check-type vector?    vec   vector-any)))
        (if (null? vectors)
            (let ((len (vector-length vec)))
              (loop1 pred? vec 0 len (- len 1)))
            (let ((len (%smallest-length vectors
                                         (vector-length vec)
                                         vector-any)))
              (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))

;;; (VECTOR-EVERY <pred?> <vector> ...) -> value
;;;   Apply PRED? to each parallel value in each VECTOR ...; if PRED?
;;;   should ever return #F, immediately stop and return #F; otherwise,
;;;   if PRED? should return a true value for each element, stopping at
;;;   the end of the shortest vector, return the last value that PRED?
;;;   returned.  In the case that there is an empty vector, return #T.
;;;   The iteration and order of application of PRED? across elements
;;;   is of the vectors is strictly left-to-right.
(define vector-every
  (letrec ((loop1 (lambda (pred? vec i len len-1)
                    (or (= i len)
                        (if (= i len-1)
                            (pred? (vector-ref vec i))
                            (and (pred? (vector-ref vec i))
                                 (loop1 pred? vec (+ i 1)
                                        len len-1))))))
           (loop2+ (lambda (pred? vectors i len len-1)
                     (or (= i len)
                         (if (= i len-1)
                             (apply pred? (vectors-ref vectors i))
                             (and (apply pred? (vectors-ref vectors i))
                                  (loop2+ pred? vectors (+ i 1)
                                          len len-1)))))))
    (lambda (pred? vec . vectors)
      (let ((pred? (check-type procedure? pred? vector-every))
            (vec   (check-type vector?    vec   vector-every)))
        (if (null? vectors)
            (let ((len (vector-length vec)))
              (loop1 pred? vec 0 len (- len 1)))
            (let ((len (%smallest-length vectors
                                         (vector-length vec)
                                         vector-every)))
              (loop2+ pred? (cons vec vectors) 0 len (- len 1))))))))

 

;;; --------------------
;;; Mutators

;;; (VECTOR-SET! <vector> <index> <value>) -> unspecified
;;;   [R5RS] Assign the location at INDEX in VECTOR to VALUE.
(define vector-set! vector-set!)

;;; (VECTOR-SWAP! <vector> <index1> <index2>) -> unspecified
;;;   Swap the values in the locations at INDEX1 and INDEX2.
(define (vector-swap! vec i j)
  (let ((vec (check-type vector? vec vector-swap!)))
    (let ((i (check-index vec i vector-swap!))
          (j (check-index vec j vector-swap!)))
      (let ((x (vector-ref vec i)))
        (vector-set! vec i (vector-ref vec j))
        (vector-set! vec j x)))))

;;; (VECTOR-FILL! <vector> <value> [<start> <end>]) -> unspecified
;;;   [R5RS+] Fill the locations in VECTOR between START, whose default
;;;   is 0, and END, whose default is the length of VECTOR, with VALUE.
;;;
;;; This one can probably be made really fast natively.
(define vector-fill!
  (let ((%vector-fill! vector-fill!))   ; Take the native one, under
                                        ;   the assumption that it's
                                        ;   faster, so we can use it if
                                        ;   there are no optional
                                        ;   arguments.
    (lambda (vec value . maybe-start+end)
      (if (null? maybe-start+end)
          (%vector-fill! vec value)     ;+++
          (let-vector-start+end vector-fill! vec maybe-start+end
                                (start end)
            (do ((i start (+ i 1)))
                ((= i end))
              (vector-set! vec i value)))))))

;;; (VECTOR-COPY! <target> <tstart> <source> [<sstart> <send>])
;;;       -> unspecified
;;;   Copy the values in the locations in [SSTART,SEND) from SOURCE to
;;;   to TARGET, starting at TSTART in TARGET.
;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
(define (vector-copy! target tstart source . maybe-sstart+send)
  (define (doit! sstart send source-length)
    (let ((tstart (check-type nonneg-int? tstart vector-copy!))
          (sstart (check-type nonneg-int? sstart vector-copy!))
          (send   (check-type nonneg-int? send vector-copy!)))
      (cond ((and (<= 0 sstart send source-length)
                  (<= (+ tstart (- send sstart)) (vector-length target)))
             (%vector-copy! target tstart source sstart send))
            (else
             (error "illegal arguments"
                    `(while calling ,vector-copy!)
                    `(target was ,target)
                    `(target-length was ,(vector-length target))
                    `(tstart was ,tstart)
                    `(source was ,source)
                    `(source-length was ,source-length)
                    `(sstart was ,sstart)
                    `(send   was ,send))))))
  (let ((n (vector-length source)))
    (cond ((null? maybe-sstart+send)
           (doit! 0 n n))
          ((null? (cdr maybe-sstart+send))
           (doit! (car maybe-sstart+send) n n))
          ((null? (cddr maybe-sstart+send))
           (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
          (else
           (error "too many arguments"
                  vector-copy!
                  (cddr maybe-sstart+send))))))

;;; (VECTOR-REVERSE-COPY! <target> <tstart> <source> [<sstart> <send>])
;;; [wdc] Corrected to allow 0 <= sstart <= send <= (vector-length source).
(define (vector-reverse-copy! target tstart source . maybe-sstart+send)
  (define (doit! sstart send source-length)
    (let ((tstart (check-type nonneg-int? tstart vector-reverse-copy!))
          (sstart (check-type nonneg-int? sstart vector-reverse-copy!))
          (send   (check-type nonneg-int? send vector-reverse-copy!)))
      (cond ((and (eq? target source)
                  (or (between? sstart tstart send)
                      (between? tstart sstart
                                (+ tstart (- send sstart)))))
               (error "vector range for self-copying overlaps"
                      vector-reverse-copy!
                      `(vector was ,target)
                      `(tstart was ,tstart)
                      `(sstart was ,sstart)
                      `(send   was ,send)))
            ((and (<= 0 sstart send source-length)
                  (<= (+ tstart (- send sstart)) (vector-length target)))
             (%vector-reverse-copy! target tstart source sstart send))
            (else
             (error "illegal arguments"
                    `(while calling ,vector-reverse-copy!)
                    `(target was ,target)
                    `(target-length was ,(vector-length target))
                    `(tstart was ,tstart)
                    `(source was ,source)
                    `(source-length was ,source-length)
                    `(sstart was ,sstart)
                    `(send   was ,send))))))
  (let ((n (vector-length source)))
    (cond ((null? maybe-sstart+send)
           (doit! 0 n n))
          ((null? (cdr maybe-sstart+send))
           (doit! (car maybe-sstart+send) n n))
          ((null? (cddr maybe-sstart+send))
           (doit! (car maybe-sstart+send) (cadr maybe-sstart+send) n))
          (else
           (error "too many arguments"
                  vector-reverse-copy!
                  (cddr maybe-sstart+send))))))

;;; (VECTOR-REVERSE! <vector> [<start> <end>]) -> unspecified
;;;   Destructively reverse the contents of the sequence of locations
;;;   in VECTOR between START, whose default is 0, and END, whose
;;;   default is the length of VECTOR.
(define (vector-reverse! vec . start+end)
  (let-vector-start+end vector-reverse! vec start+end
                        (start end)
    (%vector-reverse! vec start end)))

 

;;; --------------------
;;; Conversion

;;; (VECTOR->LIST <vector> [<start> <end>]) -> list
;;;   [R5RS+] Produce a list containing the elements in the locations
;;;   between START, whose default is 0, and END, whose default is the
;;;   length of VECTOR, from VECTOR.
(define vector->list
  (let ((%vector->list vector->list))
    (lambda (vec . maybe-start+end)
      (if (null? maybe-start+end)       ; Oughta use CASE-LAMBDA.
          (%vector->list vec)           ;+++
          (let-vector-start+end vector->list vec maybe-start+end
                                (start end)
            ;(unfold (lambda (i)        ; No SRFI 1.
            ;          (< i start))
            ;        (lambda (i) (vector-ref vec i))
            ;        (lambda (i) (- i 1))
            ;        (- end 1))
            (do ((i (- end 1) (- i 1))
                 (result '() (cons (vector-ref vec i) result)))
                ((< i start) result)))))))

;;; (REVERSE-VECTOR->LIST <vector> [<start> <end>]) -> list
;;;   Produce a list containing the elements in the locations between
;;;   START, whose default is 0, and END, whose default is the length
;;;   of VECTOR, from VECTOR, in reverse order.
(define (reverse-vector->list vec . maybe-start+end)
  (let-vector-start+end reverse-vector->list vec maybe-start+end
                        (start end)
    ;(unfold (lambda (i) (= i end))     ; No SRFI 1.
    ;        (lambda (i) (vector-ref vec i))
    ;        (lambda (i) (+ i 1))
    ;        start)
    (do ((i start (+ i 1))
         (result '() (cons (vector-ref vec i) result)))
        ((= i end) result))))

;;; (LIST->VECTOR <list> [<start> <end>]) -> vector
;;;   [R5RS+] Produce a vector containing the elements in LIST, which
;;;   must be a proper list, between START, whose default is 0, & END,
;;;   whose default is the length of LIST.  It is suggested that if the
;;;   length of LIST is known in advance, the START and END arguments
;;;   be passed, so that LIST->VECTOR need not call LENGTH to determine
;;;   the the length.
;;;
;;; This implementation diverges on circular lists, unless LENGTH fails
;;; and causes - to fail as well.  Given a LENGTH* that computes the
;;; length of a list's cycle, this wouldn't diverge, and would work
;;; great for circular lists.
(define list->vector
  (let ((%list->vector list->vector))
    (lambda (lst . maybe-start+end)
      ;; Checking the type of a proper list is expensive, so we do it
      ;; amortizedly, or let %LIST->VECTOR or LIST-TAIL do it.
      (if (null? maybe-start+end)       ; Oughta use CASE-LAMBDA.
          (%list->vector lst)           ;+++
          ;; We can't use LET-VECTOR-START+END, because we're using the
          ;; bounds of a _list_, not a vector.
          (let*-optionals maybe-start+end
              ((start 0)
               (end (length lst)))      ; Ugh -- LENGTH
            (let ((start (check-type nonneg-int? start list->vector))
                  (end   (check-type nonneg-int? end   list->vector)))
              ((lambda (f)
                 (vector-unfold f (- end start) (list-tail lst start)))
               (lambda (index l)
                 (cond ((null? l)
                        (error "list was too short"
                               `(list was ,lst)
                               `(attempted end was ,end)
                               `(while calling ,list->vector)))
                       ((pair? l)
                        (values (car l) (cdr l)))
                       (else
                        ;; Make this look as much like what CHECK-TYPE
                        ;; would report as possible.
                        (error "erroneous value"
                               ;; We want SRFI 1's PROPER-LIST?, but it
                               ;; would be a waste to link all of SRFI
                               ;; 1 to this module for only the single
                               ;; function PROPER-LIST?.
                               (list list? lst)
                               `(while calling
                                 ,list->vector))))))))))))

;;; (REVERSE-LIST->VECTOR <list> [<start> <end>]) -> vector
;;;   Produce a vector containing the elements in LIST, which must be a
;;;   proper list, between START, whose default is 0, and END, whose
;;;   default is the length of LIST, in reverse order.  It is suggested
;;;   that if the length of LIST is known in advance, the START and END
;;;   arguments be passed, so that REVERSE-LIST->VECTOR need not call
;;;   LENGTH to determine the the length.
;;;
;;; This also diverges on circular lists unless, again, LENGTH returns
;;; something that makes - bork.
(define (reverse-list->vector lst . maybe-start+end)
  (let*-optionals maybe-start+end
      ((start 0)
       (end (length lst)))              ; Ugh -- LENGTH
    (let ((start (check-type nonneg-int? start reverse-list->vector))
          (end   (check-type nonneg-int? end   reverse-list->vector)))
      ((lambda (f)
         (vector-unfold-right f (- end start) (list-tail lst start)))
       (lambda (index l)
         (cond ((null? l)
                (error "list too short"
                       `(list was ,lst)
                       `(attempted end was ,end)
                       `(while calling ,reverse-list->vector)))
               ((pair? l)
                (values (car l) (cdr l)))
               (else
                (error "erroneous value"
                       (list list? lst)
                       `(while calling ,reverse-list->vector)))))))))

Added srfi/s43/vectors.sls.



















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s43 vectors)
  (export
    ;;; * Constructors
    make-vector vector
    vector-unfold         vector-unfold-right
    vector-copy           vector-reverse-copy
    vector-append         vector-concatenate
    ;;; * Predicates
    vector?
    vector-empty?
    vector=
    ;;; * Selectors
    vector-ref
    vector-length
    ;;; * Iteration
    vector-fold           vector-fold-right
    vector-map            vector-map!
    vector-for-each
    vector-count
    ;;; * Searching
    vector-index          vector-skip
    vector-index-right    vector-skip-right
    vector-binary-search  vector-any    vector-every
    ;;; * Mutators
    vector-set!
    vector-swap!
    (rename (my:vector-fill! vector-fill!))
    vector-reverse!
    vector-copy!          vector-reverse-copy!
    ;;; * Conversion
    (rename (my:vector->list vector->list))          reverse-vector->list
    (rename (my:list->vector list->vector))          reverse-list->vector )
  (import
    (except (rnrs) vector-map vector-for-each)
    (rnrs r5rs)
    (srfi s23 error tricks)
    (srfi s8 receive)
    (for (srfi private vanish) expand)
    (srfi private include))

  ;; I do these let-syntax tricks so the original vector-lib.scm file does
  ;; not have to be modified at all.
  (let-syntax
      ((define
        (let ((vd (vanish-define define
                   (make-vector vector vector? vector-ref vector-set! vector-length))))
          (lambda (stx)
            (define (rename? id)
              (memp (lambda (x) (free-identifier=? id x))
                    (list #'vector-fill! #'vector->list #'list->vector)))
            (define (rename id)
              (datum->syntax id
               (string->symbol
                (string-append "my:" (symbol->string (syntax->datum id))))))
            (syntax-case stx ()
              ((_ name . r)
               (and (identifier? #'name)
                    (rename? #'name))
               #`(define #,(rename #'name) . r))
              (_ (vd stx))))))
       (define-syntax
        (vanish-define define-syntax
         (receive))))
    (SRFI-23-error->R6RS "(library (srfi s43 vectors))"
     (include/resolve ("srfi" "s43") "vector-lib.scm")))
)

Added srfi/s48/intermediate-format-strings.sls.



































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
#!r6rs
;;; FILE       "intermediate-format-strings.sls"
;;; IMPLEMENTS SRFI-48: Intermediary format strings
;;;            http://srfi.schemers.org/srfi-48/srfi-48.html
;;; AUTHOR     Ken Dickey
;;; UPDATED    Syntax updated for R6RS February 2008 by Ken Dickey
;;; LANGUAGE   R6RS but specific to Ikarus Scheme

;; Small changes by Derick Eddington to make the begining of `format'
;; more effecient and more abstracted. 

;;;Copyright (C) Kenneth A Dickey (2003). All Rights Reserved.
;;;
;;;Permission is hereby granted, free of charge, to any person
;;;obtaining a copy of this software and associated documentation
;;;files (the "Software"), to deal in the Software without
;;;restriction, including without limitation the rights to use,
;;;copy, modify, merge, publish, distribute, sublicense, and/or
;;;sell copies of the Software, and to permit persons to whom
;;;the Software is furnished to do so, subject to the following
;;;conditions:
;;;
;;;The above copyright notice and this permission notice shall
;;;be included in all copies or substantial portions of the Software.
;;;
;;;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;;EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
;;;OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;;NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
;;;HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
;;;WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;;FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;;;OTHER DEALINGS IN THE SOFTWARE.

; The implementation below requires SRFI-6 (Basic string ports), 
; and SRFI-38 (External Representation for Data With Shared Structure). 
 
(library (srfi s48 intermediate-format-strings)
  (export 
    format)
  (import
    (rnrs)
    (srfi s48 intermediate-format-strings compat)
    (srfi s6  basic-string-ports)
    (srfi s38 with-shared-structure))
  
  (define ascii-tab #\tab)
    
  (define (format arg0 . arg*)
    
    (define (problem msg . irts)
      (apply assertion-violation 'format msg irts))
    
    (define (_format port format-string args return-value)      

      (define (string-index str c)
        (let ( [len (string-length str)] )
          (let loop ( [i 0] )
            (cond ((= i len) #f)
                  ((eqv? c (string-ref str i)) i)
                  (else (loop (+ i 1)))))))
      
      (define (string-grow str len char)
        (let ( [off (- len (string-length str))] )
          (if (positive? off)
            (string-append (make-string off char) str)
            str)))
      
(define (compose-with-digits digits pre-str frac-str exp-str)
        (let ( [frac-len (string-length frac-str)] )
;;@@DEBUG
;;(format #t "~%@@(compose-with-digits digits=~s pre-str=~s frac-str=~s exp-str=~s ) ~%" digits pre-str frac-str exp-str)
          (cond	
            [(< frac-len digits) ;; grow frac part, pad with zeros
             (string-append pre-str "."
                            frac-str (make-string (- digits frac-len) #\0)
                            exp-str)
             ]
            [(= frac-len digits) ;; frac-part is exactly the right size
             (string-append pre-str "."
                            frac-str
                            exp-str)
             ]
            [else ;; must round to shrink frac-part
             (let* ( [first-part (substring frac-str 0 digits)]
                     [last-part  (substring frac-str digits frac-len)]
                     ;; NB: Scheme uses "Round to Even Rule" for .5
                     [rounded-frac
		     ;; NB: exact is r6; r5 is inexact->exact
                      (exact (round (string->number
                                        (string-append first-part "." last-part))))
                      ]
		     [rounded-frac-str (number->string rounded-frac)]
                     [rounded-frac-len (string-length rounded-frac-str)]
                     [carry? (and (not (zero? rounded-frac))
                                  (> rounded-frac-len digits))
                      ]
                     [new-frac
                      (let ( (pre-frac
                              (if carry? ;; trim leading "1"
                                  (substring rounded-frac-str 1 (min rounded-frac-len digits))
                                  (substring rounded-frac-str 0 (min rounded-frac-len digits))) ;; may be zero length
                              )
                           )
                        (if (< (string-length pre-frac) digits)
                            (string-grow pre-frac digits #\0)
                            pre-frac))
                      ]
                    )
;;@@DEBUG
;;(format #t "@@ first-part=~s last-part=~s rounded-frac=~s carry?=~s ~%" first-part last-part rounded-frac carry?)
               (string-append
                (if carry? (number->string (+ 1 (string->number pre-str))) pre-str)
                "."
                new-frac
                exp-str))]
            ) ) )
      
      
      (define (format-fixed number-or-string width digits) ; returns a string
;;@@DEBUG
;;(format #t "~%(format-fixed number-or-string=~s width=~s digits=~s)~%" number-or-string width digits)

        (cond
          [(string? number-or-string)
           (string-grow number-or-string width #\space)
           ]
          [(number? number-or-string)
           (let* ( [num (real-part number-or-string)]
                   [real (if digits (+ 0.0 num) num)]
                   [imag (imag-part number-or-string)]
                 )
             (cond
               [(not (zero? imag))
                (string-grow
                 (string-append (format-fixed real 0 digits)
                                (if (negative? imag) "" "+")
                                (format-fixed imag 0 digits)
                                "i")
                 width
                 #\space)
                ]
               [digits
                (let* ( [num-str   (number->string (if (rational? real)
                                                       (+ 0.0 real)
                                                       real))]
                        [dot-index (string-index  num-str #\.)]
                        [exp-index (string-index  num-str #\e)]
                        [length    (string-length num-str)]
                        [pre-string
                         (cond
                           ((and exp-index (not dot-index))
                            (substring num-str 0 exp-index)
                            )
                           (dot-index
                            (substring num-str 0 dot-index)
                            )
                           (else
                            num-str))
                         ]
                        [exp-string
                         (if exp-index (substring num-str exp-index length) "")
                         ]
                        [frac-string
                         (let ( (dot-idx (if dot-index dot-index -1)) )
                           (if exp-index
                               (substring num-str (+ dot-idx 1) exp-index)
                               (substring num-str (+ dot-idx 1) length)))
                         ]
                        )
                  (string-grow
                   (if dot-index
                     (compose-with-digits digits
                                          pre-string
                                          frac-string
                                          exp-string)
                     (string-append pre-string exp-string))
                   width
                   #\space)
                  )]
               [else ;; no digits
                (string-grow (number->string real) width #\space)])
             )]
          [else
           (error 'format "~F requires a number or a string" number-or-string)])
        )
      

      (define documentation-string
        "(format [<port>] <format-string> [<arg>...]) -- <port> is #t, #f or an output-port
OPTION  [MNEMONIC]      DESCRIPTION     -- Implementation Assumes ASCII Text Encoding
~H      [Help]          output this text
~A      [Any]           (display arg) for humans
~S      [Slashified]    (write arg) for parsers
~W      [WriteCircular] like ~s but outputs circular and recursive data structures
~~      [tilde]         output a tilde
~T      [Tab]           output a tab character
~%      [Newline]       output a newline character
~&      [Freshline]     output a newline character if the previous output was not a newline
~D      [Decimal]       the arg is a number which is output in decimal radix
~X      [heXadecimal]   the arg is a number which is output in hexdecimal radix
~O      [Octal]         the arg is a number which is output in octal radix
~B      [Binary]        the arg is a number which is output in binary radix
~w,dF   [Fixed]         the arg is a string or number which has width w and d digits after the decimal
~C      [Character]     charater arg is output by write-char
~_      [Space]         a single space character is output
~Y      [Yuppify]       the list arg is pretty-printed to the output
~?      [Indirection]   recursive format: next 2 args are format-string and list of arguments
~K      [Indirection]   same as ~?
"
        )
      
      (define (require-an-arg args)
        (when (null? args)
          (problem "too few arguments"))
        )
      
      (define (format-help p format-strg arglist)
        
        (letrec (
                 [length-of-format-string (string-length format-strg)]
                 
                 [anychar-dispatch       
                  (lambda (pos arglist last-was-newline) 
                    (if (>= pos length-of-format-string) 
                      arglist ; return unused args 
                      (let ( [char (string-ref format-strg pos)] ) 
                        (cond            
                          [(eqv? char #\~)   
                           (tilde-dispatch (+ pos 1) arglist last-was-newline)]
                          [else                   
                           (write-char char p)     
                           (anychar-dispatch (+ pos 1) arglist #f)        
                           ])             
                        )))     
                  ] ; end anychar-dispatch
                 
                 [has-newline?
                  (lambda (whatever last-was-newline)
                    (or (eqv? whatever #\newline)
                        (and (string? whatever)
                             (let ( [len (string-length whatever)] )
                               (if (zero? len)
                                 last-was-newline
                                 (eqv? #\newline (string-ref whatever (- len 1)))))))
                    )] ; end has-newline?
                 
                 [tilde-dispatch          
                  (lambda (pos arglist last-was-newline)     
                    (cond           
                      ((>= pos length-of-format-string)   
                       (write-char #\~ p) ; tilde at end of string is just output
                       arglist ; return unused args
                       )     
                      (else      
                       (case (char-upcase (string-ref format-strg pos)) 
                         ((#\A)       ; Any -- for humans
                          (require-an-arg arglist)
                          (let ( [whatever (car arglist)] )
                            (display whatever p)
                            (anychar-dispatch (+ pos 1) 
                                              (cdr arglist) 
                                              (has-newline? whatever last-was-newline))
                            ))
                         ((#\S)       ; Slashified -- for parsers
                          (require-an-arg arglist)
                          (let ( [whatever (car arglist)] )
                            (write whatever p)     
                            (anychar-dispatch (+ pos 1) 
                                              (cdr arglist) 
                                              (has-newline? whatever last-was-newline)) 
                            ))
                         ((#\W)
                          (require-an-arg arglist)
                          (let ( [whatever (car arglist)] )
                            (write-with-shared-structure whatever p)  ;; srfi-38
                            (anychar-dispatch (+ pos 1) 
                                              (cdr arglist) 
                                              (has-newline? whatever last-was-newline))
                            ))                           
                         ((#\D)       ; Decimal
                          (require-an-arg arglist)
                          (display (number->string (car arglist) 10) p)  
                          (anychar-dispatch (+ pos 1) (cdr arglist) #f)  
                          )            
                         ((#\X)       ; HeXadecimal
                          (require-an-arg arglist)
                          (display (number->string (car arglist) 16) p)
                          (anychar-dispatch (+ pos 1) (cdr arglist) #f)  
                          )             
                         ((#\O)       ; Octal
                          (require-an-arg arglist)
                          (display (number->string (car arglist)  8) p) 
                          (anychar-dispatch (+ pos 1) (cdr arglist) #f) 
                          )       
                         ((#\B)       ; Binary
                          (require-an-arg arglist)
                          (display (number->string (car arglist)  2) p)
                          (anychar-dispatch (+ pos 1) (cdr arglist) #f) 
                          )           
                         ((#\C)       ; Character
                          (require-an-arg arglist)
                          (write-char (car arglist) p) 
                          (anychar-dispatch (+ pos 1)
                                            (cdr arglist)
                                            (eqv? (car arglist) #\newline))  
                          )          
                         ((#\~)       ; Tilde  
                          (write-char #\~ p)   
                          (anychar-dispatch (+ pos 1) arglist #f) 
                          )            
                         ((#\%)       ; Newline   
                          (newline p) 
                          (anychar-dispatch (+ pos 1) arglist #t) 
                          )
                         ((#\&)      ; Freshline
                          (if (not last-was-newline) ;; (unless last-was-newline ..
                            (newline p))
                          (anychar-dispatch (+ pos 1) arglist #t)
                          )
                         ((#\_)       ; Space 
                          (write-char #\space p)   
                          (anychar-dispatch (+ pos 1) arglist #f)
                          )             
                         ((#\T)       ; Tab -- IMPLEMENTATION DEPENDENT ENCODING    
                          (write-char ascii-tab p)          
                          (anychar-dispatch (+ pos 1) arglist #f)     
                          )             
                         ((#\Y)       ; Pretty-print
                          (pretty-print (car arglist) p)
                          (anychar-dispatch (+ pos 1) (cdr arglist) #f)
                          )              
                         ((#\F)
                          (require-an-arg arglist)
                          (display (format-fixed (car arglist) 0 #f) p)
                          (anychar-dispatch (+ pos 1) (cdr arglist) #f)
                          )
                         ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
                          ;; gather "~w[,d]F" w and d digits
                          (let loop ( [index (+ pos 1)]
                                      [w-digits (list (string-ref format-strg pos))]
                                      [d-digits '()]
                                      [in-width? #t]
                                      )
                            (if (>= index length-of-format-string)
                              (problem "improper numeric format directive" format-strg)
                              (let ( [next-char (string-ref format-strg index)] )
                                (cond
                                  [(char-numeric? next-char)
                                   (if in-width?
                                     (loop (+ index 1)
                                           (cons next-char w-digits)
                                           d-digits
                                           in-width?)
                                     (loop (+ index 1)
                                           w-digits
                                           (cons next-char d-digits)
                                           in-width?))
                                   ]
                                  [(char=? (char-upcase next-char) #\F)
                                   (let ( [width
                                           (string->number
                                            (list->string
                                             (reverse w-digits)))
                                           ]
                                          [digits
                                           (if (zero? (length d-digits))
                                             #f
                                             (string->number
                                              (list->string (reverse d-digits))))]
                                          )
                                     (display
                                      (format-fixed (car arglist) width digits)
                                      p)
                                     (anychar-dispatch (+ index 1) (cdr arglist) #f))
                                   ]
                                  [(char=? next-char #\,)
                                   (if in-width?
                                     (loop (+ index 1)
                                           w-digits
                                           d-digits
                                           #f)
                                     (problem "too many commas in directive" format-strg))
                                   ]
                                  [else
                                   (problem "~w,dF directive ill-formed" format-strg)])))
                            ))
                         ((#\? #\K)       ; indirection -- take next arg as format string
                          (cond           ;  and following arg as list of format args
                            ((< (length arglist) 2)
                             (problem "less arguments than specified for ~?" arglist)
                             )
                            ((not (string? (car arglist)))
                             (problem "~? requires a string" (car arglist))
                             )
                            (else
                             (format-help p (car arglist) (cadr arglist))
                             (anychar-dispatch (+ pos 1) (cddr arglist) #f)
                             )))
                         ((#\H)      ; Help
                          (display documentation-string p)
                          (anychar-dispatch (+ pos 1) arglist #t)
                          )
                         (else                
                          (problem "unknown tilde escape" (string-ref format-strg pos)))
                         )))
                    )] ; end tilde-dispatch   
                 ) ; end letrec            
          
          ; format-help body
          (anychar-dispatch 0 arglist #f) 
          )) ; end format-help    
      
      ; _format body
      (let ( [unused-args (format-help port format-string args)] )
        (if (not (null? unused-args))
          (problem "unused arguments" unused-args)
          (return-value port))))
    
    ; format body
    (if (string? arg0)
      (_format (open-output-string) arg0 arg* get-output-string)
      (if (null? arg*)
        (problem "too few arguments" (list arg0))
        (let ([port (cond [(eq? arg0 #f) (open-output-string)]
                          [(eq? arg0 #t) (current-output-port)]
                          [(output-port? arg0) arg0]
                          [else (problem "bad output-port argument" arg0)])]
              [arg1 (car arg*)])
          (if (string? arg1)
            (_format port arg1 (cdr arg*) (if arg0 (lambda (ignore) (values)) get-output-string))
            (problem "not a string" arg1)))))) 
)

Added srfi/s48/intermediate-format-strings/compat.chezscheme.sls.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s48 intermediate-format-strings compat)
  (export pretty-print)
  (import (chezscheme)))

Added srfi/s48/intermediate-format-strings/compat.ikarus.sls.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s48 intermediate-format-strings compat)
  (export
    pretty-print)
  (import
    (only (ikarus) pretty-print))
)

Added srfi/s48/intermediate-format-strings/compat.larceny.sls.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s48 intermediate-format-strings compat)
  (export
    pretty-print)
  (import
    (primitives pretty-print))
)

Added srfi/s48/intermediate-format-strings/compat.mzscheme.sls.



























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s48 intermediate-format-strings compat)
  (export
    pretty-print)
  (import
    (only (scheme pretty) pretty-print))
)

Added srfi/s48/intermediate-format-strings/compat.ypsilon.sls.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s48 intermediate-format-strings compat)
  (export
    pretty-print)
  (import
    (only (core) pretty-print))
)

Added srfi/s6/basic-string-ports.mzscheme.sls.























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s6 basic-string-ports)
  (export
    (rename (open-string-input-port open-input-string))
    open-output-string
    get-output-string)
  (import
    (rnrs)
    (only (scheme base) make-weak-hasheq hash-ref hash-set!))
  
  (define accumed-ht (make-weak-hasheq))
  
  (define (open-output-string)
    (letrec ([sop
              (make-custom-textual-output-port
               "string-output-port"
               (lambda (string start count)  ; write!
                 (when (positive? count)
                   (let ([al (hash-ref accumed-ht sop)])
                     (hash-set! accumed-ht sop 
                       (cons (substring string start (+ start count)) al))))
                 count)
               #f  ; get-position  TODO?
               #f  ; set-position!  TODO?
               #f  #| closed  TODO? |# )])
      (hash-set! accumed-ht sop '())
      sop))
  
  (define (get-output-string sop)
    (if (output-port? sop)
      (cond [(hash-ref accumed-ht sop #f)
             => (lambda (al) (apply string-append (reverse al)))]
            [else
             (assertion-violation 'get-output-string "not a string-output-port" sop)])
      (assertion-violation 'get-output-string "not an output-port" sop)))

)

Added srfi/s6/basic-string-ports.sls.









































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s6 basic-string-ports)
  (export
    open-input-string
    open-output-string
    get-output-string)
  (import
    (rnrs base)
    (only (rnrs io ports) open-string-input-port)
    (srfi s6 basic-string-ports compat))
  
  (define (open-input-string str)
    (open-string-input-port str))
)

Added srfi/s6/basic-string-ports/compat.chezscheme.sls.













>
>
>
>
>
>
1
2
3
4
5
6

(library (srfi s6 basic-string-ports compat)
  
  (export open-output-string get-output-string)
  
  (import (only (chezscheme) open-output-string get-output-string)))

Added srfi/s6/basic-string-ports/compat.ikarus.sls.























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s6 basic-string-ports compat)
  (export
    open-output-string get-output-string)
  (import
    (only (ikarus) open-output-string get-output-string)))

Added srfi/s6/basic-string-ports/compat.larceny.sls.



























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s6 basic-string-ports compat)
  (export
    open-output-string get-output-string)
  (import
    (primitives
         open-output-string get-output-string))
)

Added srfi/s6/basic-string-ports/compat.mosh.sls.

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
;; Copyright (c) 2009 Higepon(Taro Minowa)
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; Except as contained in this notice, the name(s) of the above copyright
;; holders shall not be used in advertising or otherwise to promote the sale,
;; use or other dealings in this Software without prior written authorization.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.

#!r6rs
(library (srfi s6 basic-string-ports compat)
  (export
   get-output-string
   open-output-string)
  (import
   (only (system) get-output-string open-output-string))
)

Added srfi/s6/basic-string-ports/compat.ypsilon.sls.































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s6 basic-string-ports compat)
  (export 
   (rename
    (make-string-output-port open-output-string)
    (get-accumulated-string get-output-string)))
  (import
   (only (core) make-string-output-port get-accumulated-string))
)

Added srfi/s61/cond.sls.





























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s61 cond)
  (export
    (rename (general-cond cond)))
  (import
    (rnrs))
  
  (define-syntax general-cond
    (lambda (stx)
      (syntax-case stx ()
        [(_ clauses ...)
         (with-syntax ([(ours ...)
                        (map (lambda (c)
                               (syntax-case c (=>)
                                 [(generator guard => receiver)
                                  #'((let-values ([vals generator])
                                       (and (apply guard vals)
                                            vals))
                                     => (lambda (vals)
                                          (apply receiver vals)))]
                                 [_ c]))
                             #'(clauses ...))])
           #'(cond ours ...))])))
)

Added srfi/s64/testing.scm.



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
;; Copyright (c) 2005, 2006 Per Bothner
;;
;; Permission is hereby granted, free of charge, to any person
;; obtaining a copy of this software and associated documentation
;; files (the "Software"), to deal in the Software without
;; restriction, including without limitation the rights to use, copy,
;; modify, merge, publish, distribute, sublicense, and/or sell copies
;; of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be
;; included in all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;; SOFTWARE.

(cond-expand
 (r6rs)
 (chicken
  (require-extension syntax-case))
 (guile
  (use-modules (ice-9 syncase) (srfi srfi-9)
	       ;;(srfi srfi-34) (srfi srfi-35) - not in Guile 1.6.7
	       (srfi srfi-39)))
 (sisc
  (require-extension (srfi 9 34 35 39)))
 (kawa
  (module-compile-options warn-undefined-variable: #t
			  warn-invoke-unknown-method: #t)
  (provide 'srfi-64)
  (provide 'testing)
  (require 'srfi-34)
  (require 'srfi-35))
 (else ()
  ))

(cond-expand
 (r6rs
  (define-syntax %test-export
    (syntax-rules ()
      ((%test-export . names) (begin)))))
 (kawa
  (define-syntax %test-export
    (syntax-rules ()
      ((%test-export test-begin . other-names)
       (module-export %test-begin . other-names)))))
 (else
  (define-syntax %test-export
    (syntax-rules ()
      ((%test-export . names) (if #f #f))))))

;; List of exported names
(%test-export
 test-begin ;; must be listed first, since in Kawa (at least) it is "magic".
 test-end test-assert test-eqv test-eq test-equal
 test-approximate test-assert test-error test-apply test-with-runner
 test-match-nth test-match-all test-match-any test-match-name
 test-skip test-expect-fail test-read-eval-string
 test-runner-group-path test-group-with-cleanup
 test-result-ref test-result-set! test-result-clear test-result-remove
 test-result-kind test-passed?
 test-log-to-file
 ; Misc test-runner functions
 test-runner? test-runner-reset test-runner-null
 test-runner-simple test-runner-current test-runner-factory test-runner-get
 test-runner-create test-runner-test-name
 ;; test-runner field setter and getter functions - see %test-record-define:
 test-runner-pass-count test-runner-pass-count!
 test-runner-fail-count test-runner-fail-count!
 test-runner-xpass-count test-runner-xpass-count!
 test-runner-xfail-count test-runner-xfail-count!
 test-runner-skip-count test-runner-skip-count!
 test-runner-group-stack test-runner-group-stack!
 test-runner-on-test-begin test-runner-on-test-begin!
 test-runner-on-test-end test-runner-on-test-end!
 test-runner-on-group-begin test-runner-on-group-begin!
 test-runner-on-group-end test-runner-on-group-end!
 test-runner-on-final test-runner-on-final!
 test-runner-on-bad-count test-runner-on-bad-count!
 test-runner-on-bad-end-name test-runner-on-bad-end-name!
 test-result-alist test-result-alist!
 test-runner-aux-value test-runner-aux-value!
 ;; default/simple call-back functions, used in default test-runner,
 ;; but can be called to construct more complex ones.
 test-on-group-begin-simple test-on-group-end-simple
 test-on-bad-count-simple test-on-bad-end-name-simple
 test-on-final-simple test-on-test-end-simple
 test-on-final-simple)

(cond-expand
 (srfi-9
  (define-syntax %test-record-define
    (syntax-rules ()
      ((%test-record-define alloc runner? (name index getter setter) ...)
       (define-record-type test-runner
	 (alloc)
	 runner?
	 (name getter setter) ...)))))
 (else
  (define %test-runner-cookie (list "test-runner"))
  (define-syntax %test-record-define
    (syntax-rules ()
      ((%test-record-define alloc runner? (name index getter setter) ...)
       (begin
	 (define (runner? obj)
	   (and (vector? obj)
		(> (vector-length obj) 1)
		(eq (vector-ref obj 0) %test-runner-cookie)))
	 (define (alloc)
	   (let ((runner (make-vector 22)))
	     (vector-set! runner 0 %test-runner-cookie)
	     runner))
	 (begin
	   (define (getter runner)
	     (vector-ref runner index)) ...)
	 (begin
	   (define (setter runner value)
	     (vector-set! runner index value)) ...)))))))

(%test-record-define
 %test-runner-alloc test-runner?
 ;; Cumulate count of all tests that have passed and were expected to.
 (pass-count 1 test-runner-pass-count test-runner-pass-count!)
 (fail-count 2 test-runner-fail-count test-runner-fail-count!)
 (xpass-count 3 test-runner-xpass-count test-runner-xpass-count!)
 (xfail-count 4 test-runner-xfail-count test-runner-xfail-count!)
 (skip-count 5 test-runner-skip-count test-runner-skip-count!)
 (skip-list 6 %test-runner-skip-list %test-runner-skip-list!)
 (fail-list 7 %test-runner-fail-list %test-runner-fail-list!)
 ;; Normally #t, except when in a test-apply.
 (run-list 8 %test-runner-run-list %test-runner-run-list!)
 (skip-save 9 %test-runner-skip-save %test-runner-skip-save!)
 (fail-save 10 %test-runner-fail-save %test-runner-fail-save!)
 (group-stack 11 test-runner-group-stack test-runner-group-stack!)
 (on-test-begin 12 test-runner-on-test-begin test-runner-on-test-begin!)
 (on-test-end 13 test-runner-on-test-end test-runner-on-test-end!)
 ;; Call-back when entering a group. Takes (runner suite-name count).
 (on-group-begin 14 test-runner-on-group-begin test-runner-on-group-begin!)
 ;; Call-back when leaving a group.
 (on-group-end 15 test-runner-on-group-end test-runner-on-group-end!)
 ;; Call-back when leaving the outermost group.
 (on-final 16 test-runner-on-final test-runner-on-final!)
 ;; Call-back when expected number of tests was wrong.
 (on-bad-count 17 test-runner-on-bad-count test-runner-on-bad-count!)
 ;; Call-back when name in test=end doesn't match test-begin.
 (on-bad-end-name 18 test-runner-on-bad-end-name test-runner-on-bad-end-name!)
 ;; Cumulate count of all tests that have been done.
 (total-count 19 %test-runner-total-count %test-runner-total-count!)
 ;; Stack (list) of (count-at-start . expected-count):
 (count-list 20 %test-runner-count-list %test-runner-count-list!)
 (result-alist 21 test-result-alist test-result-alist!)
 ;; Field can be used by test-runner for any purpose.
 ;; test-runner-simple uses it for a log file.
 (aux-value 22 test-runner-aux-value test-runner-aux-value!)
)

(define (test-runner-reset runner)
    (test-result-alist! runner '())
    (test-runner-pass-count! runner 0)
    (test-runner-fail-count! runner 0)
    (test-runner-xpass-count! runner 0)
    (test-runner-xfail-count! runner 0)
    (test-runner-skip-count! runner 0)
    (%test-runner-total-count! runner 0)
    (%test-runner-count-list! runner '())
    (%test-runner-run-list! runner #t)
    (%test-runner-skip-list! runner '())
    (%test-runner-fail-list! runner '())
    (%test-runner-skip-save! runner '())
    (%test-runner-fail-save! runner '())
    (test-runner-group-stack! runner '()))

(define (test-runner-group-path runner)
  (reverse (test-runner-group-stack runner)))

(define (%test-null-callback runner) #f)

(define (test-runner-null)
  (let ((runner (%test-runner-alloc)))
    (test-runner-reset runner)
    (test-runner-on-group-begin! runner (lambda (runner name count) #f))
    (test-runner-on-group-end! runner %test-null-callback)
    (test-runner-on-final! runner %test-null-callback)
    (test-runner-on-test-begin! runner %test-null-callback)
    (test-runner-on-test-end! runner %test-null-callback)
    (test-runner-on-bad-count! runner (lambda (runner count expected) #f))
    (test-runner-on-bad-end-name! runner (lambda (runner begin end) #f))
    runner))

;; Not part of the specification.  FIXME
;; Controls whether a log file is generated.
(define test-log-to-file #F)

(define (test-runner-simple)
  (let ((runner (%test-runner-alloc)))
    (test-runner-reset runner)
    (test-runner-on-group-begin! runner test-on-group-begin-simple)
    (test-runner-on-group-end! runner test-on-group-end-simple)
    (test-runner-on-final! runner test-on-final-simple)
    (test-runner-on-test-begin! runner test-on-test-begin-simple)
    (test-runner-on-test-end! runner test-on-test-end-simple)
    (test-runner-on-bad-count! runner test-on-bad-count-simple)
    (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
    runner))

(cond-expand
 (srfi-39
  (define test-runner-current (make-parameter #f))
  (define test-runner-factory (make-parameter test-runner-simple)))
 (else
  (define %test-runner-current #f)
  (define-syntax test-runner-current
    (syntax-rules ()
      ((test-runner-current)
       %test-runner-current)
      ((test-runner-current runner)
       (set! %test-runner-current runner))))
  (define %test-runner-factory test-runner-simple)
  (define-syntax test-runner-factory
    (syntax-rules ()
      ((test-runner-factory)
       %test-runner-factory)
      ((test-runner-factory runner)
       (set! %test-runner-factory runner))))))

;; A safer wrapper to test-runner-current.
(define (test-runner-get)
  (let ((r (test-runner-current)))
    (if (not r)
	(cond-expand
	 (srfi-23 (error "test-runner not initialized - test-begin missing?"))
	 (else #t)))
    r))

(define (%test-specificier-matches spec runner)
  (spec runner))

(define (test-runner-create)
  ((test-runner-factory)))

(define (%test-any-specifier-matches list runner)
  (let ((result #f))
    (let loop ((l list))
      (cond ((null? l) result)
	    (else
	     (if (%test-specificier-matches (car l) runner)
		 (set! result #t))
	     (loop (cdr l)))))))

;; Returns #f, #t, or 'xfail.
(define (%test-should-execute runner)
  (let ((run (%test-runner-run-list runner)))
    (cond ((or
	    (not (or (eqv? run #t)
		     (%test-any-specifier-matches run runner)))
	    (%test-any-specifier-matches
	     (%test-runner-skip-list runner)
	     runner))
	    (test-result-set! runner 'result-kind 'skip)
	    #f)
	  ((%test-any-specifier-matches
	    (%test-runner-fail-list runner)
	    runner)
	   (test-result-set! runner 'result-kind 'xfail)
	   'xfail)
	  (else #t))))

(define (%test-begin suite-name count)
  (if (not (test-runner-current))
      (test-runner-current (test-runner-create)))
  (let ((runner (test-runner-current)))
    ((test-runner-on-group-begin runner) runner suite-name count)
    (%test-runner-skip-save! runner
			       (cons (%test-runner-skip-list runner)
				     (%test-runner-skip-save runner)))
    (%test-runner-fail-save! runner
			       (cons (%test-runner-fail-list runner)
				     (%test-runner-fail-save runner)))
    (%test-runner-count-list! runner
			     (cons (cons (%test-runner-total-count runner)
					 count)
				   (%test-runner-count-list runner)))
    (test-runner-group-stack! runner (cons suite-name
					(test-runner-group-stack runner)))))
(cond-expand
 ((and (not r6rs) kawa)
  ;; Kawa has test-begin built in, implemented as:
  ;; (begin
  ;;   (cond-expand (srfi-64 #!void) (else (require 'srfi-64)))
  ;;   (%test-begin suite-name [count]))
  ;; This puts test-begin but only test-begin in the default environment.,
  ;; which makes normal test suites loadable without non-portable commands.
  )
 (else
  (define-syntax test-begin
    (syntax-rules ()
      ((test-begin suite-name)
       (%test-begin suite-name #f))
      ((test-begin suite-name count)
       (%test-begin suite-name count))))))

(define (test-on-group-begin-simple runner suite-name count)
  (if (null? (test-runner-group-stack runner))
      (begin
	(display "%%%% Starting test ")
	(display suite-name)
	(if test-log-to-file
	    (let* ((log-file-name
		    (if (string? test-log-to-file) test-log-to-file
			(string-append suite-name ".log")))
		   (log-file
		    (cond-expand ((and (not r6rs) mzscheme)
				  (open-output-file log-file-name 'truncate/replace))
				 (else (open-output-file log-file-name)))))
	      (display "%%%% Starting test " log-file)
	      (display suite-name log-file)
	      (newline log-file)
	      (test-runner-aux-value! runner log-file)
	      (display "  (Writing full log to \"")
	      (display log-file-name)
	      (display "\")")))
	(newline)))
  (let ((log (test-runner-aux-value runner)))
    (if (output-port? log)
	(begin
	  (display "Group begin: " log)
	  (display suite-name log)
	  (newline log))))
  #f)

(define (test-on-group-end-simple runner)
  (let ((log (test-runner-aux-value runner)))
    (if (output-port? log)
	(begin
	  (display "Group end: " log)
	  (display (car (test-runner-group-stack runner)) log)
	  (newline log))))
  #f)

(define (%test-on-bad-count-write runner count expected-count port)
  (display "*** Total number of tests was " port)
  (display count port)
  (display " but should be " port)
  (display expected-count port)
  (display ". ***" port)
  (newline port)
  (display "*** Discrepancy indicates testsuite error or exceptions. ***" port)
  (newline port))

(define (test-on-bad-count-simple runner count expected-count)
  (%test-on-bad-count-write runner count expected-count (current-output-port))
  (let ((log (test-runner-aux-value runner)))
    (if (output-port? log)
	(%test-on-bad-count-write runner count expected-count log))))

(define (test-on-bad-end-name-simple runner begin-name end-name)
  (let ((msg (string-append (%test-format-line runner) "test-end " begin-name
			    " does not match test-begin " end-name)))
    (cond-expand
     (srfi-23 (error msg))
     (else (display msg) (newline)))))
  

(define (%test-final-report1 value label port)
  (if (> value 0)
      (begin
	(display label port)
	(display value port)
	(newline port))))

(define (%test-final-report-simple runner port)
  (%test-final-report1 (test-runner-pass-count runner)
		      "# of expected passes      " port)
  (%test-final-report1 (test-runner-xfail-count runner)
		      "# of expected failures    " port)
  (%test-final-report1 (test-runner-xpass-count runner)
		      "# of unexpected successes " port)
  (%test-final-report1 (test-runner-fail-count runner)
		      "# of unexpected failures  " port)
  (%test-final-report1 (test-runner-skip-count runner)
		      "# of skipped tests        " port))

(define (test-on-final-simple runner)
  (%test-final-report-simple runner (current-output-port))
  (let ((log (test-runner-aux-value runner)))
    (if (output-port? log)
	(%test-final-report-simple runner log))))

(define (%test-format-line runner)
   (let* ((line-info (test-result-alist runner))
	  (source-file (assq 'source-file line-info))
	  (source-line (assq 'source-line line-info))
	  (file (if source-file (cdr source-file) "")))
     (if source-line
	 (string-append file ":"
			(number->string (cdr source-line)) ": ")
	 "")))

(define (%test-end suite-name line-info)
  (let* ((r (test-runner-get))
	 (groups (test-runner-group-stack r))
	 (line (%test-format-line r)))
    (test-result-alist! r line-info)
    (if (null? groups)
	(let ((msg (string-append line "test-end not in a group")))
	  (cond-expand
	   (srfi-23 (error msg))
	   (else (display msg) (newline)))))
    (if (and suite-name (not (equal? suite-name (car groups))))
	((test-runner-on-bad-end-name r) r suite-name (car groups)))
    (let* ((count-list (%test-runner-count-list r))
	   (expected-count (cdar count-list))
	   (saved-count (caar count-list))
	   (group-count (- (%test-runner-total-count r) saved-count)))
      (if (and expected-count
	       (not (= expected-count group-count)))
	  ((test-runner-on-bad-count r) r group-count expected-count))
      ((test-runner-on-group-end r) r)
      (test-runner-group-stack! r (cdr (test-runner-group-stack r)))
      (%test-runner-skip-list! r (car (%test-runner-skip-save r)))
      (%test-runner-skip-save! r (cdr (%test-runner-skip-save r)))
      (%test-runner-fail-list! r (car (%test-runner-fail-save r)))
      (%test-runner-fail-save! r (cdr (%test-runner-fail-save r)))
      (%test-runner-count-list! r (cdr count-list))
      (if (null? (test-runner-group-stack r))
	  ((test-runner-on-final r) r)))))

(define-syntax test-group
  (syntax-rules ()
    ((test-group suite-name . body)
     (let ((r (test-runner-current)))
       ;; Ideally should also set line-number, if available.
       (test-result-alist! r (list (cons 'test-name suite-name)))
       (if (%test-should-execute r)
	   (dynamic-wind
	       (lambda () (test-begin suite-name))
	       (lambda () . body)
	       (lambda () (test-end  suite-name))))))))

(define-syntax test-group-with-cleanup
  (syntax-rules ()
    ((test-group-with-cleanup suite-name form cleanup-form)
     (test-group suite-name
		    (dynamic-wind
			(lambda () #f)
			(lambda () form)
			(lambda () cleanup-form))))
    ((test-group-with-cleanup suite-name cleanup-form)
     (test-group-with-cleanup suite-name #f cleanup-form))
    ((test-group-with-cleanup suite-name form1 form2 form3 . rest)
     (test-group-with-cleanup suite-name (begin form1 form2) form3 . rest))))

(define (test-on-test-begin-simple runner)
 (let ((log (test-runner-aux-value runner)))
    (if (output-port? log)
	(let* ((results (test-result-alist runner))
	       (source-file (assq 'source-file results))
	       (source-line (assq 'source-line results))
	       (source-form (assq 'source-form results))
	       (test-name (assq 'test-name results)))
	  (display "Test begin:" log)
	  (newline log)
	  (if test-name (%test-write-result1 test-name log))
	  (if source-file (%test-write-result1 source-file log))
	  (if source-line (%test-write-result1 source-line log))
	  (if source-form (%test-write-result1 source-form log))))))

(define-syntax test-result-ref
  (syntax-rules ()
    ((test-result-ref runner pname)
     (test-result-ref runner pname #f))
    ((test-result-ref runner pname default)
     (let ((p (assq pname (test-result-alist runner))))
       (if p (cdr p) default)))))

(define (test-on-test-end-simple runner)
  (let ((log (test-runner-aux-value runner))
	(kind (test-result-ref runner 'result-kind)))
    (if (memq kind '(fail xpass))
	(let* ((results (test-result-alist runner))
	       (source-file (assq 'source-file results))
	       (source-line (assq 'source-line results))
	       (test-name (assq 'test-name results)))
	  (if (or source-file source-line)
	      (begin
		(if source-file (display (cdr source-file)))
		(display ":")
		(if source-line (display (cdr source-line)))
		(display ": ")))
	  (display (if (eq? kind 'xpass) "XPASS" "FAIL"))
	  (if test-name
	      (begin
		(display " ")
		(display (cdr test-name))))
	  (newline)))
    (if (output-port? log)
	(begin
	  (display "Test end:" log)
	  (newline log)
	  (let loop ((list (test-result-alist runner)))
	    (if (pair? list)
		(let ((pair (car list)))
		  ;; Write out properties not written out by on-test-begin.
		  (if (not (memq (car pair)
				 '(test-name source-file source-line source-form)))
		      (%test-write-result1 pair log))
		  (loop (cdr list)))))))))

(define (%test-write-result1 pair port)
  (display "  " port)
  (display (car pair) port)
  (display ": " port)
  (write (cdr pair) port)
  (newline port))

(define (test-result-set! runner pname value)
  (let* ((alist (test-result-alist runner))
	 (p (assq pname alist)))
    (if p
	(set-cdr! p value)
	(test-result-alist! runner (cons (cons pname value) alist)))))

(define (test-result-clear runner)
  (test-result-alist! runner '()))

(define (test-result-remove runner pname)
  (let* ((alist (test-result-alist runner))
	 (p (assq pname alist)))
    (if p
	(test-result-alist! runner
				   (let loop ((r alist))
				     (if (eq? r p) (cdr r)
					 (cons (car r) (loop (cdr r)))))))))

(define (test-result-kind . rest)
  (let ((runner (if (pair? rest) (car rest) (test-runner-current))))
    (test-result-ref runner 'result-kind)))

(define (test-passed? . rest)
  (let ((runner (if (pair? rest) (car rest) (test-runner-get))))
    (memq (test-result-ref runner 'result-kind) '(pass xpass))))

(define (%test-report-result)
  (let* ((r (test-runner-get))
	 (result-kind (test-result-kind r)))
    (case result-kind
      ((pass)
       (test-runner-pass-count! r (+ 1 (test-runner-pass-count r))))
      ((fail)
       (test-runner-fail-count!	r (+ 1 (test-runner-fail-count r))))
      ((xpass)
       (test-runner-xpass-count! r (+ 1 (test-runner-xpass-count r))))
      ((xfail)
       (test-runner-xfail-count! r (+ 1 (test-runner-xfail-count r))))
      (else
       (test-runner-skip-count! r (+ 1 (test-runner-skip-count r)))))
    (%test-runner-total-count! r (+ 1 (%test-runner-total-count r)))
    ((test-runner-on-test-end r) r)))

(cond-expand
 (r6rs
  (define-syntax %test-evaluate-with-catch
    (syntax-rules ()
      ((%test-evaluate-with-catch test-expression)
       (guard (ex (else #F)) test-expression)))))
 (guile
  (define-syntax %test-evaluate-with-catch
    (syntax-rules ()
      ((%test-evaluate-with-catch test-expression)
       (catch #t (lambda () test-expression) (lambda (key . args) #f))))))
 (kawa
  (define-syntax %test-evaluate-with-catch
    (syntax-rules ()
      ((%test-evaluate-with-catch test-expression)
       (try-catch test-expression
		  (ex <java.lang.Throwable>
		      (test-result-set! (test-runner-current) 'actual-error ex)
		      #f))))))
 (srfi-34
  (define-syntax %test-evaluate-with-catch
    (syntax-rules ()
      ((%test-evaluate-with-catch test-expression)
       (guard (err (else #f)) test-expression)))))
 (chicken
  (define-syntax %test-evaluate-with-catch
    (syntax-rules ()
      ((%test-evaluate-with-catch test-expression)
       (condition-case test-expression (ex () #f))))))
 (else
  (define-syntax %test-evaluate-with-catch
    (syntax-rules ()
      ((%test-evaluate-with-catch test-expression)
       test-expression)))))
	    
(cond-expand
 ((and (not r6rs) (or kawa mzscheme))
  (cond-expand
   (mzscheme
    (define-for-syntax (%test-syntax-file form)
      (let ((source (syntax-source form)))
	(cond ((string? source) file)
				((path? source) (path->string source))
				(else #f)))))
   (kawa
    (define (%test-syntax-file form)
      (syntax-source form))))
  (define-for-syntax (%test-source-line2 form)
    (let* ((line (syntax-line form))
	   (file (%test-syntax-file form))
	   (line-pair (if line (list (cons 'source-line line)) '())))
      (cons (cons 'source-form (syntax-object->datum form))
	    (if file (cons (cons 'source-file file) line-pair) line-pair)))))
 (else
  (define (%test-source-line2 form)
    '())))

(define (%test-on-test-begin r)
  (%test-should-execute r)
  ((test-runner-on-test-begin r) r)
  (not (eq? 'skip (test-result-ref r 'result-kind))))

(define (%test-on-test-end r result)
    (test-result-set! r 'result-kind
		      (if (eq? (test-result-ref r 'result-kind) 'xfail)
			  (if result 'xpass 'xfail)
			  (if result 'pass 'fail))))

(define (test-runner-test-name runner)
  (test-result-ref runner 'test-name ""))

(define-syntax %test-comp2body
  (syntax-rules ()
		((%test-comp2body r comp expected expr)
		 (let ()
		   (if (%test-on-test-begin r)
		       (let ((exp expected))
			 (test-result-set! r 'expected-value exp)
			 (let ((res (%test-evaluate-with-catch expr)))
			   (test-result-set! r 'actual-value res)
			   (%test-on-test-end r (comp exp res)))))
		   (%test-report-result)))))

(define (%test-approximimate= error)
  (lambda (value expected)
    (and (>= value (- expected error))
         (<= value (+ expected error)))))

(define-syntax %test-comp1body
  (syntax-rules ()
    ((%test-comp1body r expr)
     (let ()
       (if (%test-on-test-begin r)
	   (let ()
	     (let ((res (%test-evaluate-with-catch expr)))
	       (test-result-set! r 'actual-value res)
	       (%test-on-test-end r res))))
       (%test-report-result)))))

(cond-expand
 ((and (not r6rs) (or kawa mzscheme))
  ;; Should be made to work for any Scheme with syntax-case
  ;; However, I haven't gotten the quoting working.  FIXME.
  (define-syntax test-end
    (lambda (x)
      (syntax-case (list x (list 'quote (%test-source-line2 x))) ()
	(((mac suite-name) line)
	 (syntax
	  (%test-end suite-name line)))
	(((mac) line)
	 (syntax
	  (%test-end #f line))))))
  (define-syntax test-assert
    (lambda (x)
      (syntax-case (list x (list 'quote (%test-source-line2 x))) ()
	(((mac tname expr) line)
	 (syntax
	  (let* ((r (test-runner-get))
		 (name tname))
	    (test-result-alist! r (cons (cons 'test-name tname) line))
	    (%test-comp1body r expr))))
	(((mac expr) line)
	 (syntax
	  (let* ((r (test-runner-get)))
	    (test-result-alist! r line)
	    (%test-comp1body r expr)))))))
  (define-for-syntax (%test-comp2 comp x)
    (syntax-case (list x (list 'quote (%test-source-line2 x)) comp) ()
      (((mac tname expected expr) line comp)
       (syntax
	(let* ((r (test-runner-get))
	       (name tname))
	  (test-result-alist! r (cons (cons 'test-name tname) line))
	  (%test-comp2body r comp expected expr))))
      (((mac expected expr) line comp)
       (syntax
	(let* ((r (test-runner-get)))
	  (test-result-alist! r line)
	  (%test-comp2body r comp expected expr))))))
  (define-syntax test-eqv
    (lambda (x) (%test-comp2 (syntax eqv?) x)))
  (define-syntax test-eq
    (lambda (x) (%test-comp2 (syntax eq?) x)))
  (define-syntax test-equal
    (lambda (x) (%test-comp2 (syntax equal?) x)))
  (define-syntax test-approximate ;; FIXME - needed for non-Kawa
    (lambda (x)
      (syntax-case (list x (list 'quote (%test-source-line2 x))) ()
      (((mac tname expected expr error) line)
       (syntax
	(let* ((r (test-runner-get))
	       (name tname))
	  (test-result-alist! r (cons (cons 'test-name tname) line))
	  (%test-comp2body r (%test-approximimate= error) expected expr))))
      (((mac expected expr error) line)
       (syntax
	(let* ((r (test-runner-get)))
	  (test-result-alist! r line)
	  (%test-comp2body r (%test-approximimate= error) expected expr))))))))
 (else
  (define-syntax test-end
    (syntax-rules ()
      ((test-end)
       (%test-end #f '()))
      ((test-end suite-name)
       (%test-end suite-name '()))))
  (define-syntax test-assert
    (syntax-rules ()
      ((test-assert tname test-expression)
       (let ((r (test-runner-get)))
	 (test-result-alist! r `((test-name . ,tname)
                                 (source-form . test-expression)))
	 (%test-comp1body r test-expression)))
      ((test-assert test-expression)
       (let ((r (test-runner-get)))
	 (test-result-alist! r '((source-form . test-expression)))
	 (%test-comp1body r test-expression)))))
  (define-syntax %test-comp2
    (syntax-rules ()
      ((%test-comp2 comp tname expected expr)
       (let ((r (test-runner-get)))
	 (test-result-alist! r `((test-name . ,tname)
                                 (source-form . expr)))
	 (%test-comp2body r comp expected expr)))
      ((%test-comp2 comp expected expr)
       (let ((r (test-runner-get)))
	 (test-result-alist! r '((source-form . expr)))
	 (%test-comp2body r comp expected expr)))))
  (define-syntax test-equal
    (syntax-rules ()
      ((test-equal . rest)
       (%test-comp2 equal? . rest))))
  (define-syntax test-eqv
    (syntax-rules ()
      ((test-eqv . rest)
       (%test-comp2 eqv? . rest))))
  (define-syntax test-eq
    (syntax-rules ()
      ((test-eq . rest)
       (%test-comp2 eq? . rest))))
  (define-syntax test-approximate
    (syntax-rules ()
      ((test-approximate tname expected expr error)
       (%test-comp2 (%test-approximimate= error) tname expected expr))
      ((test-approximate expected expr error)
       (%test-comp2 (%test-approximimate= error) expected expr))))))

(cond-expand
 (r6rs
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error etype expr)
       (let ((t etype))
         (when (procedure? t)
           (test-result-set! (test-runner-get) 'expected-error t))
         (guard (ex (else
                     (test-result-set! (test-runner-get) 'actual-error ex)
                     (if (procedure? t) (t ex) #T)))
           expr
           #F))))))
 (guile
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error r etype expr)
       (%test-comp1body r (catch #t (lambda () expr) (lambda (key . args) #t)))))))
 (mzscheme
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error r etype expr)
       (%test-comp1body r (with-handlers (((lambda (h) #t) (lambda (h) #t)))
					 (let ()
					   (test-result-set! r 'actual-value expr)
					   #f)))))))
 (chicken
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error r etype expr)
        (%test-comp1body r (condition-case expr (ex () #t)))))))
 (kawa
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error r etype expr)
       (let ()
	 (if (%test-on-test-begin r)
	     (let ((et etype))
	       (test-result-set! r 'expected-error et)
	       (%test-on-test-end r
				  (try-catch
				   (let ()
				     (test-result-set! r 'actual-value expr)
				     #f)
				   (ex <java.lang.Throwable>
				       (test-result-set! r 'actual-error ex)
				       (cond ((and (instance? et <gnu.bytecode.ClassType>)
						   (gnu.bytecode.ClassType:isSubclass et <java.lang.Throwable>))
					      (instance? ex et))
					     (else #t)))))
	       (%test-report-result))))))))
 ((and srfi-34 srfi-35)
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error r etype expr)
       (%test-comp1body r (guard (ex ((condition-type? etype)
		   (and (condition? ex) (condition-has-type? ex etype)))
		  ((procedure? etype)
		   (etype ex))
		  ((equal? type #t)
		   #t)
		  (else #t))
	      expr))))))
 (srfi-34
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error r etype expr)
       (%test-comp1body r (guard (ex (else #t)) expr))))))
 (else
  (define-syntax %test-error
    (syntax-rules ()
      ((%test-error r etype expr)
       (begin
	 ((test-runner-on-test-begin r) r)
	 (test-result-set! r 'result-kind 'skip)
	 (%test-report-result)))))))

(cond-expand
 ((and (not r6rs) (or kawa mzscheme))

  (define-syntax test-error
    (lambda (x)
      (syntax-case (list x (list 'quote (%test-source-line2 x))) ()
	(((mac tname etype expr) line)
	 (syntax
	  (let* ((r (test-runner-get))
		 (name tname))
	    (test-result-alist! r (cons (cons 'test-name tname) line))
	    (%test-error r etype expr))))
	(((mac etype expr) line)
	 (syntax
	  (let* ((r (test-runner-get)))
	    (test-result-alist! r line)
	    (%test-error r etype expr))))
	(((mac expr) line)
	 (syntax
	  (let* ((r (test-runner-get)))
	    (test-result-alist! r line)
	    (%test-error r #t expr))))))))
 (else
  (define-syntax test-error
    (syntax-rules ()
      ((test-error name etype expr)
       (test-assert name (%test-error etype expr)))
      ((test-error etype expr)
       (test-assert (%test-error etype expr)))
      ((test-error expr)
       (test-assert (%test-error #t expr)))))))

(define (test-apply first . rest)
  (if (test-runner? first)
      (test-with-runner first (apply test-apply rest))
      (let ((r (test-runner-current)))
	(if r
	    (let ((run-list (%test-runner-run-list r)))
	      (cond ((null? rest)
		     (%test-runner-run-list! r (reverse! run-list))
		     (first)) ;; actually apply procedure thunk
		    (else
		     (%test-runner-run-list!
		      r
		      (if (eq? run-list #t) (list first) (cons first run-list)))
		     (apply test-apply rest)
		     (%test-runner-run-list! r run-list))))
	    (let ((r (test-runner-create)))
	      (test-with-runner r (apply test-apply first rest))
	      ((test-runner-on-final r) r))))))

(define-syntax test-with-runner
  (syntax-rules ()
    ((test-with-runner runner form ...)
     (let ((saved-runner (test-runner-current)))
       (dynamic-wind
           (lambda () (test-runner-current runner))
           (lambda () form ...)
           (lambda () (test-runner-current saved-runner)))))))

;;; Predicates

(define (%test-match-nth n count)
  (let ((i 0))
    (lambda (runner)
      (set! i (+ i 1))
      (and (>= i n) (< i (+ n count))))))

(define-syntax test-match-nth
  (syntax-rules ()
    ((test-match-nth n)
     (test-match-nth n 1))
    ((test-match-nth n count)
     (%test-match-nth n count))))

(define (%test-match-all . pred-list)
  (lambda (runner)
    (let ((result #t))
      (let loop ((l pred-list))
	(if (null? l)
	    result
	    (begin
	      (if (not ((car l) runner))
		  (set! result #f))
	      (loop (cdr l))))))))
  
(define-syntax test-match-all
  (syntax-rules ()
    ((test-match-all pred ...)
     (%test-match-all (%test-as-specifier pred) ...))))

(define (%test-match-any . pred-list)
  (lambda (runner)
    (let ((result #f))
      (let loop ((l pred-list))
	(if (null? l)
	    result
	    (begin
	      (if ((car l) runner)
		  (set! result #t))
	      (loop (cdr l))))))))
  
(define-syntax test-match-any
  (syntax-rules ()
    ((test-match-any pred ...)
     (%test-match-any (%test-as-specifier pred) ...))))

;; Coerce to a predicate function:
(define (%test-as-specifier specifier)
  (cond ((procedure? specifier) specifier)
	((integer? specifier) (test-match-nth 1 specifier))
	((string? specifier) (test-match-name specifier))
	(else
	 (error "not a valid test specifier"))))

(define-syntax test-skip
  (syntax-rules ()
    ((test-skip pred ...)
     (let ((runner (test-runner-get)))
       (%test-runner-skip-list! runner
				  (cons (test-match-all (%test-as-specifier pred)  ...)
					(%test-runner-skip-list runner)))))))

(define-syntax test-expect-fail
  (syntax-rules ()
    ((test-expect-fail pred ...)
     (let ((runner (test-runner-get)))
       (%test-runner-fail-list! runner
				  (cons (test-match-all (%test-as-specifier pred)  ...)
					(%test-runner-fail-list runner)))))))

(define (test-match-name name)
  (lambda (runner)
    (equal? name (test-runner-test-name runner))))

(define (test-read-eval-string string)
  (let* ((port (open-input-string string))
	 (form (read port)))
    (if (eof-object? (read-char port))
	(eval form)
	(cond-expand
	 (srfi-23 (error "(not at eof)"))
	 (else "error")))))

Added srfi/s64/testing.sls.





















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s64 testing)
  (export
    test-begin
    test-end test-assert test-eqv test-eq test-equal
    test-approximate test-error test-apply test-with-runner
    test-match-nth test-match-all test-match-any test-match-name
    test-skip test-expect-fail test-read-eval-string
    test-group test-runner-group-path test-group-with-cleanup
    test-result-ref test-result-set! test-result-clear test-result-remove
    test-result-kind test-passed?
    (rename (%test-log-to-file test-log-to-file))
    ; Misc test-runner functions
    test-runner? test-runner-reset test-runner-null
    test-runner-simple test-runner-current test-runner-factory test-runner-get
    test-runner-create test-runner-test-name
    ;; test-runner field setter and getter functions - see %test-record-define:
    test-runner-pass-count test-runner-pass-count!
    test-runner-fail-count test-runner-fail-count!
    test-runner-xpass-count test-runner-xpass-count!
    test-runner-xfail-count test-runner-xfail-count!
    test-runner-skip-count test-runner-skip-count!
    test-runner-group-stack test-runner-group-stack!
    test-runner-on-test-begin test-runner-on-test-begin!
    test-runner-on-test-end test-runner-on-test-end!
    test-runner-on-group-begin test-runner-on-group-begin!
    test-runner-on-group-end test-runner-on-group-end!
    test-runner-on-final test-runner-on-final!
    test-runner-on-bad-count test-runner-on-bad-count!
    test-runner-on-bad-end-name test-runner-on-bad-end-name!
    test-result-alist test-result-alist!
    test-runner-aux-value test-runner-aux-value!
    ;; default/simple call-back functions, used in default test-runner,
    ;; but can be called to construct more complex ones.
    test-on-group-begin-simple test-on-group-end-simple
    test-on-bad-count-simple test-on-bad-end-name-simple
    test-on-final-simple test-on-test-end-simple)
  (import
    (rnrs base)
    (rnrs control)
    (rnrs exceptions)
    (rnrs io simple)
    (rnrs lists)
    (rename (rnrs eval) (eval rnrs:eval))
    (rnrs mutable-pairs)
    (srfi s0 cond-expand)
    (only (srfi s1 lists) reverse!)
    (srfi s6 basic-string-ports)
    (srfi s9 records)
    (srfi s39 parameters)
    (srfi s23 error tricks)
    (srfi private include))

  (define (eval form)
    (rnrs:eval form (environment '(rnrs)
                                 '(rnrs eval)
                                 '(rnrs mutable-pairs)
                                 '(rnrs mutable-strings)
                                 '(rnrs r5rs))))

  (define %test-log-to-file
    (case-lambda
      (() test-log-to-file)
      ((val) (set! test-log-to-file val))))
  
  (SRFI-23-error->R6RS "(library (srfi s64 testing))"
   (include/resolve ("srfi" "s64") "testing.scm"))
)

Added srfi/s67/_compare-procedures.sls.



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s67 compare-procedures)
  (export  </<=? </<? <=/<=? <=/<? <=? <? =?
           >/>=? >/>? >=/>=? >=/>? >=? >?
           boolean-compare chain<=? chain<? chain=? chain>=? chain>?
           char-compare char-compare-ci
           compare-by< compare-by<= compare-by=/< compare-by=/> compare-by> 
           compare-by>= complex-compare cond-compare
           debug-compare default-compare
           if-not=? if3 if<=? if<? if=? if>=? if>? integer-compare
           kth-largest list-compare list-compare-as-vector
           max-compare min-compare not=? number-compare
           pair-compare pair-compare-car pair-compare-cdr
           pairwise-not=? rational-compare real-compare
           refine-compare select-compare string-compare string-compare-ci 
           symbol-compare vector-compare vector-compare-as-list)
  
  (import (except (rnrs) error)
          (rnrs r5rs)    ; for modulo
          (srfi s27 random-bits)  ; for random-integer
          (srfi s23 error)
          ;; (srfi s23 error tricks)
          (srfi private include))
  
  ;; (SRFI-23-error->R6RS "(library (srfi s67 compare-procedures))"
  ;;  (include/resolve ("srfi" "s67") "compare.ss"))

; (define current-compare (make-parameter default-compare))
; (provide current-compare)

; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard.
; 
; Permission is hereby granted, free of charge, to any person obtaining
; a copy of this software and associated documentation files (the
; ``Software''), to deal in the Software without restriction, including
; without limitation the rights to use, copy, modify, merge, publish,
; distribute, sublicense, and/or sell copies of the Software, and to
; permit persons to whom the Software is furnished to do so, subject to
; the following conditions:
; 
; The above copyright notice and this permission notice shall be
; included in all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; 
; -----------------------------------------------------------------------
; 
; Compare procedures SRFI (reference implementation)
; Sebastian.Egner@philips.com, Jensaxel@soegaard.net
; history of this file:
;   SE, 14-Oct-2004: first version
;   SE, 18-Oct-2004: 1st redesign: axioms for 'compare function'
;   SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite
;   SE,  2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's
;   SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare
;   SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added
;   SE, 12-Jan-2005: pair-compare-cdr
;   SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=?
;   SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc.
;   JS, 24-Feb-2005: selection-compare added
;   SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc.
;   JS, 28-Feb-2005: kth-largest modified - is "stable" now
;   SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged
;   SE, 07-Apr-2005: compare-based type checks made explicit
;   SE, 18-Apr-2005: added (rel? compare) and eq?-test
;   SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y

; =============================================================================

; Reference Implementation
; ========================
;
; in R5RS (including hygienic macros)
;  + SRFI-16 (case-lambda) 
;  + SRFI-23 (error) 
;  + SRFI-27 (random-integer)

; Implementation remarks:
;   * In general, the emphasis of this implementation is on correctness
;     and portability, not on efficiency.
;   * Variable arity procedures are expressed in terms of case-lambda
;     in the hope that this will produce efficient code for the case
;     where the arity is statically known at the call site.
;   * In procedures that are required to type-check their arguments,
;     we use (compare x x) for executing extra checks. This relies on
;     the assumption that eq? is used to catch this case quickly.
;   * Care has been taken to reference comparison procedures of R5RS
;     only at the time the operations here are being defined. This
;     makes it possible to redefine these operations, if need be.
;   * For the sake of efficiency, some inlining has been done by hand.
;     This is mainly expressed by macros producing defines.
;   * Identifiers of the form compare:<something> are private.
;
; Hints for low-level implementation:
;   * The basis of this SRFI are the atomic compare procedures, 
;     i.e. boolean-compare, char-compare, etc. and the conditionals
;     if3, if=?, if<? etc., and default-compare. These should make
;     optimal use of the available type information.
;   * For the sake of speed, the reference implementation does not
;     use a LET to save the comparison value c for the ERROR call.
;     This can be fixed in a low-level implementation at no cost.
;   * Type-checks based on (compare x x) are made explicit by the
;     expression (compare:check result compare x ...).
;   * Eq? should  can used to speed up built-in compare procedures,
;     but it can only be used after type-checking at least one of
;     the arguments.

(define (compare:checked result compare . args)
  (for-each (lambda (x) (compare x x)) args)
  result)


; 3-sided conditional

(define-syntax if3
  (syntax-rules ()
    ((if3 c less equal greater)
     (case c
       ((-1) less)
       (( 0) equal)
       (( 1) greater)
       (else (error "comparison value not in {-1,0,1}"))))))


; 2-sided conditionals for comparisons

(define-syntax compare:if-rel?
  (syntax-rules ()
    ((compare:if-rel? c-cases a-cases c consequence)
     (compare:if-rel? c-cases a-cases c consequence (if #f #f)))
    ((compare:if-rel? c-cases a-cases c consequence alternate)
     (case c
       (c-cases consequence)
       (a-cases alternate)
       (else    (error "comparison value not in {-1,0,1}"))))))

(define-syntax if=?
  (syntax-rules ()
    ((if=? arg ...)
     (compare:if-rel? (0) (-1 1) arg ...))))

(define-syntax if<?
  (syntax-rules ()
    ((if<? arg ...)
     (compare:if-rel? (-1) (0 1) arg ...))))

(define-syntax if>?
  (syntax-rules ()
    ((if>? arg ...)
     (compare:if-rel? (1) (-1 0) arg ...))))

(define-syntax if<=?
  (syntax-rules ()
    ((if<=? arg ...)
     (compare:if-rel? (-1 0) (1) arg ...))))

(define-syntax if>=?
  (syntax-rules ()
    ((if>=? arg ...)
     (compare:if-rel? (0 1) (-1) arg ...))))

(define-syntax if-not=?
  (syntax-rules ()
    ((if-not=? arg ...)
     (compare:if-rel? (-1 1) (0) arg ...))))


; predicates from compare procedures

(define-syntax compare:define-rel?
  (syntax-rules ()
    ((compare:define-rel? rel? if-rel?)
     (define rel?
       (case-lambda
         (()        (lambda (x y) (if-rel? (default-compare x y) #t #f)))
         ((compare) (lambda (x y) (if-rel? (compare         x y) #t #f)))
         ((x y)                   (if-rel? (default-compare x y) #t #f))
         ((compare x y)
          (if (procedure? compare)
              (if-rel? (compare x y) #t #f)
              (error "not a procedure (Did you mean rel/rel??): " compare))))))))

(compare:define-rel? =?    if=?)
(compare:define-rel? <?    if<?)
(compare:define-rel? >?    if>?)
(compare:define-rel? <=?   if<=?)
(compare:define-rel? >=?   if>=?)
(compare:define-rel? not=? if-not=?)


; chains of length 3

(define-syntax compare:define-rel1/rel2?
  (syntax-rules ()
    ((compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?)
     (define rel1/rel2?
       (case-lambda
         (()
          (lambda (x y z)
            (if-rel1? (default-compare x y)
                      (if-rel2? (default-compare y z) #t #f)
                      (compare:checked #f default-compare z))))
         ((compare)
          (lambda (x y z)
            (if-rel1? (compare x y)
                      (if-rel2? (compare y z) #t #f)
                      (compare:checked #f compare z))))
         ((x y z)
          (if-rel1? (default-compare x y)
                    (if-rel2? (default-compare y z) #t #f)
                    (compare:checked #f default-compare z)))
         ((compare x y z)
          (if-rel1? (compare x y)
                    (if-rel2? (compare y z) #t #f)
                    (compare:checked #f compare z))))))))

(compare:define-rel1/rel2? </<?   if<?  if<?)
(compare:define-rel1/rel2? </<=?  if<?  if<=?)
(compare:define-rel1/rel2? <=/<?  if<=? if<?)
(compare:define-rel1/rel2? <=/<=? if<=? if<=?)
(compare:define-rel1/rel2? >/>?   if>?  if>?)
(compare:define-rel1/rel2? >/>=?  if>?  if>=?)
(compare:define-rel1/rel2? >=/>?  if>=? if>?)
(compare:define-rel1/rel2? >=/>=? if>=? if>=?)


; chains of arbitrary length

(define-syntax compare:define-chain-rel?
  (syntax-rules ()
    ((compare:define-chain-rel? chain-rel? if-rel?)
     (define chain-rel?
       (case-lambda
         ((compare)
          #t)
         ((compare x1)
          (compare:checked #t compare x1))
         ((compare x1 x2)
          (if-rel? (compare x1 x2) #t #f))
         ((compare x1 x2 x3)
          (if-rel? (compare x1 x2)
                   (if-rel? (compare x2 x3) #t #f)
                   (compare:checked #f compare x3)))
         ((compare x1 x2 . x3+)
          (if-rel? (compare x1 x2)
                   (let chain? ((head x2) (tail x3+))
                     (if (null? tail)
                         #t
                         (if-rel? (compare head (car tail))
                                  (chain? (car tail) (cdr tail))
                                  (apply compare:checked #f 
                                         compare (cdr tail)))))
                   (apply compare:checked #f compare x3+))))))))

(compare:define-chain-rel? chain=?  if=?)
(compare:define-chain-rel? chain<?  if<?)
(compare:define-chain-rel? chain>?  if>?)
(compare:define-chain-rel? chain<=? if<=?)
(compare:define-chain-rel? chain>=? if>=?)


; pairwise inequality

(define pairwise-not=?
  (let ((= =) (<= <=))
    (case-lambda
      ((compare)
       #t)
      ((compare x1)
       (compare:checked #t compare x1))
      ((compare x1 x2)
       (if-not=? (compare x1 x2) #t #f))
      ((compare x1 x2 x3)
       (if-not=? (compare x1 x2)
                 (if-not=? (compare x2 x3)
                           (if-not=? (compare x1 x3) #t #f)
                           #f)
                 (compare:checked #f compare x3)))
      ((compare . x1+)
       (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t))
         (if (< n 2)
             (if (and unchecked? (= n 1))
                 (compare:checked #t compare (car x))
                 #t)
             (let* ((i-pivot (random-integer n))
                    (x-pivot (list-ref x i-pivot)))
               (let split ((i 0) (x x) (x< '()) (x> '()))
                 (if (null? x)
                     (and (unequal? x< (length x<) #f)
                          (unequal? x> (length x>) #f))
                     (if (= i i-pivot)
                         (split (+ i 1) (cdr x) x< x>)
                         (if3 (compare (car x) x-pivot)
                              (split (+ i 1) (cdr x) (cons (car x) x<) x>)
                              (if unchecked?
                                  (apply compare:checked #f compare (cdr x))
                                  #f)
                              (split (+ i 1) (cdr x) x< (cons (car x) x>)))))))))))))


; min/max

(define min-compare
  (case-lambda
    ((compare x1)
     (compare:checked x1 compare x1))
    ((compare x1 x2)
     (if<=? (compare x1 x2) x1 x2))
    ((compare x1 x2 x3)
     (if<=? (compare x1 x2)
            (if<=? (compare x1 x3) x1 x3)
            (if<=? (compare x2 x3) x2 x3)))
    ((compare x1 x2 x3 x4)
     (if<=? (compare x1 x2)
            (if<=? (compare x1 x3)
                   (if<=? (compare x1 x4) x1 x4)
                   (if<=? (compare x3 x4) x3 x4))
            (if<=? (compare x2 x3)
                   (if<=? (compare x2 x4) x2 x4)
                   (if<=? (compare x3 x4) x3 x4))))
    ((compare x1 x2 . x3+)
     (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+))
       (if (null? xs)
           xmin
           (min (if<=? (compare xmin (car xs)) xmin (car xs))
                (cdr xs)))))))

(define max-compare
  (case-lambda
    ((compare x1)
     (compare:checked x1 compare x1))
    ((compare x1 x2)
     (if>=? (compare x1 x2) x1 x2))
    ((compare x1 x2 x3)
     (if>=? (compare x1 x2)
            (if>=? (compare x1 x3) x1 x3)
            (if>=? (compare x2 x3) x2 x3)))
    ((compare x1 x2 x3 x4)
     (if>=? (compare x1 x2)
            (if>=? (compare x1 x3)
                   (if>=? (compare x1 x4) x1 x4)
                   (if>=? (compare x3 x4) x3 x4))
            (if>=? (compare x2 x3)
                   (if>=? (compare x2 x4) x2 x4)
                   (if>=? (compare x3 x4) x3 x4))))
    ((compare x1 x2 . x3+)
     (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+))
       (if (null? xs)
           xmax
           (max (if>=? (compare xmax (car xs)) xmax (car xs))
                (cdr xs)))))))


; kth-largest

(define kth-largest
  (let ((= =) (< <))
    (case-lambda
      ((compare k x0)
       (case (modulo k 1)
         ((0)  (compare:checked x0 compare x0))
         (else (error "bad index" k))))
      ((compare k x0 x1)
       (case (modulo k 2)
         ((0) (if<=? (compare x0 x1) x0 x1))
         ((1) (if<=? (compare x0 x1) x1 x0))
         (else (error "bad index" k))))
      ((compare k x0 x1 x2)
       (case (modulo k 3)
         ((0) (if<=? (compare x0 x1)
                     (if<=? (compare x0 x2) x0 x2)
                     (if<=? (compare x1 x2) x1 x2)))
         ((1) (if3 (compare x0 x1)
                   (if<=? (compare x1 x2)
                          x1
                          (if<=? (compare x0 x2) x2 x0))
                   (if<=? (compare x0 x2) x1 x0)
                   (if<=? (compare x0 x2)
                          x0
                          (if<=? (compare x1 x2) x2 x1))))
         ((2) (if<=? (compare x0 x1)
                     (if<=? (compare x1 x2) x2 x1)
                     (if<=? (compare x0 x2) x2 x0)))
         (else (error "bad index" k))))
      ((compare k x0 . x1+) ; |x1+| >= 1
       (if (not (and (integer? k) (exact? k)))
           (error "bad index" k))
       (let ((n (+ 1 (length x1+))))
         (let kth ((k   (modulo k n))
                   (n   n)  ; = |x|
                   (rev #t) ; are x<, x=, x> reversed?
                   (x   (cons x0 x1+)))
           (let ((pivot (list-ref x (random-integer n))))
             (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0))
               (if (null? x)
                   (cond
                     ((< k n<)
                      (kth k n< (not rev) x<))
                     ((< k (+ n< n=))
                      (if rev
                          (list-ref x= (- (- n= 1) (- k n<)))
                          (list-ref x= (- k n<))))
                     (else
                      (kth (- k (+ n< n=)) n> (not rev) x>)))
                   (if3 (compare (car x) pivot)
                        (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>)
                        (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>)
                        (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1))))))))))))


; compare functions from predicates

(define compare-by<
  (case-lambda
    ((lt)     (lambda (x y) (if (lt x y) -1 (if (lt y x)  1 0))))
    ((lt x y)               (if (lt x y) -1 (if (lt y x)  1 0)))))

(define compare-by>
  (case-lambda
    ((gt)     (lambda (x y) (if (gt x y) 1 (if (gt y x)  -1 0))))
    ((gt x y)               (if (gt x y) 1 (if (gt y x)  -1 0)))))

(define compare-by<=
  (case-lambda
    ((le)     (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1)))
    ((le x y)               (if (le x y) (if (le y x) 0 -1) 1))))

(define compare-by>=
  (case-lambda
    ((ge)     (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1)))
    ((ge x y)               (if (ge x y) (if (ge y x) 0 1) -1))))

(define compare-by=/<
  (case-lambda
    ((eq lt)     (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1))))
    ((eq lt x y)               (if (eq x y) 0 (if (lt x y) -1 1)))))

(define compare-by=/>
  (case-lambda
    ((eq gt)     (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1))))
    ((eq gt x y)               (if (eq x y) 0 (if (gt x y) 1 -1)))))

; refine and extend construction

(define-syntax refine-compare
  (syntax-rules ()
    ((refine-compare)
     0)
    ((refine-compare c1)
     c1)
    ((refine-compare c1 c2 cs ...)
     (if3 c1 -1 (refine-compare c2 cs ...) 1))))

(define-syntax select-compare
  (syntax-rules (else)
    ((select-compare x y clause ...)
     (let ((x-val x) (y-val y))
       (select-compare (x-val y-val clause ...))))
    ; used internally: (select-compare (x y clause ...))
    ((select-compare (x y))
     0)
    ((select-compare (x y (else c ...)))
     (refine-compare c ...))
    ((select-compare (x y (t? c ...) clause ...))
     (let ((t?-val t?))
       (let ((tx (t?-val x)) (ty (t?-val y)))
         (if tx
             (if ty (refine-compare c ...) -1)
             (if ty 1 (select-compare (x y clause ...)))))))))

(define-syntax cond-compare
  (syntax-rules (else)
    ((cond-compare)
     0)
    ((cond-compare (else cs ...))
     (refine-compare cs ...))
    ((cond-compare ((tx ty) cs ...) clause ...)
     (let ((tx-val tx) (ty-val ty))
       (if tx-val
           (if ty-val (refine-compare cs ...) -1)
           (if ty-val 1 (cond-compare clause ...)))))))


; R5RS atomic types

(define-syntax compare:type-check
  (syntax-rules ()
    ((compare:type-check type? type-name x)
     (if (not (type? x))
         (error (string-append "not " type-name ":") x)))
    ((compare:type-check type? type-name x y)
     (begin (compare:type-check type? type-name x)
            (compare:type-check type? type-name y)))))

(define-syntax compare:define-by=/<
  (syntax-rules ()
    ((compare:define-by=/< compare = < type? type-name)
     (define compare
       (let ((= =) (< <))
         (lambda (x y)
           (if (type? x)
               (if (eq? x y)
                   0
                   (if (type? y)
                       (if (= x y) 0 (if (< x y) -1 1))
                       (error (string-append "not " type-name ":") y)))
               (error (string-append "not " type-name ":") x))))))))

(define (boolean-compare x y)
  (compare:type-check boolean? "boolean" x y)
  (if x (if y 0 1) (if y -1 0)))

(compare:define-by=/< char-compare char=? char<? char? "char")

(compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char")

(compare:define-by=/< string-compare string=? string<? string? "string")

(compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string")

(define (symbol-compare x y)
  (compare:type-check symbol? "symbol" x y)
  (string-compare (symbol->string x) (symbol->string y)))

(compare:define-by=/< integer-compare = < integer? "integer")

(compare:define-by=/< rational-compare = < rational? "rational")

(compare:define-by=/< real-compare = < real? "real")

(define (complex-compare x y)
  (compare:type-check complex? "complex" x y)
  (if (and (real? x) (real? y))
      (real-compare x y)
      (refine-compare (real-compare (real-part x) (real-part y))
                      (real-compare (imag-part x) (imag-part y)))))

(define (number-compare x y)
  (compare:type-check number? "number" x y)
  (complex-compare x y))


; R5RS compound data structures: dotted pair, list, vector

(define (pair-compare-car compare)
  (lambda (x y)
    (compare (car x) (car y))))

(define (pair-compare-cdr compare)
  (lambda (x y)
    (compare (cdr x) (cdr y))))

(define pair-compare
  (case-lambda
    
    ; dotted pair
    ((pair-compare-car pair-compare-cdr x y)
     (refine-compare (pair-compare-car (car x) (car y))
                     (pair-compare-cdr (cdr x) (cdr y))))
    
    ; possibly improper lists
    ((compare x y)
     (cond-compare 
      (((null? x) (null? y)) 0)
      (((pair? x) (pair? y)) (compare              (car x) (car y))
                             (pair-compare compare (cdr x) (cdr y)))
      (else                  (compare x y))))
    
    ; for convenience
    ((x y)
     (pair-compare default-compare x y))))

(define list-compare
  (case-lambda
    ((compare x y empty? head tail)
     (cond-compare
      (((empty? x) (empty? y)) 0)
      (else (compare              (head x) (head y))
            (list-compare compare (tail x) (tail y) empty? head tail))))
    
    ; for convenience
    ((        x y empty? head tail)
     (list-compare default-compare x y empty? head tail))
    ((compare x y              )
     (list-compare compare         x y null? car   cdr))
    ((        x y              )
     (list-compare default-compare x y null? car   cdr))))

(define list-compare-as-vector
  (case-lambda
    ((compare x y empty? head tail)
     (refine-compare
      (let compare-length ((x x) (y y))
        (cond-compare
         (((empty? x) (empty? y)) 0)
         (else (compare-length (tail x) (tail y)))))
      (list-compare compare x y empty? head tail)))
    
    ; for convenience
    ((        x y empty? head tail)
     (list-compare-as-vector default-compare x y empty? head tail))
    ((compare x y              )
     (list-compare-as-vector compare         x y null?  car  cdr))
    ((        x y              )
     (list-compare-as-vector default-compare x y null?  car  cdr))))

(define vector-compare
  (let ((= =))
    (case-lambda
      ((compare x y size ref)
       (let ((n (size x)) (m (size y)))
         (refine-compare 
          (integer-compare n m)
          (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
            (if (= i n)
                0
                (refine-compare (compare (ref x i) (ref y i))
                                (compare-rest (+ i 1))))))))
      
      ; for convenience
      ((        x y size ref)
       (vector-compare default-compare x y size          ref))
      ((compare x y           )
       (vector-compare compare         x y vector-length vector-ref))
      ((        x y           )
       (vector-compare default-compare x y vector-length vector-ref)))))

(define vector-compare-as-list
  (let ((= =))
    (case-lambda
      ((compare x y size ref)
       (let ((nx (size x)) (ny (size y)))
         (let ((n (min nx ny)))
           (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
             (if (= i n)
                 (integer-compare nx ny)
                 (refine-compare (compare (ref x i) (ref y i))
                                 (compare-rest (+ i 1))))))))
      
      ; for convenience
      ((        x y size ref)
       (vector-compare-as-list default-compare x y size          ref))
      ((compare x y           )
       (vector-compare-as-list compare         x y vector-length vector-ref))
      ((        x y           )
       (vector-compare-as-list default-compare x y vector-length vector-ref)))))


; default compare

(define (default-compare x y)
  (select-compare 
   x y
   (null?    0)
   (pair?    (default-compare (car x) (car y))
             (default-compare (cdr x) (cdr y)))
   (boolean? (boolean-compare x y))
   (char?    (char-compare    x y))
   (string?  (string-compare  x y))
   (symbol?  (symbol-compare  x y))
   (number?  (number-compare  x y))
   (vector?  (vector-compare default-compare x y))
   (else (error "unrecognized type in default-compare" x y))))

; Note that we pass default-compare to compare-{pair,vector} explictly.
; This makes sure recursion proceeds with this default-compare, which 
; need not be the one in the lexical scope of compare-{pair,vector}.


; debug compare

(define (debug-compare c)
  
  (define (checked-value c x y)
    (let ((c-xy (c x y)))
      (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1))
          c-xy
          (error "compare value not in {-1,0,1}" c-xy (list c x y)))))
  
  (define (random-boolean)
    (zero? (random-integer 2)))
  
  (define q ; (u v w) such that u <= v, v <= w, and not u <= w
    '#(
       ;x < y   x = y   x > y   [x < z]
       0       0       0    ; y < z
               0    (z y x) (z y x) ; y = z
               0    (z y x) (z y x) ; y > z
               
               ;x < y   x = y   x > y   [x = z]
               (y z x) (z x y)    0    ; y < z
               (y z x)    0    (x z y) ; y = z
               0    (y x z) (x z y) ; y > z
               
               ;x < y   x = y   x > y   [x > z]
               (x y z) (x y z)    0    ; y < z
               (x y z) (x y z)    0    ; y = z
               0       0       0    ; y > z
               ))
  
  (let ((z? #f) (z #f)) ; stored element from previous call
    (lambda (x y)
      (let ((c-xx (checked-value c x x))
            (c-yy (checked-value c y y))
            (c-xy (checked-value c x y))
            (c-yx (checked-value c y x)))
        (if (not (zero? c-xx))
            (error "compare error: not reflexive" c x))
        (if (not (zero? c-yy))
            (error "compare error: not reflexive" c y))
        (if (not (zero? (+ c-xy c-yx)))
            (error "compare error: not anti-symmetric" c x y))
        (if z?
            (let ((c-xz (checked-value c x z))
                  (c-zx (checked-value c z x))
                  (c-yz (checked-value c y z))
                  (c-zy (checked-value c z y)))
              (if (not (zero? (+ c-xz c-zx)))
                  (error "compare error: not anti-symmetric" c x z))
              (if (not (zero? (+ c-yz c-zy)))
                  (error "compare error: not anti-symmetric" c y z))
              (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13))))
                (if (list? ijk)
                    (apply error
                           "compare error: not transitive"
                           c 
                           (map (lambda (i) (case i ((x) x) ((y) y) ((z) z)))
                                ijk)))))
            (set! z? #t))
        (set! z (if (random-boolean) x y)) ; randomized testing
        c-xy))))
  
  )

Added srfi/s67/compare-procedures.sls.















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s67 compare-procedures)
  (export  </<=? </<? <=/<=? <=/<? <=? <? =?
           >/>=? >/>? >=/>=? >=/>? >=? >?
           boolean-compare chain<=? chain<? chain=? chain>=? chain>?
           char-compare char-compare-ci
           compare-by< compare-by<= compare-by=/< compare-by=/> compare-by> 
           compare-by>= complex-compare cond-compare
           debug-compare default-compare
           if-not=? if3 if<=? if<? if=? if>=? if>? integer-compare
           kth-largest list-compare list-compare-as-vector
           max-compare min-compare not=? number-compare
           pair-compare pair-compare-car pair-compare-cdr
           pairwise-not=? rational-compare real-compare
           refine-compare select-compare string-compare string-compare-ci 
           symbol-compare vector-compare vector-compare-as-list)
  
  (import (except (rnrs) error)
          (rnrs r5rs)    ; for modulo
          (srfi s27 random-bits)  ; for random-integer
          (srfi s23 error)
          ;; (srfi s23 error tricks)
          (srfi private include))

  (define (default-compare x y)
    (select-compare 
     x y
     (null?    0)
     (pair?    (default-compare (car x) (car y))
               (default-compare (cdr x) (cdr y)))
     (boolean? (boolean-compare x y))
     (char?    (char-compare    x y))
     (string?  (string-compare  x y))
     (symbol?  (symbol-compare  x y))
     (number?  (number-compare  x y))
     (vector?  (vector-compare default-compare x y))
     (else (error "unrecognized type in default-compare" x y))))  
  
  ;; (SRFI-23-error->R6RS "(library (srfi s67 compare-procedures))"
  ;;  (include/resolve ("srfi" "s67") "compare.ss"))

; (define current-compare (make-parameter default-compare))
; (provide current-compare)

; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard.
; 
; Permission is hereby granted, free of charge, to any person obtaining
; a copy of this software and associated documentation files (the
; ``Software''), to deal in the Software without restriction, including
; without limitation the rights to use, copy, modify, merge, publish,
; distribute, sublicense, and/or sell copies of the Software, and to
; permit persons to whom the Software is furnished to do so, subject to
; the following conditions:
; 
; The above copyright notice and this permission notice shall be
; included in all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; 
; -----------------------------------------------------------------------
; 
; Compare procedures SRFI (reference implementation)
; Sebastian.Egner@philips.com, Jensaxel@soegaard.net
; history of this file:
;   SE, 14-Oct-2004: first version
;   SE, 18-Oct-2004: 1st redesign: axioms for 'compare function'
;   SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite
;   SE,  2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's
;   SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare
;   SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added
;   SE, 12-Jan-2005: pair-compare-cdr
;   SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=?
;   SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc.
;   JS, 24-Feb-2005: selection-compare added
;   SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc.
;   JS, 28-Feb-2005: kth-largest modified - is "stable" now
;   SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged
;   SE, 07-Apr-2005: compare-based type checks made explicit
;   SE, 18-Apr-2005: added (rel? compare) and eq?-test
;   SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y

; =============================================================================

; Reference Implementation
; ========================
;
; in R5RS (including hygienic macros)
;  + SRFI-16 (case-lambda) 
;  + SRFI-23 (error) 
;  + SRFI-27 (random-integer)

; Implementation remarks:
;   * In general, the emphasis of this implementation is on correctness
;     and portability, not on efficiency.
;   * Variable arity procedures are expressed in terms of case-lambda
;     in the hope that this will produce efficient code for the case
;     where the arity is statically known at the call site.
;   * In procedures that are required to type-check their arguments,
;     we use (compare x x) for executing extra checks. This relies on
;     the assumption that eq? is used to catch this case quickly.
;   * Care has been taken to reference comparison procedures of R5RS
;     only at the time the operations here are being defined. This
;     makes it possible to redefine these operations, if need be.
;   * For the sake of efficiency, some inlining has been done by hand.
;     This is mainly expressed by macros producing defines.
;   * Identifiers of the form compare:<something> are private.
;
; Hints for low-level implementation:
;   * The basis of this SRFI are the atomic compare procedures, 
;     i.e. boolean-compare, char-compare, etc. and the conditionals
;     if3, if=?, if<? etc., and default-compare. These should make
;     optimal use of the available type information.
;   * For the sake of speed, the reference implementation does not
;     use a LET to save the comparison value c for the ERROR call.
;     This can be fixed in a low-level implementation at no cost.
;   * Type-checks based on (compare x x) are made explicit by the
;     expression (compare:check result compare x ...).
;   * Eq? should  can used to speed up built-in compare procedures,
;     but it can only be used after type-checking at least one of
;     the arguments.

(define (compare:checked result compare . args)
  (for-each (lambda (x) (compare x x)) args)
  result)


; 3-sided conditional

(define-syntax if3
  (syntax-rules ()
    ((if3 c less equal greater)
     (case c
       ((-1) less)
       (( 0) equal)
       (( 1) greater)
       (else (error "comparison value not in {-1,0,1}"))))))


; 2-sided conditionals for comparisons

(define-syntax compare:if-rel?
  (syntax-rules ()
    ((compare:if-rel? c-cases a-cases c consequence)
     (compare:if-rel? c-cases a-cases c consequence (if #f #f)))
    ((compare:if-rel? c-cases a-cases c consequence alternate)
     (case c
       (c-cases consequence)
       (a-cases alternate)
       (else    (error "comparison value not in {-1,0,1}"))))))

(define-syntax if=?
  (syntax-rules ()
    ((if=? arg ...)
     (compare:if-rel? (0) (-1 1) arg ...))))

(define-syntax if<?
  (syntax-rules ()
    ((if<? arg ...)
     (compare:if-rel? (-1) (0 1) arg ...))))

(define-syntax if>?
  (syntax-rules ()
    ((if>? arg ...)
     (compare:if-rel? (1) (-1 0) arg ...))))

(define-syntax if<=?
  (syntax-rules ()
    ((if<=? arg ...)
     (compare:if-rel? (-1 0) (1) arg ...))))

(define-syntax if>=?
  (syntax-rules ()
    ((if>=? arg ...)
     (compare:if-rel? (0 1) (-1) arg ...))))

(define-syntax if-not=?
  (syntax-rules ()
    ((if-not=? arg ...)
     (compare:if-rel? (-1 1) (0) arg ...))))


; predicates from compare procedures

(define-syntax compare:define-rel?
  (syntax-rules ()
    ((compare:define-rel? rel? if-rel?)
     (define rel?
       (case-lambda
         (()        (lambda (x y) (if-rel? (default-compare x y) #t #f)))
         ((compare) (lambda (x y) (if-rel? (compare         x y) #t #f)))
         ((x y)                   (if-rel? (default-compare x y) #t #f))
         ((compare x y)
          (if (procedure? compare)
              (if-rel? (compare x y) #t #f)
              (error "not a procedure (Did you mean rel/rel??): " compare))))))))

(compare:define-rel? =?    if=?)
(compare:define-rel? <?    if<?)
(compare:define-rel? >?    if>?)
(compare:define-rel? <=?   if<=?)
(compare:define-rel? >=?   if>=?)
(compare:define-rel? not=? if-not=?)


; chains of length 3

(define-syntax compare:define-rel1/rel2?
  (syntax-rules ()
    ((compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?)
     (define rel1/rel2?
       (case-lambda
         (()
          (lambda (x y z)
            (if-rel1? (default-compare x y)
                      (if-rel2? (default-compare y z) #t #f)
                      (compare:checked #f default-compare z))))
         ((compare)
          (lambda (x y z)
            (if-rel1? (compare x y)
                      (if-rel2? (compare y z) #t #f)
                      (compare:checked #f compare z))))
         ((x y z)
          (if-rel1? (default-compare x y)
                    (if-rel2? (default-compare y z) #t #f)
                    (compare:checked #f default-compare z)))
         ((compare x y z)
          (if-rel1? (compare x y)
                    (if-rel2? (compare y z) #t #f)
                    (compare:checked #f compare z))))))))

(compare:define-rel1/rel2? </<?   if<?  if<?)
(compare:define-rel1/rel2? </<=?  if<?  if<=?)
(compare:define-rel1/rel2? <=/<?  if<=? if<?)
(compare:define-rel1/rel2? <=/<=? if<=? if<=?)
(compare:define-rel1/rel2? >/>?   if>?  if>?)
(compare:define-rel1/rel2? >/>=?  if>?  if>=?)
(compare:define-rel1/rel2? >=/>?  if>=? if>?)
(compare:define-rel1/rel2? >=/>=? if>=? if>=?)


; chains of arbitrary length

(define-syntax compare:define-chain-rel?
  (syntax-rules ()
    ((compare:define-chain-rel? chain-rel? if-rel?)
     (define chain-rel?
       (case-lambda
         ((compare)
          #t)
         ((compare x1)
          (compare:checked #t compare x1))
         ((compare x1 x2)
          (if-rel? (compare x1 x2) #t #f))
         ((compare x1 x2 x3)
          (if-rel? (compare x1 x2)
                   (if-rel? (compare x2 x3) #t #f)
                   (compare:checked #f compare x3)))
         ((compare x1 x2 . x3+)
          (if-rel? (compare x1 x2)
                   (let chain? ((head x2) (tail x3+))
                     (if (null? tail)
                         #t
                         (if-rel? (compare head (car tail))
                                  (chain? (car tail) (cdr tail))
                                  (apply compare:checked #f 
                                         compare (cdr tail)))))
                   (apply compare:checked #f compare x3+))))))))

(compare:define-chain-rel? chain=?  if=?)
(compare:define-chain-rel? chain<?  if<?)
(compare:define-chain-rel? chain>?  if>?)
(compare:define-chain-rel? chain<=? if<=?)
(compare:define-chain-rel? chain>=? if>=?)


; pairwise inequality

(define pairwise-not=?
  (let ((= =) (<= <=))
    (case-lambda
      ((compare)
       #t)
      ((compare x1)
       (compare:checked #t compare x1))
      ((compare x1 x2)
       (if-not=? (compare x1 x2) #t #f))
      ((compare x1 x2 x3)
       (if-not=? (compare x1 x2)
                 (if-not=? (compare x2 x3)
                           (if-not=? (compare x1 x3) #t #f)
                           #f)
                 (compare:checked #f compare x3)))
      ((compare . x1+)
       (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t))
         (if (< n 2)
             (if (and unchecked? (= n 1))
                 (compare:checked #t compare (car x))
                 #t)
             (let* ((i-pivot (random-integer n))
                    (x-pivot (list-ref x i-pivot)))
               (let split ((i 0) (x x) (x< '()) (x> '()))
                 (if (null? x)
                     (and (unequal? x< (length x<) #f)
                          (unequal? x> (length x>) #f))
                     (if (= i i-pivot)
                         (split (+ i 1) (cdr x) x< x>)
                         (if3 (compare (car x) x-pivot)
                              (split (+ i 1) (cdr x) (cons (car x) x<) x>)
                              (if unchecked?
                                  (apply compare:checked #f compare (cdr x))
                                  #f)
                              (split (+ i 1) (cdr x) x< (cons (car x) x>)))))))))))))


; min/max

(define min-compare
  (case-lambda
    ((compare x1)
     (compare:checked x1 compare x1))
    ((compare x1 x2)
     (if<=? (compare x1 x2) x1 x2))
    ((compare x1 x2 x3)
     (if<=? (compare x1 x2)
            (if<=? (compare x1 x3) x1 x3)
            (if<=? (compare x2 x3) x2 x3)))
    ((compare x1 x2 x3 x4)
     (if<=? (compare x1 x2)
            (if<=? (compare x1 x3)
                   (if<=? (compare x1 x4) x1 x4)
                   (if<=? (compare x3 x4) x3 x4))
            (if<=? (compare x2 x3)
                   (if<=? (compare x2 x4) x2 x4)
                   (if<=? (compare x3 x4) x3 x4))))
    ((compare x1 x2 . x3+)
     (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+))
       (if (null? xs)
           xmin
           (min (if<=? (compare xmin (car xs)) xmin (car xs))
                (cdr xs)))))))

(define max-compare
  (case-lambda
    ((compare x1)
     (compare:checked x1 compare x1))
    ((compare x1 x2)
     (if>=? (compare x1 x2) x1 x2))
    ((compare x1 x2 x3)
     (if>=? (compare x1 x2)
            (if>=? (compare x1 x3) x1 x3)
            (if>=? (compare x2 x3) x2 x3)))
    ((compare x1 x2 x3 x4)
     (if>=? (compare x1 x2)
            (if>=? (compare x1 x3)
                   (if>=? (compare x1 x4) x1 x4)
                   (if>=? (compare x3 x4) x3 x4))
            (if>=? (compare x2 x3)
                   (if>=? (compare x2 x4) x2 x4)
                   (if>=? (compare x3 x4) x3 x4))))
    ((compare x1 x2 . x3+)
     (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+))
       (if (null? xs)
           xmax
           (max (if>=? (compare xmax (car xs)) xmax (car xs))
                (cdr xs)))))))


; kth-largest

(define kth-largest
  (let ((= =) (< <))
    (case-lambda
      ((compare k x0)
       (case (modulo k 1)
         ((0)  (compare:checked x0 compare x0))
         (else (error "bad index" k))))
      ((compare k x0 x1)
       (case (modulo k 2)
         ((0) (if<=? (compare x0 x1) x0 x1))
         ((1) (if<=? (compare x0 x1) x1 x0))
         (else (error "bad index" k))))
      ((compare k x0 x1 x2)
       (case (modulo k 3)
         ((0) (if<=? (compare x0 x1)
                     (if<=? (compare x0 x2) x0 x2)
                     (if<=? (compare x1 x2) x1 x2)))
         ((1) (if3 (compare x0 x1)
                   (if<=? (compare x1 x2)
                          x1
                          (if<=? (compare x0 x2) x2 x0))
                   (if<=? (compare x0 x2) x1 x0)
                   (if<=? (compare x0 x2)
                          x0
                          (if<=? (compare x1 x2) x2 x1))))
         ((2) (if<=? (compare x0 x1)
                     (if<=? (compare x1 x2) x2 x1)
                     (if<=? (compare x0 x2) x2 x0)))
         (else (error "bad index" k))))
      ((compare k x0 . x1+) ; |x1+| >= 1
       (if (not (and (integer? k) (exact? k)))
           (error "bad index" k))
       (let ((n (+ 1 (length x1+))))
         (let kth ((k   (modulo k n))
                   (n   n)  ; = |x|
                   (rev #t) ; are x<, x=, x> reversed?
                   (x   (cons x0 x1+)))
           (let ((pivot (list-ref x (random-integer n))))
             (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0))
               (if (null? x)
                   (cond
                     ((< k n<)
                      (kth k n< (not rev) x<))
                     ((< k (+ n< n=))
                      (if rev
                          (list-ref x= (- (- n= 1) (- k n<)))
                          (list-ref x= (- k n<))))
                     (else
                      (kth (- k (+ n< n=)) n> (not rev) x>)))
                   (if3 (compare (car x) pivot)
                        (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>)
                        (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>)
                        (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1))))))))))))


; compare functions from predicates

(define compare-by<
  (case-lambda
    ((lt)     (lambda (x y) (if (lt x y) -1 (if (lt y x)  1 0))))
    ((lt x y)               (if (lt x y) -1 (if (lt y x)  1 0)))))

(define compare-by>
  (case-lambda
    ((gt)     (lambda (x y) (if (gt x y) 1 (if (gt y x)  -1 0))))
    ((gt x y)               (if (gt x y) 1 (if (gt y x)  -1 0)))))

(define compare-by<=
  (case-lambda
    ((le)     (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1)))
    ((le x y)               (if (le x y) (if (le y x) 0 -1) 1))))

(define compare-by>=
  (case-lambda
    ((ge)     (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1)))
    ((ge x y)               (if (ge x y) (if (ge y x) 0 1) -1))))

(define compare-by=/<
  (case-lambda
    ((eq lt)     (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1))))
    ((eq lt x y)               (if (eq x y) 0 (if (lt x y) -1 1)))))

(define compare-by=/>
  (case-lambda
    ((eq gt)     (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1))))
    ((eq gt x y)               (if (eq x y) 0 (if (gt x y) 1 -1)))))

; refine and extend construction

(define-syntax refine-compare
  (syntax-rules ()
    ((refine-compare)
     0)
    ((refine-compare c1)
     c1)
    ((refine-compare c1 c2 cs ...)
     (if3 c1 -1 (refine-compare c2 cs ...) 1))))

(define-syntax select-compare
  (syntax-rules (else)
    ((select-compare x y clause ...)
     (let ((x-val x) (y-val y))
       (select-compare (x-val y-val clause ...))))
    ; used internally: (select-compare (x y clause ...))
    ((select-compare (x y))
     0)
    ((select-compare (x y (else c ...)))
     (refine-compare c ...))
    ((select-compare (x y (t? c ...) clause ...))
     (let ((t?-val t?))
       (let ((tx (t?-val x)) (ty (t?-val y)))
         (if tx
             (if ty (refine-compare c ...) -1)
             (if ty 1 (select-compare (x y clause ...)))))))))

(define-syntax cond-compare
  (syntax-rules (else)
    ((cond-compare)
     0)
    ((cond-compare (else cs ...))
     (refine-compare cs ...))
    ((cond-compare ((tx ty) cs ...) clause ...)
     (let ((tx-val tx) (ty-val ty))
       (if tx-val
           (if ty-val (refine-compare cs ...) -1)
           (if ty-val 1 (cond-compare clause ...)))))))


; R5RS atomic types

(define-syntax compare:type-check
  (syntax-rules ()
    ((compare:type-check type? type-name x)
     (if (not (type? x))
         (error (string-append "not " type-name ":") x)))
    ((compare:type-check type? type-name x y)
     (begin (compare:type-check type? type-name x)
            (compare:type-check type? type-name y)))))

(define-syntax compare:define-by=/<
  (syntax-rules ()
    ((compare:define-by=/< compare = < type? type-name)
     (define compare
       (let ((= =) (< <))
         (lambda (x y)
           (if (type? x)
               (if (eq? x y)
                   0
                   (if (type? y)
                       (if (= x y) 0 (if (< x y) -1 1))
                       (error (string-append "not " type-name ":") y)))
               (error (string-append "not " type-name ":") x))))))))

(define (boolean-compare x y)
  (compare:type-check boolean? "boolean" x y)
  (if x (if y 0 1) (if y -1 0)))

(compare:define-by=/< char-compare char=? char<? char? "char")

(compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char")

(compare:define-by=/< string-compare string=? string<? string? "string")

(compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string")

(define (symbol-compare x y)
  (compare:type-check symbol? "symbol" x y)
  (string-compare (symbol->string x) (symbol->string y)))

(compare:define-by=/< integer-compare = < integer? "integer")

(compare:define-by=/< rational-compare = < rational? "rational")

(compare:define-by=/< real-compare = < real? "real")

(define (complex-compare x y)
  (compare:type-check complex? "complex" x y)
  (if (and (real? x) (real? y))
      (real-compare x y)
      (refine-compare (real-compare (real-part x) (real-part y))
                      (real-compare (imag-part x) (imag-part y)))))

(define (number-compare x y)
  (compare:type-check number? "number" x y)
  (complex-compare x y))


; R5RS compound data structures: dotted pair, list, vector

(define (pair-compare-car compare)
  (lambda (x y)
    (compare (car x) (car y))))

(define (pair-compare-cdr compare)
  (lambda (x y)
    (compare (cdr x) (cdr y))))

(define pair-compare
  (case-lambda
    
    ; dotted pair
    ((pair-compare-car pair-compare-cdr x y)
     (refine-compare (pair-compare-car (car x) (car y))
                     (pair-compare-cdr (cdr x) (cdr y))))
    
    ; possibly improper lists
    ((compare x y)
     (cond-compare 
      (((null? x) (null? y)) 0)
      (((pair? x) (pair? y)) (compare              (car x) (car y))
                             (pair-compare compare (cdr x) (cdr y)))
      (else                  (compare x y))))
    
    ; for convenience
    ((x y)
     (pair-compare default-compare x y))))

(define list-compare
  (case-lambda
    ((compare x y empty? head tail)
     (cond-compare
      (((empty? x) (empty? y)) 0)
      (else (compare              (head x) (head y))
            (list-compare compare (tail x) (tail y) empty? head tail))))
    
    ; for convenience
    ((        x y empty? head tail)
     (list-compare default-compare x y empty? head tail))
    ((compare x y              )
     (list-compare compare         x y null? car   cdr))
    ((        x y              )
     (list-compare default-compare x y null? car   cdr))))

(define list-compare-as-vector
  (case-lambda
    ((compare x y empty? head tail)
     (refine-compare
      (let compare-length ((x x) (y y))
        (cond-compare
         (((empty? x) (empty? y)) 0)
         (else (compare-length (tail x) (tail y)))))
      (list-compare compare x y empty? head tail)))
    
    ; for convenience
    ((        x y empty? head tail)
     (list-compare-as-vector default-compare x y empty? head tail))
    ((compare x y              )
     (list-compare-as-vector compare         x y null?  car  cdr))
    ((        x y              )
     (list-compare-as-vector default-compare x y null?  car  cdr))))

(define vector-compare
  (let ((= =))
    (case-lambda
      ((compare x y size ref)
       (let ((n (size x)) (m (size y)))
         (refine-compare 
          (integer-compare n m)
          (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
            (if (= i n)
                0
                (refine-compare (compare (ref x i) (ref y i))
                                (compare-rest (+ i 1))))))))
      
      ; for convenience
      ((        x y size ref)
       (vector-compare default-compare x y size          ref))
      ((compare x y           )
       (vector-compare compare         x y vector-length vector-ref))
      ((        x y           )
       (vector-compare default-compare x y vector-length vector-ref)))))

(define vector-compare-as-list
  (let ((= =))
    (case-lambda
      ((compare x y size ref)
       (let ((nx (size x)) (ny (size y)))
         (let ((n (min nx ny)))
           (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
             (if (= i n)
                 (integer-compare nx ny)
                 (refine-compare (compare (ref x i) (ref y i))
                                 (compare-rest (+ i 1))))))))
      
      ; for convenience
      ((        x y size ref)
       (vector-compare-as-list default-compare x y size          ref))
      ((compare x y           )
       (vector-compare-as-list compare         x y vector-length vector-ref))
      ((        x y           )
       (vector-compare-as-list default-compare x y vector-length vector-ref)))))


; default compare

;; (define (default-compare x y)
;;   (select-compare 
;;    x y
;;    (null?    0)
;;    (pair?    (default-compare (car x) (car y))
;;              (default-compare (cdr x) (cdr y)))
;;    (boolean? (boolean-compare x y))
;;    (char?    (char-compare    x y))
;;    (string?  (string-compare  x y))
;;    (symbol?  (symbol-compare  x y))
;;    (number?  (number-compare  x y))
;;    (vector?  (vector-compare default-compare x y))
;;    (else (error "unrecognized type in default-compare" x y))))

; Note that we pass default-compare to compare-{pair,vector} explictly.
; This makes sure recursion proceeds with this default-compare, which 
; need not be the one in the lexical scope of compare-{pair,vector}.


; debug compare

(define (debug-compare c)
  
  (define (checked-value c x y)
    (let ((c-xy (c x y)))
      (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1))
          c-xy
          (error "compare value not in {-1,0,1}" c-xy (list c x y)))))
  
  (define (random-boolean)
    (zero? (random-integer 2)))
  
  (define q ; (u v w) such that u <= v, v <= w, and not u <= w
    '#(
       ;x < y   x = y   x > y   [x < z]
       0       0       0    ; y < z
               0    (z y x) (z y x) ; y = z
               0    (z y x) (z y x) ; y > z
               
               ;x < y   x = y   x > y   [x = z]
               (y z x) (z x y)    0    ; y < z
               (y z x)    0    (x z y) ; y = z
               0    (y x z) (x z y) ; y > z
               
               ;x < y   x = y   x > y   [x > z]
               (x y z) (x y z)    0    ; y < z
               (x y z) (x y z)    0    ; y = z
               0       0       0    ; y > z
               ))
  
  (let ((z? #f) (z #f)) ; stored element from previous call
    (lambda (x y)
      (let ((c-xx (checked-value c x x))
            (c-yy (checked-value c y y))
            (c-xy (checked-value c x y))
            (c-yx (checked-value c y x)))
        (if (not (zero? c-xx))
            (error "compare error: not reflexive" c x))
        (if (not (zero? c-yy))
            (error "compare error: not reflexive" c y))
        (if (not (zero? (+ c-xy c-yx)))
            (error "compare error: not anti-symmetric" c x y))
        (if z?
            (let ((c-xz (checked-value c x z))
                  (c-zx (checked-value c z x))
                  (c-yz (checked-value c y z))
                  (c-zy (checked-value c z y)))
              (if (not (zero? (+ c-xz c-zx)))
                  (error "compare error: not anti-symmetric" c x z))
              (if (not (zero? (+ c-yz c-zy)))
                  (error "compare error: not anti-symmetric" c y z))
              (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13))))
                (if (list? ijk)
                    (apply error
                           "compare error: not transitive"
                           c 
                           (map (lambda (i) (case i ((x) x) ((y) y) ((z) z)))
                                ijk)))))
            (set! z? #t))
        (set! z (if (random-boolean) x y)) ; randomized testing
        c-xy))))
  
  )

Added srfi/s67/compare.ss.













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
; (define current-compare (make-parameter default-compare))
; (provide current-compare)

; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard.
; 
; Permission is hereby granted, free of charge, to any person obtaining
; a copy of this software and associated documentation files (the
; ``Software''), to deal in the Software without restriction, including
; without limitation the rights to use, copy, modify, merge, publish,
; distribute, sublicense, and/or sell copies of the Software, and to
; permit persons to whom the Software is furnished to do so, subject to
; the following conditions:
; 
; The above copyright notice and this permission notice shall be
; included in all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; 
; -----------------------------------------------------------------------
; 
; Compare procedures SRFI (reference implementation)
; Sebastian.Egner@philips.com, Jensaxel@soegaard.net
; history of this file:
;   SE, 14-Oct-2004: first version
;   SE, 18-Oct-2004: 1st redesign: axioms for 'compare function'
;   SE, 29-Oct-2004: 2nd redesign: higher order reverse/map/refine/unite
;   SE,  2-Nov-2004: 3rd redesign: macros cond/refine-compare replace h.o.f's
;   SE, 10-Nov-2004: (im,re) replaced by (re,im) in complex-compare
;   SE, 11-Nov-2004: case-compare by case (not by cond); select-compare added
;   SE, 12-Jan-2005: pair-compare-cdr
;   SE, 15-Feb-2005: stricter typing for compare-<type>; pairwise-not=?
;   SE, 16-Feb-2005: case-compare -> if-compare -> if3; <? </<? chain<? etc.
;   JS, 24-Feb-2005: selection-compare added
;   SE, 25-Feb-2005: selection-compare -> kth-largest modified; if<? etc.
;   JS, 28-Feb-2005: kth-largest modified - is "stable" now
;   SE, 28-Feb-2005: simplified pairwise-not=?/kth-largest; min/max debugged
;   SE, 07-Apr-2005: compare-based type checks made explicit
;   SE, 18-Apr-2005: added (rel? compare) and eq?-test
;   SE, 16-May-2005: naming convention changed; compare-by< etc. optional x y

; =============================================================================

; Reference Implementation
; ========================
;
; in R5RS (including hygienic macros)
;  + SRFI-16 (case-lambda) 
;  + SRFI-23 (error) 
;  + SRFI-27 (random-integer)

; Implementation remarks:
;   * In general, the emphasis of this implementation is on correctness
;     and portability, not on efficiency.
;   * Variable arity procedures are expressed in terms of case-lambda
;     in the hope that this will produce efficient code for the case
;     where the arity is statically known at the call site.
;   * In procedures that are required to type-check their arguments,
;     we use (compare x x) for executing extra checks. This relies on
;     the assumption that eq? is used to catch this case quickly.
;   * Care has been taken to reference comparison procedures of R5RS
;     only at the time the operations here are being defined. This
;     makes it possible to redefine these operations, if need be.
;   * For the sake of efficiency, some inlining has been done by hand.
;     This is mainly expressed by macros producing defines.
;   * Identifiers of the form compare:<something> are private.
;
; Hints for low-level implementation:
;   * The basis of this SRFI are the atomic compare procedures, 
;     i.e. boolean-compare, char-compare, etc. and the conditionals
;     if3, if=?, if<? etc., and default-compare. These should make
;     optimal use of the available type information.
;   * For the sake of speed, the reference implementation does not
;     use a LET to save the comparison value c for the ERROR call.
;     This can be fixed in a low-level implementation at no cost.
;   * Type-checks based on (compare x x) are made explicit by the
;     expression (compare:check result compare x ...).
;   * Eq? should  can used to speed up built-in compare procedures,
;     but it can only be used after type-checking at least one of
;     the arguments.

(define (compare:checked result compare . args)
  (for-each (lambda (x) (compare x x)) args)
  result)


; 3-sided conditional

(define-syntax if3
  (syntax-rules ()
    ((if3 c less equal greater)
     (case c
       ((-1) less)
       (( 0) equal)
       (( 1) greater)
       (else (error "comparison value not in {-1,0,1}"))))))


; 2-sided conditionals for comparisons

(define-syntax compare:if-rel?
  (syntax-rules ()
    ((compare:if-rel? c-cases a-cases c consequence)
     (compare:if-rel? c-cases a-cases c consequence (if #f #f)))
    ((compare:if-rel? c-cases a-cases c consequence alternate)
     (case c
       (c-cases consequence)
       (a-cases alternate)
       (else    (error "comparison value not in {-1,0,1}"))))))

(define-syntax if=?
  (syntax-rules ()
    ((if=? arg ...)
     (compare:if-rel? (0) (-1 1) arg ...))))

(define-syntax if<?
  (syntax-rules ()
    ((if<? arg ...)
     (compare:if-rel? (-1) (0 1) arg ...))))

(define-syntax if>?
  (syntax-rules ()
    ((if>? arg ...)
     (compare:if-rel? (1) (-1 0) arg ...))))

(define-syntax if<=?
  (syntax-rules ()
    ((if<=? arg ...)
     (compare:if-rel? (-1 0) (1) arg ...))))

(define-syntax if>=?
  (syntax-rules ()
    ((if>=? arg ...)
     (compare:if-rel? (0 1) (-1) arg ...))))

(define-syntax if-not=?
  (syntax-rules ()
    ((if-not=? arg ...)
     (compare:if-rel? (-1 1) (0) arg ...))))


; predicates from compare procedures

(define-syntax compare:define-rel?
  (syntax-rules ()
    ((compare:define-rel? rel? if-rel?)
     (define rel?
       (case-lambda
         (()        (lambda (x y) (if-rel? (default-compare x y) #t #f)))
         ((compare) (lambda (x y) (if-rel? (compare         x y) #t #f)))
         ((x y)                   (if-rel? (default-compare x y) #t #f))
         ((compare x y)
          (if (procedure? compare)
              (if-rel? (compare x y) #t #f)
              (error "not a procedure (Did you mean rel/rel??): " compare))))))))

(compare:define-rel? =?    if=?)
(compare:define-rel? <?    if<?)
(compare:define-rel? >?    if>?)
(compare:define-rel? <=?   if<=?)
(compare:define-rel? >=?   if>=?)
(compare:define-rel? not=? if-not=?)


; chains of length 3

(define-syntax compare:define-rel1/rel2?
  (syntax-rules ()
    ((compare:define-rel1/rel2? rel1/rel2? if-rel1? if-rel2?)
     (define rel1/rel2?
       (case-lambda
         (()
          (lambda (x y z)
            (if-rel1? (default-compare x y)
                      (if-rel2? (default-compare y z) #t #f)
                      (compare:checked #f default-compare z))))
         ((compare)
          (lambda (x y z)
            (if-rel1? (compare x y)
                      (if-rel2? (compare y z) #t #f)
                      (compare:checked #f compare z))))
         ((x y z)
          (if-rel1? (default-compare x y)
                    (if-rel2? (default-compare y z) #t #f)
                    (compare:checked #f default-compare z)))
         ((compare x y z)
          (if-rel1? (compare x y)
                    (if-rel2? (compare y z) #t #f)
                    (compare:checked #f compare z))))))))

(compare:define-rel1/rel2? </<?   if<?  if<?)
(compare:define-rel1/rel2? </<=?  if<?  if<=?)
(compare:define-rel1/rel2? <=/<?  if<=? if<?)
(compare:define-rel1/rel2? <=/<=? if<=? if<=?)
(compare:define-rel1/rel2? >/>?   if>?  if>?)
(compare:define-rel1/rel2? >/>=?  if>?  if>=?)
(compare:define-rel1/rel2? >=/>?  if>=? if>?)
(compare:define-rel1/rel2? >=/>=? if>=? if>=?)


; chains of arbitrary length

(define-syntax compare:define-chain-rel?
  (syntax-rules ()
    ((compare:define-chain-rel? chain-rel? if-rel?)
     (define chain-rel?
       (case-lambda
         ((compare)
          #t)
         ((compare x1)
          (compare:checked #t compare x1))
         ((compare x1 x2)
          (if-rel? (compare x1 x2) #t #f))
         ((compare x1 x2 x3)
          (if-rel? (compare x1 x2)
                   (if-rel? (compare x2 x3) #t #f)
                   (compare:checked #f compare x3)))
         ((compare x1 x2 . x3+)
          (if-rel? (compare x1 x2)
                   (let chain? ((head x2) (tail x3+))
                     (if (null? tail)
                         #t
                         (if-rel? (compare head (car tail))
                                  (chain? (car tail) (cdr tail))
                                  (apply compare:checked #f 
                                         compare (cdr tail)))))
                   (apply compare:checked #f compare x3+))))))))

(compare:define-chain-rel? chain=?  if=?)
(compare:define-chain-rel? chain<?  if<?)
(compare:define-chain-rel? chain>?  if>?)
(compare:define-chain-rel? chain<=? if<=?)
(compare:define-chain-rel? chain>=? if>=?)


; pairwise inequality

(define pairwise-not=?
  (let ((= =) (<= <=))
    (case-lambda
      ((compare)
       #t)
      ((compare x1)
       (compare:checked #t compare x1))
      ((compare x1 x2)
       (if-not=? (compare x1 x2) #t #f))
      ((compare x1 x2 x3)
       (if-not=? (compare x1 x2)
                 (if-not=? (compare x2 x3)
                           (if-not=? (compare x1 x3) #t #f)
                           #f)
                 (compare:checked #f compare x3)))
      ((compare . x1+)
       (let unequal? ((x x1+) (n (length x1+)) (unchecked? #t))
         (if (< n 2)
             (if (and unchecked? (= n 1))
                 (compare:checked #t compare (car x))
                 #t)
             (let* ((i-pivot (random-integer n))
                    (x-pivot (list-ref x i-pivot)))
               (let split ((i 0) (x x) (x< '()) (x> '()))
                 (if (null? x)
                     (and (unequal? x< (length x<) #f)
                          (unequal? x> (length x>) #f))
                     (if (= i i-pivot)
                         (split (+ i 1) (cdr x) x< x>)
                         (if3 (compare (car x) x-pivot)
                              (split (+ i 1) (cdr x) (cons (car x) x<) x>)
                              (if unchecked?
                                  (apply compare:checked #f compare (cdr x))
                                  #f)
                              (split (+ i 1) (cdr x) x< (cons (car x) x>)))))))))))))


; min/max

(define min-compare
  (case-lambda
    ((compare x1)
     (compare:checked x1 compare x1))
    ((compare x1 x2)
     (if<=? (compare x1 x2) x1 x2))
    ((compare x1 x2 x3)
     (if<=? (compare x1 x2)
            (if<=? (compare x1 x3) x1 x3)
            (if<=? (compare x2 x3) x2 x3)))
    ((compare x1 x2 x3 x4)
     (if<=? (compare x1 x2)
            (if<=? (compare x1 x3)
                   (if<=? (compare x1 x4) x1 x4)
                   (if<=? (compare x3 x4) x3 x4))
            (if<=? (compare x2 x3)
                   (if<=? (compare x2 x4) x2 x4)
                   (if<=? (compare x3 x4) x3 x4))))
    ((compare x1 x2 . x3+)
     (let min ((xmin (if<=? (compare x1 x2) x1 x2)) (xs x3+))
       (if (null? xs)
           xmin
           (min (if<=? (compare xmin (car xs)) xmin (car xs))
                (cdr xs)))))))

(define max-compare
  (case-lambda
    ((compare x1)
     (compare:checked x1 compare x1))
    ((compare x1 x2)
     (if>=? (compare x1 x2) x1 x2))
    ((compare x1 x2 x3)
     (if>=? (compare x1 x2)
            (if>=? (compare x1 x3) x1 x3)
            (if>=? (compare x2 x3) x2 x3)))
    ((compare x1 x2 x3 x4)
     (if>=? (compare x1 x2)
            (if>=? (compare x1 x3)
                   (if>=? (compare x1 x4) x1 x4)
                   (if>=? (compare x3 x4) x3 x4))
            (if>=? (compare x2 x3)
                   (if>=? (compare x2 x4) x2 x4)
                   (if>=? (compare x3 x4) x3 x4))))
    ((compare x1 x2 . x3+)
     (let max ((xmax (if>=? (compare x1 x2) x1 x2)) (xs x3+))
       (if (null? xs)
           xmax
           (max (if>=? (compare xmax (car xs)) xmax (car xs))
                (cdr xs)))))))


; kth-largest

(define kth-largest
  (let ((= =) (< <))
    (case-lambda
      ((compare k x0)
       (case (modulo k 1)
         ((0)  (compare:checked x0 compare x0))
         (else (error "bad index" k))))
      ((compare k x0 x1)
       (case (modulo k 2)
         ((0) (if<=? (compare x0 x1) x0 x1))
         ((1) (if<=? (compare x0 x1) x1 x0))
         (else (error "bad index" k))))
      ((compare k x0 x1 x2)
       (case (modulo k 3)
         ((0) (if<=? (compare x0 x1)
                     (if<=? (compare x0 x2) x0 x2)
                     (if<=? (compare x1 x2) x1 x2)))
         ((1) (if3 (compare x0 x1)
                   (if<=? (compare x1 x2)
                          x1
                          (if<=? (compare x0 x2) x2 x0))
                   (if<=? (compare x0 x2) x1 x0)
                   (if<=? (compare x0 x2)
                          x0
                          (if<=? (compare x1 x2) x2 x1))))
         ((2) (if<=? (compare x0 x1)
                     (if<=? (compare x1 x2) x2 x1)
                     (if<=? (compare x0 x2) x2 x0)))
         (else (error "bad index" k))))
      ((compare k x0 . x1+) ; |x1+| >= 1
       (if (not (and (integer? k) (exact? k)))
           (error "bad index" k))
       (let ((n (+ 1 (length x1+))))
         (let kth ((k   (modulo k n))
                   (n   n)  ; = |x|
                   (rev #t) ; are x<, x=, x> reversed?
                   (x   (cons x0 x1+)))
           (let ((pivot (list-ref x (random-integer n))))
             (let split ((x x) (x< '()) (n< 0) (x= '()) (n= 0) (x> '()) (n> 0))
               (if (null? x)
                   (cond
                     ((< k n<)
                      (kth k n< (not rev) x<))
                     ((< k (+ n< n=))
                      (if rev
                          (list-ref x= (- (- n= 1) (- k n<)))
                          (list-ref x= (- k n<))))
                     (else
                      (kth (- k (+ n< n=)) n> (not rev) x>)))
                   (if3 (compare (car x) pivot)
                        (split (cdr x) (cons (car x) x<) (+ n< 1) x= n= x> n>)
                        (split (cdr x) x< n< (cons (car x) x=) (+ n= 1) x> n>)
                        (split (cdr x) x< n< x= n= (cons (car x) x>) (+ n> 1))))))))))))


; compare functions from predicates

(define compare-by<
  (case-lambda
    ((lt)     (lambda (x y) (if (lt x y) -1 (if (lt y x)  1 0))))
    ((lt x y)               (if (lt x y) -1 (if (lt y x)  1 0)))))

(define compare-by>
  (case-lambda
    ((gt)     (lambda (x y) (if (gt x y) 1 (if (gt y x)  -1 0))))
    ((gt x y)               (if (gt x y) 1 (if (gt y x)  -1 0)))))

(define compare-by<=
  (case-lambda
    ((le)     (lambda (x y) (if (le x y) (if (le y x) 0 -1) 1)))
    ((le x y)               (if (le x y) (if (le y x) 0 -1) 1))))

(define compare-by>=
  (case-lambda
    ((ge)     (lambda (x y) (if (ge x y) (if (ge y x) 0 1) -1)))
    ((ge x y)               (if (ge x y) (if (ge y x) 0 1) -1))))

(define compare-by=/<
  (case-lambda
    ((eq lt)     (lambda (x y) (if (eq x y) 0 (if (lt x y) -1 1))))
    ((eq lt x y)               (if (eq x y) 0 (if (lt x y) -1 1)))))

(define compare-by=/>
  (case-lambda
    ((eq gt)     (lambda (x y) (if (eq x y) 0 (if (gt x y) 1 -1))))
    ((eq gt x y)               (if (eq x y) 0 (if (gt x y) 1 -1)))))

; refine and extend construction

(define-syntax refine-compare
  (syntax-rules ()
    ((refine-compare)
     0)
    ((refine-compare c1)
     c1)
    ((refine-compare c1 c2 cs ...)
     (if3 c1 -1 (refine-compare c2 cs ...) 1))))

(define-syntax select-compare
  (syntax-rules (else)
    ((select-compare x y clause ...)
     (let ((x-val x) (y-val y))
       (select-compare (x-val y-val clause ...))))
    ; used internally: (select-compare (x y clause ...))
    ((select-compare (x y))
     0)
    ((select-compare (x y (else c ...)))
     (refine-compare c ...))
    ((select-compare (x y (t? c ...) clause ...))
     (let ((t?-val t?))
       (let ((tx (t?-val x)) (ty (t?-val y)))
         (if tx
             (if ty (refine-compare c ...) -1)
             (if ty 1 (select-compare (x y clause ...)))))))))

(define-syntax cond-compare
  (syntax-rules (else)
    ((cond-compare)
     0)
    ((cond-compare (else cs ...))
     (refine-compare cs ...))
    ((cond-compare ((tx ty) cs ...) clause ...)
     (let ((tx-val tx) (ty-val ty))
       (if tx-val
           (if ty-val (refine-compare cs ...) -1)
           (if ty-val 1 (cond-compare clause ...)))))))


; R5RS atomic types

(define-syntax compare:type-check
  (syntax-rules ()
    ((compare:type-check type? type-name x)
     (if (not (type? x))
         (error (string-append "not " type-name ":") x)))
    ((compare:type-check type? type-name x y)
     (begin (compare:type-check type? type-name x)
            (compare:type-check type? type-name y)))))

(define-syntax compare:define-by=/<
  (syntax-rules ()
    ((compare:define-by=/< compare = < type? type-name)
     (define compare
       (let ((= =) (< <))
         (lambda (x y)
           (if (type? x)
               (if (eq? x y)
                   0
                   (if (type? y)
                       (if (= x y) 0 (if (< x y) -1 1))
                       (error (string-append "not " type-name ":") y)))
               (error (string-append "not " type-name ":") x))))))))

(define (boolean-compare x y)
  (compare:type-check boolean? "boolean" x y)
  (if x (if y 0 1) (if y -1 0)))

(compare:define-by=/< char-compare char=? char<? char? "char")

(compare:define-by=/< char-compare-ci char-ci=? char-ci<? char? "char")

(compare:define-by=/< string-compare string=? string<? string? "string")

(compare:define-by=/< string-compare-ci string-ci=? string-ci<? string? "string")

(define (symbol-compare x y)
  (compare:type-check symbol? "symbol" x y)
  (string-compare (symbol->string x) (symbol->string y)))

(compare:define-by=/< integer-compare = < integer? "integer")

(compare:define-by=/< rational-compare = < rational? "rational")

(compare:define-by=/< real-compare = < real? "real")

(define (complex-compare x y)
  (compare:type-check complex? "complex" x y)
  (if (and (real? x) (real? y))
      (real-compare x y)
      (refine-compare (real-compare (real-part x) (real-part y))
                      (real-compare (imag-part x) (imag-part y)))))

(define (number-compare x y)
  (compare:type-check number? "number" x y)
  (complex-compare x y))


; R5RS compound data structures: dotted pair, list, vector

(define (pair-compare-car compare)
  (lambda (x y)
    (compare (car x) (car y))))

(define (pair-compare-cdr compare)
  (lambda (x y)
    (compare (cdr x) (cdr y))))

(define pair-compare
  (case-lambda
    
    ; dotted pair
    ((pair-compare-car pair-compare-cdr x y)
     (refine-compare (pair-compare-car (car x) (car y))
                     (pair-compare-cdr (cdr x) (cdr y))))
    
    ; possibly improper lists
    ((compare x y)
     (cond-compare 
      (((null? x) (null? y)) 0)
      (((pair? x) (pair? y)) (compare              (car x) (car y))
                             (pair-compare compare (cdr x) (cdr y)))
      (else                  (compare x y))))
    
    ; for convenience
    ((x y)
     (pair-compare default-compare x y))))

(define list-compare
  (case-lambda
    ((compare x y empty? head tail)
     (cond-compare
      (((empty? x) (empty? y)) 0)
      (else (compare              (head x) (head y))
            (list-compare compare (tail x) (tail y) empty? head tail))))
    
    ; for convenience
    ((        x y empty? head tail)
     (list-compare default-compare x y empty? head tail))
    ((compare x y              )
     (list-compare compare         x y null? car   cdr))
    ((        x y              )
     (list-compare default-compare x y null? car   cdr))))

(define list-compare-as-vector
  (case-lambda
    ((compare x y empty? head tail)
     (refine-compare
      (let compare-length ((x x) (y y))
        (cond-compare
         (((empty? x) (empty? y)) 0)
         (else (compare-length (tail x) (tail y)))))
      (list-compare compare x y empty? head tail)))
    
    ; for convenience
    ((        x y empty? head tail)
     (list-compare-as-vector default-compare x y empty? head tail))
    ((compare x y              )
     (list-compare-as-vector compare         x y null?  car  cdr))
    ((        x y              )
     (list-compare-as-vector default-compare x y null?  car  cdr))))

(define vector-compare
  (let ((= =))
    (case-lambda
      ((compare x y size ref)
       (let ((n (size x)) (m (size y)))
         (refine-compare 
          (integer-compare n m)
          (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
            (if (= i n)
                0
                (refine-compare (compare (ref x i) (ref y i))
                                (compare-rest (+ i 1))))))))
      
      ; for convenience
      ((        x y size ref)
       (vector-compare default-compare x y size          ref))
      ((compare x y           )
       (vector-compare compare         x y vector-length vector-ref))
      ((        x y           )
       (vector-compare default-compare x y vector-length vector-ref)))))

(define vector-compare-as-list
  (let ((= =))
    (case-lambda
      ((compare x y size ref)
       (let ((nx (size x)) (ny (size y)))
         (let ((n (min nx ny)))
           (let compare-rest ((i 0)) ; compare x[i..n-1] y[i..n-1]
             (if (= i n)
                 (integer-compare nx ny)
                 (refine-compare (compare (ref x i) (ref y i))
                                 (compare-rest (+ i 1))))))))
      
      ; for convenience
      ((        x y size ref)
       (vector-compare-as-list default-compare x y size          ref))
      ((compare x y           )
       (vector-compare-as-list compare         x y vector-length vector-ref))
      ((        x y           )
       (vector-compare-as-list default-compare x y vector-length vector-ref)))))


; default compare

(define (default-compare x y)
  (select-compare 
   x y
   (null?    0)
   (pair?    (default-compare (car x) (car y))
             (default-compare (cdr x) (cdr y)))
   (boolean? (boolean-compare x y))
   (char?    (char-compare    x y))
   (string?  (string-compare  x y))
   (symbol?  (symbol-compare  x y))
   (number?  (number-compare  x y))
   (vector?  (vector-compare default-compare x y))
   (else (error "unrecognized type in default-compare" x y))))

; Note that we pass default-compare to compare-{pair,vector} explictly.
; This makes sure recursion proceeds with this default-compare, which 
; need not be the one in the lexical scope of compare-{pair,vector}.


; debug compare

(define (debug-compare c)
  
  (define (checked-value c x y)
    (let ((c-xy (c x y)))
      (if (or (eqv? c-xy -1) (eqv? c-xy 0) (eqv? c-xy 1))
          c-xy
          (error "compare value not in {-1,0,1}" c-xy (list c x y)))))
  
  (define (random-boolean)
    (zero? (random-integer 2)))
  
  (define q ; (u v w) such that u <= v, v <= w, and not u <= w
    '#(
       ;x < y   x = y   x > y   [x < z]
       0       0       0    ; y < z
               0    (z y x) (z y x) ; y = z
               0    (z y x) (z y x) ; y > z
               
               ;x < y   x = y   x > y   [x = z]
               (y z x) (z x y)    0    ; y < z
               (y z x)    0    (x z y) ; y = z
               0    (y x z) (x z y) ; y > z
               
               ;x < y   x = y   x > y   [x > z]
               (x y z) (x y z)    0    ; y < z
               (x y z) (x y z)    0    ; y = z
               0       0       0    ; y > z
               ))
  
  (let ((z? #f) (z #f)) ; stored element from previous call
    (lambda (x y)
      (let ((c-xx (checked-value c x x))
            (c-yy (checked-value c y y))
            (c-xy (checked-value c x y))
            (c-yx (checked-value c y x)))
        (if (not (zero? c-xx))
            (error "compare error: not reflexive" c x))
        (if (not (zero? c-yy))
            (error "compare error: not reflexive" c y))
        (if (not (zero? (+ c-xy c-yx)))
            (error "compare error: not anti-symmetric" c x y))
        (if z?
            (let ((c-xz (checked-value c x z))
                  (c-zx (checked-value c z x))
                  (c-yz (checked-value c y z))
                  (c-zy (checked-value c z y)))
              (if (not (zero? (+ c-xz c-zx)))
                  (error "compare error: not anti-symmetric" c x z))
              (if (not (zero? (+ c-yz c-zy)))
                  (error "compare error: not anti-symmetric" c y z))
              (let ((ijk (vector-ref q (+ c-xy (* 3 c-yz) (* 9 c-xz) 13))))
                (if (list? ijk)
                    (apply error
                           "compare error: not transitive"
                           c 
                           (map (lambda (i) (case i ((x) x) ((y) y) ((z) z)))
                                ijk)))))
            (set! z? #t))
        (set! z (if (random-boolean) x y)) ; randomized testing
        c-xy))))

Added srfi/s69/basic-hash-tables.sls.



















































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!r6rs
;; Copyright (C) 2009 Andreas Rottmann. All rights reserved. Licensed
;; under an MIT-style license. See the file LICENSE in the original
;; collection this file is distributed with.

(library (srfi s69 basic-hash-tables)
  (export
    ;; Type constructors and predicate
    make-hash-table hash-table? alist->hash-table

    ;; Reflective queries
    hash-table-equivalence-function hash-table-hash-function

    ;; Dealing with single elements
    hash-table-ref hash-table-ref/default hash-table-set!
    hash-table-delete! hash-table-exists?
    hash-table-update! hash-table-update!/default

    ;; Dealing with the whole contents
    hash-table-size hash-table-keys hash-table-values hash-table-walk
    hash-table-fold hash-table->alist hash-table-copy hash-table-merge!

    ;; Hashing
    hash string-hash string-ci-hash hash-by-identity)
  (import
    (rename (rnrs)
            (string-hash rnrs:string-hash)
            (string-ci-hash rnrs:string-ci-hash)))

(define make-hash-table
  (case-lambda
    ((eql? hash)
     (make-hashtable hash eql?))
    ((eql?)
     (cond ((eq? eql? eq?)
            (make-eq-hashtable))
           ((eq? eql? eqv?)
            (make-eqv-hashtable))
           ((eq? eql? equal?)
            (make-hashtable equal-hash eql?))
           ((eq? eql? string=?)
            (make-hashtable rnrs:string-hash eql?))
           ((eq? eql? string-ci=?)
            (make-hashtable rnrs:string-ci-hash eql?))
           (else
            (assertion-violation 'make-hash-table
             "unrecognized equivalence predicate" eql?))))
    (()
     (make-hashtable equal-hash equal?))))

(define hash-table? hashtable?)

(define not-there (list 'not-there))

(define (alist->hash-table alist . args)
  (let ((table (apply make-hash-table args)))
    (for-each (lambda (entry)
                (hashtable-update! table
                                   (car entry)
                                   (lambda (x)
                                     (if (eq? x not-there) (cdr entry) x))
                                   not-there))
              alist)
    table))

(define hash-table-equivalence-function hashtable-equivalence-function)
(define hash-table-hash-function hashtable-hash-function)

(define (failure-thunk who key)
  (lambda ()
    (assertion-violation who "no association for key" key)))

(define hash-table-ref
  (case-lambda
    ((table key thunk)
     (let ((val (hashtable-ref table key not-there)))
       (if (eq? val not-there)
           (thunk)
           val)))
    ((table key)
     (hash-table-ref table key (failure-thunk 'hash-table-ref key)))))

(define hash-table-ref/default hashtable-ref)
(define hash-table-set! hashtable-set!)
(define hash-table-delete! hashtable-delete!)
(define hash-table-exists? hashtable-contains?)

(define hash-table-update!
  (case-lambda
    ((table key proc thunk)
     (hashtable-update! table
                        key
                        (lambda (val)
                          (if (eq? val not-there)
                              (thunk)
                              (proc val)))
                        not-there))
    ((table key proc)
     (hash-table-update! table key proc (failure-thunk 'hash-table-update! key)))))

(define hash-table-update!/default hashtable-update!)

(define hash-table-size hashtable-size)

(define (hash-table-keys table)
  (vector->list (hashtable-keys table)))

(define (hash-table-values table)
  (let-values (((keys values) (hashtable-entries table)))
    (vector->list values)))

(define (hash-table-walk table proc)
  (let-values (((keys values) (hashtable-entries table)))
    (vector-for-each proc keys values)))

(define (hash-table-fold table kons knil)
  (let-values (((keys values) (hashtable-entries table)))
    (let ((size (vector-length keys)))
      (let loop ((i 0)
                 (val knil))
        (if (>= i size)
            val
            (loop (+ i 1)
                  (kons (vector-ref keys i) (vector-ref values i) val)))))))

(define (hash-table->alist table)
  (hash-table-fold table
                   (lambda (k v l)
                     (cons (cons k v) l))
                   '()))

(define hash-table-copy hashtable-copy)

(define (hash-table-merge! table1 table2)
  (hash-table-walk table2 (lambda (k v)
                            (hashtable-set! table1 k v)))
  table1)

(define (make-hasher hash-proc)
  (case-lambda
    ((obj)
     ;; R6RS doesn't guarantee that the result of the hash procedure
     ;; is non-negative, so we use mod.
     (mod (hash-proc obj) (greatest-fixnum)))
    ((obj bound)
     (mod (hash-proc obj) bound))))

(define hash (make-hasher equal-hash))
(define hash-by-identity (make-hasher equal-hash))  ;; Very slow.
(define string-hash (make-hasher rnrs:string-hash))
(define string-ci-hash (make-hasher rnrs:string-ci-hash))

)

Added srfi/s78/check.scm.





















































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
; <PLAINTEXT>
; Copyright (c) 2005-2006 Sebastian Egner.
; 
; Permission is hereby granted, free of charge, to any person obtaining
; a copy of this software and associated documentation files (the
; ``Software''), to deal in the Software without restriction, including
; without limitation the rights to use, copy, modify, merge, publish,
; distribute, sublicense, and/or sell copies of the Software, and to
; permit persons to whom the Software is furnished to do so, subject to
; the following conditions:
; 
; The above copyright notice and this permission notice shall be
; included in all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; 
; -----------------------------------------------------------------------
; Modified by Derick Eddington to print things a little differently.
; 
; Lightweight testing (reference implementation)
; ==============================================
;
; Sebastian.Egner@philips.com
; in R5RS + SRFI 23 (error) + SRFI 42 (comprehensions)
;
; history of this file:
;   SE, 25-Oct-2004: first version based on code used in SRFIs 42 and 67
;   SE, 19-Jan-2006: (arg ...) made optional in check-ec
;
; Naming convention "check:<identifier>" is used only internally.

; -- portability --

; PLT:      (require (lib "23.ss" "srfi") (lib "42.ss" "srfi"))
; Scheme48: ,open srfi-23 srfi-42 

; -- utilities --

#;(define check:write write)

(define (print/header/padded x header padding)
  (define (print/lines)
    (let* ((str (call-with-string-output-port
                  (lambda (sop) (check:write x sop))))
           (sip (open-string-input-port str)))
      (let loop ((lines '()))
        (let ((l (get-line sip)))
          (if (eof-object? l)
            (reverse lines)
            (loop (cons l lines)))))))
  (let ((lines (print/lines)))
    (display header)
    (display (car lines))
    (let loop ((lines (cdr lines)))
      (unless (null? lines)
        (newline)
        (display padding)
        (display (car lines))
        (loop (cdr lines))))))

; You can also use a pretty printer if you have one.
; However, the output might not improve for most cases
; because the pretty printers usually output a trailing
; newline.

; PLT:      (require (lib "pretty.ss")) (define check:write pretty-print)
; Scheme48: ,open pp (define check:write p)

; -- mode --

(define check:mode 
  (make-parameter 'report
                  (lambda (v) (case v
                                ((off)           0)
                                ((summary)       1)
                                ((report-failed) 10)
                                ((report)        100)
                                (else (error "unrecognized mode" v))))))

(define (check-set-mode! mode)
  (check:mode mode))

; -- state --

(define check:correct 0)
(define check:failed '())

(define (check-reset!)
  (set! check:correct 0)
  (set! check:failed   '()))

(define (check:add-correct!)
  (set! check:correct (+ check:correct 1)))

(define (check:add-failed! expression actual-result expected-result pred)
  (set! check:failed
        (cons (list expression actual-result expected-result pred)
              check:failed)))

; -- reporting --

(define (check:report-expression expression pred)
  (newline)
  (check:write expression)
  (if pred
    (begin (print/header/padded pred "(=> " "    ")
           (display ")\n"))
    (display "=>\n")))

(define (check:report-actual-result actual-result)
  (check:write actual-result))

(define (check:report-correct cases)
  (display "; correct")
  (if (not (= cases 1))
      (begin (display " (")
             (display cases)
             (display " cases checked)")))
  (newline))

(define (check:report-failed expected-result)
  (display "; *** failed ***\n")
  (print/header/padded expected-result "; expected result: "
                                       ";                  ")
  (newline))

(define (check-report)
  (if (>= (check:mode) 1)
      (begin
        (newline)
        (display "; *** checks *** : ")
        (display check:correct)
        (display " correct, ")
        (display (length check:failed))
        (display " failed.")
        (if (or (null? check:failed) (<= (check:mode) 1))
            (newline)
            (let* ((w (car (reverse check:failed)))
                   (expression (car w))
                   (actual-result (cadr w))
                   (expected-result (caddr w))
                   (pred (cadddr w)))                  
              (display " First failed example:")
              (newline)
              (check:report-expression expression pred)
              (check:report-actual-result actual-result)
              (check:report-failed expected-result))))))

(define (check-passed? expected-total-count)
  (and (= (length check:failed) 0)
       (= check:correct expected-total-count)))
       
; -- simple checks --

(define (check:proc expression thunk equal equal-expr expected-result)
  (case (check:mode)
    ((0) #f)
    ((1)
     (let ((actual-result (thunk)))
       (if (equal actual-result expected-result)
           (check:add-correct!)
           (check:add-failed!
            expression actual-result expected-result equal-expr))))
    ((10)
     (let ((actual-result (thunk)))
       (if (equal actual-result expected-result)
           (check:add-correct!)
           (begin
             (check:report-expression expression equal-expr)
             (check:report-actual-result actual-result)
             (check:report-failed expected-result)
             (check:add-failed!
              expression actual-result expected-result equal-expr)))))
    ((100)
     (check:report-expression expression equal-expr)
     (let ((actual-result (thunk)))
       (check:report-actual-result actual-result)
       (if (equal actual-result expected-result)
           (begin (check:report-correct 1)
                  (check:add-correct!))
           (begin (check:report-failed expected-result)
                  (check:add-failed!
                   expression actual-result expected-result equal-expr)))))
    (else (error "unrecognized check:mode" (check:mode))))
  (if #f #f))

(define-syntax check
  (syntax-rules (=>)
    ((check expr => expected)
     (if (>= (check:mode) 1)
	 (check:proc 'expr (lambda () expr) equal? #F expected)))
    ((check expr (=> equal) expected)
     (if (>= (check:mode) 1)
	 (check:proc 'expr (lambda () expr) equal 'equal expected)))))

; -- parametric checks --

(define (check:proc-ec w)
  (let ((correct? (car w))
        (expression (cadr w))
        (actual-result (caddr w))
        (expected-result (cadddr w))
	(cases (car (cddddr w)))
        (equal-expr (cadr (cddddr w))))
    (if correct?
        (begin (if (>= (check:mode) 100)
                   (begin (check:report-expression expression equal-expr)
                          (check:report-actual-result actual-result)
                          (check:report-correct cases)))
               (check:add-correct!))
        (begin (if (>= (check:mode) 10)
                   (begin (check:report-expression expression equal-expr)
                          (check:report-actual-result actual-result)
                          (check:report-failed expected-result)))
               (check:add-failed!
                expression actual-result expected-result equal-expr)))))

(define-syntax check-ec:make
  (syntax-rules (=>)
    ((check-ec:make qualifiers expr (=> equal) expected (arg ...))
     (if (>= (check:mode) 1)
         (check:proc-ec
	  (let ((cases 0))
	    (let ((w (first-ec 
		      #f
		      qualifiers
		      (:let equal-pred equal)
		      (:let expected-result expected)
		      (:let actual-result
                            (let ((arg arg) ...) ; (*)
                              expr))
		      (begin (set! cases (+ cases 1)))
		      (if (not (equal-pred actual-result expected-result)))
		      (list (list 'let (list (list 'arg arg) ...) 'expr)
			    actual-result
			    expected-result
			    cases
                            'equal))))
	      (if w
		  (cons #f w)
		  (list #t 
			'(check-ec qualifiers 
				   expr (=> equal) 
				   expected (arg ...))
			(if #f #f)
		        (if #f #f)
			cases
                        'equal)))))))))

; (*) is a compile-time check that (arg ...) is a list
; of pairwise disjoint bound variables at this point.

(define-syntax check-ec
  (syntax-rules (nested =>)
    ((check-ec expr => expected)
     (check-ec:make (nested) expr (=> equal?) expected ()))
    ((check-ec expr (=> equal) expected)
     (check-ec:make (nested) expr (=> equal) expected ()))
    ((check-ec expr => expected (arg ...))
     (check-ec:make (nested) expr (=> equal?) expected (arg ...)))
    ((check-ec expr (=> equal) expected (arg ...))
     (check-ec:make (nested) expr (=> equal) expected (arg ...)))

    ((check-ec qualifiers expr => expected)
     (check-ec:make qualifiers expr (=> equal?) expected ()))
    ((check-ec qualifiers expr (=> equal) expected)
     (check-ec:make qualifiers expr (=> equal) expected ()))
    ((check-ec qualifiers expr => expected (arg ...))
     (check-ec:make qualifiers expr (=> equal?) expected (arg ...)))
    ((check-ec qualifiers expr (=> equal) expected (arg ...))
     (check-ec:make qualifiers expr (=> equal) expected (arg ...)))

    ((check-ec (nested q1 ...) q etc ...)
     (check-ec (nested q1 ... q) etc ...))
    ((check-ec q1 q2             etc ...)
     (check-ec (nested q1 q2)    etc ...))))

Added srfi/s78/lightweight-testing.sls.



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s78 lightweight-testing)
  (export
    check
    check-ec
    check-report
    check-set-mode!
    check-reset!
    check-passed?)
  (import 
    (rnrs)
    (srfi s78 lightweight-testing compat)
    (srfi s39 parameters)
    (srfi private include)
    (srfi s23 error tricks)
    (srfi s42 eager-comprehensions))
  
  ;; (SRFI-23-error->R6RS "(library (srfi s78 lightweight-testing))"
  ;;  (include/resolve ("srfi" "%3a78") "check.scm"))

  (SRFI-23-error->R6RS "(library (srfi s78 lightweight-testing))"
   (include/resolve ("srfi" "s78") "check.scm"))
)

Added srfi/s78/lightweight-testing/compat.chezscheme.sls.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s78 lightweight-testing compat)
  (export (rename (pretty-print check:write)))
  (import (chezscheme)))

Added srfi/s78/lightweight-testing/compat.ikarus.sls.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s78 lightweight-testing compat)
  (export
    (rename (pretty-print check:write)))
  (import
    (only (ikarus) pretty-print))
)

Added srfi/s78/lightweight-testing/compat.larceny.sls.

























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s78 lightweight-testing compat)
  (export
    (rename (pretty-print check:write)))
  (import
    (primitives pretty-print))
)

Added srfi/s78/lightweight-testing/compat.mosh.sls.



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
;; Copyright (c) 2009 Derick Eddington
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; Except as contained in this notice, the name(s) of the above copyright
;; holders shall not be used in advertising or otherwise to promote the sale,
;; use or other dealings in this Software without prior written authorization.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.

(library (srfi s78 lightweight-testing compat)
  (export
   (rename (pretty-print check:write)))
  (import
   (only (mosh pp) pretty-print)))

Added srfi/s78/lightweight-testing/compat.mzscheme.sls.



























>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s78 lightweight-testing compat)
  (export
    (rename (pretty-print check:write)))
  (import
    (only (scheme pretty) pretty-print))
)

Added srfi/s78/lightweight-testing/compat.ypsilon.sls.









































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

(library (srfi s78 lightweight-testing compat)
  (export
    check:write)
  (import
    (rnrs)
    (only (core) pretty-print))

  (define check:write
    (case-lambda
      ((x) (check:write x (current-output-port)))
      ((x p)
       (pretty-print x p)
       (newline p))))
)

Added srfi/s8/receive.sls.







































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s8 receive)
  (export receive)
  (import (rnrs))
  
  (define-syntax receive
    (syntax-rules ()
      [(_ formals expression b b* ...)
       (call-with-values 
         (lambda () expression)
         (lambda formals b b* ...))]))
  
)

Added srfi/s9/records.sls.







































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

#!r6rs
(library (srfi s9 records)
  (export 
    (rename (srfi:define-record-type define-record-type)))
  (import 
    (rnrs))
  
  (define-syntax srfi:define-record-type
    (lambda (stx)
      (syntax-case stx ()
        [(_ type (constructor constructor-tag ...)
                 predicate
                 (field-tag accessor setter ...) ...)         
         (and (for-all identifier? 
                       #'(type constructor predicate constructor-tag ... 
                               field-tag ... accessor ...))
              (for-all (lambda (s) 
                         (or (and (= 1 (length s)) (identifier? (car s)))
                             (= 0 (length s))))
                       #'((setter ...) ...))
              (for-all (lambda (ct) 
                         (memp (lambda (ft) (bound-identifier=? ct ft))
                               #'(field-tag ...)))
                       #'(constructor-tag ...)))         
         (with-syntax ([(field-clause ...)
                        (map (lambda (clause)
                               (if (= 2 (length clause)) 
                                 #`(immutable . #,clause) 
                                 #`(mutable . #,clause)))
                             #'((field-tag accessor setter ...) ...))]
                       [(unspec-tag ...)
                        (remp (lambda (ft) 
                                (memp (lambda (ct) (bound-identifier=? ft ct))
                                      #'(constructor-tag ...)))
                              #'(field-tag ...))])
           #'(define-record-type (type constructor predicate)
               (sealed #t)
               (protocol (lambda (ctor)
                           (lambda (constructor-tag ...)
                             (define unspec-tag)
                             ...
                             (ctor field-tag ...))))
               (fields field-clause ...)))])))
  
)

Added srfi/s98/os-environment-variables.chezscheme.sls.











>
>
>
>
>
1
2
3
4
5
(library (srfi s98 os-environment-variables)
  (export (rename (getenv get-environment-variable)
                  ;;(... get-environment-variables)
                  ))
  (import (chezscheme)))

Added srfi/s98/os-environment-variables.ikarus.sls.













>
>
>
>
>
>
1
2
3
4
5
6
(library (srfi s98 os-environment-variables)
  (export
    (rename (getenv get-environment-variable)
            (environ get-environment-variables)))
  (import
    (only (ikarus) getenv environ)))

Added srfi/s98/os-environment-variables.larceny.sls.



































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

;; NOTE: I believe this currently works only on Linux.
;; NOTE: If Larceny's FFI changes, this may no longer work.

(library (srfi s98 os-environment-variables)
  (export
    get-environment-variable get-environment-variables)
  (import
    (rnrs base)
    (rnrs control)
    (rnrs bytevectors)
    (rnrs io ports)
    (primitives
     foreign-procedure #;foreign-variable foreign-null-pointer? sizeof:pointer
     %peek-pointer %peek8u void*->address ffi/dlopen ffi/dlsym)
    (srfi private feature-cond))

  ;; TODO: Will the convenient string converters use the native transcoder in
  ;;       the future?  So that scheme-str->c-str-bv and c-str-ptr->scheme-str
  ;;       won't be needed.

  (define (scheme-str->c-str-bv x)
    (let* ((bv (string->bytevector x (native-transcoder)))
           (len (bytevector-length bv))
           (bv/z (make-bytevector (+ 1 len))))
      (bytevector-copy! bv 0 bv/z 0 len)
      (bytevector-u8-set! bv/z len 0)
      bv/z))

  (define (c-str-ptr->scheme-str x)
    (let loop ((x x) (a '()))
      (let ((b (%peek8u x)))
        (if (zero? b)
          (bytevector->string (u8-list->bytevector (reverse a))
                              (native-transcoder))
          (loop (+ 1 x) (cons b a))))))
  
  (define getenv
    (foreign-procedure "getenv" '(boxed) 'void*))
  
  (define (get-environment-variable name) 
    (unless (string? name)
      (assertion-violation 'get-environment-variable "not a string" name))
    (let ((p (getenv (scheme-str->c-str-bv name))))
      (and p
           (c-str-ptr->scheme-str (void*->address p)))))

  ;; TODO: Will foreign-variable support a pointer type in the future?
  ;;       Would this be the correct way to use it?
  #;(define environ
      (foreign-variable "environ" 'void*))

  ;; TODO: Is (ffi/dlopen "") okay?  It works for me on Ubuntu Linux 8.10.
  (define environ
    (feature-cond
     (linux
      (%peek-pointer (ffi/dlsym (ffi/dlopen "") "environ")))))

  (define (get-environment-variables)
    (define (entry->pair x) 
      (let* ((s (c-str-ptr->scheme-str x))
             (len (string-length s)))
        (let loop ((i 0))
          (if (< i len)
            (if (char=? #\= (string-ref s i))
              (cons (substring s 0 i)
                    (substring s (+ 1 i) len))
              (loop (+ 1 i)))
            (cons s #F)))))
    (let loop ((e environ) (a '()))
      (let ((entry (%peek-pointer e)))
        (if (foreign-null-pointer? entry)
          a
          (loop (+ sizeof:pointer e)
                (cons (entry->pair entry) a))))))
)

Added srfi/s98/os-environment-variables.mzscheme.sls.











































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.
;; Licensed under an MIT-style license.  My license is in the file
;; named LICENSE from the original collection this file is distributed
;; with.  If this file is redistributed with some other collection, my
;; license must also be included.

;; Inspired by Danny Yoo's get-environment PLaneT package.

#!r6rs
(library (srfi s98 os-environment-variables)
  (export
    (rename (getenv get-environment-variable))
    get-environment-variables)
  (import
    (rnrs base)
    (only (scheme base) getenv)
    (scheme foreign))

  (unsafe!)

  (define environ (get-ffi-obj "environ" (ffi-lib #F) _pointer))

  (define (get-environment-variables)
    (let loop ((i 0) (accum '()))
      (let ((next (ptr-ref environ _string/locale i)))
        (if next
          (loop (+ 1 i)
                (cons (let loop ((i 0) (len (string-length next)))
                        (if (< i len)
                          (if (char=? #\= (string-ref next i))
                            (cons (substring next 0 i)
                                  (substring next (+ 1 i) len))
                            (loop (+ 1 i) len))
                          (cons next #F)))
                      accum))
          accum))))
)

Added srfi/s98/os-environment-variables.ypsilon.sls.













>
>
>
>
>
>
1
2
3
4
5
6
(library (srfi s98 os-environment-variables)
  (export
    (rename (lookup-process-environment get-environment-variable)
            (process-environment->alist get-environment-variables)))
  (import
    (only (core) lookup-process-environment process-environment->alist)))

Added srfi/s99/records.sls.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
(library (srfi s99 records)
  (export
   make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator
   record? record-rtd rtd-name rtd-parent
   rtd-field-names rtd-all-field-names rtd-field-mutable?
   define-record-type)
  (import (srfi s99 records procedural)
          (srfi s99 records inspection)
          (srfi s99 records syntactic)))

Added srfi/s99/records/helper.sls.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
;; Copyright (C) William D Clinger 2008. All Rights Reserved.
;;
;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the "Software"), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO
;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

; This library breaks a circular interdependence between the
; procedural and inspection libraries.

#!r6rs
(library (srfi s99 records helper)
  (export
    rtd?)
  (import
    (rnrs base)
    (rnrs records procedural))

  (define rtd? record-type-descriptor?)
)

Added srfi/s99/records/inspection.larceny.sls.











>
>
>
>
>
1
2
3
4
5
(library (srfi s99 records inspection)
  (export
   record? record-rtd rtd-name rtd-parent
   rtd-field-names rtd-all-field-names rtd-field-mutable?)
  (import (err5rs records inspection)))

Added srfi/s99/records/inspection.sls.





































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
;; Copyright (C) William D Clinger 2008. All Rights Reserved.
;;
;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the "Software"), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO
;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#!r6rs
(library (srfi s99 records inspection)

  (export record? record-rtd
          rtd-name rtd-parent
          rtd-field-names rtd-all-field-names rtd-field-mutable?)

  (import (rnrs base)
          (rnrs lists)
          (rnrs records inspection)
          (srfi s99 records helper))

  ; The record? predicate is already defined by (rnrs records inspection).

  ; The record-rtd procedure is already defined by (rnrs records inspection).

  (define rtd-name record-type-name)

  (define rtd-parent record-type-parent)

  (define rtd-field-names record-type-field-names)

  (define (rtd-all-field-names rtd)
    (define (loop rtd othernames)
      (let ((parent (rtd-parent rtd))
            (names (append (vector->list
                            (rtd-field-names rtd))
                           othernames)))
        (if parent
            (loop parent names)
            (list->vector names))))
    (loop rtd '()))

  (define (rtd-field-mutable? rtd0 fieldname)
    (define (loop rtd)
      (if (rtd? rtd)
          (let* ((names (vector->list (rtd-field-names rtd)))
                 (probe (memq fieldname names)))
            (if probe
                (record-field-mutable? rtd (- (length names) (length probe)))
                (loop (rtd-parent rtd))))
          (assertion-violation 'rtd-field-mutable?
                               "illegal argument" rtd0 fieldname)))
    (loop rtd0))

)

Added srfi/s99/records/procedural.larceny.sls.









>
>
>
>
1
2
3
4
(library (srfi s99 records procedural)
  (export
   make-rtd rtd? rtd-constructor rtd-predicate rtd-accessor rtd-mutator)
  (import (err5rs records procedural)))

Added srfi/s99/records/procedural.sls.

















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
;; Copyright (C) William D Clinger 2008. All Rights Reserved.
;;
;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the "Software"), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO
;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; ERR5RS Records.
;
; This is a quick-and-dirty reference implementation that favors
; simplicity over quality error messages and performance.  It is
; implemented using the R6RS procedural and inspection layers,
; with which it interoperates nicely.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#!r6rs
(library (srfi s99 records procedural)

  (export make-rtd rtd? rtd-constructor
          rtd-predicate rtd-accessor rtd-mutator)

  (import (rnrs base)
          (rnrs lists)
          (rnrs records procedural)
          (srfi s99 records inspection))

  ; Note: the options are permitted by ERR5RS,
  ; but are not part of ERR5RS.

  (define (make-rtd name fieldspecs . rest)
    (let* ((parent (if (null? rest) #f (car rest)))
           (options (if (null? rest) '() (cdr rest)))
           (sealed? (and (memq 'sealed options) #t))
           (opaque? (and (memq 'opaque options) #t))
           (uid (let ((probe (memq 'uid options)))
                  (if (and probe (not (null? (cdr probe))))
                      (cadr probe)
                      #f))))
      (make-record-type-descriptor
       name
       parent
       uid
       sealed?
       opaque?
       (vector-map (lambda (fieldspec)
                     (if (symbol? fieldspec)
                         (list 'mutable fieldspec)
                         fieldspec))
                   fieldspecs))))

  (define rtd? record-type-descriptor?)

  (define (rtd-constructor rtd . rest)

    ; Computes permutation and allocates permutation buffer
    ; when the constructor is created, not when the constructor
    ; is called.  More error checking is recommended.

    (define (make-constructor fieldspecs allnames maker)
      (let* ((k (length fieldspecs))
             (n (length allnames))
             (buffer (make-vector n))
             (reverse-all-names (reverse allnames)))

        (define (position fieldname)
          (let ((names (memq fieldname reverse-all-names)))
            (assert names)
            (- (length names) 1)))

        (let ((indexes (map position fieldspecs)))

          ; The following can be made quite efficient by
          ; hand-coding it in some lower-level language,
          ; e.g. Larceny's mal.  Even case-lambda would
          ; be good enough in most systems.

          (lambda args
            (assert (= (length args) k))
            (for-each (lambda (arg posn)
                        (vector-set! buffer posn arg))
                      args indexes)
            (apply maker (vector->list buffer))))))

    (if (null? rest)
        (record-constructor
         (make-record-constructor-descriptor rtd #f #f))
        (begin (assert (null? (cdr rest)))
               (make-constructor
                (vector->list (car rest))
                (vector->list (rtd-all-field-names rtd))
                (record-constructor
                 (make-record-constructor-descriptor rtd #f #f))))))

  (define rtd-predicate record-predicate)

  (define (rtd-accessor rtd0 fieldname)
    (define (loop rtd)
      (if (rtd? rtd)
          (let* ((names (vector->list (rtd-field-names rtd)))
                 (probe (memq fieldname names)))
            (if probe
                (record-accessor rtd (- (length names) (length probe)))
                (loop (rtd-parent rtd))))
          (assertion-violation 'rtd-accessor
                               "illegal argument" rtd0 fieldname)))
    (loop rtd0))

  (define (rtd-mutator rtd0 fieldname)
    (define (loop rtd)
      (if (rtd? rtd)
          (let* ((names (vector->list (rtd-field-names rtd)))
                 (probe (memq fieldname names)))
            (if probe
                (record-mutator rtd (- (length names) (length probe)))
                (loop (rtd-parent rtd))))
          (assertion-violation 'rtd-mutator
                               "illegal argument" rtd0 fieldname)))
    (loop rtd0))

)

Added srfi/s99/records/syntactic.larceny.sls.







>
>
>
1
2
3
(library (srfi s99 records syntactic)
  (export define-record-type)
  (import (err5rs records syntactic)))

Added srfi/s99/records/syntactic.sls.















































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
;; Copyright (C) William D Clinger 2008. All Rights Reserved.
;;
;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the "Software"), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO
;; SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
;; CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#!r6rs
(library (srfi s99 records syntactic)

  (export define-record-type)

  (import (for (rnrs base) run expand)
          (for (rnrs lists) run expand)
          (for (rnrs syntax-case) run expand)
          (srfi s99 records procedural))

  (define-syntax define-record-type
    (syntax-rules ()
     ((_ (type-name parent) constructor-spec predicate-spec . field-specs)
      (define-record-type-helper0
       type-name parent constructor-spec predicate-spec . field-specs))
     ((_ type-name constructor-spec predicate-spec . field-specs)
      (define-record-type-helper0
       type-name #f constructor-spec predicate-spec . field-specs))))

  (define-syntax define-record-type-helper0
    (lambda (x)

      ; Given syntax objects, passes them to helper macro.

      (define (construct-record-type-definitions
               tname fields parent cspec pred afields mfields)
        (let ()

          (define (frob x)
            (cond ((identifier? x)
                   x)
                  ((pair? x)
                   (cons (frob (car x)) (frob (cdr x))))
                  ((vector? x)
                   (vector-map frob x))
                  ((symbol? x)
                   (datum->syntax tname x))
                  (else
                   x)))

          #`(#,(frob #'define-record-type-helper)
             #,(frob tname)
             #,(frob fields)
             #,(frob parent)
             #,(frob cspec)
             #,(frob pred)
             #,(frob afields)
             #,(frob mfields))))

      ; Given a syntax object that represents a non-empty list,
      ; returns the syntax object for its first element.

      (define (syntax-car x)
        (syntax-case x ()
         ((x0 x1 ...)
          #'x0)))

      ; Given a syntax object that represents a non-empty list,
      ; returns the syntax object obtained by omitting the first
      ; element of that list.

      (define (syntax-cdr x)
        (syntax-case x ()
         ((x0 x1 ...)
          #'(x1 ...))))

      ; Given a syntax object that represents a non-empty list,
      ; returns the corresponding list of syntax objects.

      (define (syntax->list x)
        (syntax-case x ()
         (()
          '())
         ((x0 . x1)
          (cons #'x0 (syntax->list #'x1)))))

      (define (complain)
        (syntax-violation 'define-record-type "illegal syntax" x))

      ; tname and pname are always identifiers here.

      (syntax-case x ()
       ((_ tname pname constructor-spec predicate-spec . field-specs)
        (let* ((type-name (syntax->datum #'tname))
               (cspec (syntax->datum #'constructor-spec))
               (pspec (syntax->datum #'predicate-spec))
               (fspecs (syntax->datum #'field-specs))
               (type-name-string
                (begin (if (not (symbol? type-name))
                           (complain))
                       (symbol->string type-name)))
               (constructor-name
                (cond ((eq? cspec #f)
                       #'constructor-spec)
                      ((eq? cspec #t)
                       (datum->syntax
                        #'tname
                        (string->symbol
                         (string-append "make-" type-name-string))))
                      ((symbol? cspec)
                       #'constructor-spec)
                      ((and (pair? cspec) (symbol? (car cspec)))
                       (syntax-car #'constructor-spec))
                      (else (complain))))
               (constructor-args
                (cond ((pair? cspec)
                       (if (not (for-all symbol? cspec))
                           (complain)
                           (list->vector
                            (syntax->list (syntax-cdr #'constructor-spec)))))
                      (else #f)))
               (new-constructor-spec
                (if constructor-args
                    (list constructor-name constructor-args)
                    constructor-name))
               (predicate-name
                (cond ((eq? pspec #f)
                       #'predicate-spec)
                      ((eq? pspec #t)
                       (datum->syntax
                        #'tname
                        (string->symbol
                         (string-append type-name-string "?"))))
                      ((symbol? pspec)
                       #'predicate-spec)
                      (else (complain))))
               (field-specs
                (map (lambda (fspec field-spec)
                       (cond ((symbol? fspec)
                              (list 'immutable
                                    fspec
                                    (string->symbol
                                     (string-append
                                      type-name-string
                                      "-"
                                      (symbol->string fspec)))))
                             ((not (pair? fspec))
                              (complain))
                             ((not (list? fspec))
                              (complain))
                             ((not (for-all symbol? fspec))
                              (complain))
                             ((null? (cdr fspec))
                              (list 'mutable
                                    (car fspec)
                                    (string->symbol
                                     (string-append
                                      type-name-string
                                      "-"
                                      (symbol->string (car fspec))))
                                    (string->symbol
                                     (string-append
                                      type-name-string
                                      "-"
                                      (symbol->string (car fspec))
                                      "-set!"))))
                             ((null? (cddr fspec))
                              (list 'immutable
                                    (car fspec)
                                    (syntax-car (syntax-cdr field-spec))))
                             ((null? (cdddr fspec))
                              (list 'mutable
                                    (car fspec)
                                    (syntax-car (syntax-cdr field-spec))
                                    (syntax-car (syntax-cdr
                                                 (syntax-cdr field-spec)))))
                             (else (complain))))
                     fspecs
                     (syntax->list #'field-specs)))

               (fields (list->vector (map cadr field-specs)))

               (accessor-fields
                (map (lambda (x) (list (caddr x) (cadr x)))
                     (filter (lambda (x) (>= (length x) 3))
                             field-specs)))

               (mutator-fields
                (map (lambda (x) (list (cadddr x) (cadr x)))
                     (filter (lambda (x) (= (length x) 4))
                             field-specs))))

          (construct-record-type-definitions
           #'tname
           fields
           #'pname
           new-constructor-spec
           predicate-name
           accessor-fields
           mutator-fields))))))

  (define-syntax define-record-type-helper
    (syntax-rules ()

     ((_ type-name fields parent #f predicate
         ((accessor field) ...) ((mutator mutable-field) ...))
      (define-record-type-helper
       type-name fields parent ignored predicate
       ((accessor field) ...) ((mutator mutable-field) ...)))

     ((_ type-name fields parent constructor #f
         ((accessor field) ...) ((mutator mutable-field) ...))
      (define-record-type-helper
       type-name fields parent constructor ignored
       ((accessor field) ...) ((mutator mutable-field) ...)))

     ((_ type-name fields parent (constructor args) predicate
         ((accessor field) ...) ((mutator mutable-field) ...))
      (begin (define type-name (make-rtd 'type-name 'fields parent))
             (define constructor (rtd-constructor type-name 'args))
             (define predicate (rtd-predicate type-name))
             (define accessor (rtd-accessor type-name 'field))
             ...
             (define mutator (rtd-mutator type-name 'mutable-field))
             ...))

     ((_ type-name fields parent constructor predicate
         ((accessor field) ...) ((mutator mutable-field) ...))
      (begin (define type-name (make-rtd 'type-name 'fields parent))
             (define constructor (rtd-constructor type-name))
             (define predicate (rtd-predicate type-name))
             (define accessor (rtd-accessor type-name 'field))
             ...
             (define mutator (rtd-mutator type-name 'mutable-field))
             ...))))

)

Added srfi/tests/and-let.sps.





































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.  Licensed under an
;; MIT-style license.  My license is in the file named LICENSE from the original
;; collection this file is distributed with.  If this file is redistributed with
;; some other collection, my license must also be included.

#!r6rs
(import 
  (rnrs)
  (rnrs eval)
  (surfage s2 and-let)
  (surfage s78 lightweight-testing))

(define-syntax expect
  (syntax-rules ()
    [(_ expr result)
     (check expr => result)]))

(define-syntax must-be-a-syntax-error
  (syntax-rules ()
    [(_ expr)
     (check 
       (guard (ex [#t (syntax-violation? ex)])  
         (eval 'expr (environment '(rnrs) '(surfage s2 and-let))))
       => #t)]))

;; Taken straight from the reference implementation tests

(expect  (and-let* () 1) 1)
(expect  (and-let* () 1 2) 2)
(expect  (and-let* () ) #t)

(expect (let ((x #f)) (and-let* (x))) #f)
(expect (let ((x 1)) (and-let* (x))) 1)
(expect (and-let* ((x #f)) ) #f)
(expect (and-let* ((x 1)) ) 1)
(must-be-a-syntax-error (and-let* ( #f (x 1))) )
(expect (and-let* ( (#f) (x 1)) ) #f)
(must-be-a-syntax-error (and-let* (2 (x 1))) )
(expect (and-let* ( (2) (x 1)) ) 1)
(expect (and-let* ( (x 1) (2)) ) 2)
(expect (let ((x #f)) (and-let* (x) x)) #f)
(expect (let ((x "")) (and-let* (x) x)) "")
(expect (let ((x "")) (and-let* (x)  )) "")
(expect (let ((x 1)) (and-let* (x) (+ x 1))) 2)
(expect (let ((x #f)) (and-let* (x) (+ x 1))) #f)
(expect (let ((x 1)) (and-let* (((positive? x))) (+ x 1))) 2)
(expect (let ((x 1)) (and-let* (((positive? x))) )) #t)
(expect (let ((x 0)) (and-let* (((positive? x))) (+ x 1))) #f)
(expect (let ((x 1)) (and-let* (((positive? x)) (x (+ x 1))) (+ x 1)))  3)
;; This next one is from the reference implementation tests
;; but I can't see how it "must be a syntax-error".
#;(must-be-a-syntax-error
  (let ((x 1)) (and-let* (((positive? x)) (x (+ x 1)) (x (+ x 1))) (+ x 1))))

(expect (let ((x 1)) (and-let* (x ((positive? x))) (+ x 1))) 2)
(expect (let ((x 1)) (and-let* ( ((begin x)) ((positive? x))) (+ x 1))) 2)
(expect (let ((x 0)) (and-let* (x ((positive? x))) (+ x 1))) #f)
(expect (let ((x #f)) (and-let* (x ((positive? x))) (+ x 1))) #f)
(expect (let ((x #f)) (and-let* ( ((begin x)) ((positive? x))) (+ x 1))) #f)

(expect  (let ((x 1)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) #f)
(expect  (let ((x 0)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) #f)
(expect  (let ((x #f)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) #f)
(expect  (let ((x 3)) (and-let* (x (y (- x 1)) ((positive? y))) (/ x y))) 3/2)

(check-report)

Added srfi/tests/compare-procedures.sps.





































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
#!r6rs
;; NOTE: All tests of complex numbers have been commented out,
;;       since many complex primitives are(were?) missing from Ikarus.

(import (rnrs) 
        (rnrs r5rs)
        (rename (only (rnrs) write) (write pretty-print))
        (surfage s27 random-bits)
        (surfage s42 eager-comprehensions)
        (surfage s67 compare-procedures))

; Copyright (c) 2005 Sebastian Egner and Jens Axel S{\o}gaard.
; 
; Permission is hereby granted, free of charge, to any person obtaining
; a copy of this software and associated documentation files (the
; ``Software''), to deal in the Software without restriction, including
; without limitation the rights to use, copy, modify, merge, publish,
; distribute, sublicense, and/or sell copies of the Software, and to
; permit persons to whom the Software is furnished to do so, subject to
; the following conditions:
; 
; The above copyright notice and this permission notice shall be
; included in all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; 
; -----------------------------------------------------------------------
;
; Compare procedures SRFI (confidence tests)
; Sebastian.Egner@philips.com, Jensaxel@soegaard.net, 2005
;
; history of this file:
;   SE, 14-Oct-2004: first version
;   ..
;   SE, 28-Feb-2005: adapted to make it one-source PLT,S48,Chicken
;   JS, 01-Mar-2005: first version
;   SE, 18-Apr-2005: added (<? [c] [x y]) and (</<? [c] [x y z])
;   SE, 13-May-2005: included examples for <? etc.
;   SE, 16-May-2005: naming convention changed; compare-by< optional x y
;
; This program runs some examples on 'compare.scm'.
; It has been tested under 
;   * PLT 208p1
;   * Scheme 48 1.1
;   * Chicken 1.70.

; Portability workarounds
; =======================
;
; The purpose of these procedures is to push the examples
; through a Scheme system with severe limitations. It is
; not the intention to supply the functionality.

; poor man's complex
(define (pm-complex? z)             (or (real? z) (and (pair? z) (eq? (car z) 'complex))))
(define (pm-number? z)              (or (real? z) (pm-complex? z)))
(define (pm-make-rectangular re im) (list 'complex re im))
(define (pm-real-part z)            (if (pm-complex? z) (cadr z) z))
(define (pm-imag-part z)            (if (pm-complex? z) (caddr z) z))

; apply on truncated argument list
(define (make-apply limit)
  (let ((original-apply apply))
    (lambda (f . xs)
      (let ((args (let loop ((xs xs) (rev-args '()))
                    (cond
                      ((null? xs)
                       (reverse rev-args))
                      ((null? (cdr xs))
                       (append (reverse rev-args) (car xs)))
                      (else
                       (loop (cdr xs) (cons (car xs) rev-args)))))))
        (if (<= (length args) limit)
            (original-apply f args)
            (original-apply
             f
             (begin (display "*** warning: truncated apply")
                    (newline)
                    (let truncate ((n 0) (rev-args '()) (xs args))
                      (if (= n limit)
                          (reverse rev-args)
                          (truncate (+ n 1) (cons (car xs) rev-args) (cdr xs)))))))))))

; =============================================================================

; Running the examples in Ikarus 
; ======================================
;
; 1. Uncomment the following lines:
;

(define pretty-write pretty-print)

; 2. Run this file.



; Running the examples in PLT (DrScheme)
; ======================================
;
; 1. Uncomment the following lines:
;
;plt (require 
;plt    (lib "16.ss" "srfi") ; case-lambda
;plt    (lib "23.ss" "srfi") ; error
;plt    (lib "27.ss" "srfi") ; random-integer
;plt    (lib "42.ss" "srfi") ; eager comprehensions list-ec etc.
;plt    (lib "pretty.ss"))   ; pretty-print
;plt (define pretty-write pretty-print)
;plt (load "compare.scm")
;
; 2. Run this file.

; Running the examples in Scheme-48
; =================================
;
; 1. Invoke scheme48 with sufficient heap size (-h <words>).
; 2. Paste this into the REPL:
;      ,open srfi-16 srfi-23 srfi-27 srfi-42 pp
;      (define pretty-write p)
;      ,load compare.scm examples.scm

; Running the examples in the Chicken Scheme Interpreter
; ======================================================
;
; 1. Fetch and install the srfi-42 egg from the Chicken homepage
; 2. Uncomment the following lines:
;      (require-extension srfi-23)
;      (define random-integer random)
;      (require-extension srfi-42)
;      (define pretty-write display)
;      (define complex? pm-complex?)
;      (define number? pm-number?)
;      (define make-rectangular pm-make-rectangular)
;      (define real-part pm-real-part)
;      (define imag-part pm-imag-part)
;      (define apply (make-apply 126)) ; Grrr...
;      (load "compare.scm")
; 3. Invoke csi with:
;      csi -syntax examples.scm
;        
; Note: Chicken doesn't have complex numbers and has a
;       severe limit on the number of arguments for apply.

; =============================================================================

; Test engine
; ===========
;
; We use an extended version of the the checker of SRFI-42 (with
; Felix' reduction on codesize) for running a batch of tests for
; the various procedures of 'compare.scm'. Moreover, we use the
; comprehensions of SRFI-42 to generate examples systematically.

(define my-equal?       equal?)
(define my-pretty-write pretty-write)

(define my-check-correct 0)
(define my-check-wrong   0)

(define (my-check-reset)
  (set! my-check-correct 0)
  (set! my-check-wrong   0))

; (my-check expr => desired-result)
;   evaluates expr and compares the value with desired-result.

(define-syntax my-check
  (syntax-rules (=>)
    ((my-check expr => desired-result)
     (my-check-proc 'expr (lambda () expr) desired-result))))

(define (my-check-proc expr thunk desired-result)
  (newline)
  (my-pretty-write expr)
  (display "  => ")
  (let ((actual-result (thunk)))
    (write actual-result)
    (if (my-equal? actual-result desired-result)
        (begin
          (display " ; correct")
          (set! my-check-correct (+ my-check-correct 1)) )
        (begin
          (display " ; *** wrong ***, desired result:")
          (newline)
          (display "  => ")
          (write desired-result)
          (set! my-check-wrong (+ my-check-wrong 1))))
    (newline)))

; (my-check-ec <qualifier>* <ok?> <expr>)
;    runs (every?-ec <qualifier>* <ok?>), counting the times <ok?>
;    is evaluated as a correct example, and stopping at the first
;    counter example for which <expr> provides the argument.

(define-syntax my-check-ec
  (syntax-rules (nested)
    ((my-check-ec (nested q1 ...) q etc1 etc2 etc ...)
     (my-check-ec (nested q1 ... q) etc1 etc2 etc ...))
    ((my-check-ec q1 q2             etc1 etc2 etc ...)
     (my-check-ec (nested q1 q2)    etc1 etc2 etc ...))
    ((my-check-ec ok? expr)
     (my-check-ec (nested) ok? expr))
    ((my-check-ec (nested q ...) ok? expr)
     (my-check-ec-proc
      '(every?-ec q ... ok?)
      (lambda ()
        (first-ec 
         'ok
         (nested q ...)
         (:let ok ok?)
         (begin 
           (if ok
               (set! my-check-correct (+ my-check-correct 1))
               (set! my-check-wrong   (+ my-check-wrong   1))))
         (if (not ok))
         (list expr)))
      'expr))
    ((my-check-ec q ok? expr)
     (my-check-ec (nested q) ok? expr))))

(define (my-check-ec-proc expr thunk arg-counter-example)
  (let ((my-check-correct-save my-check-correct))
    (newline)
    (my-pretty-write expr)
    (display "  => ")
    (let ((result (thunk)))
      (if (eqv? result 'ok)
          (begin 
            (display "#t ; correct (")
            (write (- my-check-correct my-check-correct-save))
            (display " examples)")
            (newline))
          (begin
            (display "#f ; *** wrong *** (after ")
            (write (- my-check-correct my-check-correct-save))               
            (display " correct examples).")
            (newline)
            (display "        ; Argument of the first counter example:")
            (newline)
            (display "        ;   ")
            (write arg-counter-example)
            (display " = ")
            (write (car result)))))))

(define (my-check-summary)
  (begin
    (newline)
    (newline)
    (display "*** correct examples: ")
    (display my-check-correct)
    (newline)
    (display "*** wrong examples:   ")
    (display my-check-wrong)
    (newline)
    (newline)))

; =============================================================================

; Abstractions etc.
; =================

(define ci integer-compare) ; very frequently used

; (result-ok? actual desired)
;   tests if actual and desired specify the same ordering.

(define (result-ok? actual desired)
  (eqv? actual desired))

; (my-check-compare compare increasing-elements)
;    evaluates (compare x y) for x, y in increasing-elements
;    and checks the result against -1, 0, or 1 depending on
;    the position of x and y in the list increasing-elements.

(define-syntax my-check-compare
  (syntax-rules ()
    ((my-check-compare compare increasing-elements)
     (my-check-ec
      (:list x (index ix) increasing-elements)
      (:list y (index iy) increasing-elements)
      (result-ok? (compare x y) (ci ix iy))
      (list x y)))))

; sorted lists

(define my-booleans   '(#f #t))
(define my-chars      '(#\a #\b #\c))
(define my-chars-ci   '(#\a #\B #\c #\D))
(define my-strings    '("" "a" "aa" "ab" "b" "ba" "bb"))
(define my-strings-ci '("" "a" "aA" "Ab" "B" "bA" "BB"))
(define my-symbols    '(a aa ab b ba bb))

(define my-reals
  (append-ec (:range xn -6 7) 
             (:let x (/ xn 3))
             (list x (+ x (exact->inexact (/ 1 100))))))

(define my-rationals
  (list-ec (:list x my-reals)
           (and (exact? x) (rational? x))
           x))

(define my-integers
  (list-ec (:list x my-reals)
           (if (and (exact? x) (integer? x)))
           x))

#;(define my-complexes
  (list-ec (:list re-x my-reals)
           (if (inexact? re-x))
           (:list im-x my-reals)
           (if (inexact? im-x))
           (make-rectangular re-x im-x)))

(define my-lists
  '(() (1) (1 1) (1 2) (2) (2 1) (2 2)))

(define my-vector-as-lists
  (map list->vector my-lists))

(define my-list-as-vectors
  '(() (1) (2) (1 1) (1 2) (2 1) (2 2)))

(define my-vectors
  (map list->vector my-list-as-vectors))

(define my-null-or-pairs 
  '(()
    (1) (1 1) (1 2) (1 . 1) (1 . 2) 
    (2) (2 1) (2 2) (2 . 1) (2 . 2)))

(define my-objects
  (append my-null-or-pairs
          my-booleans
          my-chars
          my-strings
          my-symbols
          my-integers
          my-vectors))

; =============================================================================

; The checks
; ==========

(define (check:if3)
  
  ; basic functionality
  
  (my-check (if3 -1 'n 'z 'p) => 'n)
  (my-check (if3  0 'n 'z 'p) => 'z)
  (my-check (if3  1 'n 'z 'p) => 'p)
  
  ; check arguments are evaluated only once
  
  (my-check 
   (let ((x -1))
     (if3 (let ((x0 x)) (set! x (+ x 1)) x0) 'n 'z 'p))
   => 'n)
  
  (my-check 
   (let ((x -1) (y 0)) 
     (if3 (let ((x0 x)) (set! x (+ x 1)) x0)
          (begin (set! y (+ y 1))   y)
          (begin (set! y (+ y 10))  y)
          (begin (set! y (+ y 100)) y)))
   => 1)
  
  (my-check 
   (let ((x 0) (y 0)) 
     (if3 (let ((x0 x)) (set! x (+ x 1)) x0)
          (begin (set! y (+ y 1))   y)
          (begin (set! y (+ y 10))  y)
          (begin (set! y (+ y 100)) y)))
   => 10)
  
  (my-check 
   (let ((x 1) (y 0)) 
     (if3 (let ((x0 x)) (set! x (+ x 1)) x0)
          (begin (set! y (+ y 1))   y)
          (begin (set! y (+ y 10))  y)
          (begin (set! y (+ y 100)) y)))
   => 100)
  
  ) ; check:if3

(define-syntax my-check-if2
  (syntax-rules ()
    ((my-check-if2 if-rel? rel)
     (begin
       ; check result
       (my-check (if-rel? -1 'yes 'no) => (if (rel -1 0) 'yes 'no))
       (my-check (if-rel?  0 'yes 'no) => (if (rel  0 0) 'yes 'no))
       (my-check (if-rel?  1 'yes 'no) => (if (rel  1 0) 'yes 'no))
       
       ; check result of 'laterally challenged if'
       (my-check (let ((x #f)) (if-rel? -1 (set! x #t)) x) => (rel -1 0))
       (my-check (let ((x #f)) (if-rel?  0 (set! x #t)) x) => (rel  0 0))
       (my-check (let ((x #f)) (if-rel?  1 (set! x #t)) x) => (rel  1 0))
       
       ; check that <c> is evaluated exactly once
       (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) -1) #t #f) n) => 1)
       (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1))  0) #t #f) n) => 1)
       (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1))  1) #t #f) n) => 1)
       (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1)) -1) #t) n) => 1)
       (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1))  0) #t) n) => 1)
       (my-check (let ((n 0)) (if-rel? (begin (set! n (+ n 1))  1) #t) n) => 1)
       ))))

(define (check:ifs)
  
  (my-check-if2 if=?     =)
  (my-check-if2 if<?     <)
  (my-check-if2 if>?     >)
  (my-check-if2 if<=?    <=)
  (my-check-if2 if>=?    >=)
  (my-check-if2 if-not=? (lambda (x y) (not (= x y))))
  
  ) ; check:if2

; <? etc. macros

(define-syntax my-check-chain2
  (syntax-rules ()
    ((my-check-chain2 rel? rel)
     (begin
       ; all chains of length 2
       (my-check (rel? ci 0 0) => (rel 0 0))
       (my-check (rel? ci 0 1) => (rel 0 1))
       (my-check (rel? ci 1 0) => (rel 1 0))
       
       ; using default-compare
       (my-check (rel? 0 0) => (rel 0 0))
       (my-check (rel? 0 1) => (rel 0 1))
       (my-check (rel? 1 0) => (rel 1 0))

       ; as a combinator
       (my-check ((rel? ci) 0 0) => (rel 0 0))
       (my-check ((rel? ci) 0 1) => (rel 0 1))
       (my-check ((rel? ci) 1 0) => (rel 1 0))

       ; using default-compare as a combinator
       (my-check ((rel?) 0 0) => (rel 0 0))
       (my-check ((rel?) 0 1) => (rel 0 1))
       (my-check ((rel?) 1 0) => (rel 1 0))
       ))))

(define (list->set xs) ; xs a list of integers
  (if (null? xs)
      '()
      (let ((max-xs
             (let max-without-apply ((m 1) (xs xs))
               (if (null? xs)
                   m
                   (max-without-apply (max m (car xs)) (cdr xs))))))
        (let ((in-xs? (make-vector (+ max-xs 1) #f)))
          (do-ec (:list x xs) (vector-set! in-xs? x #t))
          (list-ec (:vector in? (index x) in-xs?)
                   (if in?)
                   x)))))

(define-syntax arguments-used ; set of arguments (integer, >=0) used in compare
  (syntax-rules ()
    ((arguments-used (rel1/rel2 compare arg ...))
     (let ((used '()))
       (rel1/rel2 (lambda (x y)
                    (set! used (cons x (cons y used)))
                    (compare x y))
                  arg ...)
       (list->set used)))))

(define-syntax my-check-chain3
  (syntax-rules ()
    ((my-check-chain3 rel1/rel2? rel1 rel2)
     (begin     
       ; all chains of length 3
       (my-check (rel1/rel2? ci 0 0 0) => (and (rel1 0 0) (rel2 0 0)))
       (my-check (rel1/rel2? ci 0 0 1) => (and (rel1 0 0) (rel2 0 1)))
       (my-check (rel1/rel2? ci 0 1 0) => (and (rel1 0 1) (rel2 1 0)))
       (my-check (rel1/rel2? ci 1 0 0) => (and (rel1 1 0) (rel2 0 0)))
       (my-check (rel1/rel2? ci 1 1 0) => (and (rel1 1 1) (rel2 1 0)))
       (my-check (rel1/rel2? ci 1 0 1) => (and (rel1 1 0) (rel2 0 1)))
       (my-check (rel1/rel2? ci 0 1 1) => (and (rel1 0 1) (rel2 1 1)))
       (my-check (rel1/rel2? ci 0 1 2) => (and (rel1 0 1) (rel2 1 2)))
       (my-check (rel1/rel2? ci 0 2 1) => (and (rel1 0 2) (rel2 2 1)))
       (my-check (rel1/rel2? ci 1 2 0) => (and (rel1 1 2) (rel2 2 0)))
       (my-check (rel1/rel2? ci 1 0 2) => (and (rel1 1 0) (rel2 0 2)))
       (my-check (rel1/rel2? ci 2 0 1) => (and (rel1 2 0) (rel2 0 1)))
       (my-check (rel1/rel2? ci 2 1 0) => (and (rel1 2 1) (rel2 1 0)))
       
       ; using default-compare
       (my-check (rel1/rel2? 0 0 0) => (and (rel1 0 0) (rel2 0 0)))
       (my-check (rel1/rel2? 0 0 1) => (and (rel1 0 0) (rel2 0 1)))
       (my-check (rel1/rel2? 0 1 0) => (and (rel1 0 1) (rel2 1 0)))
       (my-check (rel1/rel2? 1 0 0) => (and (rel1 1 0) (rel2 0 0)))
       (my-check (rel1/rel2? 1 1 0) => (and (rel1 1 1) (rel2 1 0)))
       (my-check (rel1/rel2? 1 0 1) => (and (rel1 1 0) (rel2 0 1)))
       (my-check (rel1/rel2? 0 1 1) => (and (rel1 0 1) (rel2 1 1)))
       (my-check (rel1/rel2? 0 1 2) => (and (rel1 0 1) (rel2 1 2)))
       (my-check (rel1/rel2? 0 2 1) => (and (rel1 0 2) (rel2 2 1)))
       (my-check (rel1/rel2? 1 2 0) => (and (rel1 1 2) (rel2 2 0)))
       (my-check (rel1/rel2? 1 0 2) => (and (rel1 1 0) (rel2 0 2)))
       (my-check (rel1/rel2? 2 0 1) => (and (rel1 2 0) (rel2 0 1)))
       (my-check (rel1/rel2? 2 1 0) => (and (rel1 2 1) (rel2 1 0)))
       
       ; as a combinator
       (my-check ((rel1/rel2? ci) 0 0 0) => (and (rel1 0 0) (rel2 0 0)))
       (my-check ((rel1/rel2? ci) 0 0 1) => (and (rel1 0 0) (rel2 0 1)))
       (my-check ((rel1/rel2? ci) 0 1 0) => (and (rel1 0 1) (rel2 1 0)))
       (my-check ((rel1/rel2? ci) 1 0 0) => (and (rel1 1 0) (rel2 0 0)))
       (my-check ((rel1/rel2? ci) 1 1 0) => (and (rel1 1 1) (rel2 1 0)))
       (my-check ((rel1/rel2? ci) 1 0 1) => (and (rel1 1 0) (rel2 0 1)))
       (my-check ((rel1/rel2? ci) 0 1 1) => (and (rel1 0 1) (rel2 1 1)))
       (my-check ((rel1/rel2? ci) 0 1 2) => (and (rel1 0 1) (rel2 1 2)))
       (my-check ((rel1/rel2? ci) 0 2 1) => (and (rel1 0 2) (rel2 2 1)))
       (my-check ((rel1/rel2? ci) 1 2 0) => (and (rel1 1 2) (rel2 2 0)))
       (my-check ((rel1/rel2? ci) 1 0 2) => (and (rel1 1 0) (rel2 0 2)))
       (my-check ((rel1/rel2? ci) 2 0 1) => (and (rel1 2 0) (rel2 0 1)))
       (my-check ((rel1/rel2? ci) 2 1 0) => (and (rel1 2 1) (rel2 1 0)))

       ; as a combinator using default-compare
       (my-check ((rel1/rel2?) 0 0 0) => (and (rel1 0 0) (rel2 0 0)))
       (my-check ((rel1/rel2?) 0 0 1) => (and (rel1 0 0) (rel2 0 1)))
       (my-check ((rel1/rel2?) 0 1 0) => (and (rel1 0 1) (rel2 1 0)))
       (my-check ((rel1/rel2?) 1 0 0) => (and (rel1 1 0) (rel2 0 0)))
       (my-check ((rel1/rel2?) 1 1 0) => (and (rel1 1 1) (rel2 1 0)))
       (my-check ((rel1/rel2?) 1 0 1) => (and (rel1 1 0) (rel2 0 1)))
       (my-check ((rel1/rel2?) 0 1 1) => (and (rel1 0 1) (rel2 1 1)))
       (my-check ((rel1/rel2?) 0 1 2) => (and (rel1 0 1) (rel2 1 2)))
       (my-check ((rel1/rel2?) 0 2 1) => (and (rel1 0 2) (rel2 2 1)))
       (my-check ((rel1/rel2?) 1 2 0) => (and (rel1 1 2) (rel2 2 0)))
       (my-check ((rel1/rel2?) 1 0 2) => (and (rel1 1 0) (rel2 0 2)))
       (my-check ((rel1/rel2?) 2 0 1) => (and (rel1 2 0) (rel2 0 1)))
       (my-check ((rel1/rel2?) 2 1 0) => (and (rel1 2 1) (rel2 1 0)))
       
       ; test if all arguments are type checked
       (my-check (arguments-used (rel1/rel2? ci 0 1 2)) => '(0 1 2))
       (my-check (arguments-used (rel1/rel2? ci 0 2 1)) => '(0 1 2))
       (my-check (arguments-used (rel1/rel2? ci 1 2 0)) => '(0 1 2))
       (my-check (arguments-used (rel1/rel2? ci 1 0 2)) => '(0 1 2))
       (my-check (arguments-used (rel1/rel2? ci 2 0 1)) => '(0 1 2))
       (my-check (arguments-used (rel1/rel2? ci 2 1 0)) => '(0 1 2))
       ))))

(define-syntax my-check-chain
  (syntax-rules ()
    ((my-check-chain chain-rel? rel)
     (begin
       ; the chain of length 0
       (my-check (chain-rel? ci) => #t)
       
       ; a chain of length 1
       (my-check (chain-rel? ci 0) => #t)
       
       ; all chains of length 2
       (my-check (chain-rel? ci 0 0) => (rel 0 0))
       (my-check (chain-rel? ci 0 1) => (rel 0 1))
       (my-check (chain-rel? ci 1 0) => (rel 1 0))
       
       ; all chains of length 3
       (my-check (chain-rel? ci 0 0 0) => (rel 0 0 0))
       (my-check (chain-rel? ci 0 0 1) => (rel 0 0 1))
       (my-check (chain-rel? ci 0 1 0) => (rel 0 1 0))
       (my-check (chain-rel? ci 1 0 0) => (rel 1 0 0))
       (my-check (chain-rel? ci 1 1 0) => (rel 1 1 0))
       (my-check (chain-rel? ci 1 0 1) => (rel 1 0 1))
       (my-check (chain-rel? ci 0 1 1) => (rel 0 1 1))
       (my-check (chain-rel? ci 0 1 2) => (rel 0 1 2))
       (my-check (chain-rel? ci 0 2 1) => (rel 0 2 1))
       (my-check (chain-rel? ci 1 2 0) => (rel 1 2 0))
       (my-check (chain-rel? ci 1 0 2) => (rel 1 0 2))
       (my-check (chain-rel? ci 2 0 1) => (rel 2 0 1))
       (my-check (chain-rel? ci 2 1 0) => (rel 2 1 0))
       
       ; check if all arguments are used
       (my-check (arguments-used (chain-rel? ci 0)) => '(0))
       (my-check (arguments-used (chain-rel? ci 0 1)) => '(0 1))
       (my-check (arguments-used (chain-rel? ci 1 0)) => '(0 1))
       (my-check (arguments-used (chain-rel? ci 0 1 2)) => '(0 1 2))
       (my-check (arguments-used (chain-rel? ci 0 2 1)) => '(0 1 2))
       (my-check (arguments-used (chain-rel? ci 1 2 0)) => '(0 1 2))
       (my-check (arguments-used (chain-rel? ci 1 0 2)) => '(0 1 2))
       (my-check (arguments-used (chain-rel? ci 2 0 1)) => '(0 1 2))
       (my-check (arguments-used (chain-rel? ci 2 1 0)) => '(0 1 2))
       ))))

(define (check:predicates-from-compare)
  
  (my-check-chain2 =?    =)
  (my-check-chain2 <?    <)
  (my-check-chain2 >?    >)
  (my-check-chain2 <=?   <=)
  (my-check-chain2 >=?   >=)
  (my-check-chain2 not=? (lambda (x y) (not (= x y))))
  
  (my-check-chain3 </<?   <  <)
  (my-check-chain3 </<=?  <  <=)
  (my-check-chain3 <=/<?  <= <)
  (my-check-chain3 <=/<=? <= <=)
  
  (my-check-chain3 >/>?   >  >)
  (my-check-chain3 >/>=?  >  >=)
  (my-check-chain3 >=/>?  >= >)
  (my-check-chain3 >=/>=? >= >=)
  
  (my-check-chain chain=?  =)
  (my-check-chain chain<?  <)
  (my-check-chain chain>?  >)
  (my-check-chain chain<=? <=)
  (my-check-chain chain>=? >=)
  
  ) ; check:predicates-from-compare

; pairwise-not=?

(define pairwise-not=?:long-sequences
  (let ()
    
    (define (extremal-pivot-sequence r)
      ; The extremal pivot sequence of order r is a 
      ; permutation of {0..2^(r+1)-2} such that the
      ; middle element is minimal, and this property
      ; holds recursively for each binary subdivision.
      ;   This sequence exposes a naive implementation of
      ; pairwise-not=? chosing the middle element as pivot.
      (if (zero? r)
          '(0)
          (let* ((s (extremal-pivot-sequence (- r 1)))
                 (ns (length s)))
            (append (list-ec (:list x s) (+ x 1))
                    '(0)
                    (list-ec (:list x s) (+ x ns 1))))))
    
    (list (list-ec (: i 4096) i)
          (list-ec (: i 4097 0 -1) i)
          (list-ec (: i 4099) (modulo (* 1003 i) 4099))
          (extremal-pivot-sequence 11))))

(define pairwise-not=?:short-sequences
  (let ()
    
    (define (combinations/repeats n l)
      ; return list of all sublists of l of size n,
      ; the order of the elements occur in the sublists 
      ; of the output is the same as in the input
      (let ((len (length l)))
        (cond
          ((= n 0)   '())
          ((= n 1)   (map list l))
          ((= len 1) (do ((r '() (cons (car l) r))
                          (i n (- i 1)))
                       ((= i 0) (list r))))
          (else      (append (combinations/repeats n (cdr l))
                             (map (lambda (c) (cons (car l) c))
                                  (combinations/repeats (- n 1) l)))))))
    
    (define (permutations l)
      ; return a list of all permutations of l
      (let ((len (length l)))
        (cond
          ((= len 0) '(()))
          ((= len 1) (list l))
          (else      (apply append
                            (map (lambda (p) (insert-every-where (car l) p))
                                 (permutations (cdr l))))))))      
    
    (define (insert-every-where x xs)
      (let loop ((result '()) (before '()) (after  xs))
        (let ((new (append before (cons x after))))
          (cond
            ((null? after) (cons new result))
            (else          (loop (cons new result)
                                 (append before (list (car after)))
                                 (cdr after))))))) 
    
    (define (sequences n max)
      (apply append
             (map permutations
                  (combinations/repeats n (list-ec (: i max) i)))))
    
    (append-ec (: n 5) (sequences n 5))))

(define (colliding-compare x y)
  (ci (modulo x 3) (modulo y 3)))

(define (naive-pairwise-not=? compare . xs)
  (let ((xs (list->vector xs)))
    (every?-ec (:range i (- (vector-length xs) 1))
               (:let xs-i (vector-ref xs i))
               (:range j (+ i 1) (vector-length xs))
               (:let xs-j (vector-ref xs j))
               (not=? compare xs-i xs-j))))

(define (check:pairwise-not=?)
  
  ; 0-ary, 1-ary
  (my-check (pairwise-not=? ci)   => #t)
  (my-check (pairwise-not=? ci 0) => #t)
  
  ; 2-ary
  (my-check (pairwise-not=? ci 0 0) => #f)
  (my-check (pairwise-not=? ci 0 1) => #t)
  (my-check (pairwise-not=? ci 1 0) => #t)
  
  ; 3-ary
  (my-check (pairwise-not=? ci 0 0 0) => #f)
  (my-check (pairwise-not=? ci 0 0 1) => #f)
  (my-check (pairwise-not=? ci 0 1 0) => #f)
  (my-check (pairwise-not=? ci 1 0 0) => #f)
  (my-check (pairwise-not=? ci 1 1 0) => #f)
  (my-check (pairwise-not=? ci 1 0 1) => #f)
  (my-check (pairwise-not=? ci 0 1 1) => #f)
  (my-check (pairwise-not=? ci 0 1 2) => #t)
  (my-check (pairwise-not=? ci 0 2 1) => #t)
  (my-check (pairwise-not=? ci 1 2 0) => #t)
  (my-check (pairwise-not=? ci 1 0 2) => #t)
  (my-check (pairwise-not=? ci 2 0 1) => #t)
  (my-check (pairwise-not=? ci 2 1 0) => #t)
  
  ; n-ary, n large: [0..n-1], [n,n-1..1], 5^[0..96] mod 97
  (my-check (apply pairwise-not=? ci (list-ec (: i 10) i)) => #t)
  (my-check (apply pairwise-not=? ci (list-ec (: i 100) i)) => #t)
  (my-check (apply pairwise-not=? ci (list-ec (: i 1000) i)) => #t)
  
  (my-check (apply pairwise-not=? ci (list-ec (: i 10 0 -1) i)) => #t)
  (my-check (apply pairwise-not=? ci (list-ec (: i 100 0 -1) i)) => #t)
  (my-check (apply pairwise-not=? ci (list-ec (: i 1000 0 -1) i)) => #t)
  
  (my-check (apply pairwise-not=? ci 
                   (list-ec (: i 97) (modulo (* 5 i) 97)))
            => #t)
  
  ; bury another copy of 72 = 5^50 mod 97 in 5^[0..96] mod 97
  (my-check (apply pairwise-not=? ci 
                   (append (list-ec (: i 0 23) (modulo (* 5 i) 97))
                           '(72)
                           (list-ec (: i 23 97) (modulo (* 5 i) 97))))
            => #f)
  (my-check (apply pairwise-not=? ci 
                   (append (list-ec (: i 0 75) (modulo (* 5 i) 97))
                           '(72)
                           (list-ec (: i 75 97) (modulo (* 5 i) 97))))
            => #f)
  
  ; check if all arguments are used
  (my-check (arguments-used (pairwise-not=? ci 0)) => '(0))
  (my-check (arguments-used (pairwise-not=? ci 0 1)) => '(0 1))
  (my-check (arguments-used (pairwise-not=? ci 1 0)) => '(0 1))
  (my-check (arguments-used (pairwise-not=? ci 0 2 1)) => '(0 1 2))
  (my-check (arguments-used (pairwise-not=? ci 1 2 0)) => '(0 1 2))
  (my-check (arguments-used (pairwise-not=? ci 1 0 2)) => '(0 1 2))
  (my-check (arguments-used (pairwise-not=? ci 2 0 1)) => '(0 1 2))
  (my-check (arguments-used (pairwise-not=? ci 2 1 0)) => '(0 1 2))
  (my-check (arguments-used (pairwise-not=? ci 0 0 0 1 0 0 0 2 0 0 0 3))
            => '(0 1 2 3))
  
  ; Guess if the implementation is O(n log n):
  ;   The test is run for 2^e pairwise unequal inputs, e >= 1,
  ;   and the number of calls to the compare procedure is counted.
  ;     all pairs:          A = Binomial[2^e, 2] = 2^(2 e - 1) * (1 - 2^-e).
  ;     divide and conquer: D = e 2^e.
  ;   Since an implementation can be randomized, the actual count may
  ;   be a random number. We put a threshold at 100 e 2^e and choose
  ;   e such that A/D >= 150, i.e. e >= 12.
  ;     The test is applied to several inputs that are known to cause
  ;   trouble in simplistic sorting algorithms: (0..2^e-1), (2^e+1,2^e..1),
  ;   a pseudo-random permutation, and a sequence with an extremal pivot
  ;   at the center of each subsequence.
  
  (my-check-ec 
   (:list input pairwise-not=?:long-sequences)
   (let ((compares 0))
     (apply pairwise-not=? 
            (lambda (x y)
              (set! compares (+ compares 1))
              (ci x y))
            input)
     ;     (display compares) (newline)
     (< compares (* 100 12 4096)))
   (length input))
  
  ; check many short sequences
  
  (my-check-ec 
   (:list input pairwise-not=?:short-sequences)
   (eq?
    (apply pairwise-not=? colliding-compare input)
    (apply naive-pairwise-not=? colliding-compare input))
   input)
  
  ; check if the arguments are used for short sequences
  
  (my-check-ec 
   (:list input pairwise-not=?:short-sequences)
   (let ((args '()))
     (apply pairwise-not=? 
            (lambda (x y)
              (set! args (cons x (cons y args)))
              (colliding-compare x y))
            input)
     (equal? (list->set args) (list->set input)))
   input)
  
  ) ; check:pairwise-not=?


; min/max

(define min/max:sequences
  (append pairwise-not=?:short-sequences
          pairwise-not=?:long-sequences))

(define (check:min/max)
  
  ; all lists of length 1,2,3
  (my-check (min-compare ci 0) => 0)
  (my-check (min-compare ci 0 0) => 0)
  (my-check (min-compare ci 0 1) => 0)
  (my-check (min-compare ci 1 0) => 0)
  (my-check (min-compare ci 0 0 0) => 0)
  (my-check (min-compare ci 0 0 1) => 0)
  (my-check (min-compare ci 0 1 0) => 0)
  (my-check (min-compare ci 1 0 0) => 0)
  (my-check (min-compare ci 1 1 0) => 0)
  (my-check (min-compare ci 1 0 1) => 0)
  (my-check (min-compare ci 0 1 1) => 0)
  (my-check (min-compare ci 0 1 2) => 0)
  (my-check (min-compare ci 0 2 1) => 0)
  (my-check (min-compare ci 1 2 0) => 0)
  (my-check (min-compare ci 1 0 2) => 0)
  (my-check (min-compare ci 2 0 1) => 0)
  (my-check (min-compare ci 2 1 0) => 0)
  
  (my-check (max-compare ci 0) => 0)
  (my-check (max-compare ci 0 0) => 0)
  (my-check (max-compare ci 0 1) => 1)
  (my-check (max-compare ci 1 0) => 1)
  (my-check (max-compare ci 0 0 0) => 0)
  (my-check (max-compare ci 0 0 1) => 1)
  (my-check (max-compare ci 0 1 0) => 1)
  (my-check (max-compare ci 1 0 0) => 1)
  (my-check (max-compare ci 1 1 0) => 1)
  (my-check (max-compare ci 1 0 1) => 1)
  (my-check (max-compare ci 0 1 1) => 1)
  (my-check (max-compare ci 0 1 2) => 2)
  (my-check (max-compare ci 0 2 1) => 2)
  (my-check (max-compare ci 1 2 0) => 2)
  (my-check (max-compare ci 1 0 2) => 2)
  (my-check (max-compare ci 2 0 1) => 2)
  (my-check (max-compare ci 2 1 0) => 2)
  
  ; check that the first minimal value is returned
  (my-check (min-compare (pair-compare-car ci)
                         '(0 1) '(0 2) '(0 3))
            => '(0 1))
  (my-check (max-compare (pair-compare-car ci)
                         '(0 1) '(0 2) '(0 3))
            => '(0 1))
  
  ; check for many inputs
  (my-check-ec 
   (:list input min/max:sequences)
   (= (apply min-compare ci input)
      (apply min (apply max input) input))
   input)
  (my-check-ec 
   (:list input min/max:sequences)
   (= (apply max-compare ci input)
      (apply max (apply min input) input))
   input)
  ; Note the stupid extra argument in the apply for
  ; the standard min/max makes sure the elements are
  ; identical when apply truncates the arglist.
  
  ) ; check:min/max


; kth-largest

(define kth-largest:sequences
  pairwise-not=?:short-sequences)

(define (naive-kth-largest compare k . xs)
  (let ((vec (list->vector xs)))
    ; bubble sort: simple, stable, O(|xs|^2)
    (do-ec (:range n (- (vector-length vec) 1))
           (:range i 0 (- (- (vector-length vec) 1) n))
           (if>? (compare (vector-ref vec i)
                          (vector-ref vec (+ i 1)))
                 (let ((vec-i (vector-ref vec i)))
                   (vector-set! vec i (vector-ref vec (+ i 1)))
                   (vector-set! vec (+ i 1) vec-i))))
    (vector-ref vec (modulo k (vector-length vec)))))

(define (check:kth-largest)
  
  ; check extensively against naive-kth-largest
  (my-check-ec 
   (:list input kth-largest:sequences)
   (: k (- -2 (length input)) (+ (length input) 2))
   (= (apply naive-kth-largest colliding-compare k input)
      (apply kth-largest colliding-compare k input))
   (list input k))
  
  ) ;check:kth-largest

; compare-by< etc. procedures

(define (check:compare-from-predicates)
  
  (my-check-compare
   (compare-by< <)
   my-integers)
  
  (my-check-compare
   (compare-by> >)
   my-integers)
  
  (my-check-compare
   (compare-by<= <=)
   my-integers)
  
  (my-check-compare
   (compare-by>= >=)
   my-integers)
  
  (my-check-compare
   (compare-by=/< = <)
   my-integers)
  
  (my-check-compare
   (compare-by=/> = >)
   my-integers)
  
  ; with explicit arguments

  (my-check-compare
   (lambda (x y) (compare-by< < x y))
   my-integers)
  
  (my-check-compare
   (lambda (x y) (compare-by> > x y))
   my-integers)
  
  (my-check-compare
   (lambda (x y) (compare-by<= <= x y))
   my-integers)
  
  (my-check-compare
   (lambda (x y) (compare-by>= >= x y))
   my-integers)
  
  (my-check-compare
   (lambda (x y) (compare-by=/< = < x y))
   my-integers)
  
  (my-check-compare
   (lambda (x y) (compare-by=/> = > x y))
   my-integers)
  
  ) ; check:compare-from-predicates


(define (check:atomic)
  
  (my-check-compare boolean-compare   my-booleans)
  
  (my-check-compare char-compare      my-chars)
  
  (my-check-compare char-compare-ci   my-chars-ci)
  
  (my-check-compare string-compare    my-strings)
  
  (my-check-compare string-compare-ci my-strings-ci)
  
  (my-check-compare symbol-compare    my-symbols)
  
  (my-check-compare integer-compare   my-integers)
  
  (my-check-compare rational-compare  my-rationals)
  
  (my-check-compare real-compare      my-reals)
  
  #;(my-check-compare complex-compare   my-complexes)
  
  #;(my-check-compare number-compare    my-complexes)
  
  ) ; check:atomic

(define (check:refine-select-cond)
  
  ; refine-compare
  
  (my-check-compare
   (lambda (x y) (refine-compare))
   '(#f))
  
  (my-check-compare
   (lambda (x y) (refine-compare (integer-compare x y)))
   my-integers)
  
  (my-check-compare
   (lambda (x y)
     (refine-compare (integer-compare (car x) (car y))
                     (symbol-compare  (cdr x) (cdr y))))
   '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c)))
  
  (my-check-compare
   (lambda (x y)
     (refine-compare (integer-compare (car   x) (car   y))
                     (symbol-compare  (cadr  x) (cadr  y))
                     (string-compare  (caddr x) (caddr y))))
   '((1 a "a") (1 b "a") (1 b "b") (2 b "c") (2 c "a") (3 a "b") (3 c "b")))
  
  ; select-compare
  
  (my-check-compare
   (lambda (x y) (select-compare x y))
   '(#f))
  
  (my-check-compare
   (lambda (x y)
     (select-compare x y 
                     (integer? (ci x y))))
   my-integers)
  
  (my-check-compare
   (lambda (x y)
     (select-compare x y 
                     (pair? (integer-compare (car x) (car y))
                            (symbol-compare  (cdr x) (cdr y)))))
   '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c)))
  
  (my-check-compare
   (lambda (x y)
     (select-compare x y 
                     (else (integer-compare x y))))
   my-integers)
  
  (my-check-compare
   (lambda (x y)
     (select-compare x y 
                     (else (integer-compare (car x) (car y))
                           (symbol-compare  (cdr x) (cdr y)))))
   '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c)))
  
  (my-check-compare
   (lambda (x y)
     (select-compare x y
                     (symbol? (symbol-compare x y))
                     (string? (string-compare x y))))
   '(a b c "a" "b" "c" 1)) ; implicit (else 0)
  
  (my-check-compare
   (lambda (x y)
     (select-compare x y
                     (symbol? (symbol-compare x y))
                     (else    (string-compare x y))))
   '(a b c "a" "b" "c"))
  
  ; test if arguments are only evaluated once
  
  (my-check
   (let ((nx 0) (ny 0) (nt 0))
     (select-compare (begin (set! nx (+ nx 1)) 1)
                     (begin (set! ny (+ ny 1)) 2)
                     ((lambda (z) (set! nt (+ nt   1)) #f) 0)
                     ((lambda (z) (set! nt (+ nt  10)) #f) 0)
                     ((lambda (z) (set! nt (+ nt 100)) #f) 0)
                     (else 0))
     (list nx ny nt))
   => '(1 1 222))
  
  ; cond-compare
  
  (my-check-compare
   (lambda (x y) (cond-compare))
   '(#f))
  
  (my-check-compare
   (lambda (x y) 
     (cond-compare 
      (((integer? x) (integer? y)) (integer-compare x y))))
   my-integers)
  
  (my-check-compare
   (lambda (x y) 
     (cond-compare 
      (((pair? x) (pair? y)) (integer-compare (car x) (car y))
                             (symbol-compare  (cdr x) (cdr y)))))
   '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c)))
  
  (my-check-compare
   (lambda (x y)
     (cond-compare
      (else (integer-compare x y))))
   my-integers)
  
  (my-check-compare
   (lambda (x y) 
     (cond-compare 
      (else (integer-compare (car x) (car y))
            (symbol-compare  (cdr x) (cdr y)))))
   '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a) (3 . c)))
  
  (my-check-compare
   (lambda (x y)
     (cond-compare 
      (((symbol? x) (symbol? y)) (symbol-compare x y))
      (((string? x) (string? y)) (string-compare x y))))
   '(a b c "a" "b" "c" 1)) ; implicit (else 0)
  
  (my-check-compare
   (lambda (x y)
     (cond-compare 
      (((symbol? x) (symbol? y)) (symbol-compare x y))
      (else                      (string-compare x y))))
   '(a b c "a" "b" "c"))
  
  ) ; check:refine-select-cond


; We define our own list/vector data structure
; as '(my-list x[1] .. x[n]), n >= 0, in order
; to make sure the default ops don't work on it.

(define (my-list-checked obj) 
  (if (and (list? obj) (eqv? (car obj) 'my-list))
      obj
      (error "expected my-list but received" obj)))

(define (list->my-list list) (cons 'my-list list))
(define (my-empty? x)        (null? (cdr (my-list-checked x))))
(define (my-head x)          (cadr (my-list-checked x)))
(define (my-tail x)          (cons 'my-list (cddr (my-list-checked x))))
(define (my-size x)          (- (length (my-list-checked x)) 1))
(define (my-ref x i)         (list-ref (my-list-checked x) (+ i 1)))

(define (check:data-structures)
  
  (my-check-compare
   (pair-compare-car ci)
   '((1 . b) (2 . a) (3 . c)))
  
  (my-check-compare
   (pair-compare-cdr ci)
   '((b . 1) (a . 2) (c . 3)))
  
  ; pair-compare
  
  (my-check-compare pair-compare my-null-or-pairs)
  
  (my-check-compare
   (lambda (x y) (pair-compare ci x y))
   my-null-or-pairs)
  
  (my-check-compare
   (lambda (x y) (pair-compare ci symbol-compare x y))
   '((1 . a) (1 . b) (2 . b) (2 . c) (3 . a)))
  
  ; list-compare
  
  (my-check-compare list-compare my-lists)
  
  (my-check-compare
   (lambda (x y) (list-compare ci x y))
   my-lists)
  
  (my-check-compare
   (lambda (x y) (list-compare x y my-empty? my-head my-tail))
   (map list->my-list my-lists))
  
  (my-check-compare
   (lambda (x y) (list-compare ci x y my-empty? my-head my-tail))
   (map list->my-list my-lists))
  
  ; list-compare-as-vector
  
  (my-check-compare list-compare-as-vector my-list-as-vectors)
  
  (my-check-compare
   (lambda (x y) (list-compare-as-vector ci x y))
   my-list-as-vectors)
  
  (my-check-compare
   (lambda (x y) (list-compare-as-vector x y my-empty? my-head my-tail))
   (map list->my-list my-list-as-vectors))
  
  (my-check-compare
   (lambda (x y) (list-compare-as-vector ci x y my-empty? my-head my-tail))
   (map list->my-list my-list-as-vectors))
  
  ; vector-compare
  
  (my-check-compare vector-compare my-vectors)
  
  (my-check-compare
   (lambda (x y) (vector-compare ci x y))
   my-vectors)
  
  (my-check-compare
   (lambda (x y) (vector-compare x y my-size my-ref))
   (map list->my-list my-list-as-vectors))
  
  (my-check-compare
   (lambda (x y) (vector-compare ci x y my-size my-ref))
   (map list->my-list my-list-as-vectors))
  
  ; vector-compare-as-list
  
  (my-check-compare vector-compare-as-list my-vector-as-lists)
  
  (my-check-compare
   (lambda (x y) (vector-compare-as-list ci x y))
   my-vector-as-lists)
  
  (my-check-compare
   (lambda (x y) (vector-compare-as-list x y my-size my-ref))
   (map list->my-list my-lists))
  
  (my-check-compare
   (lambda (x y) (vector-compare-as-list ci x y my-size my-ref))
   (map list->my-list my-lists))
  
  ) ; check:data-structures


(define (check:default-compare)
  
  (my-check-compare default-compare my-objects)
  
  ; check if default-compare refines pair-compare
  
  (my-check-ec
   (:list x (index ix) my-objects)
   (:list y (index iy) my-objects)
   (:let c-coarse (pair-compare x y))
   (:let c-fine (default-compare x y))
   (or (eqv? c-coarse 0) (eqv? c-fine c-coarse))
   (list x y))
  
  ; check if default-compare passes on debug-compare
  
  (my-check-compare (debug-compare default-compare) my-objects)
  
  ) ; check:default-compare


(define (sort-by-less xs pred) ; trivial quicksort
  (if (or (null? xs) (null? (cdr xs)))
      xs
      (append 
       (sort-by-less (list-ec (:list x (cdr xs))
			      (if (pred x (car xs))) 
			      x) 
		     pred)
       (list (car xs))
       (sort-by-less (list-ec (:list x (cdr xs))
			      (if (not (pred x (car xs))))
			      x) 
		     pred))))

(define (check:more-examples)
  
  ; define recursive order on tree type (nodes are dotted pairs)
  
  (my-check-compare
   (letrec ((c (lambda (x y)
                 (cond-compare (((null? x) (null? y)) 0)
                               (else (pair-compare c c x y))))))
     c)
   (list '() (list '()) (list '() '()) (list (list '())))
   ;'(() (() . ()) (() . (() . ())) ((() . ()) . ()))   ; Chicken can't parse this ?
   )
  
  ; redefine default-compare using select-compare
  
  (my-check-compare
   (letrec ((c (lambda (x y)
                 (select-compare x y
                                 (null? 0)
                                 (pair?    (pair-compare    c c x y))
                                 (boolean? (boolean-compare x y))
                                 (char?    (char-compare    x y))
                                 (string?  (string-compare  x y))
                                 (symbol?  (symbol-compare  x y))
                                 (number?  (number-compare  x y))
                                 (vector?  (vector-compare  c x y))
                                 (else (error "unrecognized type in c" x y))))))
     c)
   my-objects)
  
  ; redefine default-compare using cond-compare
  
  (my-check-compare
   (letrec ((c (lambda (x y)
                 (cond-compare
                  (((null?    x) (null?    y)) 0)
                  (((pair?    x) (pair?    y)) (pair-compare    c c x y))
                  (((boolean? x) (boolean? y)) (boolean-compare x y))
                  (((char?    x) (char?    y)) (char-compare    x y))
                  (((string?  x) (string?  y)) (string-compare  x y))
                  (((symbol?  x) (symbol?  y)) (symbol-compare  x y))
                  (((number?  x) (number?  y)) (number-compare  x y))
                  (((vector?  x) (vector?  y)) (vector-compare  c x y))
                  (else (error "unrecognized type in c" x y))))))
     c)
   my-objects)
  
  ; compare strings with character order reversed
  
  (my-check-compare
   (lambda (x y)
     (vector-compare-as-list
      (lambda (x y) (char-compare y x))
      x y string-length string-ref))
   '("" "b" "bb" "ba" "a" "ab" "aa"))

  ; examples from SRFI text for <? etc.

  (my-check (>? "laugh" "LOUD") => #t)
  (my-check (<? string-compare-ci "laugh" "LOUD") => #t)
  (my-check (sort-by-less '(1 a "b") (<?)) => '("b" a 1))
  (my-check (sort-by-less '(1 a "b") (>?)) => '(1 a "b"))
  
  ) ; check:more-examples


; Real life examples
; ==================

; (update/insert compare x s)
;    inserts x into list s, or updates an equivalent element by x.
;      It is assumed that s is sorted with respect to compare,
;    i.e. (apply chain<=? compare s). The result is a list with x
;    replacing the first element s[i] for which (=? compare s[i] x),
;    or with x inserted in the proper place.
;      The algorithm uses linear insertion from the front.

(define (insert/update compare x s) ; insert x into list s, or update
  (if (null? s)
      (list x)
      (if3 (compare x (car s))
           (cons x s)
           (cons x (cdr s))
           (cons (car s) (insert/update compare x (cdr s))))))

; (index-in-vector compare vec x)
;    an index i such that (=? compare vec[i] x), or #f if there is none.
;      It is assumed that s is sorted with respect to compare,
;    i.e. (apply chain<=? compare (vector->list s)). If there are 
;    several elements equivalent to x then it is unspecified which
;    these is chosen.
;      The algorithm uses binary search.

(define (index-in-vector compare vec x)
  (let binary-search ((lo -1) (hi (vector-length vec)))
    ; invariant: vec[lo] < x < vec[hi]
    (if (=? (- hi lo) 1)
        #f
        (let ((mi (quotient (+ lo hi) 2)))
          (if3 (compare x (vector-ref vec mi))
               (binary-search lo mi)
               mi
               (binary-search mi hi))))))  


; Run the checks 
; ==============

(my-check-reset)

; comment in/out as needed
(check:atomic)
(check:if3)
(check:ifs)
(check:predicates-from-compare)
(check:pairwise-not=?)
(check:min/max)
; (check:kth-largest)
(check:compare-from-predicates)
(check:refine-select-cond)
(check:data-structures)
(check:default-compare)
(check:more-examples)

(my-check-summary) ; all examples (99486) correct?

Added srfi/tests/eager-comprehensions.sps.



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
#!r6rs
; <PLAINTEXT>
; Examples for Eager Comprehensions in [outer..inner|expr]-Convention
; ===================================================================
;
; sebastian.egner@philips.com, Eindhoven, The Netherlands, 25-Apr-2005.
; Scheme R5RS (incl. macros), SRFI-23 (error).
; 
; Running the examples in Scheme48 (version 1.1):
;   ,open srfi-23
;   ,load ec.scm
;   (define my-open-output-file open-output-file)
;   (define my-call-with-input-file call-with-input-file)
;   ,load examples.scm
;
; Running the examples in PLT/DrScheme (version 208): 
;   (load "ec.scm")
;   (define (my-open-output-file filename)
;     (open-output-file filename 'replace 'text) )
;   (define (my-call-with-input-file filename thunk)
;     (call-with-input-file filename thunk 'text) )
;   (load "examples.scm")
;
; Running the examples in SCM (version 5d7):
;   (require 'macro) (require 'record)
;   (load "ec.scm")
;   (define my-open-output-file open-output-file)
;   (define my-call-with-input-file call-with-input-file)
;   (load "examples.scm")

(import
  (except (rnrs) error)
  (rnrs mutable-strings)
  (surfage s42 eager-comprehensions)
  (surfage s23 error))

(define (my-open-output-file filename)
  (open-file-output-port filename 
                         (file-options no-fail)
                         'block
                         (native-transcoder)))

(define (my-call-with-input-file filename thunk)
  (call-with-input-file filename thunk))

; Tools for checking results
; ==========================

(define (my-equal? x y)
  (cond
   ((or (boolean? x) 
        (null? x)
        (symbol? x) 
        (char? x) 
        (input-port? x)
        (output-port? x) )
    (eqv? x y) )
   ((string? x)
    (and (string? y) (string=? x y)) )
   ((vector? x)
    (and (vector? y)
         (my-equal? (vector->list x) (vector->list y)) ))
   ((pair? x)
    (and (pair? y)
         (my-equal? (car x) (car y))
         (my-equal? (cdr x) (cdr y)) ))
   ((real? x)
    (and (real? y)
         (eqv? (exact? x) (exact? y))
         (if (exact? x)
             (= x y)
             (< (abs (- x y)) (/ 1 (expt 10 6))) ))) ; will do here
   (else
    (error "unrecognized type" x) )))

(define my-check-correct 0)
(define my-check-wrong   0)

(define-syntax my-check
  (syntax-rules (=>)
    ((my-check ec => desired-result)
     (begin
       (newline)
       (write (quote ec))
       (newline)
       (let ((actual-result ec))
         (display "  => ")
         (write actual-result)
         (if (my-equal? actual-result desired-result)
             (begin
               (display " ; correct")
               (set! my-check-correct (+ my-check-correct 1)) )
             (begin
               (display " ; *** wrong ***, desired result:")
               (newline)
               (display "  => ")
               (write desired-result)
               (set! my-check-wrong (+ my-check-wrong 1)) ))
         (newline) )))))
             

; ==========================================================================
; do-ec 
; ==========================================================================

(my-check 
  (let ((x 0)) (do-ec (set! x (+ x 1))) x) 
  => 1)

(my-check 
  (let ((x 0)) (do-ec (:range i 10) (set! x (+ x 1))) x) 
  => 10)

(my-check 
  (let ((x 0)) (do-ec (:range n 10) (:range k n) (set! x (+ x 1))) x) 
  => 45)


; ==========================================================================
; list-ec and basic qualifiers 
; ==========================================================================

(my-check (list-ec 1) => '(1))

(my-check (list-ec (:range i 4) i) => '(0 1 2 3))

(my-check (list-ec (:range n 3) (:range k (+ n 1)) (list n k)) 
  => '((0 0) (1 0) (1 1) (2 0) (2 1) (2 2)) )

(my-check 
  (list-ec (:range n 5) (if (even? n)) (:range k (+ n 1)) (list n k)) 
  => '((0 0) (2 0) (2 1) (2 2) (4 0) (4 1) (4 2) (4 3) (4 4)) )

(my-check 
  (list-ec (:range n 5) (not (even? n)) (:range k (+ n 1)) (list n k)) 
  => '((1 0) (1 1) (3 0) (3 1) (3 2) (3 3)) )

(my-check
  (list-ec (:range n 5) 
           (and (even? n) (> n 2)) 
           (:range k (+ n 1)) 
           (list n k) )
  => '((4 0) (4 1) (4 2) (4 3) (4 4)) )

(my-check
  (list-ec (:range n 5) 
           (or (even? n) (> n 3)) 
           (:range k (+ n 1)) 
           (list n k) )
  => '((0 0) (2 0) (2 1) (2 2) (4 0) (4 1) (4 2) (4 3) (4 4)) )

(my-check
 (let ((x 0)) (list-ec (:range n 10) (begin (set! x (+ x 1))) n) x)
 => 10 )

(my-check
 (list-ec (nested (:range n 3) (:range k n)) k)
 => '(0 0 1) )


; ==========================================================================
; Other comprehensions
; ==========================================================================

(my-check (append-ec '(a b)) => '(a b))
(my-check (append-ec (:range i 0) '(a b)) => '())
(my-check (append-ec (:range i 1) '(a b)) => '(a b))
(my-check (append-ec (:range i 2) '(a b)) => '(a b a b))

(my-check (string-ec #\a) => (string #\a))
(my-check (string-ec (:range i 0) #\a) => "")
(my-check (string-ec (:range i 1) #\a) => "a")
(my-check (string-ec (:range i 2) #\a) => "aa")

(my-check (string-append-ec "ab") => "ab")
(my-check (string-append-ec (:range i 0) "ab") => "")
(my-check (string-append-ec (:range i 1) "ab") => "ab")
(my-check (string-append-ec (:range i 2) "ab") => "abab")

(my-check (vector-ec 1) => (vector 1))
(my-check (vector-ec (:range i 0) i) => (vector))
(my-check (vector-ec (:range i 1) i) => (vector 0))
(my-check (vector-ec (:range i 2) i) => (vector 0 1))

(my-check (vector-of-length-ec 1 1) => (vector 1))
(my-check (vector-of-length-ec 0 (:range i 0) i) => (vector))
(my-check (vector-of-length-ec 1 (:range i 1) i) => (vector 0))
(my-check (vector-of-length-ec 2 (:range i 2) i) => (vector 0 1))

(my-check (sum-ec 1) => 1)
(my-check (sum-ec (:range i 0) i) => 0)
(my-check (sum-ec (:range i 1) i) => 0)
(my-check (sum-ec (:range i 2) i) => 1)
(my-check (sum-ec (:range i 3) i) => 3)

(my-check (product-ec 1) => 1)
(my-check (product-ec (:range i 1 0) i) => 1)
(my-check (product-ec (:range i 1 1) i) => 1)
(my-check (product-ec (:range i 1 2) i) => 1)
(my-check (product-ec (:range i 1 3) i) => 2)
(my-check (product-ec (:range i 1 4) i) => 6)

(my-check (min-ec 1) => 1)
(my-check (min-ec (:range i 1) i) => 0)
(my-check (min-ec (:range i 2) i) => 0)

(my-check (max-ec 1) => 1)
(my-check (max-ec (:range i 1) i) => 0)
(my-check (max-ec (:range i 2) i) => 1)

(my-check (first-ec #f 1) => 1)
(my-check (first-ec #f (:range i 0) i) => #f)
(my-check (first-ec #f (:range i 1) i) => 0)
(my-check (first-ec #f (:range i 2) i) => 0)

(my-check 
  (let ((last-i -1))
    (first-ec #f (:range i 10) (begin (set! last-i i)) i)
    last-i )
  => 0 )

(my-check (last-ec #f 1) => 1)
(my-check (last-ec #f (:range i 0) i) => #f)
(my-check (last-ec #f (:range i 1) i) => 0)
(my-check (last-ec #f (:range i 2) i) => 1)

(my-check (any?-ec #f) => #f)
(my-check (any?-ec #t) => #t)
(my-check (any?-ec (:range i 2 2) (even? i)) => #f)
(my-check (any?-ec (:range i 2 3) (even? i)) => #t)

(my-check (every?-ec #f) => #f)
(my-check (every?-ec #t) => #t)
(my-check (every?-ec (:range i 2 2) (even? i)) => #t)
(my-check (every?-ec (:range i 2 3) (even? i)) => #t)
(my-check (every?-ec (:range i 2 4) (even? i)) => #f)

(my-check 
 (let ((sum-sqr (lambda (x result) (+ result (* x x)))))
   (fold-ec 0 (:range i 10) i sum-sqr) )
 => 285 )

(my-check 
 (let ((minus-1 (lambda (x) (- x 1)))
       (sum-sqr (lambda (x result) (+ result (* x x)))))
   (fold3-ec (error "wrong") (:range i 10) i minus-1 sum-sqr) )
 => 284 )

(my-check 
 (fold3-ec 'infinity (:range i 0) i min min)
 => 'infinity )


; ==========================================================================
; Typed generators
; ==========================================================================

(my-check (list-ec (:list x '()) x) => '())
(my-check (list-ec (:list x '(1)) x) => '(1))
(my-check (list-ec (:list x '(1 2 3)) x) => '(1 2 3))
(my-check (list-ec (:list x '(1) '(2)) x) => '(1 2))
(my-check (list-ec (:list x '(1) '(2) '(3)) x) => '(1 2 3))

(my-check (list-ec (:string c "") c) => '())
(my-check (list-ec (:string c "1") c) => '(#\1))
(my-check (list-ec (:string c "123") c) => '(#\1 #\2 #\3))
(my-check (list-ec (:string c "1" "2") c) => '(#\1 #\2))
(my-check (list-ec (:string c "1" "2" "3") c) => '(#\1 #\2 #\3))

(my-check (list-ec (:vector x (vector)) x) => '())
(my-check (list-ec (:vector x (vector 1)) x) => '(1))
(my-check (list-ec (:vector x (vector 1 2 3)) x) => '(1 2 3))
(my-check (list-ec (:vector x (vector 1) (vector 2)) x) => '(1 2))
(my-check 
 (list-ec (:vector x (vector 1) (vector 2) (vector 3)) x)
 => '(1 2 3))

(my-check (list-ec (:range x -2) x) => '())
(my-check (list-ec (:range x -1) x) => '())
(my-check (list-ec (:range x  0) x) => '())
(my-check (list-ec (:range x  1) x) => '(0))
(my-check (list-ec (:range x  2) x) => '(0 1))

(my-check (list-ec (:range x  0  3) x) => '(0 1 2))
(my-check (list-ec (:range x  1  3) x) => '(1 2))
(my-check (list-ec (:range x -2 -1) x) => '(-2))
(my-check (list-ec (:range x -2 -2) x) => '())

(my-check (list-ec (:range x 1 5  2) x) => '(1 3))
(my-check (list-ec (:range x 1 6  2) x) => '(1 3 5))
(my-check (list-ec (:range x 5 1 -2) x) => '(5 3))
(my-check (list-ec (:range x 6 1 -2) x) => '(6 4 2))

(my-check (list-ec (:real-range x 0.0 3.0)     x) => '(0. 1. 2.))
(my-check (list-ec (:real-range x 0   3.0)     x) => '(0. 1. 2.))
(my-check (list-ec (:real-range x 0   3   1.0) x) => '(0. 1. 2.))

(my-check 
 (string-ec (:char-range c #\a #\z) c) 
 => "abcdefghijklmnopqrstuvwxyz" )

(my-check 
 (begin
   (let ((f (my-open-output-file "tmp1")))
     (do-ec (:range n 10) (begin (write n f) (newline f)))
     (close-output-port f))
   (my-call-with-input-file "tmp1"
    (lambda (port) (list-ec (:port x port read) x)) ))
 => (list-ec (:range n 10) n) )

(my-check 
 (begin
   (let ((f (my-open-output-file "tmp1")))
     (do-ec (:range n 10) (begin (write n f) (newline f)))
     (close-output-port f))
   (my-call-with-input-file "tmp1"                 
     (lambda (port) (list-ec (:port x port) x)) ))
 => (list-ec (:range n 10) n) )


; ==========================================================================
; The special generators :do :let :parallel :while :until
; ==========================================================================

(my-check (list-ec (:do ((i 0)) (< i 4) ((+ i 1))) i) => '(0 1 2 3))

(my-check 
 (list-ec 
  (:do (let ((x 'x)))
       ((i 0)) 
       (< i 4) 
       (let ((j (- 10 i))))
       #t
       ((+ i 1)) )
  j )
 => '(10 9 8 7) )

(my-check (list-ec (:let x 1) x) => '(1))
(my-check (list-ec (:let x 1) (:let y (+ x 1)) y) => '(2))
(my-check (list-ec (:let x 1) (:let x (+ x 1)) x) => '(2))

(my-check 
 (list-ec (:parallel (:range i 1 10) (:list x '(a b c))) (list i x))
 => '((1 a) (2 b) (3 c)) )

(my-check 
 (list-ec (:while (:range i 1 10) (< i 5)) i)
 => '(1 2 3 4) )

(my-check 
 (list-ec (:until (:range i 1 10) (>= i 5)) i)
 => '(1 2 3 4 5) )

; with generator that might use inner bindings

(my-check
 (list-ec (:while (:list i '(1 2 3 4 5 6 7 8 9)) (< i 5)) i)
 => '(1 2 3 4) )
; Was broken in original reference implementation as pointed
; out by sunnan@handgranat.org on 24-Apr-2005 comp.lang.scheme.
; Refer to http://groups-beta.google.com/group/comp.lang.scheme/
; browse_thread/thread/f5333220eaeeed66/75926634cf31c038#75926634cf31c038

(my-check 
 (list-ec (:until (:list i '(1 2 3 4 5 6 7 8 9)) (>= i 5)) i)
 => '(1 2 3 4 5) )

; combine :while/:until and :parallel

(my-check
 (list-ec (:while (:parallel (:range i 1 10)
                             (:list j '(1 2 3 4 5 6 7 8 9)))
                  (< i 5))
          (list i j))
 => '((1 1) (2 2) (3 3) (4 4)))

(my-check
 (list-ec (:until (:parallel (:range i 1 10)
                             (:list j '(1 2 3 4 5 6 7 8 9)))
                  (>= i 5))
          (list i j))
 => '((1 1) (2 2) (3 3) (4 4) (5 5)))

; check that :while/:until really stop the generator

(my-check
 (let ((n 0))
   (do-ec (:while (:range i 1 10) (begin (set! n (+ n 1)) (< i 5)))
          (if #f #f))
   n)
 => 5)

(my-check
 (let ((n 0))
   (do-ec (:until (:range i 1 10) (begin (set! n (+ n 1)) (>= i 5)))
          (if #f #f))
   n)
 => 5)

(my-check
 (let ((n 0))
   (do-ec (:while (:parallel (:range i 1 10)
                             (:do () (begin (set! n (+ n 1)) #t) ()))
                  (< i 5))
          (if #f #f))
   n)
 => 5)

(my-check
 (let ((n 0))
   (do-ec (:until (:parallel (:range i 1 10)
                             (:do () (begin (set! n (+ n 1)) #t) ()))
                  (>= i 5))
          (if #f #f))
   n)
 => 5)

; ==========================================================================
; The dispatching generator
; ==========================================================================

(my-check (list-ec (: c '(a b)) c) => '(a b))
(my-check (list-ec (: c '(a b) '(c d)) c) => '(a b c d))

(my-check (list-ec (: c "ab") c) => '(#\a #\b))
(my-check (list-ec (: c "ab" "cd") c) => '(#\a #\b #\c #\d))

(my-check (list-ec (: c (vector 'a 'b)) c) => '(a b))
(my-check (list-ec (: c (vector 'a 'b) (vector 'c)) c) => '(a b c))

(my-check (list-ec (: i 0) i) => '())
(my-check (list-ec (: i 1) i) => '(0))
(my-check (list-ec (: i 10) i) => '(0 1 2 3 4 5 6 7 8 9))
(my-check (list-ec (: i 1 2) i) => '(1))
(my-check (list-ec (: i 1 2 3) i) => '(1))
(my-check (list-ec (: i 1 9 3) i) => '(1 4 7))

(my-check (list-ec (: i 0.0 1.0 0.2) i) => '(0. 0.2 0.4 0.6 0.8))

(my-check (list-ec (: c #\a #\c) c) => '(#\a #\b #\c))

(my-check 
 (begin
   (let ((f (my-open-output-file "tmp1")))
     (do-ec (:range n 10) (begin (write n f) (newline f)))
     (close-output-port f))
   (my-call-with-input-file "tmp1"                 
     (lambda (port) (list-ec (: x port read) x)) ))
 => (list-ec (:range n 10) n) )
    
(my-check 
 (begin
   (let ((f (my-open-output-file "tmp1")))
     (do-ec (:range n 10) (begin (write n f) (newline f)))
     (close-output-port f))
   (my-call-with-input-file "tmp1"                 
     (lambda (port) (list-ec (: x port) x)) ))
 => (list-ec (:range n 10) n) )


; ==========================================================================
; With index variable
; ==========================================================================

(my-check (list-ec (:list c (index i) '(a b)) (list c i)) => '((a 0) (b 1)))
(my-check (list-ec (:string c (index i) "a") (list c i)) => '((#\a 0)))
(my-check (list-ec (:vector c (index i) (vector 'a)) (list c i)) => '((a 0)))

(my-check 
 (list-ec (:range i (index j) 0 -3 -1) (list i j)) 
 => '((0 0) (-1 1) (-2 2)) )

(my-check 
 (list-ec (:real-range i (index j) 0 1 0.2) (list i j)) 
 => '((0. 0) (0.2 1) (0.4 2) (0.6 3) (0.8 4)) )

(my-check 
 (list-ec (:char-range c (index i) #\a #\c) (list c i)) 
 => '((#\a 0) (#\b 1) (#\c 2)) )

(my-check 
 (list-ec (: x (index i) '(a b c d)) (list x i))
 => '((a 0) (b 1) (c 2) (d 3)) )

(my-check 
 (begin
   (let ((f (my-open-output-file "tmp1")))
     (do-ec (:range n 10) (begin (write n f) (newline f)))
     (close-output-port f))
   (my-call-with-input-file "tmp1"
     (lambda (port) (list-ec (: x (index i) port) (list x i))) ))
 => '((0 0) (1 1) (2 2) (3 3) (4 4) (5 5) (6 6) (7 7) (8 8) (9 9)) )


; ==========================================================================
; The examples from the SRFI document
; ==========================================================================

; from Abstract

(my-check (list-ec (: i 5) (* i i)) => '(0 1 4 9 16))

(my-check 
  (list-ec (: n 1 4) (: i n) (list n i)) 
  => '((1 0) (2 0) (2 1) (3 0) (3 1) (3 2)) )

; from Generators

(my-check 
  (list-ec (: x (index i) "abc") (list x i)) 
  => '((#\a 0) (#\b 1) (#\c 2)) )

(my-check
  (list-ec (:string c (index i) "a" "b") (cons c i))
  => '((#\a . 0) (#\b . 1)) )


; ==========================================================================
; Little Shop of Horrors
; ==========================================================================

(my-check (list-ec (:range x 5) (:range x x) x) => '(0 0 1 0 1 2 0 1 2 3))

(my-check (list-ec (:list x '(2 "23" (4))) (: y x) y) => '(0 1 #\2 #\3 4))

(my-check 
 (list-ec (:parallel (:integers x) 
                     (:do ((i 10)) (< x i) ((- i 1))))
          (list x i))
 => '((0 10) (1 9) (2 8) (3 7) (4 6)) )


; ==========================================================================
; Less artificial examples
; ==========================================================================

(define (factorial n) ; n * (n-1) * .. * 1 for n >= 0
  (product-ec (:range k 2 (+ n 1)) k) )

(my-check (factorial  0) => 1)
(my-check (factorial  1) => 1)
(my-check (factorial  3) => 6)
(my-check (factorial  5) => 120)


(define (eratosthenes n) ; primes in {2..n-1} for n >= 1
  (let ((p? (make-string n #\1)))
    (do-ec (:range k 2 n)
           (if (char=? (string-ref p? k) #\1))
           (:range i (* 2 k) n k)
           (string-set! p? i #\0) )
    (list-ec (:range k 2 n) (if (char=? (string-ref p? k) #\1)) k) ))

(my-check 
 (eratosthenes 50)
 => '(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47) )

(my-check
 (length (eratosthenes 100000))
 => 9592 ) ; we expect 10^5/ln(10^5)


(define (pythagoras n) ; a, b, c s.t. 1 <= a <= b <= c <= n, a^2 + b^2 = c^2
  (list-ec 
   (:let sqr-n (* n n))
   (:range a 1 (+ n 1))
; (begin (display a) (display " "))
   (:let sqr-a (* a a))
   (:range b a (+ n 1)) 
   (:let sqr-c (+ sqr-a (* b b)))
   (if (<= sqr-c sqr-n))
   (:range c b (+ n 1))
   (if (= (* c c) sqr-c))
   (list a b c) ))
           
(my-check
 (pythagoras 15)
 => '((3 4 5) (5 12 13) (6 8 10) (9 12 15)) )

(my-check
 (length (pythagoras 200))
 => 127 )


(define (qsort xs) ; stable
  (if (null? xs)
      '()
      (let ((pivot (car xs)) (xrest (cdr xs)))
        (append
         (qsort (list-ec (:list x xrest) (if (<  x pivot)) x))
         (list pivot)
         (qsort (list-ec (:list x xrest) (if (>= x pivot)) x)) ))))

(my-check 
 (qsort '(1 5 4 2 4 5 3 2 1 3))
 => '(1 1 2 2 3 3 4 4 5 5) )


(define (pi-BBP m) ; approx. of pi within 16^-m (Bailey-Borwein-Plouffe)
  (sum-ec 
    (:range n 0 (+ m 1))
    (:let n8 (* 8 n))
    (* (- (/ 4 (+ n8 1))
          (+ (/ 2 (+ n8 4))
             (/ 1 (+ n8 5))
             (/ 1 (+ n8 6))))
       (/ 1 (expt 16 n)) )))

(my-check
 (pi-BBP 5)
 => (/ 40413742330349316707 12864093722915635200) )

(define (read-lines filename) ; list of all lines
  (my-call-with-input-file 
   filename
   (lambda (port)
     (list-ec (:port line port get-line) line) )))

(my-check
 (begin
   (let ((f (my-open-output-file "tmp1")))
     (do-ec (:range n 10) (begin (write n f) (newline f)))
     (close-output-port f))
   (read-lines "tmp1") )
 => (list-ec (:char-range c #\0 #\9) (string c)) )


; ==========================================================================
; Summary
; ==========================================================================

(begin
  (newline)
  (newline)
  (display "correct examples : ")
  (display my-check-correct)
  (newline)
  (display "wrong examples   : ")
  (display my-check-wrong)
  (newline)
  (newline) )

Added srfi/tests/intermediate-format-strings.sps.

































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#!r6rs
(import
  (rnrs)
  (rnrs mutable-pairs)
  (surfage s48 intermediate-format-strings)
  (surfage s78 lightweight-testing))

#;(define (format-lots n f fmt-str . args)
  (let loop ([i 0] [r #f])
    (if (= i n)
      r
      (loop (+ 1 i) (apply f fmt-str args)))))

(define-syntax expect
   (syntax-rules ()
     [(_ expected expr)
      (check expr => expected)]))

;;;===================================================

(expect (format "test ~s" 'me) (format #f "test ~a" "me"))

(check (format "~6,3F" 1/3)
       (=> member)  
         '(" 0.333" "  .333"))

(expect "  12" (format "~4F" 12))

(expect "  12.346" (format "~8,3F" 12.3456))

(expect "123.346" (format "~6,3F" 123.3456))

(expect "123.346" (format "~4,3F" 123.3456))

(expect "0.000+1.949i" (format "~8,3F" (sqrt -3.8)))

(expect " 32.00" (format "~6,2F" 32))

(expect "    32" (format "~6F" 32))

(check (format "~6F" 32.)
;; NB: (not (and (exact? 32.) (integer? 32.)))
       (=> member)  
         '("  32.0" "   32."))

(check (format "~8F" 32e45)
       (=> member)  
       '("  3.2e46" " 3.2e+46"))

(expect " 3.2e-44" (format "~8,1F" 32e-45))

(check (format "~8F" 32e20)
       (=> member)  
       '("  3.2e21" " 3.2e+21"))

(check (format "~8F" 32e5)
       (=> member)  
         '("3200000.0"  "   3.2e6" "  3.2e+6"))

(check (format "~8F" 32e2)
       (=> member)  
       '("  3200.0" "   3200."))

(check (format "~8,2F" 32e10)
       (=> member)  
         '(" 3.20e11" "3.20e+11" "320000000000.00"))

(check (format "~0,3F" 20263/2813)
       (=> member)
       '( "7.203" ))

(check (format "~0,2F" 20263/2813)
       (=> member)
       '( "7.20" ))


(expect "      1.2345" (format "~12F" 1.2345))

(expect "        1.23" (format "~12,2F" 1.2345))

(expect "       1.234" (format "~12,3F" 1.2345)) ;; "round to even"

(expect "        0.000+1.949i" (format "~20,3F" (sqrt -3.8)))

(expect "0.000+1.949i" (format "~8,3F" (sqrt -3.8)))

(check (format "~8,2F" 3.4567e11)
       (=> member)  
       '(" 3.46e11" "3.46e+11" "345670000000.00"))


(check (format "~w" (let ( (c (list 'a 'b 'c)) ) (set-cdr! (cddr c) c) c))
       (=> member)
       '("#0=(a b c . #0#)" "#1=(a b c . #1#)"))

(expect "
"
        (format "~A~A~&" (list->string (list #\newline)) ""))

(expect "a new test"
        (format "~a ~? ~a" 'a "~a" '(new) 'test))

(expect "a \"new\" test"
        (format "~a ~? ~a" 'a "~s" '("new") 'test))

;; from SLIB

(define-syntax test
   (syntax-rules ()
     [(test <format-args> <expected>)
      (check (apply format <format-args>) => <expected>)]))

(test '("abc") "abc")
(test '("~a" 10) "10")
(test '("~a" -1.2) "-1.2")
(test '("~a" a) "a")
(test '("~a" #t) "#t")
(test '("~a" #f) "#f")
(test '("~a" "abc") "abc")
(test '("~a" #(1 2 3)) "#(1 2 3)")
(test '("~a" ()) "()")
(test '("~a" (a)) "(a)")
(test '("~a" (a b)) "(a b)")
(test '("~a" (a (b c) d)) "(a (b c) d)")
(test '("~a" (a . b)) "(a . b)")
(test '("~a" (a (b c . d))) "(a (b c . d))")

; # argument test

(test '("~a ~a" 10 20) "10 20")
(test '("~a abc ~a def" 10 20) "10 abc 20 def")

; numerical test

(test '("~d" 100) "100")
(test '("~x" 100) "64")
(test '("~o" 100) "144")
(test '("~b" 100) "1100100")


; character test

(test '("~c" #\a) "a")


; tilde test

(test '("~~~~") "~~")


; whitespace character test

(test '("~%") "
")
(test '("~&") "
")
(test '("abc~&") "abc
")
(test '("abc~&def") "abc
def")
(test '("~&") "
")
(test '("~_~_~_") "   ")



; indirection test

(test '("~a ~? ~a" 10 "~a ~a" (20 30) 40) "10 20 30 40")



; slashify test

(test '("~s" "abc") "\"abc\"")
(test '("~s" "abc \\ abc") "\"abc \\\\ abc\"")
(test '("~a" "abc \\ abc") "abc \\ abc")
(test '("~s" "abc \" abc") "\"abc \\\" abc\"")
(test '("~a" "abc \" abc") "abc \" abc")
(test '("~s" #\space) "#\\space")
;(test '("~s" #\newline) "#\\newline")
(test '("~s" #\a) "#\\a")
(test '("~s" (a "b" c)) "(a \"b\" c)")
(test '("~a" (a "b" c)) "(a b c)")


; fixed floating points

  (test '("~6,2f" 3.14159) "  3.14")
  (test '("~6,1f" 3.14159) "   3.1")
  (test '("~6,0f" 3.14159) "    3.")
  (test '("~5,1f" 0) "  0.0")
  (test '("~10,7f" 3.14159) " 3.1415900")
  (test '("~10,7f" -3.14159) "-3.1415900")
  (test '("~6,3f" 0.0)    " 0.000")
  (check (format "~6,4f" 0.007)
         (=> member)
         '("  7e-3" "0.0070" ".0070"))
  (check (format "~6,3f" 0.007)
         (=> member)
         '("  7e-3"  " 0.007"))
  (check (format "~6,2f" 0.007)
         (=> member)
         '("  7e-3" "  0.01"))
  (check (format "~3,2f" 0.007)
         (=> member)
         '("7e-3" ".01" "0.01"))
  (check (format "~3,2f" -0.007)
          (=> member)
          '("-7e-3" "-.01" "-0.01"))
  (test '("~6,3f" 12345.6789) "12345.679")
  (test '("~6f" 23.4) "  23.4")
  (test '("~6f" 1234.5) "1234.5")
  (test '("~6f" 12345678) "12345678")
  (test '("~6,2f" 123.56789) "123.57")
  (test '("~6f" 123.0) " 123.0")
  (test '("~6f" -123.0) "-123.0")
  (test '("~6f" 0.0) "   0.0")
  (test '("~3,1f" 3.141) "3.1")
  (test '("~2,0f" 3.141) "3.")
  (test '("~1f" 3.141) "3.141")
  (test '("~f" 123.56789) "123.56789")
  (test '("~f" -314.0) "-314.0")
  (check (format "~f" 1e4)
         (=> member)
         '("1e4" "10000.0"))
  (check (format "~f" -1.23e10)
         (=> member)
         '("-1.23e10" "-1.23e+10" "-12300000000.0" "-12300000000."))
  (check (format "~f" 1e-4)
         (=> member)
         '("1e-4" "0.0001" ".0001"))
  (check (format "~f" -1.23e-10)
         (=> member)
         '("-0.000000000123" "-1.23e-10"))


(check-report)

;; #!eof

Added srfi/tests/lists.sps.

















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
; Test suite for SRFI-1
; 2003-12-29 / lth
;
; Note: In Larceny, we require that the procedures designated as
; "linear update" variants in the spec (eg append!) side-effect their
; arguments, and there are tests here that check that side-effecting
; occurs.
;
; For linear update we only require that the cells of the result are
; taken from the cells of the input.  We could be stricter and require
; that the cells of the results are the cells of the input with only
; the CDR changed, ie, values are never moved from one cell to another.

(import (except (rnrs base) map for-each)
        (rnrs io simple)
        (rnrs r5rs)
        (surfage s1 lists))

(define (writeln . xs)
  (for-each display xs)
  (newline))

(define (fail token . more)
  (writeln "Error: test failed: " token)
  #f)

; Test cases are ordered as in the spec.  R5RS procedures are left out.

(or (equal? (xcons 1 2) '(2 . 1))
    (fail 'xcons:1))

(or (equal? (cons* 1) 1)
    (fail 'cons*:1))
(or (equal? (cons* 1 2 3 4 5) '(1 2 3 4 . 5))
    (fail 'cons*:2))

(or (equal? (make-list 5 #t) '(#t #t #t #t #t))
    (fail 'make-list:1))
(or (equal? (make-list 0 #f) '())
    (fail 'make-list:2))
(or (equal? (length (make-list 3)) 3)
    (fail 'make-list:3))

(or (equal? (list-tabulate 5 (lambda (x) x)) '(0 1 2 3 4))
    (fail 'list-tabulate:1))
(or (equal? (list-tabulate 0 (lambda (x) (error #f "FOO!"))) '())
    (fail 'list-tabluate:2))

(or (call-with-current-continuation
     (lambda (abort)
       (let* ((c  (list 1 2 3 4 5))
	      (cp (list-copy c)))
	 (or (equal? c cp)
	     (abort #f))
	 (let loop ((c c) (cp cp))
	   (if (not (null? c))
	       (begin
		 (or (not (eq? c cp))
		     (abort #f))
		 (loop (cdr c) (cdr cp)))))
	 #t)))
    (fail 'list-copy:1))

(or (equal? (list-copy '(1 2 3 . 4)) '(1 2 3 . 4))
    (fail 'list-copy:2))

(or (not (list? (circular-list 1 2 3)))
    (fail 'circular-list:1))
(or (let* ((a (list 'a))
	   (b (list 'b))
	   (c (list 'c))
	   (x (circular-list a b c)))
      (and (eq? a (car x))
	   (eq? b (cadr x))
	   (eq? c (caddr x))
	   (eq? a (cadddr x))))
    (fail 'circular-list:2))

(or (equal? (iota 0) '())
    (fail 'iota:1))
(or (equal? (iota 5 2 3) '(2 5 8 11 14))
    (fail 'iota:2))
(or (equal? (iota 5 2) '(2 3 4 5 6))
    (fail 'iota:3))

(or (proper-list? '(1 2 3 4 5))
    (fail 'proper-list?:1))
(or (proper-list? '())
    (fail 'proper-list?:2))
(or (not (proper-list? '(1 2 . 3)))
    (fail 'proper-list?:3))
(or (not (proper-list? (circular-list 1 2 3)))
    (fail 'proper-list:4))

(or (not (circular-list? '(1 2 3 4 5)))
    (fail 'circular-list?:1))
(or (not (circular-list? '()))
    (fail 'circular-list?:2))
(or (not (circular-list? '(1 2 . 3)))
    (fail 'circular-list?:3))
(or (circular-list? (circular-list 1 2 3))
    (fail 'circular-list:4))

(or (not (dotted-list? '(1 2 3 4 5)))
    (fail 'dotted-list?:1))
(or (not (dotted-list? '()))
    (fail 'dotted-list?:2))
(or (dotted-list? '(1 2 . 3))
    (fail 'dotted-list?:3))
(or (not (dotted-list? (circular-list 1 2 3)))
    (fail 'dotted-list:4))

(or (null-list? '())
    (fail 'null-list?:1))
(or (not (null-list? '(1 2)))
    (fail 'null-list?:2))
(or (not (null-list? (circular-list 1 2)))
    (fail 'null-list?:3))

(or (not-pair? 1)
    (fail 'not-pair:1))
(or (not (not-pair? (cons 1 2)))
    (fail 'not-pair:2))

(or (list= = '(1 2 3) '(1 2 3) '(1 2 3))
    (fail 'list=:1))
(or (not (list= = '(1 2 3) '(1 2 3) '(1 4 3)))
    (fail 'list=:2))
; Checks that l0 is not being used when testing l2, cf spec
(or (list= (lambda (a b) (not (eq? a b))) '(#f #f #f) '(#t #t #t) '(#f #f #f))
    (fail 'list=:3))
(or (list= =)
    (fail 'list=:4))

(or (= (first '(1 2 3 4 5 6 7 8 9 10)) 1) (fail 'first))
(or (= (second '(1 2 3 4 5 6 7 8 9 10)) 2) (fail 'second))
(or (= (third '(1 2 3 4 5 6 7 8 9 10)) 3) (fail 'third))
(or (= (fourth '(1 2 3 4 5 6 7 8 9 10)) 4) (fail 'fourth))
(or (= (fifth '(1 2 3 4 5 6 7 8 9 10)) 5) (fail 'fifth))
(or (= (sixth '(1 2 3 4 5 6 7 8 9 10)) 6) (fail 'sixth))
(or (= (seventh '(1 2 3 4 5 6 7 8 9 10)) 7) (fail 'seventh))
(or (= (eighth '(1 2 3 4 5 6 7 8 9 10)) 8) (fail 'eighth))
(or (= (ninth '(1 2 3 4 5 6 7 8 9 10)) 9) (fail 'ninth))
(or (= (tenth '(1 2 3 4 5 6 7 8 9 10)) 10) (fail 'tenth))

(let-values (((a b) (car+cdr '(1 . 2))))
  (or (and (= a 1) (= b 2))
      (fail 'car+cdr:1)))

(or (equal? '(1 2 3) (take '(1 2 3 4 5 6) 3))
    (fail 'take:1))
(or (equal? '(1) (take '(1) 1))
    (fail 'take:2))

(or (let ((x (list 1 2 3 4 5 6)))
      (eq? (cdddr x) (drop x 3)))
    (fail 'drop:1))
(or (let ((x (list 1 2 3)))
      (eq? x (drop x 0)))
    (fail 'drop:2))

(or (equal? '(4 5 6) (take-right '(1 2 3 4 5 6) 3))
    (fail 'take-right:1))
(or (null? (take-right '(1 2 3 4 5 6) 0))
    (fail 'take-right:2))
(or (equal? '(2 3 . 4) (take-right '(1 2 3 . 4) 2))
    (fail 'take-right:3))
(or (equal? 4 (take-right '(1 2 3 . 4) 0))
    (fail 'take-right:4))

(or (equal? '(1 2 3) (drop-right '(1 2 3 4 5 6) 3))
    (fail 'drop-right:1))
(or (equal? '(1 2 3) (drop-right '(1 2 3) 0))
    (fail 'drop-right:2))
(or (equal? '(1 2 3) (drop-right '(1 2 3 . 4) 0))
    (fail 'drop-right:3))

(or (let ((x (list 1 2 3 4 5 6)))
      (let ((y (take! x 3)))
	(and (eq? x y)
	     (eq? (cdr x) (cdr y))
	     (eq? (cddr x) (cddr y))
	     (equal? y '(1 2 3)))))
    (fail 'take!:1))

(or (let ((x (list 1 2 3 4 5 6)))
      (let ((y (drop-right! x 3)))
	(and (eq? x y)
	     (eq? (cdr x) (cdr y))
	     (eq? (cddr x) (cddr y))
	     (equal? y '(1 2 3)))))
    (fail 'drop-right!:1))

(or (let-values (((a b) (split-at '(1 2 3 4 5 6) 2)))
      (and (equal? a '(1 2))
	   (equal? b '(3 4 5 6))))
    (fail 'split-at:1))

(or (let* ((x (list 1 2 3 4 5 6))
	   (y (cddr x)))
      (let-values (((a b) (split-at! x 2)))
        (and (equal? a '(1 2))
	     (eq? a x)
	     (equal? b '(3 4 5 6))
	     (eq? b y))))
    (fail 'split-at!:1))

(or (eqv? 37 (last '(1 2 3 37)))
    (fail 'last:1))

(or (not (length+ (circular-list 1 2 3)))
    (fail 'length+:1))
(or (equal? 4 (length+ '(1 2 3 4)))
    (fail 'length+:2))

(or (let ((x (list 1 2))
	  (y (list 3 4))
	  (z (list 5 6)))
      (let ((r (append! x y '() z)))
	(and (equal? r '(1 2 3 4 5 6))
	     (eq? r x)
	     (eq? (cdr r) (cdr x))
	     (eq? (cddr r) y)
	     (eq? (cdddr r) (cdr y))
	     (eq? (cddddr r) z)
	     (eq? (cdr (cddddr r)) (cdr z)))))
    (fail 'append!:1))

(or (equal? (concatenate '((1 2 3) (4 5 6) () (7 8 9))) '(1 2 3 4 5 6 7 8 9))
    (fail 'concatenate:1))

(or (equal? (concatenate! `(,(list 1 2 3) ,(list 4 5 6) () ,(list 7 8 9)))
	    '(1 2 3 4 5 6 7 8 9))
    (fail 'concatenate!:1))

(or (equal? (append-reverse '(3 2 1) '(4 5 6)) '(1 2 3 4 5 6))
    (fail 'append-reverse:1))

(or (equal? (append-reverse! (list 3 2 1) (list 4 5 6)) '(1 2 3 4 5 6))
    (fail 'append-reverse!:1))

(or (equal? (zip '(1 2 3) '(4 5 6)) '((1 4) (2 5) (3 6)))
    (fail 'zip:1))
(or (equal? (zip '() '() '() '()) '())
    (fail 'zip:2))
(or (equal? (zip '(1) (circular-list 1 2)) '((1 1)))
    (fail 'zip:3))

(or (equal? '(1 2 3 4 5) (unzip1 '((1) (2) (3) (4) (5))))
    (fail 'unzip1:1))

(or (let-values (((a b) (unzip2 '((10 11) (20 21) (30 31)))))
      (and (equal? a '(10 20 30))
	   (equal? b '(11 21 31))))
    (fail 'unzip2:1))

(or (let-values (((a b c) (unzip3 '((10 11 12) (20 21 22) (30 31 32)))))
      (and (equal? a '(10 20 30))
	   (equal? b '(11 21 31))
	   (equal? c '(12 22 32))))
    (fail 'unzip3:1))

(or (let-values (((a b c d) (unzip4 '((10 11 12 13)
				      (20 21 22 23)
				      (30 31 32 33)))))
      (and (equal? a '(10 20 30))
	   (equal? b '(11 21 31))
	   (equal? c '(12 22 32))
	   (equal? d '(13 23 33))))
    (fail 'unzip4:1))

(or (let-values (((a b c d e) (unzip5 '((10 11 12 13 14)
					(20 21 22 23 24)
					(30 31 32 33 34)))))
      (and (equal? a '(10 20 30))
	   (equal? b '(11 21 31))
	   (equal? c '(12 22 32))
	   (equal? d '(13 23 33))
	   (equal? e '(14 24 34))))
    (fail 'unzip5:1))

(or (equal? 3 (count even? '(3 1 4 1 5 9 2 5 6)))
    (fail 'count:1))
(or (equal? 3 (count < '(1 2 4 8) '(2 4 6 8 10 12 14 16)))
    (fail 'count:2))
(or (equal? 2 (count < '(3 1 4 1) (circular-list 1 10)))
    (fail 'count:3))

(or (equal? '(c 3 b 2 a 1) (fold cons* '() '(a b c) '(1 2 3 4 5)))
    (fail 'fold:1))

(or (equal? '(a 1 b 2 c 3) (fold-right cons* '() '(a b c) '(1 2 3 4 5)))
    (fail 'fold-right:1))

(or (let* ((x (list 1 2 3))
	   (r (list x (cdr x) (cddr x)))
	   (y (pair-fold (lambda (pair tail) 
			   (set-cdr! pair tail) pair) 
			 '()
			 x)))
      (and (equal? y '(3 2 1))
	   (every (lambda (c) (memq c r)) (list y (cdr y) (cddr y)))))
    (fail 'pair-fold:1))

(or (equal? '((a b c) (b c) (c)) (pair-fold-right cons '() '(a b c)))
    (fail 'pair-fold-right:1))

(or (equal? 5 (reduce max 'illegal '(1 2 3 4 5)))
    (fail 'reduce:1))
(or (equal? 0 (reduce max 0 '()))
    (fail 'reduce:2))

(or (equal? '(1 2 3 4 5) (reduce-right append 'illegal '((1 2) () (3 4 5))))
    (fail 'reduce-right:1))

(or (equal? '(1 4 9 16 25 36 49 64 81 100)
	    (unfold (lambda (x) (> x 10))
		    (lambda (x) (* x x))
		    (lambda (x) (+ x 1))
		    1))
    (fail 'unfold:1))

(or (equal? '(1 4 9 16 25 36 49 64 81 100)
	    (unfold-right zero? 
			  (lambda (x) (* x x))
			  (lambda (x) (- x 1))
			  10))
    (fail 'unfold-right:1))

(or (equal? '(4 1 5 1)
	    (map + '(3 1 4 1) (circular-list 1 0)))
    (fail 'map:1))

(or (equal? '(5 4 3 2 1)
	    (let ((v 1)
		  (l '()))
	      (for-each (lambda (x y)
			  (let ((n v))
			    (set! v (+ v 1))
			    (set! l (cons n l))))
			'(0 0 0 0 0)
			(circular-list 1 2))
	      l))
    (fail 'for-each:1))

(or (equal? '(1 -1 3 -3 8 -8) 
	    (append-map (lambda (x) (list x (- x))) '(1 3 8)))
    (fail 'append-map:1))

(or (equal? '(1 -1 3 -3 8 -8) 
	    (append-map! (lambda (x) (list x (- x))) '(1 3 8)))
    (fail 'append-map!:1))

(or (let* ((l (list 1 2 3))
	   (m (map! (lambda (x) (* x x)) l)))
      (and (equal? m '(1 4 9))
	   (equal? l '(1 4 9))))
    (fail 'map!:1))

(or (equal? '(1 2 3 4 5)
	    (let ((v 1))
	      (map-in-order (lambda (x)
			      (let ((n v))
				(set! v (+ v 1))
				n))
			    '(0 0 0 0 0))))
    (fail 'map-in-order:1))

(or (equal? '((3) (2 3) (1 2 3))
	    (let ((xs (list 1 2 3))
		  (l '()))
	      (pair-for-each (lambda (x) (set! l (cons x l))) xs)
	      l))
    (fail 'pair-for-each:1))

(or (equal? '(1 9 49)
	    (filter-map (lambda (x y) (and (number? x) (* x x))) 
			'(a 1 b 3 c 7)
			(circular-list 1 2)))
    (fail 'filter-map:1))

(or (equal? '(0 8 8 -4) (filter even? '(0 7 8 8 43 -4)))
    (fail 'filter:1))

(or (let-values (((a b) (partition symbol? '(one 2 3 four five 6))))
      (and (equal? a '(one four five))
	   (equal? b '(2 3 6))))
    (fail 'partition:1))

(or (equal? '(7 43) (remove even? '(0 7 8 8 43 -4)))
    (fail 'remove:1))

(or (let* ((x (list 0 7 8 8 43 -4))
	   (y (pair-fold cons '() x))
	   (r (filter! even? x)))
      (and (equal? '(0 8 8 -4) r)
	   (every (lambda (c) (memq c y)) (pair-fold cons '() r))))
    (fail 'filter!:1))

(or (let* ((x (list 'one 2 3 'four 'five 6))
	   (y (pair-fold cons '() x)))
      (let-values (((a b) (partition! symbol? x)))
	(and (equal? a '(one four five))
	     (equal? b '(2 3 6))
	     (every (lambda (c) (memq c y)) (pair-fold cons '() a))
	     (every (lambda (c) (memq c y)) (pair-fold cons '() b)))))
    (fail 'partition!:1))

(or (let* ((x (list 0 7 8 8 43 -4))
	   (y (pair-fold cons '() x))
	   (r (remove! even? x)))
      (and (equal? '(7 43) r)
	   (every (lambda (c) (memq c y)) (pair-fold cons '() r))))
    (fail 'remove!:1))

(or (equal? 4 (find even? '(3 1 4 1 5 9 8)))
    (fail 'find:1))

(or (equal? '(4 1 5 9 8) (find-tail even? '(3 1 4 1 5 9 8)))
    (fail 'find-tail:1))
(or (equal? '#f (find-tail even? '(1 3 5 7)))
    (fail 'find-tail:2))

(or (equal? '(2 18) (take-while even? '(2 18 3 10 22 9)))
    (fail 'take-while:1))

(or (let* ((x (list 2 18 3 10 22 9))
	   (r (take-while! even? x)))
      (and (equal? r '(2 18))
	   (eq? r x)
	   (eq? (cdr r) (cdr x))))
    (fail 'take-while!:1))

(or (equal? '(3 10 22 9) (drop-while even? '(2 18 3 10 22 9)))
    (fail 'drop-while:1))

(or (let-values (((a b) (span even? '(2 18 3 10 22 9))))
      (and (equal? a '(2 18))
	   (equal? b '(3 10 22 9))))
    (fail 'span:1))

(or (let-values (((a b) (break even? '(3 1 4 1 5 9))))
      (and (equal? a '(3 1))
	   (equal? b '(4 1 5 9))))
    (fail 'break:1))

(or (let* ((x     (list 2 18 3 10 22 9))
	   (cells (pair-fold cons '() x)))
      (let-values (((a b) (span! even? x)))
        (and (equal? a '(2 18))
	     (equal? b '(3 10 22 9))
	     (every (lambda (x) (memq x cells)) (pair-fold cons '() a))
	     (every (lambda (x) (memq x cells)) (pair-fold cons '() b)))))
    (fail 'span!:1))

(or (let* ((x     (list 3 1 4 1 5 9))
	   (cells (pair-fold cons '() x)))
      (let-values (((a b) (break! even? x)))
        (and (equal? a '(3 1))
	     (equal? b '(4 1 5 9))
	     (every (lambda (x) (memq x cells)) (pair-fold cons '() a))
	     (every (lambda (x) (memq x cells)) (pair-fold cons '() b)))))
    (fail 'break!:1))

(or (any integer? '(a 3 b 2.7))
    (fail 'any:1))
(or (not (any integer? '(a 3.1 b 2.7)))
    (fail 'any:2))
(or (any < '(3 1 4 1 5) (circular-list 2 7 1 8 2))
    (fail 'any:3))
(or (equal? 'yes (any (lambda (a b) (if (< a b) 'yes #f))
		      '(1 2 3) '(0 1 4)))
    (fail 'any:4))

(or (every integer? '(1 2 3))
    (fail 'every:1))
(or (not (every integer? '(3 4 5.1)))
    (fail 'every:2))
(or (every < '(1 2 3) (circular-list 2 3 4))
    (fail 'every:3))

(or (equal? 2 (list-index even? '(3 1 4 1 5 9)))
    (fail 'list-index:1))
(or (equal? 1 (list-index < '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2)))
    (fail 'list-index:2))
(or (not (list-index = '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2)))
    (fail 'list-index:3))

(or (equal? '(37 48) (member 5 '(1 2 5 37 48) <))
    (fail 'member:1))

(or (equal? '(1 2 5) (delete 5 '(1 48 2 5 37) <))
    (fail 'delete:1))
(or (equal? '(1 2 7) (delete 5 '(1 5 2 5 7)))
    (fail 'delete:2))

(or (let* ((x     (list 1 48 2 5 37))
	   (cells (pair-fold cons '() x))
	   (r     (delete! 5 x <)))
      (and (equal? r '(1 2 5))
	   (every (lambda (x) (memq x cells)) (pair-fold cons '() r))))
    (fail 'delete!:1))

(or (equal? '((a . 3) (b . 7) (c . 1))
	    (delete-duplicates '((a . 3) (b . 7) (a . 9) (c . 1))
			       (lambda (x y) (eq? (car x) (car y)))))
    (fail 'delete-duplicates:1))
(or (equal? '(a b c z) (delete-duplicates '(a b a c a b c z) eq?))
    (fail 'delete-duplicates:2))

(or (let* ((x     (list 'a 'b 'a 'c 'a 'b 'c 'z))
	   (cells (pair-fold cons '() x))
	   (r     (delete-duplicates! x)))
      (and (equal? '(a b c z) r)
	   (every (lambda (x) (memq x cells)) (pair-fold cons '() r))))
    (fail 'delete-duplicates!:1))

(or (equal? '(3 . #t) (assoc 6 
			     '((4 . #t) (3 . #t) (5 . #t))
			     (lambda (x y)
			       (zero? (remainder x y)))))
    (fail 'assoc:1))

(or (equal? '((1 . #t) (2 . #f)) (alist-cons 1 #t '((2 . #f))))
    (fail 'alist-cons:1))

(or (let* ((a (list (cons 1 2) (cons 3 4)))
	   (b (alist-copy a)))
      (and (equal? a b)
	   (every (lambda (x) (not (memq x b))) a)
	   (every (lambda (x) (not (memq x a))) b)))
    (fail 'alist-copy:1))

(or (equal? '((1 . #t) (2 . #t) (4 . #t))
	    (alist-delete 5 '((1 . #t) (2 . #t) (37 . #t) (4 . #t) (48 #t)) <))
    (fail 'alist-delete:1))
(or (equal? '((1 . #t) (2 . #t) (4 . #t))
	    (alist-delete 7 '((1 . #t) (2 . #t) (7 . #t) (4 . #t) (7 #t))))
    (fail 'alist-delete:2))

(or (let* ((x (list-copy '((1 . #t) (2 . #t) (7 . #t) (4 . #t) (7 #t))))
	   (y (list-copy x))
	   (cells (pair-fold cons '() x))
	   (r (alist-delete! 7 x)))
      (and (equal? r '((1 . #t) (2 . #t) (4 . #t)))
	   (every (lambda (x) (memq x cells)) (pair-fold cons '() r))
	   (every (lambda (x) (memq x y)) r)))
    (fail 'alist-delete!:1))

(or (lset<= eq? '(a) '(a b a) '(a b c c))
    (fail 'lset<=:1))
(or (not (lset<= eq? '(a) '(a b a) '(a)))
    (fail 'lset<=:2))
(or (lset<= eq?)
    (fail 'lset<=:3))
(or (lset<= eq? '(a))
    (fail 'lset<=:4))

(or (lset= eq? '(b e a) '(a e b) '(e e b a))
    (fail 'lset=:1))
(or (not (lset= eq? '(b e a) '(a e b) '(e e b a c)))
    (fail 'lset=:2))
(or (lset= eq?)
    (fail 'lset=:3))
(or (lset= eq? '(a))
    (fail 'lset=:4))

(or (equal? '(u o i a b c d c e) 
	    (lset-adjoin eq? '(a b c d c e) 'a 'e 'i 'o 'u))
    (fail 'lset-adjoin:1))

(or (equal? '(u o i a b c d e)
	    (lset-union eq? '(a b c d e) '(a e i o u)))
    (fail 'lset-union:1))
(or (equal? '(x a a c) (lset-union eq? '(a a c) '(x a x)))
    (fail 'lset-union:2))
(or (null? (lset-union eq?))
    (fail 'lset-union:3))
(or (equal? '(a b c) (lset-union eq? '(a b c)))
    (fail 'lset-union:4))

(or (equal? '(a e) (lset-intersection eq? '(a b c d e) '(a e i o u)))
    (fail 'lset-intersection:1))
(or (equal? '(a x a) (lset-intersection eq? '(a x y a) '(x a x z)))
    (fail 'lset-intersection:2))
(or (equal? '(a b c) (lset-intersection eq? '(a b c)))
    (fail 'lset-intersection:3))

(or (equal? '(b c d) (lset-difference eq? '(a b c d e) '(a e i o u)))
    (fail 'lset-difference:1))
(or (equal? '(a b c) (lset-difference eq? '(a b c)))
    (fail 'lset-difference:2))

(or (lset= eq? '(d c b i o u) (lset-xor eq? '(a b c d e) '(a e i o u)))
    (fail 'lset-xor:1))
(or (lset= eq? '() (lset-xor eq?))
    (fail 'lset-xor:2))
(or (lset= eq? '(a b c d e) (lset-xor eq? '(a b c d e)))
    (fail 'lset-xor:3))

(or (let-values (((d i) (lset-diff+intersection eq? '(a b c d e) '(c d f))))
      (and (equal? d '(a b e))
	   (equal? i '(c d))))
    (fail 'lset-diff+intersection:1))

; FIXME: For the following five procedures, need to check that cells
; returned are from the arguments.

(or (equal? '(u o i a b c d e)
	    (lset-union! eq? (list 'a 'b 'c 'd 'e) (list 'a 'e 'i 'o 'u)))
    (fail 'lset-union!:1))
(or (equal? '(x a a c) (lset-union! eq? (list 'a 'a 'c) (list 'x 'a 'x)))
    (fail 'lset-union!:2))
(or (null? (lset-union! eq?))
    (fail 'lset-union!:3))
(or (equal? '(a b c) (lset-union! eq? (list 'a 'b 'c)))
    (fail 'lset-union!:4))

(or (equal? '(a e) (lset-intersection! eq? (list 'a 'b 'c 'd 'e) 
				       (list 'a 'e 'i 'o 'u)))
    (fail 'lset-intersection!:1))
(or (equal? '(a x a) (lset-intersection! eq? (list 'a 'x 'y 'a) 
					 (list 'x 'a 'x 'z)))
    (fail 'lset-intersection!:2))
(or (equal? '(a b c) (lset-intersection! eq? (list 'a 'b 'c)))
    (fail 'lset-intersection!:3))

(or (equal? '(b c d) (lset-difference! eq? (list 'a 'b 'c 'd 'e)
				       (list 'a 'e 'i 'o 'u)))
    (fail 'lset-difference!:1))
(or (equal? '(a b c) (lset-difference! eq? (list 'a 'b 'c)))
    (fail 'lset-difference!:2))

(or (lset= eq? '(d c b i o u) (lset-xor! eq? (list 'a 'b 'c 'd 'e)
					 (list 'a 'e 'i 'o 'u)))
    (fail 'lset-xor!:1))
(or (lset= eq? '() (lset-xor! eq?))
    (fail 'lset-xor!:2))
(or (lset= eq? '(a b c d e) (lset-xor! eq? (list 'a 'b 'c 'd 'e)))
    (fail 'lset-xor!:3))

(or (let-values (((d i) (lset-diff+intersection! eq? (list 'a 'b 'c 'd 'e)
						 (list 'c 'd 'f))))
      (and (equal? d '(a b e))
	   (equal? i '(c d))))
    (fail 'lset-diff+intersection!:1))

(writeln "Done.")

Added srfi/tests/multi-dimensional-arrays--arlib.sps.















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.  Licensed under an
;; MIT-style license.  My license is in the file named LICENSE from the original
;; collection this file is distributed with.  If this file is redistributed with
;; some other collection, my license must also be included.

(import
  (rnrs)
  (surfage s25 multi-dimensional-arrays)
  (surfage s25 multi-dimensional-arrays arlib)
  (surfage s78 lightweight-testing)
  (srfi private include))

(define-syntax past (syntax-rules () ((_ . r) (begin))))

(let-syntax ((or
              (syntax-rules (error)
                ((_ expr (error msg))
                 (check expr => #T))
                ((_ . r) (or . r)))))
  (include/resolve ("srfi" "%3a25") "list.scm"))

(check-report)

Added srfi/tests/multi-dimensional-arrays.sps.



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
#!r6rs
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.  Licensed under an
;; MIT-style license.  My license is in the file named LICENSE from the original
;; collection this file is distributed with.  If this file is redistributed with
;; some other collection, my license must also be included.

(import
  (rnrs)
  (surfage s25 multi-dimensional-arrays)
  (surfage s78 lightweight-testing)
  (surfage private include))

;; (let-syntax ((or
;;               (syntax-rules (error)
;;                 ((_ expr (error msg))
;;                  (check (and expr #T) => #T))
;;                 ((_ . r) (or . r))))
;;              (past
;;               (syntax-rules ()
;;                 ((_ . r) (values)))))
;;   (include/resolve ("surfage" "s25") "test.scm"))

(let-syntax ((or
              (syntax-rules (error)
                ((_ expr (error msg))
                 (check (and expr #T) => #T))
                ((_ . r) (or . r))))

             (past
              (syntax-rules ()
                ((_ . r) (values))))

             )

  ;; (include/resolve ("surfage" "s25") "test.scm")

;;; array test
;;; 2001 Jussi Piitulainen

;; (define past
;;   (let ((stones '()))
;;     (lambda stone
;;       (if (null? stone)
;;           (reverse stones)
;;           (set! stones (cons (apply (lambda (stone) stone) stone) stones))))))

(define (tail n)
  (if (< n (length (past)))
      (list-tail (past) (- (length (past)) n))
      (past)))

;;; Simple tests

(or (and (shape)
         (shape -1 -1)
         (shape -1 0)
         (shape -1 1)
         (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8))
    (error "(shape ...) failed"))

(past "shape")

(or (and (make-array (shape))
         (make-array (shape) *)
         (make-array (shape -1 -1))
         (make-array (shape -1 -1) *)
         (make-array (shape -1 1))
         (make-array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4) *))
    (error "(make-array (shape ...) [o]) failed"))

(past "make-array")

(or (and (array (shape) *)
         (array (shape -1 -1))
         (array (shape -1 1) * *)
         (array (shape 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8) *))
    (error "(array (shape ...) ...) failed"))

(past "array")

(or (and (= (array-rank (shape)) 2)
         (= (array-rank (shape -1 -1)) 2)
         (= (array-rank (shape -1 1)) 2)
         (= (array-rank (shape 1 2 3 4 5 6 7 8)) 2))
    (error "(array-rank (shape ...)) failed"))

(past "array-rank of shape")

(or (and (= (array-rank (make-array (shape))) 0)
         (= (array-rank (make-array (shape -1 -1))) 1)
         (= (array-rank (make-array (shape -1 1))) 1)
         (= (array-rank (make-array (shape 1 2 3 4 5 6 7 8))) 4))
    (error "(array-rank (make-array ...)) failed"))

(past "array-rank of make-array")

(or (and (= (array-rank (array (shape) *)) 0)
         (= (array-rank (array (shape -1 -1))) 1)
         (= (array-rank (array (shape -1 1) * *)) 1)
         (= (array-rank (array (shape 1 2 3 4 5 6 7 8) *)) 4))
    (error "(array-rank (array ...)) failed"))

(past "array-rank of array")

(or (and (= (array-start (shape -1 -1) 0) 0)
         (= (array-start (shape -1 -1) 1) 0)
         (= (array-start (shape -1 1) 0) 0)
         (= (array-start (shape -1 1) 1) 0)
         (= (array-start (shape 1 2 3 4 5 6 7 8) 0) 0)
         (= (array-start (shape 1 2 3 4 5 6 7 8) 1) 0))
    (error "(array-start (shape ...)) failed"))

(past "array-start of shape")

(or (and (= (array-end (shape -1 -1) 0) 1)
         (= (array-end (shape -1 -1) 1) 2)
         (= (array-end (shape -1 1) 0) 1)
         (= (array-end (shape -1 1) 1) 2)
         (= (array-end (shape 1 2 3 4 5 6 7 8) 0) 4)
         (= (array-end (shape 1 2 3 4 5 6 7 8) 1) 2))
    (error "(array-end (shape ...)) failed"))

(past "array-end of shape")

(or (and (= (array-start (make-array (shape -1 -1)) 0) -1)
         (= (array-start (make-array (shape -1 1)) 0) -1)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 0) 1)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 1) 3)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 2) 5)
         (= (array-start (make-array (shape 1 2 3 4 5 6 7 8)) 3) 7))
    (error "(array-start (make-array ...)) failed"))

(past "array-start of make-array")

(or (and (= (array-end (make-array (shape -1 -1)) 0) -1)
         (= (array-end (make-array (shape -1 1)) 0) 1)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 0) 2)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 1) 4)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 2) 6)
         (= (array-end (make-array (shape 1 2 3 4 5 6 7 8)) 3) 8))
    (error "(array-end (make-array ...)) failed"))

(past "array-end of make-array")

(or (and (= (array-start (array (shape -1 -1)) 0) -1)
         (= (array-start (array (shape -1 1) * *) 0) -1)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 0) 1)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 1) 3)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 2) 5)
         (= (array-start (array (shape 1 2 3 4 5 6 7 8) *) 3) 7))
    (error "(array-start (array ...)) failed"))

(past "array-start of array")

(or (and (= (array-end (array (shape -1 -1)) 0) -1)
         (= (array-end (array (shape -1 1) * *) 0) 1)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 0) 2)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 1) 4)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 2) 6)
         (= (array-end (array (shape 1 2 3 4 5 6 7 8) *) 3) 8))
    (error "(array-end (array ...)) failed"))

(past "array-end of array")

(or (and (eq? (array-ref (make-array (shape) 'a)) 'a)
         (eq? (array-ref (make-array (shape -1 1) 'b) -1) 'b)
         (eq? (array-ref (make-array (shape -1 1) 'c) 0) 'c)
         (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd) 1 3 5 7) 'd))
    (error "array-ref of make-array with arguments failed"))

(past "array-ref of make-array with arguments")

(or (and (eq? (array-ref (make-array (shape) 'a) '#()) 'a)
         (eq? (array-ref (make-array (shape -1 1) 'b) '#(-1)) 'b)
         (eq? (array-ref (make-array (shape -1 1) 'c) '#(0)) 'c)
         (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd)
                         '#(1 3 5 7))
              'd))
    (error "array-ref of make-array with vector failed"))

(past "array-ref of make-array with vector")

(or (and (eq? (array-ref (make-array (shape) 'a)
                         (array (shape 0 0)))
              'a)
         (eq? (array-ref (make-array (shape -1 1) 'b)
                         (array (shape 0 1) -1))
              'b)
         (eq? (array-ref (make-array (shape -1 1) 'c)
                         (array (shape 0 1) 0))
              'c)
         (eq? (array-ref (make-array (shape 1 2 3 4 5 6 7 8) 'd)
                         (array (shape 0 4) 1 3 5 7))
              'd))
    (error "(array-ref of make-array with array failed"))

(past "array-ref of make-array with array")

(or (and (let ((arr (make-array (shape) 'o)))
           (array-set! arr 'a)
           (eq? (array-ref arr) 'a))
         (let ((arr (make-array (shape -1 1) 'o)))
           (array-set! arr -1 'b)
           (array-set! arr 0 'c)
           (and (eq? (array-ref arr -1) 'b)
                (eq? (array-ref arr 0) 'c)))
         (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o)))
           (array-set! arr 1 3 5 7 'd)
           (eq? (array-ref arr 1 3 5 7) 'd)))
    (error "array-set! with arguments failed"))

(past "array-set! of make-array with arguments")

(or (and (let ((arr (make-array (shape) 'o)))
           (array-set! arr '#() 'a)
           (eq? (array-ref arr) 'a))
         (let ((arr (make-array (shape -1 1) 'o)))
           (array-set! arr '#(-1) 'b)
           (array-set! arr '#(0) 'c)
           (and (eq? (array-ref arr -1) 'b)
                (eq? (array-ref arr 0) 'c)))
         (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o)))
           (array-set! arr '#(1 3 5 7) 'd)
           (eq? (array-ref arr 1 3 5 7) 'd)))
    (error "array-set! with vector failed"))

(past "array-set! of make-array with vector")

(or (and (let ((arr (make-array (shape) 'o)))
           (array-set! arr 'a)
           (eq? (array-ref arr) 'a))
         (let ((arr (make-array (shape -1 1) 'o)))
           (array-set! arr (array (shape 0 1) -1) 'b)
           (array-set! arr (array (shape 0 1) 0) 'c)
           (and (eq? (array-ref arr -1) 'b)
                (eq? (array-ref arr 0) 'c)))
         (let ((arr (make-array (shape 1 2 3 4 5 6 7 8) 'o)))
           (array-set! arr (array (shape 0 4) 1 3 5 7) 'd)
           (eq? (array-ref arr 1 3 5 7) 'd)))
    (error "array-set! with arguments failed"))

(past "array-set! of make-array with array")

;;; Share and change:
;;;
;;;  org     brk     swp            box
;;;
;;;   0 1     1 2     5 6
;;; 6 a b   2 a b   3 d c   0 2 4 6 8: e
;;; 7 c d   3 e f   4 f e
;;; 8 e f

(or (let* ((org (array (shape 6 9 0 2) 'a 'b 'c 'd 'e 'f))
           (brk (share-array
                 org
                 (shape 2 4 1 3)
                 (lambda (r k)
                   (values
                    (+ 6 (* 2 (- r 2)))
                    (- k 1)))))
           (swp (share-array
                 org
                 (shape 3 5 5 7)
                 (lambda (r k)
                   (values
                    (+ 7 (- r 3))
                    (- 1 (- k 5))))))
           (box (share-array
                 swp
                 (shape 0 1 2 3 4 5 6 7 8 9)
                 (lambda _ (values 4 6))))
           (org-contents (lambda ()
                           (list (array-ref org 6 0) (array-ref org 6 1)
                                 (array-ref org 7 0) (array-ref org 7 1)
                                 (array-ref org 8 0) (array-ref org 8 1))))
           (brk-contents (lambda ()
                           (list (array-ref brk 2 1) (array-ref brk 2 2)
                                 (array-ref brk 3 1) (array-ref brk 3 2))))
           (swp-contents (lambda ()
                           (list (array-ref swp 3 5) (array-ref swp 3 6)
                                 (array-ref swp 4 5) (array-ref swp 4 6))))
           (box-contents (lambda ()
                           (list (array-ref box 0 2 4 6 8)))))
      (and (equal? (org-contents) '(a b c d e f))
           (equal? (brk-contents) '(a b e f))
           (equal? (swp-contents) '(d c f e))
           (equal? (box-contents) '(e))
           (begin (array-set! org 6 0 'x) #t)
           (equal? (org-contents) '(x b c d e f))
           (equal? (brk-contents) '(x b e f))
           (equal? (swp-contents) '(d c f e))
           (equal? (box-contents) '(e))
           (begin (array-set! brk 3 1 'y) #t)
           (equal? (org-contents) '(x b c d y f))
           (equal? (brk-contents) '(x b y f))
           (equal? (swp-contents) '(d c f y))
           (equal? (box-contents) '(y))
           (begin (array-set! swp 4 5 'z) #t)
           (equal? (org-contents) '(x b c d y z))
           (equal? (brk-contents) '(x b y z))
           (equal? (swp-contents) '(d c z y))
           (equal? (box-contents) '(y))
           (begin (array-set! box 0 2 4 6 8 'e) #t)
           (equal? (org-contents) '(x b c d e z))
           (equal? (brk-contents) '(x b e z))
           (equal? (swp-contents) '(d c z e))
           (equal? (box-contents) '(e))))
    (error "shared change failed"))

(past "shared change")

;;; Check that arrays copy the shape specification

(or (let ((shp (shape 10 12)))
      (let ((arr (make-array shp))
            (ars (array shp * *))
            (art (share-array (make-array shp) shp (lambda (k) k))))
        (array-set! shp 0 0 '?)
        (array-set! shp 0 1 '!)
        (and (= (array-rank shp) 2)
             (= (array-start shp 0) 0)
             (= (array-end shp 0) 1)
             (= (array-start shp 1) 0)
             (= (array-end shp 1) 2)
             (eq? (array-ref shp 0 0) '?)
             (eq? (array-ref shp 0 1) '!)
             (= (array-rank arr) 1)
             (= (array-start arr 0) 10)
             (= (array-end arr 0) 12)
             (= (array-rank ars) 1)
             (= (array-start ars 0) 10)
             (= (array-end ars 0) 12)
             (= (array-rank art) 1)
             (= (array-start art 0) 10)
             (= (array-end art 0) 12))))
    (error "array-set! of shape failed"))

(past "array-set! of shape")

;;; Check that index arrays work even when they share
;;;
;;; arr       ixn
;;;   5  6      0 1
;;; 4 nw ne   0 4 6
;;; 5 sw se   1 5 4

(or (let ((arr (array (shape 4 6 5 7) 'nw 'ne 'sw 'se))
          (ixn (array (shape 0 2 0 2) 4 6 5 4)))
      (let ((col0 (share-array
                   ixn
                   (shape 0 2)
                   (lambda (k)
                     (values k 0))))
            (row0 (share-array
                   ixn
                   (shape 0 2)
                   (lambda (k)
                     (values 0 k))))
            (wor1 (share-array
                   ixn
                   (shape 0 2)
                   (lambda (k)
                     (values 1 (- 1 k)))))
            (cod (share-array
                  ixn
                  (shape 0 2)
                  (lambda (k)
                    (case k
                      ((0) (values 1 0))
                      ((1) (values 0 1))))))
            (box (share-array
                  ixn
                  (shape 0 2)
                  (lambda (k)
                    (values 1 0)))))
        (and (eq? (array-ref arr col0) 'nw)
             (eq? (array-ref arr row0) 'ne)
             (eq? (array-ref arr wor1) 'nw)
             (eq? (array-ref arr cod) 'se)
             (eq? (array-ref arr box) 'sw)
             (begin
               (array-set! arr col0 'ul)
               (array-set! arr row0 'ur)
               (array-set! arr cod 'lr)
               (array-set! arr box 'll)
               #t)
             (eq? (array-ref arr 4 5) 'ul)
             (eq? (array-ref arr 4 6) 'ur)
             (eq? (array-ref arr 5 5) 'll)
             (eq? (array-ref arr 5 6) 'lr)
             (begin
               (array-set! arr wor1 'xx)
               (eq? (array-ref arr 4 5) 'xx)))))
    (error "array access with sharing index array failed"))

(past "array access with sharing index array")

;;; Check that shape arrays work even when they share
;;;
;;; arr             shp       shq       shr       shs
;;;    1  2  3  4      0  1      0  1      0  1      0  1 
;;; 1 10 12 16 20   0 10 12   0 12 20   0 10 10   0 12 12
;;; 2 10 11 12 13   1 10 11   1 11 13   1 11 12   1 12 12
;;;                                     2 12 16
;;;                                     3 13 20

(or (let ((arr (array (shape 1 3 1 5) 10 12 16 20 10 11 12 13)))
      (let ((shp (share-array
                  arr
                  (shape 0 2 0 2)
                  (lambda (r k)
                    (values (+ r 1) (+ k 1)))))
            (shq (share-array
                  arr
                  (shape 0 2 0 2)
                  (lambda (r k)
                    (values (+ r 1) (* 2 (+ 1 k))))))
            (shr (share-array
                  arr
                  (shape 0 4 0 2)
                  (lambda (r k)
                    (values (- 2 k) (+ r 1)))))
            (shs (share-array
                  arr
                  (shape 0 2 0 2)
                  (lambda (r k)
                    (values 2 3)))))
        (and (let ((arr-p (make-array shp)))
               (and (= (array-rank arr-p) 2)
                    (= (array-start arr-p 0) 10)
                    (= (array-end arr-p 0) 12)
                    (= (array-start arr-p 1) 10)
                    (= (array-end arr-p 1) 11)))
             (let ((arr-q (array shq * * * *  * * * *  * * * *  * * * *)))
               (and (= (array-rank arr-q) 2)
                    (= (array-start arr-q 0) 12)
                    (= (array-end arr-q 0) 20)
                    (= (array-start arr-q 1) 11)
                    (= (array-end arr-q 1) 13)))
             (let ((arr-r (share-array
                           (array (shape) *)
                           shr
                           (lambda _ (values)))))
               (and (= (array-rank arr-r) 4)
                    (= (array-start arr-r 0) 10)
                    (= (array-end arr-r 0) 10)
                    (= (array-start arr-r 1) 11)
                    (= (array-end arr-r 1) 12)
                    (= (array-start arr-r 2) 12)
                    (= (array-end arr-r 2) 16)
                    (= (array-start arr-r 3) 13)
                    (= (array-end arr-r 3) 20)))
             (let ((arr-s (make-array shs)))
               (and (= (array-rank arr-s) 2)
                    (= (array-start arr-s 0) 12)
                    (= (array-end arr-s 0) 12)
                    (= (array-start arr-s 1) 12)
                    (= (array-end arr-s 1) 12))))))
    (error "sharing shape array failed"))

(past "sharing shape array")

(let ((super (array (shape 4 7 4 7)
                    1 * *
                    * 2 *
                    * * 3))
      (subshape (share-array
                 (array (shape 0 2 0 3)
                        * 4 *
                        * 7 *)
                 (shape 0 1 0 2)
                 (lambda (r k)
                   (values k 1)))))
  (let ((sub (share-array super subshape (lambda (k) (values k k)))))
    ;(array-equal? subshape (shape 4 7))
    (or (and (= (array-rank subshape) 2)
             (= (array-start subshape 0) 0)
             (= (array-end subshape 0) 1)
             (= (array-start subshape 1) 0)
             (= (array-end subshape 1) 2)
             (= (array-ref subshape 0 0) 4)
             (= (array-ref subshape 0 1) 7))
        (error "sharing subshape failed"))
    ;(array-equal? sub (array (shape 4 7) 1 2 3))
    (or (and (= (array-rank sub) 1)
             (= (array-start sub 0) 4)
             (= (array-end sub 0) 7)
             (= (array-ref sub 4) 1)
             (= (array-ref sub 5) 2)
             (= (array-ref sub 6) 3))
        (error "sharing with sharing subshape failed"))))

(past "sharing with sharing subshape")

)

(check-report)

Added srfi/tests/os-environment-variables.sps.































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.  Licensed under an
;; MIT-style license.  My license is in the file named LICENSE from the original
;; collection this file is distributed with.  If this file is redistributed with
;; some other collection, my license must also be included.

#!r6rs
(import
  (rename (rnrs) (for-all andmap))
  (surfage s78 lightweight-testing)
  (surfage s98 os-environment-variables))

(check (list? (get-environment-variables))
       => #T)
(check (andmap (lambda (a)
                 (and (pair? a)
                      (string? (car a))
                      (positive? (string-length (car a)))
                      (string? (cdr a))))
               (get-environment-variables))
       => #T)
(check (andmap (lambda (a)
                 (let ((v (get-environment-variable (car a))))
                   (and (string? v)
                        (string=? v (cdr a)))))
               (get-environment-variables))
       => #T)
(assert (not (assoc "BLAH" (get-environment-variables))))
(check (get-environment-variable "BLAH")
       => #F)

(check-report)

Added srfi/tests/print-ascii.sps.















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

;;; Test format import of implementation
;;; specific routine: pretty-print

#|
LARCENY USAGE:
==> larceny -r6rs -program print-ascii.ss

IKARUS USAGE
==> ikarus --r6rs-script print-ascii.ss

|#

(import (rnrs (6))
        (surfage s48 intermediate-format-strings))
        
(define pa
 '(define (print-ascii-chart . radix+port)  
  (let ( (radix (if (null? radix+port) 16 (car radix+port)))         
         (port  (if (or (null? radix+port) (null? (cdr radix+port)))
                  (current-output-port)
                  (cadr radix+port)))  
         (max-row    15)         
         (max-col     7)         
         (max-ascii 127)         
         (max-control 31)  ; [0..31] are control codes
       )   

    (define (printable? N) ; N.B.: integer input       
      (< max-control N max-ascii)) ; control or DEL  

    (define (print-a-char N) 
      (if (printable? N) 
        (begin        
          (display #\'               port)
          (display (integer->char N) port) 
          (display #\'               port) 
          )        
        (cond ; print a control character  
         ((= N max-ascii) (display "DEL" port))  
         (else            
          (display #\^   port)    
          (display (integer->char (+ (char->integer #\@) N)) port) 
          ) )     )      
      (display " = "                    port)  
      (display (number->string N radix) port) 
      (display #\space                  port)  
      (display #\space                  port) 
      (display #\space                  port)   
      )   

    ; output the chart...  
    (newline port)   
    (let row-loop ( (row 0) )  
      (if (> row max-row)        
        (newline port)  ; done          
        (let column-loop ( (col 0) )   
          (print-a-char (+ row (* col (+ max-row 1)))) 
          (if (< col max-col)        
            (column-loop (+ col 1))   
            (begin                  
              (newline  port)      
              (row-loop (+ row 1))  
              )   )          
          )   )    
      )) )
)

(format #t "~Y~%" pa)

;;		--- E O F ---		;;

Added srfi/tests/random-conftest.sps.





















































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#!r6rs
(import (rnrs) (rnrs r5rs) (surfage s27 random-bits))

; CONFIDENCE TESTS FOR SRFI-27 "Sources of Random Bits"
; =====================================================
;
; Sebastian.Egner@philips.com, 2002.
;
; This file contains a small collection of checks for the
; implementation of SRFI-27. It is not meant to be complete
; or to test the actual properties of the underlying generator.
; It is merely meant to run the code and to check some of the
; assumptions made by specification. There is an interface to
; G. Marsaglia's DIEHARD battery of tests for random number
; generators, though.

; History of this file:
;   SE, 19-Mar-2002: initial version, based on earlier tests
;   SE, 22-Mar-2002: adapted to new procedure names
;   SE, 25-Mar-2002: more descriptive output
;   SE, 04-Apr-2002: some quick timings; check up

; (check expr)
;    evals expr and issues an error if it is not #t.

#;
(define (check expr)
  (if (not (eq? (eval expr (interaction-environment)) #t))
      (error "check failed" expr)))

; Basic Tests of the Interface
; ============================

(define (my-random-integer n)
  (let ((x (random-integer n)))
    (if (<= 0 x (- n 1))
        x
        (error "(random-integer n) returned illegal value" x))))

(define (my-random-real)
  (let ((x (random-real)))
    (if (< 0 x 1)
        x
        (error "(random-real) returned illegal value" x))))

(define (check-basics-1)

  ; generate increasingly large numbers
  (display "; generating large numbers [bits]: ")
  (do ((k 0 (+ k 1))
       (n 1 (* n 2)))
      ((> k 1024))
    (display k)
    (display " ")
    (my-random-integer n))
  (display "ok")
  (newline)

  ; generate some reals
  (display "; generating reals [1000 times]: ")
  (do ((k 0 (+ k 1))
       (x (my-random-real) (+ x (my-random-real))))
      ((= k 1000)
       x))
  (display "ok")
  (newline)

  ; get/set the state
  (display "; get/set state: ")
  (let* ((state1 (random-source-state-ref default-random-source))
         (x1 (my-random-integer (expt 2 32)))
         (state2 (random-source-state-ref default-random-source))
         (x2 (my-random-integer (expt 2 32))))
    (random-source-state-set! default-random-source state1)
    (let ((y1 (my-random-integer (expt 2 32))))
      (if (not (= x1 y1))
          (error "state get/set doesn't work" x1 y1 state1)))
    (random-source-state-set! default-random-source state2)
    (let ((y2 (my-random-integer (expt 2 32))))
      (if (not (= x2 y2))
          (error "state get/set doesn't work" x2 y2 state2))))
  (display "ok")
  (newline)

  ; randomize!
  (display "; randomize!: ")
  (let* ((state1 (random-source-state-ref default-random-source))
         (x1 (my-random-integer (expt 2 32))))
    (random-source-state-set! default-random-source state1)
    (random-source-randomize! default-random-source)
    (let ((y1 (my-random-integer (expt 2 32))))
      (if (= x1 y1)
          (error "random-source-randomize! didn't work" x1 state1))))
  (display "ok")
  (newline)

  ; pseudo-randomize!
  (display "; pseudo-randomize!: ")
  (let* ((state1 (random-source-state-ref default-random-source))
         (x1 (my-random-integer (expt 2 32))))
    (random-source-state-set! default-random-source state1)
    (random-source-pseudo-randomize! default-random-source 0 1)
    (let ((y1 (my-random-integer (expt 2 32))))
      (if (= x1 y1)
          (error "random-source-pseudo-randomize! didn't work" x1 state1)))
    (random-source-state-set! default-random-source state1)
    (random-source-pseudo-randomize! default-random-source 1 0)
    (let ((y1 (my-random-integer (expt 2 32))))
      (if (= x1 y1)
          (error "random-source-pseudo-randomize! didn't work" x1 state1))))
  (display "ok")
  (newline)
  (newline))


; Testing the MRG32k3a Generator (if implemented)
; ===============================================

; (check-mrg32k3a)
;   tests if the underlying generator is the MRG32k3a generator
;   as implemented in the reference implementation. This function
;   is useful to check whether the reference implementation computes
;   the right numbers.

(define (check-mrg32k3a)
 
  ; check if the initial state is A^16 * (1 0 0 1 0 0)
  (display "; check A^16 * (1 0 0 1 0 0)")
  (let* ((s (make-random-source))
         (state1 (random-source-state-ref s))
	 (rand (random-source-make-reals s)))
    (random-source-state-set! s '(lecuyer-mrg32k3a 1 0 0 1 0 0))
    (do ((k 0 (+ k 1)))
	((= k 16)
	 (let ((state2 (random-source-state-ref s)))
	   (if (not (equal? state1 state2))
	       (error "16-th state after (1 0 0 1 0 0) is wrong"))))
	(rand)))
  (display "ok")
  (newline)

  ; check if pseudo-randomize! advances properly
  (display "; checking (random-source-pseudo-randomize! s 1 2)")
  (let ((s (make-random-source)))
    (random-source-pseudo-randomize! s 1 2)
    (if (not (equal? (random-source-state-ref s)
		     '(lecuyer-mrg32k3a 
		       1250826159 
		       3004357423 
		        431373563 
		       3322526864 
		        623307378 
		       2983662421)))
	(error "pseudo-randomize! gives wrong result")))
  (display "ok")
  (newline)

  ; run the check published by Pierre L'Ecuyer:
  ;   Note that the reference implementation deals slightly different
  ;   with reals mapping m1-1 into 1-1/(m1+1) and not into 0 as in
  ;   L'Ecuyer's original proposal. However, for the first 10^7 reals
  ;   that makes no difference as m1-1 is not generated.
  (display "; checking (random-source-pseudo-randomize! s 1 2)...")
  (let* ((x 0.0) 
	 (s (make-random-source))
	 (rand (random-source-make-reals s)))
    (random-source-state-set!
     s
     '(lecuyer-mrg32k3a 12345 12345 12345 12345 12345 12345))
    (do ((k 0 (+ k 1)))
	((= k 10000000)
	 (if (not (< (abs (- x 5001090.95)) 0.01))
	     (error "bad sum over 10^7 reals" x)))
      (set! x (+ x (rand)))))
  (display "ok")
  (newline))


; Writing Data to DIEHARD
; =======================

; (write-diehard filename s bytes-per-call calls)
;    creates a binary file to which bytes-per-call * calls bytes are
;    written. The bytes are obtained from the random source s using
;    the range n = (expt 256 bytes-per-call).
;       The intention of write-diehard is to give implementors a 
;    '15 min.'-way of running their favourite random number generator 
;    through a pretty tough testsuite.
;
;    try: For the reference implementation, the call
;
;       (write-diehard "outfile" (make-random-source) 4 2867200)
;
;    should create a file that looks as follows (od -A x -t x1 outfile):
;
;       0000000 92 bb 7e db 1b 14 f6 bb bb 54 a1 55 c2 3e cd ca
;       0000010 23 01 20 35 06 47 65 b0 52 4c b8 c0 21 48 af 67
;       0000020 63 a9 8c 78 50 73 29 08 62 d1 22 7f a6 89 96 77
;       0000030 98 28 65 2d 2d 8b f9 52 41 be 8e 3f c5 84 0f ca
;       0000040 c0 fa 03 d6 f0 65 9d 3a 9b ab 6f fe d1 aa 5f 92
;       0000050 0f ea f6 3b 78 b9 fe ad 63 5e 49 f1 9d c9 8e 2f
;       0000060 53 a9 5d 32 d4 20 51 1d 1c 2e 82 f0 8b 26 40 c0
;       ...total length is 11468800 bytes.
;
;    The message digest is md5sum = 4df554f56cb5ed251bd04b0d50767443.
;
;    try: For the reference implementation, the call
;
;       (write-diehard "outfile" (make-random-source) 3 3822934)
;
;    should create a file that looks as follows (od -A x -t x1 outfile):
;
;       000000 bb 7e db 30 a3 49 14 f6 bb d0 f2 d0 54 a1 55 8b
;       000010 8c 03 3e cd ca a3 88 1d 01 20 35 e8 50 c8 47 65
;       000020 b0 e7 d9 28 4c b8 c0 f2 82 35 48 af 67 42 3e 8a
;       000030 a9 8c 78 12 ef b6 73 29 08 ff e9 71 d1 22 7f 52
;       000040 b8 f0 89 96 77 dc 71 86 28 65 2d c2 82 fc 8b f9
;       000050 52 d7 23 2a be 8e 3f 61 a8 99 84 0f ca 44 83 65
;       000060 fa 03 d6 c2 11 c0 65 9d 3a c2 7a dd ab 6f fe 1c
;       ...total length is 11468802 bytes.
;
;    The message digest is md5sum = 750ac219ff40c50bb2d04ff5eff9b24c.

(define (write-diehard filename s bytes-per-call calls)
  (let ((port (open-output-file filename))
        (rand (random-source-make-integers s))
        (n (expt 256 bytes-per-call)))
    (do ((i 0 (+ i 1)))
        ((= i calls)
         (close-output-port port))
      (let ((x (rand n)))
        (do ((k 0 (+ k 1))) ((= k bytes-per-call))
          (put-u8 port (modulo x 256))
          (set! x (quotient x 256)))))))

; run some tests
(check-basics-1)
(display "passed (check-basics-1)")
(newline)

(check-mrg32k3a)
(display "passed (check-mrg32k3a)")
(newline)

; (display "Generating diehard1 with expected MD5=4df554f56cb5ed251bd04b0d50767443\n")
; (write-diehard "diehard1" (make-random-source) 4 2867200)

;(display "Generating diehard2 with expected MD5=750ac219ff40c50bb2d04ff5eff9b24c\n")
; (display "Generating diehard2 with expected MD5=9c4cb1f6251efa301a98f226a76de5b9")
; (write-diehard "diehard2" (make-random-source) 3 3822934) 

Added srfi/tests/random.sps.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.  Licensed under an
;; MIT-style license.  My license is in the file named LICENSE from the original
;; collection this file is distributed with.  If this file is redistributed with
;; some other collection, my license must also be included.

#!r6rs
(import (rnrs) (surfage s27 random-bits))

(do ((i 0 (+ i 1)))
  ((= i 10) 'done)
  (display (random-integer 100))
  (newline))

(do ((i 0 (+ i 1)))
  ((= i 10) 'done)
  (display (random-real))
  (newline))

Added srfi/tests/rec-factorial.sps.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.  Licensed under an
;; MIT-style license.  My license is in the file named LICENSE from the original
;; collection this file is distributed with.  If this file is redistributed with
;; some other collection, my license must also be included.

#!r6rs
(import (rnrs) (surfage s31 rec))

(display 
 ((rec (F N) 
    (if (zero? N) 1 
      (* N (F (- N 1)))))
  10))
(newline)

Added srfi/tests/records.sps.











































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
;; Copyright (c) 2009 Derick Eddington.  All rights reserved.  Licensed under an
;; MIT-style license.  My license is in the file named LICENSE from the original
;; collection this file is distributed with.  If this file is redistributed with
;; some other collection, my license must also be included.

#!r6rs
(import
  (rnrs base) ; no R6RS records
  (only (rnrs io simple) display write newline)
  (surfage s9 records))

(define-record-type thing
  (make-thing x)
  thing?
  (x thing-x)
  (y thing-y set-thing-y!))

(define t (make-thing 123))
(display "t => ") (write t) (newline)
(set-thing-y! t 'blah)
(display "t => ") (write t) (newline)

Added srfi/tests/s26-cut.sps.











































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!r6rs
; CONFIDENCE TEST FOR IMPLEMENTATION OF SRFI-26
; =============================================
;
; Sebastian.Egner@philips.com, 3-Jun-2002.
;
; This file checks a few assertions about the implementation.
; If you run it and no error message is issued, the implementation
; is correct on the cases that have been tested.
;
; compliance:
;   Scheme R5RS with
;     SRFI-23: error
;
; loading this file into Scheme 48 0.57 after 'cut.scm' has been loaded:
;   ,open srfi-23
;   ,load check.scm

; (check expr)
;    evals expr and issues an error if it is not #t.

;; Extended by Derick Eddington to test free-identifier=? of <> and <...>.

(import
  (rnrs)
  (rnrs eval))

(define (check expr)
  (if (not (eq? (eval expr (environment '(rnrs) '(surfage s26 cut)))
                #t))
      (assertion-violation 'check "check failed" expr)))

; (check-all)
;    runs several tests on cut and reports.

(define (check-all)
  (for-each 
   check
   '( ; cuts
     (equal? ((cut list)) '())
     (equal? ((cut list <...>)) '())
     (equal? ((cut list 1)) '(1))
     (equal? ((cut list <>) 1) '(1))
     (equal? ((cut list <...>) 1) '(1))
     (equal? ((cut list 1 2)) '(1 2))
     (equal? ((cut list 1 <>) 2) '(1 2))
     (equal? ((cut list 1 <...>) 2) '(1 2))
     (equal? ((cut list 1 <...>) 2 3 4) '(1 2 3 4))
     (equal? ((cut list 1 <> 3 <>) 2 4) '(1 2 3 4))
     (equal? ((cut list 1 <> 3 <...>) 2 4 5 6) '(1 2 3 4 5 6))
     (equal? (let* ((x 'wrong) (y (cut list x))) (set! x 'ok) (y)) '(ok))
     (equal? 
      (let ((a 0))
	(map (cut + (begin (set! a (+ a 1)) a) <>)
	     '(1 2))
	a)
      2)
     (equal?
      (let* ((<> 'wrong) (f (cut list <> <...>)))
        (set! <> 'ok)
        (f 1 2))
      '(ok 1 2))
     (equal?
      (let* ((<...> 'wrong) (f (cut list <> <...>)))
        (set! <...> 'ok)
        (f 1))
      '(1 ok))
      ; cutes
     (equal? ((cute list)) '())
     (equal? ((cute list <...>)) '())
     (equal? ((cute list 1)) '(1))
     (equal? ((cute list <>) 1) '(1))
     (equal? ((cute list <...>) 1) '(1))
     (equal? ((cute list 1 2)) '(1 2))
     (equal? ((cute list 1 <>) 2) '(1 2))
     (equal? ((cute list 1 <...>) 2) '(1 2))
     (equal? ((cute list 1 <...>) 2 3 4) '(1 2 3 4))
     (equal? ((cute list 1 <> 3 <>) 2 4) '(1 2 3 4))
     (equal? ((cute list 1 <> 3 <...>) 2 4 5 6) '(1 2 3 4 5 6))
     (equal? 
      (let ((a 0))
	(map (cute + (begin (set! a (+ a 1)) a) <>)
	     '(1 2))
	a)
      1)
     (equal?
      (let* ((<> 'ok) (f (cute list <> <...>)))
        (set! <> 'wrong)
        (f 1 2))
      '(ok 1 2))
     (equal?
      (let* ((<...> 'ok) (f (cute list <> <...>)))
        (set! <...> 'wrong)
        (f 1))
      '(1 ok))
     )))

; run the checks when loading
(check-all)
;; (display "passed")
;; (newline)

Added srfi/tests/s78-lightweight-testing.sps.























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!r6rs
; <PLAINTEXT>
; Copyright (c) 2005-2006 Sebastian Egner.
; 
; Permission is hereby granted, free of charge, to any person obtaining
; a copy of this software and associated documentation files (the
; ``Software''), to deal in the Software without restriction, including
; without limitation the rights to use, copy, modify, merge, publish,
; distribute, sublicense, and/or sell copies of the Software, and to
; permit persons to whom the Software is furnished to do so, subject to
; the following conditions:
; 
; The above copyright notice and this permission notice shall be
; included in all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; 
; -----------------------------------------------------------------------

; Lightweight testing (examples)
; ==============================
;
; Sebastian.Egner@philips.com
; in R5RS + SRFI 23 (error) + SRFI 42 (comprehensions)
;
; history of this file:
;   SE, 25-Oct-2004: first version

; -- portability --

; PLT:
; (require (lib "23.ss" "srfi") (lib "42.ss" "srfi")) (load "check.scm")
; (load "examples.scm")

; Scheme48: 
; ,open srfi-23 srfi-42
; ,load check.scm examples.scm

(import 
  (rnrs)
  (rnrs r5rs)
  (surfage s42 eager-comprehensions)
  (surfage s78 lightweight-testing))

(check-set-mode! 'report-failed)

; -- simple test --

(check (+ 1 1) => 2)
;; (check (+ 1 1) => 3) ; fails

; -- different equality predicate --

(check (vector 1) => (vector 1))

;; (check (vector 1) (=> eq?) (vector 1)) ; fails

; -- parametric tests --

(check-ec (+ 1 1) => 2)

(check-ec (: x 10) (+ x 1) => (+ x 1) (x))

;; (check-ec (: e 100) (positive? (expt 2 e)) => #t (e)) ; fails on fixnums

;; (check-ec (: e 100) (:let x (expt 2.0 e)) (= (+ x 1) x) => #f (x)) ; fails

;; (check-ec (: e 100) (:let x (expt 2.0 e)) (= (+ x 1) x) => #f)

(check-ec (: x 10) (: y 10) (: z 10)
          (* x (+ y z)) => (+ (* x y) (* x z))
          (x y z)) ; passes with 10^3 cases checked

; -- toy examples --

(define (fib n)
  (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))

(check (fib 1) => 1)
(check (fib 2) => 1)
(check-ec (: n 1 31) (even? (fib n)) => (= (modulo n 3) 0) (n))

; -- reporting --

;; (check-report)

Added srfi/tests/testing.sps.































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
;;;
;;;  This is a test suite written in the notation of 
;;;  SRFI-64, A Scheme API for test suites
;;;

#!r6rs
(import
  (except (rnrs base) error)
  (rnrs lists)
  (surfage s64 testing))

(define (error msg)
  (assertion-violation "(surfage s64 testing) test program" msg))

(test-begin "SRFI 64 - Meta-Test Suite")

;;;
;;;  Ironically, in order to set up the meta-test environment,
;;;  we have to invoke one of the most sophisticated features:
;;;  custom test runners
;;;

;;;  The `prop-runner' invokes `thunk' in the context of a new
;;;  test runner, and returns the indicated properties of the 
;;;  last-executed test result.

(define (prop-runner props thunk)
  (let ((r (test-runner-null))
        (plist '()))
    ;;
    (test-runner-on-test-end!
     r
     (lambda (runner)
       (set! plist (test-result-alist runner))))
    ;;
    (test-with-runner r (thunk))
    ;; reorder the properties so they are in the order
    ;; given by `props'.  Note that any property listed in `props'
    ;; that is not in the property alist will occur as #f
    (map (lambda (k)
           (assq k plist))
         props)))

;;;  `on-test-runner' creates a null test runner and then
;;;  arranged for `visit' to be called with the runner
;;;  whenever a test is run.  The results of the calls to
;;;  `visit' are returned in a list

(define (on-test-runner thunk visit)
  (let ((r (test-runner-null))
        (results '()))
    ;;
    (test-runner-on-test-end!
     r
     (lambda (runner)
       (set! results (cons (visit r) results))))
    ;;
    (test-with-runner r (thunk))
    (reverse results)))

;;;
;;;  The `triv-runner' invokes `thunk'
;;;  and returns a list of 6 lists, the first 5 of which
;;;  are a list of the names of the tests that, respectively,
;;;  PASS, FAIL, XFAIL, XPASS, and SKIP.
;;;  The last item is a list of counts.
;;;

(define (triv-runner thunk)
  (let ((r (test-runner-null))
        (accum-pass '())
        (accum-fail '())
        (accum-xfail '())
        (accum-xpass '())
        (accum-skip '()))
    ;;
    (test-runner-on-bad-count!
     r
     (lambda (runner count expected-count)
       (error (string-append "bad count " (number->string count)
			     " but expected "
			     (number->string expected-count)))))
    (test-runner-on-bad-end-name!
     r
     (lambda (runner begin end)
       (error (string-append "bad end grojup name " end
			     " but expected " begin))))
    (test-runner-on-test-end! 
     r 
     (lambda (runner)
       (let ((n (test-runner-test-name runner)))
         (case (test-result-kind runner)
           ((pass) (set! accum-pass (cons n accum-pass)))
           ((fail) (set! accum-fail (cons n accum-fail)))
           ((xpass) (set! accum-xpass (cons n accum-xpass)))
           ((xfail) (set! accum-xfail (cons n accum-xfail)))
           ((skip) (set! accum-skip (cons n accum-skip)))))))
    ;;
    (test-with-runner r (thunk))
    (list (reverse accum-pass)    ; passed as expected
          (reverse accum-fail)    ; failed, but was expected to pass
          (reverse accum-xfail)   ; failed as expected
          (reverse accum-xpass)   ; passed, but was expected to fail
          (reverse accum-skip)    ; was not executed
          (list (test-runner-pass-count r)
                (test-runner-fail-count r)
                (test-runner-xfail-count r)
                (test-runner-xpass-count r)
                (test-runner-skip-count r)))))

(define (path-revealing-runner thunk)
  (let ((r (test-runner-null))
        (seq '()))
    ;;
    (test-runner-on-test-end! 
     r 
     (lambda (runner)
       (set! seq (cons (list (test-runner-group-path runner)
                             (test-runner-test-name runner))
                       seq))))
    (test-with-runner r (thunk))
    (reverse seq)))

;;;
;;;  Now we can start testing compliance with SRFI-64
;;;

(test-begin "1. Simple test-cases")

(test-begin "1.1. test-assert")

(define (t)
  (triv-runner
   (lambda ()
     (test-assert "a" #t)
     (test-assert "b" #f))))

(test-equal
 "1.1.1. Very simple"
 '(("a") ("b") () () () (1 1 0 0 0))
 (t))

(test-equal
 "1.1.2. A test with no name"
 '(("a") ("") () () () (1 1 0 0 0))
 (triv-runner (lambda () (test-assert "a" #t) (test-assert #f))))

(test-equal
 "1.1.3. Tests can have the same name"
 '(("a" "a") () () () () (2 0 0 0 0))
 (triv-runner (lambda () (test-assert "a" #t) (test-assert "a" #t))))

(define (choke)
  (vector-ref '#(1 2) 3))

(test-equal
 "1.1.4. One way to FAIL is to throw an error"
 '(() ("a") () () () (0 1 0 0 0))
 (triv-runner (lambda () (test-assert "a" (choke)))))

(test-end);1.1

(test-begin "1.2. test-eqv")

(define (mean x y)
  (/ (+ x y) 2.0))

(test-equal
 "1.2.1.  Simple numerical equivalence"
 '(("c") ("a" "b") () () () (1 2 0 0 0))
 (triv-runner
  (lambda ()
    (test-eqv "a" (mean 3 5) 4)
    (test-eqv "b" (mean 3 5) 4.5)
    (test-eqv "c" (mean 3 5) 4.0))))

(test-end);1.2

(test-end "1. Simple test-cases")

;;;
;;;
;;;

(test-begin "2. Tests for catching errors")

(test-begin "2.1. test-error")

(test-equal
 "2.1.1. Baseline test; PASS with no optional args"
 '(("") () () () () (1 0 0 0 0))
 (triv-runner
  (lambda ()
    ;; PASS
    (test-error (vector-ref '#(1 2) 9)))))

(test-equal
 "2.1.2. Baseline test; FAIL with no optional args"
 '(() ("") () () () (0 1 0 0 0))
 (triv-runner
  (lambda ()
    ;; FAIL: the expr does not raise an error and `test-error' is
    ;;       claiming that it will, so this test should FAIL
    (test-error (vector-ref '#(1 2) 0)))))

(test-equal
 "2.1.3. PASS with a test name and error type"
 '(("a") () () () () (1 0 0 0 0))
 (triv-runner
  (lambda ()
    ;; PASS
    (test-error "a" #t (vector-ref '#(1 2) 9)))))

(test-end "2.1. test-error")

(test-end "2. Tests for catching errors")

;;;
;;;
;;;

(test-begin "3. Test groups and paths")

(test-equal
 "3.1. test-begin with unspecific test-end"
 '(("b") () () () () (1 0 0 0 0))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-assert "b" #t)
    (test-end))))

(test-equal
 "3.2. test-begin with name-matching test-end"
 '(("b") () () () () (1 0 0 0 0))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-assert "b" #t)
    (test-end "a"))))

;;; since the error raised by `test-end' on a mismatch is not a test
;;; error, we actually expect the triv-runner itself to fail

(test-error
 "3.3. test-begin with mismatched test-end"
#t
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-assert "b" #t)
    (test-end "x"))))

(test-equal
 "3.4. test-begin with name and count"
 '(("b" "c") () () () () (2 0 0 0 0))
 (triv-runner
  (lambda ()
    (test-begin "a" 2)
    (test-assert "b" #t)
    (test-assert "c" #t)
    (test-end "a"))))

;; similarly here, a mismatched count is a lexical error
;; and not a test failure...

(test-error
 "3.5. test-begin with mismatched count"
 #t
 (triv-runner
  (lambda ()
    (test-begin "a" 99)
    (test-assert "b" #t)
    (test-end "a"))))

(test-equal
 "3.6. introspecting on the group path"
 '((() "w")
   (("a" "b") "x")
   (("a" "b") "y")
   (("a") "z"))
 ;;
 ;;  `path-revealing-runner' is designed to return a list
 ;;  of the tests executed, in order.  Each entry is a list
 ;;  (GROUP-PATH TEST-NAME), and each GROUP-PATH is a list
 ;;  of test groups starting from the topmost
 ;;
 (path-revealing-runner
  (lambda ()
    (test-assert "w" #t)
    (test-begin "a")
    (test-begin "b")
    (test-assert "x" #t)
    (test-assert "y" #t)
    (test-end)
    (test-assert "z" #t))))


(test-end "3. Test groups and paths")

;;;
;;;
;;;

(test-begin "4. Handling set-up and cleanup")

(test-equal "4.1. Normal exit path"
             '(in 1 2 out)
             (let ((ex '()))
               (define (do s)
                 (set! ex (cons s ex)))
               ;;
               (triv-runner
                (lambda ()
                  (test-group-with-cleanup
                   "foo"
                   (do 'in)
                   (do 1)
                   (do 2)
                   (do 'out))))
               (reverse ex)))
               
(test-equal "4.2. Exception exit path"
             '(in 1 out)
             (let ((ex '()))
               (define (do s)
                 (set! ex (cons s ex)))
               ;;
               ;; the outer runner is to run the `test-error' in, to
               ;; catch the exception raised in the inner runner,
               ;; since we don't want to depend on any other
               ;; exception-catching support
               ;;
               (triv-runner
                (lambda ()
                  (test-error
                   (triv-runner
                    (lambda ()
                      (test-group-with-cleanup
                       "foo"
                       (do 'in) (test-assert #t)
                       (do 1)   (test-assert #t)
                       (choke)  (test-assert #t)
                       (do 2)   (test-assert #t)
                       (do 'out)))))))
               (reverse ex)))

(test-end "4. Handling set-up and cleanup")

;;;
;;;
;;;

(test-begin "5. Test specifiers")

(test-begin "5.1. test-match-named")

(test-equal "5.1.1. match test names"
            '(("y") () () () ("x") (1 0 0 0 1))
            (triv-runner
             (lambda ()
               (test-skip (test-match-name "x"))
               (test-assert "x" #t)
               (test-assert "y" #t))))

(test-equal "5.1.2. but not group names"
            '(("z") () () () () (1 0 0 0 0))
            (triv-runner
             (lambda ()
               (test-skip (test-match-name "x"))
               (test-begin "x")
               (test-assert "z" #t)
               (test-end))))

(test-end)

(test-begin "5.2. test-match-nth")
;; See also: [6.4. Short-circuit evaluation]

(test-equal "5.2.1. skip the nth one after"
            '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
            (triv-runner
             (lambda ()
               (test-assert "v" #t)
               (test-skip (test-match-nth 2))
               (test-assert "w" #t)             ; 1
               (test-assert "x" #t)             ; 2 SKIP
               (test-assert "y" #t)             ; 3
               (test-assert "z" #t))))          ; 4

(test-equal "5.2.2. skip m, starting at n"
            '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
            (triv-runner
             (lambda ()
               (test-assert "v" #t)
               (test-skip (test-match-nth 2 2))
               (test-assert "w" #t)             ; 1
               (test-assert "x" #t)             ; 2 SKIP
               (test-assert "y" #t)             ; 3 SKIP
               (test-assert "z" #t))))          ; 4

(test-end)

(test-begin "5.3. test-match-any")
(test-equal "5.3.1. basic disjunction"
            '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
            (triv-runner
             (lambda ()
               (test-assert "v" #t)
               (test-skip (test-match-any (test-match-nth 3)
                                          (test-match-name "x")))
               (test-assert "w" #t)             ; 1
               (test-assert "x" #t)             ; 2 SKIP(NAME)
               (test-assert "y" #t)             ; 3 SKIP(COUNT)
               (test-assert "z" #t))))          ; 4

(test-equal "5.3.2. disjunction is commutative"
            '(("v" "w" "z") () () () ("x" "y") (3 0 0 0 2))
            (triv-runner
             (lambda ()
               (test-assert "v" #t)
               (test-skip (test-match-any (test-match-name "x")
                                          (test-match-nth 3)))
               (test-assert "w" #t)             ; 1
               (test-assert "x" #t)             ; 2 SKIP(NAME)
               (test-assert "y" #t)             ; 3 SKIP(COUNT)
               (test-assert "z" #t))))          ; 4

(test-end)

(test-begin "5.4. test-match-all")
(test-equal "5.4.1. basic conjunction"
            '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
            (triv-runner
             (lambda ()
               (test-assert "v" #t)
               (test-skip (test-match-all (test-match-nth 2 2)
                                          (test-match-name "x")))
               (test-assert "w" #t)             ; 1
               (test-assert "x" #t)             ; 2 SKIP(NAME) & SKIP(COUNT)
               (test-assert "y" #t)             ; 3 SKIP(COUNT)
               (test-assert "z" #t))))          ; 4

(test-equal "5.4.2. conjunction is commutative"
            '(("v" "w" "y" "z") () () () ("x") (4 0 0 0 1))
            (triv-runner
             (lambda ()
               (test-assert "v" #t)
               (test-skip (test-match-all (test-match-name "x")
                                          (test-match-nth 2 2)))
               (test-assert "w" #t)             ; 1
               (test-assert "x" #t)             ; 2 SKIP(NAME) & SKIP(COUNT)
               (test-assert "y" #t)             ; 3 SKIP(COUNT)
               (test-assert "z" #t))))          ; 4

(test-end)

(test-end "5. Test specifiers")

;;;
;;;
;;;

(test-begin "6. Skipping selected tests")

(test-equal
 "6.1. Skip by specifier - match-name"
 '(("x") () () () ("y") (1 0 0 0 1))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip (test-match-name "y"))
    (test-assert "x" #t)      ; PASS
    (test-assert "y" #f)      ; SKIP
    (test-end))))

(test-equal
 "6.2. Shorthand specifiers"
 '(("x") () () () ("y") (1 0 0 0 1))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip "y")
    (test-assert "x" #t)      ; PASS
    (test-assert "y" #f)      ; SKIP
    (test-end))))

(test-begin "6.3. Specifier Stack")

(test-equal
 "6.3.1. Clearing the Specifier Stack"
 '(("x" "x") ("y") () () ("y") (2 1 0 0 1))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip "y")
    (test-assert "x" #t)      ; PASS
    (test-assert "y" #f)      ; SKIP
    (test-end)
    (test-begin "b")
    (test-assert "x" #t)      ; PASS
    (test-assert "y" #f)      ; FAIL
    (test-end))))

(test-equal
 "6.3.2. Inheriting the Specifier Stack"
 '(("x" "x") () () () ("y" "y") (2 0 0 0 2))
 (triv-runner
  (lambda ()
    (test-skip "y")
    (test-begin "a")
    (test-assert "x" #t)      ; PASS
    (test-assert "y" #f)      ; SKIP
    (test-end)
    (test-begin "b")
    (test-assert "x" #t)      ; PASS
    (test-assert "y" #f)      ; SKIP
    (test-end))))

(test-end);6.3

(test-begin "6.4. Short-circuit evaluation")

(test-equal
 "6.4.1. In test-match-all"
 '(("x") ("y" "x" "z") () () ("y") (1 3 0 0 1))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip (test-match-all "y" (test-match-nth 2)))
    ;; let's label the substructure forms so we can
    ;; see which one `test-match-nth' is going to skip
    ;;                        ; #   "y"  2   result
    (test-assert "x" #t)      ; 1 - #f   #f  PASS   
    (test-assert "y" #f)      ; 2 - #t   #t  SKIP 
    (test-assert "y" #f)      ; 3 - #t   #f  FAIL
    (test-assert "x" #f)      ; 4 - #f   #f  FAIL
    (test-assert "z" #f)      ; 5 - #f   #f  FAIL
    (test-end))))

(test-equal
 "6.4.2. In separate skip-list entries"
 '(("x") ("x" "z") () () ("y" "y") (1 2 0 0 2))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip "y")
    (test-skip (test-match-nth 2))
    ;; let's label the substructure forms so we can
    ;; see which one `test-match-nth' is going to skip
    ;;                        ; #   "y"  2   result
    (test-assert "x" #t)      ; 1 - #f   #f  PASS   
    (test-assert "y" #f)      ; 2 - #t   #t  SKIP 
    (test-assert "y" #f)      ; 3 - #t   #f  SKIP
    (test-assert "x" #f)      ; 4 - #f   #f  FAIL
    (test-assert "z" #f)      ; 5 - #f   #f  FAIL
    (test-end))))

(test-begin "6.4.3. Skipping test suites")

(test-equal
 "6.4.3.1. Introduced using 'test-begin'"
 '(("x") () () () () (1 0 0 0 0))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip "b")
    (test-begin "b")            ; not skipped
    (test-assert "x" #t)
    (test-end "b")
    (test-end "a"))))

(test-expect-fail 1) ;; ???
(test-equal
 "6.4.3.2. Introduced using 'test-group'"
 '(() () () () () (0 0 0 0 1))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip "b")
    (test-group 
     "b"            ; skipped
     (test-assert "x" #t))
    (test-end "a"))))

(test-equal
 "6.4.3.3. Non-skipped 'test-group'"
 '(("x") () () () () (1 0 0 0 0))
 (triv-runner
  (lambda ()
    (test-begin "a")
    (test-skip "c")
    (test-group "b" (test-assert "x" #t))
    (test-end "a"))))

(test-end) ; 6.4.3
 
(test-end);6.4

(test-end "6. Skipping selected tests")

;;;
;;;
;;;

(test-begin "7. Expected failures")

(test-equal "7.1. Simple example"
            '(() ("x") ("z") () () (0 1 1 0 0))
            (triv-runner
             (lambda ()
               (test-assert "x" #f)
               (test-expect-fail "z")
               (test-assert "z" #f))))

(test-equal "7.2. Expected exception"
            '(() ("x") ("z") () () (0 1 1 0 0))
            (triv-runner
             (lambda ()
               (test-assert "x" #f)
               (test-expect-fail "z")
               (test-assert "z" (choke)))))

(test-equal "7.3. Unexpectedly PASS"
            '(() () ("y") ("x") () (0 0 1 1 0))
            (triv-runner
             (lambda ()
               (test-expect-fail "x")
               (test-expect-fail "y")
               (test-assert "x" #t)
               (test-assert "y" #f))))
               


(test-end "7. Expected failures")

;;;
;;;
;;;

(test-begin "8. Test-runner")

;;;
;;;  Because we want this test suite to be accurate even
;;;  when the underlying implementation chooses to use, e.g.,
;;;  a global variable to implement what could be thread variables
;;;  or SRFI-39 parameter objects, we really need to save and restore
;;;  their state ourselves
;;;
(define (with-factory-saved thunk)
  (let* ((saved (test-runner-factory))
         (result (thunk)))
    (test-runner-factory saved)
    result))

(test-begin "8.1. test-runner-current")
(test-assert "8.1.1. automatically restored"
             (let ((a 0)
                   (b 1)
                   (c 2))
               ;
               (triv-runner
                (lambda ()
                  (set! a (test-runner-current))
                  ;;
                  (triv-runner
                   (lambda ()
                     (set! b (test-runner-current))))
                  ;;
                  (set! c (test-runner-current))))
               ;;
               (and (eq? a c)
                    (not (eq? a b)))))
              
(test-end)

(test-begin "8.2. test-runner-simple")
(test-assert "8.2.1. default on-test hook"
             (eq? (test-runner-on-test-end (test-runner-simple))
                  test-on-test-end-simple))
(test-assert "8.2.2. default on-final hook"
             (eq? (test-runner-on-final (test-runner-simple))
                  test-on-final-simple))
(test-end)

(test-begin "8.3. test-runner-factory")

(test-assert "8.3.1. default factory"
             (eq? (test-runner-factory) test-runner-simple))

(test-assert "8.3.2. settable factory"
             (with-factory-saved
              (lambda ()
                (test-runner-factory test-runner-null)
                ;; we have no way, without bringing in other SRFIs,
                ;; to make sure the following doesn't print anything,
                ;; but it shouldn't:
                (test-with-runner
                 (test-runner-create)
                 (lambda ()
                   (test-begin "a")
                   (test-assert #t)             ; pass
                   (test-assert #f)             ; fail
                   (test-assert (vector-ref '#(3) 10))  ; fail with error
                   (test-end "a")))
                (eq? (test-runner-factory) test-runner-null))))
                
(test-end)

;;; This got tested about as well as it could in 8.3.2

(test-begin "8.4. test-runner-create")
(test-end)

;;; This got tested about as well as it could in 8.3.2 

(test-begin "8.5. test-runner-factory")
(test-end)

(test-begin "8.6. test-apply")
(test-equal "8.6.1. Simple (form 1) test-apply"
            '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
            (triv-runner
             (lambda ()
               (test-begin "a")
               (test-assert "w" #t)
               (test-apply
                (test-match-name "p")
                (lambda ()
                  (test-begin "p")
                  (test-assert "x" #t)
                  (test-end)
                  (test-begin "z")
                  (test-assert "p" #t)  ; only this one should execute in here
                  (test-end)))
               (test-assert "v" #t))))

(test-equal "8.6.2. Simple (form 2) test-apply"
            '(("w" "p" "v") () () () ("x") (3 0 0 0 1))
            (triv-runner
             (lambda ()
               (test-begin "a")
               (test-assert "w" #t)
               (test-apply
                (test-runner-current)
                (test-match-name "p")
                (lambda ()
                  (test-begin "p")
                  (test-assert "x" #t)
                  (test-end)
                  (test-begin "z")
                  (test-assert "p" #t)  ; only this one should execute in here
                  (test-end)))
               (test-assert "v" #t))))

(test-expect-fail 1) ;; depends on all test-match-nth being called.
(test-equal "8.6.3. test-apply with skips"
            '(("w" "q" "v") () () () ("x" "p" "x") (3 0 0 0 3))
            (triv-runner
             (lambda ()
               (test-begin "a")
               (test-assert "w" #t)
               (test-skip (test-match-nth 2))
               (test-skip (test-match-nth 4))
               (test-apply
                (test-runner-current)
                (test-match-name "p")
                (test-match-name "q")
                (lambda ()
                                        ; only execute if SKIP=no and APPLY=yes
                  (test-assert "x" #t)  ; # 1 SKIP=no  APPLY=no
                  (test-assert "p" #t)  ; # 2 SKIP=yes APPLY=yes
                  (test-assert "q" #t)  ; # 3 SKIP=no  APPLY=yes
                  (test-assert "x" #f)  ; # 4 SKIP=yes APPLY=no
                  0))
               (test-assert "v" #t))))

;;;  Unfortunately, since there is no way to UNBIND the current test runner,
;;;  there is no way to test the behavior of `test-apply' in the absence
;;;  of a current runner within our little meta-test framework.
;;;
;;;  To test the behavior manually, you should be able to invoke:
;;;
;;;     (test-apply "a" (lambda () (test-assert "a" #t)))
;;;
;;;  from the top level (with SRFI 64 available) and it should create a
;;;  new, default (simple) test runner.

(test-end)

;;;  This entire suite depends heavily on 'test-with-runner'.  If it didn't
;;;  work, this suite would probably go down in flames
(test-begin "8.7. test-with-runner")
(test-end)

;;;  Again, this suite depends heavily on many of the test-runner
;;;  components.  We'll just test those that aren't being exercised
;;;  by the meta-test framework
(test-begin "8.8. test-runner components")

(define (auxtrack-runner thunk)
  (let ((r (test-runner-null)))
    (test-runner-aux-value! r '())
    (test-runner-on-test-end! r (lambda (r)
                              (test-runner-aux-value!
                               r
                               (cons (test-runner-test-name r)
                                     (test-runner-aux-value r)))))
    (test-with-runner r (thunk))
    (reverse (test-runner-aux-value r))))

(test-equal "8.8.1. test-runner-aux-value"
            '("x" "" "y")
            (auxtrack-runner
             (lambda ()
               (test-assert "x" #t)
               (test-begin "a")
               (test-assert #t)
               (test-end)
               (test-assert "y" #f))))

(test-end) ; 8.8

(test-end "8. Test-runner")

(test-begin "9. Test Result Properties")

(test-begin "9.1. test-result-alist")

(define (symbol-alist? l)
  (if (null? l)
      #t
      (and (pair? l)
           (pair? (car l))
           (symbol? (caar l))
           (symbol-alist? (cdr l)))))

;;; check the various syntactic forms

(test-assert (symbol-alist?
              (car (on-test-runner
                    (lambda ()
                      (test-assert #t))
                    (lambda (r)
                      (test-result-alist r))))))

(test-assert (symbol-alist?
              (car (on-test-runner
                    (lambda ()
                      (test-assert #t))
                    (lambda (r)
                      (test-result-alist r))))))

;;; check to make sure the required properties are returned

(test-equal '((result-kind . pass))
	    (prop-runner
             '(result-kind)
             (lambda ()
               (test-assert #t)))
	    )

(test-equal 
            '((result-kind . fail)
              (expected-value . 2)
              (actual-value . 3))
	    (prop-runner
             '(result-kind expected-value actual-value)
             (lambda ()
               (test-equal 2 (+ 1 2)))))

(test-end "9.1. test-result-alist")

(test-begin "9.2. test-result-ref")

(test-equal '(pass)
	    (on-test-runner
             (lambda ()
               (test-assert #t))
             (lambda (r)
               (test-result-ref r 'result-kind))))

(test-equal '(pass)
	    (on-test-runner
             (lambda ()
               (test-assert #t))
             (lambda (r)
               (test-result-ref r 'result-kind))))

(test-equal '(fail pass)
	    (on-test-runner
             (lambda ()
               (test-assert (= 1 2))
               (test-assert (= 1 1)))
             (lambda (r)
               (test-result-ref r 'result-kind))))

(test-end "9.2. test-result-ref")

(test-begin "9.3. test-result-set!")

(test-equal '(100 100)
	    (on-test-runner
             (lambda ()
               (test-assert (= 1 2))
               (test-assert (= 1 1)))
             (lambda (r)
               (test-result-set! r 'foo 100)
               (test-result-ref r 'foo))))

(test-end "9.3. test-result-set!")

(test-end "9. Test Result Properties")

;;;
;;;
;;;

#|  Time to stop having fun...

(test-begin "9. For fun, some meta-test errors")

(test-equal
 "9.1. Really PASSes, but test like it should FAIL"
 '(() ("b") () () ())
 (triv-runner
  (lambda ()
    (test-assert "b" #t))))

(test-expect-fail "9.2. Expect to FAIL and do so")
(test-expect-fail "9.3. Expect to FAIL but PASS")
(test-skip "9.4. SKIP this one")

(test-assert "9.2. Expect to FAIL and do so" #f)
(test-assert "9.3. Expect to FAIL but PASS" #t)
(test-assert "9.4. SKIP this one" #t)

(test-end)
 |#

(test-end "SRFI 64 - Meta-Test Suite")

;;;

Added srfi/tests/time.sps.























































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
;;; simple test procedures
#!r6rs
(import
  (rnrs)
  (rnrs mutable-pairs)
  (surfage s19 time)
  (surfage s48 intermediate-format-strings))

(define (printf fmt-str . args)
  (display (apply format fmt-str args)))

(define s19-tests (list))

(define (define-s19-test! name thunk)
  (let ((name (if (symbol? name) name (string->symbol name)))
	(pr (assoc name s19-tests)))
    (if pr
	(set-cdr! pr thunk)
	(set! s19-tests (append s19-tests (list (cons name thunk)))))))

(define (run-s19-test name thunk verbose)
  (if verbose (begin (display ";;; Running ") (display name)))
  (let ((result (thunk)))
    (if verbose (begin (display ": ") (display (not (not result))) (newline)))
    result))

(define (run-s19-tests . verbose)
  (let ((runs 0) (goods 0) (bads 0) (verbose (if (cdr verbose) (cdr verbose) #f)))
    (for-each (lambda (pr)
		(set! runs (+ runs 1))
		(if (run-s19-test (car pr) (cdr pr) verbose)
		    (set! goods (+ goods 1))
		    (set! bads (+ bads 1))))
	      s19-tests)
    (if verbose
	(begin
	  (display ";;; Results: Runs: ")
	  (display runs)
	  (display "; Goods: ")
	  (display goods)
	  (display "; Bads: ")
	  (display bads)
	  (if (> runs 0)
	      (begin
		(display "; Pass rate: ")
		(display (/ goods runs)))
	      (display "; No tests."))
	  (newline)))
    (values runs goods bads)))

(set! s19-tests (list))

(define-s19-test! "Creating time structures"
  (lambda ()
    (not (null? (list (current-time 'time-tai)
		      (current-time 'time-utc)
		      (current-time 'time-monotonic)
		      #|(current-time 'time-thread)
		      (current-time 'time-process)|#)))))

(define-s19-test! "Testing time resolutions"
  (lambda ()
    (not (null? (list (time-resolution 'time-tai)
		      (time-resolution 'time-utc)
		      (time-resolution 'time-monotonic)
		      #|(time-resolution 'time-thread)
		      (time-resolution 'time-process)|#)))))

(define-s19-test! "Time comparisons (time=?, etc.)"
  (lambda ()
    (let ((t1 (make-time 'time-utc 0 1))
	  (t2 (make-time 'time-utc 0 1))
	  (t3 (make-time 'time-utc 0 2))
	  (t11 (make-time 'time-utc 1001 1))
	  (t12 (make-time 'time-utc 1001 1))
	  (t13 (make-time 'time-utc 1001 2))
	  )
      (and (time=? t1 t2)
	   (time>? t3 t2)
	   (time<? t2 t3)
	   (time>=? t1 t2)
	   (time>=? t3 t2)
	   (time<=? t1 t2)
	   (time<=? t2 t3)
	   (time=? t11 t12)
	   (time>? t13 t12)
	   (time<? t12 t13)
	   (time>=? t11 t12)
	   (time>=? t13 t12)
	   (time<=? t11 t12)
	   (time<=? t12 t13)
	   ))))

(define-s19-test! "Time difference"
  (lambda ()
    (let ((t1 (make-time 'time-utc 0 3000))
	  (t2 (make-time 'time-utc 0 1000))
	  (t3 (make-time 'time-duration 0 2000))
	  (t4 (make-time 'time-duration 0 -2000)))
      (and
       (time=? t3 (time-difference t1 t2))
       (time=? t4 (time-difference t2 t1))))))


(define-s19-test! "Time difference, nanoseconds"
  (lambda ()
    (let ((t1 (make-time time-utc 1000 3000))
          (t2 (make-time time-utc 0 3000))
          (t3 (make-time time-duration 1000 0))
          (t4 (make-time time-duration 999999000 -1)))
      (and
        (time=? t3 (time-difference t1 t2))
        (time=? t4 (time-difference t2 t1))))))

(define (test-one-utc-tai-edge utc tai-diff tai-last-diff)
  (let* (;; right on the edge they should be the same
	 (utc-basic (make-time 'time-utc 0 utc))
	 (tai-basic (make-time 'time-tai 0 (+ utc tai-diff)))
	 (utc->tai-basic (time-utc->time-tai utc-basic))
	 (tai->utc-basic (time-tai->time-utc tai-basic))
	 ;; a second before they should be the old diff
	 (utc-basic-1 (make-time 'time-utc 0 (- utc 1)))
	 (tai-basic-1 (make-time 'time-tai 0 (- (+ utc tai-last-diff) 1)))
	 (utc->tai-basic-1 (time-utc->time-tai utc-basic-1))
	 (tai->utc-basic-1 (time-tai->time-utc tai-basic-1))
	 ;; a second later they should be the new diff
	 (utc-basic+1 (make-time 'time-utc 0 (+ utc 1)))
	 (tai-basic+1 (make-time 'time-tai 0 (+ (+ utc tai-diff) 1)))
	 (utc->tai-basic+1 (time-utc->time-tai utc-basic+1))
	 (tai->utc-basic+1 (time-tai->time-utc tai-basic+1))
	 ;; ok, let's move the clock half a month or so plus half a second
	 (shy (* 15 24 60 60))
	 (hs (/ (expt 10 9) 2))
	 ;; a second later they should be the new diff
	 (utc-basic+2 (make-time 'time-utc hs (+ utc shy)))
	 (tai-basic+2 (make-time 'time-tai hs (+ (+ utc tai-diff) shy)))
	 (utc->tai-basic+2 (time-utc->time-tai utc-basic+2))
	 (tai->utc-basic+2 (time-tai->time-utc tai-basic+2))
	 )
    (and (time=? utc-basic tai->utc-basic)
	 (time=? tai-basic utc->tai-basic)
	 (time=? utc-basic-1 tai->utc-basic-1)
	 (time=? tai-basic-1 utc->tai-basic-1)
	 (time=? utc-basic+1 tai->utc-basic+1)
	 (time=? tai-basic+1 utc->tai-basic+1)
	 (time=? utc-basic+2 tai->utc-basic+2)
	 (time=? tai-basic+2 utc->tai-basic+2) 
	 )))

(define-s19-test! "TAI-UTC Conversions"
  (lambda ()
    (and
     (test-one-utc-tai-edge 915148800  32 31)
     (test-one-utc-tai-edge 867715200  31 30)
     (test-one-utc-tai-edge 820454400  30 29)
     (test-one-utc-tai-edge 773020800  29 28)
     (test-one-utc-tai-edge 741484800  28 27)
     (test-one-utc-tai-edge 709948800  27 26)
     (test-one-utc-tai-edge 662688000  26 25)
     (test-one-utc-tai-edge 631152000  25 24)
     (test-one-utc-tai-edge 567993600  24 23)
     (test-one-utc-tai-edge 489024000  23 22)
     (test-one-utc-tai-edge 425865600  22 21)
     (test-one-utc-tai-edge 394329600  21 20)
     (test-one-utc-tai-edge 362793600  20 19)
     (test-one-utc-tai-edge 315532800  19 18)
     (test-one-utc-tai-edge 283996800  18 17)
     (test-one-utc-tai-edge 252460800  17 16)
     (test-one-utc-tai-edge 220924800  16 15)
     (test-one-utc-tai-edge 189302400  15 14)
     (test-one-utc-tai-edge 157766400  14 13)
     (test-one-utc-tai-edge 126230400  13 12)
     (test-one-utc-tai-edge 94694400   12 11)
     (test-one-utc-tai-edge 78796800   11 10)
     (test-one-utc-tai-edge 63072000   10 0)
     (test-one-utc-tai-edge 0   0 0) ;; at the epoch
     (test-one-utc-tai-edge 10   0 0) ;; close to it ...
     (test-one-utc-tai-edge 1045789645 32 32) ;; about now ...
     )))

(define (tm:date= d1 d2)
  (and (= (date-year d1) (date-year d2))
       (= (date-month d1) (date-month d2))
       (= (date-day d1) (date-day d2))
       (= (date-hour d1) (date-hour d2))
       (= (date-second d1) (date-second d2))
       (= (date-nanosecond d1) (date-nanosecond d2))
       (= (date-zone-offset d1) (date-zone-offset d2))))

(define-s19-test! "TAI-Date Conversions"
  (lambda ()
    (and
     (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 29)) 0)
	       (make-date 0 58 59 23 31 12 1998 0))
     (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 30)) 0)
	       (make-date 0 59 59 23 31 12 1998 0))
     (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 31)) 0)
	       (make-date 0 60 59 23 31 12 1998 0))
     (tm:date= (time-tai->date (make-time time-tai 0 (+ 915148800 32)) 0)
	       (make-date 0 0 0 0 1 1 1999 0)))))

(define-s19-test! "Date-UTC Conversions"
  (lambda ()
    (and
     (time=? (make-time time-utc 0 (- 915148800 2))
	     (date->time-utc (make-date 0 58 59 23 31 12 1998 0)))
     (time=? (make-time time-utc 0 (- 915148800 1))
	     (date->time-utc (make-date 0 59 59 23 31 12 1998 0)))
     ;; yes, I think this is acutally right.
     (time=? (make-time time-utc 0 (- 915148800 0))
	     (date->time-utc (make-date 0 60 59 23 31 12 1998 0)))
     (time=? (make-time time-utc 0 (- 915148800 0))
	     (date->time-utc (make-date 0 0 0 0 1 1 1999 0)))
     (time=? (make-time time-utc 0 (+ 915148800 1))
	     (date->time-utc (make-date 0 1 0 0 1 1 1999 0))))))

(define-s19-test! "TZ Offset conversions"
  (lambda ()
    (let ((ct-utc (make-time time-utc 6320000 1045944859))
	  (ct-tai (make-time time-tai 6320000 1045944891))
	  (cd (make-date 6320000 19 14 15 22 2 2003 -18000)))
      (and
       (time=? ct-utc (date->time-utc cd))
       (time=? ct-tai (date->time-tai cd))))))

(begin (newline) (run-s19-tests #t))


(define (date->string/all-formats)
  ;; TODO: figure out why ~f isn't working
  ;; TODO: figure out why ~x and ~X aren't doing what the srfi-19 doc says they do
  (define fs
    '("~~" "~a" "~A" "~b" "~B" "~c" "~d" "~D" "~e" #;"~f" "~h" "~H"
      "~I" "~j" "~k" "~l" "~m" "~M" "~n" "~N" "~p" "~r" "~s"
      "~S" "~t" "~T" "~U" "~V" "~w" "~W" "~x" "~X" "~y" "~Y"
      "~z" "~Z" "~1" "~2" "~3" "~4" "~5"))
  (define cd (current-date))
  (display "\n;;; Running date->string format exercise\n")
  (printf "(current-date)\n => ~s\n" cd)
  (for-each
   (lambda (f)
     (printf "\n--- Format: ~a ----------------------------------------\n" f) 
     (display (date->string cd f)) (newline))
   fs))

;;TODO
#;(define (string->date/all-formats)
  )

(date->string/all-formats)
#;(string->date/all-formats)

Added srfi/tests/tmp1.





















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9

Added srfi/tests/vectors.sps.









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
; Test suite for SRFI 43
;
; $Id: srfi-43-test.sps 6152 2009-03-19 22:30:05Z will $

(import (except (rnrs base) 
                vector-fill!
                vector->list
                list->vector
                vector-map
                vector-for-each)
        (rnrs lists)
        (rnrs io simple)
        (surfage s6 basic-string-ports)
        (surfage s43 vectors))

(define (writeln . xs)
  (for-each display xs)
  (newline))

(define (fail token . more)
  (writeln "Error: test failed: " token)
  #f)

(or (vector? (make-vector 0))
    (fail 'make-vector:0))

(or (= 10 (vector-length (make-vector 10)))
    (fail 'vector-length:basic))

(or (= 97 (vector-ref (make-vector 500 97) 499))
    (fail 'vector-ref:basic))

(or (equal? (vector) '#())
    (fail 'vector:0))

(or (equal? (vector 'a 'b 97) '#(a b 97))
    (fail 'vector))

(or (equal? (vector-unfold (lambda (i x) (values x (- x 1)))
                           10 0)
            '#(0 -1 -2 -3 -4 -5 -6 -7 -8 -9)) ; but SRFI 43 says -8 -8 at end
    (fail 'vector-unfold:1))

(or (equal? (vector-unfold values 10) '#(0 1 2 3 4 5 6 7 8 9))
    (fail 'vector-unfold:2))

(or (let ((vector '#(a b 97)))
      (equal? (vector-unfold (lambda (i) (vector-ref vector i))
                             (vector-length vector))
              vector))
    (fail 'vector-unfold:3))

(or (equal? (vector-unfold-right (lambda (i x) (values x (+ x 1))) 8 0)
            '#(7 6 5 4 3 2 1 0))
    (fail 'vector-unfold-right:1))

(or (let ((vector '#(3 1 4 5 9)))
      (equal? (vector-unfold-right (lambda (i x)
                                     (values (vector-ref vector x) (+ x 1)))
                                   (vector-length vector)
                                   0)
              '#(9 5 4 1 3)))
    (fail 'vector-unfold-right:2))

(or (equal? (vector-copy '#(a b c d e f g h i))
            '#(a b c d e f g h i))
    (fail 'vector-copy:1))

(or (equal? (vector-copy '#(a b c d e f g h i) 6)
            '#(g h i))
    (fail 'vector-copy:2))

(or (equal? (vector-copy '#(a b c d e f g h i) 3 6)
            '#(d e f))
    (fail 'vector-copy:3))

(or (equal? (vector-copy '#(a b c d e f g h i) 6 12 'x)
            '#(g h i x x x))
    (fail 'vector-copy:4))

(or (equal? (vector-reverse-copy '#(5 4 3 2 1 0) 1 5)
            '#(1 2 3 4))
    (fail 'vector-reverse-copy))

(or (equal? (vector-append '#(x) '#(y))
            '#(x y))
    (fail 'vector-append:1))

(or (equal? (vector-append '#(a) '#(b c d))
            '#(a b c d))
    (fail 'vector-append:2))

(or (equal? (vector-append '#(a #(b)) '#(#(c)))
            '#(a #(b) #(c)))
    (fail 'vector-append:3))

(or (equal? (vector-concatenate '(#(a b) #(c d)))
            '#(a b c d))
    (fail 'vector-concatenate))

(or (and (eq? (vector? '#(a b c)) #t)
         (eq? (vector? '(a b c)) #f)
         (eq? (vector? #t) #f)
         (eq? (vector? '#()) #t)
         (eq? (vector? '()) #f))
    (fail 'vector?))

(or (and (eq? (vector-empty? '#(a)) #f)
         (eq? (vector-empty? '#(())) #f)
         (eq? (vector-empty? '#(#())) #f)
         (eq? (vector-empty? '#()) #t))
    (fail 'vector-empty?))

(or (and (eq? (vector= eq? '#(a b c d) '#(a b c d)) #t)
         (eq? (vector= eq? '#(a b c d) '#(a b d c)) #f)
         (eq? (vector= = '#(1 2 3 4 5) '#(1 2 3 4)) #f)
         (eq? (vector= = '#(1 2 3 4) '#(1 2 3 4))   #t)
         (eq? (vector= eq?) #t)
         (eq? (vector= eq? '#(a)) #t)
         (eq? (vector= eq? (vector (vector 'a)) (vector (vector 'a))) #f)
         (eq? (vector= equal? (vector (vector 'a)) (vector (vector 'a))) #t))
    (fail 'vector=))

(or (eq? (vector-ref '#(a b c d) 2) 'c)
    (fail 'vector-ref))

(or (eq? (vector-length '#(a b c)) 3)
    (fail 'vector-length))

(or (equal? (vector-fold (lambda (index len str) (max (string-length str) len))
                         0 '#("a" "b" "" "dd" "e"))
            2)
    (fail 'vector-fold:1))

(or (equal? (vector-fold (lambda (index tail elt) (cons elt tail))
                         '() '#(0 1 2 3 4))
            '(4 3 2 1 0))
    (fail 'vector-fold:2))

(or (equal? (vector-fold (lambda (index counter n)
                           (if (even? n) (+ counter 1) counter))
                         0 '#(0 1 2 3 4 4 4 5 6 7))
            6)
    (fail 'vector-fold:3))

(or (equal? (vector-fold-right (lambda (index tail elt) (cons elt tail))
                               '() '#(a b c d))
            '(a b c d))
    (fail 'vector-fold-right))


(or (equal? (vector-map (lambda (i x) (* x x))
                        (vector-unfold (lambda (i x) (values x (+ x 1))) 4 1))
            '#(1 4 9 16))
    (fail 'vector-map:1))

(or (equal? (vector-map (lambda (i x y) (* x y))
                        (vector-unfold (lambda (i x) (values x (+ x 1))) 5 1)
                        (vector-unfold (lambda (i x) (values x (- x 1))) 5 5))
            '#(5 8 9 8 5))
    (fail 'vector-map:2))

(or (member (let ((count 0))
              (vector-map (lambda (ignored-index ignored-elt)
                            (set! count (+ count 1))
                            count)
                          '#(a b)))
            '(#(1 2) #(2 1)))
    (fail 'vector-map:3))

(or (equal? (let ((v (vector 1 2 3 4)))
              (vector-map! (lambda (i elt) (+ i elt)) v)
              v)
            '#(1 3 5 7))
    (fail 'vector-map!))

(or (equal? (let ((p (open-output-string)))
              (vector-for-each (lambda (i x) (display x p) (newline p))
                               '#("foo" "bar" "baz" "quux" "zot"))
              (get-output-string p))
            "foo\nbar\nbaz\nquux\nzot\n")
    (fail 'vector-for-each))

(or (equal? (vector-count (lambda (i elt) (even? elt)) '#(3 1 4 1 5 9 2 5 6))
            3)
    (fail 'vector-count:1))

(or (equal? (vector-count (lambda (i x y) (< x y))
                          '#(1 3 6 9)
                          '#(2 4 6 8 10 12))
            2)
    (fail 'vector-count:2))

(or (equal? (vector-index even? '#(3 1 4 1 5 9))
            2)
    (fail 'vector-index:1))

(or (equal? (vector-index < '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2))
            1)
    (fail 'vector-index:2))

(or (equal? (vector-index = '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2))
            #f)
    (fail 'vector-index:3))

(or (equal? (vector-index-right even? '#(3 1 4 1 5 9))
            2)
    (fail 'vector-index-right:1))

(or (equal? (vector-index-right < '#(3 1 4 1 5) '#(2 7 1 8 2))
            3)
    (fail 'vector-index-right:2))

(or (equal? (vector-index-right = '#(3 1 4 1 5) '#(2 7 1 8 2))
            #f)
    (fail 'vector-index-right:3))

(or (equal? (vector-skip even? '#(3 1 4 1 5 9))
            0)
    (fail 'vector-skip:1))

(or (equal? (vector-skip < '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2))
            0)
    (fail 'vector-skip:2))

(or (equal? (vector-skip = '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2))
            0)
    (fail 'vector-skip:3))

(or (equal? (vector-skip > '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2))
            1)
    (fail 'vector-skip:4))

(or (equal? (vector-skip-right even? '#(3 1 4 1 5 9))
            5)
    (fail 'vector-skip-right:1))

(or (equal? (vector-skip-right < '#(3 1 4 1 5) '#(2 7 1 8 2))
            4)
    (fail 'vector-skip-right:2))

(or (equal? (vector-skip-right = '#(3 1 4 1 5) '#(2 7 1 8 2))
            4)
    (fail 'vector-skip-right:3))

(or (equal? (vector-skip-right > '#(3 1 4 1 5) '#(2 7 1 8 2))
            3)
    (fail 'vector-skip-right:4))

(define (string-comparator s1 s2)
  (cond ((< (string-length s1) (string-length s2))
         -1)
        ((> (string-length s1) (string-length s2))
         +1)
        ((string<? s1 s2)
         -1)
        ((string>? s1 s2)
         +1)
        (else
         0)))

(or (equal? (vector-binary-search '#()
                                  "bad"
                                  string-comparator)
            #f)
    (fail 'vector-binary-search:0))

(or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa")
                                  "bad"
                                  string-comparator)
            #f)
    (fail 'vector-binary-search:1))

(or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa")
                                  ""
                                  string-comparator)
            #f)
    (fail 'vector-binary-search:2))

(or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa")
                                  "hello"
                                  string-comparator)
            #f)
    (fail 'vector-binary-search:3))

(or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa")
                                  "ab"
                                  string-comparator)
            0)
    (fail 'vector-binary-search:4))

(or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa")
                                  "aaaa"
                                  string-comparator)
            5)
    (fail 'vector-binary-search:5))

(or (equal? (vector-binary-search '#("ab" "cd" "ef" "bcd" "cde" "aaaa")
                                  "bcd"
                                  string-comparator)
            3)
    (fail 'vector-binary-search:6))

(or (equal? (vector-any list '#() '#(a b c))
            #f)
    (fail 'vector-any:0))

(or (equal? (vector-any list '#(a b c) '#())
            #f)
    (fail 'vector-any:1))

(or (equal? (vector-any list '#(a b c) '#(d))
            '(a d))
    (fail 'vector-any:2))

(or (equal? (vector-any memq '#(a b c) '#(() (c d e) (b c 97)))
            '(c 97))
    (fail 'vector-any:3))

(or (equal? (vector-every list '#() '#(a b c))
            #t)
    (fail 'vector-every:0))

(or (equal? (vector-every list '#(a b c) '#())
            #t)
    (fail 'vector-every:1))

(or (equal? (vector-every list '#(a b c) '#(d))
            '(a d))
    (fail 'vector-every:2))

(or (equal? (vector-every memq '#(a b c) '#(() (c d e) (b c 97)))
            #f)
    (fail 'vector-every:3))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-set! v 1 11)
              v)
            '#(0 11 2 3))
    (fail 'vector-set!))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-swap! v 1 3)
              v)
            '#(0 3 2 1))
    (fail 'vector-swap!))

(or (equal? (let ((v (vector)))
              (vector-fill! v 97)
              v)
            '#())
    (fail 'vector-fill!:0))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-fill! v 97)
              v)
            '#(97 97 97 97))
    (fail 'vector-fill!:1))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-fill! v 97 1)
              v)
            '#(0 97 97 97))
    (fail 'vector-fill!:2))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-fill! v 97 1 2)
              v)
            '#(0 97 2 3))
    (fail 'vector-fill!:3))

(or (equal? (let ((v (vector)))
              (vector-reverse! v)
              v)
            '#())
    (fail 'vector-reverse!:0))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-reverse! v)
              v)
            '#(3 2 1 0))
    (fail 'vector-reverse!:1))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-reverse! v 1)
              v)
            '#(0 3 2 1))
    (fail 'vector-reverse!:2))

(or (equal? (let ((v (vector 0 1 2 3)))
              (vector-reverse! v 1 3)
              v)
            '#(0 2 1 3))
    (fail 'vector-reverse!:3))

(or (equal? (let ((v (vector))
                  (src '#(100 101 102 103 104 105)))
              (vector-copy! v 0 v)
              v)
            '#())
    (fail 'vector-copy!:0))

(or (equal? (let ((v (vector 0 1 2 3 4 5))
                  (src '#(100 101 102 103 104 105)))
              (vector-copy! v 0 src)
              v)
            '#(100 101 102 103 104 105))
    (fail 'vector-copy!:1))

(or (equal? (let ((v (vector 0 1 2 3))
                  (src '#(100 101 102 103 104 105)))
              (vector-copy! v 1 src 4)
              v)
            '#(0 104 105 3))
    (fail 'vector-copy!:2))

(or (equal? (let ((v (vector 0 1 2 3))
                  (src '#(100 101 102 103 104 105)))
              (vector-copy! v 1 src 2 4)
              v)
            '#(0 102 103 3))
    (fail 'vector-copy!:3))

(or (equal? (let ((v (vector))
                  (src '#(100 101 102 103 104 105)))
              (vector-reverse-copy! v 0 v)
              v)
            '#())
    (fail 'vector-reverse-copy!:0))

(or (equal? (let ((v (vector 0 1 2 3 4 5))
                  (src '#(100 101 102 103 104 105)))
              (vector-reverse-copy! v 0 src)
              v)
            '#(105 104 103 102 101 100))
    (fail 'vector-reverse-copy!:1))

(or (equal? (let ((v (vector 0 1 2 3))
                  (src '#(100 101 102 103 104 105)))
              (vector-reverse-copy! v 1 src 4)
              v)
            '#(0 105 104 3))
    (fail 'vector-reverse-copy!:2))

(or (equal? (let ((v (vector 0 1 2 3))
                  (src '#(100 101 102 103 104 105)))
              (vector-reverse-copy! v 1 src 2 4)
              v)
            '#(0 103 102 3))
    (fail 'vector-reverse-copy!:3))

(or (equal? (vector->list '#())
            '())
    (fail 'vector->list:0))

(or (equal? (vector->list '#(a b c))
            '(a b c))
    (fail 'vector->list:1))

(or (equal? (vector->list '#(a b c d e) 1)
            '(b c d e))
    (fail 'vector->list:2))

(or (equal? (vector->list '#(a b c d e) 1 4)
            '(b c d))
    (fail 'vector->list:3))

(or (equal? (reverse-vector->list '#())
            '())
    (fail 'reverse-vector->list:0))

(or (equal? (reverse-vector->list '#(a b c))
            '(c b a))
    (fail 'reverse-vector->list:1))

(or (equal? (reverse-vector->list '#(a b c d e) 1)
            '(e d c b))
    (fail 'reverse-vector->list:2))

(or (equal? (reverse-vector->list '#(a b c d e) 1 3)
            '(c b))
    (fail 'reverse-vector->list:3))

(or (equal? (list->vector '())
            '#())
    (fail 'list->vector:0))

(or (equal? (list->vector '(a b c))
            '#(a b c))
    (fail 'list->vector:1))

(or (equal? (reverse-list->vector '())
            '#())
    (fail 'reverse-list->vector:0))

(or (equal? (reverse-list->vector '(a b c))
            '#(c b a))
    (fail 'reverse-list->vector:1))

(writeln "Done.")