src/rule-trees.lisp @ fc378d24dd2f
default tip
Make zdd union a bit cleaner
author |
Steve Losh <steve@stevelosh.com> |
date |
Tue, 30 May 2017 15:13:42 +0000 |
parents |
60c18fdad2b8 |
children |
(none) |
(in-package :scully.rule-trees)
(in-readtable :fare-quasiquote)
;;;; Rule Trees ---------------------------------------------------------------
(defun abs< (x y)
(declare (type fixnum x y))
(< (abs x) (abs y)))
(adt:defdata rule-tree
(node t rule-tree rule-tree)
(top t)
bottom)
(defun rule-tree-hi (tree)
(adt:match rule-tree tree
((node _ hi _) hi)
(_ (error "No hi for rule tree ~S" tree))))
(defun rule-tree-lo (tree)
(adt:match rule-tree tree
((node _ _ lo) lo)
(_ (error "No lo for rule tree ~S" tree))))
(defun find-smallest-body-term (bodies)
"Find the smallest body term in `bodies`.
Each body in `bodies` must already be sorted. No body should be empty.
"
(iterate (for body :in bodies)
(for term = (the fixnum (first body)))
(minimizing (abs term))))
(defun partition (bodies)
"Partition `bodies` into exclusive groups based on the smallest element.
`bodies` must each be already sorted.
Four values will be returned:
1. The smallest element in any body.
2. All bodies that DISALLOW that element.
3. All bodies that REQUIRE that element.
4. All bodies that DON'T CARE about that element.
"
(iterate
(with element = (the fixnum (find-smallest-body-term bodies)))
(with negation = (the fixnum (- element)))
(for body :in bodies)
(for term = (the fixnum (first body)))
(cond
((= term element) (collect body :into requires))
((= term negation) (collect body :into disallows))
(t (collect body :into ignores)))
(finally (return (values element disallows requires ignores)))))
(defun make-node (cache term hi lo)
(if (eql hi lo)
hi
(ensure-gethash (list term hi lo) cache
(node term hi lo))))
(defun sort-body (body)
(sort body #'abs<))
(defun make-rule-tree (rules)
"Make a rule tree for `rules`.
All rules must have the same head (this is not checked). Bodies do not need
to be sorted.
"
(let* ((head (rule-head (first rules)))
(top (top head))
(cache (make-hash-table :test #'equal)))
;; (pr head)
(recursively ((bodies (mapcar (compose #'sort-body #'rule-body) rules)))
(cond
((null bodies) bottom)
((some #'null bodies) top)
(t (multiple-value-bind (term disallows requires ignores)
(partition bodies)
(make-node cache
term
(recur (append (mapcar #'rest requires) ignores))
(recur (append (mapcar #'rest disallows) ignores)))))))))
(defun rule-tree-size (tree)
(adt:match rule-tree tree
(bottom 1)
((top _) 1)
((node _ hi lo) (+ 1
(rule-tree-size hi)
(rule-tree-size lo)))))
(defun head (tree)
(adt:match rule-tree tree
(bottom nil)
((top term) term)
((node _ hi lo) (or (head hi) (head lo)))))
;;;; Scratch ------------------------------------------------------------------