;;  Copyright (c) Jeffrey Straszheim. All rights reserved.  The use and
;;  distribution terms for this software are covered by the Eclipse Public
;;  License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can
;;  be found in the file epl-v10.html at the root of this distribution.  By
;;  using this software in any fashion, you are agreeing to be bound by the
;;  terms of this license.  You must not remove this notice, or any other,
;;  from this software.
;;
;;  util.clj
;;
;;  A Clojure implementation of Datalog -- Utilities
;;
;;  straszheimjeffrey (gmail)
;;  Created 3 Feburary 2009


(ns clojure.contrib.datalog.util
  (:use [clojure.contrib.seq :only (separate)]))



;;; Bindings and logic vars.  A binding in a hash of logic vars to
;;; bound values.  Logic vars are any symbol prefixed with a \?.

(defn is-var?
  "Is this a logic variable: e.g. a symbol prefixed with a ?"
  [sym]
  (when (symbol? sym)
    (let [name (name sym)]
      (and (= \? (first name))
           (not= \? (fnext name))))))

(defn is-query-var?
  "Is this a query variable: e.g. a symbol prefixed with ??"
  [sym]
  (when (symbol? sym)
    (let [name (name sym)]
      (and (= \? (first name))
           (= \? (fnext name))))))

(defn map-values
  "Like map, but works over the values of a hash map"
  [f hash]
  (let [key-vals (map (fn [[key val]] [key (f val)]) hash)]
    (if (seq key-vals)
      (apply conj (empty hash) key-vals)
      hash)))

(defn keys-to-vals
  "Given a map and a collection of keys, return the collection of vals"
  [m ks]
  (vals (select-keys m ks)))

(defn reverse-map
  "Reverse the keys/values of a map"
  [m]
  (into {} (map (fn [[k v]] [v k]) m)))


;;; Preduce -- A parallel reduce over hashes
  
(defn preduce
  "Similar to merge-with, but the contents of each key are merged in
   parallel using f.

   f - a function of 2 arguments.
   data - a collection of hashes."
  [f data]
  (let [data-1 (map (fn [h] (map-values #(list %) h)) data)
        merged (doall (apply merge-with concat data-1))
        ; Groups w/ multiple elements are identified for parallel processing
        [complex simple] (separate (fn [[key vals]] (> (count vals) 1)) merged)
        fold-group (fn [[key vals]] {key (reduce f vals)})
        fix-single (fn [[key [val]]] [key val])]
    (apply merge (concat (pmap fold-group merged) (map fix-single simple)))))
  

;;; Debuging and Tracing

(def *trace-datalog* nil)

(defmacro trace-datalog
  "If *test-datalog* is set to true, run the enclosed commands"
  [& body]
  `(when *trace-datalog*
     ~@body))

 	
;; End of file
