mirror of
https://github.com/ashinn/chibi-scheme.git
synced 2025-05-18 21:29:19 +02:00
275 lines
9.2 KiB
Scheme
275 lines
9.2 KiB
Scheme
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Cursors
|
|
|
|
;;> Returns true iff \var{iset} is empty.
|
|
|
|
(define (iset-empty? iset)
|
|
(and (iset? iset)
|
|
(cond ((iset-bits iset) => zero?) (else #f))
|
|
(let ((l (iset-left iset))) (or (not l) (iset-empty? l)))
|
|
(let ((r (iset-right iset))) (or (not r) (iset-empty? r)))))
|
|
|
|
(define-record-type Iset-Cursor
|
|
(make-iset-cursor node pos stack)
|
|
iset-cursor?
|
|
(node iset-cursor-node iset-cursor-node-set!)
|
|
(pos iset-cursor-pos iset-cursor-pos-set!)
|
|
(stack iset-cursor-stack iset-cursor-stack-set!))
|
|
|
|
(define (%iset-cursor iset . o)
|
|
(iset-cursor-advance
|
|
(make-iset-cursor iset
|
|
(or (iset-bits iset) (iset-start iset))
|
|
(if (pair? o) (car o) '()))))
|
|
|
|
;;> Create a new iset cursor pointing to the first element of iset,
|
|
;;> with an optional stack argument.
|
|
|
|
(define (iset-cursor iset . o)
|
|
(let ((stack (if (pair? o) (car o) '())))
|
|
(if (iset-left iset)
|
|
(iset-cursor (iset-left iset) (cons iset stack))
|
|
(%iset-cursor iset stack))))
|
|
|
|
;; Continue to the next node in the search stack.
|
|
(define (iset-cursor-pop cur)
|
|
(let ((node (iset-cursor-node cur))
|
|
(stack (iset-cursor-stack cur)))
|
|
(cond
|
|
((iset-right node)
|
|
(iset-cursor (iset-right node) stack))
|
|
((pair? stack)
|
|
(%iset-cursor (car stack) (cdr stack)))
|
|
(else
|
|
cur))))
|
|
|
|
;; Advance to the next node+pos that can be referenced if at the end
|
|
;; of this node's range.
|
|
(define (iset-cursor-advance cur)
|
|
(let ((node (iset-cursor-node cur))
|
|
(pos (iset-cursor-pos cur)))
|
|
(cond
|
|
((if (iset-bits node) (zero? pos) (> pos (iset-end node)))
|
|
(iset-cursor-pop cur))
|
|
(else cur))))
|
|
|
|
;;> Return a new iset cursor pointing to the next element of
|
|
;;> \var{iset} after \var{cur}. If \var{cur} is already at
|
|
;;> \scheme{end-of-iset?}, the resulting cursor is as well.
|
|
|
|
(define (iset-cursor-next iset cur)
|
|
(iset-cursor-advance
|
|
(let ((node (iset-cursor-node cur))
|
|
(pos (iset-cursor-pos cur))
|
|
(stack (iset-cursor-stack cur)))
|
|
(let ((pos (if (iset-bits node) (bitwise-and pos (- pos 1)) (+ pos 1))))
|
|
(make-iset-cursor node pos stack)))))
|
|
|
|
;;> Return the element of iset \var{iset} at cursor \var{cur}. If the
|
|
;;> cursor is at \scheme{end-of-iset?}, raises an error.
|
|
|
|
(define (iset-ref iset cur)
|
|
(let ((node (iset-cursor-node cur))
|
|
(pos (iset-cursor-pos cur)))
|
|
(cond
|
|
((iset-bits node)
|
|
(if (zero? pos)
|
|
(error "cursor reference past end of iset")
|
|
(+ (iset-start node)
|
|
(integer-length (- pos (bitwise-and pos (- pos 1))))
|
|
-1)))
|
|
(else
|
|
(if (> pos (iset-end node))
|
|
(error "cursor reference past end of iset")
|
|
pos)))))
|
|
|
|
;;> Returns true iff \var{cur} is at the end of iset, such that
|
|
;;> \scheme{iset-ref} is no longer valid.
|
|
|
|
(define (end-of-iset? cur)
|
|
(let ((node (iset-cursor-node cur)))
|
|
(and (if (iset-bits node)
|
|
(zero? (iset-cursor-pos cur))
|
|
(> (iset-cursor-pos cur) (iset-end node)))
|
|
(not (iset-right node))
|
|
(null? (iset-cursor-stack cur)))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Rank/Select operations, acting directly on isets without an
|
|
;; optimized data structure.
|
|
|
|
(define (iset-node-size iset)
|
|
(if (iset-bits iset)
|
|
(bit-count (iset-bits iset))
|
|
(+ 1 (- (iset-end iset) (iset-start iset)))))
|
|
|
|
;; Number of bits set in i below index n.
|
|
(define (bit-rank i n)
|
|
(bit-count (bitwise-and i (- (arithmetic-shift 1 n) 1))))
|
|
|
|
;;> Returns the rank (i.e. index within the iset) of the given
|
|
;;> element, a number in [0, size). This can be used to compress an
|
|
;;> integer set to a minimal consecutive set of integets. Can also be
|
|
;;> thought of as the number of elements in iset smaller than element.
|
|
(define (iset-rank iset element)
|
|
(let lp ((iset iset) (count 0))
|
|
(cond
|
|
((< element (iset-start iset))
|
|
(if (iset-left iset)
|
|
(lp (iset-left iset) count)
|
|
(error "integer not in iset" iset element)))
|
|
((> element (iset-end iset))
|
|
(if (iset-right iset)
|
|
(lp (iset-right iset)
|
|
(+ count
|
|
(cond ((iset-left iset) => iset-size) (else 0))
|
|
(iset-node-size iset)))
|
|
(error "integer not in iset" iset element)))
|
|
((iset-bits iset)
|
|
(+ count
|
|
(cond ((iset-left iset) => iset-size) (else 0))
|
|
(bit-rank (iset-bits iset)
|
|
(- element (iset-start iset)))))
|
|
(else
|
|
(+ count
|
|
(cond ((iset-left iset) => iset-size) (else 0))
|
|
(integer-length (- element (iset-start iset))))))))
|
|
|
|
(define (nth-set-bit i n)
|
|
;; TODO: optimize
|
|
(if (zero? n)
|
|
(first-set-bit i)
|
|
(nth-set-bit (bitwise-and i (- i 1)) (- n 1))))
|
|
|
|
;;> Selects the index-th element of iset starting at 0. The inverse
|
|
;;> operation of \scheme{iset-rank}.
|
|
(define (iset-select iset index)
|
|
(let lp ((iset iset) (index index) (stack '()))
|
|
(if (and iset (iset-left iset))
|
|
(lp (iset-left iset) index (cons iset stack))
|
|
(let ((iset (if iset iset (car stack)))
|
|
(stack (if iset stack (cdr stack))))
|
|
(let ((node-size (iset-node-size iset)))
|
|
(cond
|
|
((and (< index node-size) (iset-bits iset))
|
|
(+ (iset-start iset)
|
|
(nth-set-bit (iset-bits iset) index)))
|
|
((< index node-size)
|
|
(+ (iset-start iset) index))
|
|
((iset-right iset)
|
|
(lp (iset-right iset) (- index node-size) stack))
|
|
((pair? stack)
|
|
(lp #f (- index node-size) stack))
|
|
(else
|
|
(error "iset index out of range" iset index))))))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Equality
|
|
|
|
(define (iset2= is1 is2)
|
|
(let lp ((cur1 (iset-cursor is1))
|
|
(cur2 (iset-cursor is2)))
|
|
(cond ((end-of-iset? cur1) (end-of-iset? cur2))
|
|
((end-of-iset? cur2) #f)
|
|
((= (iset-ref is1 cur1) (iset-ref is2 cur2))
|
|
(lp (iset-cursor-next is1 cur1) (iset-cursor-next is2 cur2)))
|
|
(else
|
|
#f))))
|
|
|
|
(define (iset2<= is1 is2)
|
|
(let lp ((cur1 (iset-cursor is1))
|
|
(cur2 (iset-cursor is2)))
|
|
(cond ((end-of-iset? cur1))
|
|
((end-of-iset? cur2) #f)
|
|
(else
|
|
(let ((i1 (iset-ref is1 cur1))
|
|
(i2 (iset-ref is1 cur2)))
|
|
(cond ((> i1 i2)
|
|
(lp cur1 (iset-cursor-next is2 cur2)))
|
|
((= i1 i2)
|
|
(lp (iset-cursor-next is1 cur1)
|
|
(iset-cursor-next is2 cur2)))
|
|
(else
|
|
;; (< i1 i2) - i1 won't occur in is2
|
|
#f)))))))
|
|
|
|
;;> Returns true iff all arguments contain the same elements. Always
|
|
;;> returns true if there are less than two arguments.
|
|
|
|
(define (iset= . o)
|
|
(or (null? o)
|
|
(let lp ((a (car o)) (ls (cdr o)))
|
|
(or (null? ls) (and (iset2= a (car ls)) (lp (car ls) (cdr ls)))))))
|
|
|
|
;;> Returns true iff the arguments are monotonically increasing, that
|
|
;;> is each argument contains every element of all preceding
|
|
;;> arguments. Always returns true if there are less than two
|
|
;;> arguments.
|
|
|
|
(define (iset<= . o)
|
|
(or (null? o)
|
|
(let lp ((a (car o)) (ls (cdr o)))
|
|
(or (null? ls) (and (iset2<= a (car ls)) (lp (car ls) (cdr ls)))))))
|
|
|
|
;;> Returns true iff the arguments are monotonically decreasing, that
|
|
;;> is each argument contains every element of all succeeding
|
|
;;> arguments. Always returns true if there are less than two
|
|
;;> arguments.
|
|
|
|
(define (iset>= . o)
|
|
(apply iset<= (reverse o)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Folding
|
|
|
|
(define (iset-fold-node kons knil iset)
|
|
(let lp ((is iset) (acc knil))
|
|
(let* ((left (iset-left is))
|
|
(acc (kons is (if left (lp left acc) acc)))
|
|
(right (iset-right is)))
|
|
(if right (lp right acc) acc))))
|
|
|
|
;;> The fundamental iset iterator. Applies \var{kons} to every
|
|
;;> element of \var{iset} along with an accumulator, starting with
|
|
;;> \var{knil}. Returns \var{knil} if \var{iset} is empty.
|
|
|
|
(define (iset-fold kons knil iset)
|
|
(iset-fold-node
|
|
(lambda (is acc)
|
|
(let ((start (iset-start is))
|
|
(end (iset-end is))
|
|
(bits (iset-bits is)))
|
|
(if bits
|
|
(let ((limit (+ 1 (- end start))))
|
|
(do ((n1 bits n2)
|
|
(n2 (bitwise-and bits (- bits 1)) (bitwise-and n2 (- n2 1)))
|
|
(acc acc (kons (+ start (integer-length (- n1 n2)) -1) acc)))
|
|
((zero? n1) acc)))
|
|
(do ((i start (+ i 1))
|
|
(acc acc (kons i acc)))
|
|
((> i end) acc)))))
|
|
knil
|
|
iset))
|
|
|
|
(define (iset-for-each-node proc iset)
|
|
(iset-fold-node (lambda (node acc) (proc node)) #f iset))
|
|
|
|
;;> Runs \var{proc} on every element of iset, discarding the results.
|
|
|
|
(define (iset-for-each proc iset)
|
|
(iset-fold (lambda (i acc) (proc i)) #f iset))
|
|
|
|
;;> Returns a list of every integer in \var{iset} in sorted
|
|
;;> (increasing) order.
|
|
|
|
(define (iset->list iset)
|
|
(reverse (iset-fold cons '() iset)))
|
|
|
|
;;> Returns the number of elements in \var{iset}.
|
|
|
|
(define (iset-size iset)
|
|
(iset-fold-node
|
|
(lambda (is acc) (+ acc (iset-node-size is)))
|
|
0
|
|
iset))
|