dlvhex  2.5.0
testsuite/TestPlugin.cpp
Go to the documentation of this file.
00001 /* dlvhex -- Answer-Set Programming with external interfaces.
00002  * Copyright (C) 2005, 2006, 2007 Roman Schindlauer
00003  * Copyright (C) 2006, 2007, 2008, 2009, 2010 Thomas Krennwallner
00004  * Copyright (C) 2009, 2010 Peter Schüller
00005  * 
00006  * This file is part of dlvhex.
00007  *
00008  * dlvhex is free software; you can redistribute it and/or modify it
00009  * under the terms of the GNU Lesser General Public License as
00010  * published by the Free Software Foundation; either version 2.1 of
00011  * the License, or (at your option) any later version.
00012  *
00013  * dlvhex is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with dlvhex; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00021  * 02110-1301 USA.
00022  */
00023 
00024 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif // HAVE_CONFIG_H
00039 
00040 #if defined(HAVE_OWLCPP)
00041 #include "owlcpp/rdf/triple_store.hpp"
00042 #include "owlcpp/io/input.hpp"
00043 #include "owlcpp/io/catalog.hpp"
00044 #include "owlcpp/terms/node_tags_owl.hpp"
00045 #endif 
00046 
00047 
00048 #include "dlvhex2/ExternalLearningHelper.h"
00049 #include "dlvhex2/ComfortPluginInterface.h"
00050 #include "dlvhex2/Term.h"
00051 #include "dlvhex2/Registry.h"
00052 #include "dlvhex2/ProgramCtx.h"
00053 #include "dlvhex2/Printer.h"
00054 
00055 #include <boost/foreach.hpp>
00056 #include <boost/functional/hash.hpp>
00057 #include <boost/program_options.hpp>
00058 #include <boost/range.hpp>
00059 #include <boost/filesystem.hpp>
00060 #include <boost/lexical_cast.hpp>
00061 
00062 #include <string>
00063 #include <sstream>
00064 #include <iostream>
00065 #include <map>
00066 
00067 #include <cstdio>
00068 #include <cassert>
00069 
00070 DLVHEX_NAMESPACE_BEGIN
00071 
00072 class TestAAtom:
00073   public ComfortPluginAtom
00074 {
00075 public:
00076     TestAAtom():
00077     ComfortPluginAtom("testA")
00078     {
00079         addInputPredicate();
00080         setOutputArity(1);
00081     }
00082 
00083     virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer)
00084     {
00085         ComfortTuple tu;
00086         if( query.interpretation.empty() )
00087     {
00088             tu.push_back(ComfortTerm::createConstant("foo"));
00089     }
00090     else
00091     {
00092             tu.push_back(ComfortTerm::createConstant("bar"));
00093     }
00094 
00095         answer.insert(tu);
00096     }
00097 };
00098 
00099 class TestBAtom: 
00100   public ComfortPluginAtom
00101 {
00102 public:
00103     TestBAtom():
00104     ComfortPluginAtom("testB")
00105     {
00106         addInputPredicate();
00107         addInputPredicate();
00108         setOutputArity(1);
00109     }
00110 
00111     virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer)
00112     {
00113         if( query.interpretation.size() <= 1 )
00114         {
00115       ComfortTuple tu;
00116       tu.push_back(ComfortTerm::createConstant("bar"));
00117       answer.insert(tu);
00118         }
00119     else if( query.interpretation.size() > 1 )
00120         {
00121       ComfortTuple tu;
00122       tu.push_back(ComfortTerm::createConstant("foo"));
00123       answer.insert(tu);
00124         }
00125     }
00126 };
00127 
00128 class TestCAtom:
00129   public ComfortPluginAtom
00130 {
00131 public:
00132   TestCAtom():
00133     ComfortPluginAtom("testC")
00134   {
00135     addInputPredicate();
00136     setOutputArity(1);
00137   }
00138   
00139   virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer)
00140   {
00141     assert(query.input.size() > 0);
00142     assert(query.input[0].isConstant());
00143 
00144     // was: std::string t = "-" + query.input[0].strval;
00145     std::string t = query.input[0].strval;
00146     
00147     ComfortInterpretation proj;
00148     query.interpretation.matchPredicate(t, proj);
00149 
00150     for(ComfortInterpretation::const_iterator it = proj.begin();
00151         it != proj.end(); ++it)
00152     {
00153       const ComfortAtom& at = *it;
00154       ComfortTuple::const_iterator itt = at.tuple.begin();
00155       assert(itt != at.tuple.end());
00156       // skip predicate
00157       itt++;
00158       while( itt != at.tuple.end() )
00159       {
00160         // add each constant of the atom as separate output tuple
00161         // so foo(a,b,c) will end up as three tuples [a], [b], and [c]
00162         ComfortTuple tu;
00163         tu.push_back(*itt);
00164         answer.insert(tu);
00165         itt++;
00166       }
00167     }
00168   }
00169 };
00170 
00171 // this is no comfortplugin atom as we don't need comfort here
00172 class TestZeroArityAtom:
00173   public PluginAtom
00174 {
00175 protected:
00176     bool succeed;
00177 public:
00178   TestZeroArityAtom(const std::string& name, bool succeed):
00179     PluginAtom(name, true),
00180     succeed(succeed)
00181   {
00182     // no inputs
00183     setOutputArity(0);
00184   }
00185   
00186   virtual void retrieve(const Query&, Answer& answer)
00187   {
00188         if( succeed )
00189         {
00190             // succeed by returning an empty tuple
00191             answer.get().push_back(Tuple());
00192         }
00193         else
00194         {
00195             // fail by returning no tuple (but mark answer as set)
00196             answer.use();
00197         }
00198   }
00199 };
00200 
00201 # if 0
00202 // comfort implementation
00203 class TestConcatAtom:
00204   public ComfortPluginAtom
00205 {
00206 public:
00207   TestConcatAtom():
00208     ComfortPluginAtom("testConcat", true) // monotonic, and no predicate inputs anyway
00209   {
00210     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
00211     addInputTuple();
00212     setOutputArity(1);
00213   }
00214 
00215   virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer)
00216   {
00217     std::stringstream s;
00218 
00219     BOOST_FOREACH(const ComfortTerm& t, query.input)
00220     {
00221       if( t.isInteger() )
00222         s << t.intval;
00223       else if( t.isConstant() )
00224         s << t.strval;
00225       else
00226         throw PluginError("encountered unknown term type!");
00227     }
00228     
00229     ComfortTuple tu;
00230     tu.push_back(ComfortTerm::createConstant(s.str()));
00231     answer.insert(tu);
00232   }
00233 };
00234 #else
00235 // non-comfort implementation
00236 class TestConcatAtom:
00237   public PluginAtom
00238 {
00239 public:
00240   TestConcatAtom():
00241     PluginAtom("testConcat", true) // monotonic, as there is no predicate input anyway
00242   {
00243     addInputTuple();
00244     setOutputArity(1);
00245 
00246     prop.functional = true;
00247   }
00248 
00249   virtual void retrieve(const Query& query, Answer& answer)
00250   {
00251     std::stringstream s;
00252 
00253         bool hasStrings = false;
00254     BOOST_FOREACH(ID tid, query.input)
00255     {
00256             assert(tid.isTerm());
00257       if( tid.isIntegerTerm() )
00258         s << tid.address;
00259       else if( tid.isConstantTerm() )
00260             {
00261                 const std::string& str = registry->getTermStringByID(tid);
00262                 if( str[0] == '"' )
00263                 {
00264                     hasStrings = true;
00265                     s << str.substr(1,str.size()-2);
00266                 }
00267                 else
00268                 {
00269                     s << str;
00270                 }
00271             }
00272       else
00273         throw PluginError("encountered unknown term type!");
00274     }
00275 
00276     // check if the result is an integer
00277     try
00278     {
00279         int intval = boost::lexical_cast<short>(s.str());
00280                 Tuple tu;
00281                 tu.push_back(ID::termFromInteger(intval));
00282                 answer.get().push_back(tu);
00283     }
00284     catch(const boost::bad_lexical_cast &)
00285     {
00286             // not an integer
00287             Term resultterm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, s.str());
00288             if( hasStrings )
00289                 resultterm.symbol = "\"" + resultterm.symbol + "\"";
00290           Tuple tu;
00291           tu.push_back(registry->storeTerm(resultterm));
00292             // the next line would also work and be more efficient, but the above line tests more
00293           //tu.push_back(registry->storeConstOrVarTerm(resultterm));
00294           answer.get().push_back(tu);
00295     }
00296   }
00297 };
00298 #endif
00299 
00300 class TestConcatAllAtom:
00301   public PluginAtom
00302 {
00303 public:
00304   TestConcatAllAtom():
00305     PluginAtom("testConcatAll", false) // monotonic, as there is no predicate input anyway
00306   {
00307     addInputPredicate();
00308     setOutputArity(1);
00309 
00310     prop.functional = true;
00311   }
00312 
00313   virtual void retrieve(const Query& query, Answer& answer)
00314   {
00315     std::stringstream s;
00316 
00317 //      RawPrinter printer(s, registry);
00318     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
00319     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
00320     while (en < en_end){
00321 //          printer.print(registry->ogatoms.getIDByAddress(*en));
00322             s << *en << ";";
00323       en++;
00324     }
00325 
00326     // check if the result is an integer
00327     try
00328     {
00329         int intval = boost::lexical_cast<short>(s.str());
00330                 Tuple tu;
00331                 tu.push_back(ID::termFromInteger(intval));
00332                 answer.get().push_back(tu);
00333     }
00334     catch(const boost::bad_lexical_cast &)
00335     {
00336             // not an integer
00337             Term resultterm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + s.str() + "\"");
00338           Tuple tu;
00339           tu.push_back(registry->storeTerm(resultterm));
00340             // the next line would also work and be more efficient, but the above line tests more
00341           //tu.push_back(registry->storeConstOrVarTerm(resultterm));
00342           answer.get().push_back(tu);
00343     }
00344     }
00345 };
00346 
00347 class TestListDomainAtom:
00348   public PluginAtom
00349 {
00350 public:
00351   TestListDomainAtom():
00352     PluginAtom("testListDomain", true) // monotonic, as there is no predicate input anyway
00353   {
00354     addInputTuple();
00355     setOutputArity(1);
00356 
00357     prop.functional = true;
00358   }
00359 
00360   std::vector<std::string> permute(std::vector<std::string> input){
00361   
00362     if (input.size() > 0){
00363         std::vector<std::string> res;
00364         for (uint32_t i = 0; i < input.size(); ++i){
00365             DBGLOG(DBG, "Choosing " << i);
00366             std::vector<std::string> i2 = input;
00367             i2.erase(i2.begin() + i);
00368             BOOST_FOREACH (std::string subperm, permute(i2)){
00369                 std::stringstream ss;
00370                 ss << input[i] << (subperm.length() > 0 ? ";" : "") << subperm;
00371                 DBGLOG(DBG, "Permutation: " << ss.str());
00372                 res.push_back(ss.str());
00373 
00374                 ss.str("");
00375                 ss << subperm;
00376                 DBGLOG(DBG, "Permutation: " << ss.str());
00377                 res.push_back(ss.str());
00378             }
00379         }
00380         return res;
00381     }else{
00382         input.push_back("");
00383         return input;
00384     }
00385   }
00386 
00387   virtual void retrieve(const Query& query, Answer& answer)
00388   {
00389     const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString();
00390 
00391     // extract the list elements
00392     DBGLOG(DBG, "Computing elements in " << str);
00393     std::vector<std::string> elements;
00394     std::stringstream element;
00395     for (uint32_t i = 0; i <= str.length(); i++){
00396         if (str[i] == ';' || str[i] == '\0'){
00397             DBGLOG(DBG, "Delimiter detected; element: " << element.str());
00398             if (element.str().length() > 0) elements.push_back(element.str());
00399             element.str("");
00400         }else{
00401             DBGLOG(DBG, "Consuming character " << str[i]);
00402             element << str[i];
00403         }
00404     }
00405 
00406     // compute all permutations and return them
00407     DBGLOG(DBG, "Computing permutations over " << elements.size() << " elements");
00408     BOOST_FOREACH (std::string perm, permute(elements)){
00409       Term t(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + perm + "\"");
00410       Tuple tu;
00411       tu.push_back(registry->storeTerm(t));
00412       answer.get().push_back(tu);
00413     }
00414   }
00415 };
00416 
00417 class TestListConcatAtom:
00418   public PluginAtom
00419 {
00420 public:
00421   TestListConcatAtom():
00422     PluginAtom("testListConcat", true) // monotonic, as there is no predicate input anyway
00423   {
00424     addInputTuple();
00425     setOutputArity(1);
00426 
00427     prop.functional = true;
00428   }
00429 
00430   virtual void retrieve(const Query& query, Answer& answer)
00431   {
00432     std::stringstream ss;
00433 
00434     BOOST_FOREACH(ID tid, query.input)
00435     {
00436             assert(tid.isTerm());
00437       if( tid.isIntegerTerm() )
00438         ss << tid.address;
00439       else if( tid.isConstantTerm() )
00440             {
00441                 const std::string& str = registry->terms.getByID(tid).getUnquotedString();
00442                 if (ss.str().length() > 0) ss << ";";
00443                 ss << str;
00444             }
00445       else
00446         throw PluginError("encountered unknown term type!");
00447     }
00448     
00449         Term resultterm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + ss.str() + "\"");
00450     Tuple tu;
00451     tu.push_back(registry->storeTerm(resultterm));
00452 
00453     answer.get().push_back(tu);
00454   }
00455 };
00456 
00457 class TestListLengthAtom:
00458   public PluginAtom
00459 {
00460 public:
00461   TestListLengthAtom():
00462     PluginAtom("testListLength", true)
00463   {
00464     addInputConstant();
00465     addInputConstant();
00466     setOutputArity(1);
00467 
00468     prop.functional = true;
00469   }
00470 
00471   virtual void retrieve(const Query& query, Answer& answer)
00472   {
00473     const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString();
00474     int len = 0;
00475     if (str.length() > 0) len++;
00476     for (uint32_t i = 0; i < str.length(); i++){
00477         if (str[i] == ';') len++;
00478     }
00479 
00480     Tuple tu;
00481     tu.push_back(ID::termFromInteger(len / query.input[1].address));
00482     answer.get().push_back(tu);
00483   }
00484 };
00485 
00486 
00487 class TestListSplitAtom:
00488   public PluginAtom
00489 {
00490 public:
00491   TestListSplitAtom():
00492     PluginAtom("testListSplit", true) // monotonic, as there is no predicate input anyway
00493   {
00494     addInputConstant();
00495     addInputConstant();
00496     setOutputArity(2);
00497 
00498     prop.functional = true;
00499     prop.wellorderingStrlen.insert(std::pair<int, int>(0, 0));
00500     prop.wellorderingStrlen.insert(std::pair<int, int>(0, 1));
00501   }
00502 
00503   virtual void retrieve(const Query& query, Answer& answer)
00504   {
00505     const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString();
00506     int cnt = query.input[1].address;
00507 
00508     std::stringstream substr1, substr2;
00509     int nr = 0;
00510     for (uint32_t i = 0; i < str.length(); i++){
00511         if (str[i] == ';'){
00512             nr++;
00513             if (nr == cnt) continue;
00514         }
00515         if (nr >= cnt) substr2 << str[i];
00516         else substr1 << str[i];
00517     }
00518 
00519     Term t1(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr1.str() + "\"");
00520     Term t2(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr2.str() + "\"");
00521     Tuple tu;
00522     tu.push_back(registry->storeTerm(t1));
00523     tu.push_back(registry->storeTerm(t2));
00524     answer.get().push_back(tu);
00525   }
00526 };
00527 
00528 
00529 class TestListSplitHalfAtom:
00530   public PluginAtom
00531 {
00532 public:
00533   TestListSplitHalfAtom():
00534     PluginAtom("testListSplitHalf", true) // monotonic, as there is no predicate input anyway
00535   {
00536     addInputConstant();
00537     setOutputArity(2);
00538 
00539     prop.functional = true;
00540     prop.wellorderingStrlen.insert(std::pair<int, int>(0, 0));
00541     prop.wellorderingStrlen.insert(std::pair<int, int>(0, 1));
00542   }
00543 
00544   virtual void retrieve(const Query& query, Answer& answer)
00545   {
00546     const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString();
00547 
00548     int len = 0;
00549     if (str.length() > 0) len++;
00550     for (uint32_t i = 0; i < str.length(); i++){
00551         if (str[i] == ';') len++;
00552     }
00553 
00554     int cnt = len / 2;
00555 
00556     std::stringstream substr1, substr2;
00557     int nr = 0;
00558     for (uint32_t i = 0; i < str.length(); i++){
00559         if (str[i] == ';'){
00560             nr++;
00561             if (nr == cnt) continue;
00562         }
00563         if (nr >= cnt) substr2 << str[i];
00564         else substr1 << str[i];
00565     }
00566 
00567     Term t1(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr1.str() + "\"");
00568     Term t2(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr2.str() + "\"");
00569     Tuple tu;
00570     tu.push_back(registry->storeTerm(t1));
00571     tu.push_back(registry->storeTerm(t2));
00572     answer.get().push_back(tu);
00573   }
00574 };
00575 
00576 class TestListMergeAtom:
00577   public PluginAtom
00578 {
00579 public:
00580   TestListMergeAtom():
00581     PluginAtom("testListMerge", true) // monotonic, as there is no predicate input anyway
00582   {
00583     addInputConstant();
00584     addInputConstant();
00585     addInputConstant();
00586     setOutputArity(2);
00587 
00588     prop.functional = true;
00589   }
00590 
00591   virtual void retrieve(const Query& query, Answer& answer)
00592   {
00593     const std::string& str1 = registry->terms.getByID(query.input[1]).getUnquotedString();
00594     const std::string& str2 = registry->terms.getByID(query.input[2]).getUnquotedString();
00595 
00596     std::stringstream element1, element2, merged;
00597     uint32_t c1 = 0;
00598     uint32_t c2 = 0;
00599     
00600     std::vector<std::string> list1, list2;
00601     
00602     // extract list 1
00603     while(str1[c1] != '\0'){
00604         if (str1[c1] == ';') c1++;
00605         element1.str("");
00606         while (str1[c1] != ';' && str1[c1] != '\0'){
00607             element1 << str1[c1];
00608             c1++;
00609         }
00610         list1.push_back(element1.str());
00611     }
00612     
00613     // extract list 2
00614     while(str2[c2] != '\0'){
00615         if (str2[c2] == ';') c2++;
00616         element2.str("");
00617         while (str2[c2] != ';' && str2[c2] != '\0'){
00618             element2 << str2[c2];
00619             c2++;
00620         }
00621         list2.push_back(element2.str());
00622     }
00623     
00624     // merge
00625     c1 = 0;
00626     c2 = 0;
00627     while (c1 < list1.size() || c2 < list2.size()){
00628         if (c1 > 0 || c2 > 0) merged << ";";
00629         if (c1 == list1.size()){
00630             merged << list2[c2];
00631             c2++;
00632         }
00633         else if (c2 == list2.size()){
00634             merged << list1[c1];
00635             c1++;
00636         }
00637         else if (list1[c1].compare(list2[c2]) < 0){
00638             merged << list1[c1];
00639             c1++;
00640         }
00641         else{
00642             assert(list1[c1].compare(list2[c2]) >= 0);
00643             merged << list2[c2];
00644             c2++;
00645         }
00646     }
00647 
00648     Term t(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + merged.str() + "\"");
00649     Tuple tu;
00650     tu.push_back(query.input[0]);
00651     tu.push_back(registry->storeTerm(t));
00652     answer.get().push_back(tu);
00653   }
00654 };
00655 
00656 class TestSubstrAtom:
00657   public PluginAtom
00658 {
00659 public:
00660   TestSubstrAtom():
00661     PluginAtom("testSubstr", true) // monotonic, as there is no predicate input anyway
00662   {
00663     addInputConstant();
00664     addInputConstant();
00665     addInputConstant();
00666     setOutputArity(1);
00667 
00668     prop.functional = true;
00669     prop.wellorderingStrlen.insert(std::pair<int, int>(0, 0));
00670   }
00671 
00672   virtual void retrieve(const Query& query, Answer& answer)
00673   {
00674     if (!query.input[1].isIntegerTerm()) throw GeneralError("testSubstr expects an integer as its second argument");
00675     if (!query.input[2].isIntegerTerm()) throw GeneralError("testSubstr expects an integer as its third argument");
00676 
00677     try{
00678       int start = query.input[1].address;
00679       int len = query.input[2].address;
00680       std::string str = registry->terms.getByID(query.input[0]).getUnquotedString();
00681       int clen = str.length() - start;
00682       if (clen < len) len = clen;
00683       std::string substring = str.substr(start, len);
00684       if (registry->terms.getByID(query.input[0]).isQuotedString()) substring = "\"" + substring + "\"";
00685       ID resultterm = registry->storeConstantTerm(substring);
00686       Tuple tu;
00687       tu.push_back(resultterm);
00688       answer.get().push_back(tu);
00689     }catch(...){
00690       // specified substring is out of bounds
00691       // return nothing
00692     }
00693   }
00694 };
00695 
00696 class TestSmallerThanAtom:
00697   public PluginAtom
00698 {
00699 public:
00700   TestSmallerThanAtom():
00701     PluginAtom("testSmallerThan", true) // monotonic, as there is no predicate input anyway
00702   {
00703     addInputConstant();
00704     addInputConstant();
00705     setOutputArity(0);
00706 
00707     prop.functional = true;
00708   }
00709 
00710   virtual void retrieve(const Query& query, Answer& answer)
00711   {
00712     if (query.input[0].isIntegerTerm() && query.input[1].isIntegerTerm()){
00713       // integer comparison
00714       if (query.input[0].address < query.input[1].address){
00715         Tuple tu;
00716         answer.get().push_back(tu);
00717       }
00718     }else{
00719       // string comparison
00720       std::string str1 = registry->terms.getByID(query.input[0]).getUnquotedString();
00721       std::string str2 = registry->terms.getByID(query.input[1]).getUnquotedString();
00722       if (str1.compare(str2) < 0){
00723         Tuple tu;
00724         answer.get().push_back(tu);
00725       }
00726     }
00727   }
00728 };
00729 
00730 class TestFirstAtom:
00731   public PluginAtom
00732 {
00733 public:
00734   TestFirstAtom():
00735     PluginAtom("testFirst", true) // monotonic, as there is no predicate input anyway
00736   {
00737     addInputConstant();
00738     setOutputArity(2);
00739 
00740     prop.functional = true;
00741   }
00742 
00743   virtual void retrieve(const Query& query, Answer& answer)
00744   {
00745     std::string str = registry->terms.getByID(query.input[0]).getUnquotedString();
00746 
00747     Tuple tu;
00748     tu.push_back(registry->storeConstantTerm("\"" + (str.length() >= 1 ? str.substr(0, 1) : "") + "\"", true));
00749     tu.push_back(registry->storeConstantTerm("\"" + (str.length() >= 1 ? str.substr(1) : "") + "\"", true));
00750     answer.get().push_back(tu);
00751   }
00752 };
00753 
00754 class TestPushAtom:
00755   public PluginAtom
00756 {
00757 public:
00758   TestPushAtom():
00759     PluginAtom("testPush", true) // monotonic, as there is no predicate input anyway
00760   {
00761     addInputConstant();
00762     addInputConstant();
00763     setOutputArity(1);
00764 
00765     prop.functional = true;
00766   }
00767 
00768   virtual void retrieve(const Query& query, Answer& answer)
00769   {
00770     std::string str1 = registry->terms.getByID(query.input[0]).getUnquotedString();
00771     std::string str2 = registry->terms.getByID(query.input[1]).getUnquotedString();
00772 
00773     Tuple tu;
00774     tu.push_back(registry->storeConstantTerm("\"" + str1 + str2 + "\""));
00775     answer.get().push_back(tu);
00776   }
00777 };
00778 
00779 class TestMoveAtom:
00780   public PluginAtom
00781 {
00782 public:
00783   TestMoveAtom():
00784     PluginAtom("testMove", true) // monotonic, as there is no predicate input anyway
00785   {
00786     addInputPredicate();
00787     addInputConstant();
00788     addInputConstant();
00789     addInputConstant();
00790     setOutputArity(2);
00791 
00792     prop.functional = true;
00793   }
00794 
00795   virtual void retrieve(const Query& query, Answer& answer)
00796   {
00797     ID state = query.input[1];
00798     std::string ichar = registry->terms.getByID(query.input[2]).getUnquotedString();
00799     std::string schar = registry->terms.getByID(query.input[3]).getUnquotedString();
00800 
00801     // go through the tuples of the accessability relation
00802     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
00803     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
00804     while (en < en_end){
00805       const OrdinaryAtom& oatom = registry->ogatoms.getByAddress(*en);
00806       // match?
00807       if (oatom.tuple[1] == state &&
00808           registry->terms.getByID(oatom.tuple[2]).getUnquotedString() == ichar &&
00809           registry->terms.getByID(oatom.tuple[3]).getUnquotedString() == schar){
00810 
00811           // go to this state
00812           Tuple tu;
00813           tu.push_back(oatom.tuple[4]);
00814           tu.push_back(oatom.tuple[5]);
00815           answer.get().push_back(tu);
00816       }
00817       en++;
00818     }
00819   }
00820 };
00821 
00822 
00823 class TestStrlenAtom:
00824   public PluginAtom
00825 {
00826 public:
00827   TestStrlenAtom():
00828     PluginAtom("testStrlen", true) // monotonic, as there is no predicate input anyway
00829   {
00830     addInputConstant();
00831     setOutputArity(1);
00832 
00833     prop.functional = true;
00834     prop.finiteFiber = true;
00835   }
00836 
00837   virtual void retrieve(const Query& query, Answer& answer)
00838   {
00839     Tuple tu;
00840     tu.push_back(ID::termFromInteger(registry->terms.getByID(query.input[0]).getUnquotedString().length()));
00841     answer.get().push_back(tu);
00842   }
00843 };
00844 
00845 class TestSetMinusAtom:
00846   public ComfortPluginAtom
00847 {
00848 public:
00849   TestSetMinusAtom():
00850     // this nonmonotonicity is very important,
00851     // because this atom is definitively nonmonotonic
00852     // and there are testcases that fail if this is set to true!
00853     ComfortPluginAtom("testSetMinus", false)
00854   {
00855     addInputPredicate();
00856     addInputPredicate();
00857     setOutputArity(1);
00858     prop.monotonicInputPredicates.insert(0);
00859     prop.antimonotonicInputPredicates.insert(1);
00860     prop.finiteOutputDomain.insert(0);
00861   }
00862 
00863   virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer)
00864   {
00865     assert(query.input.size() == 2);
00866     if( !query.input[0].isConstant() || !query.input[1].isConstant() )
00867       throw PluginError("need constant predicates as input to testSetMinus!");
00868 
00869     // extract predicates from input
00870     std::vector<ComfortInterpretation> psets;
00871     psets.resize(2); // allocate exactly two sets
00872     query.interpretation.matchPredicate(query.input[0].strval, psets[0]);
00873     query.interpretation.matchPredicate(query.input[1].strval, psets[1]);
00874     
00875     // extract terms from predicate sets
00876     std::vector<std::set<ComfortTerm> > tsets;
00877     BOOST_FOREACH(const ComfortInterpretation& pset, psets)
00878     {
00879       // put result into termsets
00880       tsets.push_back(std::set<ComfortTerm>());
00881       std::set<ComfortTerm>& tset = tsets.back();
00882 
00883       // extract (assume unary predicates)
00884       BOOST_FOREACH(const ComfortAtom& pred, pset)
00885       {
00886         if( pred.tuple.size() != 2 )
00887                 {
00888                     std::stringstream s;
00889                     s << "can only process atoms of arity 2 with testSetMinus" <<
00890                         "(got " << printrange(pred.tuple) << ")";
00891           throw PluginError(s.str());
00892                 }
00893         // simply insert the argument into the set
00894         tset.insert(pred.tuple[1]);
00895       }
00896     }
00897 
00898     // do set difference between tsets
00899     std::set<ComfortTerm> result;
00900     std::insert_iterator<std::set<ComfortTerm> > 
00901       iit(result, result.begin());
00902     std::set_difference(
00903         tsets[0].begin(), tsets[0].end(),
00904         tsets[1].begin(), tsets[1].end(),
00905         iit);
00906     BOOST_FOREACH(const ComfortTerm& t, result)
00907     {
00908       ComfortTuple tu;
00909       tu.push_back(t);
00910       answer.insert(tu);
00911     }
00912   }
00913 
00914   class EAHeuristics : public ExternalAtomEvaluationHeuristics{
00915   public:
00916     EAHeuristics(RegistryPtr reg) : ExternalAtomEvaluationHeuristics(reg) {}
00917     bool doEvaluate(const ExternalAtom& eatom, InterpretationConstPtr eatomMask, InterpretationConstPtr programMask, InterpretationConstPtr partialAssignment, InterpretationConstPtr assigned, InterpretationConstPtr changed) { return true; }
00918   };
00919 
00920   class EAHeuristicsFactory : public ExternalAtomEvaluationHeuristicsFactory{
00921   public:
00922     ExternalAtomEvaluationHeuristicsPtr createHeuristics(RegistryPtr reg){ return ExternalAtomEvaluationHeuristicsPtr(new EAHeuristics(reg)); }
00923   };
00924 
00925   bool providesCustomExternalAtomEvaluationHeuristicsFactory() const { return true; }
00926 
00927   ExternalAtomEvaluationHeuristicsFactoryPtr getCustomExternalAtomEvaluationHeuristicsFactory() const
00928     { return ExternalAtomEvaluationHeuristicsFactoryPtr(new EAHeuristicsFactory()); }
00929 
00930 };
00931 
00932 class TestSetMinusNonComfortAtom:   // tests user-defined external learning
00933   public PluginAtom
00934 {
00935 public:
00936   TestSetMinusNonComfortAtom():
00937     PluginAtom("testSetMinusNonComfort", false) // monotonic, and no predicate inputs anyway
00938   {
00939     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
00940     addInputPredicate();
00941     addInputPredicate();
00942     prop.monotonicInputPredicates.insert(0);
00943     prop.antimonotonicInputPredicates.insert(1);
00944     setOutputArity(1);
00945   }
00946 
00947   virtual void retrieve(const Query& query, Answer& answer)
00948   {
00949     static std::map<std::string, ID> ruleIDs;
00950 
00951     // find relevant input
00952     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
00953     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
00954 
00955     std::vector<Tuple> tuples1;
00956     std::vector<Tuple> tuples2;
00957     while (en < en_end){
00958 
00959         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
00960         Tuple tu;
00961         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
00962             tu.push_back(atom.tuple[i]);
00963         }
00964         if (atom.tuple[0] == query.input[0]){
00965             tuples1.push_back(tu);
00966         }
00967         if (atom.tuple[0] == query.input[1]){
00968             tuples2.push_back(tu);
00969         }
00970         en++;
00971     }
00972 
00973     // Learning of the nogoods
00974     BOOST_FOREACH (Tuple t, tuples1){
00975         if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){
00976             answer.get().push_back(t);
00977         }
00978     }
00979   }
00980 };
00981 
00982 class TestSetMinusPartialAtom:  // tests user-defined external learning
00983   public PluginAtom
00984 {
00985 public:
00986   TestSetMinusPartialAtom():
00987     PluginAtom("testSetMinusPartial", false) // monotonic, and no predicate inputs anyway
00988   {
00989     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
00990     addInputPredicate();
00991     addInputPredicate();
00992     prop.monotonicInputPredicates.insert(0);
00993     prop.antimonotonicInputPredicates.insert(1);
00994         prop.setProvidesPartialAnswer(true);
00995     setOutputArity(1);
00996   }
00997 
00998   virtual void retrieve(const Query& query, Answer& answer)
00999   {
01000     static std::map<std::string, ID> ruleIDs;
01001 
01002     // find relevant input
01003     bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first();
01004     bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end();
01005 
01006     std::vector<Tuple> tuples1true;
01007     std::vector<Tuple> tuples1unknown;
01008     std::vector<Tuple> tuples2true;
01009     std::vector<Tuple> tuples2unknown;
01010     while (en < en_end){
01011         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01012         Tuple tu;
01013         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
01014             tu.push_back(atom.tuple[i]);
01015         }
01016         if (!query.assigned || query.assigned->getFact(*en) ) {
01017             // assigned
01018             if (query.interpretation->getFact(*en) ){
01019                 // assigned to true?
01020                 if (atom.tuple[0] == query.input[0]){
01021                     tuples1true.push_back(tu);
01022                 }
01023                 if (atom.tuple[0] == query.input[1]){
01024                     tuples2true.push_back(tu);
01025                 }
01026             }
01027         }else{
01028             // not assigned
01029             if (atom.tuple[0] == query.input[0]){
01030                 tuples1unknown.push_back(tu);
01031             }
01032             if (atom.tuple[0] == query.input[1]){
01033                 tuples2unknown.push_back(tu);
01034             }
01035         }
01036         en++;
01037     }
01038 
01039     // Learning of the nogoods
01040     BOOST_FOREACH (Tuple t, tuples1true){
01041         if (std::find(tuples2true.begin(), tuples2true.end(), t) != tuples2true.end()){
01042             // true in first predicate, true in second --> false in the result
01043         }else if (std::find(tuples2unknown.begin(), tuples2unknown.end(), t) != tuples2unknown.end()){
01044             // true in first predicate, unknown in second --> unknown in the result
01045             answer.getUnknown().push_back(t);
01046         }else{
01047             // true in first predicate, false in second --> true in the result
01048             answer.get().push_back(t);
01049         }
01050     }
01051     BOOST_FOREACH (Tuple t, tuples1unknown){
01052         if (std::find(tuples2true.begin(), tuples2true.end(), t) == tuples2true.end()){
01053             // unknown in first predicate, false or unknown in second --> unknown in the result
01054             answer.getUnknown().push_back(t);
01055         }
01056     }
01057     // false in the first predicate --> false in the result
01058   }
01059 };
01060 
01061 class TestSetMinusNogoodBasedLearningAtom:  // tests user-defined external learning
01062   public PluginAtom
01063 {
01064 public:
01065   TestSetMinusNogoodBasedLearningAtom():
01066     PluginAtom("testSetMinusNogoodBasedLearning", false) // monotonic, and no predicate inputs anyway
01067   {
01068     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01069     addInputPredicate();
01070     addInputPredicate();
01071     setOutputArity(1);
01072   }
01073 
01074   virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods)
01075   {
01076     static std::map<std::string, ID> ruleIDs;
01077 
01078     // find relevant input
01079     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01080     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01081 
01082     std::vector<Tuple> tuples1;
01083     std::vector<Tuple> tuples2;
01084     while (en < en_end){
01085 
01086         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01087         Tuple tu;
01088         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
01089             tu.push_back(atom.tuple[i]);
01090         }
01091         if (atom.tuple[0] == query.input[0]){
01092             tuples1.push_back(tu);
01093         }
01094         if (atom.tuple[0] == query.input[1]){
01095             tuples2.push_back(tu);
01096         }
01097         en++;
01098     }
01099 
01100     // Learning of the nogoods
01101     BOOST_FOREACH (Tuple t, tuples1){
01102         if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){
01103             answer.get().push_back(t);
01104 
01105             // Test: Learning based on direct definition of nogoods
01106             if (nogoods != NogoodContainerPtr()){
01107                 if (query.ctx->config.getOption("ExternalLearningUser")){
01108                     // learn that presence of t in query.input[0] and absence in query.input[1] implies presence in output
01109                     OrdinaryAtom at1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG);
01110                     at1.tuple.push_back(query.input[0]);
01111                     for (uint32_t i = 0; i < t.size(); ++i) at1.tuple.push_back(t[i]);
01112                     OrdinaryAtom at2(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG);
01113                     at2.tuple.push_back(query.input[1]);
01114                     for (uint32_t i = 0; i < t.size(); ++i) at2.tuple.push_back(t[i]);
01115 
01116                     Nogood nogood;
01117                     nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryGAtom(at1).address, true));
01118                     nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryGAtom(at2).address, false));
01119                     nogood.insert(ExternalLearningHelper::getOutputAtom(query, t, false));
01120                     nogoods->addNogood(nogood);
01121 
01122                     DBGLOG(DBG, "Learned user-defined nogood: " << nogood);
01123                 }else{
01124                     DBGLOG(DBG, "No user-defined learning");
01125                 }
01126             }else{
01127                 DBGLOG(DBG, "No user-defined learning");
01128             }
01129         }
01130     }
01131   }
01132 };
01133 
01134 class TestSetMinusNongroundNogoodBasedLearningAtom: // tests user-defined external learning
01135   public PluginAtom
01136 {
01137 
01138 public:
01139 
01140   TestSetMinusNongroundNogoodBasedLearningAtom():
01141     PluginAtom("testSetMinusNongroundNogoodBasedLearning", false) // monotonic, and no predicate inputs anyway
01142   {
01143     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01144     addInputPredicate();
01145     addInputPredicate();
01146     prop.monotonicInputPredicates.insert(0);
01147     prop.antimonotonicInputPredicates.insert(1);
01148     setOutputArity(1);
01149   }
01150 
01151   virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods)
01152   {
01153     static std::map<std::string, ID> ruleIDs;
01154 
01155     int arity = -1;
01156 
01157     // find relevant input
01158     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01159     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01160 
01161     std::vector<Tuple> tuples1;
01162     std::vector<Tuple> tuples2;
01163     while (en < en_end){
01164 
01165         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01166         arity = atom.tuple.size() - 1;
01167         Tuple tu;
01168         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
01169             tu.push_back(atom.tuple[i]);
01170         }
01171         if (atom.tuple[0] == query.input[0]){
01172             tuples1.push_back(tu);
01173         }
01174         if (atom.tuple[0] == query.input[1]){
01175             tuples2.push_back(tu);
01176         }
01177         en++;
01178     }
01179 
01180     BOOST_FOREACH (Tuple t, tuples1){
01181         if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){
01182             answer.get().push_back(t);
01183         }
01184     }
01185 
01186     // Test: Learning based on direct definition of nogoods
01187     if (nogoods != NogoodContainerPtr() && arity > -1){
01188         if (query.ctx->config.getOption("ExternalLearningUser")){
01189             // learn that presence of t in query.input[0] and absence in query.input[1] implies presence in output
01190             OrdinaryAtom at1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN);
01191             OrdinaryAtom at2(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN);
01192             at1.tuple.push_back(query.input[0]);
01193             at2.tuple.push_back(query.input[1]);
01194             Tuple t;
01195             for (int i = 0; i < arity; ++i){
01196                 std::stringstream var;
01197                 var << "X" << i;
01198                 at1.tuple.push_back(getRegistry()->storeVariableTerm(var.str()));
01199                 at2.tuple.push_back(getRegistry()->storeVariableTerm(var.str()));
01200                 t.push_back(getRegistry()->storeVariableTerm(var.str()));
01201             }
01202 
01203             Nogood nogood;
01204             nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryNAtom(at1).address, true, false));
01205             nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryNAtom(at2).address, false, false));
01206             nogood.insert(NogoodContainer::createLiteral(ExternalLearningHelper::getOutputAtom(query, t, false).address, true, false));
01207             nogoods->addNogood(nogood);
01208 
01209             DBGLOG(DBG, "Learned user-defined nogood: " << nogood);
01210         }else{
01211             DBGLOG(DBG, "No user-defined learning");
01212         }
01213     }else{
01214         DBGLOG(DBG, "No user-defined learning");
01215     }
01216   }
01217 };
01218 
01219 class TestSetMinusRuleBasedLearningAtom:    // tests user-defined external learning
01220   public PluginAtom
01221 {
01222 private:
01223   ProgramCtx* ctx;
01224 
01225 public:
01226   TestSetMinusRuleBasedLearningAtom(ProgramCtx* ctx):
01227     ctx(ctx),
01228     PluginAtom("testSetMinusRuleBasedLearning", false) // monotonic, and no predicate inputs anyway
01229   {
01230     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01231     addInputPredicate();
01232     addInputPredicate();
01233     setOutputArity(1);
01234   }
01235 
01236   virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods)
01237   {
01238     static std::map<std::string, ID> ruleIDs;
01239 
01240     // find relevant input
01241     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01242     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01243 
01244     std::vector<Tuple> tuples1;
01245     std::vector<Tuple> tuples2;
01246     while (en < en_end){
01247 
01248         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01249         Tuple tu;
01250         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
01251             tu.push_back(atom.tuple[i]);
01252         }
01253         if (atom.tuple[0] == query.input[0]){
01254             tuples1.push_back(tu);
01255         }
01256         if (atom.tuple[0] == query.input[1]){
01257             tuples2.push_back(tu);
01258         }
01259         en++;
01260     }
01261 
01262     // Test: Rule-based learning
01263     if (nogoods != NogoodContainerPtr()){
01264         if (ctx->config.getOption("ExternalLearningUser")){
01265             std::string rule = "out(X) :- in1(X), not in2(X).";
01266 
01267             if (ruleIDs.find(rule) == ruleIDs.end()){
01268                 ruleIDs[rule] = ExternalLearningHelper::getIDOfLearningRule(ctx, rule);
01269             }
01270             ID rid = ruleIDs[rule];
01271             if (rid == ID_FAIL){
01272                 DBGLOG(DBG, "Could not learn from rule because parsing failed");
01273                 exit(0);
01274             }else{
01275                 ExternalLearningHelper::learnFromRule(query, rid, ctx, nogoods);
01276             }
01277         }
01278     }
01279 
01280     BOOST_FOREACH (Tuple t, tuples1){
01281         if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){
01282             answer.get().push_back(t);
01283         }
01284     }
01285   }
01286 };
01287 
01288 class TestNonmonAtom:   // tests user-defined external learning
01289   public PluginAtom
01290 {
01291 public:
01292   TestNonmonAtom():
01293     PluginAtom("testNonmon", false) // monotonic, and no predicate inputs anyway
01294   {
01295     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01296     addInputPredicate();
01297     setOutputArity(1);
01298 
01299     prop.finiteOutputDomain.insert(0);
01300   }
01301 
01302   virtual void retrieve(const Query& query, Answer& answer)
01303   {
01304     // find relevant input
01305     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01306     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01307 
01308     std::vector<Tuple> tuples;
01309     while (en < en_end){
01310 
01311         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01312         Tuple tu;
01313         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
01314             tu.push_back(atom.tuple[i]);
01315         }
01316         if (tu.size() != 1) throw PluginError("TestNonmonAtom can only process input predicates with arity 1!");
01317         tuples.push_back(tu);
01318         en++;
01319     }
01320 
01321     Tuple t1, t2;
01322     t1.push_back(ID::termFromInteger(1));
01323     t2.push_back(ID::termFromInteger(2));
01324 
01325     // {} -> {2}, {1} -> {1}, {2} -> {1}, {1,2} -> {1,2}
01326     if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){
01327         answer.get().push_back(t2);
01328     }
01329     if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){
01330         answer.get().push_back(t1);
01331     }
01332     if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){
01333         answer.get().push_back(t1);
01334     }
01335     if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){
01336         answer.get().push_back(t1);
01337         answer.get().push_back(t2);
01338     }
01339   }
01340 };
01341 
01342 class TestNonmon2Atom:  // tests user-defined external learning
01343   public PluginAtom
01344 {
01345 public:
01346   TestNonmon2Atom():
01347     PluginAtom("testNonmon2", false) // monotonic, and no predicate inputs anyway
01348   {
01349     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01350     addInputPredicate();
01351     setOutputArity(1);
01352   }
01353 
01354   virtual void retrieve(const Query& query, Answer& answer)
01355   {
01356     // find relevant input
01357     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01358     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01359 
01360     std::vector<Tuple> tuples;
01361     while (en < en_end){
01362 
01363         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01364         Tuple tu;
01365         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
01366             tu.push_back(atom.tuple[i]);
01367         }
01368         if (tu.size() != 1) throw PluginError("TestNonmon2Atom can only process input predicates with arity 1!");
01369         tuples.push_back(tu);
01370         en++;
01371     }
01372 
01373     Tuple t1, t2;
01374     t1.push_back(ID::termFromInteger(1));
01375     t2.push_back(ID::termFromInteger(2));
01376 
01377     // {} -> {2}, {1} -> {2}, {2} -> {}, {1,2} -> {1,2}
01378     if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){
01379         answer.get().push_back(t2);
01380     }
01381     if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){
01382         answer.get().push_back(t2);
01383     }
01384     if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){
01385     }
01386     if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){
01387         answer.get().push_back(t1);
01388         answer.get().push_back(t2);
01389     }
01390   }
01391 };
01392 
01393 class TestIdAtom:   // tests user-defined external learning
01394   public PluginAtom
01395 {
01396 public:
01397   TestIdAtom():
01398     PluginAtom("id", false) // monotonic
01399   {
01400     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01401     addInputPredicate();
01402     setOutputArity(1);
01403   }
01404 
01405   virtual void retrieve(const Query& query, Answer& answer)
01406   {
01407     // find relevant input
01408     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01409     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01410 
01411     while (en < en_end){
01412 
01413         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01414         if (atom.tuple.size() != 2) throw PluginError("TestIdAtom can only process input predicates with arity 1!");
01415         Tuple tu;
01416         tu.push_back(atom.tuple[1]);
01417         answer.get().push_back(tu);
01418         en++;
01419     }
01420   }
01421 };
01422 
01423 class TestIdpAtom:  // tests user-defined external learning
01424   public PluginAtom
01425 {
01426 public:
01427   TestIdpAtom():
01428     PluginAtom("idp", false) // monotonic
01429   {
01430     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01431     addInputPredicate();
01432     setOutputArity(1);
01433 
01434     prop.setProvidesPartialAnswer(true);
01435   }
01436 
01437   virtual void retrieve(const Query& query, Answer& answer)
01438   {
01439     // find relevant input
01440     bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first();
01441     bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end();
01442 
01443     while (en < en_end){
01444 
01445         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01446         if (atom.tuple.size() != 2) throw PluginError("TestIdpAtom can only process input predicates with arity 1!");
01447 
01448         Tuple tu;
01449         tu.push_back(atom.tuple[1]);
01450         if (query.interpretation->getFact(*en)) answer.get().push_back(tu);
01451         else answer.getUnknown().push_back(tu);
01452         en++;
01453     }
01454   }
01455 };
01456 
01457 class TestIdcAtom:  // tests user-defined external learning
01458   public PluginAtom
01459 {
01460 public:
01461   TestIdcAtom():
01462     PluginAtom("idc", false) // monotonic, and no predicate inputs anyway
01463   {
01464     addInputConstant();
01465     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01466     setOutputArity(1);
01467   }
01468 
01469   virtual void retrieve(const Query& query, Answer& answer)
01470   {
01471     Tuple tu;
01472     tu.push_back(query.input[0]);
01473     answer.get().push_back(tu);
01474   }
01475 };
01476 
01477 class TestNegAtom:  // tests user-defined external learning
01478   public PluginAtom
01479 {
01480 public:
01481   TestNegAtom():
01482     PluginAtom("neg", false)
01483   {
01484     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01485     addInputConstant();
01486     addInputPredicate();
01487     setOutputArity(1);
01488   }
01489 
01490   virtual void retrieve(const Query& query, Answer& answer)
01491   {
01492     // find relevant input
01493     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01494     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01495 
01496     while (en < en_end){
01497 
01498         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01499         if (atom.tuple.size() != 2) throw PluginError("TestNegAtom can only process input predicates with arity 1!");
01500         if (atom.tuple[1] == query.input[0]){
01501             return;
01502         }
01503         en++;
01504     }
01505     Tuple tu;
01506     tu.push_back(query.input[0]);
01507     answer.get().push_back(tu);
01508   }
01509 };
01510 
01511 class TestMinusOneAtom:
01512     public ComfortPluginAtom
01513 {
01514 public:
01515     TestMinusOneAtom():
01516         // monotonic, as only constant inputs
01517         ComfortPluginAtom("testMinusOne", true)
01518     {
01519             addInputConstant();
01520             setOutputArity(1);
01521     }
01522 
01523     virtual void
01524     retrieve(const ComfortQuery& query, ComfortAnswer& answer)
01525     {
01526         assert(query.input.size() == 1);
01527         if( !query.input[0].isInteger() )
01528             throw PluginError("TestMinusOneAtom can only process integer inputs!");
01529 
01530         int i = query.input[0].intval;
01531         if( i > 0 )
01532             i--;
01533 
01534         ComfortTuple t;
01535         t.push_back(ComfortTerm::createInteger(i));
01536         answer.insert(t);
01537     }
01538 };
01539 
01540 
01541 class TestEvenAtom:
01542     public ComfortPluginAtom
01543 {
01544 public:
01545   TestEvenAtom():
01546         ComfortPluginAtom("testEven", false)
01547   {
01548     addInputPredicate();
01549     addInputPredicate();
01550     setOutputArity(0);
01551   }
01552  
01553   virtual void
01554   retrieve(const ComfortQuery& query, ComfortAnswer& answer)
01555   {
01556     // Even is true, iff input predicates hold for even individuals
01557  
01558     if (query.interpretation.size() % 2 == 0)
01559         {
01560             // succeed by returning an empty tuple
01561             answer.insert(ComfortTuple());
01562         }
01563     else
01564         {
01565             // fail by returning no tuple
01566         }
01567   }
01568 };
01569  
01570 class TestOddAtom:
01571     public ComfortPluginAtom
01572 {
01573 public:
01574   TestOddAtom():
01575         ComfortPluginAtom("testOdd", false)
01576   {
01577     addInputPredicate();
01578     addInputPredicate();
01579     setOutputArity(0);
01580   }
01581 
01582   virtual void
01583   retrieve(const ComfortQuery& query, ComfortAnswer& answer)
01584   {
01585     if (query.interpretation.size() % 2 != 0)
01586         {
01587             // succeed by returning an empty tuple
01588             answer.insert(ComfortTuple());
01589         }
01590     else
01591         {
01592             // fail by returning no tuple
01593         }
01594   }
01595 };
01596 
01597 class TestLessThanAtom:
01598     public PluginAtom
01599 {
01600 public:
01601   TestLessThanAtom():
01602         PluginAtom("testLessThan", false)
01603   {
01604     addInputPredicate();
01605     addInputPredicate();
01606     setOutputArity(0);
01607 
01608     prop.antimonotonicInputPredicates.insert(0);
01609   }
01610 
01611   virtual void
01612   retrieve(const Query& query, Answer& answer)
01613   {
01614     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01615     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01616 
01617     int a = 0;
01618     int b = 0;
01619     while (en < en_end){
01620         
01621         if (getRegistry()->ogatoms.getByAddress(*en).tuple[0] == query.input[0]){
01622             a++;
01623         }else{
01624             b++;
01625         }
01626         en++;
01627     }
01628 
01629     if (a < b){
01630         // succeed by returning an empty tuple
01631         Tuple t;
01632         answer.get().push_back(t);
01633     }else{
01634         // fail by returning no tuple
01635     }
01636   }
01637 };
01638 
01639 class TestEqualAtom:
01640     public PluginAtom
01641 {
01642 public:
01643   TestEqualAtom():
01644         PluginAtom("testEqual", false)
01645   {
01646     addInputPredicate();
01647     addInputPredicate();
01648     setOutputArity(0);
01649 
01650     prop.antimonotonicInputPredicates.insert(0);
01651   }
01652 
01653   virtual void
01654   retrieve(const Query& query, Answer& answer)
01655   {
01656     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01657     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01658 
01659     int a = 0;
01660     int b = 0;
01661     while (en < en_end){
01662         
01663         if (getRegistry()->ogatoms.getByAddress(*en).tuple[0] == query.input[0]){
01664             a++;
01665         }else{
01666             b++;
01667         }
01668         en++;
01669     }
01670 
01671     if (a == b){
01672         // succeed by returning an empty tuple
01673         Tuple t;
01674         answer.get().push_back(t);
01675     }else{
01676         // fail by returning no tuple
01677     }
01678   }
01679 };
01680 
01681 class TestTransitiveClosureAtom:
01682     public PluginAtom
01683 {
01684 public:
01685     TestTransitiveClosureAtom():
01686         // monotonic, as only constant inputs
01687         PluginAtom("testTransitiveClosure", true)
01688     {
01689             addInputPredicate();
01690             setOutputArity(2);
01691 
01692             prop.monotonicInputPredicates.insert(0);
01693     }
01694 
01695     virtual void
01696     retrieve(const Query& query, Answer& answer)
01697     {
01698         assert(query.input.size() == 1);
01699 
01700         std::set<ID> nodes;
01701         std::set<std::pair<ID, ID> > edges;
01702 
01703         bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01704         bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01705         while (en < en_end){
01706             const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en);
01707 
01708             nodes.insert(ogatom.tuple[1]);
01709             nodes.insert(ogatom.tuple[2]);
01710             edges.insert(std::pair<ID, ID>(ogatom.tuple[1], ogatom.tuple[2]));
01711             en++;
01712         }
01713 
01714         BOOST_FOREACH (ID n, nodes){
01715             BOOST_FOREACH (ID m, nodes){
01716                 BOOST_FOREACH (ID o, nodes){
01717                     if (std::find(edges.begin(), edges.end(), std::pair<ID, ID>(n, m)) != edges.end() &&
01718                         std::find(edges.begin(), edges.end(), std::pair<ID, ID>(m, o)) != edges.end()){
01719                         Tuple t;
01720                         t.push_back(n);
01721                         t.push_back(o);
01722                         answer.get().push_back(t);
01723                     }
01724                 }
01725             }
01726         }
01727     }
01728 };
01729 
01730 class TestCycleAtom:
01731     public PluginAtom
01732 {
01733 public:
01734     TestCycleAtom():
01735         // monotonic, as only constant inputs
01736         PluginAtom("testCycle", true)
01737     {
01738             addInputPredicate();
01739             addInputConstant();
01740             setOutputArity(0);
01741 
01742             prop.monotonicInputPredicates.insert(0);
01743     }
01744 
01745     bool dfscycle(bool directed, ID parent, ID node, std::map<ID, std::set<ID> >& outedges, std::map<ID, bool>& visited, std::set<std::pair<ID, ID> >& cycle){
01746 
01747         // if the node was already visited in the dfs search, then we have a cycle
01748         if (visited[node]) return true;
01749 
01750         // otherwise: visit the node
01751         visited[node] = true;
01752 
01753         // visit all child nodes
01754         BOOST_FOREACH (ID child, outedges[node]){
01755             cycle.insert(std::pair<ID, ID>(node, child));
01756             if (directed || child != parent){
01757                 if (dfscycle(directed, node, child, outedges, visited, cycle)) return true;
01758             }
01759             cycle.erase(std::pair<ID, ID>(node, child));
01760         }
01761 
01762         visited[node] = false;
01763         return false;
01764     }
01765 
01766     virtual void
01767     retrieve(const Query& query, Answer& answer)
01768     {
01769         assert(query.input.size() == 1);
01770 
01771         Term dir(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "directed");
01772         bool directed = query.input[1] == getRegistry()->storeTerm(dir);
01773 
01774         std::set<ID> nodes;
01775         std::map<ID, std::set<ID> > outedges;
01776         std::map<ID, bool> visited;
01777         std::set<std::pair<ID, ID> > cycle;
01778 
01779         bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01780         bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01781         while (en < en_end){
01782             const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en);
01783 
01784             nodes.insert(ogatom.tuple[1]);
01785             nodes.insert(ogatom.tuple[2]);
01786             outedges[ogatom.tuple[1]].insert(ogatom.tuple[2]);
01787             if (!directed) outedges[ogatom.tuple[2]].insert(ogatom.tuple[1]);
01788             en++;
01789         }
01790 
01791         BOOST_FOREACH (ID n, nodes){
01792             if (dfscycle(directed, ID_FAIL, n, outedges, visited, cycle)){
01793                 Tuple t;
01794                 answer.get().push_back(t);
01795             }
01796         }
01797     }
01798 };
01799 
01800 class TestAppendAtom:
01801     public PluginAtom
01802 {
01803 public:
01804   TestAppendAtom():
01805         PluginAtom("testAppend", true)
01806   {
01807     addInputPredicate();
01808     addInputConstant();
01809     setOutputArity(1);
01810 
01811     prop.antimonotonicInputPredicates.insert(0);
01812   }
01813 
01814   virtual void
01815   retrieve(const Query& query, Answer& answer)
01816   {
01817     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01818     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01819 
01820     while (en < en_end){
01821 
01822         std::string str = getRegistry()->terms.getByID(getRegistry()->ogatoms.getByAddress(*en).tuple[1]).getUnquotedString();
01823         str = str + getRegistry()->terms.getByID(query.input[1]).getUnquotedString();
01824         Term term(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, str);
01825         ID idout = getRegistry()->storeTerm(term);
01826         Tuple t;
01827         t.push_back(idout);
01828         answer.get().push_back(t);
01829 
01830         en++;
01831     }
01832   }
01833 };
01834 
01835 class TestDisjAtom:
01836   public PluginAtom
01837 {
01838 public:
01839   TestDisjAtom():
01840     PluginAtom("testDisj", false)
01841   {
01842     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
01843     addInputPredicate();    // interpretation i
01844     addInputPredicate();    // positive p
01845     addInputPredicate();    // negative n
01846     // The external atom implements the following disjunction:
01847     // (bigvee_{\vec{t} \in ext(p)} i(\vec{t})) \vee (bigvee_{\vec{t} \in ext(n)} \naf i(\vec{t}))
01848     // Example: &testDisj[i, p, n]() with p(1), p(2), n(0) is true iff i(1) \vee i(2) \vee \naf i(0) holds
01849     setOutputArity(0);
01850   }
01851 
01852   virtual void retrieve(const Query& query, Answer& answer)
01853   {
01854     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01855     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01856 
01857     while (en < en_end){
01858 
01859         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
01860         if (atom.tuple[0] == query.input[1]){
01861             OrdinaryAtom iatom = atom;
01862             iatom.tuple[0] = query.input[0];
01863             if (query.interpretation->getFact(getRegistry()->storeOrdinaryGAtom(iatom).address)){
01864                 Tuple t;
01865                 answer.get().push_back(t);
01866                 return;
01867             }
01868         }
01869         if (atom.tuple[0] == query.input[2]){
01870             OrdinaryAtom iatom = atom;
01871             iatom.tuple[0] = query.input[0];
01872             if (!query.interpretation->getFact(getRegistry()->storeOrdinaryGAtom(iatom).address)){
01873                 Tuple t;
01874                 answer.get().push_back(t);
01875                 return;
01876             }
01877         }
01878         en++;
01879     }
01880   }
01881 };
01882 
01883 class TestHashAtom:
01884   public PluginAtom
01885 {
01886 public:
01887   TestHashAtom():
01888     PluginAtom("testHash", false)
01889   {
01890     addInputPredicate();
01891     setOutputArity(1);
01892   }
01893 
01894   virtual void retrieve(const Query& query, Answer& answer)
01895   {
01896     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01897     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01898 
01899     std::size_t hashValue = 0;
01900 
01901     while (en < en_end){
01902         const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en);
01903         BOOST_FOREACH (ID t, ogatom.tuple){
01904             boost::hash_combine(hashValue, t.address);
01905         }
01906         en++;
01907     }
01908 
01909     std::stringstream ss;
01910     ss << "h" << hashValue;
01911     Tuple t;
01912     t.push_back(getRegistry()->storeConstantTerm(ss.str()));
01913     answer.get().push_back(t);
01914   }
01915 };
01916 
01917 // just always true and takes 5 constant inputs
01918 class TestTrueMultiInpAtom:
01919   public PluginAtom
01920 {
01921 public:
01922   TestTrueMultiInpAtom():
01923     PluginAtom("testTrueMultiInp", true)
01924   {
01925     addInputConstant();
01926     addInputConstant();
01927     addInputConstant();
01928     addInputConstant();
01929     addInputConstant();
01930     setOutputArity(0);
01931   }
01932 
01933   virtual void retrieve(const Query& query, Answer& answer)
01934   {
01935         Tuple t;
01936         answer.get().push_back(t);
01937   }
01938 };
01939 
01940 // takes 5 constant inputs, just always returns integer 1 in output
01941 class TestTrueMultiInpAtom2:
01942   public PluginAtom
01943 {
01944 public:
01945   TestTrueMultiInpAtom2():
01946     PluginAtom("testTrueMultiInp2", true)
01947   {
01948     addInputConstant();
01949     addInputConstant();
01950     addInputConstant();
01951     addInputConstant();
01952     addInputConstant();
01953     setOutputArity(1);
01954   }
01955 
01956   virtual void retrieve(const Query& query, Answer& answer)
01957   {
01958         Tuple t;
01959         t.push_back(ID::termFromInteger(1));
01960         answer.get().push_back(t);
01961   }
01962 };
01963 
01964 class TestReachableAtom:
01965   public PluginAtom
01966 {
01967 public:
01968   TestReachableAtom():
01969     PluginAtom("testReachable", true)
01970   {
01971     addInputPredicate();
01972     addInputConstant();
01973     setOutputArity(1);
01974     
01975     prop.relativeFiniteOutputDomain.insert(std::pair<int, int>(0, 0));
01976   }
01977 
01978   virtual void retrieve(const Query& query, Answer& answer)
01979   {
01980         bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
01981         bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
01982 
01983         while (en < en_end){
01984             const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en);
01985             if (ogatom.tuple[1] == query.input[1]){
01986                 Tuple t;
01987                 t.push_back(ogatom.tuple[2]);
01988                 answer.get().push_back(t);
01989             }
01990             en++;
01991         }
01992   }
01993 };
01994 
01995 
01996 
01997 class TestDLSimulatorAtom:
01998   public PluginAtom
01999 {
02000 public:
02001   TestDLSimulatorAtom():
02002     PluginAtom("testDLSimulator", false)
02003   {
02004     addInputConstant();     // mode: 1=concept retrieval, 0=consistency check
02005     addInputConstant();     // domain size: all even domain elements are non-Fliers, all odd domain elements are Fliers
02006     addInputPredicate();    // plus-concept Flier
02007     setOutputArity(1);      // \neg Flier
02008   }
02009 
02010   virtual void retrieve(const Query& query, Answer& answer)
02011   {
02012         if (query.input[0].address == 0){
02013             // consistency check: check if an odd element is in plus-concept; if yes, then we have inconsistency
02014             bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
02015             bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
02016 
02017             while (en < en_end){
02018                 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en);
02019                 if (ogatom.tuple[1].address % 2 == 0) return;   // inconsistent
02020                 en++;
02021             }
02022             Tuple t;
02023             t.push_back(ID::termFromInteger(0));
02024             answer.get().push_back(t);  // consistent
02025         }else{
02026             // concept \neg C query
02027 
02028             // add all even elements up to the specified size; if inconsistent, then add also all odd elements
02029             bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
02030             bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
02031 
02032             bool inc = false;
02033             while (en < en_end){
02034                 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en);
02035                 if (ogatom.tuple[1].address % 2 == 0) inc = true;   // inconsistent
02036                 en++;
02037             }
02038 
02039             for (uint32_t i = 0; i <= query.input[1].address; ++i){
02040                 if (i % 2 == 0 || inc){
02041                     Tuple t;
02042                     t.push_back(ID::termFromInteger(i));
02043                     answer.get().push_back(t);
02044                 }
02045             }
02046         }
02047   }
02048 };
02049 
02050 
02051 
02052 
02053 // Common base class for cautious and brave queries.
02054 // The only difference between answering cautious and brave queries
02055 // concerns the aggregation of the answer sets of the subprogram,
02056 // thus almost everything is implemented in this class.
02057 class TestASPQueryAtom:
02058   public PluginAtom
02059 {
02060 private:
02061     ProgramCtx& ctx;
02062 
02063 public:
02064   TestASPQueryAtom(ProgramCtx& ctx, std::string atomName):
02065     ctx(ctx), PluginAtom(atomName, false /* not monotonic */)
02066   {
02067     addInputConstant();     // program file
02068     addInputPredicate();    // input interpretation
02069     addInputConstant();     // query predicate
02070     setOutputArity(0);
02071 
02072         prop.variableOutputArity = true;                    // the output arity of this external atom depends on the arity of the query predicate
02073         prop.supportSets = true;                                    // we provide support sets
02074                 prop.onlySafeSupportSets = true;
02075         prop.completePositiveSupportSets = true;    // we even provide (positive) complete support sets
02076   }
02077 
02078   virtual void retrieve(const Query& query, Answer& answer)
02079   {
02080         assert(false);
02081     }
02082 
02083 
02084   virtual void learnSupportSets(const Query& query, NogoodContainerPtr nogoods)
02085   {
02086     Answer ans;
02087     retrieveOrLearnSupportSets(query, ans, nogoods, true);
02088   }
02089 
02090   virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods)
02091   {
02092     retrieveOrLearnSupportSets(query, answer, nogoods, false);
02093   }
02094 
02095   virtual void retrieveOrLearnSupportSets(const Query& query, Answer& answer, NogoodContainerPtr nogoods, bool learnSupportSets)
02096   {
02097         RegistryPtr reg = getRegistry();
02098 
02099         // input parameters to external atom &testCautiousQuery["prog", p, q](x):
02100         //  query.input[0] (i.e. "prog"): filename of the program P over which we do query answering
02101         //  query.input[1] (i.e. p): a predicate name; the set F of all atoms over this predicate are added to P as facts before evaluation
02102         //  query.input[2] (i.e. q): name of the query predicate; the external atom will be true for all output vectors x such that q(x) is true in every answer set of P \cup F
02103 
02104         // read the subprogram from the file
02105         InputProviderPtr ip(new InputProvider());
02106         ip->addFileInput(getRegistry()->terms.getByID(query.input[0]).getUnquotedString());
02107 
02108         // prepare data structures for the subprogram P
02109         ProgramCtx pc = ctx;
02110         pc.idb.clear();
02111         pc.edb = InterpretationPtr(new Interpretation(reg));
02112         pc.currentOptimum.clear();
02113         pc.config.setOption("NumberOfModels",0);
02114         pc.inputProvider = ip;
02115         ip.reset();
02116 
02117         // add facts F to the EDB of P
02118         pc.edb->getStorage() |= query.interpretation->getStorage();
02119 
02120         // compute all answer sets of P \cup F
02121         std::vector<InterpretationPtr> answersets = ctx.evaluateSubprogram(pc, true);
02122 
02123         // learn support sets (only if --supportsets option is specified on the command line)
02124         if (learnSupportSets && !!nogoods && query.ctx->config.getOption("SupportSets")){
02125             SimpleNogoodContainerPtr preparedNogoods = SimpleNogoodContainerPtr(new SimpleNogoodContainer());
02126 
02127             // for all rules r of P
02128             BOOST_FOREACH (ID ruleID, pc.idb){
02129                 const Rule& rule = reg->rules.getByID(ruleID);
02130 
02131                 // Check if r is a rule of form
02132                 //          hatom :- B,
02133                 // where hatom is a single atom and B contains only positive atoms.
02134                 bool posBody = true;
02135                 BOOST_FOREACH (ID b, rule.body){
02136                     if (b.isNaf()) posBody = false;
02137                 }
02138                 if (rule.head.size() == 1 /*&& posBody*/){
02139                     // We learn the following (nonground) nogoods: { T b | b \in B } \cup { F hatom }.
02140                     Nogood nogood;
02141 
02142                     // add all (positive) body atoms
02143                     BOOST_FOREACH (ID blit, rule.body) nogood.insert(NogoodContainer::createLiteral(blit));
02144 
02145                     // add the negated head atom
02146                     nogood.insert(NogoodContainer::createLiteral(rule.head[0] | ID(ID::NAF_MASK, 0)));
02147 
02148                     // actually learn this nogood
02149                     DBGLOG(DBG, "Learn prepared nogood " << nogood.getStringRepresentation(reg));
02150                     preparedNogoods->addNogood(nogood);
02151                 }
02152             }
02153 
02154             // exhaustively generate all resolvents of the prepared nogoods
02155             DBGLOG(DBG, "Computing resolvents of prepared nogoods up to size " << (query.interpretation->getStorage().count() + 1));
02156             preparedNogoods->addAllResolvents(reg, query.interpretation->getStorage().count() + 1);
02157 
02158             // all nogoods of form
02159             //      { T b | b \in B } \cup { F q(X) }
02160             // containing only atoms over p and q are transformed into support sets of form
02161             //      { T b | b \in B } \cup { F e_{&testCautiousQuery["prog", p, q]}(X) }
02162             // This is because if all body atoms are in the input (atoms over predicate p), then q(X) is true in every answer set of P \cup F.
02163             // But then, since q is the query predicate, also &testCautiousQuery["prog", p, q](X) is true.
02164             DBGLOG(DBG, "Extracting support sets from prepared nogoods");
02165             for (int i = 0; i < preparedNogoods->getNogoodCount(); i++){
02166                 const Nogood& ng = preparedNogoods->getNogood(i);
02167                 bool isSupportSet = true;
02168                 Nogood supportSet;
02169                 BOOST_FOREACH (ID id, ng){
02170                     ID pred = reg->lookupOrdinaryAtom(id).tuple[0];
02171                     if (pred == query.input[1]){
02172                         supportSet.insert(id);
02173                     }else if (pred == query.input[2]){
02174                         const OrdinaryAtom& hatom = reg->lookupOrdinaryAtom(id);
02175                         // add e_{&testCautiousQuery["prog", p, q]}(X) using a helper function  
02176                         supportSet.insert(NogoodContainer::createLiteral(
02177                                                                 ExternalLearningHelper::getOutputAtom(
02178                                                                         query,  // this parameter is always the same
02179                                                                         Tuple(hatom.tuple.begin() + 1, hatom.tuple.end()),  // hatom.tuple[0]=q and hatom.tuple[i] for i >= 1 stores the elements of X;
02180                                                                                                                                                                                 // here we need only the X and use hatom.tuple.begin() + 1 to eliminate the predicate q
02181                                                                         !id.isNaf() /* technical detail, is set to true almost always */).address,
02182                                                                 true,                                                                                               // sign of the literal e_{&testCautiousQuery["prog", p, q]}(X) in the nogood
02183                                                                 id.isOrdinaryGroundAtom()                                                           /* specify if this literal is ground or nonground (the same as the head atom) */ ));
02184                     }else{
02185                         isSupportSet = false;
02186                         break;
02187                     }
02188                 }
02189                 if (isSupportSet){
02190                     DBGLOG(DBG, "Learn support set: " << supportSet.getStringRepresentation(reg));
02191                     nogoods->addNogood(supportSet);
02192                 }
02193             }
02194         }
02195 
02196         // create a mask for the query predicate, i.e., retrieve all atoms over the query predicate
02197         PredicateMaskPtr pm = PredicateMaskPtr(new PredicateMask());
02198         pm->setRegistry(reg);
02199         pm->addPredicate(query.input[2]);
02200         pm->updateMask();
02201 
02202         // now since we know all answer sets, we can answer the query
02203         answerQuery(pm, answersets, query, answer);
02204   }
02205 
02206   // define an abstract method for aggregating the answer sets (this part is specific for cautious and brave queries)
02207     virtual void answerQuery(PredicateMaskPtr pm, std::vector<InterpretationPtr>& answersets, const Query& query, Answer& answer) = 0;
02208 };
02209 
02210 class TestCautiousQueryAtom:
02211   public TestASPQueryAtom
02212 {
02213 public:
02214   TestCautiousQueryAtom(ProgramCtx& ctx):
02215     TestASPQueryAtom(ctx, "testCautiousQuery")
02216   { }
02217 
02218     // implement the specific part
02219     void answerQuery(PredicateMaskPtr pm, std::vector<InterpretationPtr>& answersets, const Query& query, Answer& answer){
02220 
02221         RegistryPtr reg = getRegistry();
02222 
02223         // special case: if there are no answer sets, cautious ground queries are trivially true, but cautious non-ground queries are always false for all ground substituions (by definition)
02224         if (answersets.size() == 0){
02225             if (query.pattern.size() == 0){
02226                 // return the empty tuple
02227                 Tuple t;
02228                 answer.get().push_back(t);
02229             }
02230         }else{
02231 
02232             InterpretationPtr out = InterpretationPtr(new Interpretation(reg));
02233             out->add(*pm->mask());
02234 
02235             // get the set of atoms over the query predicate which are true in all answer sets
02236             BOOST_FOREACH (InterpretationPtr intr, answersets){
02237                 out->getStorage() &= intr->getStorage();
02238             }
02239 
02240             // retrieve all output atoms oatom=q(c)
02241             bm::bvector<>::enumerator en = out->getStorage().first();
02242             bm::bvector<>::enumerator en_end = out->getStorage().end();
02243             while (en < en_end){
02244                 const OrdinaryAtom& oatom = reg->ogatoms.getByAddress(*en);
02245 
02246                 // add c to the output
02247                 answer.get().push_back(Tuple(oatom.tuple.begin() + 1, oatom.tuple.end()));
02248                 en++;
02249             }
02250         }
02251     }
02252 };
02253 
02254 class TestBraveQueryAtom:
02255   public TestASPQueryAtom
02256 {
02257 public:
02258   TestBraveQueryAtom(ProgramCtx& ctx):
02259     TestASPQueryAtom(ctx, "testCautiousBrave")
02260   { }
02261 
02262     // implement the specific part
02263     virtual void answerQuery(PredicateMaskPtr pm, std::vector<InterpretationPtr>& answersets, const Query& query, Answer& answer){
02264 
02265         RegistryPtr reg = getRegistry();
02266 
02267         InterpretationPtr out = InterpretationPtr(new Interpretation(reg));
02268 
02269         // get the set of atoms over the query predicate which are true in all answer sets
02270         BOOST_FOREACH (InterpretationPtr intr, answersets){
02271             out->getStorage() |= (pm->mask()->getStorage() & intr->getStorage());
02272         }
02273 
02274         // retrieve all output atoms oatom=q(c)
02275         bm::bvector<>::enumerator en = out->getStorage().first();
02276         bm::bvector<>::enumerator en_end = out->getStorage().end();
02277         while (en < en_end){
02278             const OrdinaryAtom& oatom = reg->ogatoms.getByAddress(*en);
02279 
02280             // add c to the output
02281             answer.get().push_back(Tuple(oatom.tuple.begin() + 1, oatom.tuple.end()));
02282             en++;
02283         }
02284     }
02285 };
02286 
02287 class TestFinalCallback:
02288     public FinalCallback
02289 {
02290 public:
02291     TestFinalCallback(ProgramCtx& ctx):
02292         ctx(ctx),
02293         first(true)
02294     {
02295     }
02296 
02297   virtual void operator()()
02298     {
02299         std::cout << "TestFinalCallback::operator()()" << std::endl;
02300         if( first )
02301         {
02302             // repeat
02303             ctx.config.setOption("RepeatEvaluation",1);
02304         }
02305         else
02306         {
02307             // don't repeat again
02308         }
02309         first = false;
02310     }
02311 
02312 private:
02313     ProgramCtx& ctx;
02314     bool first;
02315 };
02316 
02317 class TestPlugin:
02318   public PluginInterface
02319 {
02320 public:
02321     struct CtxData:
02322         public PluginData
02323     {
02324     public:
02325         bool testRepetition;
02326 
02327     public:
02328         CtxData():
02329             testRepetition(false) {}
02330         virtual ~CtxData() {}
02331     };
02332 
02333 public:
02334   TestPlugin():
02335     PluginInterface()
02336   {
02337     setNameVersion("dlvhex-testplugin", 0, 0, 1);
02338   }
02339 
02340   virtual void processOptions(std::list<const char*>& pluginOptions, ProgramCtx& ctx)
02341     {
02342         TestPlugin::CtxData& pcd = ctx.getPluginData<TestPlugin>();
02343 
02344         typedef std::list<const char*>::iterator Iterator;
02345         Iterator it;
02346         WARNING("create (or reuse, maybe from potassco?) cmdline option processing facility")
02347         it = pluginOptions.begin();
02348         while( it != pluginOptions.end() )
02349         {
02350             bool processed = false;
02351             const std::string str(*it);
02352             if( str == "--testplugin-test-repetition" )
02353             {
02354                 pcd.testRepetition = true;
02355                 std::cerr << "going to test repetition" << std::endl;
02356                 processed = true;
02357             }
02358 
02359             if( processed )
02360             {
02361                 it = pluginOptions.erase(it);
02362             }
02363             else
02364             {
02365                 it++;
02366             }
02367         }
02368     }
02369 
02370 class TestSetUnionAtom: // tests user-defined external learning
02371   public PluginAtom
02372 {
02373 
02374 public:
02375 
02376     TestSetUnionAtom():
02377         // testSetUnion is the name of our external atom
02378     PluginAtom("testSetUnion", true) // monotonic
02379   {
02380     WARNING("TODO if a plugin atom has only constant inputs, is it always monotonic? if yes, automate this, at least create a warning")
02381         DBGLOG(DBG,"Constructor of SetUnion plugi is started!");
02382     addInputPredicate(); // the first set
02383     addInputPredicate(); // the second set
02384     setOutputArity(1); // arity of the output list
02385   }
02386 
02387 // function that evaluates external atom without learning
02388 // input parameters: 
02389 // 1. Query is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Query)
02390 // 2. Answer is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Answer)
02391   virtual void retrieve(const Query& query, Answer& answer)
02392   {
02393     // find relevant input
02394     // Iterators (objects that mark the begin and the end of some structure)
02395   DBGLOG(DBG,"Retrieve function is started");
02396     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
02397     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
02398 
02399     std::vector<Tuple> tuples1;
02400     std::vector<Tuple> tuples2;
02401     // go through all atoms using the iterator
02402     while (en < en_end){
02403     // extract the current atom
02404     // *emn is the id of the current atom, to which the iterator points 
02405         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
02406         Tuple tu;
02407     // Iterate over the input elements of the current atom (for p(x,y), we go through x and y)
02408     // We start with 1 because the position 0 is the predicate itself 
02409         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
02410     // Get element number i from the input list
02411             tu.push_back(atom.tuple[i]);
02412         }
02413     
02414         if (atom.tuple[0] == query.input[0]){
02415             tuples1.push_back(tu);
02416         }
02417         if (atom.tuple[0] == query.input[1]){
02418             tuples2.push_back(tu);
02419         }
02420         en++;
02421     }
02422 
02423     // for each element t of tuples1 add t to the answer 
02424     BOOST_FOREACH (Tuple t, tuples1){
02425         answer.get().push_back(t);
02426     }
02427 
02428     BOOST_FOREACH (Tuple t, tuples2){
02429         answer.get().push_back(t);
02430     }
02431   }
02432 
02433 
02434 // function that evaluates external atom with learning
02435 // input parameters: 
02436 // 1. Query is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Query)
02437 // 2. Answer is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Answer)
02438 // 3. Learnt Nogoods
02439 
02440   virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods)
02441   {
02442     // find relevant input
02443     // Iterators (objects that mark the begin and the end of some structure)
02444     bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
02445     bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
02446 
02447     std::vector<Tuple> tuples1;
02448     std::vector<Tuple> tuples2;
02449     // go through all atoms using the iterator
02450     while (en < en_end){
02451     // extract the current atom
02452     // *emn is the id of the current atom, to which the iterator points 
02453         const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en));
02454         Tuple tu;
02455     // Iterate over the input elements of the current atom (for p(x,y), we go through x and y)
02456     // We start with 1 because the position 0 is the predicate itself 
02457         for (uint32_t i = 1; i < atom.tuple.size(); ++i){
02458     // Get element number i from the input list
02459             tu.push_back(atom.tuple[i]);
02460         }
02461     
02462         if (atom.tuple[0] == query.input[0]){
02463             tuples1.push_back(tu);
02464         }
02465         if (atom.tuple[0] == query.input[1]){
02466             tuples2.push_back(tu);
02467         }
02468         en++;
02469     }
02470     
02471     // for each element t of tuples1 add t to the answer 
02472     BOOST_FOREACH (Tuple t, tuples1){
02473         answer.get().push_back(t);
02474         // G in the end stands for ground learning (N for nonground)
02475         // Create a new object where we store the copy of the first input predictae
02476         OrdinaryAtom at1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG);
02477         // Copy input predicate with the parameters to at1
02478         at1.tuple.push_back(query.input[0]);
02479         // arity is always 1 here
02480         BOOST_FOREACH (ID i, t) {   
02481             at1.tuple.push_back(i);
02482         }
02483         // Start with empty nogood
02484         Nogood nogood;
02485         // Add the first literal
02486         // In case of a nonground nogood, we need to store NAtom (storeOrdinaryNAtom)
02487         // First true is the sign of the literal
02488         // Second parameter is true if we create ground nogood
02489  
02490         nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryGAtom(at1).address, true, true));
02491 
02492         // ExternalLearningHelper is a function that helps to create an element in a nogood for external atom: call the function for the given output tuple
02493         // Always the same (add the false output in case if under the input parameters the result is true)
02494         nogood.insert(NogoodContainer::createLiteral(ExternalLearningHelper::getOutputAtom(query, t, false).address, true, false));
02495         // add the nogood to the set of all nogoods if nogoods is not zero
02496         if (!!nogoods)
02497             nogoods->addNogood(nogood);
02498     DBGLOG(DBG,"nogood is " << nogood);
02499 
02500     }
02501 
02502     BOOST_FOREACH (Tuple t, tuples2){
02503         answer.get().push_back(t);
02504     }
02505   }
02506 };
02507 
02508 class TestGen2Atom:      // tests user-defined external learning
02509   public PluginAtom
02510 {
02511 public:
02512   TestGen2Atom(std::string name, int arity):
02513     PluginAtom(name, false)
02514   {
02515     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
02516     addInputPredicate();
02517     for (int i = 0; i < arity; ++i) addInputConstant();
02518     setOutputArity(0);
02519   }
02520 
02521   virtual void retrieve(const Query& query, Answer& answer)
02522   {
02523     OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN);
02524     myat.tuple = query.input;
02525 
02526         // find relevant input
02527     bool match = false;
02528         bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
02529         bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
02530         while (en < en_end){
02531 
02532                 if (getRegistry()->ogatoms.getByAddress(*en).unifiesWith(myat)){
02533             match = true;
02534             break;
02535         }
02536         en++;
02537         }
02538         Tuple tu;
02539         if (match) answer.get().push_back(tu);
02540   }
02541 };
02542 
02543 class TestIsEmpty:      // tests user-defined external learning
02544   public PluginAtom
02545 {
02546 public:
02547   TestIsEmpty():
02548     PluginAtom("testIsEmpty", false)
02549   {
02550     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
02551     addInputPredicate();
02552     setOutputArity(0);
02553   }
02554 
02555   virtual void retrieve(const Query& query, Answer& answer)
02556   {
02557         OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN);
02558         myat.tuple = query.input;
02559 
02560         // find relevant input
02561         bm::bvector<>::enumerator en = query.interpretation->getStorage().first();
02562         bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end();
02563         while (en < en_end){
02564         return; // not empty
02565         }
02566 
02567     // empty
02568         Tuple tu;
02569         answer.get().push_back(tu);
02570   }
02571 };
02572 
02573 class TestNumberOfBalls:      // tests user-defined external learning
02574   public PluginAtom
02575 {
02576 public:
02577   TestNumberOfBalls():
02578     PluginAtom("testNumberOfBalls", false)
02579   {
02580     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
02581     addInputPredicate();
02582     addInputConstant();
02583     addInputConstant();
02584     setOutputArity(0);
02585 
02586     prop.providesPartialAnswer = true;
02587   }
02588 
02589   virtual void retrieve(const Query& query, Answer& answer)
02590   {
02591         OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN);
02592         myat.tuple = query.input;
02593 
02594         // find relevant input
02595     int tr = 0;
02596     int fa = 0;
02597     int un = 0;
02598         bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first();
02599         bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end();
02600         while (en < en_end){
02601         if ((!query.assigned || query.assigned->getFact(*en)) && query.interpretation->getFact(*en)) tr++;
02602         else if ((!query.assigned || query.assigned->getFact(*en)) && !query.interpretation->getFact(*en)) fa++;
02603         else un++;
02604         en++;
02605         }
02606 
02607     if (tr >= query.input[1].address && (tr + un) <= query.input[2].address){
02608         // true
02609             Tuple tu;
02610             answer.get().push_back(tu);
02611     }else if ((tr + un) >= query.input[1].address && tr <= query.input[2].address){
02612         // unknwon
02613         Tuple tu;
02614         answer.getUnknown().push_back(tu);
02615     }else{
02616         // false
02617     }
02618   }
02619 };
02620 
02621 class TestNumberOfBallsSE:      // tests user-defined external learning
02622   public PluginAtom
02623 {
02624 public:
02625   TestNumberOfBallsSE():
02626     PluginAtom("testNumberOfBallsSE", false)
02627   {
02628     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
02629     addInputPredicate();
02630     addInputConstant();
02631     setOutputArity(0);
02632 
02633     prop.providesPartialAnswer = true;
02634     prop.antimonotonicInputPredicates.insert(0);
02635   }
02636 
02637   virtual void retrieve(const Query& query, Answer& answer)
02638   {
02639         OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN);
02640         myat.tuple = query.input;
02641 
02642         // find relevant input
02643         int tr = 0;
02644         int fa = 0;
02645         int un = 0;
02646         bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first();
02647         bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end();
02648         while (en < en_end){
02649                 if ((!query.assigned || query.assigned->getFact(*en)) && query.interpretation->getFact(*en)) tr++;
02650                 else if ((!query.assigned || query.assigned->getFact(*en)) && !query.interpretation->getFact(*en)) fa++;
02651                 else un++;
02652                 en++;
02653         }
02654 
02655         if ((tr + un) <= query.input[1].address){
02656                 // true
02657                 Tuple tu;
02658                 answer.get().push_back(tu);
02659         }else if (tr <= query.input[1].address){
02660                 // unknwon
02661                 Tuple tu;
02662                 answer.getUnknown().push_back(tu);
02663         }else{
02664                 // false
02665         }
02666   }
02667 };
02668 
02669 class TestNumberOfBallsGE:      // tests user-defined external learning
02670   public PluginAtom
02671 {
02672 public:
02673   TestNumberOfBallsGE():
02674     PluginAtom("testNumberOfBallsGE", false)
02675   {
02676     WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning")
02677     addInputPredicate();
02678     addInputConstant();
02679     setOutputArity(0);
02680 
02681     prop.providesPartialAnswer = true;
02682     prop.monotonicInputPredicates.insert(0);
02683   }
02684 
02685   virtual void retrieve(const Query& query, Answer& answer)
02686   {
02687         OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN);
02688         myat.tuple = query.input;
02689 
02690         // find relevant input
02691         int tr = 0;
02692         int fa = 0;
02693         int un = 0;
02694         bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first();
02695         bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end();
02696         while (en < en_end){
02697                 if ((!query.assigned || query.assigned->getFact(*en)) && query.interpretation->getFact(*en)) tr++;
02698                 else if ((!query.assigned || query.assigned->getFact(*en)) && !query.interpretation->getFact(*en)) fa++;
02699                 else un++;
02700                 en++;
02701         }
02702 
02703         if (tr >= query.input[1].address) {
02704                 // true
02705                 Tuple tu;
02706                 answer.get().push_back(tu);
02707         }else if ((tr + un) >= query.input[1].address) {
02708                 // unknwon
02709                 Tuple tu;
02710                 answer.getUnknown().push_back(tu);
02711         }else{
02712                 // false
02713         }
02714   }
02715 };
02716 
02717 
02718     class SumNonZeroAtom : public PluginAtom
02719     {
02720         public:
02721             SumNonZeroAtom() : PluginAtom("sumD0", 1)
02722             {
02723                 addInputPredicate();
02724                 setOutputArity(1);
02725             }
02726       
02727             virtual void
02728             retrieve(const Query& query, Answer& answer) throw (PluginError)
02729             {
02730                 Registry &registry = *getRegistry();
02731     
02732                 int sum = 0;
02733                 int pos = query.interpretation.get()->getStorage().get_first();
02734                 while (pos != 0){
02735                     const OrdinaryAtom& oatom = registry.ogatoms.getByAddress(pos);
02736                     if(oatom.tuple[1].address == 0)
02737                         sum += oatom.tuple[2].address;
02738                     else
02739                         sum -= oatom.tuple[2].address;
02740                     pos = query.interpretation.get()->getStorage().get_next(pos);
02741                 }
02742         
02743                 Tuple out;
02744                 out.push_back(ID::termFromInteger(sum == 0 ? 0 : 1));
02745                 answer.get().push_back(out);
02746             }
02747     };
02748 
02749     class ProductionRequirementsAtom : public PluginAtom
02750     {
02751         public:
02752             ProductionRequirementsAtom() : PluginAtom("getreq", false)
02753             {
02754                 addInputPredicate();
02755                 addInputPredicate();
02756                 setOutputArity(1);
02757 
02758                 prop.setProvidesPartialAnswer(true);
02759             }
02760       
02761             virtual void
02762             retrieve(const Query& query, Answer& answer) throw (PluginError)
02763             {
02764                 Registry &registry = *getRegistry();
02765 
02766                 std::set<ID> produced;
02767                 std::set<ID> possiblyproduced;
02768                 std::set<ID> allrequirements;
02769 
02770                 ID const_p = registry.storeConstantTerm("p");
02771                 ID const_n = registry.storeConstantTerm("n");
02772 
02773                 // extract requirements and production plan
02774                 bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first();
02775                 bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end();
02776                 while (en < en_end){
02777                     ID id = registry.ogatoms.getIDByAddress(*en);
02778                     const OrdinaryAtom& ogatom = registry.ogatoms.getByAddress(*en);
02779                     if (ogatom.tuple[0] == query.input[0]){
02780                         if (!query.assigned || query.assigned->getFact(*en)){
02781                             if (query.interpretation->getFact(*en)) { produced.insert(ogatom.tuple[1]); }
02782                         } else { possiblyproduced.insert(ogatom.tuple[1]); }
02783                     }
02784                     if (ogatom.tuple[0] == query.input[1]){
02785                         allrequirements.insert(ogatom.tuple[1]);
02786                     }
02787                     en++;
02788                 }
02789 
02790                 // decide for each requirement if it is true, false or unknown
02791                 BOOST_FOREACH (ID req, allrequirements) {
02792                     en = query.predicateInputMask->getStorage().first();
02793                     en_end = query.predicateInputMask->getStorage().end();
02794                     while (en < en_end){
02795                         const OrdinaryAtom& ogatom = registry.ogatoms.getByAddress(*en);
02796                         if (ogatom.tuple[0] == query.input[1] && ogatom.tuple[1] == req){
02797                             if (ogatom.tuple[2] != const_p) throw PluginError("requirements specification must be of form req(Name, p, ..., n, ...)");
02798                             bool cursat = true;
02799                             bool curviolated = false;
02800                             bool pos = true;
02801                             for (int i = 3; i < ogatom.tuple.size(); ++i) {
02802                                 // switch to negative requirements
02803                                 if (ogatom.tuple[i] == const_n) {
02804                                     pos = false;
02805                                     continue;
02806                                 }
02807                                 // check for satisfaction of the current product
02808                                 cursat &= (pos && produced.find(ogatom.tuple[i]) != produced.end()) || (!pos && produced.find(ogatom.tuple[i]) == produced.end() && possiblyproduced.find(ogatom.tuple[i]) == possiblyproduced.end());
02809                                 curviolated |= (pos && produced.find(ogatom.tuple[i]) == produced.end() && possiblyproduced.find(ogatom.tuple[i]) == possiblyproduced.end()) || (!pos && produced.find(ogatom.tuple[i]) != produced.end());
02810                             }
02811                             assert (!(cursat && curviolated) && "precondition for requirement is satisfied and violated at the same time");
02812                             // requirement is definitely true
02813                             if (cursat) {
02814                                 Tuple out;
02815                                 out.push_back(req);
02816                                 answer.get().push_back(out);
02817                                 break;
02818                             }
02819                             // requirement could be true
02820                             if (!curviolated) {
02821                                 Tuple out;
02822                                 out.push_back(req);
02823                                 answer.getUnknown().push_back(out);
02824                                 break;
02825                             }
02826                         }
02827                         en++;
02828                     }
02829                 }
02830             }
02831     };
02832 
02833   virtual std::vector<PluginAtomPtr> createAtoms(ProgramCtx& ctx) const
02834   {
02835     std::vector<PluginAtomPtr> ret;
02836 
02837         // return smart pointer with deleter (i.e., delete code compiled into this plugin)
02838     ret.push_back(PluginAtomPtr(new TestAAtom, PluginPtrDeleter<PluginAtom>()));
02839       ret.push_back(PluginAtomPtr(new TestBAtom, PluginPtrDeleter<PluginAtom>()));
02840       ret.push_back(PluginAtomPtr(new TestCAtom, PluginPtrDeleter<PluginAtom>()));
02841       ret.push_back(PluginAtomPtr(new TestZeroArityAtom("testZeroArity0", false), PluginPtrDeleter<PluginAtom>()));
02842       ret.push_back(PluginAtomPtr(new TestZeroArityAtom("testZeroArity1", true), PluginPtrDeleter<PluginAtom>()));
02843       ret.push_back(PluginAtomPtr(new TestConcatAtom, PluginPtrDeleter<PluginAtom>()));
02844       ret.push_back(PluginAtomPtr(new TestConcatAllAtom, PluginPtrDeleter<PluginAtom>()));
02845       ret.push_back(PluginAtomPtr(new TestListDomainAtom, PluginPtrDeleter<PluginAtom>()));
02846       ret.push_back(PluginAtomPtr(new TestListConcatAtom, PluginPtrDeleter<PluginAtom>()));
02847       ret.push_back(PluginAtomPtr(new TestListLengthAtom, PluginPtrDeleter<PluginAtom>()));
02848       ret.push_back(PluginAtomPtr(new TestListSplitAtom, PluginPtrDeleter<PluginAtom>()));
02849       ret.push_back(PluginAtomPtr(new TestListSplitHalfAtom, PluginPtrDeleter<PluginAtom>()));
02850       ret.push_back(PluginAtomPtr(new TestListMergeAtom, PluginPtrDeleter<PluginAtom>()));
02851       ret.push_back(PluginAtomPtr(new TestSubstrAtom, PluginPtrDeleter<PluginAtom>()));
02852       ret.push_back(PluginAtomPtr(new TestSmallerThanAtom, PluginPtrDeleter<PluginAtom>()));
02853       ret.push_back(PluginAtomPtr(new TestFirstAtom, PluginPtrDeleter<PluginAtom>()));
02854       ret.push_back(PluginAtomPtr(new TestPushAtom, PluginPtrDeleter<PluginAtom>()));
02855       ret.push_back(PluginAtomPtr(new TestMoveAtom, PluginPtrDeleter<PluginAtom>()));
02856       ret.push_back(PluginAtomPtr(new TestStrlenAtom, PluginPtrDeleter<PluginAtom>()));
02857       ret.push_back(PluginAtomPtr(new TestSetMinusAtom, PluginPtrDeleter<PluginAtom>()));
02858       ret.push_back(PluginAtomPtr(new TestSetMinusNogoodBasedLearningAtom, PluginPtrDeleter<PluginAtom>()));
02859       ret.push_back(PluginAtomPtr(new TestSetMinusNonComfortAtom, PluginPtrDeleter<PluginAtom>()));
02860       ret.push_back(PluginAtomPtr(new TestSetMinusPartialAtom, PluginPtrDeleter<PluginAtom>()));
02861       ret.push_back(PluginAtomPtr(new TestSetMinusNongroundNogoodBasedLearningAtom, PluginPtrDeleter<PluginAtom>()));
02862       ret.push_back(PluginAtomPtr(new TestSetMinusRuleBasedLearningAtom(&ctx), PluginPtrDeleter<PluginAtom>()));
02863     ret.push_back(PluginAtomPtr(new TestSetUnionAtom, PluginPtrDeleter<PluginAtom>()));
02864       ret.push_back(PluginAtomPtr(new TestNonmonAtom, PluginPtrDeleter<PluginAtom>()));
02865       ret.push_back(PluginAtomPtr(new TestNonmon2Atom, PluginPtrDeleter<PluginAtom>()));
02866       ret.push_back(PluginAtomPtr(new TestIdAtom, PluginPtrDeleter<PluginAtom>()));
02867       ret.push_back(PluginAtomPtr(new TestIdpAtom, PluginPtrDeleter<PluginAtom>()));
02868       ret.push_back(PluginAtomPtr(new TestIdcAtom, PluginPtrDeleter<PluginAtom>()));
02869       ret.push_back(PluginAtomPtr(new TestNegAtom, PluginPtrDeleter<PluginAtom>()));
02870       ret.push_back(PluginAtomPtr(new TestMinusOneAtom, PluginPtrDeleter<PluginAtom>()));
02871       ret.push_back(PluginAtomPtr(new TestEvenAtom, PluginPtrDeleter<PluginAtom>()));
02872       ret.push_back(PluginAtomPtr(new TestOddAtom, PluginPtrDeleter<PluginAtom>()));
02873       ret.push_back(PluginAtomPtr(new TestLessThanAtom, PluginPtrDeleter<PluginAtom>()));
02874       ret.push_back(PluginAtomPtr(new TestEqualAtom, PluginPtrDeleter<PluginAtom>()));
02875       ret.push_back(PluginAtomPtr(new TestTransitiveClosureAtom, PluginPtrDeleter<PluginAtom>()));
02876       ret.push_back(PluginAtomPtr(new TestCycleAtom, PluginPtrDeleter<PluginAtom>()));
02877       ret.push_back(PluginAtomPtr(new TestAppendAtom, PluginPtrDeleter<PluginAtom>()));
02878       ret.push_back(PluginAtomPtr(new TestDisjAtom, PluginPtrDeleter<PluginAtom>()));
02879       ret.push_back(PluginAtomPtr(new TestHashAtom, PluginPtrDeleter<PluginAtom>()));
02880       ret.push_back(PluginAtomPtr(new TestTrueMultiInpAtom, PluginPtrDeleter<PluginAtom>()));
02881       ret.push_back(PluginAtomPtr(new TestTrueMultiInpAtom2, PluginPtrDeleter<PluginAtom>()));
02882       ret.push_back(PluginAtomPtr(new TestReachableAtom, PluginPtrDeleter<PluginAtom>()));
02883       ret.push_back(PluginAtomPtr(new TestDLSimulatorAtom, PluginPtrDeleter<PluginAtom>()));
02884       ret.push_back(PluginAtomPtr(new TestCautiousQueryAtom(ctx), PluginPtrDeleter<PluginAtom>()));
02885       ret.push_back(PluginAtomPtr(new TestBraveQueryAtom(ctx), PluginPtrDeleter<PluginAtom>()));
02886           ret.push_back(PluginAtomPtr(new TestGen2Atom("gen1", 1), PluginPtrDeleter<PluginAtom>()));
02887           ret.push_back(PluginAtomPtr(new TestGen2Atom("gen2", 2), PluginPtrDeleter<PluginAtom>()));
02888           ret.push_back(PluginAtomPtr(new TestGen2Atom("gen3", 3), PluginPtrDeleter<PluginAtom>()));
02889           ret.push_back(PluginAtomPtr(new TestIsEmpty, PluginPtrDeleter<PluginAtom>()));
02890           ret.push_back(PluginAtomPtr(new TestNumberOfBalls, PluginPtrDeleter<PluginAtom>()));
02891           ret.push_back(PluginAtomPtr(new TestNumberOfBallsSE, PluginPtrDeleter<PluginAtom>()));
02892           ret.push_back(PluginAtomPtr(new TestNumberOfBallsGE, PluginPtrDeleter<PluginAtom>()));
02893           ret.push_back(PluginAtomPtr(new SumNonZeroAtom, PluginPtrDeleter<PluginAtom>()));
02894           ret.push_back(PluginAtomPtr(new ProductionRequirementsAtom, PluginPtrDeleter<PluginAtom>()));
02895 
02896     return ret;
02897     }
02898 
02899 
02900   virtual void setupProgramCtx(ProgramCtx& ctx)
02901     {
02902         TestPlugin::CtxData& pcd = ctx.getPluginData<TestPlugin>();
02903 
02904         if( pcd.testRepetition )
02905         {
02906             ctx.finalCallbacks.push_back(
02907                     FinalCallbackPtr(new TestFinalCallback(ctx)));
02908         }
02909     }
02910 };
02911 
02912 TestPlugin theTestPlugin;
02913 
02914 DLVHEX_NAMESPACE_END
02915 
02916 IMPLEMENT_PLUGINABIVERSIONFUNCTION
02917 
02918 // return plain C type s.t. all compilers and linkers will like this code
02919 extern "C"
02920 DLVHEX_PLUGINEXPORT
02921 void * PLUGINIMPORTFUNCTION()
02922 {
02923     return reinterpret_cast<void*>(& DLVHEX_NAMESPACE theTestPlugin);
02924 }
02925 
02926 /* vim: set noet sw=2 ts=2 tw=80: */