Advertisement
logicmoo

JDatalkog

Aug 15th, 2018
574
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 77.79 KB | None | 0 0
  1. //.h file code:
  2.  
  3. #include "Exception.h"
  4. #include <string>
  5. #include <unordered_map>
  6. #include <unordered_set>
  7. #include <vector>
  8. #include <list>
  9. #include <algorithm>
  10. #include <iostream>
  11. #include <cmath>
  12. #include <cctype>
  13. #include <stdexcept>
  14. #include <any>
  15. #include <optional>
  16. #include <mutex>
  17. #include "exceptionhelper.h"
  18. #include "stringhelper.h"
  19. #include "stringbuilder.h"
  20.  
  21. //JAVA TO C++ CONVERTER NOTE: Forward class declarations:
  22. class Expr;
  23. class Rule;
  24. class DatalogException;
  25. class StringBuilder;
  26. class StackMap;
  27. class NumberFormatException;
  28. class ?;
  29. class ? extends V;
  30.  
  31. /*
  32. Java Datalog Engine with Stratified Negation
  33. Copyright 2016 Werner Stoop
  34. Licensed under the Apache License, Version 2.0 (the "License");
  35. you may not use this file except in compliance with the License.
  36. You may obtain a copy of the License at
  37.     http://www.apache.org/licenses/LICENSE-2.0
  38. Unless required by applicable law or agreed to in writing, software
  39. distributed under the License is distributed on an "AS IS" BASIS,
  40. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  41. See the License for the specific language governing permissions and
  42. limitations under the License.
  43. Notes, Features and Properties:
  44.  * It implements stratified negation, or Stratified Datalog~.
  45.  * It can parse and evaluate Datalog programs from files and Strings (actually anything that implements java.io.Reader).
  46.  * It has a fluent API through which it can be embedded in Java applications to run queries. See the main() method for
  47.     examples.
  48.  * It implements "=", "<>" (alternatively "!="), "<", "<=", ">" and ">=" as built-in predicates.
  49.  * It avoids third party dependencies because it is a proof-of-concept. It should be able to stand alone.
  50.  * Values with "quoted strings" are supported, but to prevent a value like "Hello World" from being confused with a
  51.     variable, it is stored internally with a " prefix, i.e. "Hello" is stored as `"Hello`.
  52.     This is why the toString(Map<String, String> bindings) method uses substring(1) on the term.
  53.  * Also, predicates can't be in quotes. Is this a desirable feature?
  54. 2016/04/26 - It now supports built-in predicates "=", "<>" (alternatively "!="), "<", "<=", ">" and ">=".
  55. 2016/04/19 - It now implements stratified negation, or Stratified Datalog~.
  56. 2016/04/12 - It now implements simple negation for non-recursive programs, or semipositive Datalog~ (my keyboard
  57.     doesn't have their "not" sign) as described in section 2.3.1 of [gree]. It won't be able to compute very
  58.     complex negations correctly.
  59. 2016/04/11 - Work-In-Progress: I'm busy implementing negation.
  60. 2016/04/08 - I've replaced the code that uses the Streams API with more old-fashioned code. The Streams version
  61.     worked properly, but my rough benchmarks show that the code is ever so slightly more efficient now.
  62. 2016/04/06 - It now implements "Semi-Naive Bottom-Up" evaluation.
  63. 2016/03/29 - Added a profiler
  64. 2016/03/24 - Implemented a parser - the original version only had the fluent API.
  65. 2016/03/23 - The original version was "Naive Bottom-Up"
  66. Compile like so:
  67.     > javac JDatalog.java
  68. Run like so:
  69.     > java JDatalog test.dl
  70. References:
  71.   [wiki]  Wikipedia topic, http://en.wikipedia.org/wiki/Datalog
  72.   [elma]  Fundamentals of Database Systems (3rd Edition); Ramez Elmasri, Shamkant Navathe
  73.   [ceri]  What You Always Wanted to Know About Datalog (And Never Dared to Ask); Stefano Ceri, Georg Gottlob, and Letizia Tanca
  74.   [bra1]  Deductive Databases and Logic Programming; Stefan Brass, Univ. Halle, 2009
  75.             http://dbs.informatik.uni-halle.de/Lehre/LP09/c6_botup.pdf
  76.   [banc]  An Amateur’s Introduction to Recursive Query Processing Strategies; Francois Bancilhon, Raghu Ramakrishnan
  77.   [mixu]  mixu/datalog.js; Mikito Takada, https://github.com/mixu/datalog.js
  78.   [kett]  bottom-up-datalog-js; Frederic Kettelhoit http://fkettelhoit.github.io/bottom-up-datalog-js/docs/dl.html
  79.   [davi]  Inference in Datalog; Ernest Davis, http://cs.nyu.edu/faculty/davise/ai/datalog.html
  80.   [gree]  Datalog and Recursive Query Processing; Todd J. Green, Shan Shan Huang, Boon Thau Loo and Wenchao Zhou
  81.             Foundations and Trends in Databases Vol. 5, No. 2 (2012) 105–195, 2013
  82.             http://blogs.evergreen.edu/sosw/files/2014/04/Green-Vol5-DBS-017.pdf
  83.   [bra2]  Bottom-Up Query Evaluation in Extended Deductive Databases, Stefan Brass, 1996
  84.             https://www.deutsche-digitale-bibliothek.de/binary/4ENXEC32EMXHKP7IRB6OKPBWSGJV5JMJ/full/1.pdf
  85.   [sund]  Datalog Evaluation Algorithms, Dr. Raj Sunderraman, 1998
  86.             http://tinman.cs.gsu.edu/~raj/8710/f98/alg.html
  87.   [ull1]  Lecture notes: Datalog Rules Programs Negation; Jeffrey D. Ullman;
  88.             http://infolab.stanford.edu/~ullman/cs345notes/cs345-1.ppt
  89.   [ull2]  Lecture notes: Datalog Logical Rules Recursion; Jeffrey D. Ullman;
  90.             http://infolab.stanford.edu/~ullman/dscb/pslides/dlog.ppt
  91.   [meye]  Prolog in Python, Chris Meyers, http://www.openbookproject.net/py4fun/prolog/intro.html
  92.   [alec]  G53RDB Theory of Relational Databases Lecture 14; Natasha Alechina;
  93.             http://www.cs.nott.ac.uk/~psznza/G53RDB07/rdb14.pdf
  94.   [rack]  Datalog: Deductive Database Programming, Jay McCarthy, https://docs.racket-lang.org/datalog/
  95.             (Datalog library for the Racket language)
  96. ---------------------------------
  97. [davi] explains how using top-down evaluation might lead to infinite loops, but says that it is sufficient to prevent this by failing
  98. the test if you encounter a goal that you've already encountered in the stack. A very early version of this module basically ported [mixu]'s
  99. top-down evaluator to Java, but encountered this precise problem (and solved it in the way described).
  100. ---------------------------------
  101. I initially just assumed that using the StackMap would be faster, so I tried an implementation with a HashMap where I just did a
  102. newMap.putAll(parent) and removed the StackMap entirely. My rough benchmarks showed the StackMap-based implementeation to be about 30%
  103. faster than the alternative.
  104. ---------------------------------
  105. It occurred to me that if you /really/ want the equivalent of JDBC *prepared statements* then you can pass pass in a Map<String, String> containing
  106. the bound variables. This map will then be sent all the way down to `matchRule()` where it can be passed as the `bindings` parameter in the call
  107. to `matchGoals()` - so the map will end up at the bottom of the StackMap stack.
  108. This will allow you to use statements like for example `jDatalog.query(new Expr("foo","X", "Y"), binding)`, with `binding = {X : "bar"}`, in the
  109. fluent API to perform bulk inserts or queries and so on.
  110. A problem is that the varargs ... operator must come last in the method declaration, but I can work around this by having a method that only accepts
  111. List<Expr> as an argument rather than varargs.
  112. You can then create a method `prepareStatement()` that can return a List<Expr> from a parsed query.
  113. Actually, the List<Expr> should be wrapped in a JStatement (or something) interface so that you can run insert rules, insert facts and delete facts
  114. through these "prepared statements".
  115. ---------------------------------
  116. I've decided that the built-in predicates are to be written as `new Expr("!=", "X", "Y")` for `X != Y` in the fluent API. I've considered
  117. special static methods and Builder objects, but none really appealed to me.
  118. In a future I might consider `Expr.eq("X", "Y")`, `Expr.ne("X", "Y")`, `Expr.gt("X", "Y")`, `Expr.lt("X", "Y")`, `Expr.ge("X", "Y")` and
  119. `Expr.le("X", "Y")`.
  120. ---------------------------------
  121. There are opportunities to run some of the methods in parallel using the Java 8 Streams API (I'm thinking of the calls to expandStrata() in
  122. buildDatabase() and the calls to matchRule() in expandStrata() in particular). The biggest obstacle at the moment is that the Expr#evalBuiltIn()
  123. throws a DatalogException, and DatalogException is necessarily checked because it is used for proper reporting of user errors.
  124. It seems the most practical solution is to wrap the DatalogException in a RuntimeException and then unwrap it at a higher level.
  125. See http://stackoverflow.com/a/27648758/115589
  126. ---------------------------------
  127. The Racket language has a Datalog module as part of its library [rack]. I've looked at its API for ideas for my own. They use the syntax
  128. `<clause>~` for a retraction, e.g parent(bob, john)~, which is a syntax I might want to adopt. The [rack] implementation lacks some of the
  129. features of my version, like negation and queries with multiple goals.
  130. ---------------------------------
  131. It is conceptually possible to make the `List<String> terms` of Expr a `List<Object>` instead, so that you can store complex Java objects in the
  132. database (as POJOs). The `isVariable()` method will just have to be modified to check whether its parameter is instanceof String and starts with
  133. an uppercase character, the bindings will become a Map<String, Object>, the result of `query()` will be a List<Map<String, Object>> and a
  134. couple of toString() methods will have to be modified. It won't be that useful a feature if you just use the parser, but it could be a "nice to
  135. have" if you use the fluent API. I don't intend to implement it at the moment, though.
  136. ---------------------------------
  137. I've decided against arithmetic built-in predicates, such as plus(X,Y,Z) => X + Y = Z, for now:
  138.   - Arithmetic predicates aren't that simple. They should be evaluated as soon as the input variables (X and Y) in this case becomes available,
  139.     so that Z can be computed and bound for the remaining goals.
  140.   - Arithmetic expressions would require a more complex parser and there would be a need for Expr to have child Expr objects to build a parse tree.
  141.     The parse tree would be simpler if the `terms` of Expr was a List<Object> - see my note above.
  142. */
  143.  
  144.  
  145.  
  146. class JDatalog
  147. {
  148.  
  149. private:
  150.     std::vector<Expr*> edb; // Facts
  151.     Collection<Rule*>* idb; // Rules
  152.  
  153.     static bool debugEnable;
  154.  
  155. public:
  156.     virtual ~JDatalog()
  157.     {
  158.         delete idb;
  159.     }
  160.  
  161.     JDatalog();
  162.  
  163. public:
  164.     class Expr
  165.     {
  166.  
  167.     private:
  168.         std::string predicate;
  169.         std::vector<std::string> terms;
  170.  
  171.     protected:
  172.         bool negated = false;
  173.  
  174.     public:
  175.         Expr(const std::string& predicate, std::vector<std::string>& terms);
  176.  
  177.         Expr(const std::string& predicate, std::vector<std::string> &terms);
  178.  
  179.         virtual int arity();
  180.  
  181.         virtual bool isGround();
  182.  
  183.         virtual bool isNegated();
  184.  
  185.         virtual bool isBuiltIn();
  186.  
  187.         static Expr* not(const std::string& predicate, std::vector<std::string> &terms);
  188.  
  189.         virtual bool unify(Expr* that, std::unordered_map<std::string, std::string>& bindings);
  190.  
  191.         virtual Expr* substitute(std::unordered_map<std::string, std::string>& bindings);
  192.  
  193.         virtual bool evalBuiltIn(std::unordered_map<std::string, std::string>& bindings) throw(DatalogException);
  194.  
  195.         bool equals(std::any other) override;
  196.  
  197.         int hashCode() override;
  198.  
  199.         std::string toString() override;
  200.  
  201.     private:
  202.         static StringBuilder* termToString(StringBuilder* sb, const std::string& term);
  203.  
  204.     };
  205.  
  206.     /* Reorganize the goals in a query so that negated literals are at the end.
  207.     A rule such as `a(X) :- not b(X), c(X)` won't work if the `not b(X)` is evaluated first, since X will not
  208.     be bound to anything yet, meaning there are an infinite number of values for X that satisfy `not b(X)`.
  209.     Reorganising the goals will solve the problem: every variable in the negative literals will have a binding
  210.     by the time they are evaluated if the rule is /safe/, which we assume they are - see Rule#validate()
  211.     Also, the built-in predicates (except `=`) should only be evaluated after their variables have been bound
  212.     for the same reason; see [ceri] for more information. */
  213. private:
  214.     static std::vector<Expr*> reorderQuery(std::vector<Expr*>& query);
  215.  
  216. public:
  217.     class Rule
  218.     {
  219.  
  220.     public:
  221.         Expr* head;
  222.         std::vector<Expr*> body;
  223.  
  224.         virtual ~Rule()
  225.         {
  226.             delete head;
  227.         }
  228.  
  229.         Rule(Expr* head, std::vector<Expr*>& body);
  230.  
  231.         Rule(Expr* head, std::vector<Expr> &body);
  232.  
  233.         virtual void validate() throw(DatalogException);
  234.  
  235.         std::string toString() override;
  236.     };
  237.  
  238. private:
  239.     static bool isVariable(const std::string& term);
  240.  
  241.     // Methods for the fluent interface
  242. public:
  243.     virtual JDatalog* rule(Expr* head, std::vector<Expr> &body) throw(DatalogException);
  244.     virtual JDatalog* rule(Rule* newRule) throw(DatalogException);
  245.  
  246.     virtual JDatalog* fact(const std::string& predicate, std::vector<std::string> &terms) throw(DatalogException);
  247.     virtual JDatalog* fact(Expr* newFact) throw(DatalogException);
  248.  
  249.     /* If you're executing a file that may contain multiple queries, you can pass
  250.     #execute(Reader, QueryOutput) a QueryOutput object that will be used to display
  251.     all the results from the separate queries, with their goals.
  252.     Otherwise #execute(Reader, QueryOutput) will just return the answers from the
  253.     last query. */
  254. public:
  255.     class QueryOutput
  256.     {
  257.     public:
  258.         virtual void writeResult(std::vector<Expr*>& goals, Collection<std::unordered_map<std::string, std::string> >* answers) = 0;
  259.     };
  260.  
  261.     /* Default implementation of QueryOutput */
  262. public:
  263.     class StandardQueryOutput : public QueryOutput
  264.     {
  265.     public:
  266.         void writeResult(std::vector<Expr*>& goals, Collection<std::unordered_map<std::string, std::string> >* answers) override override;
  267.     };
  268.  
  269.     /* Executes all the statements in a file/string/whatever the Reader is wrapping */
  270. public:
  271.     virtual Collection<std::unordered_map<std::string, std::string> >* execute(Reader* reader, QueryOutput* output) throw(DatalogException);
  272.  
  273. private:
  274.     Collection<std::unordered_map<std::string, std::string> >* parseStmt(StreamTokenizer* scan, std::vector<Expr*>& goals) throw(DatalogException);
  275.  
  276.     static Expr* parseExpr(StreamTokenizer* scan) throw(DatalogException);
  277.  
  278.     static const std::vector<std::string> validOperators;
  279.  
  280.     static Expr* parseBuiltInPredicate(const std::string& lhs, StreamTokenizer* scan) throw(DatalogException);
  281.  
  282.     static std::string numberToString(const double& nval);
  283.  
  284.     // Regex for tryParseDouble()
  285.     // There are several suggestions at http://stackoverflow.com/q/1102891/115589, but I chose to roll my own.
  286.     static Pattern* const  numberPattern;
  287.  
  288.     static bool tryParseDouble(const std::string& str);
  289.  
  290.     /* Normal Datalog exception. */
  291. public:
  292.     class DatalogException : public std::exception
  293.     {
  294.     public:
  295.         DatalogException(const std::string& message);
  296.         DatalogException(std::exception cause);
  297.         DatalogException(const std::string& message, std::exception cause);
  298.     };
  299.  
  300. public:
  301.     virtual Collection<std::unordered_map<std::string, std::string> >* query(const std::string& statement) throw(DatalogException);
  302.  
  303.     virtual Collection<std::unordered_map<std::string, std::string> >* query(std::vector<Expr> &goals) throw(DatalogException);
  304.  
  305.     virtual Collection<std::unordered_map<std::string, std::string> >* query(std::vector<Expr*>& goals) throw(DatalogException);
  306.  
  307.     /* Returns a list of rules that are relevant to the query.
  308.         If for example you're querying employment status, you don't care about family relationships, etc.
  309.         The advantages of this of this optimization becomes bigger the complexer the rules get. */
  310. private:
  311.     Collection<Rule*>* getRelevantRules(std::vector<Expr*>& originalGoals);
  312.  
  313.     /* This basically constructs the dependency graph for semi-naive evaluation: In the returned map, the string
  314.         is a predicate in the rules' heads that maps to a collection of all the rules that have that predicate in
  315.         their body so that we can easily find the rules that are affected when new facts are deduced in different
  316.         iterations of buildDatabase().
  317.         For example if you have a rule p(X) :- q(X) then there will be a mapping from "q" to that rule
  318.         so that when new facts q(Z) are deduced, the rule will be run in the next iteration to deduce p(Z) */
  319.     std::unordered_map<std::string, Collection<Rule*>*> buildDependantRules(Collection<Rule*>* rules);
  320.  
  321.     Collection<Rule*>* getDependantRules(Collection<Expr*>* facts, std::unordered_map<std::string, Collection<Rule*>*>& dependants);
  322.  
  323.     Collection<Expr*>* buildDatabase(Set<Expr*>* facts, Collection<Rule*>* allRules) throw(DatalogException);
  324.  
  325.     std::vector<Collection<Rule*>*> computeStratification(Collection<Rule*>* allRules) throw(DatalogException);
  326.  
  327.     int depthFirstSearch(Expr* goal, Collection<Rule*>* graph, std::vector<Expr*>& visited, const int& level) throw(DatalogException);
  328.  
  329.     Collection<Expr*>* expandStrata(Set<Expr*>* facts, Collection<Rule*>* strataRules) throw(DatalogException);
  330.  
  331.     Set<Expr*>* matchRule(Collection<Expr*>* const facts, Rule* rule) throw(DatalogException);
  332.  
  333.     Collection<std::unordered_map<std::string, std::string> >* matchGoals(std::vector<Expr*>& goals, Collection<Expr*>* const facts, std::unordered_map<std::string, std::string>& bindings) throw(DatalogException);
  334.  
  335. public:
  336.     virtual bool delete_RenamedTODO(std::vector<Expr> &facts) throw(DatalogException);
  337.  
  338.     /* Queries a set of goals and deletes all the facts that matches the query */
  339.     virtual bool delete_RenamedTODO(std::vector<Expr*>& goals) throw(DatalogException);
  340.  
  341.     virtual void validate() throw(DatalogException);
  342.  
  343.     virtual void dump(PrintStream* out) throw(DatalogException);
  344.  
  345.     static void debug(const std::string& message);
  346.  
  347.     template<typename T1>
  348.     static std::string toString(Collection<T1>* collection);
  349.  
  350.     static std::string toString(std::unordered_map<std::string, std::string>& bindings);
  351.  
  352.     // If you want to do benchmarking, run the file several times to get finer grained results.
  353. private:
  354.     static constexpr bool BENCHMARK = false;
  355.     static constexpr int NUM_RUNS = 1000;
  356.  
  357.     static void main(std::vector<std::string> &args);
  358.  
  359.     /* Map<> implementation that has a parent Map<> where it looks up a value if the value is not in `this`. Its behaviour is equivalent
  360.         to a HashMap that is passed a parent map in its constructor, except that it keeps a reference to the parent map rather than
  361.         copying it. It never modifies the parent map. (If the behaviour deviates from this, it is a bug and must be fixed)
  362.         Internally, it has two maps: `self` and `parent`. The `get()` method will look up a key in self and if it doesn't find it, looks
  363.         for it in `parent`. The `put()` method always adds values to `self`. The parent in turn may also be a StackMap, so some method
  364.         calls may be recursive. The `flatten()` method combines the map with its parents into a single one.
  365.         It is used by JDatalog for the scoped contexts where the variable bindings enter and leave scope frequently during the
  366.         recursive building of the database, but where the parent Map<> needs to be kept for subsequent recursions.
  367.         Methods like entrySet(), keySet() and values() are required by the Map<> interface that their returned collections be backed
  368.         by the Map<>. Therefore, their implementations here will flatten the map first. Once these methods are called StackMap just
  369.         becomes a wrapper around the internal HashMap, hence JDatalog avoids these methods internally.
  370.         The remove() method also flattens `this` to avoid modifying the parent while and the clear() method just sets parent to null
  371.         and clears `self` .
  372.         I've tried a version that extends AbstractMap, but it proved to be significantly slower.
  373.     */
  374. public:
  375.     template<typename K, typename V>
  376.     class StackMap : public std::unordered_map<K, V>
  377.     {
  378.     private:
  379.         std::unordered_map<K, V> self;
  380.         std::unordered_map<K, V> parent;
  381.  
  382.     public:
  383.         StackMap()
  384.         {
  385.             self = std::unordered_map<K, V>();
  386.             this->parent.clear();
  387.         }
  388.  
  389.         StackMap(std::unordered_map<K, V>& parent)
  390.         {
  391.             self = std::unordered_map<K, V>();
  392.             this->parent = parent;
  393.         }
  394.  
  395.         virtual std::unordered_map<K, V> flatten()
  396.         {
  397.             std::unordered_map<K, V> map;
  398.             // I don't use map.putAll(this) to avoid relying on entrySet()
  399.             if(parent.size() > 0)
  400.             {
  401.                 map.putAll(parent);
  402.             }
  403.             map.putAll(self);
  404.             return map;
  405.         }
  406.  
  407.         std::string toString() override
  408.         {
  409.             StringBuilder* sb = new StringBuilder("{");
  410.             Set<K>* keys = std::unordered_set<K>(self.keySet());
  411.             keys->addAll(parent.keySet());
  412.             int s = keys->size(), i = 0;
  413.             for(auto k : keys)
  414.             {
  415.                 sb->append(k)->append(": ");
  416.                 sb->append(get(k));
  417.                 if(++i < s)
  418.                 {
  419.                     sb->append(", ");
  420.                 }
  421.             }
  422.             sb->append("}");
  423.             return sb->toString();
  424.         }
  425.  
  426.         V put(K key, V value) override
  427.         {
  428.             return self.emplace(key, value);
  429.         }
  430.  
  431.         bool containsKey(std::any key) override
  432.         {
  433.             if(self.find(key) != self.end())
  434.             {
  435.                 return true;
  436.             }
  437.             if(parent.size() > 0)
  438.             {
  439.                 return parent.find(key) != parent.end();
  440.             }
  441.             return false;
  442.         }
  443.  
  444.         V get(std::any key) override
  445.         {
  446.             V value = self[key];
  447.             if(value != nullptr)
  448.             {
  449.                 return value;
  450.             }
  451.             if(parent.size() > 0)
  452.             {
  453.                 return parent[key];
  454.             }
  455.             return nullptr;
  456.         }
  457.  
  458.         Set<std::unordered_map::Entry<K, V>*>* entrySet() override
  459.         {
  460.             if(parent.size() > 0)
  461.             {
  462.                 self = flatten(); // caveat emptor
  463.                 parent.clear();
  464.             }
  465.             return self.entrySet();
  466.         }
  467.  
  468.         int size() override
  469.         {
  470.             int s = self.size();
  471.             if(parent.size() > 0)
  472.             {
  473.                 // Work around situations where self contains a `key` that's already in `parent`.
  474.                 // These situations shouldn't occur in JDatalog, though
  475.                 for(auto k : parent)
  476.                 {
  477.                     if(self.find(k.first) == self.end())
  478.                     {
  479.                         s++;
  480.                     }
  481.                 }
  482.             }
  483.             return s;
  484.         }
  485.  
  486.         void clear() override
  487.         {
  488.             // We don't want to modify the parent, so we just orphan this
  489.             parent.clear();
  490.             self.clear();
  491.         }
  492.  
  493.         bool equals(std::any o) override
  494.         {
  495.             if(o == nullptr || !(dynamic_cast<std::unordered_map>(o) != nullptr))
  496.             {
  497.                 return false;
  498.             }
  499.             std::unordered_map that = std::any_cast<std::unordered_map>(o);
  500.             return entrySet()->equals(that.entrySet());
  501.         }
  502.         int hashCode() override
  503.         {
  504.             int h = 0;
  505.             for(auto entry : entrySet())
  506.             {
  507.                 h += entry.hashCode();
  508.             }
  509.             return h;
  510.         }
  511.         Set<K>* keySet() override
  512.         {
  513.             if(parent.size() > 0)
  514.             {
  515.                 self = flatten(); // caveat emptor
  516.                 parent.clear();
  517.             }
  518.             return self.keySet();
  519.         }
  520.         Collection<V>* values() override
  521.         {
  522.             if(parent.size() > 0)
  523.             {
  524.                 self = flatten(); // caveat emptor
  525.                 parent.clear();
  526.             }
  527.             return self.values();
  528.         }
  529.         template<typename T1, typename T1>
  530. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ template equivalent to this generic constraint:
  531. //ORIGINAL LINE: @Override public void putAll(java.util.Map<? extends K,? extends V> m)
  532.         void putAll(std::unordered_map<T1> m)
  533.         {
  534.             self.putAll(m);
  535.         }
  536.         V remove(std::any key) override
  537.         {
  538.             if(parent.size() > 0)
  539.             {
  540.                 self = flatten(); // caveat emptor
  541.                 parent.clear();
  542.             }
  543.             return self.erase(key);
  544.         }
  545.         bool containsValue(std::any value) override
  546.         {
  547.             return self.containsValue(value) || (parent.size() > 0 && parent.containsValue(value));
  548.         }
  549.         bool isEmpty() override
  550.         {
  551.             if(self.empty())
  552.             {
  553.                 if(parent.size() > 0)
  554.                 {
  555.                     return parent.empty();
  556.                 } else
  557.                 {
  558.                     return true;
  559.                 }
  560.             }
  561.             return false;
  562.         }
  563.     };
  564.  
  565.     /* Profiling class.
  566.     I had to write my own because I didn't want to pull in any external dependencies.
  567.     `buckets` maps the name of a bucket to a List of elapsed times so that you can have
  568.     multiple timers under different names.
  569.     */
  570. public:
  571.     class Profiler
  572.     {
  573.  
  574.         // FYI: Java 8 actually has Instant and Duration classes.
  575.         // Not sure whether they're really useful here, though.
  576.  
  577.     public:
  578.         class Timer
  579.         {
  580.         private:
  581.             long long start = 0;
  582.             std::vector<long long> bucket;
  583.  
  584.         public:
  585.             Timer(std::vector<long long>& bucket);
  586.  
  587.             virtual long long stop();
  588.         };
  589.  
  590.     private:
  591.         std::unordered_map<std::string, std::vector<long long> > buckets;
  592.  
  593.         static Profiler* instance;
  594.  
  595.         Profiler();
  596.  
  597.     public:
  598.         static Profiler* getInstance();
  599.  
  600.         static void reset();
  601.  
  602.         static Timer* getTimer(const std::string& name);
  603.  
  604.         virtual std::vector<long long> getBucket(const std::string& name);
  605.  
  606.         static double average(const std::string& name);
  607.  
  608.         static long long total(const std::string& name);
  609.  
  610.         static int count(const std::string& name);
  611.  
  612.         static double stdDev(const std::string& name);
  613.  
  614.         static Set<std::string>* keySet();
  615.     };
  616. };
  617.  
  618. //stringhelper.h:
  619.  
  620. //----------------------------------------------------------------------------------------
  621. //  Copyright © 2007 - 2018 Tangible Software Solutions Inc.
  622. //  This class can be used by anyone provided that the copyright notice remains intact.
  623. //
  624. //  This class is used to replace some string methods, including
  625. //  conversions to or from strings.
  626. //----------------------------------------------------------------------------------------
  627. #include <string>
  628. #include <sstream>
  629. #include <vector>
  630. #include <exception>
  631. #include <cctype>
  632.  
  633. class StringHelper
  634. {
  635. public:
  636.     static std::string toLower(std::string source)
  637.     {
  638.         std::transform(source.begin(), source.end(), source.begin(), std::tolower);
  639.         return source;
  640.     }
  641.  
  642.     static std::string toUpper(std::string source)
  643.     {
  644.         std::transform(source.begin(), source.end(), source.begin(), std::toupper);
  645.         return source;
  646.     }
  647.  
  648.     static std::string trimStart(std::string source, const std::string &trimChars = " \t\n\r\v\f")
  649.     {
  650.         return source.erase(0, source.find_first_not_of(trimChars));
  651.     }
  652.  
  653.     static std::string trimEnd(std::string source, const std::string &trimChars = " \t\n\r\v\f")
  654.     {
  655.         return source.erase(source.find_last_not_of(trimChars) + 1);
  656.     }
  657.  
  658.     static std::string trim(std::string source, const std::string &trimChars = " \t\n\r\v\f")
  659.     {
  660.         return trimStart(trimEnd(source, trimChars), trimChars);
  661.     }
  662.  
  663.     static std::string replace(std::string source, const std::string &find, const std::string &replace)
  664.     {
  665.         size_t pos = 0;
  666.         while ((pos = source.find(find, pos)) != std::string::npos)
  667.         {
  668.             source.replace(pos, find.length(), replace);
  669.             pos += replace.length();
  670.         }
  671.         return source;
  672.     }
  673.  
  674.     static bool startsWith(const std::string &source, const std::string &value)
  675.     {
  676.         if (source.length() < value.length())
  677.             return false;
  678.         else
  679.             return source.compare(0, value.length(), value) == 0;
  680.     }
  681.  
  682.     static bool endsWith(const std::string &source, const std::string &value)
  683.     {
  684.         if (source.length() < value.length())
  685.             return false;
  686.         else
  687.             return source.compare(source.length() - value.length(), value.length(), value) == 0;
  688.     }
  689.  
  690.     static std::vector<std::string> split(const std::string &source, char delimiter)
  691.     {
  692.         std::vector<std::string> output;
  693.         std::istringstream ss(source);
  694.         std::string nextItem;
  695.  
  696.         while (std::getline(ss, nextItem, delimiter))
  697.         {
  698.             output.push_back(nextItem);
  699.         }
  700.  
  701.         return output;
  702.     }
  703.  
  704.     template<typename T>
  705.     static std::string toString(const T &subject)
  706.     {
  707.         std::ostringstream ss;
  708.         ss << subject;
  709.         return ss.str();
  710.     }
  711.  
  712.     template<typename T>
  713.     static T fromString(const std::string &subject)
  714.     {
  715.         std::istringstream ss(subject);
  716.         T target;
  717.         ss >> target;
  718.         return target;
  719.     }
  720.  
  721.     template<typename T>
  722.     static std::string formatSimple(const std::string &input, T arg1)
  723.     {
  724.         std::ostringstream ss;
  725.         int lastFormatChar = -1;
  726.         int percent = -1;
  727.         while ((percent = input.find('%', percent + 1)) > -1)
  728.         {
  729.             if (percent + 1 < input.length())
  730.             {
  731.                 if (input[percent + 1] == '%')
  732.                 {
  733.                     percent++;
  734.                     continue;
  735.                 }
  736.  
  737.                 int formatEnd = -1;
  738.                 std::string index;
  739.                 for (int i = percent + 1; i < input.length(); i++)
  740.                 {
  741.                     if (input[i] == 's')
  742.                     {
  743.                         index = "1";
  744.                         formatEnd = i;
  745.                         break;
  746.                     }
  747.                     else if (input[i] == '$' && i + 1 < input.length() && input[i + 1] == 's')
  748.                     {
  749.                         index = input.substr(percent + 1, i - percent - 1);
  750.                         formatEnd = i + 1;
  751.                         break;
  752.                     }
  753.                     else if (!std::isdigit(input[i]))
  754.                         break;                 
  755.                 }
  756.  
  757.                 if (formatEnd > -1)
  758.                 {
  759.                     ss << input.substr(lastFormatChar + 1, percent - lastFormatChar - 1);
  760.                     lastFormatChar = formatEnd;
  761.  
  762.                     if (index == "1")
  763.                         ss << arg1;
  764.                     else
  765.                         throw std::exception("Only simple positional format specifiers are handled by the 'formatSimple' helper method.");
  766.                 }
  767.             }
  768.         }
  769.  
  770.         if (lastFormatChar + 1 < input.length())
  771.             ss << input.substr(lastFormatChar + 1);
  772.  
  773.         return ss.str();
  774.     }
  775.  
  776.     template<typename T1, typename T2>
  777.     static std::string formatSimple(const std::string &input, T1 arg1, T2 arg2)
  778.     {
  779.         std::ostringstream ss;
  780.         int lastFormatChar = -1;
  781.         int percent = -1;
  782.         while ((percent = input.find('%', percent + 1)) > -1)
  783.         {
  784.             if (percent + 1 < input.length())
  785.             {
  786.                 if (input[percent + 1] == '%')
  787.                 {
  788.                     percent++;
  789.                     continue;
  790.                 }
  791.  
  792.                 int formatEnd = -1;
  793.                 std::string index;
  794.                 for (int i = percent + 1; i < input.length(); i++)
  795.                 {
  796.                     if (input[i] == 's')
  797.                     {
  798.                         index = "1";
  799.                         formatEnd = i;
  800.                         break;
  801.                     }
  802.                     else if (input[i] == '$' && i + 1 < input.length() && input[i + 1] == 's')
  803.                     {
  804.                         index = input.substr(percent + 1, i - percent - 1);
  805.                         formatEnd = i + 1;
  806.                         break;
  807.                     }
  808.                     else if (!std::isdigit(input[i]))
  809.                         break;                 
  810.                 }
  811.  
  812.                 if (formatEnd > -1)
  813.                 {
  814.                     ss << input.substr(lastFormatChar + 1, percent - lastFormatChar - 1);
  815.                     lastFormatChar = formatEnd;
  816.  
  817.                     if (index == "1")
  818.                         ss << arg1;
  819.                     else if (index == "2")
  820.                         ss << arg2;
  821.                     else
  822.                         throw std::exception("Only simple positional format specifiers are handled by the 'formatSimple' helper method.");
  823.                 }
  824.             }
  825.         }
  826.  
  827.         if (lastFormatChar + 1 < input.length())
  828.             ss << input.substr(lastFormatChar + 1);
  829.  
  830.         return ss.str();
  831.     }
  832.  
  833.     template<typename T1, typename T2, typename T3>
  834.     static std::string formatSimple(const std::string &input, T1 arg1, T2 arg2, T3 arg3)
  835.     {
  836.         std::ostringstream ss;
  837.         int lastFormatChar = -1;
  838.         int percent = -1;
  839.         while ((percent = input.find('%', percent + 1)) > -1)
  840.         {
  841.             if (percent + 1 < input.length())
  842.             {
  843.                 if (input[percent + 1] == '%')
  844.                 {
  845.                     percent++;
  846.                     continue;
  847.                 }
  848.  
  849.                 int formatEnd = -1;
  850.                 std::string index;
  851.                 for (int i = percent + 1; i < input.length(); i++)
  852.                 {
  853.                     if (input[i] == 's')
  854.                     {
  855.                         index = "1";
  856.                         formatEnd = i;
  857.                         break;
  858.                     }
  859.                     else if (input[i] == '$' && i + 1 < input.length() && input[i + 1] == 's')
  860.                     {
  861.                         index = input.substr(percent + 1, i - percent - 1);
  862.                         formatEnd = i + 1;
  863.                         break;
  864.                     }
  865.                     else if (!std::isdigit(input[i]))
  866.                         break;                 
  867.                 }
  868.  
  869.                 if (formatEnd > -1)
  870.                 {
  871.                     ss << input.substr(lastFormatChar + 1, percent - lastFormatChar - 1);
  872.                     lastFormatChar = formatEnd;
  873.  
  874.                     if (index == "1")
  875.                         ss << arg1;
  876.                     else if (index == "2")
  877.                         ss << arg2;
  878.                     else if (index == "3")
  879.                         ss << arg3;
  880.                     else
  881.                         throw std::exception("Only simple positional format specifiers are handled by the 'formatSimple' helper method.");
  882.                 }
  883.             }
  884.         }
  885.  
  886.         if (lastFormatChar + 1 < input.length())
  887.             ss << input.substr(lastFormatChar + 1);
  888.  
  889.         return ss.str();
  890.     }
  891. };
  892.  
  893. //stringbuilder.h:
  894.  
  895. //----------------------------------------------------------------------------------------
  896. //  Copyright © 2007 - 2018 Tangible Software Solutions Inc.
  897. //  This class can be used by anyone provided that the copyright notice remains intact.
  898. //
  899. //  This class is used to replace the Java StringBuilder in native C++.
  900. //----------------------------------------------------------------------------------------
  901. #include <string>
  902. #include <sstream>
  903.  
  904. class StringBuilder
  905. {
  906. private:
  907.     std::string privateString;
  908.  
  909. public:
  910.     StringBuilder()
  911.     {
  912.     }
  913.  
  914.     StringBuilder(const std::string &initialString)
  915.     {
  916.         privateString = initialString;
  917.     }
  918.  
  919.     StringBuilder(std::size_t capacity)
  920.     {
  921.         ensureCapacity(capacity);
  922.     }
  923.  
  924.     char charAt(std::size_t index)
  925.     {
  926.         return privateString[index];
  927.     }
  928.  
  929.     StringBuilder *append(const std::string &toAppend)
  930.     {
  931.         privateString += toAppend;
  932.         return this;
  933.     }
  934.  
  935.     template<typename T>
  936.     StringBuilder *append(const T &toAppend)
  937.     {
  938.         privateString += toString(toAppend);
  939.         return this;
  940.     }
  941.  
  942.     StringBuilder *insert(std::size_t position, const std::string &toInsert)
  943.     {
  944.         privateString.insert(position, toInsert);
  945.         return this;
  946.     }
  947.  
  948.     template<typename T>
  949.     StringBuilder *insert(std::size_t position, const T &toInsert)
  950.     {
  951.         privateString.insert(position, toString(toInsert));
  952.         return this;
  953.     }
  954.  
  955.     std::string toString()
  956.     {
  957.         return privateString;
  958.     }
  959.  
  960.     std::size_t length()
  961.     {
  962.         return privateString.length();
  963.     }
  964.  
  965.     void setLength(std::size_t newLength)
  966.     {
  967.         privateString.resize(newLength);
  968.     }
  969.  
  970.     std::size_t capacity()
  971.     {
  972.         return privateString.capacity();
  973.     }
  974.  
  975.     void ensureCapacity(std::size_t minimumCapacity)
  976.     {
  977.         privateString.reserve(minimumCapacity);
  978.     }
  979.  
  980.     StringBuilder *remove(std::size_t start, std::size_t end)
  981.     {
  982.         privateString.erase(start, end - start);
  983.         return this;
  984.     }
  985.  
  986.     StringBuilder *replace(std::size_t start, std::size_t end, const std::string &newString)
  987.     {
  988.         privateString.replace(start, end - start, newString);
  989.         return this;
  990.     }
  991.  
  992. private:
  993.     template<typename T>
  994.     static std::string toString(const T &subject)
  995.     {
  996.         std::ostringstream ss;
  997.         ss << subject;
  998.         return ss.str();
  999.     }
  1000. };
  1001.  
  1002. //exceptionhelper.h:
  1003.  
  1004. #include <exception>
  1005.  
  1006. class NumberFormatException : public std::exception
  1007. {
  1008. private:
  1009.     std::string msg;
  1010.  
  1011. public:
  1012.     NumberFormatException(const std::string& message = "") : msg(message)
  1013.     {
  1014.     }
  1015.  
  1016.     const char * what() const throw()
  1017.     {
  1018.         return msg.c_str();
  1019.     }
  1020. };
  1021.  
  1022. class IOException : public std::exception
  1023. {
  1024. private:
  1025.     std::string msg;
  1026.  
  1027. public:
  1028.     IOException(const std::string& message = "") : msg(message)
  1029.     {
  1030.     }
  1031.  
  1032.     const char * what() const throw()
  1033.     {
  1034.         return msg.c_str();
  1035.     }
  1036. };
  1037.  
  1038. //.cpp file code:
  1039.  
  1040. using namespace std;
  1041.  
  1042. #include "StringBuilder.h"
  1043. #include "StackMap.h"
  1044. #include "NumberFormatException.h"
  1045. #include "?.h"
  1046. #include "? extends V.h"
  1047.  
  1048. bool JDatalog::debugEnable = false;
  1049.  
  1050. JDatalog::JDatalog()
  1051. {
  1052.     this->edb = vector<>();
  1053.     this->idb = vector<>();
  1054. }
  1055.  
  1056. JDatalog::Expr::Expr(const wstring& predicate, vector<wstring>& terms)
  1057. {
  1058.     this->predicate = predicate;
  1059.     // I've seen both versions of the symbol for not equals being used, so I allow
  1060.     // both, but we convert to "<>" internally to simplify matters later.
  1061.     if(this->predicate == "!=")
  1062.     {
  1063.         this->predicate = "<>";
  1064.     }
  1065.     this->terms = terms;
  1066. }
  1067.  
  1068. JDatalog::Expr::Expr(const wstring& predicate, vector<wstring> &terms) : Expr(predicate, Arrays::asList(terms))
  1069. {
  1070. }
  1071.  
  1072. int JDatalog::Expr::arity()
  1073. {
  1074.     return terms.size();
  1075. }
  1076.  
  1077. bool JDatalog::Expr::isGround()
  1078. {
  1079.     for(auto term : terms)
  1080.     {
  1081.         if(isVariable(term))
  1082.         {
  1083.             return false;
  1084.         }
  1085.     }
  1086.     return true;
  1087. }
  1088.  
  1089. bool JDatalog::Expr::isNegated()
  1090. {
  1091.     return negated;
  1092. }
  1093.  
  1094. bool JDatalog::Expr::isBuiltIn()
  1095. {
  1096.     char op = predicate[0];
  1097.     return !isalnum(op) && op != '\"';
  1098. }
  1099.  
  1100. Expr* JDatalog::Expr::not(const wstring& predicate, vector<wstring> &terms)
  1101. {
  1102.     Expr* e = new Expr(predicate, terms);
  1103.     e->negated = true;
  1104.     return e;
  1105. }
  1106.  
  1107. bool JDatalog::Expr::unify(Expr* that, unordered_map<wstring, wstring>& bindings)
  1108. {
  1109.     if(this->predicate != that->predicate || this->arity() != that->arity())
  1110.     {
  1111.         return false;
  1112.     }
  1113.     for(int i = 0; i < this->arity(); i++)
  1114.     {
  1115.         wstring term1 = this->terms[i];
  1116.         wstring term2 = that->terms[i];
  1117.         if(isVariable(term1))
  1118.         {
  1119.             if(term1 != term2)
  1120.             {
  1121.                 if(bindings.find(term1) == bindings.end())
  1122.                 {
  1123.                     bindings.emplace(term1, term2);
  1124.                 } else if(bindings[term1] != term2)
  1125.                 {
  1126.                     return false;
  1127.                 }
  1128.             }
  1129.         } else if(isVariable(term2))
  1130.         {
  1131.             if(bindings.find(term2) == bindings.end())
  1132.             {
  1133.                 bindings.emplace(term2, term1);
  1134.             } else if(bindings[term2] != term1)
  1135.             {
  1136.                 return false;
  1137.             }
  1138.         } else if(term1 != term2)
  1139.         {
  1140.             return false;
  1141.         }
  1142.     }
  1143.     return true;
  1144. }
  1145.  
  1146. Expr* JDatalog::Expr::substitute(unordered_map<wstring, wstring>& bindings)
  1147. {
  1148.     // that.terms.add() below doesn't work without the new ArrayList()
  1149.     Expr* that = new Expr(this->predicate, vector<>());
  1150.     that->negated = negated;
  1151.     for(auto term : this->terms)
  1152.     {
  1153.         wstring value;
  1154.         if(isVariable(term))
  1155.         {
  1156.             value = bindings[term];
  1157.             if(value == "")
  1158.             {
  1159.                 value = term;
  1160.             }
  1161.         } else
  1162.         {
  1163.             value = term;
  1164.         }
  1165.         that->terms.push_back(value);
  1166.     }
  1167.     return that;
  1168. }
  1169.  
  1170. bool JDatalog::Expr::evalBuiltIn(unordered_map<wstring, wstring>& bindings) throw(DatalogException)
  1171. {
  1172.     //System.out.println("EVAL " + this + "; " + bindings);
  1173.     wstring term1 = terms[0];
  1174.     if(isVariable(term1) && bindings.find(term1) != bindings.end())
  1175.     {
  1176.         term1 = bindings[term1];
  1177.     }
  1178.     wstring term2 = terms[1];
  1179.     if(isVariable(term2) && bindings.find(term2) != bindings.end())
  1180.     {
  1181.         term2 = bindings[term2];
  1182.     }
  1183.     if(predicate == "=")
  1184.     {
  1185.         // '=' is special
  1186.         if(isVariable(term1))
  1187.         {
  1188.             if(isVariable(term2))
  1189.             {
  1190.                 throw DatalogException("Both operands of '=' are unbound (" + term1 + ", " + term2 + ") in evaluation of " + this);
  1191.             }
  1192.             bindings.emplace(term1, term2);
  1193.             return true;
  1194.         } else if(isVariable(term2))
  1195.         {
  1196.             bindings.emplace(term2, term1);
  1197.             return true;
  1198.         } else
  1199.         {
  1200.             return term1 == term2;
  1201.         }
  1202.     } else
  1203.     {
  1204.         try
  1205.         {
  1206.             if(isVariable(term1))
  1207.             {
  1208.                 throw DatalogException("Unbound variable " + term1 + " in evaluation of " + this);
  1209.             }
  1210.             if(isVariable(term2))
  1211.             {
  1212.                 throw DatalogException("Unbound variable " + term2 + " in evaluation of " + this);
  1213.             }
  1214.  
  1215.             if(predicate == "<>")
  1216.             {
  1217.                 // '<>' is also a bit special
  1218.                 if(tryParseDouble(term1) && tryParseDouble(term2))
  1219.                 {
  1220.                         double d1 = stod(term1);
  1221.                         double d2 = stod(term2);
  1222.                         return d1 != d2;
  1223.                 } else
  1224.                 {
  1225.                     return term1 != term2;
  1226.                 }
  1227.             } else
  1228.             {
  1229.                 // ordinary comparison operator
  1230.                 if(!tryParseDouble(term1) || !tryParseDouble(term2))
  1231.                 {
  1232.                     throw DatalogException("Both parameters of " + predicate + " must be numeric (" + term1 + ", " + term2 + ") in evaluation of " + this);
  1233.                 }
  1234.                 double d1 = stod(term1);
  1235.                 double d2 = stod(term2);
  1236.                 switch(predicate)
  1237.                 {
  1238.                     case "<":
  1239.                         return d1 < d2;
  1240.                     case "<=":
  1241.                         return d1 <= d2;
  1242.                     case ">":
  1243.                         return d1 > d2;
  1244.                     case ">=":
  1245.                         return d1 >= d2;
  1246.                 }
  1247.             }
  1248.         } catch(const NumberFormatException& e)
  1249.         {
  1250.             // You found a way to write a double in a way that the regex in tryParseDouble() doesn't understand.
  1251. //JAVA TO C++ CONVERTER TODO TASK: The std::exception constructor has no parameters:
  1252. //ORIGINAL LINE: throw new RuntimeException("tryParseDouble() experienced a false positive!?", e);
  1253.             throw exception();
  1254.         }
  1255.     }
  1256. //JAVA TO C++ CONVERTER TODO TASK: The std::exception constructor has no parameters:
  1257. //ORIGINAL LINE: throw new RuntimeException("Unimplemented built-in predicate " + predicate);
  1258.     throw exception();
  1259. }
  1260.  
  1261. bool JDatalog::Expr::equals(any other)
  1262. {
  1263.     if(other == nullptr || !(dynamic_cast<Expr*>(other) != nullptr))
  1264.     {
  1265.         return false;
  1266.     }
  1267.     Expr* that = (any_cast<Expr*>(other));
  1268.     if(this->predicate != that->predicate)
  1269.     {
  1270.         return false;
  1271.     }
  1272.     if(arity() != that->arity() || negated != that->negated)
  1273.     {
  1274.         return false;
  1275.     }
  1276.     for(int i = 0; i < terms.size(); i++)
  1277.     {
  1278.         if(terms[i] != that->terms[i])
  1279.         {
  1280.             return false;
  1281.         }
  1282.     }
  1283.     return true;
  1284. }
  1285.  
  1286. int JDatalog::Expr::hashCode()
  1287. {
  1288.     int hash = predicate.hashCode();
  1289.     for(auto term : terms)
  1290.     {
  1291.         hash += term.hashCode();
  1292.     }
  1293.     return hash;
  1294. }
  1295.  
  1296. wstring JDatalog::Expr::toString()
  1297. {
  1298.     StringBuilder* sb = new StringBuilder();
  1299.     if(isNegated())
  1300.     {
  1301.         sb->append("not ");
  1302.     }
  1303.     if(isBuiltIn())
  1304.     {
  1305.         termToString(sb, terms[0]);
  1306.         sb->append(" ")->append(predicate)->append(" ");
  1307.         termToString(sb, terms[1]);
  1308.     } else
  1309.     {
  1310.         sb->append(predicate)->append('(');
  1311.         for(int i = 0; i < terms.size(); i++)
  1312.         {
  1313.             wstring term = terms[i];
  1314.             termToString(sb, term);
  1315.             if(i < terms.size() - 1)
  1316.             {
  1317.                 sb->append(", ");
  1318.             }
  1319.         }
  1320.         sb->append(')');
  1321.     }
  1322.     return sb->toString();
  1323. }
  1324.  
  1325. StringBuilder* JDatalog::Expr::termToString(StringBuilder* sb, const wstring& term)
  1326. {
  1327.     if(StringHelper::startsWith(term, "\""))
  1328.     {
  1329.         sb->append('"')->append(term.substr(1)->replaceAll("\"", "\\\\\""))->append('"');
  1330.     } else
  1331.     {
  1332.         sb->append(term);
  1333.     }
  1334.     return sb;
  1335. }
  1336.  
  1337. vector<Expr*> JDatalog::reorderQuery(vector<Expr*>& query)
  1338. {
  1339.     vector<Expr*> ordered = vector<Expr*>(query.size());
  1340.     for(auto e : query)
  1341.     {
  1342.         if(!e->isNegated() && !(e->isBuiltIn() && !e->predicate.equals("=")))
  1343.         {
  1344.             ordered.push_back(e);
  1345.         }
  1346.     }
  1347.     // Note that a rule like s(A, B) :- r(A, B), X = Y, q(Y), A > X. will cause an error relating to both sides
  1348.     // of the '=' being unbound, and it can be fixed by moving the '=' operators to here, but I've decided against
  1349.     // it, because the '=' should be evaluated ASAP, and it is difficult to determine programatically when that is.
  1350.     // The onus is thus on the user to structure '=' operators properly.
  1351.     for(auto e : query)
  1352.     {
  1353.         if(e->isNegated() || (e->isBuiltIn() && !e->predicate.equals("=")))
  1354.         {
  1355.             ordered.push_back(e);
  1356.         }
  1357.     }
  1358.     return ordered;
  1359. }
  1360.  
  1361. JDatalog::Rule::Rule(Expr* head, vector<Expr*>& body)
  1362. {
  1363.     this->head = head;
  1364.  
  1365.     this->body = reorderQuery(body);
  1366. }
  1367.  
  1368. JDatalog::Rule::Rule(Expr* head, vector<Expr> &body) : Rule(head, Arrays::asList(body))
  1369. {
  1370. }
  1371.  
  1372. void JDatalog::Rule::validate() throw(DatalogException)
  1373. {
  1374.  
  1375.     Set<wstring>* headVars = head->terms.stream().filter([&] (any term)
  1376.     {
  1377.         isVariable(term);
  1378.     }).collect(Collectors::toSet());
  1379.  
  1380.     Set<wstring>* bodyVars = body.stream().flatMap([&] (any expr)
  1381.     {
  1382.         expr::terms::stream();
  1383.     }).filter([&] (any term)
  1384.     {
  1385.         isVariable(term);
  1386.     }).collect(Collectors::toSet());
  1387.  
  1388.     // Enforce the rule that variables in the head must appear in the body
  1389.     headVars->removeAll(bodyVars);
  1390.     if(!headVars->isEmpty())
  1391.     {
  1392. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  1393.         throw DatalogException("These variables from the head of rule " + toString() + " must appear in the body: " + JDatalog::toString(headVars));
  1394.     }
  1395.  
  1396.     // Check for /safety/: each variable in the body of a rule should appear at least once in a positive expression,
  1397.     // to prevent infinite results. E.g. p(X) :- not q(X, Y) is unsafe because there are an infinite number of values
  1398.     // for Y that satisfies `not q`. This is a requirement for negation - [gree] contains a nice description.
  1399.     // We also leave out variables from the built-in predicates because variables must be bound to be able to compare
  1400.     // them, i.e. a rule like `s(A, B) :- r(A,B), A > X` is invalid ('=' is an exception because it can bind variables)
  1401.     // You won't be able to tell if the variables have been bound to _numeric_ values until you actually evaluate the
  1402.     // expression, though.
  1403.     Set<wstring>* positiveVars = body.stream().flatMap([&] (any expr)
  1404.     {
  1405.         (!expr::isNegated() && !(expr::isBuiltIn() && !expr::predicate::equals("="))) ? expr::terms::stream() : java::util::stream::Stream::empty();
  1406.     }).filter([&] (any term)
  1407.     {
  1408.         isVariable(term);
  1409.     }).collect(Collectors::toSet());
  1410.     bodyVars->removeAll(positiveVars);
  1411.     if(!bodyVars->isEmpty())
  1412.     {
  1413. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  1414.         throw DatalogException("Each variable of rule " + toString() + " must appear in at least one positive expression: " + JDatalog::toString(bodyVars));
  1415.     }
  1416. }
  1417.  
  1418. wstring JDatalog::Rule::toString()
  1419. {
  1420.     StringBuilder* sb = new StringBuilder();
  1421.     sb->append(head);
  1422.     sb->append(" :- ");
  1423.     for(int i = 0; i < body.size(); i++)
  1424.     {
  1425.         sb->append(body[i]);
  1426.         if(i < body.size() - 1)
  1427.         {
  1428.             sb->append(", ");
  1429.         }
  1430.     }
  1431.     return sb->toString();
  1432. }
  1433.  
  1434. bool JDatalog::isVariable(const wstring& term)
  1435. {
  1436.     return isupper(term[0]);
  1437. }
  1438.  
  1439. JDatalog* JDatalog::rule(Expr* head, vector<Expr> &body) throw(DatalogException)
  1440. {
  1441.     Rule* newRule = new Rule(head, body);
  1442.     return rule(newRule);
  1443. }
  1444.  
  1445. JDatalog* JDatalog::rule(Rule* newRule) throw(DatalogException)
  1446. {
  1447.     newRule->validate();
  1448.     idb->add(newRule);
  1449.     return this;
  1450. }
  1451.  
  1452. JDatalog* JDatalog::fact(const wstring& predicate, vector<wstring> &terms) throw(DatalogException)
  1453. {
  1454.     Expr tempVar(predicate, terms);
  1455.     return fact(&tempVar);
  1456. }
  1457.  
  1458. JDatalog* JDatalog::fact(Expr* newFact) throw(DatalogException)
  1459. {
  1460.     if(!newFact->isGround())
  1461.     {
  1462.         throw DatalogException("Facts must be ground: " + newFact);
  1463.     }
  1464.     if(newFact->isNegated())
  1465.     {
  1466.         throw DatalogException("Facts cannot be negated: " + newFact);
  1467.     }
  1468.     // You can also match the arity of the fact against existing facts in the EDB,
  1469.     // but it's more of a principle than a technical problem; see JDatalog#validate()
  1470.     edb.push_back(newFact);
  1471.     return this;
  1472. }
  1473.  
  1474. void JDatalog::StandardQueryOutput::writeResult(vector<Expr*>& goals, Collection<unordered_map<wstring, wstring> >* answers) override
  1475. {
  1476.     Profiler::Timer* timer = Profiler::getTimer("output");
  1477.     try
  1478.     {
  1479. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  1480.         cout << JDatalog::toString(goals) << "?" << endl;
  1481.         if(!answers->isEmpty())
  1482.         {
  1483.             if(answers->begin()->next()->isEmpty())
  1484.             {
  1485.                 cout << "  Yes." << endl;
  1486.             } else
  1487.             {
  1488.                 for(auto answer : answers)
  1489.                 {
  1490. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  1491.                     cout << "  " << JDatalog::toString(answer) << endl;
  1492.                 }
  1493.             }
  1494.         } else
  1495.         {
  1496.             cout << "  No." << endl;
  1497.         }
  1498.     } finally
  1499.     {
  1500.         timer->stop();
  1501.     }
  1502. }
  1503.  
  1504. Collection<unordered_map<wstring, wstring> >* JDatalog::execute(Reader* reader, QueryOutput* output) throw(DatalogException)
  1505. {
  1506.     Profiler::Timer* timer = Profiler::getTimer("execute");
  1507.     try
  1508.     {
  1509.         debug("Executing reader...");
  1510.  
  1511.         StreamTokenizer* scan = new StreamTokenizer(reader);
  1512.         scan->ordinaryChar('.'); // '.' looks like a number to StreamTokenizer by default
  1513.         scan->commentChar('%'); // Prolog-style % comments; slashSlashComments and slashStarComments can stay as well.
  1514.         scan->quoteChar('"');
  1515.         scan->quoteChar('\'');
  1516.         //scan.parseNumbers(); // WTF? You can't disable parsing of numbers unless you reset the syntax (http://stackoverflow.com/q/8856750/115589)
  1517.         scan->nextToken();
  1518.  
  1519.         // Tracks the last query's answers
  1520.         Collection<unordered_map<wstring, wstring> >* answers = nullptr;
  1521.  
  1522.         // Tracks the last query's goals (for output purposes)
  1523.         vector<Expr*> goals = vector<Expr*>();
  1524.  
  1525.         while(scan->ttype != StreamTokenizer::TT_EOF)
  1526.         {
  1527.             scan->pushBack();
  1528.             answers = parseStmt(scan, goals);
  1529.             if(answers != nullptr && output != nullptr)
  1530.             {
  1531.                 output->writeResult(goals, answers);
  1532.             }
  1533.             scan->nextToken();
  1534.         }
  1535.         return answers;
  1536.     } catch(const IOException& e)
  1537.     {
  1538.         throw DatalogException(e);
  1539.     } finally
  1540.     {
  1541.         timer->stop();
  1542.     }
  1543. }
  1544.  
  1545. Collection<unordered_map<wstring, wstring> >* JDatalog::parseStmt(StreamTokenizer* scan, vector<Expr*>& goals) throw(DatalogException)
  1546. {
  1547.     Profiler::Timer* timer = Profiler::getTimer("parseStmt");
  1548.     try
  1549.     {
  1550.  
  1551.         scan->nextToken();
  1552.         // "delete a(b,X)." deletes facts from the IDB that matches the query.
  1553.         // It is not standard Datalog AFAIK.
  1554.         if(scan->ttype == StreamTokenizer::TT_WORD && scan->sval.equalsIgnoreCase("delete"))
  1555.         {
  1556.             goals.clear();
  1557.             do
  1558.             {
  1559.                 Expr* e = parseExpr(scan);
  1560.                 goals.push_back(e);
  1561.             } while(scan->nextToken() == ',');
  1562.             if(scan->ttype != '.')
  1563.             {
  1564.                 throw DatalogException("[line " + scan->lineno() + "] Expected '.' after 'delete'");
  1565.             }
  1566.             if(debugEnable)
  1567.             {
  1568.                 cout << "Parser [line " << scan->lineno() << "]: Deleting goals: " << toString(goals) << endl;
  1569.             }
  1570.             delete(goals);
  1571.             return nullptr;
  1572.         } else
  1573.         {
  1574.             scan->pushBack();
  1575.         }
  1576.  
  1577.         Expr* head = parseExpr(scan);
  1578.         if(scan->nextToken() == ':')
  1579.         {
  1580.             // We're dealing with a rule
  1581.             if(scan->nextToken() != '-')
  1582.             {
  1583.                 throw DatalogException("[line " + scan->lineno() + "] Expected ':-'");
  1584.             }
  1585.             vector<Expr*> body = vector<Expr*>();
  1586.             do
  1587.             {
  1588.                 Expr* arg = parseExpr(scan);
  1589.                 body.push_back(arg);
  1590.             } while(scan->nextToken() == ',');
  1591.  
  1592.             if(scan->ttype != '.')
  1593.             {
  1594.                 throw DatalogException("[line " + scan->lineno() + "] Expected '.' after rule");
  1595.             }
  1596.             try
  1597.             {
  1598.                 Rule* newRule = new Rule(head, body);
  1599.                 rule(newRule);
  1600.                 debug("Parser [line " + scan->lineno() + "]: Got rule: " + newRule);
  1601.             } catch(const DatalogException& de)
  1602.             {
  1603.                 throw DatalogException("[line " + scan->lineno() + "] Rule is invalid", de);
  1604.             }
  1605.         } else
  1606.         {
  1607.             // We're dealing with a fact, or a query
  1608.             if(scan->ttype == '.')
  1609.             {
  1610.                 // It's a fact
  1611.                 try
  1612.                 {
  1613.                     fact(head);
  1614.                     debug("Parser [line " + scan->lineno() + "]: Got fact: " + head);
  1615.                 } catch(const DatalogException& de)
  1616.                 {
  1617.                     throw DatalogException("[line " + scan->lineno() + "] Fact is invalid", de);
  1618.                 }
  1619.             } else
  1620.             {
  1621.                 // It's a query
  1622.                 goals.clear();
  1623.                 goals.push_back(head);
  1624.                 if(scan->ttype != '.' && scan->ttype != '?' && scan->ttype != ',')
  1625.                 {
  1626.                     /* You _can_ write facts like `a = 5 .` but I recommend against it; if you do then you *must* have the space between the
  1627.                     5 and the '.' otherwise the parser sees it as 5.0 and the error message can be a bit confusing. */
  1628.                     throw DatalogException("[line " + scan->lineno() + "] Expected one of '.', ',' or '?' after fact/query expression");
  1629.                 }
  1630.                 while(scan->ttype == ',')
  1631.                 {
  1632.                     goals.push_back(parseExpr(scan));
  1633.                     scan->nextToken();
  1634.                 }
  1635.                 debug("Parser [line " + scan->lineno() + "]: Got query: " + toString(goals));
  1636.  
  1637.                 if(scan->ttype == '?')
  1638.                 {
  1639.                     try
  1640.                     {
  1641.                         return query(goals);
  1642.                     } catch(const DatalogException& e)
  1643.                     {
  1644.                         // Attach the line number to any exceptions thrown; the small drawback is that you lose
  1645.                         // the original stacktrace, but the line number is more important for users.
  1646.                         throw DatalogException("[line " + scan->lineno() + "] " + e->what());
  1647.                     }
  1648.                 } else
  1649.                 {
  1650.                     throw DatalogException("[line " + scan->lineno() + "] Expected '?' after query");
  1651.                 }
  1652.             }
  1653.         }
  1654.     } catch(const IOException& e)
  1655.     {
  1656.         throw DatalogException(e);
  1657.     } finally
  1658.     {
  1659.         timer->stop();
  1660.     }
  1661.     return nullptr;
  1662. }
  1663.  
  1664. JDatalog::Expr* JDatalog::parseExpr(StreamTokenizer* scan) throw(DatalogException)
  1665. {
  1666.     Profiler::Timer* timer = Profiler::getTimer("parseExpr");
  1667.     try
  1668.     {
  1669.         scan->nextToken();
  1670.  
  1671.         bool negated = false;
  1672.         if(scan->ttype == StreamTokenizer::TT_WORD && scan->sval.equalsIgnoreCase("not"))
  1673.         {
  1674.             negated = true;
  1675.             scan->nextToken();
  1676.         }
  1677.  
  1678.         wstring lhs = "";
  1679.         bool builtInExpected = false;
  1680.         if(scan->ttype == StreamTokenizer::TT_WORD)
  1681.         {
  1682.             lhs = scan->sval;
  1683.         } else if(scan->ttype == '"' || scan->ttype == '\'')
  1684.         {
  1685.             lhs = scan->sval;
  1686.             builtInExpected = true;
  1687.         } else if(scan->ttype == StreamTokenizer::TT_NUMBER)
  1688.         {
  1689.             lhs = numberToString(scan->nval);
  1690.             builtInExpected = true;
  1691.         } else
  1692.         {
  1693.             throw DatalogException("[line " + scan->lineno() + "] Predicate or start of expression expected");
  1694.         }
  1695.  
  1696.         scan->nextToken();
  1697.         if(scan->ttype == StreamTokenizer::TT_WORD || scan->ttype == '=' || scan->ttype == '!' || scan->ttype == '<' || scan->ttype == '>')
  1698.         {
  1699.             scan->pushBack();
  1700.             Expr* e = parseBuiltInPredicate(lhs, scan);
  1701.             e->negated = negated;
  1702.             debug("Got built-in predicate: " + e);
  1703.             return e;
  1704.         }
  1705.  
  1706.         if(builtInExpected)
  1707.         {
  1708.             // LHS was a number or a quoted string but we didn't get an operator
  1709.             throw DatalogException("[line " + scan->lineno() + "] Built-in predicate expected");
  1710.         } else if(scan->ttype != '(')
  1711.         {
  1712.             throw DatalogException("[line " + scan->lineno() + "] Expected '(' after predicate or an operator");
  1713.         }
  1714.  
  1715.         vector<wstring> terms = vector<wstring>();
  1716.         if(scan->nextToken() != ')')
  1717.         {
  1718.             scan->pushBack();
  1719.             do
  1720.             {
  1721.                 if(scan->nextToken() == StreamTokenizer::TT_WORD)
  1722.                 {
  1723.                     terms.push_back(scan->sval);
  1724.                 } else if(scan->ttype == '"' || scan->ttype == '\'')
  1725.                 {
  1726.                     terms.push_back("\"" + scan->sval);
  1727.                 } else if(scan->ttype == StreamTokenizer::TT_NUMBER)
  1728.                 {
  1729.                     terms.push_back(numberToString(scan->nval));
  1730.                 } else
  1731.                 {
  1732.                     throw DatalogException("[line " + scan->lineno() + "] Expected term in expression");
  1733.                 }
  1734.             } while(scan->nextToken() == ',');
  1735.             if(scan->ttype != ')')
  1736.             {
  1737.                 throw DatalogException("[line " + scan->lineno() + "] Expected ')'");
  1738.             }
  1739.         }
  1740.         Expr* e = new Expr(lhs, terms);
  1741.         e->negated = negated;
  1742.         return e;
  1743.     } catch(const IOException& e)
  1744.     {
  1745.         throw DatalogException(e);
  1746.     } finally
  1747.     {
  1748.         timer->stop();
  1749.     }
  1750. }
  1751.  
  1752. const vector<wstring> JDatalog::validOperators = java::util::Arrays::asList(std::vector<wstring> { "=", "!=", "<>", "<", "<=", ">", ">=" });
  1753.  
  1754. JDatalog::Expr* JDatalog::parseBuiltInPredicate(const wstring& lhs, StreamTokenizer* scan) throw(DatalogException)
  1755. {
  1756.     try
  1757.     {
  1758.         wstring operator_RenamedTODO;
  1759.         scan->nextToken();
  1760.         if(scan->ttype == StreamTokenizer::TT_WORD)
  1761.         {
  1762.             // At some point I was going to have "eq" and "ne" for string comparisons, but it wasn't a good idea.
  1763.             operator_RenamedTODO = scan->sval;
  1764.         } else
  1765.         {
  1766. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  1767.             operator_RenamedTODO = Character::toString(static_cast<char>(scan->ttype));
  1768.             scan->nextToken();
  1769.             if(scan->ttype == '=' || scan->ttype == '>')
  1770.             {
  1771. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  1772.                 operator_RenamedTODO = operator_RenamedTODO + Character::toString(static_cast<char>(scan->ttype));
  1773.             } else
  1774.             {
  1775.                 scan->pushBack();
  1776.             }
  1777.         }
  1778.  
  1779.         if(!find(validOperators.begin(), validOperators.end(), operator_RenamedTODO) != validOperators.end())
  1780.         {
  1781.             throw DatalogException("Invalid operator '" + operator_RenamedTODO + "'");
  1782.         }
  1783.  
  1784.         wstring rhs = "";
  1785.         scan->nextToken();
  1786.         if(scan->ttype == StreamTokenizer::TT_WORD)
  1787.         {
  1788.             rhs = scan->sval;
  1789.         } else if(scan->ttype == '"' || scan->ttype == '\'')
  1790.         {
  1791.             rhs = scan->sval;
  1792.         } else if(scan->ttype == StreamTokenizer::TT_NUMBER)
  1793.         {
  1794.             rhs = numberToString(scan->nval);
  1795.         } else
  1796.         {
  1797.             throw DatalogException("[line " + scan->lineno() + "] Right hand side of expression expected");
  1798.         }
  1799.  
  1800.         return new Expr(operator_RenamedTODO, lhs, rhs);
  1801.  
  1802.     } catch(const IOException& e)
  1803.     {
  1804.         throw DatalogException(e);
  1805.     }
  1806. }
  1807.  
  1808. wstring JDatalog::numberToString(const double& nval)
  1809. {
  1810.     // Remove trailing zeros; http://stackoverflow.com/a/14126736/115589
  1811.     if(nval == static_cast<long long>(nval))
  1812.     {
  1813.         return wstring::format("%d",static_cast<long long>(nval));
  1814.     } else
  1815.     {
  1816.         return StringHelper::formatSimple("%s",nval);
  1817.     }
  1818. }
  1819.  
  1820. java::util::regex::Pattern* const  JDatalog::numberPattern = java::util::regex::Pattern::compile("[+-]?\\d+(\\.\\d*)?([Ee][+-]?\\d+)?");
  1821.  
  1822. bool JDatalog::tryParseDouble(const wstring& str)
  1823. {
  1824.     return numberPattern->matcher(str).matches();
  1825. }
  1826.  
  1827. JDatalog::DatalogException::DatalogException(const wstring& message) : Exception(message)
  1828. {
  1829. }
  1830.  
  1831. JDatalog::DatalogException::DatalogException(exception cause) : Exception(cause)
  1832. {
  1833. }
  1834.  
  1835. JDatalog::DatalogException::DatalogException(const wstring& message, exception cause) : Exception(message, cause)
  1836. {
  1837. }
  1838.  
  1839. Collection<unordered_map<wstring, wstring> >* JDatalog::query(const wstring& statement) throw(DatalogException)
  1840. {
  1841.     // It would've been fun to wrap the results in a java.sql.ResultSet, but damn,
  1842.     // those are a lot of methods to implement:
  1843.     // https://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html
  1844.     StringReader* reader = new StringReader(statement);
  1845.     return execute(reader, nullptr);
  1846. }
  1847.  
  1848. Collection<unordered_map<wstring, wstring> >* JDatalog::query(vector<Expr> &goals) throw(DatalogException)
  1849. {
  1850.     return query(Arrays::asList(goals));
  1851. }
  1852.  
  1853. Collection<unordered_map<wstring, wstring> >* JDatalog::query(vector<Expr*>& goals) throw(DatalogException)
  1854. {
  1855.     Profiler::Timer* timer = Profiler::getTimer("query");
  1856.     try
  1857.     {
  1858.         if(goals.empty())
  1859.         {
  1860.             return Collections::emptyList();
  1861.         }
  1862.  
  1863.         // Reorganize the goals so that negated literals are at the end.
  1864.         vector<Expr*> orderedGoals = reorderQuery(goals);
  1865.  
  1866.         // getRelevantRules() strips a large part of the rules, so if I want to
  1867.         // do some benchmarking of buildDatabase(), I use the IDB directly instead:
  1868.         // Collection<Rule> rules = idb;
  1869.         Collection<Rule*>* rules = getRelevantRules(goals);
  1870.         if(debugEnable)
  1871.         {
  1872.             cout << "To answer query, we need to evaluate: " << toString(rules) << endl;
  1873.         }
  1874.  
  1875.         // Build the database. A Set ensures that the facts are unique
  1876.         Collection<Expr*>* dataset = buildDatabase(unordered_set<Expr*>(edb), rules);
  1877.         if(debugEnable)
  1878.         {
  1879.             cout << "query(): Database = " << toString(dataset) << endl;
  1880.         }
  1881.         return matchGoals(orderedGoals, dataset, nullptr);
  1882.     } finally
  1883.     {
  1884.         timer->stop();
  1885.     }
  1886. }
  1887.  
  1888. Collection<Rule*>* JDatalog::getRelevantRules(vector<Expr*>& originalGoals)
  1889. {
  1890.     Profiler::Timer* timer = Profiler::getTimer("getRelevantRules");
  1891.     try
  1892.     {
  1893.         Collection<Rule*>* relevant = unordered_set<Rule*>();
  1894.         list<Expr*> goals = list<Expr*>(originalGoals);
  1895.         while(!goals.empty())
  1896.         {
  1897.             Expr* expr = goals.pop_front();
  1898.             for(auto rule : idb)
  1899.             {
  1900.                 if(rule->head->predicate.equals(expr->predicate) && !relevant->contains(rule))
  1901.                 {
  1902.                     relevant->add(rule);
  1903.                     goals.addAll(rule->body);
  1904.                 }
  1905.             }
  1906.         }
  1907.         return relevant;
  1908.     } finally
  1909.     {
  1910.         timer->stop();
  1911.     }
  1912. }
  1913.  
  1914. unordered_map<wstring, Collection<Rule*>*> JDatalog::buildDependantRules(Collection<Rule*>* rules)
  1915. {
  1916.     Profiler::Timer* timer = Profiler::getTimer("buildDependantRules");
  1917.     try
  1918.     {
  1919.         unordered_map<wstring, Collection<Rule*>*> map = unordered_map<wstring, Collection<Rule*>*>();
  1920.         for(auto rule : rules)
  1921.         {
  1922.             for(auto goal : rule->body)
  1923.             {
  1924.                 Collection<Rule*>* dependants = map[goal->predicate];
  1925.                 if(dependants == nullptr)
  1926.                 {
  1927.                     dependants = vector<>();
  1928.                     map.emplace(goal->predicate, dependants);
  1929.                 }
  1930.                 if(!dependants->contains(rule))
  1931.                 {
  1932.                     dependants->add(rule);
  1933.                 }
  1934.             }
  1935.         }
  1936.         return map;
  1937.     } finally
  1938.     {
  1939.         timer->stop();
  1940.     }
  1941. }
  1942.  
  1943. Collection<Rule*>* JDatalog::getDependantRules(Collection<Expr*>* facts, unordered_map<wstring, Collection<Rule*>*>& dependants)
  1944. {
  1945.     Profiler::Timer* timer = Profiler::getTimer("getDependantRules");
  1946.     try
  1947.     {
  1948.         Set<Rule*>* dependantRules = unordered_set<Rule*>();
  1949.         for(auto fact : facts)
  1950.         {
  1951.             Collection<Rule*>* rules = dependants[fact->predicate];
  1952.             if(rules != nullptr)
  1953.             {
  1954.                 dependantRules->addAll(rules);
  1955.             }
  1956.         }
  1957.         return dependantRules;
  1958.     } finally
  1959.     {
  1960.         timer->stop();
  1961.     }
  1962. }
  1963.  
  1964. Collection<Expr*>* JDatalog::buildDatabase(Set<Expr*>* facts, Collection<Rule*>* allRules) throw(DatalogException)
  1965. {
  1966.     Profiler::Timer* timer = Profiler::getTimer("buildDatabase");
  1967.     try
  1968.     {
  1969.         vector<Collection<Rule*>*> strata = computeStratification(allRules);
  1970.         for(int i = 0; i < strata.size(); i++)
  1971.         {
  1972.             debug("Expanding strata " + to_string(i));
  1973.             Collection<Rule*>* rules = strata[i];
  1974.             if(rules != nullptr && !rules->isEmpty())
  1975.             {
  1976.                 expandStrata(facts, rules);
  1977.             }
  1978.         }
  1979.         return facts;
  1980.     } finally
  1981.     {
  1982.         timer->stop();
  1983.     }
  1984. }
  1985.  
  1986. vector<Collection<Rule*>*> JDatalog::computeStratification(Collection<Rule*>* allRules) throw(DatalogException)
  1987. {
  1988.     Profiler::Timer* timer = Profiler::getTimer("computeStratification");
  1989.     try
  1990.     {
  1991.         vector<Collection<Rule*>*> strata = vector<Collection<Rule*>*>(10);
  1992.  
  1993.         unordered_map<wstring, int> strats = unordered_map<wstring, int>();
  1994.         for(auto rule : allRules)
  1995.         {
  1996.             wstring pred = rule->head->predicate;
  1997.             optional<int> stratum = strats[pred];
  1998.             if(!stratum)
  1999.             {
  2000.                 stratum = depthFirstSearch(rule->head, allRules, vector<>(), 0);
  2001.                 strats.emplace(pred, stratum);
  2002.                 if(debugEnable)
  2003.                 {
  2004.                     cout << "Strata{" << pred << "} == " << strats[pred] << endl;
  2005.                 }
  2006.             }
  2007.  
  2008.             while(stratum >= strata.size())
  2009.             {
  2010.                 strata.push_back(vector<>());
  2011.             }
  2012.             strata[stratum]->add(rule);
  2013.         }
  2014.  
  2015.         strata.push_back(allRules);
  2016.         return strata;
  2017.     } finally
  2018.     {
  2019.         timer->stop();
  2020.     }
  2021. }
  2022.  
  2023. int JDatalog::depthFirstSearch(Expr* goal, Collection<Rule*>* graph, vector<Expr*>& visited, const int& level) throw(DatalogException)
  2024. {
  2025.     wstring pred = goal->predicate;
  2026.  
  2027.     // Step (1): Guard against negative recursion
  2028.     bool negated = goal->isNegated();
  2029.     StringBuilder* route = new StringBuilder(pred); // for error reporting
  2030.     for(int i = visited.size() - 1; i >= 0; i--)
  2031.     {
  2032.         Expr* e = visited[i];
  2033.         route->append(e->isNegated() ? " <- ~" : " <- ")->append(e->predicate);
  2034.         if(e->predicate.equals(pred))
  2035.         {
  2036.             if(negated)
  2037.             {
  2038.                 throw DatalogException("Program is not stratified - predicate " + pred + " has a negative recursion: " + route);
  2039.             }
  2040.             return 0;
  2041.         }
  2042.         if(e->isNegated())
  2043.         {
  2044.             negated = true;
  2045.         }
  2046.     }
  2047.     visited.push_back(goal);
  2048.  
  2049.     // Step (2): Do the actual depth-first search to compute the strata
  2050.     int m = 0;
  2051.     for(auto rule : graph)
  2052.     {
  2053.         if(rule->head->predicate.equals(pred))
  2054.         {
  2055.             for(auto expr : rule->body)
  2056.             {
  2057.                 int x = depthFirstSearch(expr, graph, visited, level + 1);
  2058.                 if(expr->negated)
  2059.                 {
  2060.                     x++;
  2061.                 }
  2062.                 if(x > m)
  2063.                 {
  2064.                     m = x;
  2065.                 }
  2066.             }
  2067.         }
  2068.     }
  2069.     visited.pop_back();
  2070.  
  2071.     return m;
  2072. }
  2073.  
  2074. Collection<Expr*>* JDatalog::expandStrata(Set<Expr*>* facts, Collection<Rule*>* strataRules) throw(DatalogException)
  2075. {
  2076.     Profiler::Timer* timer = Profiler::getTimer("expandStrata");
  2077.     try
  2078.     {
  2079.         Collection<Rule*>* rules = strataRules;
  2080.  
  2081.         unordered_map<wstring, Collection<Rule*>*> dependants = buildDependantRules(strataRules);
  2082.  
  2083.         while(true)
  2084.         {
  2085.             Profiler::Timer* loopTimer = Profiler::getTimer("loopTimer");
  2086.             try
  2087.             {
  2088.  
  2089.                 // Match each rule to the facts
  2090.                 Profiler::Timer* matchRuleTimer = Profiler::getTimer("matchRules");
  2091.                 Set<Expr*>* newFacts = unordered_set<Expr*>();
  2092.                 for(auto rule : rules)
  2093.                 {
  2094.                     newFacts->addAll(matchRule(facts, rule));
  2095.                 }
  2096.                 matchRuleTimer->stop();
  2097.  
  2098.                 // Delta facts: The new facts that have been added in this iteration for semi-naive evaluation.
  2099.                 Profiler::Timer* deltaFactsTimer = Profiler::getTimer("deltaFacts");
  2100.                 newFacts->removeAll(facts);
  2101.                 deltaFactsTimer->stop();
  2102.  
  2103.                 // Repeat until there are no more facts added
  2104.                 if(newFacts->isEmpty())
  2105.                 {
  2106.                     return facts;
  2107.                 }
  2108.                 if(debugEnable)
  2109.                 {
  2110.                     cout << "expandStrata(): deltaFacts = " << toString(newFacts) << endl;
  2111.                 }
  2112.  
  2113.                 rules = getDependantRules(newFacts, dependants);
  2114.  
  2115.                 Profiler::Timer* addAllTimer = Profiler::getTimer("addAll");
  2116.                 facts->addAll(newFacts);
  2117.                 addAllTimer->stop();
  2118.  
  2119.             } finally
  2120.             {
  2121.                 loopTimer->stop();
  2122.             }
  2123.         }
  2124.     } finally
  2125.     {
  2126.         timer->stop();
  2127.     }
  2128. }
  2129.  
  2130. Set<Expr*>* JDatalog::matchRule(Collection<Expr*>* const facts, Rule* rule) throw(DatalogException)
  2131. {
  2132.     Profiler::Timer* timer = Profiler::getTimer("matchRule");
  2133.     try
  2134.     {
  2135.         if(rule->body.empty()) // If this happens, you're using the API wrong.
  2136.         {
  2137.             return Collections::emptySet();
  2138.         }
  2139.  
  2140.         // Match the rule body to the facts.
  2141.         Collection<unordered_map<wstring, wstring> >* answers = matchGoals(rule->body, facts, nullptr);
  2142.  
  2143.         // For each match found, substitute the bindings into the head to create a new fact.
  2144.         Set<Expr*>* results = unordered_set<Expr*>();
  2145.         for(auto answer : answers)
  2146.         {
  2147.             results->add(rule->head->substitute(answer));
  2148.         }
  2149.         return results;
  2150.     } finally
  2151.     {
  2152.         timer->stop();
  2153.     }
  2154. }
  2155.  
  2156. Collection<unordered_map<wstring, wstring> >* JDatalog::matchGoals(vector<Expr*>& goals, Collection<Expr*>* const facts, unordered_map<wstring, wstring>& bindings) throw(DatalogException)
  2157. {
  2158.  
  2159.     Expr* goal = goals[0]; // First goal; Assumes goals won't be empty
  2160.  
  2161.     bool lastGoal = (goals.size() == 1);
  2162.  
  2163.     if(goal->isBuiltIn())
  2164.     {
  2165.         unordered_map<wstring, wstring> newBindings = new StackMap<wstring, wstring>(bindings);
  2166.         bool eval = goal->evalBuiltIn(newBindings);
  2167.         if(eval && !goal->isNegated() || !eval && goal->isNegated())
  2168.         {
  2169.             if(lastGoal)
  2170.             {
  2171.                 if(debugEnable)
  2172.                 {
  2173.                     cout << "** (+) Goal: " << goal << "; " << newBindings << endl;
  2174.                 }
  2175.                 return Collections::singletonList(newBindings);
  2176.             } else
  2177.             {
  2178.                 return matchGoals(goals.subList(1, goals.size()), facts, newBindings);
  2179.             }
  2180.         }
  2181.         return Collections::emptyList();
  2182.     }
  2183.  
  2184.     Collection<unordered_map<wstring, wstring> >* answers = vector<unordered_map<wstring, wstring> >();
  2185.     if(!goal->isNegated())
  2186.     {
  2187.         // Positive rule: Match each fact to the first goal.
  2188.         // If the fact matches: If it is the last/only goal then we can return the bindings
  2189.         // as an answer, otherwise we recursively check the remaining goals.
  2190.         for(auto fact : facts)
  2191.         {
  2192.             if(!fact->predicate.equals(goal->predicate))
  2193.             {
  2194.                 continue;
  2195.             }
  2196.             unordered_map<wstring, wstring> newBindings = new StackMap<wstring, wstring>(bindings);
  2197.             if(fact->unify(goal, newBindings))
  2198.             {
  2199.                 if(lastGoal)
  2200.                 {
  2201.                     if(debugEnable)
  2202.                     {
  2203.                         cout << "** (+) Goal: " << goal << "; " << newBindings << endl;
  2204.                     }
  2205.                     answers->add(newBindings);
  2206.                 } else
  2207.                 {
  2208.                     // More goals to match. Recurse with the remaining goals.
  2209.                     answers->addAll(matchGoals(goals.subList(1, goals.size()), facts, newBindings));
  2210.                 }
  2211.             }
  2212.         }
  2213.     } else
  2214.     {
  2215.         // Negated rule: If you find any fact that matches the goal, then the goal is false.
  2216.         // See definition 4.3.2 of [bra2] and section VI-B of [ceri].
  2217.         // Substitute the bindings in the rule first.
  2218.         // If your rule is `und(X) :- stud(X), not grad(X)` and you're at the `not grad` part, and in the
  2219.         // previous goal stud(a) was true, then bindings now contains X:a so we want to search the database
  2220.         // for the fact grad(a).
  2221.         if(bindings.size() > 0)
  2222.         {
  2223.             goal = goal->substitute(bindings);
  2224.         }
  2225.         for(auto fact : facts)
  2226.         {
  2227.             if(!fact->predicate.equals(goal->predicate))
  2228.             {
  2229.                 continue;
  2230.             }
  2231.             unordered_map<wstring, wstring> newBindings = new StackMap<wstring, wstring>(bindings);
  2232.             if(fact->unify(goal, newBindings))
  2233.             {
  2234.                 return Collections::emptyList();
  2235.             }
  2236.         }
  2237.         // not found
  2238.         if(debugEnable)
  2239.         {
  2240.             cout << "** (-) Goal: " << goal << "; " << bindings << endl;
  2241.         }
  2242.         if(lastGoal)
  2243.         {
  2244.             answers->add(bindings);
  2245.         } else
  2246.         {
  2247.             answers->addAll(matchGoals(goals.subList(1, goals.size()), facts, bindings));
  2248.         }
  2249.     }
  2250.     return answers;
  2251. }
  2252.  
  2253. bool JDatalog::delete_RenamedTODO(vector<Expr> &facts) throw(DatalogException)
  2254. {
  2255.     return delete({ Arrays::asList(facts) });
  2256. }
  2257.  
  2258. bool JDatalog::delete_RenamedTODO(vector<Expr*>& goals) throw(DatalogException)
  2259. {
  2260.     Profiler::Timer* timer = Profiler::getTimer("delete");
  2261.     try
  2262.     {
  2263.         Collection<unordered_map<wstring, wstring> >* answers = query(goals);
  2264.         vector<Expr*> facts = answers->stream().flatMap([&] (any answer)
  2265.         {
  2266.             goals.stream().map([&] (any goal)
  2267.             {
  2268.                 goal::substitute(answer);
  2269.             });
  2270.         }).collect(Collectors::toList());
  2271.             // and substitute the answer on each goal
  2272.         if(debugEnable)
  2273.         {
  2274.             cout << "Facts to delete: " << toString(facts) << endl;
  2275.         }
  2276.         return edb.removeAll(facts);
  2277.     } finally
  2278.     {
  2279.         timer->stop();
  2280.     }
  2281. }
  2282.  
  2283. void JDatalog::validate() throw(DatalogException)
  2284. {
  2285.     for(auto rule : idb)
  2286.     {
  2287.         rule->validate();
  2288.     }
  2289.  
  2290.     // Search for negated loops:
  2291.     computeStratification(idb);
  2292.  
  2293.     for(int i = 0; i < edb.size(); i++)
  2294.     {
  2295.         Expr* fact = edb[i];
  2296.         if(!fact->isGround())
  2297.         {
  2298.             throw DatalogException("Fact " + fact + " is not ground");
  2299.         } else if(fact->isNegated())
  2300.         {
  2301.             throw DatalogException("Fact " + fact + " is negated");
  2302.         }
  2303.         // else if(fact.isBuiltIn()) // I allow facts like `a = 5` or `=(a,5)`
  2304.  
  2305.         for(int j = i + 1; j < edb.size(); j++)
  2306.         {
  2307.             Expr* that = edb[j];
  2308.             if(fact->predicate.equals(that->predicate))
  2309.             {
  2310.                 if(fact->arity() != that->arity())
  2311.                 {
  2312.                     // Technically we don't really require the arity of two facts to be the same if they have the same
  2313.                     // predicate, since they simply won't unify with the goals in the queries.
  2314.                     throw DatalogException("Arity mismatch in EDB: " + fact->predicate + "/" + to_string(fact->arity()) + " vs " + that->predicate + "/" + to_string(that->arity()));
  2315.                 }
  2316.             }
  2317.         }
  2318.     }
  2319. }
  2320.  
  2321. void JDatalog::dump(PrintStream* out) throw(DatalogException)
  2322. {
  2323.     out->println("% Facts:");
  2324.     for(auto fact : edb)
  2325.     {
  2326.         out->println(fact + ".");
  2327.     }
  2328.     out->println("\n% Rules:");
  2329.     for(auto rule : idb)
  2330.     {
  2331.         out->println(rule + ".");
  2332.     }
  2333. }
  2334.  
  2335. void JDatalog::debug(const wstring& message)
  2336. {
  2337.     // I'm not happy with this. Remove later.
  2338.     if(debugEnable)
  2339.     {
  2340.         cout << message << endl;
  2341.     }
  2342. }
  2343.  
  2344. template<typename T1>
  2345. wstring JDatalog::toString(Collection<T1>* collection)
  2346. {
  2347.     StringBuilder* sb = new StringBuilder("[");
  2348.     for(auto o : collection)
  2349.     {
  2350. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  2351.         sb->append(o.toString())->append(". ");
  2352.     }
  2353.     sb->append("]");
  2354.     return sb->toString();
  2355. }
  2356.  
  2357. wstring JDatalog::toString(unordered_map<wstring, wstring>& bindings)
  2358. {
  2359.     StringBuilder* sb = new StringBuilder("{");
  2360.     int s = bindings.size(), i = 0;
  2361.     for(auto k : bindings)
  2362.     {
  2363.         wstring v = bindings[k.first];
  2364.         sb->append(k.first)->append(": ");
  2365.         if(StringHelper::startsWith(v, "\""))
  2366.         {
  2367.             // Needs more org.apache.commons.lang3.StringEscapeUtils#escapeJava(String)
  2368.             sb->append('"')->append(v.substr(1)->replaceAll("\"", "\\\\\""))->append("\"");
  2369.         } else
  2370.         {
  2371.             sb->append(v);
  2372.         }
  2373.         if(++i < s)
  2374.         {
  2375.             sb->append(", ");
  2376.         }
  2377.     }
  2378.     sb->append("}");
  2379.     return sb->toString();
  2380. }
  2381.  
  2382. void JDatalog::main(vector<wstring> &args)
  2383. {
  2384.  
  2385.      if(args->length > 0)
  2386.      {
  2387.         // Read input from a file...
  2388.         try
  2389.         {
  2390.  
  2391.             if(BENCHMARK)
  2392.             {
  2393.                 // Execute the program a couple of times without output
  2394.                 // to "warm up" the JVM for profiling.
  2395.                 for(int run = 0; run < 5; run++)
  2396.                 {
  2397.                     cout << "" << (5 - run) << "...";
  2398.                     JDatalog* jDatalog = new JDatalog();
  2399.                     for(wstring arg : args)
  2400.                     {
  2401. //JAVA TO C++ CONVERTER NOTE: The following 'try with resources' block is replaced by its C++ equivalent:
  2402. //ORIGINAL LINE: try(java.io.Reader reader = new java.io.BufferedReader(new java.io.FileReader(arg)))
  2403.                         {
  2404.                             java::io::FileReader tempVar(arg);
  2405.                         java::io::Reader* reader = new java::io::BufferedReader(&tempVar);
  2406.                             jDatalog->execute(reader, nullptr);
  2407.                         delete reader;
  2408.                         }
  2409.                     }
  2410.                 }
  2411.                 Profiler::reset();
  2412.                 System::gc();
  2413.                 System::runFinalization();
  2414.                 cout << endl;
  2415.  
  2416.                 QueryOutput* qo = new StandardQueryOutput();
  2417.                 for(int run = 0; run < NUM_RUNS; run++)
  2418.                 {
  2419.  
  2420.                     JDatalog* jDatalog = new JDatalog();
  2421.  
  2422.                     for(wstring arg : args)
  2423.                     {
  2424.                         debug("Executing file " + arg);
  2425. //JAVA TO C++ CONVERTER NOTE: The following 'try with resources' block is replaced by its C++ equivalent:
  2426. //ORIGINAL LINE: try(java.io.Reader reader = new java.io.BufferedReader(new java.io.FileReader(arg)))
  2427.                         {
  2428.                             java::io::FileReader tempVar2(arg);
  2429.                         java::io::Reader* reader = new java::io::BufferedReader(&tempVar2);
  2430.                             jDatalog->execute(reader, qo);
  2431.                         delete reader;
  2432.                         }
  2433.                     }
  2434.                 }
  2435.  
  2436.                 cout << "Profile for running " << toString(Arrays::asList(args)) << "; NUM_RUNS=" << NUM_RUNS << endl;
  2437.                 Profiler::keySet()->stream().sorted().forEach([&] (any key)
  2438.                 {
  2439.                     double time = Profiler::average(key);
  2440.                     double total = Profiler::total(key);
  2441.                     int count = Profiler::count(key);
  2442.                     cout << wstring::format("%-20s time: %10.4fms; total: %12.2fms; count: %d", key, time, total, count) << endl;
  2443.                 });
  2444.             } else
  2445.             {
  2446.                 JDatalog* jDatalog = new JDatalog();
  2447.                 QueryOutput* qo = new StandardQueryOutput();
  2448.                 for(wstring arg : args)
  2449.                 {
  2450.                     debug("Executing file " + arg);
  2451. //JAVA TO C++ CONVERTER NOTE: The following 'try with resources' block is replaced by its C++ equivalent:
  2452. //ORIGINAL LINE: try(java.io.Reader reader = new java.io.BufferedReader(new java.io.FileReader(arg)))
  2453.                     {
  2454.                         java::io::FileReader tempVar3(arg);
  2455.                     java::io::Reader* reader = new java::io::BufferedReader(&tempVar3);
  2456.                         jDatalog->execute(reader, qo);
  2457.                     delete reader;
  2458.                     }
  2459.                 }
  2460.             }
  2461.  
  2462.         }
  2463. //JAVA TO C++ CONVERTER TODO TASK: There is no equivalent in C++ to Java 'multi-catch' syntax:
  2464.         catch(DatalogException | IOException e)
  2465.         {
  2466.             e::printStackTrace();
  2467.         }
  2468.      } else
  2469.      {
  2470.         // Get input from command line
  2471.         JDatalog* jDatalog = new JDatalog();
  2472.         InputStreamReader tempVar4(System::in);
  2473.         BufferedReader* buffer = new BufferedReader(&tempVar4);
  2474.         cout << "JDatalog: Java Datalog engine\nInteractive mode; type 'exit' to quit." << endl;
  2475.         while(true)
  2476.         {
  2477.             try
  2478.             {
  2479.                 cout << "> ";
  2480.                 wstring line = buffer->readLine();
  2481.                 if(line == "")
  2482.                 {
  2483.                     break; // EOF
  2484.                 }
  2485.                 line = StringHelper::trim(line);
  2486.                 if(line.equalsIgnoreCase("exit"))
  2487.                 {
  2488.                     break;
  2489.                 } else if(line.equalsIgnoreCase("dump"))
  2490.                 {
  2491.                     jDatalog->dump(System::out);
  2492.                     continue;
  2493.                 } else if(line.equalsIgnoreCase("validate"))
  2494.                 {
  2495.                     jDatalog->validate();
  2496.                     cout << "OK." << endl; // exception not thrown
  2497.                     continue;
  2498.                 }
  2499.  
  2500.                 Collection<unordered_map<wstring, wstring> >* answers = jDatalog->query(line);
  2501.  
  2502.                 // If `answers` is null, the line passed to `jDatalog.query(line)` was a statement that didn't
  2503.                 //      produce any results, like a fact or a rule, rather than a query.
  2504.                 // If `answers` is empty, then it was a query that doesn't have any answers, so the output is "No."
  2505.                 // If `answers` is a list of empty maps, then it was the type of query that only wanted a yes/no
  2506.                 //      answer, like `siblings(alice,bob)?` and the answer is "Yes."
  2507.                 // Otherwise `answers` is a list of all bindings that satisfy the query.
  2508.                 if(answers != nullptr)
  2509.                 {
  2510.                     if(!answers->isEmpty())
  2511.                     {
  2512.                         if(answers->begin()->next()->isEmpty())
  2513.                         {
  2514.                             cout << "Yes." << endl;
  2515.                         } else
  2516.                         {
  2517.                             for(auto answer : answers)
  2518.                             {
  2519. //JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
  2520.                                 cout << JDatalog::toString(answer) << endl;
  2521.                             }
  2522.                         }
  2523.                     } else
  2524.                     {
  2525.                         cout << "No." << endl;
  2526.                     }
  2527.                 }
  2528.  
  2529.             }
  2530. //JAVA TO C++ CONVERTER TODO TASK: There is no equivalent in C++ to Java 'multi-catch' syntax:
  2531.             catch(DatalogException | IOException e)
  2532.             {
  2533.                 e::printStackTrace();
  2534.             }
  2535.         }
  2536.     }
  2537.  
  2538.     /* //This is how you would use the fluent API:
  2539.     try {
  2540.         JDatalog jDatalog = new JDatalog();
  2541.         jDatalog.fact("parent", "a", "aa")
  2542.             .fact("parent", "a", "ab")
  2543.             .fact("parent", "aa", "aaa")
  2544.             .fact("parent", "aa", "aab")
  2545.             .fact("parent", "aaa", "aaaa")
  2546.             .fact("parent", "c", "ca");
  2547.         jDatalog.rule(new Expr("ancestor", "X", "Y"), new Expr("parent", "X", "Z"), new Expr("ancestor", "Z", "Y"))
  2548.             .rule(new Expr("ancestor", "X", "Y"), new Expr("parent", "X", "Y"))
  2549.             .rule(new Expr("sibling", "X", "Y"), new Expr("parent", "Z", "X"), new Expr("parent", "Z", "Y"), new Expr("!=", "X", "Y"))
  2550.             .rule(new Expr("related", "X", "Y"), new Expr("ancestor", "Z", "X"), new Expr("ancestor", "Z", "Y"));
  2551.         Collection<Map<String, String>> answers;
  2552.         // Run a query "who are siblings?"; print the answers
  2553.         answers = jDatalog.query(new Expr("sibling", "A", "B"));
  2554.         System.out.println("Siblings:");
  2555.         answers.stream().forEach(answer -> System.out.println(" -> " + toString(answer)));
  2556.         // Run a query "who are aa's descendants?"; print the answers
  2557.         answers = jDatalog.query(new Expr("ancestor", "aa", "X"));
  2558.         System.out.println("Descendants:");
  2559.         answers.stream().forEach(answer -> System.out.println(" -> " + toString(answer)));
  2560.         // This demonstrates how you would use a built-in predicate in the fluent API.
  2561.         System.out.println("Built-in predicates:");
  2562.         answers = jDatalog.query(new Expr("parent", "aa", "A"), new Expr("parent", "aa", "B"), new Expr("!=", "A", "B"));
  2563.         answers.stream().forEach(answer -> System.out.println(" -> " + toString(answer)));
  2564.         System.out.println("Before Deletion: " + toString(jDatalog.edb));
  2565.         jDatalog.delete(new Expr("parent", "aa", "X"), new Expr("parent", "X", "aaaa")); // deletes parent(aa,aaa) and parent(aaa,aaaa)
  2566.         System.out.println("After Deletion: " + toString(jDatalog.edb));
  2567.         // "who are aa's descendants now?"
  2568.         answers = jDatalog.query(new Expr("ancestor", "aa", "X"));
  2569.         System.out.println("Descendants:");
  2570.         answers.stream().forEach(answer -> System.out.println(" -> " + toString(answer)));
  2571.     } catch (DatalogException e) {
  2572.         e.printStackTrace();
  2573.     } */
  2574.  
  2575.     /* // The JDatalog.query(String) method runs queries directly.
  2576.     try{
  2577.         JDatalog jDatalog = new JDatalog();
  2578.         jDatalog.query("foo(bar). foo(baz).");
  2579.         Collection<Map<String, String>> answers = jDatalog.query("foo(What)?");
  2580.         answers.stream().forEach(answer -> System.out.println(" -> " + toString(answer)));
  2581.     } catch (DatalogException e) {
  2582.         e.printStackTrace();
  2583.     } */
  2584.  
  2585. }
  2586.  
  2587. JDatalog::Profiler::Timer::Timer(vector<long long>& bucket)
  2588. {
  2589.     start = System::currentTimeMillis();
  2590.     this->bucket = bucket;
  2591. }
  2592.  
  2593. long long JDatalog::Profiler::Timer::stop()
  2594. {
  2595.     long long elapsed = (System::currentTimeMillis() - start);
  2596.     bucket.push_back(elapsed);
  2597.     return elapsed;
  2598. }
  2599.  
  2600. Profiler* JDatalog::Profiler::instance;
  2601.  
  2602. JDatalog::Profiler::Profiler()
  2603. {
  2604.     buckets = new ConcurrentHashMap<wstring, vector<long long> >();
  2605. }
  2606.  
  2607. Profiler* JDatalog::Profiler::getInstance()
  2608. {
  2609.     if(instance == nullptr)
  2610.     {
  2611.         instance = new Profiler();
  2612.     }
  2613.     return instance;
  2614. }
  2615.  
  2616. void JDatalog::Profiler::reset()
  2617. {
  2618.     getInstance()->buckets.clear();
  2619. }
  2620.  
  2621. JDatalog::Profiler::Timer* JDatalog::Profiler::getTimer(const wstring& name)
  2622. {
  2623.     vector<long long> bucket = getInstance()->getBucket(name);
  2624.     return new Timer(bucket);
  2625. }
  2626.  
  2627. vector<long long> JDatalog::Profiler::getBucket(const wstring& name)
  2628. {
  2629.     if(buckets.find(name) == buckets.end())
  2630.     {
  2631.         vector<long long> list = Collections::synchronizedList(vector<long long>(NUM_RUNS));
  2632.         buckets.try_emplace(name, list);
  2633.     }
  2634.     return buckets[name];
  2635. }
  2636.  
  2637. double JDatalog::Profiler::average(const wstring& name)
  2638. {
  2639.     vector<long long> times = getInstance()->getBucket(name);
  2640.     if(times.empty())
  2641.     {
  2642.         return 0;
  2643.     }
  2644.     {
  2645. lock_guard<mutex> lock(times);
  2646.         long long sum = 0;
  2647.         for(* : :optional<long long> time : times)
  2648.         {
  2649.             sum += time;
  2650.         }
  2651.         return static_cast<double>(sum) / times.size();
  2652.     }
  2653. }
  2654.  
  2655. long long JDatalog::Profiler::total(const wstring& name)
  2656. {
  2657.     vector<long long> times = getInstance()->getBucket(name);
  2658.     long long sum = 0;
  2659.     {
  2660. lock_guard<mutex> lock(times);
  2661.         for(* : :optional<long long> time : times)
  2662.         {
  2663.             sum += time;
  2664.         }
  2665.     }
  2666.     return sum;
  2667. }
  2668.  
  2669. int JDatalog::Profiler::count(const wstring& name)
  2670. {
  2671.     return getInstance()->getBucket(name).size();
  2672. }
  2673.  
  2674. double JDatalog::Profiler::stdDev(const wstring& name)
  2675. {
  2676.     // I'm sure I'm going to be really embarrased when I figure out
  2677.     // why the stdDev looks so strange...
  2678.     vector<long long> times = getInstance()->getBucket(name);
  2679.     {
  2680. lock_guard<mutex> lock(times);
  2681.         double avg = average(name);
  2682.         double sumSq = 0.0;
  2683.         for(* : :optional<long long> time : times)
  2684.         {
  2685.             sumSq += (time - avg) * (time - avg);
  2686.         }
  2687.         double variance = sumSq / times.size();
  2688.         return sqrt(variance);
  2689.     }
  2690. }
  2691.  
  2692. Set<wstring>* JDatalog::Profiler::keySet()
  2693. {
  2694.     return getInstance()->buckets.keySet();
  2695. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement