emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master a7732cf: * externals-list: Convert `dict-tree` from subtre


From: Stefan Monnier
Subject: [elpa] master a7732cf: * externals-list: Convert `dict-tree` from subtree to external
Date: Mon, 14 Dec 2020 12:22:39 -0500 (EST)

branch: master
commit a7732cf7000cda62089039621c91af4b4d0d14cf
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>

    * externals-list: Convert `dict-tree` from subtree to external
---
 externals-list                  |    1 +
 packages/dict-tree/dict-tree.el | 4115 ---------------------------------------
 2 files changed, 1 insertion(+), 4115 deletions(-)

diff --git a/externals-list b/externals-list
index 4910dae..c6549d6 100644
--- a/externals-list
+++ b/externals-list
@@ -97,6 +97,7 @@
  ("dbus-codegen"       :external "https://github.com/ueno/dbus-codegen-el.git";)
  ("debbugs"            :external nil)
  ("delight"            :external "https://git.savannah.gnu.org/r/delight.git";)
+ ("dict-tree"          :external "http://www.dr-qubit.org/git/predictive.git";)
  ("diffview"            :external 
"https://github.com/mgalgs/diffview-mode.git";)
  ("diff-hl"            :external "https://github.com/dgutov/diff-hl.git";)
  ("dired-du"           :external nil)
