#lang typed/racket #:optimize
(require (prefix-in sh: scheme/base))
(provide filter remove
list ->list empty? cons empty head tail
(rename-out [first* first] [rest* rest] [list-map map]
[list-foldr foldr] [list-foldl foldl])
list-ref list-set drop list-length List)
(define-struct: (A) Leaf ([fst : A]))
(define-struct: (A) Node ([fst : A]
[lft : (Tree A)]
[rgt : (Tree A)]))
(define-type-alias Tree (All (A) (U (Leaf A) (Node A))))
(define-struct: (A) Root ([weight : Integer]
[fst : (Tree A)]))
(define-type-alias List (All (A) (Listof (Root A))))
(define empty null)
(: empty? : (All (A) ((List A) -> Boolean)))
(define (empty? sralist)
(null? sralist))
(: getWeight : (All (A) ((Root A) -> Integer)))
(define (getWeight root)
(Root-weight root))
(: cons : (All (A) (A (List A) -> (List A))))
(define (cons elem sralist)
(if (or (null? sralist) (null? (cdr sralist)))
(sh:cons (make-Root 1 (make-Leaf elem)) sralist)
(let ([wgt1 (getWeight (car sralist))]
[wgt2 (getWeight (car (cdr sralist)))])
(if (eq? wgt1 wgt2)
(sh:cons (make-Root (+ 1 wgt1 wgt2)
(make-Node elem
(Root-fst (car sralist))
(Root-fst (car (cdr sralist)))))
(cdr (cdr sralist)))
(sh:cons (make-Root 1 (make-Leaf elem)) sralist)))))
(: head : (All (A) ((List A) -> A)))
(define (head sralist)
(if (null? sralist)
(error 'head "given list is empty")
(let ([fst (Root-fst (car sralist))])
(if (Leaf? fst)
(Leaf-fst fst)
(Node-fst fst)))))
(: tail : (All (A) ((List A) -> (List A))))
(define (tail sralist)
(if (null? sralist)
(error 'tail "given list is empty")
(let* ([fst (Root-fst (car sralist))]
[wgt (arithmetic-shift (getWeight (car sralist)) -1)])
(if (Leaf? fst)
(cdr sralist)
(list* (make-Root wgt (Node-lft fst))
(make-Root wgt (Node-rgt fst))
(cdr sralist))))))
(: tree-lookup : (All (A) (Integer (Tree A) Integer -> A)))
(define (tree-lookup wgt tre pos)
(let ([new-wgt (arithmetic-shift wgt -1)]
[pos0? (zero? pos)])
(cond
[(and (Leaf? tre) pos0?) (Leaf-fst tre)]
[(Node? tre) (tl-help new-wgt tre pos pos0?)]
[else (error 'list-ref "given index out of bounds")])))
(: tl-help : (All (A) (Integer (Node A) Integer Boolean -> A)))
(define (tl-help new-wgt tre pos pos0?)
(cond
[pos0? (Node-fst tre)]
[(<= pos new-wgt)
(tree-lookup new-wgt (Node-lft tre) (sub1 pos))]
[else (tree-lookup new-wgt (Node-rgt tre) (- pos 1 new-wgt))]))
(: tree-update : (All (A) (Integer (Tree A) Integer A -> (Tree A))))
(define (tree-update wgt tre pos elem)
(let ([new-wgt (arithmetic-shift wgt -1)]
[pos0? (zero? pos)])
(cond
[(and (Leaf? tre) pos0?) (make-Leaf elem)]
[(Node? tre) (tu-help new-wgt tre pos pos0? elem)]
[else (error 'list-set "given index out of bounds")])))
(: tu-help : (All (A) (Integer (Node A) Integer Boolean A -> (Tree A))))
(define (tu-help new-wgt tre pos pos0? elem)
(let ([lft (Node-lft tre)]
[rgt (Node-rgt tre)]
[fst (Node-fst tre)])
(cond
[pos0? (make-Node elem lft rgt)]
[(<= pos new-wgt) (make-Node fst
(tree-update new-wgt lft (sub1 pos) elem)
rgt)]
[else (make-Node fst lft (tree-update new-wgt rgt
(- pos 1 new-wgt) elem))])))
(: list-ref : (All (A) ((List A) Integer -> A)))
(define (list-ref sralist pos)
(cond
[(null? sralist) (error 'list-ref "given index out of bounds")]
[(< pos (getWeight (car sralist)))
(tree-lookup (getWeight (car sralist)) (Root-fst (car sralist)) pos)]
[else (list-ref (cdr sralist) (- pos (getWeight (car sralist))))]))
(: list-set : (All (A) ((List A) Integer A -> (List A))))
(define (list-set sralist pos elem)
(cond
[(null? sralist) (error 'list-set "given index out of bounds")]
[(< pos (getWeight (car sralist)))
(sh:cons (make-Root (getWeight (car sralist))
(tree-update (getWeight (car sralist))
(Root-fst (car sralist)) pos elem))
(cdr sralist))]
[else (sh:cons (car sralist)
(list-set (cdr sralist)
(- pos (getWeight (car sralist)))
elem))]))
(: tree-drop : (All (A) (Integer (Tree A) Integer (List A) -> (List A))))
(define (tree-drop size tre pos ralist)
(let ([newsize (arithmetic-shift size -1)])
(cond
[(zero? pos) (sh:cons (make-Root size tre) ralist)]
[(and (Leaf? tre) (= pos 1)) ralist]
[(and (Node? tre) (<= pos newsize))
(tree-drop newsize
(Node-lft tre) (- pos 1)
(sh:cons (make-Root newsize (Node-rgt tre)) ralist))]
[(and (Node? tre) (> pos newsize))
(tree-drop newsize
(Node-rgt tre) (- pos 1 newsize)
ralist)]
[else (error 'drop "not enough elements to drop")])))
(: drop : (All (A) (Integer (List A) -> (List A))))
(define (drop pos ralist)
(cond
[(zero? pos) ralist]
[(null? ralist) (error 'drop "not enough elements to drop")]
[else (drop-helper (car ralist) (cdr ralist) pos)]))
(: drop-helper : (All (A) ((Root A) (List A) Integer -> (List A))))
(define (drop-helper root rest pos)
(let ([size (Root-weight root)]
[tree (Root-fst root)])
(if (< pos size)
(tree-drop size tree pos rest)
(drop (- pos size) rest))))
(: list-length : (All (A) ((List A) -> Integer)))
(define (list-length ralist)
(foldl + 0 (map (inst getWeight A) ralist)))
(: list-map :
(All (A C B ...)
(case-lambda
((A -> C) (List A) -> (List C))
((A B ... B -> C) (List A) (List B) ... B -> (List C)))))
(define list-map
(pcase-lambda: (A C B ...)
[([func : (A -> C)]
[list : (List A)])
(if (empty? list)
empty
(cons (func (head list)) (list-map func (tail list))))]
[([func : (A B ... B -> C)]
[list : (List A)] . [lists : (List B) ... B])
(if (or (empty? list) (ormap empty? lists))
empty
(cons (apply func (head list) (map head lists))
(apply list-map func (tail list)
(map tail lists))))]))
(: list-foldr :
(All (A C B ...)
(case-lambda ((C A -> C) C (List A) -> C)
((C A B ... B -> C) C (List A) (List B) ... B -> C))))
(define list-foldr
(pcase-lambda: (A C B ...)
[([func : (C A -> C)]
[base : C]
[list : (List A)])
(if (empty? list)
base
(func (list-foldr func base (tail list))
(head list)))]
[([func : (C A B ... B -> C)]
[base : C]
[list : (List A)] . [lists : (List B) ... B])
(if (or (empty? list) (ormap empty? lists))
base
(apply func (apply list-foldr func base (tail list)
(map tail lists))
(head list) (map head lists)))]))
(: list-foldl :
(All (A C B ...)
(case-lambda ((C A -> C) C (List A) -> C)
((C A B ... B -> C) C (List A) (List B) ... B -> C))))
(define list-foldl
(pcase-lambda: (A C B ...)
[([func : (C A -> C)]
[base : C]
[list : (List A)])
(if (empty? list)
base
(list-foldl func (func base (head list)) (tail list)))]
[([func : (C A B ... B -> C)]
[base : C]
[list : (List A)] . [lists : (List B) ... B])
(if (or (empty? list) (ormap empty? lists))
base
(apply list-foldl func
(apply func base (head list) (map head lists))
(tail list) (map tail lists)))]))
(: ->list : (All (A) ((List A) -> (Listof A))))
(define (->list ralist)
(if (empty? ralist)
null
(sh:cons (head ralist) (->list (tail ralist)))))
(: list : (All (A) (A * -> (List A))))
(define (list . lst)
(foldr (inst cons A) null lst))
(define first* head)
(define rest* tail)
(: filter : (All (A) ((A -> Boolean) (List A) -> (List A))))
(define (filter func ral)
(if (empty? ral)
empty
(let ([head (head ral)]
[tail (tail ral)])
(if (func head)
(cons head (filter func tail))
(filter func tail)))))
(: remove : (All (A) ((A -> Boolean) (List A) -> (List A))))
(define (remove func ral)
(if (empty? ral)
empty
(let ([head (head ral)]
[tail (tail ral)])
(if (func head)
(remove func tail)
(cons head (remove func tail))))))
(: reverse : (All (A) ((List A) -> (List A))))
(define (reverse ral)
(: local-reverse : (All (A) ((List A) (List A) -> (List A))))
(define (local-reverse ral accum)
(if (empty? ral)
accum
(local-reverse (tail ral) (cons (head ral) accum))))
(local-reverse ral empty))