266 lines
9.2 KiB
Racket
266 lines
9.2 KiB
Racket
#lang racket/base
|
|
;; owned by ryanc
|
|
(require racket/contract
|
|
racket/promise
|
|
racket/dict
|
|
unstable/skip-list)
|
|
|
|
;; NOTE-1
|
|
;; I need to be able to split intervals. So I can either have
|
|
;; closed intervals on the integers or half-open intervals of
|
|
;; arbitrary total orders. I'm going to do half-open intervals.
|
|
|
|
;; An interval-map is (make-interval-map skip-list =? <? translate)
|
|
;; skip-list maps Start => (cons End Value)
|
|
;; Invariant: intervals are disjoint (but the end of one interval
|
|
;; can be the same as the start of the next, since half-open).
|
|
|
|
(define make-interval-map*
|
|
(let ([make-interval-map
|
|
(lambda (=? <? [translate #f])
|
|
(make-interval-map (make-skip-list =? <?) =? <? translate))])
|
|
make-interval-map))
|
|
|
|
(define (make-numeric-interval-map)
|
|
(define (translate x y)
|
|
(let ([offset (- y x)])
|
|
(lambda (z) (+ z offset))))
|
|
(make-interval-map* = < translate))
|
|
|
|
(define (interval-map-ref im key [default (interval-map-error key)])
|
|
(let* ([s (interval-map-s im)]
|
|
[<? (interval-map-<? im)]
|
|
[istart (skip-list-iterate-greatest/<=? s key)])
|
|
(cond [istart
|
|
(let ([istartvalue (skip-list-iterate-value s istart)])
|
|
(if (<? key (car istartvalue))
|
|
(cdr istartvalue)
|
|
(if (procedure? default) (default) default)))]
|
|
[else
|
|
(if (procedure? default) (default) default)])))
|
|
|
|
(define ((interval-map-error x))
|
|
(error 'interval-map-ref "no mapping found for: ~e" x))
|
|
|
|
;; (POST x) =
|
|
;; (if (start <= x < end)
|
|
;; (updater (PRE x default))
|
|
;; (PRE x))
|
|
(define (interval-map-update*! im start end updater [default (error-for 'interval-map-update*!)])
|
|
(define updated-defaultp
|
|
(delay (updater (if (procedure? default) (default) default))))
|
|
(let ([s (interval-map-s im)]
|
|
[<? (interval-map-<? im)]
|
|
[=? (interval-map-=? im)])
|
|
(check-interval im start end 'interval-map-update*!)
|
|
(split! s start <?)
|
|
(split! s end <?)
|
|
;; Interval ix needs updating iff start <= key(ix) < end
|
|
;; (Also need to insert missing intervals)
|
|
;; Main loop:
|
|
(let loop ([start start] [ix (skip-list-iterate-least/>=? s start)])
|
|
(let ([ixstart (and ix (skip-list-iterate-key s ix))])
|
|
(cond [(and ix (<? ixstart end))
|
|
;; First do leading gap, [ start, key(ix) )
|
|
(when (<? start ixstart)
|
|
(skip-list-set! s start (cons ixstart (force updated-defaultp))))
|
|
;; Then interval, [ ixstart, end(ix) )
|
|
(let ([ixvalue (skip-list-iterate-value s ix)])
|
|
(skip-list-iterate-set-value! s ix
|
|
(cons (car ixvalue) (updater (cdr ixvalue))))
|
|
(loop (car ixvalue) (skip-list-iterate-next s ix)))]
|
|
[else
|
|
;; Do gap, [ start, end )
|
|
(when (<? start end)
|
|
(skip-list-set! s start (cons end (force updated-defaultp))))])))))
|
|
|
|
|
|
(define (interval-map-cons*! im start end obj [default null])
|
|
(check-interval im start end 'interval-map-cons*!)
|
|
(interval-map-update*! im start end (lambda (old) (cons obj old)) default))
|
|
|
|
(define ((error-for who))
|
|
(error who "no mapping found"))
|
|
|
|
;; (POST x) = (if (start <= x < end) value (PRE x))
|
|
(define (interval-map-set! im start end value)
|
|
(check-interval im start end 'interval-map-set!)
|
|
(interval-map-remove! im start end)
|
|
(interval-map-update*! im start end values value))
|
|
|
|
(define (interval-map-remove! im start end)
|
|
(let ([s (interval-map-s im)]
|
|
[<? (interval-map-<? im)]
|
|
[=? (interval-map-=? im)])
|
|
(check-interval im start end 'interval-map-remove!)
|
|
(split! s start <?)
|
|
(split! s end <?)
|
|
;; Interval ix needs removing iff start <= key(ix) < end
|
|
;; FIXME: add batch remove to skip-lists
|
|
(let loop ([ix (skip-list-iterate-least/>=? s start)])
|
|
(when ix
|
|
(let ([ixstart (skip-list-iterate-key s ix)])
|
|
(when (<? ixstart end)
|
|
;; Get next before we remove current
|
|
(let ([next (skip-list-iterate-next s ix)])
|
|
(skip-list-remove! s ixstart)
|
|
(loop next))))))))
|
|
|
|
;; split!
|
|
;; Ensures that if an interval contains x, it starts at x
|
|
(define (split! s x <?)
|
|
(let* ([ix (skip-list-iterate-greatest/<? s x)]
|
|
[ixstart (and ix (skip-list-iterate-key s ix))])
|
|
;; (ix = #f) or (key(ix) < x)
|
|
(cond [(eq? ix #f)
|
|
;; x <= all existing intervals; that is, either
|
|
;; 1) x starts its own interval (=), or
|
|
;; 2) x < all existing intervals (<)
|
|
;; Either way, nothing to split.
|
|
(void)]
|
|
[else
|
|
(let* ([ixvalue (skip-list-iterate-value s ix)]
|
|
[ixend (car ixvalue)])
|
|
(cond [(<? x ixend)
|
|
;; Split; adjust ix to start at x, insert [ixstart, x)
|
|
(skip-list-iterate-set-key! s ix x)
|
|
(skip-list-set! s ixstart (cons x (cdr ixvalue)))]
|
|
[else
|
|
;; x not in ix
|
|
(void)]))])))
|
|
|
|
(define (check-interval im start end who)
|
|
(let ([<? (interval-map-<? im)])
|
|
(unless (<? start end)
|
|
(error who "bad interval: start ~e not less than end ~e" start end))))
|
|
|
|
;; Iteration
|
|
|
|
(define-struct interval-map-iter (si))
|
|
|
|
(define (interval-map-iterate-first im)
|
|
(cond [(skip-list-iterate-first (interval-map-s im))
|
|
=> make-interval-map-iter]
|
|
[else #f]))
|
|
|
|
(define (interval-map-iterate-next im iter)
|
|
(cond [(skip-list-iterate-next (interval-map-s im)
|
|
(interval-map-iter-si iter))
|
|
=> make-interval-map-iter]
|
|
[else #f]))
|
|
|
|
(define (interval-map-iterate-key im iter)
|
|
(let ([s (interval-map-s im)]
|
|
[is (interval-map-iter-si iter)])
|
|
(cons (skip-list-iterate-key s is)
|
|
(car (skip-list-iterate-value s is)))))
|
|
|
|
(define (interval-map-iterate-value im iter)
|
|
(let ([s (interval-map-s im)]
|
|
[is (interval-map-iter-si iter)])
|
|
(cdr (skip-list-iterate-value s is))))
|
|
|
|
;; Interval map
|
|
|
|
(define-struct interval-map (s =? <? translate)
|
|
#:property prop:dict
|
|
(vector interval-map-ref
|
|
#f ;; set!
|
|
#f ;; set
|
|
#f ;; remove!
|
|
#f ;; remove
|
|
(lambda (im) (error 'interval-map-count "not supported"))
|
|
interval-map-iterate-first
|
|
interval-map-iterate-next
|
|
interval-map-iterate-key
|
|
interval-map-iterate-value))
|
|
|
|
(define (interval-map-with-translate? x)
|
|
(and (interval-map? x)
|
|
(procedure? (interval-map-translate x))))
|
|
|
|
(define (interval-map-contract! im from to)
|
|
(let ([<? (interval-map-<? im)])
|
|
(unless (<? from to)
|
|
(error 'interval-map-contract!
|
|
"start ~e not less than end ~e" from to)))
|
|
(interval-map-remove! im from to)
|
|
(let* ([s (interval-map-s im)]
|
|
[translate ((interval-map-translate im) to from)])
|
|
(apply-offset! s translate to)))
|
|
|
|
(define (interval-map-expand! im from to)
|
|
(let ([<? (interval-map-<? im)])
|
|
(unless (<? from to)
|
|
(error 'interval-map-expand!
|
|
"start ~e not less than end ~e" from to))
|
|
(let* ([s (interval-map-s im)]
|
|
[translate ((interval-map-translate im) from to)])
|
|
(split! s from <?)
|
|
(apply-offset! s translate from))))
|
|
|
|
(define (apply-offset! s translate to)
|
|
(let loop ([ix (skip-list-iterate-least/>=? s to)])
|
|
(when ix
|
|
(let* ([ixkey (skip-list-iterate-key s ix)]
|
|
[ixvalue (skip-list-iterate-value s ix)])
|
|
(skip-list-iterate-set-key! s ix (translate ixkey))
|
|
(skip-list-iterate-set-value! s ix
|
|
(cons (translate (car ixvalue)) (cdr ixvalue))))
|
|
(loop (skip-list-iterate-next s ix)))))
|
|
|
|
(provide/contract
|
|
[rename make-interval-map* make-interval-map
|
|
(-> procedure? procedure? interval-map?)]
|
|
[make-numeric-interval-map
|
|
(-> interval-map-with-translate?)]
|
|
[interval-map?
|
|
(-> any/c any)]
|
|
[interval-map-with-translate?
|
|
(-> any/c any)]
|
|
[interval-map-ref
|
|
(->* (interval-map? any/c) (any/c) any)]
|
|
[interval-map-set!
|
|
(-> interval-map? any/c any/c any/c any)]
|
|
[interval-map-update*!
|
|
(->* (interval-map? any/c any/c (-> any/c any/c)) (any/c) any)]
|
|
[interval-map-cons*!
|
|
(->* (interval-map? any/c any/c any/c) (any/c) any)]
|
|
[interval-map-remove!
|
|
(-> interval-map? any/c any/c any)]
|
|
[interval-map-contract!
|
|
(-> interval-map-with-translate? any/c any/c any)]
|
|
[interval-map-expand!
|
|
(-> interval-map-with-translate? any/c any/c any)]
|
|
[interval-map-iterate-first
|
|
(-> interval-map? (or/c interval-map-iter? #f))]
|
|
[interval-map-iterate-next
|
|
(-> interval-map? interval-map-iter? (or/c interval-map-iter? #f))]
|
|
[interval-map-iterate-key
|
|
(-> interval-map? interval-map-iter? any)]
|
|
[interval-map-iterate-value
|
|
(-> interval-map? interval-map-iter? any)]
|
|
[interval-map-iter?
|
|
(-> any/c any)])
|
|
|
|
#|
|
|
;; Testing
|
|
(define (dump im)
|
|
(dict-map (interval-map-s im) list))
|
|
|
|
(define im (make-interval-map* = <))
|
|
(interval-map-set! im 1 3 '(a))
|
|
(interval-map-set! im 4 7 '(b))
|
|
(dump im)
|
|
;;(interval-map-remove! im 2 5)
|
|
(interval-map-cons*! im 2 5 'c null)
|
|
(dump im)
|
|
|#
|
|
|
|
#|
|
|
(define sim (make-interval-map* string=? string<?))
|
|
(interval-map-set! sim "apple" "orange" 'fruit)
|
|
(interval-map-set! sim "banana" "guava" 'tropical-fruit)
|
|
(dump sim)
|
|
|#
|