#lang scheme/base
(require (for-syntax scheme/base)
scheme/contract
scheme/match
(only-in srfi/1/list make-list fold take drop alist-delete)
srfi/26/cut
(file "base.ss")
(file "contract.ss")
(file "debug.ss")
(file "number.ss"))
(define (make-list* num items)
(define-values (num-complete num-remaining)
(quotient/remainder num (length items)))
(let loop ([num-complete num-complete] [num-remaining num-remaining] [items items])
(if (zero? num-complete)
(take items num-remaining)
(append items (loop (sub1 num-complete) num-remaining items)))))
(define-syntax (assemble-list stx)
(define (expand-clause clause-stx)
(syntax-case clause-stx (unquote-splicing)
[(#t (unquote-splicing items)) (list #',@items)]
[(#f (unquote-splicing items)) null]
[(expr (unquote-splicing items)) (list #`,@(if expr items null))]
[(#t item ...) (syntax->list #'((unquote item) ...))]
[(#f item ...) null]
[(expr item ...) (list #`,@(if expr (list item ...) null))]))
(define (expand-clauses clause-stxs)
(if (null? clause-stxs)
null
(let ([curr (car clause-stxs)]
[rest (cdr clause-stxs)])
(append (expand-clause curr)
(expand-clauses rest)))))
(syntax-case stx ()
[(_ clause ...)
#``(#,@(expand-clauses (syntax->list #'(clause ...))))]))
(define (in-list/cycle items)
(make-do-sequence
(lambda ()
(values (lambda (pos)
(car pos))
(lambda (pos)
(if (null? (cdr pos))
items
(cdr pos)))
items
(lambda (pos)
#t)
(lambda (val)
#t)
(lambda (pos val)
#t)))))
(define (list-swap data index1 index2)
(cond [(< index2 index1)
(list-swap data index2 index1)]
[(= index1 index2)
(raise-exn exn:fail:contract
(format "List indices must be differnet: ~a ~a" index1 index2))]
[(or (< index2 0) (> index1 (length data)))
(raise-exn exn:fail:contract
(format "List indices out of bounds: ~a ~a" index1 index2))]
[else (let ([item1 (list-ref data index1)]
[item2 (list-ref data index2)]
[slice0-1 (take data index1)]
[slice1-2 (take (drop data (add1 index1)) (sub1 (- index2 index1)))]
[slice2-3 (drop data (add1 index2))])
(append slice0-1
(cons item2 slice1-2)
(cons item1 slice2-3)))]))
(define (list-delimit list delimiter)
(if (null? list)
null
(let loop ([rest list])
(if (null? (cdr rest))
(cons (car rest)
null)
(cons (car rest)
(cons delimiter
(loop (cdr rest))))))))
(define (list-pad lis target-length [item #f])
(let loop ([current-length (length lis)] [accum lis])
(if (< current-length target-length)
(loop (add1 current-length) (cons item accum))
accum)))
(define (list-pad-right lis target-length [item #f])
(reverse (list-pad (reverse lis) target-length item)))
(define (merge-sorted-lists list1 list2 same? less-than?)
(define (swallow item list)
(cond [(null? list) list]
[(same? item (car list)) (swallow item (cdr list))]
[else list]))
(define (merge list1 list2)
(cond [(null? list1) list2]
[(null? list2) list1]
[else (let ([head1 (car list1)]
[head2 (car list2)])
(cond [(same? head1 head2)
(cons head1 (merge (swallow head1 list1)
(swallow head2 list2)))]
[(less-than? head1 head2)
(cons head1 (merge (swallow head1 list1) list2))]
[else
(cons head2 (merge list1 (swallow head2 list2)))]))]))
(merge list1 list2))
(define (char-iota count [start #\a] [step 1])
(let loop ([i 0] [curr (char->integer start)])
(if (< i count)
(cons (integer->char curr)
(loop (add1 i) (+ curr step)))
null)))
(define (assoc-value key alist)
(let ([kvp (assoc key alist)])
(if kvp
(cdr kvp)
(error "assoc-value: key not found:" key alist))))
(define (assoc-value/default key alist default)
(define kvp (assoc key alist))
(if kvp
(cdr kvp)
default))
(define (alist-set key val alist [same? equal?])
(define found #f)
(define new-alist
(alist-map (lambda (key1 val1)
(if (same? key key1)
(begin (set! found #t)
(cons key1 val))
(begin (cons key1 val1))))
alist))
(if found
new-alist
(append new-alist
(list (cons key val)))))
(define (alist-map proc alist)
(map (match-lambda
[(list-rest key val)
(proc key val)]
[other (raise-exn exn:fail:contract
(format "Expected (listof pair), recevied ~s" alist))])
alist))
(define (alist-for-each proc alist)
(for-each (match-lambda
[(list-rest key val)
(proc key val)]
[other (raise-exn exn:fail:contract
(format "Expected (listof pair), recevied ~s" alist))])
alist))
(define (alist-merge list1 list2 [prefer 'first] [find assoc])
(define proc
(if (eq? prefer 'first)
(lambda (item accum)
(let ([key (car item)]
[x (car accum)]
[y (cdr accum)])
(if (find key x)
(cons x y)
(cons x (cons item y)))))
(lambda (item accum)
(let ([key (car item)]
[x (car accum)]
[y (cdr accum)])
(if (find key x)
(cons (alist-set key (cdr item) x) y)
(cons x (cons item y)))))))
(let ([x-and-y (fold proc (cons list1 null) list2)])
(append (car x-and-y) (reverse (cdr x-and-y)))))
(define qlist/c (or/c pair? null?))
(provide assemble-list
alist-delete)
(provide/contract
[make-list* (-> natural? (cons/c any/c list?) list?)]
[in-list/cycle (-> (cons/c any/c list?) sequence?)]
[list-swap (-> qlist/c any/c any/c any)]
[list-delimit (-> qlist/c any/c any)]
[merge-sorted-lists (-> qlist/c qlist/c (arity/c 2) (arity/c 2) any)]
[char-iota (->* (integer?) (char? integer?) any)]
[list-pad (->* (qlist/c integer?) (any/c) any)]
[list-pad-right (->* (qlist/c integer?) (any/c) any)]
[assoc-value (-> any/c qlist/c any)]
[assoc-value/default (-> any/c qlist/c any/c any)]
[alist-set (->* (any/c any/c qlist/c) (procedure?) any)]
[alist-map (-> (arity/c 2) qlist/c any)]
[alist-for-each (-> (arity/c 2) qlist/c any)]
[alist-merge (->* (qlist/c qlist/c) ((symbols 'first 'second)) any)])