radix-tree.el 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. ;;; radix-tree.el --- A simple library of radix trees -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2016-2017 Free Software Foundation, Inc.
  3. ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
  4. ;; Keywords:
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; There are many different options for how to represent radix trees
  18. ;; in Elisp. Here I chose a very simple one. A radix-tree can be either:
  19. ;; - a node, of the form ((PREFIX . PTREE) . RTREE) where PREFIX is a string
  20. ;; meaning that everything that starts with PREFIX is in PTREE,
  21. ;; and everything else in RTREE. It also has the property that
  22. ;; everything that starts with the first letter of PREFIX but not with
  23. ;; that whole PREFIX is not in RTREE (i.e. is not in the tree at all).
  24. ;; - anything else is taken as the value to associate with the empty string.
  25. ;; So every node is basically an (improper) alist where each mapping applies
  26. ;; to a different leading letter.
  27. ;;
  28. ;; The main downside of this representation is that the lookup operation
  29. ;; is slower because each level of the tree is an alist rather than some kind
  30. ;; of array, so every level's lookup is O(N) rather than O(1). We could easily
  31. ;; solve this by using char-tables instead of alists, but that would make every
  32. ;; level take up a lot more memory, and it would make the resulting
  33. ;; data structure harder to read (by a human) when printed out.
  34. ;;; Code:
  35. (defun radix-tree--insert (tree key val i)
  36. (pcase tree
  37. (`((,prefix . ,ptree) . ,rtree)
  38. (let* ((ni (+ i (length prefix)))
  39. (cmp (compare-strings prefix nil nil key i ni)))
  40. (if (eq t cmp)
  41. (let ((nptree (radix-tree--insert ptree key val ni)))
  42. `((,prefix . ,nptree) . ,rtree))
  43. (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
  44. (if (zerop n)
  45. (let ((nrtree (radix-tree--insert rtree key val i)))
  46. `((,prefix . ,ptree) . ,nrtree))
  47. (let* ((nprefix (substring prefix 0 n))
  48. (kprefix (substring key (+ i n)))
  49. (pprefix (substring prefix n))
  50. (ktree (if (equal kprefix "") val
  51. `((,kprefix . ,val)))))
  52. `((,nprefix
  53. . ((,pprefix . ,ptree) . ,ktree))
  54. . ,rtree)))))))
  55. (_
  56. (if (= (length key) i) val
  57. (let ((prefix (substring key i)))
  58. `((,prefix . ,val) . ,tree))))))
  59. (defun radix-tree--remove (tree key i)
  60. (pcase tree
  61. (`((,prefix . ,ptree) . ,rtree)
  62. (let* ((ni (+ i (length prefix)))
  63. (cmp (compare-strings prefix nil nil key i ni)))
  64. (if (eq t cmp)
  65. (pcase (radix-tree--remove ptree key ni)
  66. (`nil rtree)
  67. (`((,pprefix . ,pptree))
  68. `((,(concat prefix pprefix) . ,pptree) . ,rtree))
  69. (nptree `((,prefix . ,nptree) . ,rtree)))
  70. (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
  71. (if (zerop n)
  72. (let ((nrtree (radix-tree--remove rtree key i)))
  73. `((,prefix . ,ptree) . ,nrtree))
  74. tree)))))
  75. (_
  76. (if (= (length key) i) nil tree))))
  77. (defun radix-tree--lookup (tree string i)
  78. (pcase tree
  79. (`((,prefix . ,ptree) . ,rtree)
  80. (let* ((ni (+ i (length prefix)))
  81. (cmp (compare-strings prefix nil nil string i ni)))
  82. (if (eq t cmp)
  83. (radix-tree--lookup ptree string ni)
  84. (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
  85. (if (zerop n)
  86. (radix-tree--lookup rtree string i)
  87. (+ i n))))))
  88. (val
  89. (if (and val (equal (length string) i))
  90. (if (integerp val) `(t . ,val) val)
  91. i))))
  92. ;; (defun radix-tree--trim (tree string i)
  93. ;; (if (= i (length string))
  94. ;; tree
  95. ;; (pcase tree
  96. ;; (`((,prefix . ,ptree) . ,rtree)
  97. ;; (let* ((ni (+ i (length prefix)))
  98. ;; (cmp (compare-strings prefix nil nil string i ni))
  99. ;; ;; FIXME: We could compute nrtree more efficiently
  100. ;; ;; whenever cmp is not -1 or 1.
  101. ;; (nrtree (radix-tree--trim rtree string i)))
  102. ;; (if (eq t cmp)
  103. ;; (pcase (radix-tree--trim ptree string ni)
  104. ;; (`nil nrtree)
  105. ;; (`((,pprefix . ,pptree))
  106. ;; `((,(concat prefix pprefix) . ,pptree) . ,nrtree))
  107. ;; (nptree `((,prefix . ,nptree) . ,nrtree)))
  108. ;; (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
  109. ;; (cond
  110. ;; ((equal (+ n i) (length string))
  111. ;; `((,prefix . ,ptree) . ,nrtree))
  112. ;; (t nrtree))))))
  113. ;; (val val))))
  114. (defun radix-tree--prefixes (tree string i prefixes)
  115. (pcase tree
  116. (`((,prefix . ,ptree) . ,rtree)
  117. (let* ((ni (+ i (length prefix)))
  118. (cmp (compare-strings prefix nil nil string i ni))
  119. ;; FIXME: We could compute prefixes more efficiently
  120. ;; whenever cmp is not -1 or 1.
  121. (prefixes (radix-tree--prefixes rtree string i prefixes)))
  122. (if (eq t cmp)
  123. (radix-tree--prefixes ptree string ni prefixes)
  124. prefixes)))
  125. (val
  126. (if (null val)
  127. prefixes
  128. (cons (cons (substring string 0 i)
  129. (if (eq (car-safe val) t) (cdr val) val))
  130. prefixes)))))
  131. (defun radix-tree--subtree (tree string i)
  132. (if (equal (length string) i) tree
  133. (pcase tree
  134. (`((,prefix . ,ptree) . ,rtree)
  135. (let* ((ni (+ i (length prefix)))
  136. (cmp (compare-strings prefix nil nil string i ni)))
  137. (if (eq t cmp)
  138. (radix-tree--subtree ptree string ni)
  139. (let ((n (if (< cmp 0) (- -1 cmp) (- cmp 1))))
  140. (cond
  141. ((zerop n) (radix-tree--subtree rtree string i))
  142. ((equal (+ n i) (length string))
  143. (let ((nprefix (substring prefix n)))
  144. `((,nprefix . ,ptree))))
  145. (t nil))))))
  146. (_ nil))))
  147. ;;; Entry points
  148. (defconst radix-tree-empty nil
  149. "The empty radix-tree.")
  150. (defun radix-tree-insert (tree key val)
  151. "Insert a mapping from KEY to VAL in radix TREE."
  152. (when (consp val) (setq val `(t . ,val)))
  153. (if val (radix-tree--insert tree key val 0)
  154. (radix-tree--remove tree key 0)))
  155. (defun radix-tree-lookup (tree key)
  156. "Return the value associated to KEY in radix TREE.
  157. If not found, return nil."
  158. (pcase (radix-tree--lookup tree key 0)
  159. (`(t . ,val) val)
  160. ((pred numberp) nil)
  161. (val val)))
  162. (defun radix-tree-subtree (tree string)
  163. "Return the subtree of TREE rooted at the prefix STRING."
  164. (radix-tree--subtree tree string 0))
  165. ;; (defun radix-tree-trim (tree string)
  166. ;; "Return a TREE which only holds entries \"related\" to STRING.
  167. ;; \"Related\" is here defined as entries where there's a `string-prefix-p' relation
  168. ;; between STRING and the key."
  169. ;; (radix-tree-trim tree string 0))
  170. (defun radix-tree-prefixes (tree string)
  171. "Return an alist of all bindings in TREE for prefixes of STRING."
  172. (radix-tree--prefixes tree string 0 nil))
  173. (eval-and-compile
  174. (pcase-defmacro radix-tree-leaf (vpat)
  175. ;; FIXME: We'd like to use a negative pattern (not consp), but pcase
  176. ;; doesn't support it. Using `atom' works but generates sub-optimal code.
  177. `(or `(t . ,,vpat) (and (pred atom) ,vpat))))
  178. (defun radix-tree-iter-subtrees (tree fun)
  179. "Apply FUN to every immediate subtree of radix TREE.
  180. FUN is called with two arguments: PREFIX and SUBTREE.
  181. You can test if SUBTREE is a leaf (and extract its value) with the
  182. pcase pattern (radix-tree-leaf PAT)."
  183. (while tree
  184. (pcase tree
  185. (`((,prefix . ,ptree) . ,rtree)
  186. (funcall fun prefix ptree)
  187. (setq tree rtree))
  188. (_ (funcall fun "" tree)
  189. (setq tree nil)))))
  190. (defun radix-tree-iter-mappings (tree fun &optional prefix)
  191. "Apply FUN to every mapping in TREE.
  192. FUN is called with two arguments: KEY and VAL.
  193. PREFIX is only used internally."
  194. (radix-tree-iter-subtrees
  195. tree
  196. (lambda (p s)
  197. (let ((nprefix (concat prefix p)))
  198. (pcase s
  199. ((radix-tree-leaf v) (funcall fun nprefix v))
  200. (_ (radix-tree-iter-mappings s fun nprefix)))))))
  201. ;; (defun radix-tree->alist (tree)
  202. ;; (let ((al nil))
  203. ;; (radix-tree-iter-mappings tree (lambda (p v) (push (cons p v) al)))
  204. ;; al))
  205. (defun radix-tree-count (tree)
  206. (let ((i 0))
  207. (radix-tree-iter-mappings tree (lambda (_k _v) (setq i (1+ i))))
  208. i))
  209. (defun radix-tree-from-map (map)
  210. ;; Aka (cl-defmethod map-into (map (type (eql radix-tree)))) ...)
  211. (require 'map)
  212. (let ((rt nil))
  213. (map-apply (lambda (k v) (setq rt (radix-tree-insert rt k v))) map)
  214. rt))
  215. (provide 'radix-tree)
  216. ;;; radix-tree.el ends here