diff --git a/packages/dict-tree/dict-tree.el b/packages/dict-tree/dict-tree.el
deleted file mode 100644
index 0930a34..0000000
--- a/packages/dict-tree/dict-tree.el
+++ /dev/null
@@ -1,4115 +0,0 @@
-;;; dict-tree.el --- Dictionary data structure  -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2004-2015, 2017  Free Software Foundation, Inc
-
-;; Author: Toby Cubitt <toby-predictive@dr-qubit.org>
-;; Version: 0.14
-;; Keywords: extensions, matching, data structures
-;;           trie, tree, dictionary, completion, regexp
-;; Package-Requires: ((trie "0.3") (tNFA "0.1.1") (heap "0.3"))
-;; URL: http://www.dr-qubit.org/emacs.php
-
-;; This file is part of Emacs.
-;;
-;; GNU Emacs is free software: you can redistribute it and/or modify it under
-;; the terms of the GNU General Public License as published by the Free
-;; Software Foundation, either version 3 of the License, or (at your option)
-;; any later version.
-;;
-;; GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
-;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-;; more details.
-;;
-;; You should have received a copy of the GNU General Public License along
-;; with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-
-;;; Commentary:
-;;
-;; A dict-tree (created using `dictree-create') is used to store strings,
-;; along with arbitrary data associated with each string. (Note that the
-;; "strings" can be any sequence data type, not just Elisp strings.) As well
-;; as basic data insertion (`dictree-insert'), manipulation
-;; (`dictree-insert'), and retrieval (`dictree-lookup', `dictree-member-p'), a
-;; dict-tree can perform sophisticated queries on strings, including:
-;;
-;; - retrieve all completions of a prefix
-;;   (`dictree-complete')
-;;
-;; - retrieve all strings that match a regular expression
-;;   (`dictree-regexp-search')
-;;
-;; - retrieve all fuzzy matches to a string, i.e. matches within a specified
-;;   Lewenstein distance (a.k.a. edit distance)
-;;   (`dictree-fuzzy-match')
-;;
-;; - retrieve all fuzzy completions of a prefix, i.e. completions of prefixes
-;;   within a specified Lewenstein distance
-;;   (`dictree-fuzzy-complete')
-;;
-;; The results of all of these queries can be ranked in alphabetical order, or
-;; according to any other desired ranking. The results can also be limited to
-;; a given number of matches.
-;;
-;; These sophisticated string queries are fast even for very large dict-trees,
-;; and dict-tree's also cache query results (and automatically keep these
-;; caches synchronised) to speed up queries even further.
-;;
-;; Other functions allow you to:
-;;
-;; - create dict-tree stack objects, which allow efficient access to the
-;;   strings in the dictionary or in query results as though they were sorted
-;;   on a stack (useful for designing efficient algorithms on top of
-;;   dict-trees)
-;;   (`dictree-stack', `dictree-complete-stack', `dictree-regexp-stack',
-;;    `dictree-fuzzy-match-stack', `dictree-fuzzy-complete-stack')
-;;
-;; - generate dict-tree iterator objects which allow you to retrieve
-;;   successive elements by calling `iter-next'
-;;   (`dictree-iter', `dictree-complete-iter', `dictree-regexp-iter',
-;;    `dictree-fuzzy-match-iter', `dictree-fuzzy-complete-iter')
-;;
-;; - map over all strings in alphabetical order
-;;   (`dictree-mapc', `dictree-mapcar' and `dictree-mapf')
-;;
-;; Dict-trees can be combined together into a "meta dict-tree", which combines
-;; the data from identical keys in its constituent dict-trees, in whatever way
-;; you specify (`dictree-create-meta-dict'). Any number of dict-trees can be
-;; combined in this way. Meta-dicts behave *exactly* like dict-trees: all of
-;; the above functions work on meta-dicts as well as dict-trees, and
-;; meta-dicts can themselves be used in new meta-dicts.
-;;
-;; The package also provides persistent storage of dict-trees to file.
-;; (`dictree-save', `dictree-write', `dictee-load')
-;;
-;; This package uses the trie package trie.el, the tagged NFA package tNFA.el,
-;; and the heap package heap.el.
-
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-(require 'trie)
-(require 'tNFA)
-
-
-;;; ================================================================
-;;;            Replacements for CL and Elisp functions
-
-;; copied from cl-extra.el
-(defun dictree--subseq (seq start &optional end)
-  "Return the subsequence of SEQ from START to END.
-If END is omitted, it defaults to the length of the sequence.
-If START or END is negative, it counts from the end."
-  (if (stringp seq) (substring seq start end)
-    (let (len)
-      (and end (< end 0) (setq end (+ end (setq len (length seq)))))
-      (when (< start 0)
-       (setq start (+ start (or len (setq len (length seq))))))
-      (cond ((listp seq)
-            (if (> start 0) (setq seq (nthcdr start seq)))
-            (if end
-                (let ((res nil))
-                  (while (>= (setq end (1- end)) start)
-                    (push (pop seq) res))
-                  (nreverse res))
-              (copy-sequence seq)))
-           (t
-            (or end (setq end (or len (length seq))))
-            (let ((res (make-vector (max (- end start) 0) nil))
-                  (i 0))
-              (while (< start end)
-                (aset res i (aref seq start))
-                (setq i (1+ i) start (1+ start)))
-              res))))))
-
-
-
-;; `goto-line' without messing around with mark and messages
-(defun dictree--goto-line (line)
-  "Goto line LINE, counting from line 1 at beginning of buffer."
-  (goto-char 1)
-  (if (eq selective-display t)
-      (re-search-forward "[\n\C-m]" nil 'no-error (1- line))
-    (forward-line (1- line))))
-
-
-
-
-;;; ====================================================================
-;;;  Internal functions and variables for use in the dictionary package
-
-(defvar dictree-loaded-list nil
-  "Stores list of loaded dictionaries.")
-
-
-;; ----------------------------------------------------------------
-;;                   Dictionary data cell structures
-
-;; Note: It would be more elegant to use a defstruct for the data cells,
-;;       but the problem is that the resulting setf in
-;;       `dictree--wrap-insfun' won't get expanded into the cell-data
-;;       accessor function at compile-time because it's burried inside a
-;;       backquote construct. Not only is it inelegant to have to expand
-;;       macros at run-time whenever `dictree--wrap-insfun' is called,
-;;       but it also requires the 'cl-macs package to be loaded at
-;;       run-time rather than just at compile-time. We could use
-;;       `lexical-let' instead, but it doesn't seem worth it here.
-
-;; wrap data in a cons cell
-(defalias 'dictree--cell-create 'cons)  ; INTERNAL USE ONLY
-
-;; get data component from data cons cell
-(defalias 'dictree--cell-data 'car)  ; INTERNAL USE ONLY
-
-;; get property list component from data cons cell
-(defalias 'dictree--cell-plist 'cdr)  ; INTERNAL USE ONLY
-
-;; set data component of data cons cell
-(defalias 'dictree--cell-set-data 'setcar)  ; INTERNAL USE ONLY
-
-;; set property list component of data cons cell
-(defalias 'dictree--cell-set-plist 'setcdr)  ; INTERNAL USE ONLY
-
-;; define setf methods so we can use setf abstraction wherever possible
-(defsetf dictree--cell-data dictree--cell-set-data)
-(defsetf dictree--cell-plist dictree--cell-set-plist)
-
-
-;; ----------------------------------------------------------------
-;;                 Dictionary cache entry structures
-
-;; Note: We *could* use a defstruct for the cache entries, but for
-;;       something this simple it doesn't seem worth it, especially
-;;       given that we're using the defalias approach anyway for the
-;;       data cells (above).
-
-;; Construct and return a completion cache entry
-(defalias 'dictree--cache-create 'cons)  ; INTERNAL USE ONLY
-
-;; Return the completions list for cache entry CACHE
-(defalias 'dictree--cache-results 'car)  ; INTERNAL USE ONLY
-
-;; Return the max number of completions returned for cache entry CACHE
-(defalias 'dictree--cache-maxnum 'cdr)  ; INTERNAL USE ONLY
-
-;; Set the completions list for cache entry CACHE
-(defalias 'dictree--cache-set-results 'setcar)  ; INTERNAL USE ONLY
-
-;; Set the completions list for cache entry CACHE
-(defalias 'dictree--cache-set-maxnum 'setcdr)  ; INTERNAL USE ONLY
-
-;; define setf methods so we can use setf abstraction wherever possible
-(defsetf dictree--cache-results dictree--cache-set-results)
-(defsetf dictree--cache-maxnum dictree--cache-set-maxnum)
-
-
-
-;; ----------------------------------------------------------------
-;;                     Wrapping functions
-
-;; return wrapped insfun to deal with data wrapping
-(trie--if-lexical-binding
-    (defun dictree--wrap-insfun (insfun)  ; INTERNAL USE ONLY
-      (lambda (new old)
-       (dictree--cell-set-data old (funcall insfun
-                                            (dictree--cell-data new)
-                                            (dictree--cell-data old)))
-       old))
-  (defun dictree--wrap-insfun (insfun)  ; INTERNAL USE ONLY
-    `(lambda (new old)
-       (dictree--cell-set-data old (,insfun (dictree--cell-data new)
-                                           (dictree--cell-data old)))
-       old)))
-
-
-;; return wrapped rankfun to deal with data wrapping
-(trie--if-lexical-binding
-    (defun dictree--wrap-rankfun (rankfun)  ; INTERNAL USE ONLY
-      (lambda (a b)
-       (funcall rankfun
-                (cons (car a) (dictree--cell-data (cdr a)))
-                (cons (car b) (dictree--cell-data (cdr b))))))
-  (defun dictree--wrap-rankfun (rankfun)  ; INTERNAL USE ONLY
-    `(lambda (a b)
-       (,rankfun (cons (car a) (dictree--cell-data (cdr a)))
-                (cons (car b) (dictree--cell-data (cdr b)))))))
-
-
-;; return wrapped rankfun to ignore regexp grouping data
-(trie--if-lexical-binding
-    (defun dictree--wrap-regexp-rankfun (rankfun)
-      (lambda (a b)
-       ;; if car of argument contains a key+group list rather than a straight
-       ;; key, remove group list
-       ;; FIXME: the test for straight key, below, will fail if the key is a
-       ;;        list, and the first element of the key is itself a list
-       ;;        (there might be no easy way to fully fix this...)
-       (if (or (atom (car a))
-               (and (listp (car a)) (not (sequencep (caar a)))))
-           (setq a (cons (car a) (dictree--cell-data (cdr a))))
-         (setq a (cons (caar a) (dictree--cell-data (cdr a)))))
-       (if (or (atom (car b))
-               (and (listp (car b)) (not (sequencep (caar b)))))
-           (setq b (cons (car b) (dictree--cell-data (cdr b))))
-         (setq b (cons (caar b) (dictree--cell-data (cdr b)))))
-       (funcall rankfun a b)))
-  (defun dictree--wrap-regexp-rankfun (rankfun)
-    `(lambda (a b)
-       ;; if car of argument contains a key+group list rather than a straight
-       ;; key, remove group list
-       ;; FIXME: the test for straight key, below, will fail if the key is a
-       ;;        list, and the first element of the key is itself a list
-       ;;        (there might be no easy way to fully fix this...)
-       (if (or (atom (car a))
-              (and (listp (car a)) (not (sequencep (caar a)))))
-           (setq a (cons (car a) (dictree--cell-data (cdr a))))
-        (setq a (cons (caar a) (dictree--cell-data (cdr a)))))
-       (if (or (atom (car b))
-              (and (listp (car b)) (not (sequencep (caar b)))))
-          (setq b (cons (car b) (dictree--cell-data (cdr b))))
-        (setq b (cons (caar b) (dictree--cell-data (cdr b)))))
-       (,rankfun a b))))
-
-;; return wrapped sortfun to ignore regexp grouping data
-(trie--if-lexical-binding
-    (defun dictree--wrap-regexp-sortfun (cmpfun &optional reverse)
-       (let ((sortfun (trie-construct-sortfun cmpfun reverse)))
-         (lambda (a b)
-           ;; if car of argument contains a key+group list rather than a
-           ;; straight key, remove group list
-           ;; FIXME: the test for straight key, below, will fail if the key
-           ;;        is a list, and the first element of the key is itself a
-           ;;        list (there might be no easy way to fully fix this...)
-           (if (or (atom (car a))
-                   (and (listp (car a)) (not (sequencep (caar a)))))
-               (setq a (car a))
-             (setq a (caar a)))
-           (if (or (atom (car b))
-                   (and (listp (car b)) (not (sequencep (caar b)))))
-               (setq b (car b))
-             (setq b (caar b)))
-           (funcall sortfun a b))))
-  (defun dictree--wrap-regexp-sortfun (cmpfun &optional reverse)
-    (let ((sortfun (trie-construct-sortfun cmpfun reverse)))
-      `(lambda (a b)
-        ;; if car of argument contains a key+group list rather than a
-        ;; straight key, remove group list
-        ;; FIXME: the test for straight key, below, will fail if the key
-        ;;        is a list, and the first element of the key is itself a
-        ;;        list (there might be no easy way to fully fix this...)
-        (if (or (atom (car a))
-                (and (listp (car a)) (not (sequencep (caar a)))))
-            (setq a (car a))
-          (setq a (caar a)))
-        (if (or (atom (car b))
-                (and (listp (car b)) (not (sequencep (caar b)))))
-            (setq b (car b))
-          (setq b (caar b)))
-        (,sortfun a b)))))
-
-
-;; return wrapped rankfun to ignore fuzzy query distance data
-(trie--if-lexical-binding
-    (defun dictree--wrap-fuzzy-rankfun (rankfun)  ; INTERNAL USE ONLY
-      (lambda (a b)
-       (funcall rankfun
-                (cons (nth 0 (car a)) (dictree--cell-data (cdr a)))
-                (cons (nth 0 (car b)) (dictree--cell-data (cdr b))))))
-  (defun dictree--wrap-fuzzy-rankfun (rankfun)  ; INTERNAL USE ONLY
-    `(lambda (a b)
-       (,rankfun (cons (nth 0 (car a)) (dictree--cell-data (cdr a)))
-                (cons (nth 0 (car b)) (dictree--cell-data (cdr b)))))))
-
-;; return wrapped sortfun to ignore fuzzy query distance data
-(trie--if-lexical-binding
-    (defun dictree--wrap-fuzzy-sortfun (cmpfun &optional reverse)
-      (let ((sortfun (trie-construct-sortfun cmpfun reverse)))
-       (lambda (a b) (funcall sortfun (car a) (car b)))))
-  (defun dictree--wrap-fuzzy-sortfun (cmpfun &optional reverse)
-    (let ((sortfun (trie-construct-sortfun cmpfun reverse)))
-      `(lambda (a b) (,sortfun (car a) (car b))))))
-
-
-;; return wrapped combfun to deal with data wrapping
-(trie--if-lexical-binding
-    (defun dictree--wrap-combfun (combfun)  ; INTERNAL USE ONLY
-      (lambda (cell1 cell2)
-       (dictree--cell-create
-        (funcall combfun
-                 (dictree--cell-data cell1)
-                 (dictree--cell-data cell2))
-        (append (dictree--cell-plist cell1)
-                (dictree--cell-plist cell2)))))
-  (defun dictree--wrap-combfun (combfun)  ; INTERNAL USE ONLY
-    `(lambda (cell1 cell2)
-       (dictree--cell-create
-       (,combfun (dictree--cell-data cell1)
-                 (dictree--cell-data cell2))
-       (append (dictree--cell-plist cell1)
-               (dictree--cell-plist cell2))))))
-
-
-;; return wrapped filter function to deal with data wrapping
-(trie--if-lexical-binding
-    (defun dictree--wrap-filter (filter)  ; INTERNAL USE ONLY
-      (lambda (key data) (funcall filter key (dictree--cell-data data))))
-  (defun dictree--wrap-filter (filter)  ; INTERNAL USE ONLY
-    `(lambda (key data) (,filter key (dictree--cell-data data)))))
-
-
-;; return wrapped result function to deal with data wrapping
-(trie--if-lexical-binding
-    (defun dictree--wrap-resultfun (resultfun)  ; INTERNAL USE ONLY
-      (lambda (res)
-       (funcall resultfun (car res) (dictree--cell-data (cdr res)))))
-  (defun dictree--wrap-resultfun (resultfun)  ; INTERNAL USE ONLY
-    `(lambda (res) (,resultfun (car res) (dictree--cell-data (cdr res))))))
-
-
-;; construct lexicographic sort function from DICT's comparison function
-(trie--if-lexical-binding
-    (defun dictree--construct-sortfun (dict)  ; INTERNAL USE ONLY
-      (let ((sortfun (trie-construct-sortfun
-                     (dictree-comparison-function dict))))
-       (lambda (a b) (funcall sortfun (car a) (car b)))))
-    (defun dictree--construct-sortfun (dict)  ; INTERNAL USE ONLY
-      `(lambda (a b)
-        (,(trie-construct-sortfun (dictree-comparison-function (car dict)))
-         (car a) (car b)))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                 The dictionary data structures
-
-(defstruct
-  (dictree-
-   :named
-   (:constructor nil)
-   (:constructor dictree--create
-                (&optional
-                 filename
-                 (name (and filename
-                            (file-name-sans-extension
-                             (file-name-nondirectory filename))))
-                 autosave
-                 _unlisted
-                 (comparison-function #'<)
-                 (insert-function (lambda (a _b) a))
-                 (rank-function (lambda (a b) (> (cdr a) (cdr b))))
-                 (cache-policy 'time)
-                 cache-threshold
-                 (cache-update-policy 'synchronize)
-                 key-savefun key-loadfun
-                 data-savefun data-loadfun
-                 plist-savefun plist-loadfun
-                 (trie-type 'avl)
-                 &aux
-                 (modified nil)
-                 (trie (make-trie comparison-function trie-type))
-                 (lookup-cache nil)
-                 (complete-cache nil)
-                 (regexp-cache nil)
-                 (fuzzy-match-cache nil)
-                 (fuzzy-complete-cache nil)
-                 (meta-dict-list nil)
-                 ))
-   (:constructor dictree--create-custom
-                (&optional
-                 filename
-                 (name (and filename
-                            (file-name-sans-extension
-                             (file-name-nondirectory filename))))
-                 autosave
-                 _unlisted
-                 (comparison-function #'<)
-                 (insert-function (lambda (a _b) a))
-                 (rank-function (lambda (a b) (> (cdr a) (cdr b))))
-                 (cache-policy 'time)
-                 cache-threshold
-                 (cache-update-policy 'synchronize)
-                 key-savefun key-loadfun
-                 data-savefun data-loadfun
-                 plist-savefun plist-loadfun
-                 &key
-                 createfun insertfun deletefun
-                 lookupfun mapfun emptyfun
-                 stack-createfun stack-popfun stack-emptyfun
-                 transform-for-print transform-from-read
-                 &aux
-                 (modified nil)
-                 (trie (make-trie-custom
-                        comparison-function
-                        :createfun createfun
-                        :insertfun insertfun
-                        :deletefun deletefun
-                        :lookupfun lookupfun
-                        :mapfun mapfun
-                        :emptyfun emptyfun
-                        :stack-createfun stack-createfun
-                        :stack-popfun stack-popfun
-                        :stack-emptyfun stack-emptyfun
-                        :transform-for-print transform-for-print
-                        :transform-from-read transform-from-read))
-                 (lookup-cache nil)
-                 (complete-cache nil)
-                 (regexp-cache nil)
-                 (fuzzy-match-cache nil)
-                 (fuzzy-complete-cache nil)
-                 (meta-dict-list nil)
-                 ))
-   (:copier dictree--copy))
-  name filename autosave modified
-  comparison-function insert-function rank-function
-  cache-policy cache-threshold cache-update-policy
-  lookup-cache complete-cache regexp-cache
-  fuzzy-match-cache fuzzy-complete-cache
-  key-savefun key-loadfun
-  data-savefun data-loadfun
-  plist-savefun plist-loadfun
-  trie meta-dict-list)
-
-
-(defstruct
-  (dictree--meta-dict
-   :named
-   (:constructor nil)
-   (:constructor dictree--meta-dict-create
-                (dictionary-list
-                 &optional
-                 filename
-                 (name (when filename
-                         (file-name-sans-extension
-                          (file-name-nondirectory filename))))
-                 autosave
-                 _unlisted
-                 (combine-function #'+)
-                 (cache-policy 'time)
-                 cache-threshold
-                 (cache-update-policy 'synchronize)
-                 &aux
-                 (dictlist
-                  (mapcar
-                   (lambda (dic)
-                     (cond
-                      ((dictree-p dic) dic)
-                      ((symbolp dic) (symbol-value dic))
-                      (t (error "Invalid object in DICTIONARY-LIST"))))
-                   dictionary-list))
-                 (lookup-cache nil)
-                 (complete-cache nil)
-                 (regexp-cache nil)
-                 (fuzzy-match-cache nil)
-                 (fuzzy-complete-cache nil)
-                 ))
-   (:copier dictree--meta-dict-copy))
-  name filename autosave modified combine-function
-  cache-policy cache-threshold cache-update-policy
-  lookup-cache complete-cache regexp-cache
-  fuzzy-match-cache fuzzy-complete-cache
-  dictlist meta-dict-list)
-
-
-
-
-;; ----------------------------------------------------------------
-;;           Miscelaneous internal functions and macros
-
-(defun dictree--trielist (dict)
-  ;; Return a list of all the tries on which DICT is based. If DICT is a
-  ;; meta-dict, this recursively descends the hierarchy, gathering all
-  ;; the tries from the base dictionaries.
-  (dictree--do-trielist dict))
-
-(defun dictree--do-trielist (dict)
-  (if (dictree-meta-dict-p dict)
-      (apply #'nconc (mapcar #'dictree--do-trielist
-                            (dictree--meta-dict-dictlist dict)))
-    (list (dictree--trie dict))))
-
-
-(defun dictree--merge (list1 list2 cmpfun &optional combfun maxnum)
-  ;; Destructively merge together sorted lists LIST1 and LIST2, sorting
-  ;; elements according to CMPFUN. For non-null MAXNUM, only the first
-  ;; MAXNUM are kept. For non-null COMBFUN, duplicate elements will be
-  ;; merged by passing the two elements as arguments to COMBFUN, and
-  ;; using the return value as the merged element.
-  (or (listp list1) (setq list1 (append list1 nil)))
-  (or (listp list2) (setq list2 (append list2 nil)))
-  (let (res (i 0))
-
-    ;; build up result list backwards
-    (while (and list1 list2 (or (null maxnum) (< (incf i) maxnum)))
-      ;; move smaller element to result list
-      (if (funcall cmpfun (car list1) (car list2))
-         (push (pop list1) res)
-       (if (funcall cmpfun (car list2) (car list1))
-           (push (pop list2) res)
-         ;; if elements are equal, merge them for non-null COMBFUN
-         (if combfun
-             (push (funcall combfun (pop list1) (pop list2))
-                   res)
-           ;; otherwise, add both to result list, in order
-           (push (pop list1) res)
-           (push (pop list2) res)))))
-
-    ;; return result if we already have MAXNUM entries
-    (if (and maxnum (= i maxnum))
-       (nreverse res)
-      ;; otherwise, return result plus enough leftover entries to make
-      ;; up MAXNUM (only one of list1 or list2 will be non-nil)
-      (let (tmp)
-       (or (null maxnum)
-           (and (setq tmp (nthcdr (- maxnum i 1) list1))
-                (setcdr tmp nil))
-           (and (setq tmp (nthcdr (- maxnum i 1) list2))
-                (setcdr tmp nil)))
-       (nconc (nreverse res) list1 list2)))
-    ))
-
-
-;; (defun dictree--merge-sort (list sortfun &optional combfun)
-;;   ;; Destructively sort LIST according to SORTFUN, combining
-;;   ;; identical elements using COMBFUN if supplied.
-;;   (dictree--do-merge-sort list (/ (length list) 2) sortfun combfun))
-
-
-;; (defun dictree--do-merge-sort (list1 len sortfun combfun)
-;;   ;; Merge sort LIST according to SORTFUN, combining identical
-;;   ;; elements using COMBFUN.
-;;   (let* ((p (nthcdr (1- len) list1))
-;;      (list2 (cdr p)))
-;;     (setcdr p nil)
-;;     (dictree--merge
-;;      (dictree--do-merge-sort list1 (/ len 2) sortfun combfun)
-;;      (dictree--do-merge-sort list2 (/ len 2) sortfun combfun)
-;;      sortfun combfun)))
-
-
-
-
-;;; ================================================================
-;;;    The (mostly) public functions which operate on dictionaries
-
-;;;###autoload
-(defun make-dictree
-  (&optional
-   name filename autosave unlisted
-   comparison-function insert-function rank-function
-   cache-policy cache-threshold cache-update-policy
-   key-savefun key-loadfun
-   data-savefun data-loadfun
-   plist-savefun plist-loadfun
-   trie-type)
-  "Create an empty dictionary and return it.
-
-If NAME is supplied, the dictionary is stored in the variable
-NAME. Defaults to FILENAME stripped of directory and
-extension. (Regardless of the value of NAME, the dictionary will
-be stored in the default variable name when it is reloaded from
-file.)
-
-FILENAME supplies a directory and file name to use when saving
-the dictionary. If the AUTOSAVE flag is non-nil, then the
-dictionary will automatically be saved to this file when it is
-unloaded or when exiting Emacs.
-
-If UNLISTED is non-nil, the dictionary will not be added to the
-list of loaded dictionaries. Note that this disables autosaving.
-
-COMPARE-FUNCTION sets the function used to compare elements of
-the keys. It should take two arguments, A and B, both of the type
-contained by the sequences used as keys \(e.g. if the keys will
-be strings, the function will be passed two characters\). It
-should return t if the first is \"less than\" the
-second. Defaults to `<'.
-
-INSERT-FUNCTION sets the function used to insert data into the
-dictionary. It should take two arguments: the new data, and the
-data already in the dictionary, and should return the data to
-insert. Defaults to replacing any existing data with the new
-data.
-
-RANK-FUNCTION sets the function used to rank the results of
-`dictree-complete'. It should take two arguments, each a cons
-whose car is a dictree key (a sequence) and whose cdr is the data
-associated with that key. It should return non-nil if the first
-argument is \"better\" than the second, nil otherwise. It
-defaults to \"lexicographic\" comparison of the keys, ignoring
-the data \(which is not very useful, since an unranked
-`dictree-complete' query already does this much more
-efficiently\).
-
-CACHE-POLICY should be a symbol (`time', `length',
-`time-and-length' or `time-or-length'), which determines which
-query operations are cached. The `time' setting caches queries
-that take longer (in seconds) than the CACHE-THRESHOLD value.
-
-The `length' setting caches query operations based on the length
-of the string involved the query. For this setting, CACHE-POLICY
-should be a plist with properties :long and :short. Lookups,
-fuzzy matches, and regexp queries that do not end in \".*\" will
-be cached if the string is longer than the :long value (since
-long strings are likely to be the slower ones in these
-cases). Completions, fuzzy completions, and regexp queries that
-end in \".*\" will be cached if the string or regexp is shorter
-than the :short value \(since short strings are likely to be the
-slower ones for those cases\).
-
-The `time-and-length' setting only caches results if both
-conditions are satisfied simultaneously, whereas the
-`time-or-length' setting caches results if either condition is
-satisfied. For these settings, CACHE-THRESHOLD must be a plist
-with properties :time, :long and :short, specifying the
-corresponding cache thresholds.
-
-CACHE-THRESHOLD defaults to nil. The values nil and t are
-special. If CACHE-THRESHOLD is set to nil, no caching is done. If
-it is t, everything is cached for that type of query \(similar
-behaviour can be obtained by setting the a `time' CACHE-THRESHOLD
-of 0, but it is better to use t\).
-
-CACHE-UPDATE-POLICY should be a symbol (`synchronize' or
-`delete'), which determines how the caches are updated when data
-is inserted or deleted. The former updates tainted cache entries,
-which makes queries faster but insertion and deletion slower,
-whereas the latter deletes any tainted cache entries, which makes
-queries slower but insertion and deletion faster.
-
-KEY-SAVEFUN, DATA-SAVEFUN and PLIST-SAVEFUN are functions used to
-convert keys, data and property lists into lisp objects that have
-a valid read syntax, for writing to file. DATA-SAVEFUN and
-PLIST-SAVEFUN are used when saving the dictionary (see
-`dictree-save' and `dictree-write'), and all three functions are
-used when dumping the contents of the dictionary \(see
-`dictree-dump-to-buffer' and `dictree-dump-to-file'\).
-KEY-SAVEFUN, DATA-SAVEFUN and PLIST-SAVEFUN should each accept
-one argument: a key, data or property list from DICT,
-respectively. They should return a lisp object which has a valid
-read syntax. When defining these functions, be careful not to
-accidentally modify the lisp object in the dictionary; usually,
-you will need to make a copy before converting it.
-
-KEY-LOADFUN, DATA-LOADFUN and PLIST-LOADFUN are used to convert
-keys, data and property lists back again when loading a
-dictionary (only DATA-LOADFUN and PLIST-LOADFUN, see
-`dictree-save' and `dictree-write') or populating it from a
-file (all three, see `dictree-populate-from-file'). They should
-accept one argument: a lisp object of the type produced by the
-corresponding SAVEFUN, and return a lisp object to use in the
-loaded dictionary.
-
-TRIE-TYPE sets the type of trie to use as the underlying data
-structure. See `trie-create' for details."
-
-  ;; sadly, passing null values overrides the defaults in the defstruct
-  ;; dictree--create, so we have to explicitly set the defaults again here
-  (or name (setq name (and filename (make-symbol
-                                    (file-name-sans-extension
-                                    (file-name-nondirectory filename))))))
-  (or comparison-function (setq comparison-function '<))
-  (or insert-function (setq insert-function (lambda (a _b) a)))
-  (or rank-function (setq rank-function (lambda (a b) (> (cdr a) (cdr b)))))
-  (or cache-policy (setq cache-policy 'time))
-  (or cache-update-policy (setq cache-update-policy 'synchronize))
-  (or trie-type (setq trie-type 'avl))
-
-  (let ((dict
-        (dictree--create
-         filename (when name (symbol-name name)) autosave unlisted
-         comparison-function insert-function rank-function
-         cache-policy cache-threshold cache-update-policy
-         key-savefun key-loadfun
-         data-savefun data-loadfun
-         plist-savefun plist-loadfun
-         trie-type)))
-    ;; store dictionary in variable NAME
-    (when name (set name dict))
-    ;; add it to loaded dictionary list, unless it's unlisted
-    (unless (or (null name) unlisted)
-      (push dict dictree-loaded-list))
-    dict))
-
-
-;;;###autoload
-(defalias 'dictree-create 'make-dictree)
-
-
-;;;###autoload
-(defun* make-dictree-custom
-    (&optional
-     name filename autosave unlisted
-     &key
-     comparison-function insert-function rank-function
-     cache-policy cache-threshold cache-update-policy
-     key-savefun key-loadfun
-     data-savefun data-loadfun
-     plist-savefun plist-loadfun
-     createfun insertfun deletefun lookupfun mapfun emptyfun
-     stack-createfun stack-popfun stack-emptyfun
-     transform-for-print transform-from-read)
-  "Create an empty dictionary and return it.
-
-The NAME through PLIST-LOADFUN arguments are as for
-`dictree-create' (which see).
-
-The remaining arguments control the type of trie to use as the
-underlying data structure. See `trie-create' for details."
-
-  ;; sadly, passing null values over-rides the defaults in the defstruct
-  ;; dictree--create, so we have to explicitly set the defaults again
-  ;; here
-  (or name (setq name (and filename (file-name-sans-extension
-                                    (file-name-nondirectory filename)))))
-  (or comparison-function (setq comparison-function #'<))
-  (or insert-function (setq insert-function (lambda (a _b) a)))
-  (or rank-function (setq rank-function (lambda (a b) (< (cdr a) (cdr b)))))
-  (or cache-policy (setq cache-policy 'time))
-  (or cache-update-policy (setq cache-update-policy 'synchronize))
-
-  (let ((dict
-        (dictree--create-custom
-         filename (when name (symbol-name name)) autosave unlisted
-         comparison-function insert-function rank-function
-         cache-policy cache-threshold cache-update-policy
-         key-savefun key-loadfun
-         data-savefun data-loadfun
-         plist-savefun plist-loadfun
-         :createfun createfun
-         :insertfun insertfun
-         :deletefun deletefun
-         :lookupfun lookupfun
-         :mapfun mapfun
-         :emptyfun emptyfun
-         :stack-createfun stack-createfun
-         :stack-popfun stack-popfun
-         :stack-emptyfun stack-emptyfun
-         :transform-for-print transform-for-print
-         :transform-from-read transform-from-read)))
-    ;; store dictionary in variable NAME
-    (when name (set name dict))
-    ;; add it to loaded dictionary list, unless it's unlisted
-    (unless (or (null name) unlisted)
-      (push dict dictree-loaded-list))
-    dict))
-
-
-;;;###autoload
-(defalias 'dictree-create-custom 'make-dictree-custom)
-
-
-;;;###autoload
-(defun make-dictree-meta-dict
-  (dictionary-list
-   &optional
-   name filename autosave unlisted
-   combine-function
-   cache-policy cache-threshold cache-update-policy)
-  "Create a meta-dictionary based on the list of dictionaries
-in DICTIONARY-LIST.
-
-COMBINE-FUNCTION is used to combine data from different
-dictionaries. It is passed two pieces of data, each an
-association of the same key, but in different dictionaries. It
-should return a combined datum.
-
-The other arguments are as for `dictree-create'. Note that
-caching is only possible if NAME is supplied, otherwise the
-CACHE-THRESHOLD argument is ignored and caching is disabled."
-
-  ;; sadly, passing null values over-rides the defaults in the defstruct
-  ;; `dictree--create', so we have to explicitly set the defaults again
-  ;; here
-  (or name (setq name (and filename
-                          (file-name-sans-extension
-                           (file-name-nondirectory filename)))))
-  (or combine-function (setq combine-function #'+))
-  (or cache-policy (setq cache-policy 'time))
-  (or cache-update-policy (setq cache-update-policy 'synchronize))
-
-  (let ((dict
-        (dictree--meta-dict-create
-         dictionary-list filename (when name (symbol-name name))
-         autosave unlisted
-         combine-function
-         cache-policy (when name cache-threshold) cache-update-policy
-        )))
-    ;; store dictionary in variable NAME
-    (when name (set name dict))
-    ;; add it to loaded dictionary list, unless it's unlisted
-    (unless (or (null name) unlisted)
-      (push dict dictree-loaded-list))
-    ;; update meta-dict-list cells of constituent dictionaries
-    (unless (or (null name) (not cache-threshold))
-      (mapc
-       (lambda (dic)
-        (if (symbolp dic) (setq dic (symbol-value dic)))
-        (setf (dictree--meta-dict-list dic)
-              (cons dict (dictree--meta-dict-list dic))))
-       dictionary-list))
-    dict))
-
-(defalias 'dictree-create-meta-dict 'make-dictree-meta-dict)
-
-
-;;;###autoload
-(defun dictree-p (obj)
-  "Return t if OBJ is a dictionary tree, nil otherwise."
-  (or (dictree--p obj) (dictree--meta-dict-p obj)))
-
-
-(defalias 'dictree-meta-dict-p 'dictree--meta-dict-p
-  "Return t if argument is a meta-dictionary, nil otherwise.")
-
-(defun dictree-empty-p (dict)
-  "Return t if the dictionary DICT is empty, nil otherwise."
-  (if (dictree--meta-dict-p dict)
-      (catch 'nonempty
-       (mapc (lambda (dic)
-               (if (not (dictree-empty-p dic)) (throw 'nonempty t)))
-             (dictree--meta-dict-dictlist dict)))
-    (trie-empty (dictree--trie dict))))
-
-(defsubst dictree-autosave (dict)
-  "Return dictionary's autosave flag."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-autosave dict)
-    (dictree--autosave dict)))
-
-(defsetf dictree-autosave (dict) (val)
-  ;; setf method for dictionary autosave flag
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-autosave ,dict) ,val)
-     (setf (dictree--autosave ,dict) ,val)))
-
-(defsubst dictree-modified (dict)
-  "Return dictionary's modified flag."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-modified dict)
-    (dictree--modified dict)))
-
-(defsetf dictree-modified (dict) (val)
-  ;; setf method for dictionary modified flag
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-modified ,dict) ,val)
-     (setf (dictree--modified ,dict) ,val)))
-
-(defsubst dictree-name (dict)
-  "Return dictionary DICT's name."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-name dict)
-    (dictree--name dict)))
-
-(defsetf dictree-name (dict) (name)
-  ;; setf method for dictionary name
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-name ,dict) ,name)
-    (setf (dictree--name ,dict) ,name)))
-
-(defsubst dictree-filename (dict)
-  "Return dictionary DICT's associated file name."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-filename dict)
-    (dictree--filename dict)))
-
-(defsetf dictree-filename (dict) (filename)
-  ;; setf method for dictionary filename
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-filename ,dict) ,filename)
-     (setf (dictree--filename ,dict) ,filename)))
-
-(defun dictree-comparison-function (dict)
-  "Return dictionary DICT's comparison function."
-  (if (dictree--meta-dict-p dict)
-      (dictree-comparison-function
-       (car (dictree--meta-dict-dictlist dict)))
-    (dictree--comparison-function dict)))
-
-(defalias 'dictree-insert-function 'dictree--insert-function
-  "Return the insertion function for dictionary DICT.")
-
-(defun dictree-rank-function (dict)
-  "Return the rank function for dictionary DICT"
-  (if (dictree--meta-dict-p dict)
-      (dictree-rank-function (car (dictree--meta-dict-dictlist dict)))
-    (dictree--rank-function dict)))
-
-(defalias 'dictree-meta-dict-combine-function
-  'dictree--meta-dict-combine-function
-  "Return the combine function for meta-dictionary DICT.")
-
-(defalias 'dictree-meta-dict-dictlist
-  'dictree--meta-dict-dictlist
-  "Return the list of constituent dictionaries
-for meta-dictionary DICT.")
-
-(defsubst dictree-cache-policy (dict)
-  "Return the cache policy for dictionary DICT."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-cache-policy dict)
-    (dictree--cache-policy dict)))
-
-(defsubst dictree-cache-update-policy (dict)
-  "Return the cache update policy for dictionary DICT."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-cache-update-policy dict)
-    (dictree--cache-update-policy dict)))
-
-(defsubst dictree-cache-threshold (dict)
-  "Return the cache threshold for dictionary DICT."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-cache-threshold dict)
-    (dictree--cache-threshold dict)))
-
-(defsetf dictree-cache-threshold (dict) (param)
-  ;; setf method for cache threshold
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-cache-threshold ,dict)
-            ,param)
-     (setf (dictree--cache-threshold ,dict)
-          ,param)))
-
-
-(defun dictree-lookup-cache (dict)
-  ;; Return the lookup cache for dictionary DICT.
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-lookup-cache dict)
-    (dictree--lookup-cache dict)))
-
-(defsetf dictree-lookup-cache (dict) (param)
-  ;; setf method for lookup cache
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-lookup-cache ,dict)
-            ,param)
-     (setf (dictree--lookup-cache ,dict)
-          ,param)))
-
-(defun dictree-create-lookup-cache (dict)
-  ;; Create DICT's lookup cache if it doesn't already exist.
-  (unless (dictree-lookup-cache dict)
-    (setf (dictree-lookup-cache dict)
-         (make-hash-table :test 'equal))))
-
-
-(defun dictree-complete-cache (dict)
-  ;; Return the completion cache for dictionary DICT.
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-complete-cache dict)
-    (dictree--complete-cache dict)))
-
-(defsetf dictree-complete-cache (dict) (param)
-  ;; setf method for complete cache
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-complete-cache ,dict)
-            ,param)
-     (setf (dictree--complete-cache ,dict)
-          ,param)))
-
-(defun dictree-create-complete-cache (dict)
-  ;; Create DICT's completion cache if it doesn't already exist.
-  (unless (dictree-complete-cache dict)
-    (setf (dictree-complete-cache dict)
-         (make-hash-table :test 'equal))))
-
-
-(defun dictree-regexp-cache (dict)
-  ;; Return the regexp cache for dictionary DICT.
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-regexp-cache dict)
-    (dictree--regexp-cache dict)))
-
-(defsetf dictree-regexp-cache (dict) (param)
-  ;; setf method for regexp cache
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-regexp-cache ,dict)
-            ,param)
-     (setf (dictree--regexp-cache ,dict)
-          ,param)))
-
-(defun dictree-create-regexp-cache (dict)
-  ;; Create DICT's regexp cache if it doesn't already exist.
-  (unless (dictree-regexp-cache dict)
-    (setf (dictree-regexp-cache dict)
-         (make-hash-table :test 'equal))))
-
-
-(defun dictree-fuzzy-match-cache (dict)
-  ;; Return the fuzzy match cache for dictionary DICT.
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-fuzzy-match-cache dict)
-    (dictree--fuzzy-match-cache dict)))
-
-(defsetf dictree-fuzzy-match-cache (dict) (param)
-  ;; setf method for fuzzy match cache
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-fuzzy-match-cache ,dict)
-            ,param)
-     (setf (dictree--fuzzy-match-cache ,dict)
-          ,param)))
-
-(defun dictree-create-fuzzy-match-cache (dict)
-  ;; Create DICT's fuzzy match cache if it doesn't already exist.
-  (unless (dictree-fuzzy-match-cache dict)
-    (setf (dictree-fuzzy-match-cache dict)
-         (make-hash-table :test 'equal))))
-
-
-(defun dictree-fuzzy-complete-cache (dict)
-  ;; Return the regexp cache for dictionary DICT.
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-dict-fuzzy-complete-cache dict)
-    (dictree--fuzzy-complete-cache dict)))
-
-(defsetf dictree-fuzzy-complete-cache (dict) (param)
-  ;; setf method for fuzzy completion cache
-  `(if (dictree--meta-dict-p ,dict)
-       (setf (dictree--meta-dict-fuzzy-complete-cache ,dict)
-            ,param)
-     (setf (dictree--fuzzy-complete-cache ,dict)
-          ,param)))
-
-(defun dictree-create-fuzzy-complete-cache (dict)
-  ;; Create DICT's fuzzy completion cache if it doesn't already exist.
-  (unless (dictree-fuzzy-complete-cache dict)
-    (setf (dictree-fuzzy-complete-cache dict)
-         (make-hash-table :test 'equal))))
-
-
-
-
-
-;; ----------------------------------------------------------------
-;;                  Inserting and deleting data
-
-(defun dictree-insert (dict key &optional data insert-function)
-  "Insert KEY and DATA into dictionary DICT.
-If KEY does not already exist, this creates it. How the data is
-inserted depends on the dictionary's insertion function \(see
-`dictree-create'\).
-
-The optional INSERT-FUNCTION over-rides the dictionary's own
-insertion function. If KEY already exists in DICT,
-INSERT-FUNCTION is called with two arguments: the data DATA, and
-the data associated with KEY in the dictionary. Its return value
-becomes the new association for KEY."
-
-  ;; if dictionary is a meta-dictionary, insert key into all the
-  ;; dictionaries it's based on
-  (if (dictree--meta-dict-p dict)
-      (mapc (lambda (dic)
-             (dictree-insert dic key data insert-function))
-           (dictree--meta-dict-dictlist dict))
-
-    ;; otherwise...
-    (let ((insfun (or (and insert-function
-                          (dictree--wrap-insfun insert-function))
-                     (dictree--wrap-insfun (dictree--insert-function dict))))
-         olddata newdata)
-      ;; set the dictionary's modified flag
-      (setf (dictree-modified dict) t)
-      ;; insert key in dictionary's ternary search tree
-      (setq newdata
-           (trie-insert
-            (dictree--trie dict) key (dictree--cell-create data nil)
-            (lambda (nd od)
-              (setq olddata od)
-              (funcall insfun nd od))))
-      ;; update dictionary's caches
-      (dictree--update-cache dict key olddata newdata)
-      ;; update cache's of any meta-dictionaries based on dict
-      (mapc (lambda (dic) (dictree--update-cache dic key olddata newdata))
-           (dictree--meta-dict-list dict))
-
-      ;; return the new data
-      (dictree--cell-data newdata))))
-
-
-
-(defun dictree-delete (dict key &optional test)
-  "Delete KEY from DICT.
-Returns non-nil if KEY was deleted, nil if KEY was not in DICT.
-
-If TEST is supplied, it should be a function that accepts three
-arguments: the key being deleted, its associated data, and its
-associated property list. The key will then only be deleted if
-TEST returns non-nil."
-
-  (let ((dictree--delete-test test)
-       olddata deleted del)
-    (cond
-     ;; if DICT is a meta-dictionary, delete KEY from all dictionaries
-     ;; it's based on
-     ((dictree--meta-dict-p dict)
-      (dolist (dic (dictree--meta-dict-dictlist dict))
-       (when (setq del (dictree-delete dic key))
-         (setq deleted (cons del deleted))))
-      (setf (dictree-modified dict) (and deleted t))
-      (setq deleted (nreverse deleted)))
-
-     ;; otherwise...
-     (t
-      (setq deleted
-           (trie-delete (dictree--trie dict) key
-                        (lambda (k cell)
-                          (setq olddata (dictree--cell-data cell))
-                          (if dictree--delete-test
-                              (funcall dictree--delete-test
-                                       k (dictree--cell-data cell)
-                                       (dictree--cell-plist cell))
-                            t))))
-      ;; if key was deleted, have to update the caches
-      (when deleted
-       (dictree--update-cache dict key olddata nil t)
-       (setf (dictree-modified dict) t)
-       ;; update cache's of any meta-dictionaries based on DICT
-       (mapc (lambda (dic)
-               (dictree--update-cache dic key olddata nil t))
-             (dictree--meta-dict-list dict)))))
-
-    ;; return deleted key/data pair
-    (when deleted
-      (cons (car deleted) (dictree--cell-data (cdr deleted))))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                     Cache updating
-
-(defun dictree--prefix-p (prefix str)
-  "Return t if PREFIX is a prefix of STR, nil otherwise.
-
-PREFIX and STR can be any sequence type (string, vector, or
-list), but they must both be the same type. PREFIX can also be a
-list of sequences, in which case it returns t if any element of
-PREFIX is a prefix of STR."
-  ;; wrap prefix in a list if necessary
-  ;; FIXME: the test for a list of prefixes, below, will fail if the
-  ;;        PREFIX sequence is a list, and the elements of PREFIX are
-  ;;        themselves lists (there might be no easy way to fully fix
-  ;;        this...)
-  (when (or (atom prefix)
-           (and (listp prefix) (not (sequencep (car prefix)))))
-    (setq prefix (list prefix)))
-  (let (len)
-    (catch 'is-prefix
-      (dolist (pfx prefix)
-       (setq len (length pfx))
-       (when (and (<= len (length str))
-                  (equal pfx (dictree--subseq str 0 len)))
-         (throw 'is-prefix t))))))
-
-
-(defun dictree--above-cache-threshold-p
-  (time length policy threshold &optional cache-long-keys)
-  ;; Return t if query taking TIME seconds for a key of length LENGTH
-  ;; should be cached according to the cache POLICY and
-  ;; THRESHOLD. Otherwise, return nil. Optional argument CACHE-LONG-KEYS
-  ;; means that keys of length longer than THRESHOLD are to be
-  ;; cached. Default is keys of length shorter than THRESHOLD.
-  (and threshold
-       (or (eq threshold t)
-          (and (eq policy 'time) (>= time threshold))
-          (and (eq policy 'length)
-               (if cache-long-keys
-                   (>= length (plist-get threshold :long))
-                 (<= length (plist-get threshold :short))))
-          (and (eq policy 'time-and-length)
-               (>= time (plist-get threshold :time))
-               (if cache-long-keys
-                   (>= length (plist-get threshold :long))
-                 (<= length (plist-get threshold :short))))
-          (and (eq policy 'time-or-length)
-               (or (>= time (plist-get threshold :time))
-                   (if cache-long-keys
-                       (>= length (plist-get threshold :long))
-                     (<= length (plist-get threshold :short))))))))
-
-
-
-(defun dictree--update-cache (dict key olddata newdata &optional deleted)
-  ;; Synchronise dictionary DICT's caches, given that the data associated with
-  ;; KEY has been updated from OLDDATA to NEWDATA, or KEY has been deleted if
-  ;; DELETED is non-nil (NEWDATA is ignored in that case)."
-  (when (dictree-cache-threshold dict)
-
-    ;; synchronise lookup cache if dict is a meta-dictionary, since it doesn't
-    ;; happen automatically for a meta-dict
-    (when (dictree--meta-dict-p dict)
-      (cond
-       ;; updating dirty cache entries
-       ((eq (dictree-cache-update-policy dict) 'synchronize)
-       (when (and (dictree--lookup-cache dict)
-                  (gethash key (dictree--lookup-cache dict)))
-         (if deleted
-             (remhash key (dictree--lookup-cache dict))
-           (puthash key newdata (dictree--lookup-cache dict)))))
-       ;; deleting dirty cache entries
-       (t (remhash key (dictree--lookup-cache dict)))))
-
-    ;; synchronize query caches if something's actually changed
-    (when (or deleted (not (equal olddata newdata)))
-      (dolist (cachefuns
-              '((dictree-complete-cache
-                 dictree--synchronize-completion-cache
-                 dictree--prefix-p)
-                (dictree-regexp-cache
-                 dictree--synchronize-regexp-cache
-                 (lambda (arg key)
-                   (tNFA-regexp-match
-                    arg key :test (trie--construct-equality-function
-                                   (dictree--comparison-function dict)))))
-                (dictree-fuzzy-match-cache
-                 dictree--synchronize-fuzzy-match-cache
-                 (lambda (string dist key)
-                   (<= (Lewenstein-distance string key) dist)))
-                (dictree-fuzzy-complete-cache
-                 dictree--synchronize-fuzzy-completion-cache
-                 (lambda (prefix dist key)
-                   (<= (Lewenstein-distance prefix key) dist)))
-                ))
-       (when (funcall (nth 0 cachefuns) dict)
-         (maphash
-          (lambda (cache-key cache-entry)
-            (destructuring-bind
-                (arg auxargs rank-function reverse filter) cache-key
-              (when (apply (nth 2 cachefuns)
-                           (append (list arg) auxargs (list key)))
-                (cond
-                 ;; updating dirty cache entries
-                 ((eq (dictree-cache-update-policy dict) 'synchronize)
-                  (funcall (nth 1 cachefuns)
-                           dict key olddata newdata deleted cache-entry
-                           arg auxargs rank-function reverse filter))
-                 ;; deleting dirty cache entries
-                 (t (remhash (list arg auxargs rank-function reverse filter)
-                             (funcall (nth 0 cachefuns) dict)))))))
-          (funcall (nth 0 cachefuns) dict)))
-       ))))
-
-
-
-(defun dictree--synchronize-completion-cache
-    (dict key olddata newdata deleted cache-entry
-         arg auxargs rank-function reverse filter)
-  ;; Synchronize DICT's completion CACHE-ENTRY for a query with arguments ARG,
-  ;; AUXARGS, RANK-FUNCTION, REVERSE and FILTER, where KEY's data was either
-  ;; updated from OLDDATA to NEWDATA or DELETED,
-
-  (let* ((completions (dictree--cache-results cache-entry))
-        (maxnum (dictree--cache-maxnum cache-entry))
-        (cmpl (assoc key completions))
-        (rankfun (cond ((eq rank-function t)
-                        (dictree--wrap-rankfun
-                         (dictree--rank-function dict)))
-                       (rank-function
-                        (dictree--wrap-rankfun rank-function)))))
-    ;; for meta-dict, get old data from cache instead of OLDDATA
-    (when (dictree--meta-dict-p dict) (setq olddata (cdr cmpl)))
-    ;; skip cache update if key/data pair doesn't pass FILTER
-    (when (or (null filter)
-             (funcall filter key olddata)
-             (funcall filter key newdata))
-      ;; if key was...
-      (cond
-
-       ;; deleted and in cached result: remove cache entry and re-run the
-       ;; same completion to update the cache
-       ((and deleted cmpl)
-       (remhash (list arg auxargs rank-function reverse filter)
-                (dictree-complete-cache dict))
-       (dictree-complete dict arg rank-function maxnum reverse filter))
-
-       ;; modified and not in cached result: merge it into the completion
-       ;; list, retaining only the first maxnum
-       ((and (not deleted) (not cmpl))
-       (when (or (null filter) (funcall filter key newdata))
-         (setf (dictree--cache-results cache-entry)
-               (dictree--merge
-                (list (cons key newdata)) completions
-                (or rankfun
-                    `(lambda (a b)
-                       (,(trie-construct-sortfun
-                          (dictree-comparison-function dict))
-                        (car a) (car b))))
-                (when (dictree--meta-dict-p dict)
-                  (dictree--wrap-combfun
-                   (dictree--meta-dict-combine-function dict)))
-                maxnum))))
-
-       ;; modified and in the cached result
-       ((and (not deleted) cmpl)
-       ;; update the associated data if dict is a meta-dictionary (this
-       ;; happens automatically for a normal dict)
-       (when (dictree--meta-dict-p dict) (setcdr cmpl newdata))
-       ;; if updated entry gets filtered, or gets sorted at end of list,
-       ;; re-run the same query to update the cache
-       (when (or (and filter (not (funcall filter key newdata)))
-                 (and rankfun
-                      (setf (dictree--cache-results cache-entry)
-                            (sort completions rankfun))
-                      (equal key (car (last (dictree--cache-results
-                                             cache-entry))))))
-         (remhash (list arg auxargs rank-function reverse filter)
-                  (dictree-complete-cache dict))
-         (dictree-complete dict arg rank-function maxnum reverse filter)))
-
-       ;; deleted and not in cached result: requires no action
-       ))))
-
-
-(defun dictree--synchronize-regexp-cache
-    (dict key olddata newdata deleted cache-entry
-         arg auxargs rank-function reverse filter)
-  ;; Synchronize DICT's regexp CACHE-ENTRY for a query with arguments ARG,
-  ;; AUXARGS, RANK-FUNCTION, REVERSE and FILTER, where KEY's data was either
-  ;; updated from OLDDATA to NEWDATA or DELETED,
-
-  (let* ((completions (dictree--cache-results cache-entry))
-        (maxnum (dictree--cache-maxnum cache-entry))
-        group-data
-        (cmpl (catch 'found
-                (dolist (c completions)
-                  (if (and (listp (car c))
-                           (or (stringp (caar c))
-                               (vectorp (caar c))
-                               (listp (caar c))))
-                      (when (equal key (caar c)) (throw 'found c))
-                    (when (equal key (car c)) (throw 'found c))))))
-        (rankfun (cond ((eq rank-function t)
-                        (dictree--wrap-regexp-rankfun
-                         (dictree-rank-function dict)))
-                       (rank-function
-                        (dictree--wrap-regexp-rankfun rank-function)))))
-    ;; for meta-dict, get old data from cache instead of OLDDATA
-    (when (dictree--meta-dict-p dict) (setq olddata (cdr cmpl)))
-    ;; skip cache update if key/data pair doesn't pass FILTER
-    (when (or (null filter)
-             (funcall filter key olddata)
-             (funcall filter key newdata))
-      ;; if key was...
-      (cond
-
-       ;; deleted and in cached result: remove cache entry and re-run the
-       ;; same completion to update the cache
-       ((and deleted cmpl)
-       (remhash (list arg auxargs rank-function reverse filter)
-                (dictree-regexp-cache dict))
-       (dictree-regexp-search dict arg rank-function maxnum reverse filter))
-
-       ;; modified and not in cached result: merge it into the completion
-       ;; list, retaining only the first maxnum
-       ((and (not deleted) (not cmpl))
-       (when (or (null filter) (funcall filter key newdata))
-         (save-match-data
-           (set-match-data nil)
-           (tNFA-regexp-match arg key
-                              :test (trie--construct-equality-function
-                                     (dictree--comparison-function dict)))
-           (when (setq group-data (nthcdr 2 (match-data)))
-             (setq key (cons key group-data))))
-         (setf (dictree--cache-results cache-entry)
-               (dictree--merge
-                (list (cons key newdata)) completions
-                (or rankfun
-                    `(lambda (a b)
-                       (,(trie-construct-sortfun
-                          (dictree-comparison-function dict))
-                        ,(if group-data '(caar a) '(car a))
-                        ,(if group-data '(caar b) '(car b)))))
-                (when (dictree--meta-dict-p dict)
-                  (dictree--wrap-combfun
-                   (dictree--meta-dict-combine-function dict)))
-                maxnum))))
-
-       ;; modified and in the cached result
-       ((and (not deleted) cmpl)
-       ;; update the associated data if dict is a meta-dictionary (this
-       ;; happens automatically for a normal dict)
-       (when (dictree--meta-dict-p dict) (setcdr cmpl newdata))
-       ;; if updated entry gets filtered, or gets sorted at end of list,
-       ;; re-run the same query to update the cache
-       (when (or (and filter (not (funcall filter key newdata)))
-                 (and rankfun
-                      (setf (dictree--cache-results cache-entry)
-                            (sort completions rankfun))
-                      (equal key (car (last (dictree--cache-results
-                                             cache-entry))))))
-         (remhash (list arg auxargs rank-function reverse filter)
-                  (dictree-regexp-cache dict))
-         (dictree-regexp-search dict arg rank-function maxnum reverse filter)
-         ))
-
-       ;; deleted and not in cached result: requires no action
-       ))))
-
-
-(defun dictree--synchronize-fuzzy-match-cache
-    (dict key olddata newdata deleted cache-entry
-         arg auxargs rank-function reverse filter)
-  ;; Synchronize DICT's fuzzy match CACHE-ENTRY for a query with arguments
-  ;; ARG, AUXARGS, RANK-FUNCTION, REVERSE and FILTER, where KEY's data was
-  ;; either updated from OLDDATA to NEWDATA or DELETED,
-
-  (let* ((completions (dictree--cache-results cache-entry))
-        (maxnum (dictree--cache-maxnum cache-entry))
-        (cmpl (catch 'found
-                (dolist (c completions)
-                  (when (equal key (caar c)) (throw 'found c)))))
-        (distance (Lewenstein-distance key arg))
-        (rankfun (cond ((eq rank-function t)
-                        (dictree--wrap-fuzzy-rankfun
-                         (dictree-rank-function dict)))
-                       ((eq rank-function 'distance)
-                        (dictree--wrap-fuzzy-rankfun
-                         (trie--construct-Lewenstein-rankfun
-                          (dictree-comparison-function dict))))
-                       (rank-function
-                        (dictree--wrap-fuzzy-rankfun rank-function)))))
-    ;; for meta-dict, get old data from cache instead of OLDDATA
-    (when (dictree--meta-dict-p dict) (setq olddata (cdr cmpl)))
-    ;; skip cache update if key/data pair doesn't pass FILTER
-    (when (or (null filter)
-             (funcall filter key olddata)
-             (funcall filter key newdata))
-      ;; if key was...
-      (cond
-
-       ;; deleted and in cached result: remove cache entry and re-run the
-       ;; same completion to update the cache
-       ((and deleted cmpl)
-       (remhash (list arg auxargs rank-function reverse filter)
-                (dictree-fuzzy-match-cache dict))
-       (dictree-fuzzy-match dict arg (car auxargs)
-                            rank-function maxnum reverse filter))
-
-       ;; modified and not in cached result: merge it into the completion
-       ;; list, retaining only the first maxnum
-       ((and (not deleted) (not cmpl))
-       (when (or (null filter) (funcall filter key newdata))
-         (setf (dictree--cache-results cache-entry)
-               (dictree--merge
-                (list (cons (cons key distance) newdata)) completions
-                (or rankfun
-                    `(lambda (a b)
-                       (,(trie-construct-sortfun
-                          (dictree-comparison-function dict))
-                        (caar a) (caar b))))
-                (when (dictree--meta-dict-p dict)
-                  (dictree--wrap-combfun
-                   (dictree--meta-dict-combine-function dict)))
-                maxnum))))
-
-       ;; modified and in the cached result
-       ((and (not deleted) cmpl)
-       ;; update the associated data if dict is a meta-dictionary (this
-       ;; happens automatically for a normal dict)
-       (when (dictree--meta-dict-p dict) (setcdr cmpl newdata))
-       ;; if updated entry gets filtered, or gets sorted at end of list,
-       ;; re-run the same query to update the cache
-       (when (or (and filter (not (funcall filter key newdata)))
-                 (and rankfun
-                      (setf (dictree--cache-results cache-entry)
-                            (sort completions rankfun))
-                      (equal key (car (last (dictree--cache-results
-                                             cache-entry))))))
-         (remhash (list arg auxargs rank-function reverse filter)
-                  (dictree-fuzzy-match-cache dict))
-         (dictree-fuzzy-match dict arg (car auxargs)
-                              rank-function maxnum reverse filter)))
-
-       ;; deleted and not in cached result: requires no action
-       ))))
-
-
-(defun dictree--synchronize-fuzzy-complete-cache
-    (dict key olddata newdata deleted cache-entry
-         arg auxargs rank-function reverse filter)
-  ;; Synchronize DICT's fuzzy completion CACHE-ENTRY for a query with
-  ;; arguments ARG, AUXARGS, RANK-FUNCTION, REVERSE and FILTER, where KEY's
-  ;; data was either updated from OLDDATA to NEWDATA or DELETED,
-
-  (let* ((completions (dictree--cache-results cache-entry))
-        (maxnum (dictree--cache-maxnum cache-entry))
-        (cmpl (catch 'found
-                (dolist (c completions)
-                  (when (equal key (caar c)) (throw 'found c)))))
-        (distance (Lewenstein-distance key arg))
-        (rankfun (cond ((eq rank-function t)
-                        (dictree--wrap-fuzzy-rankfun
-                         (dictree-rank-function dict)))
-                       ((eq rank-function 'distance)
-                        (dictree--wrap-fuzzy-rankfun
-                         (trie--construct-Lewenstein-rankfun
-                          (dictree-comparison-function dict))))
-                       (rank-function
-                        (dictree--wrap-fuzzy-rankfun rank-function)))))
-    ;; for meta-dict, get old data from cache instead of OLDDATA
-    (when (dictree--meta-dict-p dict) (setq olddata (cdr cmpl)))
-    ;; skip cache update if key/data pair doesn't pass FILTER
-    (when (or (null filter)
-             (funcall filter key olddata)
-             (funcall filter key newdata))
-      ;; if key was...
-      (cond
-
-       ;; deleted and in cached result: remove cache entry and re-run the
-       ;; same completion to update the cache
-       ((and deleted cmpl)
-       (remhash (list arg auxargs rank-function reverse filter)
-                (dictree-fuzzy-complete-cache dict))
-       (dictree-fuzzy-complete dict arg (car auxargs)
-                               rank-function maxnum reverse filter))
-
-       ;; modified and not in cached result: merge it into the completion
-       ;; list, retaining only the first maxnum
-       ((and (not deleted) (not cmpl))
-       (when (or (null filter) (funcall filter key newdata))
-         (setf (dictree--cache-results cache-entry)
-               (dictree--merge
-                (list (cons key (cons distance newdata))) completions
-                (or rankfun
-                    `(lambda (a b)
-                       (,(trie-construct-sortfun
-                          (dictree-comparison-function dict))
-                        (car a) (car b))))
-                (when (dictree--meta-dict-p dict)
-                  (dictree--wrap-combfun
-                   (dictree--meta-dict-combine-function dict)))
-                maxnum))))
-
-       ;; modified and in the cached result
-       ((and (not deleted) cmpl)
-       ;; update the associated data if dict is a meta-dictionary (this
-       ;; happens automatically for a normal dict)
-       (when (dictree--meta-dict-p dict) (setcdr cmpl newdata))
-       ;; if updated entry gets filtered, or gets sorted at end of list,
-       ;; re-run the same query to update the cache
-       (when (or (and filter (not (funcall filter key newdata)))
-                 (and rankfun
-                      (setf (dictree--cache-results cache-entry)
-                            (sort completions rankfun))
-                      (equal key (car (last (dictree--cache-results
-                                             cache-entry))))))
-         (remhash (list arg auxargs rank-function reverse filter)
-                  (dictree-fuzzy-complete-cache dict))
-         (dictree-fuzzy-complete dict arg (car auxargs)
-                                 rank-function maxnum reverse filter)))
-
-       ;; deleted and not in cached result: requires no action
-       ))))
-
-
-(defun dictree-clear-caches (dict)
-  "Clear all DICT's query caches."
-  (interactive (list (read-dict "Dictionary: ")))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-  (dolist (cachefun '(dictree-lookup-cache
-                     dictree-complete-cache
-                     dictree-regexp-cache
-                     dictree-fuzzy-match-cache
-                     dictree-fuzzy-complete-cache))
-    (when (funcall cachefun dict)
-      (clrhash (funcall cachefun dict))))
-  (when (called-interactively-p 'interactive)
-    (message "Cleared caches for dictionary %s" (dictree-name dict))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                        Retrieving data
-
-(defun dictree-member (dict key &optional nilflag)
-  "Return the data associated with KEY in dictionary DICT,
-or nil if KEY is not in the dictionary.
-
-Optional argument NILFLAG specifies a value to return instead of
-nil if KEY does not exist in TREE. This allows a non-existent KEY
-to be distinguished from an element with a null association. (See
-also `dictree-member-p' for testing existence alone.)"
-  (let* ((data (dictree--lookup dict key nilflag)))
-    (if (eq data nilflag)
-       nilflag
-      (dictree--cell-data data))))
-
-(defalias 'dictree-lookup 'dictree-member)
-
-(defun dictree-member-p (dict key)
-  "Return t if KEY exists in DICT, nil otherwise."
-  (let ((flag '(nil)))
-    (not (eq flag (dictree-member dict key flag)))))
-
-
-(defun dictree--lookup (dict key nilflag)
-  ;; Return association of KEY in DICT, or NILFLAG if KEY does not
-  ;; exist. Does not do any data/meta-data unwrapping
-
-  (let* ((flag '(nil))
-        (data flag)
-        time)
-    ;; if KEY is in the cache, then we're done
-    (unless (and (dictree-lookup-cache dict)
-                (setq data (gethash key (dictree--lookup-cache dict))))
-
-      ;; otherwise, we have to look in the dictionary itself...
-      (cond
-       ;; if DICT is a meta-dict, look in its constituent dictionaries
-       ((dictree--meta-dict-p dict)
-       (let (newdata (newflag '(nil)))
-         ;; time the lookup for caching
-         (setq time (float-time))
-         ;; look in each constituent dictionary in turn
-         (dolist (dic (dictree--meta-dict-dictlist dict))
-           (setq newdata (dictree--lookup dic key newflag))
-           ;; skip dictionary if it doesn't contain KEY
-           (unless (eq newdata newflag)
-             ;; if we haven't found KEY before, we have now!
-             (if (eq data flag) (setq data newdata)
-               ;; otherwise, combine the previous data with the new data
-               (setq data
-                     (funcall (dictree--wrap-combfun
-                               (dictree--meta-dict-combine-function dict))
-                              data newdata)))))
-         (setq time (- (float-time) time))))
-
-       ;; otherwise, DICT is a normal dictionary, so look in it's trie
-       (t
-       ;; time the lookup for caching
-       (setq time (float-time))
-       (setq data (trie-member (dictree--trie dict) key flag))
-       (setq time (- (float-time) time))))
-
-      ;; if lookup found something, and we're above the cache-threshold, cache
-      ;; the result
-      (when (and (not (eq data flag))
-                (dictree--above-cache-threshold-p
-                 time (length key) (dictree-cache-policy dict)
-                 (dictree-cache-threshold dict) 'long-keys))
-       (setf (dictree-modified dict) t)
-       ;; create lookup cache if it doesn't already exist
-       (dictree-create-lookup-cache dict)
-       (puthash key data (dictree-lookup-cache dict))))
-
-    ;; return the desired data
-    (if (eq data flag) nilflag data)))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                 Getting and setting meta-data
-
-(defun dictree-put-property (dict key property value)
-  "Set PROPERTY for KEY in dictionary DICT.
-PROPERTY should be a symbol. Returns VALUE if successful, nil if
-KEY was not found in DICT.
-
-Note that if DICT is a meta-dictionary, then this will set KEY's
-PROPERTY to VALUE in *all* its constituent dictionaries.
-
-Unlike the data associated with a key (cf. `dictree-insert'),
-properties are not included in the results of queries on the
-dictionary \(`dictree-lookup', `dictree-complete',
-`dictree-complete-ordered'\), nor do they affect the outcome of
-any of the queries. They merely serves to tag a key with some
-additional information, and can only be retrieved using
-`dictree-get-property'."
-
-  ;; sort out arguments
-  (and (symbolp dict) (setq dict (symbol-value dict)))
-  (cond
-   ;; set PROPERTY for KEY in all constituent dicts of a meta-dict
-   ((dictree--meta-dict-p dict)
-    (warn "Setting %s property for key %s in all constituent\
- dictionaries of meta-dictionary %s" property key (dictree-name dict))
-    (setf (dictree-modified dict) t)
-    (let (dictree--put-property-ret)
-      (mapc (lambda (dic k p v)
-             (setq dictree--put-property-ret
-                   (or dictree--put-property-ret
-                       (dictree-put-property dic k p v))))
-           (dictree--meta-dict-dictlist dict))
-      ;; return VALUE if KEY was found in at least one constituent dict
-      dictree--put-property-ret))
-   (t  ;; set PROPERTY for KEY in normal dict
-    (let ((cell (trie-member (dictree--trie dict) key)))
-      (when cell
-       (setf (dictree-modified dict) t)
-       (setf (dictree--cell-plist cell)
-             (plist-put (dictree--cell-plist cell) property value))
-       value)))  ; return VALUE
-   ))
-
-
-
-(defun dictree-delete-property (dict key property)
-  "Delete PROPERTY from KEY in dictionary DICT.
-Returns the new property list for KEY, with PROPERTY deleted.
-
-Setting PROPERTY to nil using `dictree-put-property' is not quite
-the same thing as deleting it, since null property values can
-still be detected by supplying the optional argument to
-`dictree-get-propery' (which see).
-
-Note that if DICT is a meta-dictionary, then this will delete
-KEY's PROPERTY in *all* its constituent dictionaries."
-  ;; sort out arguments
-  (and (symbolp dict) (setq dict (symbol-value dict)))
-  (cond
-   ;; delete PROPERTY from KEY in all constituent dicts of a meta-dict
-   ((dictree--meta-dict-p dict)
-    (warn "Deleting %s property from key %s in all constituent\
- dictionaries of meta-dicttionary %s" property key (dictree-name dict))
-    (setf (dictree-modified dict) t)
-    (mapcar (lambda (dic k p) (dictree-delete-property dic k p))
-           (dictree--meta-dict-dictlist dict)))
-   (t  ;; delete PROPERTY from KEY in normal dict
-    (let* ((cell (trie-member (dictree--trie dict) key))
-          plist tail)
-      (when (and cell
-                (setq tail
-                      (plist-member
-                       (setq plist (dictree--cell-plist cell))
-                       property)))
-       (setf (dictree-modified dict) t)
-       ;; delete property and value from plist
-       (setcdr tail (cddr tail))
-       (setq plist (delq property plist))
-       (setf (dictree--cell-plist cell) plist))))
-   ))
-
-
-
-(defun dictree-get-property (dict key property &optional nilflag)
-  "Get the value of PROPERTY for KEY in dictionary DICT,
-or return nil if KEY is not in the dictionary.
-
-Optional argument NILFLAG specifies a value to return instead of
-nil if KEY does not exist in TREE. This allows a non-existent KEY
-to be distinguished from a key for which PROPERTY is not
-set. (See also `dictree-member-p' for testing existence alone.)"
-  (let ((cell (dictree--lookup dict key nilflag)))
-    (unless (eq cell nilflag)
-      (plist-get (dictree--cell-plist cell) property))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                        Mapping functions
-
-(defun dictree-mapc (function dict &optional type reverse)
-  "Apply FUNCTION to all entries in dictionary DICT,
-for side-effects only.
-
-FUNCTION will be passed two arguments: a key of type
-TYPE (`string', `vector', or `list', defaulting to `vector') from the
-dictionary, and the data associated with that key. The dictionary
-entries will be traversed in \"lexicographic\" order, i.e. the
-order defined by the dictionary's comparison function (cf.
-`dictree-create').
-
-If TYPE is string, it must be possible to apply the function
-`string' to the elements of sequences stored in DICT.
-
-FUNCTION is applied in ascending order, or descending order if
-REVERSE is non-nil."
-
-  ;; "rename" FUNCTION to something hopefully unique to lessen the
-  ;; likelihood of dynamic scoping bugs caused by a supplied function
-  ;; binding a variable with the same name as one of the arguments
-  (let ((--dictree-mapc--function function))
-    (dictree--mapc
-     (lambda (key data _plist)
-       (funcall --dictree-mapc--function key data))
-     dict type reverse)))
-
-
-
-(defun dictree--mapc (function dict &optional type reverse)
-  ;; Like `dictree-mapc', but FUNCTION is passed three arguments: the
-  ;; key, the data, and the property list, instead of just key and data.
-
-  ;; try to avoid dynamic binding bugs
-  (let ((--dictree--mapc--function function))
-    (if (dictree--meta-dict-p dict)
-       ;; for a meta-dict, use a dictree-stack
-       (let ((stack (dictree-stack dict))
-             entry)
-         (while (setq entry (dictree--stack-pop stack))
-           (funcall --dictree--mapc--function
-                    (car entry)
-                    (dictree--cell-data (cdr entry))
-                    (dictree--cell-plist (cdr entry)))))
-      ;; for a normal dictionary, map the function over its trie
-      (trie-mapc
-       (lambda (key cell)
-        (funcall --dictree--mapc--function
-                 key
-                 (dictree--cell-data cell)
-                 (dictree--cell-plist cell)))
-       (dictree--trie dict)
-       type reverse)
-      )))
-
-
-
-(defun dictree-mapf (function combinator dict &optional type reverse)
-  "Apply FUNCTION to all entries in dictionary DICT,
-and combine the results using COMBINATOR.
-
-FUNCTION should take two arguments: a key sequence from the
-dictionary and its associated data.
-
-Optional argument TYPE (one of the symbols `vector', `lisp' or
-`string'; defaults to `vector') sets the type of sequence passed to
-FUNCTION. If TYPE is `string', it must be possible to apply the
-function `string' to the individual elements of key sequences
-stored in DICT.
-
-The FUNCTION will be applied and the results combined in
-asscending \"lexicographic\" order (i.e. the order defined by the
-dictionary's comparison function; cf. `dictree-create'), or
-descending order if REVERSE is non-nil."
-
-  ;; try to avoid dynamic scoping bugs
-  (let ((--dictree-mapf--function function)
-       (--dictree-mapf--combinator combinator))
-
-    ;; for a normal dictionary, map the function over its trie
-    (if (not (dictree--meta-dict-p dict))
-       (trie-mapf
-        `(lambda (key data)
-           (,--dictree-mapf--function key (dictree--cell-data data)))
-        --dictree-mapf--combinator (dictree--trie dict) type reverse)
-
-      ;; for a meta-dict, use a dictree-stack
-      (let ((--dictree-mapf--stack (dictree-stack dict))
-           --dictree-mapf--entry
-           --dictree-mapf--accumulate)
-       (while (setq --dictree-mapf--entry
-                    (dictree-stack-pop --dictree-mapf--stack))
-         (setq --dictree-mapf--accumulate
-               (funcall --dictree-mapf--combinator
-                        (funcall --dictree-mapf--function
-                                 (car --dictree-mapf--entry)
-                                 (cdr --dictree-mapf--entry))
-                        --dictree-mapf--accumulate)))
-       --dictree-mapf--accumulate))))
-
-
-
-(defun dictree-mapcar (function dict &optional type reverse)
-  "Apply FUNCTION to all entries in dictionary DICT,
-and make a list of the results.
-
-FUNCTION should take two arguments: a key sequence from the
-dictionary and its associated data.
-
-Optional argument TYPE (one of the symbols `vector', `list' or
-`string'; defaults to `vector') sets the type of sequence passed
-to FUNCTION. If TYPE is string, it must be possible to apply the
-function `string' to the individual elements of key sequences
-stored in DICT.
-
-The FUNCTION will be applied and the results combined in
-asscending \"lexicographic\" order \(i.e. the order defined by
-the dictionary's comparison function; cf. `dictree-create'\), or
-descending order if REVERSE is non-nil.
-
-Note that if you don't care about the order in which FUNCTION is
-applied, just that the resulting list is in the correct order,
-then
-
-  (dictree-mapf function #\\='cons dict type (not reverse))
-
-is more efficient."
-  (nreverse (dictree-mapf function #'cons dict type reverse)))
-
-
-
-(defun dictree-size (dict)
-  "Return the number of entries in dictionary DICT.
-Interactively, DICT is read from the mini-buffer."
-  (interactive (list (read-dict "Dictionary: ")))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-  (let ((count 0))
-    (dictree-mapc (lambda (&rest _dummy) (incf count)) dict)
-    (when (called-interactively-p 'interactive)
-      (message "Dictionary %s contains %d entries"
-              (dictree--name dict) count))
-    count))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                        Using dictrees as stacks
-
-;; A dictree--meta-stack is the meta-dict version of a dictree-stack (the
-;; ordinary version is just a single trie-stack). It consists of a heap of
-;; trie-stacks for its constituent tries, where the heap order is the usual
-;; lexicographic order over the keys at the top of the trie-stacks.
-
-(defstruct
-  (dictree--meta-stack
-   (:constructor nil)
-   (:constructor dictree--meta-stack-create
-                (dict &optional (type 'vector) reverse
-                 &aux
-                 (combfun (dictree--wrap-combfun
-                           (dictree--meta-dict-combine-function dict)))
-                 (sortfun (trie-construct-sortfun
-                           (dictree-comparison-function dict)))
-                 (heap (heap-create
-                        (dictree--construct-meta-stack-heapfun sortfun)
-                        (length (dictree--trielist dict))))
-                 (pushed '())
-                 (_dummy (mapc
-                          (lambda (dic)
-                            (heap-add
-                             heap (trie-stack dic type reverse)))
-                          (dictree--trielist dict)))))
-   (:constructor dictree--complete-meta-stack-create
-                (dict prefix &optional reverse
-                 &aux
-                 (combfun (dictree--wrap-combfun
-                           (dictree--meta-dict-combine-function dict)))
-                 (sortfun (trie-construct-sortfun
-                           (dictree-comparison-function dict)))
-                 (heap (heap-create
-                        (dictree--construct-meta-stack-heapfun
-                         sortfun reverse)
-                        (length (dictree--trielist dict))))
-                 (pushed '())
-                 (_dummy (mapc
-                          (lambda (trie)
-                            (let ((stack (trie-complete-stack
-                                          trie prefix reverse)))
-                              (unless (trie-stack-empty-p stack)
-                                (heap-add heap stack))))
-                          (dictree--trielist dict)))))
-      (:constructor dictree--regexp-meta-stack-create
-                (dict regexp &optional reverse
-                 &aux
-                 (combfun (dictree--wrap-combfun
-                           (dictree--meta-dict-combine-function dict)))
-                 (sortfun (dictree--wrap-regexp-sortfun
-                           (dictree-comparison-function dict) 'reverse))
-                 (heap (heap-create
-                        (dictree--construct-meta-stack-heapfun
-                         sortfun reverse)
-                        (length (dictree--trielist dict))))
-                 (pushed '())
-                 (_dummy (mapc
-                          (lambda (trie)
-                            (let ((stack (trie-regexp-stack
-                                          trie regexp reverse)))
-                              (unless (trie-stack-empty-p stack)
-                                (heap-add heap stack))))
-                          (dictree--trielist dict)))))
-      (:constructor dictree--fuzzy-match-meta-stack-create
-                (dict string distance &optional reverse
-                 &aux
-                 (combfun (dictree--wrap-combfun
-                           (dictree--meta-dict-combine-function dict)))
-                 (sortfun (dictree--wrap-fuzzy-sortfun
-                           (dictree-comparison-function dict)))
-                 (heap (heap-create
-                        (dictree--construct-meta-stack-heapfun
-                         sortfun reverse)
-                        (length (dictree--trielist dict))))
-                 (pushed '())
-                 (_dummy (mapc
-                          (lambda (trie)
-                            (let ((stack (trie-fuzzy-match-stack
-                                          trie string distance reverse)))
-                              (unless (trie-stack-empty-p stack)
-                                (heap-add heap stack))))
-                          (dictree--trielist dict)))))
-      (:constructor dictree--fuzzy-complete-meta-stack-create
-                (dict prefix distance &optional reverse
-                 &aux
-                 (combfun (dictree--wrap-combfun
-                           (dictree--meta-dict-combine-function dict)))
-                 (sortfun (dictree--wrap-fuzzy-sortfun
-                           (dictree-comparison-function dict)))
-                 (heap (heap-create
-                        (dictree--construct-meta-stack-heapfun
-                         sortfun reverse)
-                        (length (dictree--trielist dict))))
-                 (pushed '())
-                 (_dummy (mapc
-                          (lambda (trie)
-                            (let ((stack (trie-fuzzy-complete-stack
-                                          trie prefix distance reverse)))
-                              (unless (trie-stack-empty-p stack)
-                                (heap-add heap stack))))
-                          (dictree--trielist dict)))))
-   (:copier nil))
-  combfun sortfun heap pushed)
-
-
-
-(defun dictree--construct-meta-stack-heapfun (sortfun &optional reverse)
-  ;; Wrap SORTFUN, which sorts keys, so it can act on
-  ;; dictree--meta-stack elements.
-  (if reverse
-      `(lambda (b a) (,sortfun (car (dictree-stack-first a))
-                              (car (dictree-stack-first b))))
-    `(lambda (a b) (,sortfun (car (dictree-stack-first a))
-                            (car (dictree-stack-first b))))))
-
-
-(defun dictree-stack (dict &optional type reverse)
-  "Create an object that allows DICT to be accessed as a stack.
-
-The stack is sorted in \"lexicographic\" order, i.e. the order
-defined by the DICT's comparison function, or in reverse order if
-REVERSE is non-nil. Calling `dictree-stack-pop' pops the top
-element (a key and its associated data) from the stack.
-
-Optional argument TYPE (one of the symbols `vector', `list' or
-`string') sets the type of sequence used for the keys.
-
-Note that any modification to DICT *immediately* invalidates all
-dictree-stacks created before the modification (in particular,
-calling `dictree-stack-pop' will give unpredictable results).
-
-Operations on dictree-stacks are significantly more efficient
-than constructing a real stack from the dictionary and using
-standard stack functions. As such, they can be useful in
-implementing efficient algorithms on dictionaries. However, in
-cases where mapping functions `dictree-mapc', `dictree-mapcar' or
-`dictree-mapf' would be sufficient, it is better to use one of
-those instead."
-  (if (dictree--meta-dict-p dict)
-      (dictree--meta-stack-create dict type reverse)
-    (trie-stack (dictree--trie dict) type reverse)))
-
-
-(defun dictree-complete-stack (dict prefix &optional reverse)
-  "Return an object that allows completions of PREFIX to be accessed
-as if they were a stack.
-
-The stack is sorted in \"lexicographic\" order, i.e. the order
-defined by DICT's comparison function, or in reverse order if
-REVERSE is non-nil. Calling `dictree-stack-pop' pops the top
-element (a key and its associated data) from the stack.
-
-PREFIX must be a sequence (vector, list or string) that forms the
-initial part of a DICT key. (If PREFIX is a string, it must be
-possible to apply `string' to individual elements of DICT keys.)
-The returned keys will be sequences of the same type as
-PREFIX. If PREFIX is a list of sequences, completions of all
-sequences in the list are included in the stack. All sequences in
-the list must be of the same type.
-
-Note that any modification to DICT *immediately* invalidates all
-dictree-stacks created before the modification (in particular,
-calling `dictree-stack-pop' will give unpredictable results).
-
-Operations on dictree-stacks are significantly more efficient
-than constructing a real stack from completions of PREFIX in DICT
-and using standard stack functions. As such, they can be useful
-in implementing efficient algorithms on dict-trees. However, in
-cases where `dictree-complete' is sufficient, it is better to use
-that instead."
-  (if (dictree--meta-dict-p dict)
-      (dictree--complete-meta-stack-create dict prefix reverse)
-    (trie-complete-stack (dictree--trie dict) prefix reverse)))
-
-
-(defun dictree-regexp-stack (dict regexp &optional reverse)
-  "Return an object that allows REGEXP matches to be accessed
-as if they were a stack.
-
-The stack is sorted in \"lexicographic\" order, i.e. the order
-defined by DICT's comparison function, or in reverse order if
-REVERSE is non-nil. Calling `dictree-stack-pop' pops the top
-element (a key and its associated data) from the stack.
-
-REGEXP is a regular expression, but it need not necessarily be a
-string. It must be a sequence (vector, list of string) whose
-elements are either elements of the same type as elements of the
-keys in DICT (which behave as literals in the regexp), or any of
-the usual regexp special characters and backslash constructs. If
-REGEXP is a string, it must be possible to apply `string' to
-individual elements of the keys stored in DICT. The matches
-returned in the alist will be sequences of the same type as KEY.
-
-Back-references and non-greedy postfix operators are *not*
-supported, and the matches are always anchored, so `$' and `^'
-lose their special meanings.
-
-If the regexp contains any non-shy grouping constructs, subgroup
-match data is included in the results. In this case, the car of
-each match is no longer just a key. Instead, it is a list whose
-first element is the matching key, and whose remaining elements
-are cons cells whose cars and cdrs give the start and end indices
-of the elements that matched the corresponding groups, in order.
-
-Note that any modification to DICT *immediately* invalidates all
-dictree-stacks created before the modification (in particular,
-calling `dictree-stack-pop' will give unpredictable results).
-
-Operations on dictree-stacks are significantly more efficient
-than constructing a real stack from completions of PREFIX in DICT
-and using standard stack functions. As such, they can be useful
-in implementing efficient algorithms on dict-trees. However, in
-cases where `dictree-regexp-search' is sufficient, it is better
-to use that instead."
-  (if (dictree--meta-dict-p dict)
-      (dictree--regexp-meta-stack-create dict regexp reverse)
-    (trie-regexp-stack (dictree--trie dict) regexp reverse)))
-
-
-(defun dictree-fuzzy-match-stack (dict string distance &optional reverse)
-  "Return an object that allows fuzzy matches to be accessed
-as if they were a stack.
-
-The stack is sorted in \"lexicographic\" order, i.e. the order
-defined by DICT's comparison function, or in reverse order if
-REVERSE is non-nil. Calling `dictree-stack-pop' pops the top
-element (a key and its associated data) from the stack.
-
-STRING must be a sequence (vector, list or string), and DISTANCE
-must be an integer. (If STRING is a string, it must be possible
-to apply `string' to individual elements of DICT keys.) The
-matches returned in the alist will be sequences of the same type
-as STRING that are within Lewenstein distance DISTANCE of
-STRING. If STRING is a list of sequences, keys withing DISTANCE
-of any sequences in the list are included in the stack. All
-sequences in the list must be of the same type.
-
-Note that any modification to DICT *immediately* invalidates all
-dictree-stacks created before the modification (in particular,
-calling `dictree-stack-pop' will give unpredictable results).
-
-Operations on dictree-stacks are significantly more efficient
-than constructing a real stack from fuzzy matches within DISTANCE
-of STRING in DICT and using standard stack functions. As such,
-they can be useful in implementing efficient algorithms on
-dict-trees. However, in cases where `dictree-fuzzy-match' is
-sufficient, it is better to use that instead."
-  (if (dictree--meta-dict-p dict)
-      (dictree--fuzzy-match-meta-stack-create dict string distance reverse)
-    (trie-fuzzy-match-stack (dictree--trie dict) string distance reverse)))
-
-
-(defun dictree-fuzzy-complete-stack (dict prefix distance &optional reverse)
-  "Return an object that allows fuzzy completions to be accessed
-as if they were a stack.
-
-The stack is sorted in \"lexicographic\" order, i.e. the order
-defined by DICT's comparison function, or in reverse order if
-REVERSE is non-nil. Calling `dictree-stack-pop' pops the top
-element (a key and its associated data) from the stack.
-
-PREFIX must be a sequence (vector, list or string), and DISTANCE
-must be an integer. (If PREFIX is a string, it must be possible
-to apply `string' to individual elements of DICT keys.) The
-completions returned in the alist will be sequences of the same
-type as STRING that are completions of prefixes within Lewenstein
-distance DISTANCE of PREFIX. If PREFIX is a list of sequences,
-completions within DISTANCE of any prefix in the list are
-included in the stack. All sequences in the list must be of the
-same type.
-
-Note that any modification to DICT *immediately* invalidates all
-dictree-stacks created before the modification (in particular,
-calling `dictree-stack-pop' will give unpredictable results).
-
-Operations on dictree-stacks are significantly more efficient
-than constructing a real stack from fuzzy matches within DISTANCE
-of STRING in DICT and using standard stack functions. As such,
-they can be useful in implementing efficient algorithms on
-dict-trees. However, in cases where `dictree-fuzzy-complete' is
-sufficient, it is better to use that instead."
-  (if (dictree--meta-dict-p dict)
-      (dictree--fuzzy-complete-meta-stack-create dict prefix distance reverse)
-    (trie-fuzzy-complete-stack (dictree--trie dict) prefix distance reverse)))
-
-
-(defun dictree-stack-pop (dictree-stack)
-  "Pop the first element from the DICTREE-STACK.
-Returns nil if the stack is empty."
-  (cond
-   ;; if elements have been pushed onto a dict stack, pop those first
-   ;; FIXME: shouldn't be using internal trie functions!
-   ((and (trie-stack-p dictree-stack)
-        (trie--stack-pushed dictree-stack))
-    (trie-stack-pop dictree-stack))
-   ;; if elements have been pushed onto a meta-dict stack, pop those
-   ;; first
-   ((and (dictree--meta-stack-p dictree-stack)
-        (dictree--meta-stack-pushed dictree-stack))
-    (pop (dictree--meta-stack-pushed dictree-stack)))
-   ;; otherwise, pop first element from dictree-stack
-   (t (let ((popped (dictree--stack-pop dictree-stack)))
-       (when popped
-         (cons (car popped) (dictree--cell-data (cdr popped))))))
-   ))
-
-
-(defun dictree-stack-push (element dictree-stack)
-  "Push ELEMENT onto DICTREE-STACK."
-  (if (trie-stack-p dictree-stack)
-      ;; normal dict
-      (trie-stack-push element dictree-stack)
-    ;; meta-dict
-    (push element (dictree--meta-stack-pushed dictree-stack))))
-
-
-(defun dictree-stack-first (dictree-stack)
-  "Return the first element from DICTREE-STACK, without removing it.
-Returns nil if the stack is empty."
-  ;; if elements have been pushed onto the stack, return first of those
-  (if (and (dictree--meta-stack-p dictree-stack)
-          (dictree--meta-stack-pushed dictree-stack))
-      (car (dictree--meta-stack-pushed dictree-stack))
-    ;; otherwise, return first element from dictree-stack
-    (let ((first (dictree--stack-first dictree-stack)))
-      (cons (car first) (dictree--cell-data (cdr first))))))
-
-
-(defun dictree-stack-empty-p (dictree-stack)
-  "Return t if DICTREE-STACK is empty, nil otherwise."
-  (if (trie-stack-p dictree-stack)
-      ;; normal dict
-      (trie-stack-empty-p dictree-stack)
-    ;; meta-dict
-    (and (heap-empty (dictree--meta-stack-heap dictree-stack))
-        (null (dictree--meta-stack-pushed dictree-stack)))))
-
-
-(defun dictree--stack-first (dictree-stack)
-  "Return the first element from DICTREE-STACK, without removing it.
-Returns nil if the stack is empty."
-  (if (trie-stack-p dictree-stack)
-      ;; normal dict
-      (trie-stack-first dictree-stack)
-    ;; meta-dict
-    (if (dictree--meta-stack-pushed dictree-stack)
-       ;; pushed element
-       (car (dictree--meta-stack-pushed dictree-stack))
-      ;; dictree-stack element
-      (dictree--stack-first
-       (heap-root (dictree--meta-stack-heap dictree-stack))))))
-
-
-(defun dictree--stack-pop (dictree-stack)
-  ;; Pop the raw first element from DICTREE-STACK. Returns nil if the
-  ;; stack is empty.
-
-  ;; dictree-stack for normal dictionaries is a trie-stack
-  (if (trie-stack-p dictree-stack)
-      (trie-stack-pop dictree-stack)
-
-    ;; meta-dictionary dictree-stack...more work!
-    ;; if elements have been pushed onto meta-dict stack, pop those
-    ;; first
-    (if (dictree--meta-stack-pushed dictree-stack)
-       (pop (dictree--meta-stack-pushed dictree-stack))
-      ;; otherwise...
-      (let ((heap (dictree--meta-stack-heap dictree-stack))
-           (sortfun (dictree--meta-stack-sortfun dictree-stack))
-           stack curr next)
-       (unless (heap-empty heap)
-         ;; remove the first dictree-stack from the heap, pop it's
-         ;; first element, and add it back to the heap (note that it
-         ;; will almost certainly not end up at the root again)
-         (setq stack (heap-delete-root heap))
-         (setq curr (dictree--stack-pop stack))
-         (unless (dictree-stack-empty-p stack) (heap-add heap stack))
-         ;; peek at the first element of the stack now at the root of
-         ;; the heap
-         (unless (heap-empty heap)
-           (setq next (dictree--stack-first (heap-root heap)))
-           ;; repeat this as long as we keep finding elements with the
-           ;; same key, combining them together as we go
-           (when (dictree--meta-stack-combfun dictree-stack)
-             (while (and (null (funcall sortfun
-                                        (car curr) (car next)))
-                         (null (funcall sortfun
-                                        (car next) (car curr))))
-               (setq stack (heap-delete-root heap))
-               (setq next (dictree--stack-pop stack))
-               (setq curr (cons (car curr)
-                                (funcall
-                                 (dictree--meta-stack-combfun dictree-stack)
-                                 (cdr curr) (cdr next))))
-               (heap-add heap stack)
-               (setq next (dictree--stack-first (heap-root heap))))))
-         ;; return the combined dictionary element
-         curr)))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                    dictree iterator generators
-
-;; dictree-stacks *are* iterators (with additional push and
-;; inspect-first-element operations). If we're running on a modern Emacs that
-;; includes the `generator' library, we can trivially define dictree iterator
-;; generators in terms of dictree-stacks.
-
-(heap--when-generators
- (iter-defun dictree-iter (dict &optional type reverse)
-   "Return a dictree iterator object.
-
-Calling `iter-next' on this object will retrieve the next
-element (a cons cell containing a key and its associated data)
-from DICT in \"lexicographic\" order, i.e. the order defined by
-the DICT's comparison function, or in reverse order if REVERSE is
-non-nil.
-
-Optional argument TYPE (one of the symbols `vector', `list' or
-`string') sets the type of sequence used for the keys.
-
-Note that any modification to DICT *immediately* invalidates all
-iterators created from DICT before the modification (in
-particular, calling `iter-next' will give unpredictable
-results). If DICT is a meta-dict, this includes any modifications
-to its constituent dicts."
-   (let ((stack (dictree-stack dict type reverse)))
-     (while (not (dictree-stack-empty-p stack))
-       (iter-yield (dictree-stack-pop stack))))))
-
-
-(heap--when-generators
- (iter-defun dictree-complete-iter (dict prefix &optional reverse)
-   "Return an iterator object for completions of PREFIX in DICT.
-
-Calling `iter-next' on this object will retrieve the next
-completion of PREFIX (a cons cell containing a key and its
-associated data) from DICT in \"lexicographic\" order, i.e. the
-order defined by DICT's comparison function, or in reverse order
-if REVERSE is non-nil.
-
-PREFIX must be a sequence (vector, list or string) that forms the
-initial part of a DICT key. (If PREFIX is a string, it must be
-possible to apply `string' to individual elements of DICT keys.)
-The returned keys will be sequences of the same type as
-PREFIX. If PREFIX is a list of sequences, completions of all
-sequences in the list are included in the stack. All sequences in
-the list must be of the same type.
-
-Note that any modification to DICT *immediately* invalidates all
-iterators created from DICT before the modification (in
-particular, calling `iter-next' will give unpredictable
-results). If DICT is a meta-dict, this includes any modifications
-to its constituent dicts."
-   (let ((stack (dictree-complete-stack dict prefix reverse)))
-     (while (not (dictree-stack-empty-p stack))
-       (iter-yield (dictree-stack-pop stack))))))
-
-
-(heap--when-generators
- (iter-defun dictree-regexp-iter (dict regexp &optional reverse)
-   "Return an iterator object for REGEXP matches in DICT.
-
-Calling `iter-next' on this object will retrieve the next match
-\(a cons cell containing a key and its associated data\) in
-\"lexicographic\" order, i.e. the order defined by DICT's
-comparison function, or in reverse order if REVERSE is non-nil.
-
-REGEXP is a regular expression, but it need not necessarily be a
-string. It must be a sequence (vector, list of string) whose
-elements are either elements of the same type as elements of the
-keys in DICT (which behave as literals in the regexp), or any of
-the usual regexp special characters and backslash constructs. If
-REGEXP is a string, it must be possible to apply `string' to
-individual elements of the keys stored in DICT. The matches
-returned in the alist will be sequences of the same type as KEY.
-
-Back-references and non-greedy postfix operators are *not*
-supported, and the matches are always anchored, so `$' and `^'
-lose their special meanings.
-
-If the regexp contains any non-shy grouping constructs, subgroup
-match data is included in the results. In this case, the car of
-each match is no longer just a key. Instead, it is a list whose
-first element is the matching key, and whose remaining elements
-are cons cells whose cars and cdrs give the start and end indices
-of the elements that matched the corresponding groups, in order.
-
-Note that any modification to DICT *immediately* invalidates all
-iterators created from DICT before the modification (in
-particular, calling `iter-next' will give unpredictable
-results). If DICT is a meta-dict, this includes any modifications
-to its constituent dicts."
-   (let ((stack (dictree-regexp-stack dict regexp reverse)))
-     (while (not (dictree-stack-empty-p stack))
-       (iter-yield (dictree-stack-pop stack))))))
-
-(heap--when-generators
- (iter-defun dictree-fuzzy-match-iter (dict string distance &optional reverse)
-   "Return an iterator object for fuzzy matches to STRING in DICT.
-
-Calling `iter-next' on this object will retrieve the next match
-\(a cons cell containing a key and its associated data\) in
-\"lexicographic\" order, i.e. the order defined by DICT's
-comparison function, or in reverse order if REVERSE is non-nil.
-
-STRING must be a sequence (vector, list or string), and DISTANCE
-must be an integer. (If STRING is a string, it must be possible
-to apply `string' to individual elements of DICT keys.) The
-returned keys will be sequences of the same type as STRING that
-are within Lewenstein distance DISTANCE of STRING. If STRING is a
-list of sequences, keys withing DISTANCE of any sequences in the
-list are included in the stack. All sequences in the list must be
-of the same type.
-
-Note that any modification to DICT *immediately* invalidates all
-iterators created from DICT before the modification (in
-particular, calling `iter-next' will give unpredictable
-results). If DICT is a meta-dict, this includes any modifications
-to its constituent dicts."
-   (let ((stack (dictree-fuzzy-match-stack dict string distance reverse)))
-     (while (not (dictree-stack-empty-p stack))
-       (iter-yield (dictree-stack-pop stack))))))
-
-
-(heap--when-generators
- (iter-defun dictree-fuzzy-complete-iter (dict prefix distance &optional 
reverse)
-   "Return an iterator object for fuzzy completions of PREFIX in DICT.
-
-Calling `iter-next' on this object will retrieve the next fuzzy
-completion in \"lexicographic\" order, i.e. the order defined by
-DICT's comparison function, or in reverse order if REVERSE is
-non-nil. Each returned element has the form:
-
-    ((KEY . DIST) . DATA)
-
-PREFIX must be a sequence (vector, list or string), and DISTANCE
-must be an integer. (If PREFIX is a string, it must be possible
-to apply `string' to individual elements of DICT keys.) The
-returned keys will be sequences of the same type as STRING that
-are completions of prefixes within Lewenstein distance DISTANCE
-of PREFIX. If PREFIX is a list of sequences, completions within
-DISTANCE of any prefix in the list are included in the stack. All
-sequences in the list must be of the same type.
-
-Note that any modification to DICT *immediately* invalidates all
-iterators created from DICT before the modification (in
-particular, calling `iter-next' will give unpredictable
-results). If DICT is a meta-dict, this includes any modifications
-to its constituent dicts."
-   (let ((stack (dictree-fuzzy-complete-stack dict prefix distance reverse)))
-     (while (not (dictree-stack-empty-p stack))
-       (iter-yield (dictree-stack-pop stack))))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;             Functions for building advanced queries
-
-(defun dictree--query
-    (dict triefun stackfun cachefun cachecreatefun cache-long no-cache arg
-     &optional auxargs rank-function rankfun maxnum reverse filter resultfun)
-  ;; Return results of querying DICT with argument ARG (and AUXARGS list, if
-  ;; any) using TRIEFUN or STACKFUN. If DICT's cache-threshold is non-nil,
-  ;; look first for cached result in cache returned by calling CACHEFUN on
-  ;; DICT, and cache result if query fulfils caching conditions. Non-nil
-  ;; CACHE-LONG indicates long ARGs should be cached, rather than short
-  ;; ARGs. If RANK-FUNCTION is non-nil, return results ordered
-  ;; accordingly. RANKFUN should be the appropriately wrapped version of
-  ;; RANK-FUNCTION. If MAXNUM is an integer, only the first MAXNUM results
-  ;; will be returned. If REVERSE is non-nil, results are in reverse order. A
-  ;; non-nil NO-CACHE prevents caching of results, irrespective of DICT's
-  ;; cache settings. If FILTER is supplied, only results that pass FILTER are
-  ;; included. A non-nil RESULTFUN is applied to results before adding them to
-  ;; final results list. Otherwise, an alist of key-data associations is
-  ;; returned.
-
-  ;; map over all dictionaries in list
-  (when (dictree-p dict) (setq dict (list dict)))
-  (let ((sort-function (dictree--construct-sortfun (car dict)))
-       cache results res cache-entry)
-    (dolist (dic dict)
-      (when cachefun (setq cache (funcall cachefun dic)))
-      (cond
-
-       ;; if there's a cache entry with enough results, use it
-       ((and (symbolp rank-function) (symbolp filter)
-            (setq cache-entry
-                  (when cache
-                    (gethash (list arg auxargs rank-function reverse filter)
-                             cache)))
-            (or (null (dictree--cache-maxnum cache-entry))
-                (and maxnum
-                     (<= maxnum (dictree--cache-maxnum cache-entry)))))
-       (setq res (dictree--cache-results cache-entry))
-       ;; drop any excess results
-       (when (and maxnum
-                  (or (null (dictree--cache-maxnum cache-entry))
-                      (> (dictree--cache-maxnum cache-entry) maxnum)))
-         (setcdr (nthcdr (1- maxnum) results) nil)))
-
-       (t  ;; if there was nothing useful in the cache, do query and time it
-       (let (time)
-         (setq time (float-time))
-         (setq res
-               (dictree--do-query
-                dic triefun stackfun arg auxargs rankfun maxnum reverse
-                (when filter (dictree--wrap-filter filter))))
-         (setq time (- (float-time) time))
-         ;; if we're above the dictionary's cache threshold, cache the result
-         (when (and cachefun (not no-cache)
-                    (dictree--above-cache-threshold-p
-                     time (length arg) (dictree-cache-policy dic)
-                     (dictree-cache-threshold dic) cache-long))
-           (setf (dictree-modified dic) t)
-           ;; create query cache if it doesn't already exist
-           (funcall cachecreatefun dic)
-           (puthash (list arg auxargs rank-function reverse filter)
-                    (dictree--cache-create res maxnum)
-                    (funcall cachefun dic))))))
-
-      ;; merge new result into results list
-      (setq results
-           (dictree--merge results res (or rankfun sort-function)
-                           nil maxnum)))
-
-
-    ;; return results list, applying RESULTFUN if specified, otherwise just
-    ;; stripping meta-data
-    (mapcar (if resultfun
-               (dictree--wrap-resultfun resultfun)
-             (lambda (el) (cons (car el) (dictree--cell-data (cdr el)))))
-           results)))
-
-
-
-(defun dictree--do-query
-   (dict triefun stackfun arg &optional auxargs rankfun maxnum reverse filter)
-  ;; Return first MAXNUM results of querying DICT with argument ARG (and
-  ;; AUXARGS list, if any) using TRIEFUN or STACKFUN that satisfy FILTER,
-  ;; ordered according to RANKFUN (defaulting to "lexicographic" order).
-
-  ;; for a meta-dict, use a dictree-stack
-  (if (dictree--meta-dict-p dict)
-      (let ((stack (apply stackfun
-                         (append (list dict arg) auxargs (list reverse))))
-           (heap (when rankfun
-                   (heap-create   ; heap order is inverse of rank order
-                       (if reverse
-                           rankfun
-                         (lambda (a b)
-                           (not (funcall rankfun a b))))
-                       (1+ maxnum))))
-           (i 0) res results)
-       ;; pop MAXNUM results from the stack
-       (while (and (or (null maxnum) (< i maxnum))
-                   (setq res (dictree--stack-pop stack)))
-         ;; check result passes FILTER
-         (when (or (null filter) (funcall filter res))
-           (if rankfun
-               (heap-add heap res)   ; for ranked query, add to heap
-             (push res results))     ; for lexicographic query, add to list
-           (incf i)))
-       (if (null rankfun)
-           ;; for lexicographic query, reverse and return result list (we
-           ;; built it backwards)
-           (nreverse results)
-         ;; for ranked query, pass rest of results through heap
-         (while (setq res (dictree--stack-pop stack))
-           (heap-add heap res)
-           (heap-delete-root heap))
-         ;; extract results from heap
-         (while (setq res (heap-delete-root heap))
-           (push res results))
-         results))  ; return result list
-
-    ;; for a normal dict, call corresponding trie function on dict's
-    ;; trie. Note: could use a dictree-stack here too - would it be more
-    ;; efficient?
-    (apply triefun
-          (append (list (dictree--trie dict) arg) auxargs
-                  (list rankfun maxnum reverse filter)))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                        Completing
-
-(defun dictree-complete
-  (dict prefix
-   &optional rank-function maxnum reverse filter resultfun no-cache)
-  "Return an alist containing all completions of PREFIX in DICT
-along with their associated data, sorted according to
-RANK-FUNCTION (defaulting to \"lexicographic\" order, i.e. the
-order defined by the dictionary's comparison function,
-cf. `dictree-create'). Return nil if no completions are found.
-
-PREFIX can also be a list of sequences, in which case completions
-of all elements in the list are returned, merged together in a
-single sorted alist.
-
-DICT can also be a list of dictionaries, in which case
-completions are sought in all dictionaries in the list. (Note
-that if the same key appears in multiple dictionaries, the alist
-may contain the same key multiple times, each copy associated
-with the data from a different dictionary. If you want to combine
-identical keys, use a meta-dictionary; see
-`dictree-create-meta-dict'.)
-
-If optional argument RANK-FUNCTION is t, the completions are
-sorted according to the dictionary's rank-function (see
-`dictree-create'). Any non-nil value that *is* a function
-over-rides this. In that case, RANK-FUNCTION should accept two
-arguments, both cons cells. The car of each contains a completion
-from DICT (of the same type as PREFIX), the cdr contains its
-associated data. The RANK-FUNCTION should return non-nil if first
-argument is ranked strictly higher than the second, nil
-otherwise.
-
-The optional integer argument MAXNUM limits the results to the
-first MAXNUM completions. The default is to return all matches.
-
-If the optional argument NO-CACHE is non-nil, it prevents caching
-of the result. Ignored for dictionaries that do not have
-completion caching enabled.
-
-The FILTER argument sets a filter function for the
-completions. For each potential completion, it is passed two
-arguments: the completion, and its associated data. If the filter
-function returns nil, the completion is not included in the
-results, and doesn't count towards MAXNUM.
-
-RESULTFUN defines a function used to process results before
-adding them to the final result list. If specified, it should
-accept two arguments: a key and its associated data. Its return
-value is what gets added to the final result list, instead of the
-default key-data cons cell."
-  ;; run completion query
-  (dictree--query
-   dict #'trie-complete #'dictree-complete-stack
-   #'dictree-complete-cache #'dictree-create-complete-cache
-   nil no-cache  ; cache short PREFIXes
-   prefix nil
-   rank-function
-   (when rank-function
-     (if (functionp rank-function)
-        (dictree--wrap-rankfun rank-function)
-       (dictree--wrap-rankfun
-       (dictree--rank-function (if (listp dict) (car dict) dict)))))
-   maxnum reverse filter resultfun))
-
-
-(defun dictree-collection-function (dict string predicate all)
-  "Function for use in `try-completion', `all-completions',
-and `completing-read'. To complete from dictionary DICT, use the
-following as the COLLECTION argument of any of those functions:
-
-  (lambda (string predicate all)
-    (dictree-collection-function dict string predicate all))
-
-Note that PREDICATE will be called with two arguments: the
-completion, and its associated data."
-  (let ((completions
-        (dictree-complete dict string nil nil nil predicate
-                          (lambda (key _data) key))))
-    (if all completions (try-completion "" completions))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                      Regexp search
-
-(defun dictree-regexp-search
-  (dict regexp
-   &optional rank-function maxnum reverse filter resultfun no-cache)
-  "Return an alist containing all matches for REGEXP in DICT
-along with their associated data, in the order defined by
-RANKFUN, defauling to \"lexicographic\" order. If REVERSE is
-non-nil, the completions are sorted in the reverse order. Returns
-nil if no completions are found.
-
-DICT can also be a list of dictionaries, in which case matches
-are sought in all dictionaries in the list. (Note that if the
-same key appears in multiple dictionaries, the alist may contain
-the same key multiple times, each copy associated with the data
-from a different dictionary. If you want to combine identical
-keys, use a meta-dictionary; see `dictree-create-meta-dict'.)
-
-REGEXP is a regular expression, but it need not necessarily be a
-string. It must be a sequence (vector, list of string) whose
-elements are either of the same type as elements of DICT
-keys (these behave as literals in the regexp), or any of the
-usual regexp special characters and backslash constructs. If
-REGEXP is a string, it must be possible to apply `string' to
-individual elements of the keys stored in DICT. The matches
-returned in the alist will be sequences of the same type as
-REGEXP.
-
-Only a subset of the full Emacs regular expression syntax is
-supported. There is no support for regexp constructs that are
-only meaningful for strings (character ranges and character
-classes inside character alternatives, and syntax-related
-backslash constructs). Back-references and non-greedy postfix
-operators are not supported, so `?' after a postfix operator
-loses its special meaning. Also, matches are always anchored, so
-`$' and `^' lose their special meanings (use `.*' at the
-beginning and end of the regexp to get an unanchored match).
-
-If the regexp contains any non-shy grouping constructs, subgroup
-match data is included in the results. In this case, the car of
-each match is no longer just a key. Instead, each element of the
-results list has the form
-
-    ((KEY (START1 . END1) (START2 . END2) ...) . DATA)
-
-where the (START . END) cons cells give the start and end indices
-of the elements that matched the corresponding groups, in order.
-
-
-The optional integer argument MAXNUM limits the results to the
-first MAXNUM matches. The default is to return all matches.
-
-If the optional argument NO-CACHE is non-nil, it prevents caching
-of the result. Ignored for dictionaries that do not have wildcard
-caching enabled.
-
-
-If optional argument RANK-FUNCTION is t, the matches are sorted
-according to the dictionary's rank-function (see
-`dictree-create').
-
-Any other non-nil value of RANK-FUNCTION should be a function
-which accepts two arguments. If the regexp does not contain any
-non-shy grouping constructs, both arguments are (KEY . DATA) cons
-cells, where the car is a sequence of the same type as REGEXP. If
-the regexp does contain non-shy grouping constructs, both
-arguments are of the form
-
-    ((KEY (START1 . END1) (START2 . END2) ...) . DATA)
-
-RANKFUN should return non-nil if first argument is ranked
-strictly higher than the second, nil otherwise.
-
-
-The FILTER argument sets a filter function for the matches. If
-supplied, it is called for each possible match with two
-arguments: a key and its associated data. If the regexp contains
-non-shy grouping constructs, the first argument is of the form
-
-    (KEY (START1 . END1) (START2 . END2) ...)
-
-If the FILTER function returns nil, the match is not included in
-the results, and does not count towards MAXNUM.
-
-
-RESULTFUN defines a function used to process results before
-adding them to the final result list. If specified, it should
-accept two arguments, of the same form as those for FILTER (see
-above). Its return value is what gets added to the final result
-list, instead of the default key-data cons cell."
-
-  ;; run regexp query
-  (dictree--query
-   dict #'trie-regexp-search #'dictree-regexp-stack
-   #'dictree-regexp-cache #'dictree-create-regexp-cache
-   (if (and (eq (elt regexp (- (length regexp) 2)) ?.)
-           (eq (elt regexp (- (length regexp) 1)) ?*))
-       nil  ; cache short REGEXP if it ends in .*
-     t)     ; cache long REGEXPs otherwise
-   no-cache
-   regexp nil
-   rank-function
-   (when rank-function
-     (if (functionp rank-function)
-        (dictree--wrap-regexp-rankfun rank-function)
-       (dictree--wrap-regexp-rankfun
-       (dictree-rank-function (if (listp dict) (car dict) dict)))))
-   maxnum reverse filter resultfun))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                      Fuzzy queries
-
-(defun dictree-fuzzy-match
-  (dict string distance
-   &optional rank-function maxnum reverse filter resultfun no-cache)
-  "Return matches for STRING in DICT within Lewenstein DISTANCE
-\(edit distance\) of STRING along with their associated data, in
-the order defined by RANKFUN, defauling to \"lexicographic\"
-order. If REVERSE is non-nil, the matches are sorted in the
-reverse order. Returns nil if no completions are found.
-
-Returns a list of matches, with elements of the form:
-
-    ((KEY . DIST) . DATA)
-
-where KEY is a matching key from the trie, DATA its associated
-data, and DIST is its Lewenstein distance \(edit distance\) from
-STRING.
-
-DICT can also be a list of dictionaries, in which case matches
-are sought in all dictionaries in the list. (Note that if the
-same key appears in multiple dictionaries, the alist may contain
-the same key multiple times, each copy associated with the data
-from a different dictionary. If you want to combine identical
-keys, use a meta-dictionary; see `dictree-create-meta-dict'.)
-
-STRING is a sequence (vector, list or string), whose elements
-must be of the same type as elements of the keys stored in
-DICT. If STRING is a string, it must be possible to apply
-`string' to individual elements of DICT keys. The KEYs returned
-in the list will be sequences of the same type as STRING.
-
-DISTANCE must be an integer, and specifies the maximum Lewenstein
-distance \(edit distances\) of matches from STRING.
-
-
-If optional argument RANK-FUNCTION is the symbol `distance', the
-matches are sorted according to their Lewenstein distance from
-STRING. If it is t, the matches are sorted according to the
-dictionary's rank-function (see `dictree-create').
-
-Any other non-nil value of RANK-FUNCTION should be a function
-which accepts two arguments, both of the form
-
-  ((KEY . DIST) . DATA)
-
-where KEY is a sequence from the dictionary (of the same type as
-STRING), DIST is its Lewenstein distance from STRING, and DATA is
-its associated data. The RANK-FUNCTION should return non-nil if
-the first argument is ranked strictly higher than the second, nil
-otherwise.
-
-
-The optional integer argument MAXNUM limits the results to the
-first MAXNUM matches. The default is to return all matches.
-
-If the optional argument NO-CACHE is non-nil, it disables any
-caching of the result.
-
-The FILTER argument sets a filter function for the matches. If
-supplied, it is called for each possible match with two
-arguments: a (KEY . DIST) cons cell, and DATA. If the filter
-function returns nil, the match is not included in the results,
-and does not count towards MAXNUM.
-
-RESULTFUN defines a function used to process results before
-adding them to the final result list. If specified, it should
-accept two arguments: a (KEY . DIST) cons cell, and DATA. Its
-return value is what gets added to the final result list, instead
-of the default key-dist-data list."
-
-  ;; run fuzzy-match query
-  (dictree--query
-   dict #'trie-fuzzy-match #'dictree-fuzzy-match-stack
-   #'dictree-fuzzy-match-cache #'dictree-create-fuzzy-match-cache
-   t no-cache  ; cache long STRINGs
-   string (list distance)
-   rank-function
-   (when rank-function
-     (cond
-      ((eq rank-function 'distance) t)
-      ((functionp rank-function) (dictree--wrap-fuzzy-rankfun rank-function))
-      ((eq rank-function t)
-       (dictree--wrap-fuzzy-rankfun
-       (dictree-rank-function (if (listp dict) (car dict) dict))))))
-   maxnum reverse filter resultfun))
-
-
-(defun dictree-fuzzy-complete
-  (dict prefix distance
-   &optional rank-function maxnum reverse filter resultfun no-cache)
-  "Return completion of prefixes in DICT within Lewenstein DISTANCE
-\(edit distance\) of PREFIX along with their associated data, in
-the order defined by RANKFUN, defauling to \"lexicographic\"
-order. If REVERSE is non-nil, the matches are sorted in the
-reverse order. Returns nil if no completions are found.
-
-Returns a list of completions, with elements of the form:
-
-    ((KEY DIST PFXLEN) . DATA)
-
-where KEY is a matching completion from the trie, DATA its
-associated data, PFXLEN is the length of the prefix part of KEY,
-and DIST is its Lewenstein distance \(edit distance\) from
-PREFIX.
-
-DICT can also be a list of dictionaries, in which case matches
-are sought in all dictionaries in the list. (Note that if the
-same key appears in multiple dictionaries, the alist may contain
-the same key multiple times, each copy associated with the data
-from a different dictionary. If you want to combine identical
-keys, use a meta-dictionary; see `dictree-create-meta-dict'.)
-
-PREFIX is a sequence (vector, list or string), whose elements
-must be of the same type as elements of the keys stored in
-DICT. If PREFIX is a string, it must be possible to apply
-`string' to individual elements of DICT keys. The KEYs returned
-in the list will be sequences of the same type as PREFIX.
-
-DISTANCE must be an integer, and specifies the maximum Lewenstein
-distance \(edit distances\) of prefixes from PREFIX.
-
-
-If optional argument RANK-FUNCTION is the symbol `distance', the
-matches are sorted by increasing Lewenstein distance of their
-prefix \(with same-distance prefixes ordered
-lexicographically\). If it is t, the matches are sorted according
-to the dictionary's rank-function (see `dictree-create').
-
-Any other non-nil value of RANK-FUNCTION should be a function
-that accepts two arguments, both of the form:
-
-    ((KEY DIST PFXLEN) . DATA)
-
-where KEY is a completion (of the same type as PREFIX), DIST is
-its Lewenstein distances from PREFIX, and DATA is its associated
-data. RANKFUN should return non-nil if first argument is ranked
-strictly higher than the second, nil otherwise.
-
-
-The optional integer argument MAXNUM limits the results to the
-first MAXNUM matches. The default is to return all matches.
-
-If the optional argument NO-CACHE is non-nil, it prevents caching
-of the result. Ignored for dictionaries that do not have
-fuzzy-match caching enabled.
-
-
-The FILTER argument sets a filter function for the matches. If
-supplied, it is called for each possible match with two
-arguments: a (KEY DIST PFXLEN) list, and DATA. If the filter
-function returns nil, the match is not included in the results,
-and does not count towards MAXNUM.
-
-RESULTFUN defines a function used to process results before
-adding them to the final result list. If specified, it should
-accept two arguments: a (KEY DIST PFXLEN) list, and DATA. Its
-return value is what gets added to the final result list, instead
-of the default key-dist-data list."
-
-  ;; run fuzzy-complete query
-  (dictree--query
-   dict #'trie-fuzzy-complete #'dictree-fuzzy-complete-stack
-   #'dictree-fuzzy-complete-cache #'dictree-create-fuzzy-complete-cache
-   nil no-cache  ; cache short PREFIXes
-   prefix (list distance)
-   rank-function
-   (when rank-function
-     (cond
-      ((eq rank-function 'distance) t)
-      ((functionp rank-function) (dictree--wrap-fuzzy-rankfun rank-function))
-      ((eq rank-function t)
-       (dictree--wrap-fuzzy-rankfun
-       (dictree-rank-function (if (listp dict) (car dict) dict))))))
-   maxnum reverse filter resultfun))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                    Persistent storage
-
-(defun dictree-save (dict &optional compilation)
-  "Save dictionary DICT to its associated file.
-Use `dictree-write' to save to a different file.
-
-Optional argument COMPILATION determines whether to save the
-dictionary in compiled or uncompiled form. The default is to save
-both forms. See `dictree-write'.
-
-Interactively, DICT is read from the mini-buffer."
-  (interactive (list (read-dict "Dictionary: ")))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-
-  (let ((filename (dictree-filename dict)))
-    ;; if dictionary has no associated file, prompt for one
-    (unless (and filename (> (length filename) 0))
-      (setq filename
-           (read-file-name
-            (format "Save dictionary %s to file\
- (leave blank to NOT save): "
-                    (dictree-name dict))
-            nil "")))
-
-    ;; if filename is blank, don't save
-    (if (string= filename "")
-       (message "Dictionary %s NOT saved" (dictree-name dict))
-      ;; otherwise write dictionary to file
-      (setf (dictree-filename dict) filename)
-      (dictree-write dict filename t compilation))))
-
-
-
-(defun dictree-write (dict &optional filename overwrite compilation)
-  "Write dictionary DICT to file FILENAME.
-Defaults to dictionary's current filename if FILENAME is not
-specified (like `dictree-save').
-
-If optional argument OVERWRITE is non-nil, no confirmation will
-be asked for before overwriting an existing file.
-
-The default is to create both compiled and uncompiled versions of
-the dictionary, with extensions .elc and .el respectively (if
-FILENAME has either of these extensions, they are stripped off
-before proceeding). The compiled version is always used in
-preference to the uncomplied version, as it loads
-faster. However, only the uncompiled version is portable between
-different Emacs versions.
-
-If optional argument COMPILATION is the symbol `compiled', only
-the compiled version will be created, whereas if it is the symbol
-`uncompiled', only the uncompiled version will be created.
-
-Interactively, DICT and FILENAME are read from the mini-buffer,
-and OVERWRITE is the prefix argument."
-  (interactive (list (read-dict "Dictionary: ")
-                    (read-file-name "Write dictionary to file: "
-                                    nil "")
-                    current-prefix-arg))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-  ;; default to DICT's current file, if any
-  (when (or (null filename)
-           (and (called-interactively-p 'any) (string= filename "")))
-    (setq filename (dictree-filename dict)))
-  (if (null filename)
-      (progn
-       (message "Dictionary %s NOT written" (dictree-name dict))
-       nil)  ; indicate dictionary wasn't written
-
-    (let (dictname buff tmpfile)
-      ;; remove any .el(c) extension from filename
-      (cond
-       ((and (> (length filename) 3)
-            (string= (substring filename -3) ".el"))
-       (setq filename (substring filename 0 -3)))
-       ((and (> (length filename) 4)
-            (string= (substring filename -4) ".elc"))
-       (setq filename (substring filename 0 -4))))
-      ;; create saved dictionary name from filename
-      (setq dictname (file-name-nondirectory filename))
-
-      (save-excursion
-       ;; create a temporary file
-       (setq buff
-             (find-file-noselect
-              (setq tmpfile (make-temp-file dictname))))
-       (set-buffer buff)
-       ;; call the appropriate write function to write the dictionary code
-       (if (dictree--meta-dict-p dict)
-           (dictree--write-meta-dict-code dict dictname filename)
-         (dictree--write-dict-code dict dictname filename))
-       (save-buffer)
-       (kill-buffer buff))
-
-      ;; prompt to overwrite if necessary
-      (when (or overwrite
-               (and
-                (or (eq compilation 'compiled)
-                    (not (file-exists-p (concat filename ".el"))))
-                (or (eq compilation 'uncompiled)
-                    (not (file-exists-p (concat filename ".elc")))))
-               (y-or-n-p
-                (format "File %s already exists. Overwrite? "
-                        (concat filename ".el(c)"))))
-       (condition-case nil
-           (progn
-             ;; move the uncompiled version to its final destination
-             (unless (eq compilation 'compiled)
-               (copy-file tmpfile (concat filename ".el") t))
-             ;; byte-compile and move the compiled version to its final
-             ;; destination
-             (unless (eq compilation 'uncompiled)
-               (if (save-window-excursion
-                     (let ((byte-compile-disable-print-circle t)
-                           err)
-                       (setq err (byte-compile-file tmpfile))
-                       err))
-                   (rename-file (concat tmpfile ".elc")
-                                (concat filename ".elc") t)
-                 (error ""))))
-         (error "Error writing dictionary. Dictionary %s NOT saved"
-                dictname))
-
-       ;; if writing to a different name, unload dictionary under old
-       ;; name and reload it under new one
-       (setf (dictree-modified dict) nil)
-       (setf (dictree-filename dict) filename)
-       (unless (string= dictname (dictree-name dict))
-         (dictree-unload dict)
-         (dictree-load filename)))
-
-      (delete-file tmpfile)
-      (message "Dictionary %s saved to %s" dictname filename)
-      t)  ; return t to indicate dictionary was successfully saved
-    ))
-
-
-
-(defun dictree-save-modified (&optional dict ask compilation force
-                                       no-fail-query)
-  "Save all modified dictionaries that have their autosave flag set.
-Returns t if all dictionaries were successfully saved. Otherwise,
-inform the user about the dictionaries which failed to save
-properly, ask them whether they wish to continue anyway, and
-return t or nil accordingly.
-
-If optional argument DICT is a list of dictionaries or a single
-dictionary, only save those.
-
-If optional argument ASK is non-nil, ask for confirmation before
-saving.
-
-Optional argument COMPILATION determines whether to save the
-dictionaries in compiled or uncompiled form. The default is to
-save both forms. See `dictree-write'.
-
-If optional argument FORCE is non-nil, save modified dictionaries
-irrespective of their autosave flag.
-
-If optional argument NO-FAIL-QUERY is non-nil, the user will not
-be queried if a dictionary fails to save properly, and the return
-value is always nil.
-
-Interactively, FORCE is the prefix argument, and the user will not be
-asked whether they wish to continue after a failed save."
-  (interactive "P")
-
-  ;; sort out arguments
-  (when (and (called-interactively-p 'any) dict) (setq dict nil force t))
-  (when (dictree-p dict) (setq dict (list dict)))
-
-  ;; For each dictionary in list / each loaded dictionary, check if
-  ;; dictionary has been modified. If so, save it if autosave is set or
-  ;; FORCE is non-nil.
-  (let (save-failures)
-    (dolist (dic (if (null dict)
-                    dictree-loaded-list
-                  dict))
-      (when (and (dictree-modified dic)
-                (or force (dictree-autosave dic))
-                (or (not ask)
-                    (y-or-n-p (format "Save modified dictionary %s? "
-                                      (dictree-filename dic)))))
-       (condition-case nil
-           (progn
-             (dictree-save dic compilation)
-             (setf (dictree-modified dic) nil))
-         (error (push dic save-failures)))))
-
-    ;; prompt if dictionary saving failed
-    (if save-failures
-       (if (or (called-interactively-p 'any) no-fail-query)
-           (progn
-             (message
-              (concat
-               "Error: failed to save the following modified "
-               "dictionaries: "
-               (mapconcat #'dictree--name save-failures ", ")))
-             nil)
-         (yes-or-no-p
-          (concat "Error: failed to save the following modified "
-                  "dictionaries: "
-                  (mapconcat #'dictree--name save-failures ", ")
-                  "; continue anyway? ")))
-      t)))
-
-
-;; Add the dictree-save-modified function to the kill-emacs-hook to save
-;; modified dictionaries when exiting emacs
-(add-hook 'kill-emacs-query-functions 'dictree-save-modified)
-
-
-
-;;;###autoload
-(defun dictree-load (file)
-  "Load a dictionary object from file FILE.
-Returns the dictionary if successful, nil otherwise.
-
-Interactively, FILE is read from the mini-buffer."
-  (interactive (list (read-dict "Load dictionary: " nil nil t t)))
-
-  ;; sort out dictionary name and file name
-  (if (or (symbolp file) (dictree-p file))
-      (message "Dictionary %s already loaded" (dictree-name file))
-
-    ;; load the dictionary
-    (if (not (load file t))
-       ;; if loading failed, throw error interactively, return nil
-       ;; non-interactively
-       (if (called-interactively-p 'any)
-           (error "Cannot open dictionary file: %s" file)
-         nil)
-
-      (let (dictname dict)
-       (setq dictname
-             (file-name-nondirectory (file-name-sans-extension file))
-             dict (symbol-value (intern-soft dictname)))
-       (if (not (dictree-p dict))
-           ;; if loading failed, throw error interactively, return nil
-           ;; non-interactively
-           (if (called-interactively-p 'any)
-               (error "Error loading dictionary file: %s" file)
-             nil)
-
-         ;; ensure the dictionary name and file name associated with
-         ;; the dictionary match the file it was loaded from
-         (when (and (string= (file-name-nondirectory file) file)
-                    (setq file
-                          (locate-file file load-path load-suffixes)))
-           (setf (dictree-filename dict) file))
-         (setf (dictree-name dict) dictname)
-
-         ;; make sure the dictionary is in dictree-loaded-list
-         ;; (normally the lisp code in the dictionary itself should do
-         ;; this, but just to make sure...)
-         (unless (memq dict dictree-loaded-list)
-           (push dict dictree-loaded-list))
-         (message (format "Loaded dictionary %s" dictname))
-
-         ;; return dictionary
-         dict)))))
-
-
-
-(defun dictree-unload (dict &optional dont-save)
-  "Unload dictionary DICT.
-If optional argument DONT-SAVE is non-nil, the dictionary will
-NOT be saved even if its autosave flag is set.
-
-Interactively, DICT is read from the mini-buffer, and DONT-SAVE
-is the prefix argument."
-  (interactive (list (read-dict "Dictionary: ")
-                    current-prefix-arg))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-
-  ;; if dictionary has been modified, autosave is set and not overidden,
-  ;; save it first
-  (when (and (dictree-modified dict)
-            (null dont-save)
-            (or (eq (dictree-autosave dict) t)
-                (and (eq (dictree-autosave dict) 'ask)
-                     (y-or-n-p
-                      (format
-                       "Dictionary %s modified.\
- Save before unloading? "
-                       (dictree-name dict))))))
-    (dictree-save dict))
-
-  ;; if unloading a meta-dict, remove reference to it from constituent
-  ;; dictionaries' meta-dict-list cell
-  (when (dictree--meta-dict-p dict)
-    (mapc
-     (lambda (dic)
-       (setf (dictree--meta-dict-list dic)
-            (delq dict (dictree--meta-dict-list dic))))
-     (dictree--meta-dict-dictlist dict)))
-
-  ;; remove dictionary from list of loaded dictionaries and unload it
-  (setq dictree-loaded-list (delq dict dictree-loaded-list))
-  ;; We used `unintern' here before, but that's too dangerous!
-  (makunbound (dictree-name dict))
-  (message "Dictionary %s unloaded" (dictree-name dict)))
-
-
-
-(defun dictree--write-dict-code (dict dictname filename)
-  ;; Write code for normal dictionary DICT to current buffer, giving it
-  ;; the name DICTNAME and file FILENAME.
-  (let (hashcode tmpdict tmptrie lookup-alist
-       complete-alist regexp-alist fuzzy-match-alist fuzzy-complete-alist)
-
-    ;; --- convert trie data ---
-    ;; if dictionary doesn't use any custom save functions, write
-    ;; dictionary's trie directly as is
-    (setq tmptrie (dictree--trie dict))
-    ;; otherwise, create a temporary trie and populate it with the
-    ;; converted contents of the dictionary's trie
-    (when (or (dictree--data-savefun dict)
-             (dictree--plist-savefun dict))
-      (setq tmptrie
-           (trie-create-custom
-            (trie-comparison-function tmptrie)
-            :createfun (trie--createfun tmptrie)
-            :insertfun (trie--insertfun tmptrie)
-            :deletefun (trie--deletefun tmptrie)
-            :lookupfun (trie--lookupfun tmptrie)
-            :mapfun (trie--mapfun tmptrie)
-            :emptyfun (trie--emptyfun tmptrie)
-            :stack-createfun (trie--stack-createfun tmptrie)
-            :stack-popfun (trie--stack-popfun tmptrie)
-            :stack-emptyfun (trie--stack-emptyfun tmptrie)))
-      (trie-mapc
-       (lambda (key cell)
-        (trie-insert tmptrie key
-                     (dictree--cell-create
-                      (funcall (or (dictree--data-savefun dict)
-                                   #'identity)
-                               (dictree--cell-data cell))
-                      (funcall (or (dictree--plist-savefun dict)
-                                   #'identity)
-                               (dictree--cell-plist cell)))))
-       (dictree--trie dict))
-
-      ;; generate code to convert contents of trie back to original form
-      (setq hashcode
-           (concat
-            hashcode
-            " (trie-map\n"
-            "  (lambda (key cell)\n"
-            "     (dictree--cell-create\n"
-            (if (dictree--data-loadfun dict)
-                (concat
-                 "(funcall (dictree--data-loadfun " dictname ")\n"
-                 "         (dictree--cell-data cell))\n")
-              "   (dictree--cell-data cell)\n")
-            (if (dictree--plist-loadfun dict)
-                (concat
-                 "(funcall (dictree--plist-loadfun " dictname ")\n"
-                 "         (dictree--cell-plist cell))))\n")
-              "   (dictree--cell-plist cell)))\n")
-            " (dictree--trie " dictname "))\n")))
-
-
-    ;; --- convert caches for writing to file ---
-    ;; hash tables have no read syntax in older Emacsen, so we convert
-    ;; them to alists for writing
-    (unless (featurep 'hashtable-print-readable)
-      ;; convert lookup cache hash table to alist, if it exists
-      (when (dictree--lookup-cache dict)
-       (maphash
-        (lambda (key val)
-          (push
-           (cons key
-                 (cons (mapcar #'car (dictree--cache-results val))
-                       (dictree--cache-maxnum val)))
-           lookup-alist))
-        (dictree--lookup-cache dict))
-       ;; generate code to reconstruct the lookup hash table
-       (setq hashcode
-             (concat
-              hashcode
-              "(let ((lookup-cache (make-hash-table :test #'equal))\n"
-              "      (trie (dictree--trie " dictname ")))\n"
-              "  (mapc\n"
-              "   (lambda (entry)\n"
-              "     (puthash\n"
-              "      (car entry)\n"
-              "      (dictree--cache-create\n"
-              "       (mapcar\n"
-              "        (lambda (key)\n"
-              "          (cons key (trie-member trie key)))\n"
-              "        (dictree--cache-results (cdr entry)))\n"
-              "       (dictree--cache-maxnum (cdr entry)))\n"
-              "      lookup-cache))\n"
-              "   (dictree--lookup-cache " dictname "))\n"
-              "  (setf (dictree--lookup-cache " dictname ")\n"
-              "        lookup-cache))\n")))
-
-      ;; convert query caches, if they exist
-      (dolist (cache-details
-              '((dictree--complete-cache complete-alist)
-                (dictree--regexp-cache regexp-alist)
-                (dictree--fuzzy-match-cache fuzzy-match-alist)
-                (dictree--fuzzy-complete-cache fuzzy-complete-alist)))
-       (when (funcall (nth 0 cache-details) dict)
-         ;; convert hash table to alist
-         (set (nth 1 cache-details)
-              (let (alist)
-                (maphash
-                 (lambda (key val)
-                   (push
-                    (cons key
-                          (cons
-                           (mapcar #'car (dictree--cache-results val))
-                           (dictree--cache-maxnum val)))
-                    alist))
-               (funcall (nth 0 cache-details) dict))
-                alist))
-         ;; generate code to reconstruct hash table from alist
-         (setq
-          hashcode
-          (concat
-           hashcode
-           "(let ((cache (make-hash-table :test #'equal))\n"
-           "      (trie (dictree--trie " dictname ")))\n"
-           "  (mapc\n"
-           "   (lambda (entry)\n"
-           "     (puthash\n"
-           "      (car entry)\n"
-           "      (dictree--cache-create\n"
-           "       (mapcar\n"
-           "        (lambda (key)\n"
-           "          (cons key\n"
-           "                (trie-member\n"
-           "                 trie (if (stringp key) key (car key)))))\n"
-           "        (dictree--cache-results (cdr entry)))\n"
-           "       (dictree--cache-maxnum (cdr entry)))\n"
-           "      cache))\n"
-           "   (" (symbol-name (nth 0 cache-details)) " " dictname "))\n"
-           "  (setf (" (symbol-name (nth 0 cache-details)) " "
-           dictname ")\n"
-           "        cache))\n")))))
-
-
-    ;; --- write to file ---
-    ;; generate the structure to save
-    (setq tmpdict (dictree--copy dict))
-    (setf (dictree--trie tmpdict) tmptrie
-         (dictree--name tmpdict) dictname
-         (dictree--filename tmpdict) filename
-         (dictree--modified tmpdict) nil
-         (dictree--meta-dict-list tmpdict) nil)
-    (unless (featurep 'hashtable-print-readable)
-      (setf (dictree--lookup-cache tmpdict)         lookup-alist
-           (dictree--complete-cache tmpdict)       complete-alist
-           (dictree--regexp-cache tmpdict)         regexp-alist
-           (dictree--fuzzy-match-cache tmpdict)    fuzzy-match-alist
-           (dictree--fuzzy-complete-cache tmpdict) fuzzy-complete-alist))
-
-    ;; write lisp code that generates the dictionary object
-    (let ((print-circle t) (print-level nil) (print-length nil))
-      (insert "(eval-when-compile (require 'cl))\n")
-      (insert "(require 'dict-tree)\n")
-      (insert "(defvar " dictname " nil \"Dictionary " dictname ".\")\n")
-      (unwind-protect
-         (progn
-           ;; transform trie to print form
-           (trie-transform-for-print (dictree--trie tmpdict))
-           (insert "(setq " dictname
-                   " '" (prin1-to-string tmpdict) ")\n"))
-       ;; if dictionary doesn't use any custom save functions, tmpdict's trie
-       ;; is identical to original dict, so transform it back to usable form
-       ;; on write error
-       (unless (or (dictree--data-savefun dict)
-                   (dictree--plist-savefun dict))
-         (trie-transform-from-read (dictree--trie tmpdict))))
-      (insert "(trie-transform-from-read (dictree--trie "
-             dictname "))\n")
-      (when hashcode (insert hashcode))
-      (insert "(unless (memq " dictname " dictree-loaded-list)\n"
-             "  (push " dictname " dictree-loaded-list))\n"))))
-
-
-
-(defun dictree--write-meta-dict-code (dict dictname filename)
-  ;; Write code for meta-dictionary DICT to current buffer, giving it
-  ;; the name DICTNAME and file FILENAME.
-  (let (hashcode tmpdict lookup-alist
-       complete-alist regexp-alist fuzzy-match-alist fuzzy-complete-alist)
-
-    ;; --- convert caches for writing to file ---
-    ;; hash tables have no read syntax in older Emacsen, so we convert
-    ;; them to alists for writing
-    (unless (featurep 'hashtable-print-readable)
-      ;; convert lookup cache hash table to an alist, if it exists
-      (when (dictree--meta-dict-lookup-cache dict)
-       (maphash (lambda (key val)
-                  (push (cons key (mapcar #'car val)) lookup-alist))
-                (dictree--meta-dict-lookup-cache dict))
-       ;; generate code to reconstruct the lookup hash table
-       (setq hashcode
-             (concat
-              hashcode
-              "(let ((cache (make-hash-table :test #'equal)))\n"
-              "  (mapc (lambda (entry)\n"
-              "    (puthash (car entry) (cdr entry) cache))\n"
-              "    (dictree--meta-dict-lookup-cache " dictname "))\n"
-              "  (setf (dictree--meta-dict-lookup-cache " dictname ")\n"
-              "        cache))\n")))
-
-      ;; convert query caches, if they exist
-      (dolist (cache-details
-              '((dictree--meta-dict-complete-cache       complete-alist)
-                (dictree--meta-dict-regexp-cache         regexp-alist)
-                (dictree--meta-dict-fuzzy-match-cache    fuzzy-match-alist)
-                (dictree--meta-dict-fuzzy-complete-cache 
fuzzy-complete-alist)))
-       (when (funcall (nth 0 cache-details) dict)
-         ;; convert hash table to alist
-         (set (nth 1 cache-details)
-              (let (alist)
-                (maphash (lambda (key val) (push (cons key val) alist))
-                         (funcall (nth 0 cache-details) dict))
-                alist))
-         ;; generate code to reconstruct hash table from alist
-         (setq
-          hashcode
-          (concat
-           hashcode
-           "(let ((cache (make-hash-table :test #'equal)))\n"
-           "  (mapc (lambda (entry)\n"
-           "    (puthash (car entry) (cdr entry) cache))\n"
-           "    (" (symbol-name (nth 0 cache-details)) " "
-                   dictname "))\n"
-           "  (setf (" (symbol-name (nth 0 cache-details)) " "
-                       dictname ")\n"
-           "        cache))\n")))))
-
-
-    ;; --- write to file ---
-    ;; generate the structure to save
-    (setq tmpdict (dictree--meta-dict-copy dict))
-    (setf (dictree--meta-dict-name tmpdict) dictname
-         (dictree--meta-dict-filename tmpdict) filename
-         (dictree--meta-dict-modified tmpdict) nil
-         (dictree--meta-dict-meta-dict-list tmpdict) nil
-         (dictree--meta-dict-dictlist tmpdict)
-           (mapcar (lambda (dic) (intern (dictree-name dic)))
-                   (dictree--meta-dict-dictlist dict)))
-    (unless (featurep 'hashtable-print-readable)
-      (setf (dictree--meta-dict-lookup-cache tmpdict)
-             lookup-alist
-           (dictree--meta-dict-complete-cache tmpdict)
-             complete-alist
-           (dictree--meta-dict-regexp-cache tmpdict)
-             regexp-alist
-           (dictree--meta-dict-fuzzy-match-cache tmpdict)
-             fuzzy-match-alist
-           (dictree--meta-dict-fuzzy-complete-cache tmpdict)
-             fuzzy-complete-alist))
-
-    ;; write lisp code that generates the dictionary object
-    (let ((print-circle t) (print-level nil) (print-length nil))
-      (insert "(eval-when-compile (require 'cl))\n"
-             "(require 'dict-tree)\n")
-      (mapc
-       (lambda (dic)
-        (insert "(unless (dictree-load \"" (dictree-filename dic) "\")\n"
-                "        (error \"Failed to load dictionary \\\""
-                (dictree-name dic) "\\\" required by meta-dict \\\""
-                dictname "\\\"\"))\n"))
-       (dictree--meta-dict-dictlist dict))
-      (insert "(defvar " dictname " nil \"Dictionary " dictname ".\")\n"
-             "(setq " dictname " " (prin1-to-string tmpdict) ")\n"
-             "(setf (dictree--meta-dict-dictlist " dictname ")\n"
-             "      (mapcar #'eval (dictree--meta-dict-dictlist "
-                                   dictname ")))\n")
-      (when hashcode (insert hashcode))
-      (insert "(unless (memq " dictname " dictree-loaded-list)"
-             " (push " dictname " dictree-loaded-list))\n"))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                Dumping and restoring contents
-
-(defun dictree-populate-from-file
-  (dict file
-   &optional insert-function key-loadfun data-loadfun plist-loadfun
-   balance)
-  "Populate dictionary DICT from the key list in file FILE.
-
-Each line of FILE should contain a key, either a string
-\(delimited by \"\), a vector, or a list. (Use the escape
-sequence \\\" to include a \" in a string.) If a line does not
-contain a key, it is silently ignored.
-
-Each line can optionally include data and a property list (in
-that order) to be associated with the key. If present, these
-should separated from each other and the key by whitespace.
-
-INSERT-FUNCTION, KEY-LOAD-FUNCTION, DATA-LOAD-FUNCTION and
-PLIST-LOAD-FUNCTION override the corresponding default functions
-for DICT (see `dictree-create').
-
-Interactively, DICT and FILE are read from the mini-buffer.
-
-
-Technicalities:
-
-The key, data and property list are read as lisp expressions
-using `read'. The keys will be read from FILE in order, unless
-BALANCE is non-nil, in which case they are read from the median
-element outwards (which can help ensure efficient data structures
-are created when using a trie that is not self-balancing, see
-`dictree-create')."
-  (interactive (list (read-dict "Dictionary: ")
-                    (read-file-name "File to populate from: "
-                                    nil "" t)))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-
-  (if (and (called-interactively-p 'any) (string= file ""))
-      (message "No file specified; dictionary %s NOT populated"
-              (dictree-name dict))
-
-    (unless (dictree--meta-dict-p dict)
-      (unless key-loadfun
-       (setq key-loadfun (dictree--key-loadfun dict)))
-      (unless data-loadfun
-       (setq data-loadfun (dictree--data-loadfun dict)))
-      (unless plist-loadfun
-       (setq plist-loadfun (dictree--plist-loadfun dict))))
-
-    (save-excursion
-      (let ((buff (find-file-noselect file)))
-       (set-buffer buff)
-
-       ;; insert the keys starting from the median to ensure a
-       ;; reasonably well-balanced tree
-       (let* ((lines (count-lines (point-min) (point-max)))
-              (midpt (+ (/ lines 2) (mod lines 2)))
-              entry)
-         (message "Inserting keys in %s...(1 of %d)"
-                  (dictree-name dict) lines)
-         ;; insert the median key and set the dictionary's modified
-         ;; flag
-         (if balance
-             (dictree--goto-line midpt)
-           (goto-char (point-min)))
-         (when (setq entry
-                     (condition-case nil
-                         (dictree--read-line key-loadfun data-loadfun
-                                             plist-loadfun)
-                       (error (error "Error reading line %d of %s"
-                                     midpt file))))
-           (dictree-insert dict (car entry) (nth 1 entry)
-                           insert-function)
-           (setf (dictree--cell-plist
-                  (dictree--lookup dict (car entry) nil))
-                 (nth 2 entry)))
-         ;; insert keys successively further away from the median in
-         ;; both directions
-         (dotimes (i (1- (if balance midpt lines)))
-           (if balance
-               (dictree--goto-line (+ midpt i 1))
-             (forward-line 1))
-           (when (setq entry
-                       (condition-case nil
-                           (dictree--read-line key-loadfun data-loadfun
-                                               plist-loadfun)
-                         (error (error "Error reading line %d of %s"
-                                       (+ midpt i 1) file))))
-             (dictree-insert dict (car entry) (nth 1 entry)
-                             insert-function)
-             (setf (dictree--cell-plist
-                    (dictree--lookup dict (car entry) nil))
-                   (nth 2 entry)))
-           (when (= 49 (mod i 50))
-             (message "Inserting keys in %s...(%d of %d)"
-                      (dictree-name dict)
-                      (if balance (+ (* 2 i) 2) i)
-                      lines))
-           (when balance
-             (dictree--goto-line (- midpt i 1))
-             (when (setq entry
-                         (condition-case nil
-                             (dictree--read-line key-loadfun data-loadfun
-                                                 plist-loadfun)
-                           (error (error "Error reading line %d of %s"
-                                         (- midpt i 1) file))))
-               (dictree-insert dict (car entry)
-                               (nth 1 entry) insert-function)
-               (setf
-                (dictree--cell-plist
-                 (dictree--lookup dict (car entry) nil))
-                (nth 2 entry)))))
-
-         ;; if inserting from mid-point out, and file contains an even
-         ;; number of keys, we still have to add the last one
-         (when (and balance (= 0 (mod lines 2)))
-           (dictree--goto-line lines)
-           (when (setq entry
-                       (condition-case nil
-                           (dictree--read-line key-loadfun data-loadfun
-                                               plist-loadfun)
-                         (error (error "Error reading line %d of %s"
-                                       lines file))))
-             (dictree-insert dict (car entry) (nth 1 entry)
-                             insert-function)
-             (setf (dictree--cell-plist
-                    (dictree--lookup dict (car entry) nil))
-                   (nth 2 entry))))
-
-         (message "Inserting keys in %s...done" (dictree-name dict)))
-       (kill-buffer buff)))))
-
-
-
-(defun dictree--read-line
-  (&optional key-loadfun data-loadfun plist-loadfun)
-  ;; Return a list containing the key, data (if any, otherwise nil) and
-  ;; property list (ditto) at the current line of the current buffer.
-  (save-excursion
-    (let (key data plist)
-      ;; read key
-      (beginning-of-line)
-      (when (setq key (read (current-buffer)))
-       (when key-loadfun (setq key (funcall key-loadfun key)))
-       ;; if there's anything after the key, use it as data
-       (unless (eq (line-end-position) (point))
-         (setq data (read (current-buffer))))
-       (when data-loadfun (setq data (funcall data-loadfun data)))
-       ;; if there's anything after the data, use it as the property list
-       (unless (eq (line-end-position) (point))
-         (setq plist (read (current-buffer))))
-       (when plist-loadfun (funcall plist-loadfun plist))
-       ;; return what we've read
-       (list key data plist)))))
-
-
-
-(defun dictree-dump-to-buffer (dict &optional buffer type)
-  "Dump keys and their associated data
-from dictionary DICT to BUFFER, in the same format as that used
-by `dictree-populate-from-file'. If BUFFER exists, data will be
-appended to the end of it. Otherwise, a new buffer will be
-created. If BUFFER is omitted, the current buffer is used.
-
-TYPE determines the type of sequence to use to represent the
-keys, and should be one of the symbols `string', `vector' or
-`list'. The default is `vector'.
-
-Note that if the data does not have a read syntax, the dumped
-data can not be used to recreate the dictionary using
-`dictree-populate-from-file'.
-
-Interactively, DICT and BUFFER are read from the mini-buffer,
-TYPE is always `string'."
-  (interactive (list (read-dict "Dictionary: ")
-                    (read-buffer
-                     "Buffer to dump to (defaults to current): "
-                     (buffer-name (current-buffer)))
-                    'string))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-
-  ;; select the buffer, creating it if necessary
-  (if buffer
-      (setq buffer (get-buffer-create buffer))
-    (setq buffer (current-buffer)))
-  (set-buffer buffer)
-
-  ;; move point to end of buffer and make sure it's at start of new line
-  (goto-char (point-max))
-  (unless (= (point) (line-beginning-position))
-    (insert "\n"))
-
-  ;; dump keys
-  (message "Dumping keys from %s to %s..."
-          (dictree-name dict) (buffer-name buffer))
-  (let ((count 0) (dictsize (dictree-size dict)))
-    (message "Dumping keys from %s to %s...(key 1 of %d)"
-            (dictree-name dict) (buffer-name buffer) dictsize)
-
-    ;; map dump function over dictionary
-    (dictree--mapc
-     (lambda (key data plist)
-       (when (= 99 (mod count 100))
-        (message "Dumping keys from %s to %s...(key %d of %d)"
-                 (dictree-name dict) (buffer-name buffer)
-                 (1+ count) dictsize))
-       (insert (prin1-to-string
-               (funcall (or (dictree--key-savefun dict) #'identity)
-                        key)))
-       (when (setq data
-                  (funcall (or (dictree--data-savefun dict) #'identity)
-                           data))
-        (insert " " (prin1-to-string data)))
-       (when (setq plist
-                  (funcall (or (dictree--plist-savefun dict) #'identity)
-                           plist))
-        (unless data (insert " nil"))
-        (insert " " (prin1-to-string plist)))
-       (insert "\n")
-       (setq count (1+ count)))
-     dict type)  ; dictree-mapc target
-
-    (message "Dumping keys from %s to %s...done"
-            (dictree-name dict) (buffer-name buffer)))
-  (switch-to-buffer buffer))
-
-
-
-(defun dictree-dump-to-file (dict filename &optional type overwrite)
-  "Dump keys and their associated data
-from dictionary DICT to a text file FILENAME, in the same format
-as that used by `dictree-populate-from-file'. Prompts to overwrite
-FILENAME if it already exists, unless OVERWRITE is non-nil.
-
-TYPE determines the type of sequence to use to represent the
-keys, and should be one of the symbols `string', `vector' or
-`list'. The default is `vector'.
-
-Note that if the data does not have a read syntax and no , the dumped
-data can not be used to recreate the dictionary using
-`dictree-populate-from-file'.
-
-Interactively, DICT and FILE are read from the mini-buffer,
-OVERWRITE is the prefix argument, and TYPE is always string."
-  (interactive (list (read-dict "Dictionary: ")
-                    (read-file-name "File to dump to: " nil "")))
-  (when (and (called-interactively-p 'any) (symbolp dict))
-    (setq dict (symbol-value dict)))
-
-  (if (and (called-interactively-p 'any) (string= filename ""))
-      (message "Dictionary %s NOT dumped" (dictree-name dict))
-
-    ;; check if file exists, and prompt to overwrite it if necessary
-    (if (and (file-exists-p filename)
-            (not overwrite)
-            (not (y-or-n-p
-                  (format "File %s already exists. Overwrite? "
-                          filename))))
-       (message "Key dump cancelled")
-
-      (let (buff)
-       ;; create temporary buffer, dump keys to it, and save to
-       ;; FILENAME
-       (setq buff (generate-new-buffer filename))
-       (save-window-excursion
-         (dictree-dump-to-buffer dict buff type)
-         (write-file filename))
-       (kill-buffer buff)))))
-
-
-
-
-;; ----------------------------------------------------------------
-;;                     Minibuffer completion
-
-(defvar dictree-history nil
-  "History list for commands that read a dictionary name.")
-
-(defvar dictree-loaded-history nil
-  "History list for commands that read a loaded dictionary name.")
-
-
-;;;###autoload
-(defun read-dict
-  (prompt &optional default dictlist allow-unloaded allow-unmatched)
-  "Read the name of a dictionary with completion, and return it.
-
-Prompt with PROMPT. By default, return DEFAULT. If DICTLIST is
-supplied, only complete on dictionaries in that list.
-
-If ALLOW-UNLOADED is non-nil, also complete on the names of
-unloaded dictionaries (actually, on any Elisp file in the current
-`load-path' restricted to subdirectories of your home directory
-whose file name starts with \"dict-\"). If an unloaded dictionary
-is read, the name of the Elisp file will be returned, without
-extension, suitable for passing to `load-library'."
-
-  (let (dictname paths)
-    ;; when allowing unloaded dictionaries...
-    (when allow-unloaded
-      ;; get paths in load-path that are subdirectories of home
-      ;; directory
-      (dolist (d load-path)
-       (when (eq (aref d 0) ?~) (push d paths)))
-      ;; gather names of all Elisp libraries in this restricted
-      ;; load-path
-      (dolist (f (all-completions
-                 "" (apply-partially #'locate-file-completion-table
-                                     paths (get-load-suffixes))))
-       (when (and (null (file-name-directory f))
-                  (and (> (length f) 5)
-                       (string= (substring f 0 5) "dict-"))
-                  (null (file-name-extension f))
-                  (not (member (file-name-sans-extension f) dictname)))
-         (push (file-name-sans-extension f) dictname))))
-    ;; gather names of loaded dictionaries
-    (mapc (lambda (dict)
-           (unless (or (null (dictree-name dict))
-                       (member (dictree-name dict) dictname))
-             (push (list (dictree-name dict)) dictname)))
-         (or dictlist dictree-loaded-list))
-    ;; do completing-read
-    (setq dictname (completing-read
-                   prompt
-                   (if allow-unmatched
-                       (completion-table-in-turn
-                        dictname #'read-file-name-internal)
-                     dictname)
-                   nil (not allow-unmatched) nil
-                   (if allow-unloaded
-                       'dictree-history
-                     'dictree-loaded-history)
-                   (and (dictree-p default) (dictree-name default))))
-    ;; return dictionary
-    (cond
-     ;; if user typed a file name, return that
-     ((and allow-unmatched (file-regular-p dictname)) dictname)
-     ;; if user selected a loaded dictionary, return dict itself
-     ((condition-case nil
-         (dictree-p (symbol-value (intern-soft dictname)))
-       (void-variable nil))
-      (intern-soft dictname))
-     ;; if user selected an unloaded dictionary, return dict name
-     ((and allow-unloaded (stringp dictname)) dictname)
-     ;; if DEFAULT was specified, return that
-     (default default)
-     ;; should never get here!
-     (t (error "Unknown error reading dictionary")))
-    ))
-
-
-
-
-;; ----------------------------------------------------------------
-;;            Pretty-print dictionaries during edebug
-
-;; We advise the `edebug-prin1' and `edebug-prin1-to-string' functions
-;; (actually, aliases) so that they print "#<dict-tree NAME>" instead of
-;; the full print form for dictionaries.
-;;
-;; This is because, if left to its own devices, edebug hangs for ages
-;; whilst printing large dictionaries, and you either have to wait for a
-;; *very* long time for it to finish, or kill Emacs entirely. (Even C-g
-;; C-g fails!)
-;;
-;; We do this also for lists of dictionaries, since those occur quite
-;; often, but not for other sequence types or deeper nested structures,
-;; to keep the implementation as simple as possible.
-;;
-;; Since the print form of a dictionary is practically incomprehensible
-;; anyway, we don't lose much by doing this. If you *really* want to
-;; print dictionaries in full whilst edebugging, despite this warning,
-;; disable the advice.
-
-
-(eval-when-compile
-  (require 'edebug)
-  (require 'advice))
-
-(defun dictree--prin1 (dict stream)
-  (princ (concat "#<dict-tree \"" (dictree-name dict) "\""
-                (if (dictree--lookup-cache dict)
-                    (concat " lookup "
-                            (prin1-to-string
-                             (hash-table-count
-                              (dictree--lookup-cache dict))))
-                  "")
-                (if (dictree--complete-cache dict)
-                    (concat " complete "
-                            (prin1-to-string
-                             (hash-table-count
-                              (dictree--complete-cache dict))))
-                  "")
-                (if (dictree--regexp-cache dict)
-                    (concat " regexp "
-                            (prin1-to-string
-                             (hash-table-count
-                              (dictree--regexp-cache dict))))
-                  "")
-                (if (dictree--fuzzy-match-cache dict)
-                    (concat " fuzzy-match "
-                            (prin1-to-string
-                             (hash-table-count
-                              (dictree--fuzzy-match-cache dict))))
-                  "")
-                (if (dictree--fuzzy-complete-cache dict)
-                    (concat " fuzzy-complete "
-                            (prin1-to-string
-                             (hash-table-count
-                              (dictree--fuzzy-complete-cache dict))))
-                  "")
-                ">")
-        stream))
-
-(defun dictree--edebug-pretty-print (object)
-  (cond
-   ((dictree-p object)
-    (concat "#<dict-tree \"" (dictree-name object) "\""
-           (if (dictree--lookup-cache object)
-               (concat " lookup "
-                       (prin1-to-string
-                        (hash-table-count
-                         (dictree--lookup-cache object))))
-             "")
-           (if (dictree--complete-cache object)
-               (concat " complete "
-                       (prin1-to-string
-                        (hash-table-count
-                         (dictree--complete-cache object))))
-             "")
-           (if (dictree--regexp-cache object)
-               (concat " regexp "
-                       (prin1-to-string
-                        (hash-table-count
-                         (dictree--regexp-cache object))))
-             "")
-           (if (dictree--fuzzy-match-cache object)
-               (concat " fuzzy-match "
-                       (prin1-to-string
-                        (hash-table-count
-                         (dictree--fuzzy-match-cache object))))
-             "")
-           (if (dictree--fuzzy-complete-cache object)
-               (concat " fuzzy-complete "
-                       (prin1-to-string
-                        (hash-table-count
-                         (dictree--fuzzy-complete-cache object))))
-             "")
-           ">"))
-   ((null object) "nil")
-   ((let ((dlist object) (test t))
-      (while (or (dictree-p (car-safe dlist))
-                (and dlist (setq test nil)))
-       (setq dlist (cdr dlist)))
-      test)
-    (concat "(" (mapconcat (lambda (d)
-                            (concat "#<dict-tree \""
-                                    (dictree-name d) "\">"))
-                          object " ") ")"))
-   ;; ((vectorp object)
-   ;;  (let ((pretty "[") (len (length object)))
-   ;;    (dotimes (i (1- len))
-   ;;  (setq pretty
-   ;;        (concat pretty
-   ;;                (if (trie-p (aref object i))
-   ;;                    "#<trie>" (prin1-to-string (aref object i))) " ")))
-   ;;    (concat pretty
-   ;;        (if (trie-p (aref object (1- len)))
-   ;;            "#<trie>" (prin1-to-string (aref object (1- len))))
-   ;;        "]")))
-   ))
-
-(if (fboundp 'cl-print-object)
-    (progn
-      (cl-defmethod cl-print-object ((object dictree-) stream)
-        (dictree--prin1 object stream))
-      (cl-defmethod cl-print-object ((object dictree--meta-dict) stream)
-        (dictree--prin1 object stream)))
-
-  (when (fboundp 'ad-define-subr-args)
-    (ad-define-subr-args 'edebug-prin1 '(object &optional printcharfun)))
-
-  (defadvice edebug-prin1
-      (around dictree activate compile preactivate)
-    (let ((pretty (dictree--edebug-pretty-print object)))
-      (if pretty
-         (progn
-           (prin1 pretty printcharfun)
-           (setq ad-return-value pretty))
-        ad-do-it)))
-
-  (when (fboundp 'ad-define-subr-args)
-    (ad-define-subr-args 'edebug-prin1-to-string '(object &optional noescape)))
-
-  (defadvice edebug-prin1-to-string
-      (around dictree activate compile preactivate)
-    (let ((pretty (dictree--edebug-pretty-print object)))
-      (if pretty
-         (setq ad-return-value pretty)
-        ad-do-it))))
-
-
-(provide 'dict-tree)
-
-;;; dict-tree.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]