PasteRack.org
Paste # 95307
2019-02-15 01:11:31

Fork as a new paste.

Paste viewed 331 times.


Embed:

Where Did I Go Wrong WIth TR and Quadtrees

  1. #lang typed/racket
  2.  
  3. ; a naive hack at quadtrees
  4. ; https://www.researchgate.net/profile/Raphael_Finkel/publication/220197855_Quad_Trees_A_Data_Structure_for_Retrieval_on_Composite_Keys/links/0c9605273bba2ece7b000000/Quad-Trees-A-Data-Structure-for-Retrieval-on-Composite-Keys.pdf
  5.  
  6. ; Mutually recursive with the Quadtree type. The node structure means that
  7. ; every node has coordinates embedded, and four children, which may be nodes or nulls.
  8. (struct node ([ NE : Quadtree ]
  9.               [ NW : Quadtree ]
  10.               [ SW : Quadtree ]
  11.               [ SE : Quadtree ]
  12.               [ coords : coordinate]))
  13.  
  14. ; A union type: a Quadtree will be null or a node.
  15. (define-type Quadtree (U Null node))
  16.  
  17. ; I don't know if this will help the compiler optimize, but I figure we don't actually
  18. ; need this to be an integer, so I'm limiting Quadenum to these five Symbol literals.
  19. (define-type Quadenum (U 'NE 'NW 'SW 'SE 'self))
  20.  
  21. ; X and Y coordinates, since Quadtrees' composite keys are two-dimensional constructs.
  22. (struct coordinate ([x : Integer] [y : Integer]))
  23.  
  24. ; The comparison function, it seems to me, is the beating heart at the center of quadtrees.
  25. ; You just gotta discern which quadrant below a node to categorize a set of coordinates in.
  26. ; Also, exact match/collisions.
  27. (: coordinate-compare (-> coordinate coordinate Quadenum))
  28. (define (coordinate-compare a b)
  29.   (cond [(and (equal? (coordinate-x a) (coordinate-x b))
  30.               (equal? (coordinate-y a) (coordinate-y b))) 'self]
  31.         [(and (<= (coordinate-x a) (coordinate-x b))                 ; Why are some of these -or-equals?
  32.               (< (coordinate-y a) (coordinate-y b))) 'NE]            ; To keep the tree perfectly balanced, as all things should be.
  33.         [(and (> (coordinate-x a) (coordinate-x b))                  ; Otherwise, two quadrants would get all the luck.
  34.               (<= (coordinate-y a) (coordinate-y b))) 'NW]           ; Any bias adds up and unbalances the tree.
  35.         [(and (>= (coordinate-x a) (coordinate-x b))                 ; Therefore, each quadrant owns a cardinal direction of its own.
  36.               (> (coordinate-y a) (coordinate-y b))) 'SW]
  37.         [(and (< (coordinate-x a) (coordinate-x b))
  38.               (>= (coordinate-y a) (coordinate-y b))) 'SE]
  39.         [else (raise "How did you get here that is not how integers work")])) ; I am confident the other options are exhaustive.
  40.  
  41. ; Determine if a particular coordinate pair is a member of a quadtree.
  42. (: quadtree-member? (-> Quadtree coordinate Boolean))
  43. (define (quadtree-member? quadtree coords)
  44.   (cond [(null? quadtree) #f] ; Nulls have no children.
  45.         [else
  46.          (let ([quadrant (coordinate-compare (node-coords quadtree) coords)])
  47.            (cond [(equal? quadrant 'self) #t]
  48.                  [else (quadtree-member? (map-directions quadtree quadrant) coords)]))]))
  49.  
  50. ; A mapping of struct accessor functions to the non-self enumerators returned by coordinate-compare.
  51. (: map-directions (-> node Quadenum Quadtree))
  52. (define (map-directions node quadenum)
  53.   (cond [(equal? quadenum 'NE) (node-NE node)]
  54.         [(equal? quadenum 'NW) (node-NW node)]
  55.         [(equal? quadenum 'SW) (node-SW node)]
  56.         [(equal? quadenum 'SE) (node-SE node)]
  57.         [else (raise "Don't call this on yourself!!")])) ; Self should never get mapped to.
  58.  
  59. ; Insertion
  60. ; Given a null Quadtree, return a new Quadtree with null children, containing the coordinates.
  61. ; Given a Quadtree whose coordinates equal those supplied, return the original Quadtree.
  62. ; Given a non-null, non-matching node, recursively traverse to a null child and return a new tree with the ocordinates at that location.
  63. (: quadtree-insert (-> Quadtree coordinate node))
  64. (define (quadtree-insert quadtree coords)
  65.   (cond [(null? quadtree) (node null null null null coords)]                              ; You've landed: returning a new leaf on the tree.
  66.         [(equal? 'self (coordinate-compare (node-coords quadtree) coords)) quadtree]      ; This quadtree contains the requested coordinates. Return original quadtree.
  67.         [else
  68.          (let* ([quadrant (coordinate-compare (node-coords quadtree) coords)]             ; The quadrant these coordinates belong to on quadtree.
  69.                 [recurse (quadtree-insert (map-directions quadtree quadrant) coords)]     ; Reach down `quadrant' and hand up what's down there.
  70.                 [NE (if (equal? quadrant 'NE) recurse (node-NE quadtree))]                ; Ensure correct arity when assembling this node's representation to hand up
  71.                 [NW (if (equal? quadrant 'NW) recurse (node-NW quadtree))]                ; the stack. Only one of these will call 'recurse', and that one will
  72.                 [SW (if (equal? quadrant 'SW) recurse (node-SW quadtree))]                ; be inserted into the final product in the correct field
  73.                 [SE (if (equal? quadrant 'SE) recurse (node-SE quadtree))])
  74.            (node NE NW SW SE (node-coords quadtree)))])) ; Here, we package up everything we know about the current Quadtree from this point down and send it up the recursion stack.
  75.  
  76.  
  77. ; The above doesn't seem obviously wrong to me. However:
  78. (node-coords (node-NE (quadtree-insert (quadtree-insert (quadtree-insert null (coordinate 0 0))  (coordinate 10 10)) (coordinate 20 20))))
  79. ; This fails. Which is odd, because
  80. ;(node? (node-NE (quadtree-insert (quadtree-insert (quadtree-insert null (coordinate 0 0))  (coordinate 10 10)) (coordinate 20 20))))
  81. ; returns #t.  The type checker says there's a type mismatch because node-coords is expecting a node but is given a Quadtree.

=>