summaryrefslogtreecommitdiff
path: root/slsets.scm
blob: 4b83cf445d09dedb8bcfd43b8781dd64a62aa78e (plain)
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
;;
;; Lists of symbols as sets
;;
;; Essentially, this provides specialized versions of SRFI-1's lset
;; operations for symbols but with linear instead of quadratic time
;; complexity.  It's even faster than using a side hash table, unless
;; the symbols already have large plists associated with them.
;;
(declare (disable-interrupts))

(module slsets
    (slset<= slset= slset-adjoin ;; TODO: slset-delete and slset-contains?
     slset-difference slset-intersection
     slset-difference+intersection
     slset-union slset-xor
     slset-deduplicate)

(import scheme (chicken base) (chicken foreign) (chicken plist))

(define-inline (mark! x marking)
  (when (symbol? x) (put! x marking #t)))

(define-inline (mark-list! lst marking)
  (for-each (lambda (x) (mark! x marking)) lst))

(define-inline (unmark! x marking)
  (when (symbol? x) (remprop! x marking)))

(define-inline (unmark-list! lst marking)
  (for-each (lambda (x) (unmark! x marking)) lst))

(define-inline (with-marked-list lst fun)
  (let ((marking (gensym 'm)))
    (dynamic-wind
        (lambda () (mark-list! lst marking))
        (lambda () (fun marking))
        (lambda () (unmark-list! lst marking)))))

(define-inline (every? pred lst)
  (let lp ((lst lst))
    (cond ((null? lst) #t)
          ((not (pred (car lst))) #f)
          (else (lp (cdr lst))))))

(define-inline (marked? x marking)
  (and (symbol? x) (get x marking)))

(define (last lst)
  (if (null? (cdr lst))
      (car lst)
      (last (cdr lst))))

(define-inline (filter pred lst)
  (let lp ((lst lst)
           (res '()))
    (cond ((null? lst) (reverse res))
          ((not (pred (car lst))) (lp (cdr lst) res))
          (else (lp (cdr lst) (cons (car lst) res))))))

(define (slset<= . lsts)
  (or (null? lsts)
      (let ((last-list (last lsts)))
       (with-marked-list last-list
                         (lambda (m)
                           (every? (lambda (lst)
                                     (or (eq? lst last-list) ; Avoid filtering out this one and building a new list
                                         (every? (lambda (x) (marked? x m)) lst)))
                                   lsts))))))

(define (slset= . lsts)
  (or (null? lsts)
      (let ((lst (car lsts))
            (lsts (cdr lsts)))
        (every? (lambda (l)
                  (and (slset<= lst l) (slset<= l lst)))
                lsts))))

(define (slset-adjoin lst . els)
  (with-marked-list lst
                    (lambda (m)
                      (dynamic-wind
                          void
                          (lambda ()
                            (let lp ((lst lst)
                                     (els els))
                              (cond ((null? els) lst)
                                    ((marked? (car els) m) (lp lst (cdr els)))
                                    (else (mark! (car els) m)
                                          (lp (cons (car els) lst) (cdr els))))))
                          (lambda ()
                            (unmark-list! els m))))))

(define (slset-difference lst . lsts)
  (let lp ((lsts lsts)
           (res lst))
    (if (null? lsts)
        res
        (lp (cdr lsts)
            (with-marked-list (car lsts) (lambda (m) (filter (lambda (x) (not (marked? x m))) res)))))))

(define (slset-intersection lst . lsts)
  (let lp ((lsts lsts)
           (res lst))
    (if (null? lsts)
        res
        (lp (cdr lsts)
            (with-marked-list (car lsts)
                              (lambda (m) (filter (lambda (x) (marked? x m)) res)))))))

;; TODO: Make this faster
(define (slset-difference+intersection lst . lsts)
  (values (apply slset-difference lst lsts)
          (apply slset-intersection lst lsts)))

;; NOTE: Can't use mark-list here, so roll our own!
;; TODO: Clean up if there's an error cdring down any of these lists?
(define (slset-union . lsts)
  (if (null? lsts)
      '()
      (let ((marking (gensym 'm))
            (first-list (car lsts)))
        (mark-list! first-list marking)
        (let lp ((lsts (cdr lsts))
                 (res first-list))
          (if (null? lsts)
              (begin (unmark-list! res marking)
                     res)
              (let lp2 ((lst (car lsts))
                        (res res))
                (if (null? lst)
                    (lp (cdr lsts) res)
                    (let ((x (car lst)))
                      (if (marked? x marking)
                          (lp2 (cdr lst) res)
                          (begin (mark! x marking)
                                 (lp2 (cdr lst) (cons x res))))))))))))

(define (slset-xor . lsts)
  (let lp ((lsts lsts)
           (res '()))
    (if (null? lsts)
        res
        (lp (cdr lsts)
            (append (slset-difference res (car lsts))
                    (slset-difference (car lsts) res))))))

;; NOTE: Can't use mark-list here, so roll our own!
;; TODO: Clean up if there's an error cdring down the list?
(define (slset-deduplicate lst)
  (let ((marking (gensym 'm)))
    (let lp ((lst lst)
             (res '()))
      (if (null? lst)
          (begin (unmark-list! res marking)
                 (reverse res))
          (let ((x (car lst)))
            (cond ((marked? x marking)
                   (lp (cdr lst) res))
                  (else (mark! x marking)
                        (lp (cdr lst) (cons x res)))))))))

)