;;; -*- Mode: Lisp; Package: Prolog; Base: 10.; Options: ((World Compatibility)); -*-
;;; (C) Copyright 1983,1984,1985, Uppsala University

;;This defines predicates in LM-Prolog for use in the Dec-10 Prolog compatibility package 

(define-predicate asserta
  (:options (:argument-list (clause))
	    (:documentation "asserts CLAUSE in front of other clauses."))
  ((asserta ?clause)
   (clause-predicator ?predicator ?clause)
   (define-predicate ?predicator
     :(options (type dynamic)
	       (if-old-definition keep)
	       (place-clauses before))
     ?clause)))

(define-predicate assertz
  (:options (:argument-list (clause))
	    (:documentation "asserts CLAUSE behind other clauses."))
  ((assertz ?clause)
   (clause-predicator ?predicator ?clause)
   (define-predicate ?predicator
     :(options (type dynamic)
	       (if-old-definition keep)
	       (place-clauses after))
     ?clause)))

(define-predicate exploden
  (:options (:argument-list (asciis symbol))
	    (:documentation "converts between a symbol and a list of ASCII codes."))
  ((exploden ?asciis ?symbol)
   (cases ((not-variable ?symbol)
	   (lisp-value ?asciis (exploden '?symbol) :dont-invoke))
	  ((not-variable ?asciis)
	   (lisp-value ?symbol (implode '?asciis) :dont-invoke))
	  ((error :exploden-of-two-variables
		  "can't exploden ~n and ~n" ?asciis ?symbol)))))

(define-predicate compare
  (:options (:argument-list (relation x y))
	    (:documentation "depending on the outcome of comparing X and Y, RELATION is <, =, or >."))
  ((compare ?op ?x ?y)
   (lisp-value ?signum (prolog-compare '?x '?y) :dont-invoke)
   (rules (?signum ?op) ((-1 <)) ((0 =)) ((+1 >)))))

(define-predicate standard-order
  (:options (:argument-list (x y))
	    (:documentation "succeeds unless Y is before X in the standard order"))
  ((standard-order ?x ?y)
   (lisp-value ?signum (prolog-compare '?x '?y) :dont-invoke)
   (< ?signum +1)))

(define-predicate quantified-bag-of
  (:options (:argument-list (bag +outer term &rest predications))
	    (:documentation
	      "For some values of the variables in OUTER, this unifies BAG 
with instantiations of TERM corresponding to solutions of PREDICATIONS.")
	    (:deterministic :sometimes)))

(define-predicate quantified-set-of
  (:options (:argument-list (set +outer term &rest predications))
	    (:documentation
	      "For some values of the variables in OUTER, this unifies SET
with sorted, unique instantiations of TERM such that PREDICATIONS hold.")
	    (:deterministic :sometimes)))

;;I am too lazy to write intrinsics for these, therefore we redefine the provers:

(defun #.(prolog-db-symbol 'quantified-bag-of ':compatibility 'prover)
       (continuation bag outer term &rest predications)
  (let ((mark *trail*) collection)
    (prove-conjunction-if-need-be
      predications
      (faked-continuation (funcall #'collection-insert 'nil (locf collection) outer term)))
    (untrail mark)
    (collection-traverse
      mark continuation (%dereference bag) (%dereference outer) collection)))

(defun #.(prolog-db-symbol 'quantified-set-of ':compatibility 'prover)
       (continuation set outer term &rest predications)
  (let ((mark *trail*) collection)
    (prove-conjunction-if-need-be
      predications
      (faked-continuation (funcall #'collection-insert 't (locf collection) outer term)))
    (untrail mark)
    (collection-traverse
      mark continuation (%dereference set) (%dereference outer) collection)))

;;We will build a data structure
;;  ((,outer-instance ,term-instance ,term-instance ...)
;;   (,outer-instance ,term-instance ,term-instance ...)
;;   ...)
;;sorted on increasing CARs. Each CDR is either the reverse list of solutions (for bags)
;;or unique solutions in decreasing order (for sets).
(defun collection-insert (set-p location outer term)
  (collection-insert-1 set-p location
		       (copy-term (%dereference outer))
		       (copy-term (%dereference term)))
  nil);;force it to backtrack

(deffun collection-insert-1 (set-p location outer term)
  (cond ((null (contents location))
	 (setf (contents location)
	       (prolog-list* (prolog-list* outer term nil) nil)))
	(t (let ((signum (prolog-compare outer (caar (contents location)))))
	     (selectq signum
	       (-1 (push-in-area *prolog-work-area*
				 (prolog-list* outer term nil)
				 (contents location)))
	       (0  (cond (set-p (collection-insert-term
				 (locf (cdar (contents location))) term))
			 (t (push-in-area *prolog-work-area*
					  term
					  (cdar (contents location))))))
	       (+1 (collection-insert-1
		     set-p (locf (cdr (contents location))) outer term)))))))

(deffun collection-insert-term (location term)
  (cond ((null (contents location))
	 (setf (contents location) (prolog-list* term nil)))
	(t (let ((signum (prolog-compare term (car (contents location)))))
	     (selectq signum
	       (-1 (collection-insert-term (locf (cdr (contents location))) term))
	       (0)
	       (+1 (push-in-area *prolog-work-area* term (contents location))))))))

(deffun collection-traverse (mark continuation bag outer collection)
  (cond (collection
	 (cond ((and (unify outer (caar collection))
		     (unify bag (nreverse (cdar collection)))
		     (invoke continuation)))
	       ((untrail mark))
	       ((collection-traverse mark continuation bag outer (cdr collection)))))))

;;Pretty slow versions:
;;(define-predicate quantified-bag-of
;;  ((quantified-bag-of ?bag ?outer ?term . ?predicates)
;;   (set-of ?all (?outer ?term) . ?predicates)
;;   (unique-member-first ?whats-left ?outer ?all)
;;   (bag-of ?bag ?x (member (?outer ?x) ?whats-left))))
;;
;;(define-predicate quantified-set-of
;;  ((quantified-set-of ?sorted-set ?outer ?term . ?predicates)
;;   (set-of ?all (?outer ?term) . ?predicates)
;;   (unique-member-first ?whats-left ?outer ?all)
;;   (set-of ?set ?x (member (?outer ?x) ?whats-left))
;;   (sort ?sorted-set ?set standard-order)))
;;
;;(define-predicate unique-member-first
;;  ((unique-member-first ?left ?x ?list)
;;   (unique-member-first ?left ?x ?list ()))
;;  ((unique-member-first ?left ?x ((?one ?two) . ?rest) ?seen-already)
;;   (or (and (= ?x ?one)
;;	    (cannot-prove (member ?x ?seen-already))
;;	    (= ?left ((?one ?two) . ?rest)))
;;       (unique-member-first ?left ?x ?rest (?one . ?seen-already)))))

(deffun prolog-compare (x y)
  (cond ((or (and (symbolp x) (symbolp y)) (and (stringp x) (stringp y)))
	 (signum (string-compare x y)))
	((and (consp x) (consp y))
	 (let ((signum (prolog-compare (car x) (car y))))
	   (selectq signum
	     (0 (prolog-compare (cdr x) (cdr y)))
	     (otherwise signum))))
	((and (numberp x) (numberp y))
	 (signum (- x y)))
	((and (value-cell-p x) (not (value-cell-p y)))
	 -1)
	((and (value-cell-p y) (not (value-cell-p x)))
	 +1)
	(t (let ((signum (signum (string-compare (typep x) (typep y)))))
	     (selectq signum
	       (0 (signum (%pointer-difference x y)))
	       (otherwise signum))))))
