diff --git a/libPostTagSystem/PostTagHistory.cpp b/libPostTagSystem/PostTagHistory.cpp index 8dcbace..3d55044 100644 --- a/libPostTagSystem/PostTagHistory.cpp +++ b/libPostTagSystem/PostTagHistory.cpp @@ -49,25 +49,41 @@ class PostTagHistory::Implementation { const TagState& init, const EvaluationLimits& limits, const CheckpointSpec& checkpointSpec) { + return evaluate(rule, std::vector({init}), limits, checkpointSpec).front(); + } + + std::vector evaluate(const NamedRule& rule, + const std::vector& inits, + const EvaluationLimits& limits, + const CheckpointSpec& checkpointSpec) { const ChunkEvaluationTable chunkEvaluationTable = createChunkEvaluationTable(rule); if (limits.maxEventCount % chunkEvaluationTable.eventsAtOnce != 0) { - return {ConclusionReason::InvalidInput, {{}, std::numeric_limits::max()}, 0, 0}; + return std::vector( + inits.size(), {ConclusionReason::InvalidInput, {{}, std::numeric_limits::max()}, 0, 0}); } - auto chunkedState = toChunkedState(init); - CheckpointsTrie checkpointsTrie; + CheckpointsTrie explicitCheckpointsTrie; for (const auto& checkpoint : checkpointSpec.states) { - checkpointsTrie.insert(toChunkedState(checkpoint)); + explicitCheckpointsTrie.insert(toChunkedState(checkpoint)); } - uint64_t maxIntermediateTapeLength = tapeLength(chunkedState); - ConclusionReason conclusionReason; - const auto eventCount = evaluate(chunkEvaluationTable, - &chunkedState, - &conclusionReason, - &maxIntermediateTapeLength, - limits, - &checkpointsTrie, - checkpointSpec.flags); - return {conclusionReason, fromChunkedStateDestructively(&chunkedState), eventCount, maxIntermediateTapeLength}; + + std::vector results; + results.reserve(inits.size()); + for (const auto& init : inits) { + auto chunkedState = toChunkedState(init); + uint64_t maxIntermediateTapeLength = tapeLength(chunkedState); + ConclusionReason conclusionReason; + const auto eventCount = evaluate(chunkEvaluationTable, + &chunkedState, + &conclusionReason, + &maxIntermediateTapeLength, + limits, + explicitCheckpointsTrie, + checkpointSpec.flags); + results.push_back( + {conclusionReason, fromChunkedStateDestructively(&chunkedState), eventCount, maxIntermediateTapeLength}); + } + + return results; } private: @@ -79,7 +95,7 @@ class PostTagHistory::Implementation { return foundTable->second; } - ChunkEvaluationTable createChunkEvaluationTable(const ChunkedRule& rule) { + static ChunkEvaluationTable createChunkEvaluationTable(const ChunkedRule& rule) { ChunkEvaluationTable table; table.eventsAtOnce = 8 / rule.inputLength; table.phaseCount = rule.phaseCount; @@ -94,7 +110,7 @@ class PostTagHistory::Implementation { return table; } - ChunkOutput createChunkOutput(const ChunkedRule& rule, const uint8_t inputTape, const uint8_t inputPhase) const { + static ChunkOutput createChunkOutput(const ChunkedRule& rule, const uint8_t inputTape, const uint8_t inputPhase) { uint16_t output = 0; uint8_t outputSize = 0; auto phase = inputPhase; @@ -110,7 +126,7 @@ class PostTagHistory::Implementation { return {output, outputSize, phase}; } - ChunkedState toChunkedState(const TagState& state) const { + static ChunkedState toChunkedState(const TagState& state) { ChunkedState result; for (size_t i = 0; i < state.tape.size(); ++i) { if (i % 8 == 0) result.chunks.push_back(0); @@ -123,7 +139,7 @@ class PostTagHistory::Implementation { return result; } - TagState fromChunkedStateDestructively(ChunkedState* chunkedState) const { + static TagState fromChunkedStateDestructively(ChunkedState* chunkedState) { std::vector tape; if (chunkedState->chunks.size()) { tape.reserve(8 * (chunkedState->chunks.size() - 1) + chunkedState->lastChunkSize); @@ -145,13 +161,14 @@ class PostTagHistory::Implementation { } } - uint64_t evaluate(const ChunkEvaluationTable& evaluationTable, - ChunkedState* state, - ConclusionReason* conclusionReason, - uint64_t* maxIntermediateTapeLength, - const EvaluationLimits& limits, - CheckpointsTrie* checkpoints, - const CheckpointSpecFlags& checkpointFlags) const { + static uint64_t evaluate(const ChunkEvaluationTable& evaluationTable, + ChunkedState* state, + ConclusionReason* conclusionReason, + uint64_t* maxIntermediateTapeLength, + const EvaluationLimits& limits, + const CheckpointsTrie& explicitCheckpoints, + const CheckpointSpecFlags& checkpointFlags) { + CheckpointsTrie automaticCheckpoints; uint64_t eventCount; for (eventCount = 0; eventCount < limits.maxEventCount && state->chunks.size() > 1; eventCount += evaluationTable.eventsAtOnce) { @@ -159,12 +176,16 @@ class PostTagHistory::Implementation { *conclusionReason = ConclusionReason::MaxTapeLengthExceeded; return eventCount; } - if (checkpoints->contains(*state)) { - *conclusionReason = ConclusionReason::ReachedCheckpoint; + if (explicitCheckpoints.contains(*state)) { + *conclusionReason = ConclusionReason::ReachedExplicitCheckpoint; + return eventCount; + } + if (automaticCheckpoints.contains(*state)) { + *conclusionReason = ConclusionReason::ReachedAutomaticCheckpoint; return eventCount; } if (checkpointFlags.powerOfTwoEventCounts && !isPowerOfTwo(eventCount)) { - checkpoints->insert(*state); + automaticCheckpoints.insert(*state); } evaluateOnce(evaluationTable, state); *maxIntermediateTapeLength = std::max(*maxIntermediateTapeLength, tapeLength(*state)); @@ -179,11 +200,11 @@ class PostTagHistory::Implementation { static inline bool isPowerOfTwo(const uint64_t number) { return number & (number - 1); } - uint64_t tapeLength(const ChunkedState& state) const { + static uint64_t tapeLength(const ChunkedState& state) { return std::max(0, static_cast(state.chunks.size()) - 1) * 8 + state.lastChunkSize; } - void evaluateOnce(const ChunkEvaluationTable& evaluationTable, ChunkedState* state) const { + static void evaluateOnce(const ChunkEvaluationTable& evaluationTable, ChunkedState* state) { const auto nextChunkIndex = evaluationTable.phaseCount * state->chunks.front() + state->phase; const auto& chunkOutput = evaluationTable.outputs[nextChunkIndex]; state->chunks.pop_front(); @@ -217,4 +238,11 @@ PostTagHistory::EvaluationResult PostTagHistory::evaluate(const NamedRule& rule, const CheckpointSpec& checkpoints) { return implementation_->evaluate(rule, init, limits, checkpoints); } + +std::vector PostTagHistory::evaluate(const NamedRule& rule, + const std::vector& inits, + const EvaluationLimits& limits, + const CheckpointSpec& checkpoints) { + return implementation_->evaluate(rule, inits, limits, checkpoints); +} } // namespace PostTagSystem diff --git a/libPostTagSystem/PostTagHistory.hpp b/libPostTagSystem/PostTagHistory.hpp index ef7f46a..6cbc6ef 100644 --- a/libPostTagSystem/PostTagHistory.hpp +++ b/libPostTagSystem/PostTagHistory.hpp @@ -13,7 +13,8 @@ class PostTagHistory { enum class ConclusionReason { InvalidInput, Terminated, - ReachedCheckpoint, + ReachedExplicitCheckpoint, + ReachedAutomaticCheckpoint, MaxEventCountExceeded, MaxTapeLengthExceeded }; @@ -51,6 +52,11 @@ class PostTagHistory { const EvaluationLimits& limits, const CheckpointSpec& checkpointSpec = CheckpointSpec()); + std::vector evaluate(const NamedRule& rule, + const std::vector& inits, + const EvaluationLimits& limits, + const CheckpointSpec& checkpointSpec = CheckpointSpec()); + private: class Implementation; std::shared_ptr implementation_; diff --git a/libPostTagSystem/PostTagSearcher.cpp b/libPostTagSystem/PostTagSearcher.cpp index e176e65..5ecc032 100644 --- a/libPostTagSystem/PostTagSearcher.cpp +++ b/libPostTagSystem/PostTagSearcher.cpp @@ -37,7 +37,6 @@ class PostTagSearcher::Implementation { std::vector evaluateGroup(const std::vector& states, const EvaluationParameters& parameters) { // TODO(maxitg): Implement groupTimeConstraintNs parameter - // TODO(maxitg): Implement checkpoints parameter std::vector results; results.reserve(states.size()); @@ -45,23 +44,28 @@ class PostTagSearcher::Implementation { PostTagHistory::EvaluationLimits limits; limits.maxEventCount = parameters.maxEventCount; limits.maxTapeLength = parameters.maxTapeLength; - for (const auto& init : states) { - const auto singleInitResult = evaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {{}, {true}}); + const auto singleInitResults = + evaluator.evaluate(PostTagHistory::NamedRule::Post, states, limits, {parameters.checkpoints, {true}}); + for (size_t initIndex = 0; initIndex < states.size(); ++initIndex) { EvaluationResult result; - result.eventCount = singleInitResult.eventCount; - result.maxTapeLength = singleInitResult.maxIntermediateTapeLength; - result.finalTapeLength = singleInitResult.finalState.tape.size(); - result.initialState = init; - result.finalState = singleInitResult.finalState; - switch (singleInitResult.conclusionReason) { + result.eventCount = singleInitResults[initIndex].eventCount; + result.maxTapeLength = singleInitResults[initIndex].maxIntermediateTapeLength; + result.finalTapeLength = singleInitResults[initIndex].finalState.tape.size(); + result.initialState = states[initIndex]; + result.finalState = singleInitResults[initIndex].finalState; + switch (singleInitResults[initIndex].conclusionReason) { case PostTagHistory::ConclusionReason::Terminated: result.conclusionReason = ConclusionReason::Terminated; break; - case PostTagHistory::ConclusionReason::ReachedCheckpoint: + case PostTagHistory::ConclusionReason::ReachedAutomaticCheckpoint: result.conclusionReason = ConclusionReason::ReachedCycle; break; + case PostTagHistory::ConclusionReason::ReachedExplicitCheckpoint: + result.conclusionReason = ConclusionReason::ReachedKnownCheckpoint; + break; + case PostTagHistory::ConclusionReason::MaxEventCountExceeded: result.conclusionReason = ConclusionReason::MaxEventCountExceeded; break; diff --git a/libPostTagSystem/test/PostTagSearcher_test.cpp b/libPostTagSystem/test/PostTagSearcher_test.cpp index 3e6ec96..ce2de71 100644 --- a/libPostTagSystem/test/PostTagSearcher_test.cpp +++ b/libPostTagSystem/test/PostTagSearcher_test.cpp @@ -27,17 +27,20 @@ TEST(PostTagSearcher, emptyCases) { void compareResults(const TagState& init, const PostTagSearcher::EvaluationResult& result, - uint64_t eventLimit = std::numeric_limits::max() - 7) { + const PostTagHistory::EvaluationLimits& limits = PostTagHistory::EvaluationLimits(), + const std::vector& checkpoints = {}) { PostTagHistory singleHistoryEvaluator; - const auto singleResult = singleHistoryEvaluator.evaluate( - PostTagHistory::NamedRule::Post, init, PostTagHistory::EvaluationLimits(eventLimit), {{}, {true}}); + const auto singleResult = + singleHistoryEvaluator.evaluate(PostTagHistory::NamedRule::Post, init, limits, {checkpoints, {true}}); if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::InvalidInput) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::InvalidInput); } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::Terminated) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::Terminated); - } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedCheckpoint) { + } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedAutomaticCheckpoint) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::ReachedCycle); + } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::ReachedExplicitCheckpoint) { + ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::ReachedKnownCheckpoint); } else if (singleResult.conclusionReason == PostTagHistory::ConclusionReason::MaxEventCountExceeded) { ASSERT_EQ(result.conclusionReason, PostTagSearcher::ConclusionReason::MaxEventCountExceeded); } @@ -100,7 +103,20 @@ TEST(PostTagSearcher, eventLimit) { {{0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0}, {{0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 2}, parameters); ASSERT_EQ(result.size(), 2); - compareResults(TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0), result[0], 104); - compareResults(TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 1), result[1], 104); + compareResults( + TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 0), result[0], PostTagHistory::EvaluationLimits(104)); + compareResults( + TagState({0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1}, 1), result[1], PostTagHistory::EvaluationLimits(104)); +} + +TEST(PostTagSearcher, checkpoints) { + PostTagSearcher searcher; + PostTagSearcher::EvaluationParameters parameters; + parameters.checkpoints = {{{0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0}, 2}}; + const auto result = searcher.evaluateRange(20, 123, 125, parameters); + ASSERT_EQ(result.size(), 6); + + compareResults(TagState(20, 123, 0), result[0], PostTagHistory::EvaluationLimits(), parameters.checkpoints); + compareResults(TagState(20, 124, 0), result[3], PostTagHistory::EvaluationLimits(), parameters.checkpoints); } } // namespace PostTagSystem