Use only in-memory state. Document identifiers are nonempty strings; text and query arguments are arbitrary strings. Limits are integers within the declared C++ numeric type. Tokenization lowercases Unicode code points and extracts consecutive ASCII letter/digit runs, as described above. Empty or punctuation-only documents are valid.
Prototype: implement tokenize() using the lowercase/ASCII-run rule above. Preserve order and duplicate tokens, and return a new list on every call.
Final: implement SearchEngine():
add(): returnfalsewithout mutation if that identifier already exists; otherwise store it and returntrue.get(): return the exact original text, ornulloptif missing.update(): replace an existing document and all of its indexed content; returnfalsewithout mutation if missing.erase(): remove the document and its indexed content; returnfalseif missing. A deleted identifier may be added again.search(): tokenize the query and require exactly one token, otherwise raisestd::invalid_argument. Return each matching document once, scored by that token's occurrence count.search_phrase(): tokenize the query and match consecutive tokens. Score by number of starting positions, including overlaps. An empty token sequence returns[]. A one-token phrase behaves like term search.- Both search methods rank by score descending, then document identifier lexicographically ascending, and return at most
limitresults. Zero yields[]; negative limits raisestd::invalid_argument. Term-query validation still applies with limit zero. Lists are fresh snapshots. stats(): return(document_count, total_token_count)across current documents, including empty documents in the document count. Updates and deletions must keep these values correct.
No stale matches may survive updates, deletion, or identifier reuse. Raised errors preserve state, and separate instances are independent.
The public C++ API below is authoritative for field types and return values. erase is used where another language might use a method named delete, because delete is a C++ keyword. Returned records are value snapshots with the documented named fields; mutating a record or its nested vectors cannot affect the service.
inline Strings tokenize(const Text& text);
class SearchEngine { public:
SearchEngine();
bool add(const std::string& id,const Text& text);
Opt<Text> get(const std::string& id) const;
bool update(const std::string& id,const Text& text);
bool erase(const std::string& id);
Vec<std::pair<std::string,Int>> search(const Text& term,Int limit=10) const;
Vec<std::pair<std::string,Int>> search_phrase(const Text& phrase,Int limit=10) const;
Pair stats() const;
};Compile and run from this directory:
c++ -std=c++20 -O2 -Wall -Wextra -pedantic -pthread main.cpp -o practice
./practice --stage prototype
./practice --stage finalEdit exercise.hpp. Both stages start unfinished and must report failures until implemented. Keep main.cpp as the executable contract. The runner returns nonzero for a failed check, an unexpected exception, an invalid stage, or an empty suite. C++20 and only the standard library are required.
Complete the starter code, then run the checks.
Results and errors will appear here.
C++ runtime powered by Wasmer.