Hey all. Found a major flaw in the design of Sentence, and fixed it. Now Predicate, Operator, Equality, Quantifier, each are derived from a new class called TruthValueSentence, parallel in the hierarchy to WorldValueSentence. Now, sentences such as a AND b (where a and b are constants) aren't allowed, as it should be. I also added a clone() function to the Sentence class, which allowes to create a copy of a sentence. I, of course, changed the interface appropriaetly. Again, I must emphasize that the interface, among other things, needs to be thoroughly debugged. On a happy note, we have passed the 1000 lines. When noam finishes his tree functinos, I'm sure will be over 1500 and maybe more... On a sad note, the code is only mine and noam's... Two project members writing hundreds of lines of code... while others write nothing... not even emails. Sad, sad indeed. - Yonatan.
#include "Interface.h" #include "Sentence.h" #include "SentenceTree.h" using namespace std; list<Sentence*>* makeList() { return new list<Sentence*>(); } list<Sentence*>* makeList(Sentence* s1) { list<Sentence*>* s = makeList(); s->push_back(s1); return s; } list<Sentence*>* makeList(Sentence* s1, Sentence* s2) { list<Sentence*>* s = makeList(s1); s->push_back(s2); return s; } list<Sentence*>* makeList(Sentence* s1, Sentence* s2, Sentence* s3) { list<Sentence*>* s = makeList(s1, s2); s->push_back(s3); return s; } int main() { Sentence* s1 = new QuantifierSentence(QuantifierSentence::FORALL, new VariableSentence("x"), new OperatorSentence(OperatorSentence::IMPLIES, new PredicateSentence("P", new VariableSentence("x")), new PredicateSentence("Q", new VariableSentence("x")))); Sentence* s2 = new PredicateSentence("Q", new FunctionSentence("f", new ConstantSentence("a")), new FunctionSentence("f", new ConstantSentence("b"))); Sentence* s3 = new EqualitySentence(new FunctionSentence("g", new ConstantSentence("a")), new VariableSentence("x")); Sentence* s4 = new OperatorSentence(OperatorSentence::AND, new PredicateSentence("P", new ConstantSentence("b")), new PredicateSentence("P", new ConstantSentence("c"))); SentenceTree *t = new SentenceTree(makeList(s1), new SentenceTree(makeList(s3, s4)), new SentenceTree(makeList(s2))); cout << t->toString() << endl; // Sentence* pavel = new PredicateSentence("P", new VariableSentence("x"), new ConstantSentence("a")); // const list<WorldValueSentence*>* l = dynamic_cast<PredicateSentence*>(pavel)->getParameters(); Sentence* s = Interface::iGetTruthValueSentence(Interface::getNameTable()); cout << s->toString() << endl; delete s; Sentence* s1tag = new QuantifierSentence(QuantifierSentence::FORALL, new VariableSentence("x"), new OperatorSentence(OperatorSentence::IMPLIES, new PredicateSentence("P", new VariableSentence("x")), new PredicateSentence("Q", new VariableSentence("x")))); cout << "are they equal?" << (*s1tag == *s1) << (*s1 == *s1tag) << endl; cout << "are THEY equal?" << (*s1 == *s4) << (*s3 == *s2) << endl; Sentence* s1tagayim = s1->clone(); cout << s1->toString() << endl << s1tag->toString() << endl << s1tagayim->toString() << endl; cout << "are THHTHTHEY equal?" << (*s1 == *s1tagayim) << (*s1tag == *s1tagayim) << endl; return 0; }
#include "Interface.h" #include "Sentence.h" #include "NameTable.h" #if 1 #include <iostream> #define OUTPUT_STREAM cout #define INPUT_STREAM cin #endif WorldValueSentence* Interface::iGetWorldValueSentence(const NameTable* nameTable) { OUTPUT_STREAM << "What kind of world value sentence do you want?" << endl << "1. a variable" << endl << "2. a constant" << endl << "3. a function" << endl; int choice; INPUT_STREAM >> choice; switch (choice) { case 1: return iGetVariableSentence(nameTable) ; case 2: return iGetConstantSentence(nameTable) ; case 3: return iGetFunctionSentence(nameTable) ; default: throw BadInputException("Illegal choice (choose 1-3)!"); } return 0; // never reach here. } VariableSentence* Interface::iGetVariableSentence(const NameTable* nameTable, bool quant = false) { // quant = 1 means that it's now going to be quantified (so it can/should be free) OUTPUT_STREAM << "What name do you want for your variable?" << endl; string name; INPUT_STREAM >> name; pair<int, int> ret = nameTable->find(name); if (ret.first != NameTable::VARIABLE) { throw BadInputException("Invalid variable name!"); } if (quant && nameTable->is_quantified(name)) throw BadInputException("Double quantification is disallowed!"); if (!quant && !nameTable->is_quantified(name)) throw BadInputException("This variable is free, but the sentence must be closed!"); if (quant) { nameTable->quantify(name); } return new VariableSentence(name); } ConstantSentence* Interface::iGetConstantSentence(const NameTable* nameTable) { OUTPUT_STREAM << "What name do you want for your constant?" << endl; string name; INPUT_STREAM >> name; pair<int, int> ret = nameTable->find(name); if (ret.first != NameTable::CONSTANT) { throw BadInputException("Invalid constant name!"); } return new ConstantSentence(name); } FunctionSentence* Interface::iGetFunctionSentence(const NameTable* nameTable) { OUTPUT_STREAM << "What name do you want for your function?" << endl; string name; INPUT_STREAM >> name; //OUTPUT_STREAM << "What's " << name << "'s arity (how many parameters does it take)?" << endl; int arity; //INPUT_STREAM >> arity; pair<int, int> ret = nameTable->find(name); if (ret.first != NameTable::FUNCTION) { throw BadInputException("Invalid function name!"); } arity = ret.second; list<WorldValueSentence*>* l = new list<WorldValueSentence*>() ; // check validity of arity for (int i = 1; i <= arity; ++i) { OUTPUT_STREAM << "Give " << i << "-th value: " << endl; l->push_back(iGetWorldValueSentence(nameTable) ); } return new FunctionSentence(name, l); } PredicateSentence* Interface::iGetPredicateSentence(const NameTable* nameTable) { OUTPUT_STREAM << "What name do you want for your predicate?" << endl; string name; INPUT_STREAM >> name; //OUTPUT_STREAM << "What's " << name << "'s arity (how many parameters does it take)?" << endl; int arity; //INPUT_STREAM >> arity; pair<int, int> ret = nameTable->find(name); if (ret.first != NameTable::PREDICATE) { throw BadInputException("Invalid predicate name!"); } arity = ret.second; list<WorldValueSentence*>* l = new list<WorldValueSentence*>() ; // check validity of arity for (int i = 1; i <= arity; ++i) { OUTPUT_STREAM << "Give " << i << "-th value: " << endl; l->push_back(iGetWorldValueSentence(nameTable) ); } return new PredicateSentence(name, l); } EqualitySentence* Interface::iGetEqualitySentence(const NameTable* nameTable) { OUTPUT_STREAM << "Give the world value on the left:" << endl; WorldValueSentence* f = iGetWorldValueSentence(nameTable) ; OUTPUT_STREAM << "Give the world value on the right:" << endl; WorldValueSentence* s = iGetWorldValueSentence(nameTable) ; return new EqualitySentence(f, s); } OperatorSentence* Interface::iGetOperatorSentence(const NameTable* nameTable) { OUTPUT_STREAM << "What operator do you want?" << endl << "1. Not" << endl << "2. And" << endl << "3. Or" << endl << "4. ==>" << endl << "5. <==" << endl << "6. <==>" << endl; int choice; INPUT_STREAM >> choice; if (choice == 1) { OUTPUT_STREAM << "Enter sentence to be \"notted\": " << endl; return new OperatorSentence(OperatorSentence::NOT, iGetTruthValueSentence(nameTable) ); } OUTPUT_STREAM << "Enter sentence on the left: " << endl; TruthValueSentence* s1 = iGetTruthValueSentence(nameTable) ; OUTPUT_STREAM << "Enter sentence on the right: " << endl; TruthValueSentence* s2 = iGetTruthValueSentence(nameTable) ; int op; switch(choice) { case 2: op = OperatorSentence::AND; break; case 3: op = OperatorSentence::OR; break; case 4: op = OperatorSentence::IMPLIES; break; case 5: op = OperatorSentence::IMPLIED; break; case 6: op = OperatorSentence::IFF; break; default: throw BadInputException("Illegal choice (choose 1-6)!"); } return new OperatorSentence(op, s1, s2); } QuantifierSentence* Interface::iGetQuantifierSentence(const NameTable* nameTable) { OUTPUT_STREAM << "What quantifier do you want: " << endl << "1. For all" << endl << "2. There exists" << endl; int choice; INPUT_STREAM >> choice; // check validity of choice OUTPUT_STREAM << "What variable name do you want? " << endl; VariableSentence* x = iGetVariableSentence(nameTable, true); // this will change the nametable QuantifierSentence* q = (choice == 1)? (new QuantifierSentence(QuantifierSentence::FORALL, x, iGetTruthValueSentence(nameTable))) : (new QuantifierSentence(QuantifierSentence::EXISTS, x, iGetTruthValueSentence(nameTable))); nameTable->dequantify(x->getName()); // this will change it back return q; } TruthValueSentence* Interface::iGetTruthValueSentence(const NameTable* nameTable) { TruthValueSentence* s; do { OUTPUT_STREAM << "What type of truth value sentence do you want?" << endl << "1. A predicate sentence" << endl << "2. An equality sentence" << endl << "3. An operator sentence" << endl << "4. A Quantified sentence" << endl; int choice; INPUT_STREAM >> choice; try { switch (choice) { case 1: s = iGetPredicateSentence(nameTable); break; case 2: s = iGetEqualitySentence(nameTable); break; case 3: s = iGetOperatorSentence(nameTable); break; case 4: s = iGetQuantifierSentence(nameTable); break; default: OUTPUT_STREAM << "Invalid choice (choose 1-4)!" << endl; continue; } break; }catch(BadInputException e) { OUTPUT_STREAM << e.error << endl; } }while(1); return s; } TruthValueSentence* Interface::niGetTruthValueSentence(const NameTable* nameTable) { // not implemenetd yet return 0; } NameTable* Interface::getNameTable() { OUTPUT_STREAM << "Welcome to the name table input utility." << endl << "Please enter all the names you intend to use " << endl << "(constants, variables, functions, and predicates)" << endl << "And when you wish to quit, type \"quit\"" << endl; NameTable* nameTable = new NameTable(); while (1) { OUTPUT_STREAM << "Enter name: (or quit to quit)" << endl; string name; INPUT_STREAM >> name; if (name == "quit") break; OUTPUT_STREAM << "Enter v for variable, c for constant, p for predicate, f for function: " << endl; string choice; INPUT_STREAM >> choice; if ((choice == "v") || (choice == "V")) { OUTPUT_STREAM << nameTable->insert(name, NameTable::VARIABLE); } else if ((choice == "c") || (choice == "C")) { OUTPUT_STREAM << nameTable->insert(name, NameTable::CONSTANT); } else if ((choice == "p") || (choice == "P")) { OUTPUT_STREAM << "Please give arity: " << endl; int arity; INPUT_STREAM >> arity; OUTPUT_STREAM << nameTable->insert(name, NameTable::PREDICATE, arity); } else if ((choice == "f") || (choice == "F")) { OUTPUT_STREAM << "Please give arity: " << endl; int arity; INPUT_STREAM >> arity; OUTPUT_STREAM << nameTable->insert(name, NameTable::FUNCTION, arity); } } return nameTable; }
#ifndef __INTERFACE_H__ #define __INTERFACE_H__ #include "Sentence.h" #include "NameTable.h" class Interface { private: struct BadInputException { // for light (non-hardware) cases string error; BadInputException(string e): error(e) {} }; static WorldValueSentence* iGetWorldValueSentence(const NameTable* nameTable); static VariableSentence* iGetVariableSentence(const NameTable* nameTable, bool quant = false); static ConstantSentence* iGetConstantSentence(const NameTable* nameTable); static FunctionSentence* iGetFunctionSentence(const NameTable* nameTable); static PredicateSentence* iGetPredicateSentence(const NameTable* nameTable); static EqualitySentence* iGetEqualitySentence(const NameTable* nameTable); static OperatorSentence* iGetOperatorSentence(const NameTable* nameTable); static QuantifierSentence* iGetQuantifierSentence(const NameTable* nameTable); public: static TruthValueSentence* iGetTruthValueSentence(const NameTable* nameTable); // interactive static TruthValueSentence* niGetTruthValueSentence(const NameTable* nameTable); // non-interactive static NameTable* getNameTable(); }; #endif
#include "Sentence.h" #include "PointerComparator.h" #include <sstream> using namespace::std; Sentence::Sentence() {} Sentence::~Sentence() {} WorldValueSentence::WorldValueSentence(string s): name(s) {} const string WorldValueSentence::getName() const { return name; } TruthValueSentence::TruthValueSentence() {} ConstantSentence::ConstantSentence(string s): WorldValueSentence(s) {} string ConstantSentence::toString() const { return name; } bool ConstantSentence::equals(const Sentence* s) const { const ConstantSentence* cs; if ((cs = dynamic_cast<const ConstantSentence*>(s))) { return (cs->name == name); } else return false; } ConstantSentence* ConstantSentence::clone() const { return new ConstantSentence(name); } VariableSentence::VariableSentence(string s): WorldValueSentence(s) {} string VariableSentence::toString() const { return name; } bool VariableSentence::equals(const Sentence* s) const { const VariableSentence* vs; if ((vs = dynamic_cast<const VariableSentence*>(s))) { return (vs->name == name); } else return false; } VariableSentence* VariableSentence::clone() const { return new VariableSentence(name); } FunctionSentence::FunctionSentence(): WorldValueSentence("") {} FunctionSentence::FunctionSentence(string s, WorldValueSentence* par): WorldValueSentence(s), arity(1) { parameters = new list<WorldValueSentence*>(); parameters->push_back(par); } FunctionSentence::FunctionSentence(string s, WorldValueSentence* par1, WorldValueSentence* par2): WorldValueSentence(s), arity(2) { parameters = new list<WorldValueSentence*>(); parameters->push_back(par1); parameters->push_back(par2); } FunctionSentence::FunctionSentence(string s, list<WorldValueSentence*>* l): WorldValueSentence(s), parameters(l) { if (l != 0) { // should throw something if it IS! arity = l->size(); } } FunctionSentence::~FunctionSentence() { for (list<WorldValueSentence*>::iterator it = parameters->begin(); it != parameters->end(); it++) { delete(*it); } delete parameters; } string FunctionSentence::toString() const { stringstream s; s << name << "_" << arity << "("; list<WorldValueSentence*>::const_iterator it = parameters->begin(); if (it == parameters->end()) { s << ")"; return s.str(); } s << (*it)->toString(); for (it++; it != parameters->end(); it++) { s << ", "; s << (*it)->toString(); } s << ")"; return s.str(); } int FunctionSentence::getArity() const { return arity; } const list<WorldValueSentence*>* FunctionSentence::getParameters() const { return parameters; } bool FunctionSentence::equals(const Sentence* s) const { const FunctionSentence* fs; if ((fs = dynamic_cast<const FunctionSentence*>(s))) { if ((fs->name == name) && (fs->arity == arity) && (std::equal(fs->parameters->begin(), fs->parameters->end(), parameters->begin(), PointerComparator<Sentence>()))) return true; } return false; } FunctionSentence* FunctionSentence::clone() const { FunctionSentence* fs = new FunctionSentence(); fs->name = name; fs->arity = arity; fs->parameters = new list<WorldValueSentence*>; for (list<WorldValueSentence*>::const_iterator it = parameters->begin(); it != parameters->end(); it++) { WorldValueSentence* wv = (*it)->clone(); fs->parameters->push_back(wv); } return fs; } PredicateSentence::PredicateSentence() {} PredicateSentence::PredicateSentence(string s, WorldValueSentence* par): name(s), arity(1) { parameters = new list<WorldValueSentence*>(); parameters->push_back(par); } PredicateSentence::PredicateSentence(string s, WorldValueSentence* par1, WorldValueSentence* par2): name(s), arity(2) { parameters = new list<WorldValueSentence*>(); parameters->push_back(par1); parameters->push_back(par2); } PredicateSentence::PredicateSentence(string s, list<WorldValueSentence*>* l): name(s), parameters(l) { if (l != 0) { // should throw something if it IS! arity = l->size(); } } PredicateSentence::~PredicateSentence() { for (list<WorldValueSentence*>::iterator it = parameters->begin(); it != parameters->end(); it++) { delete(*it); } delete parameters; } string PredicateSentence::toString() const { stringstream s; s << name << "_" << arity << "("; list<WorldValueSentence*>::const_iterator it = parameters->begin(); if (it == parameters->end()) { s << ")"; return s.str(); } s << (*it)->toString(); for (it++; it != parameters->end(); it++) { s << ", "; s << (*it)->toString(); } s << ")"; return s.str(); } const string PredicateSentence::getName() const { return name; } int PredicateSentence::getArity() const { return arity; } const std::list<WorldValueSentence*>* PredicateSentence::getParameters() const { return parameters; } bool PredicateSentence::equals(const Sentence* s) const { const PredicateSentence* ps; if ((ps = dynamic_cast<const PredicateSentence*>(s))) { if ((ps->name == name) && (ps->arity == arity) && (equal(ps->parameters->begin(), ps->parameters->end(), parameters->begin(), PointerComparator<Sentence>()))) return true; } return false; } PredicateSentence* PredicateSentence::clone() const { PredicateSentence* ps = new PredicateSentence(); ps->name = name; ps->arity = arity; ps->parameters = new list<WorldValueSentence*>; for (list<WorldValueSentence*>::const_iterator it = parameters->begin(); it != parameters->end(); it++) { ps->parameters->push_back((*it)->clone()); } return ps; } EqualitySentence::EqualitySentence(WorldValueSentence* f, WorldValueSentence* s): first(f), second(s) {} EqualitySentence::~EqualitySentence() { delete first; delete second; } string EqualitySentence::toString() const { string s; s += "("; s += first->toString(); s += ")"; s += "="; s += "("; s += second->toString(); s += ")"; return s; } const WorldValueSentence* EqualitySentence::getFirst() const { return first; } const WorldValueSentence* EqualitySentence::getSecond() const { return second; } bool EqualitySentence::equals(const Sentence* s) const { const EqualitySentence* es; if ((es = dynamic_cast<const EqualitySentence*>(s))) { if ((*first == *es->first) && (*second == *es->second)) return true; } return false; } EqualitySentence* EqualitySentence::clone() const { return new EqualitySentence(first->clone(), second->clone()); } OperatorSentence::OperatorSentence(int o, TruthValueSentence* f, TruthValueSentence* s = 0): op(o), firstOperand(f), secondOperand(s) {} OperatorSentence::~OperatorSentence() { delete firstOperand; delete secondOperand; } string OperatorSentence::toString() const { string s; if (op == NOT) { s += "NOT("; s += firstOperand->toString(); s += ")"; } else { s += "("; s += firstOperand->toString(); s += ")"; switch (op) { case AND: s += " AND "; break; case OR: s += " OR "; break; case IMPLIES: s+= " ==> "; break; case IMPLIED: s+= " <== "; break; case IFF: s += " <==> "; break; } s += "("; s += secondOperand->toString(); s += ")"; } return s; } int OperatorSentence::getOp() const { return op; } const TruthValueSentence* OperatorSentence::getFirstOperand() const { return firstOperand; } const TruthValueSentence* OperatorSentence::getSecondOperand() const { return secondOperand; } bool OperatorSentence::equals(const Sentence* s) const { const OperatorSentence* os; if ((os = dynamic_cast<const OperatorSentence*>(s))) { if ((*firstOperand == *os->firstOperand)) { if (((!secondOperand) && (!os->secondOperand)) || (secondOperand && os->secondOperand && *secondOperand == *os->secondOperand)) return true; } } return false; } OperatorSentence* OperatorSentence::clone() const { return new OperatorSentence(op, firstOperand->clone(), secondOperand->clone()); } QuantifierSentence::QuantifierSentence(int q, VariableSentence* v, TruthValueSentence* s): quantifier(q), variable(v), sentence(s) {} string QuantifierSentence::toString() const { string s; switch (quantifier) { case FORALL: s += "For all "; s += variable->toString(); s += ", ("; s += sentence->toString(); s += ")"; break; case EXISTS: s += "Exists "; s += variable->toString(); s += ", ("; s += sentence->toString(); s += ")"; break; } return s; } int QuantifierSentence::getQuantifier() const { return quantifier; } const WorldValueSentence* QuantifierSentence::getVariable() const { return variable; } const TruthValueSentence* QuantifierSentence::getSentence() const { return sentence; } bool QuantifierSentence::equals(const Sentence* s) const { const QuantifierSentence* qs; if ((qs = dynamic_cast<const QuantifierSentence*>(s))) { if ((*variable == *qs->variable) && (*sentence == *qs->sentence)) return true; } return false; } QuantifierSentence* QuantifierSentence::clone() const { return new QuantifierSentence(quantifier, variable->clone(), sentence->clone()); }
#ifndef __SENTENCE_H__ #define __SENTENCE_H__ // notes: // must have names of predicates and private_names #include <string> #include <map> #include <list> class Sentence { protected: virtual bool equals(const Sentence* s) const = 0; public: Sentence(); virtual std::string toString() const = 0; virtual Sentence* clone() const = 0; virtual ~Sentence(); friend inline bool operator==(const Sentence& s1, const Sentence& s2) { return s1.equals(&s2); } friend inline bool operator!=(const Sentence& s1, const Sentence& s2) { return !s1.equals(&s2); } }; class WorldValueSentence: public Sentence { protected: std::string name; public: WorldValueSentence(std::string s); const string getName() const; virtual WorldValueSentence* clone() const = 0; }; class TruthValueSentence: public Sentence { public: TruthValueSentence(); virtual TruthValueSentence* clone() const = 0; }; class ConstantSentence : public WorldValueSentence { private: bool equals(const Sentence* s) const; public: ConstantSentence(std::string s); std::string toString() const; ConstantSentence* clone() const; }; class VariableSentence: public WorldValueSentence { private: bool equals(const Sentence* s) const; public: VariableSentence(std::string s); std::string toString() const; VariableSentence* clone() const; }; class FunctionSentence: public WorldValueSentence { private: bool equals(const Sentence* s) const; int arity; std::list<WorldValueSentence*>* parameters; FunctionSentence(); public: FunctionSentence(std::string s, WorldValueSentence* par); FunctionSentence(std::string s, WorldValueSentence* par1, WorldValueSentence*\ par2); FunctionSentence(std::string s, std::list<WorldValueSentence*>* l); ~FunctionSentence(); std::string toString() const; int getArity() const; const std::list<WorldValueSentence*>* getParameters() const; FunctionSentence* clone() const; }; class PredicateSentence: public TruthValueSentence { private: bool equals(const Sentence* s) const; std::string name; int arity; std::list<WorldValueSentence*>* parameters; PredicateSentence(); public: PredicateSentence(std::string s, WorldValueSentence* par); PredicateSentence(std::string s, WorldValueSentence* par1, WorldValueSentence* par2); PredicateSentence(std::string s, std::list<WorldValueSentence*>* l); ~PredicateSentence(); std::string toString() const; const string getName() const; int getArity() const; const std::list<WorldValueSentence*>* getParameters() const; PredicateSentence* clone() const; }; class EqualitySentence: public TruthValueSentence { private: bool equals(const Sentence* s) const; WorldValueSentence* first; WorldValueSentence* second; public: EqualitySentence(WorldValueSentence* f, WorldValueSentence* s); ~EqualitySentence(); std::string toString() const; const WorldValueSentence* getFirst() const; const WorldValueSentence* getSecond() const; EqualitySentence* clone() const; }; class OperatorSentence: public TruthValueSentence { private: bool equals(const Sentence* s) const; int op; TruthValueSentence* firstOperand; TruthValueSentence* secondOperand; // not always used public: static const int AND = 1; static const int OR = 2; static const int NOT = 3; static const int IMPLIES = 4; static const int IMPLIED = 5; static const int IFF = 6; OperatorSentence(int o, TruthValueSentence* f, TruthValueSentence* s = 0); ~OperatorSentence(); std::string toString() const; int getOp() const; const TruthValueSentence* getFirstOperand() const; const TruthValueSentence* getSecondOperand() const; OperatorSentence* clone() const; }; class QuantifierSentence: public TruthValueSentence { private: bool equals(const Sentence* s) const; int quantifier; VariableSentence* variable; TruthValueSentence* sentence; public: static const int FORALL = 1; static const int EXISTS = 2; QuantifierSentence(int q, VariableSentence* v, TruthValueSentence* s); std::string toString() const; int getQuantifier() const; const WorldValueSentence* getVariable() const; const TruthValueSentence* getSentence() const; QuantifierSentence* clone() const; }; #endif