text:
/**
 *    Copyright (C) 2018-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    <http://www.mongodb.com/licensing/server-side-public-license>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 */

#include "mongo/platform/basic.h"

#include <algorithm>

#include <boost/optional/optional.hpp>
#include <boost/optional/optional_io.hpp>

#include "mongo/client/read_preference.h"
#include "mongo/db/client.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/cursor_manager.h"
#include "mongo/db/cursor_server_params.h"
#include "mongo/db/exec/queued_data_stage.h"
#include "mongo/db/exec/working_set.h"
#include "mongo/db/exec/working_set_common.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/query/plan_executor_factory.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/query_test_service_context.h"
#include "mongo/db/repl/read_concern_level.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/clock_source_mock.h"
#include "mongo/util/scopeguard.h"

namespace mongo {
namespace {
const NamespaceString kTestNss{"test.collection"};

class CursorManagerTest : public unittest::Test {
public:
    CursorManagerTest()
        : _queryServiceContext(std::make_unique<QueryTestServiceContext>()),
          _cursorManager(nullptr) {
        _queryServiceContext->getServiceContext()->setPreciseClockSource(
            std::make_unique<ClockSourceMock>());

        _cursorManager.setPreciseClockSource(
            _queryServiceContext->getServiceContext()->getPreciseClockSource());
    }

    void setUp() override {
        _opCtx = _queryServiceContext->makeOperationContext();
    }

    void tearDown() override {
        // Do nothing.
    }

    std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> makeFakePlanExecutor() {
        return makeFakePlanExecutor(_opCtx.get());
    }

    std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> makeFakePlanExecutor(
        OperationContext* opCtx) {
        // Create a mock ExpressionContext.
        auto expCtx = make_intrusive<ExpressionContext>(opCtx, nullptr, kTestNss);

        auto workingSet = std::make_unique<WorkingSet>();
        auto queuedDataStage = std::make_unique<QueuedDataStage>(expCtx.get(), workingSet.get());
        return unittest::assertGet(
            plan_executor_factory::make(expCtx,
                                        std::move(workingSet),
                                        std::move(queuedDataStage),
                                        &CollectionPtr::null,
                                        PlanYieldPolicy::YieldPolicy::NO_YIELD,
                                        QueryPlannerParams::DEFAULT,
                                        kTestNss));
    }

    ClientCursorParams makeParams(OperationContext* opCtx) {
        return {
            makeFakePlanExecutor(opCtx),
            kTestNss,
            {},
            APIParameters(),
            opCtx->getWriteConcern(),
            repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
            ReadPreferenceSetting(ReadPreference::PrimaryOnly),
            BSONObj(),
            PrivilegeVector(),
        };
    }

    ClientCursorPin makeCursor(OperationContext* opCtx) {
        return useCursorManager()->registerCursor(opCtx, makeParams(opCtx));
    }

    ClockSourceMock* useClock() {
        auto svcCtx = _queryServiceContext->getServiceContext();
        return static_cast<ClockSourceMock*>(svcCtx->getPreciseClockSource());
    }

    CursorManager* useCursorManager() {
        return &_cursorManager;
    }

protected:
    std::unique_ptr<QueryTestServiceContext> _queryServiceContext;
    ServiceContext::UniqueOperationContext _opCtx;

private:
    CursorManager _cursorManager;
};

class CursorManagerTestCustomOpCtx : public CursorManagerTest {
    void setUp() override {
        // Do nothing.
    }

    void tearDown() override {
        // Do nothing.
    }
};

/**
 * Test that an attempt to kill a pinned cursor succeeds.
 */
TEST_F(CursorManagerTest, ShouldBeAbleToKillPinnedCursor) {
    CursorManager* cursorManager = useCursorManager();
    OperationContext* const pinningOpCtx = _opCtx.get();

    auto cursorPin = cursorManager->registerCursor(
        pinningOpCtx,
        {makeFakePlanExecutor(),
         kTestNss,
         {},
         APIParameters(),
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});

    auto cursorId = cursorPin.getCursor()->cursorid();
    ASSERT_OK(cursorManager->killCursor(_opCtx.get(), cursorId));

    // The original operation should have been interrupted since the cursor was pinned.
    ASSERT_EQ(pinningOpCtx->checkForInterruptNoAssert(), ErrorCodes::CursorKilled);
}

/**
 * Test that an attempt to kill a pinned cursor succeeds with more than one client.
 */
TEST_F(CursorManagerTest, ShouldBeAbleToKillPinnedCursorMultiClient) {
    CursorManager* cursorManager = useCursorManager();
    OperationContext* const pinningOpCtx = _opCtx.get();

    // Pin the cursor from one client.
    auto cursorPin = cursorManager->registerCursor(
        pinningOpCtx,
        {makeFakePlanExecutor(),
         kTestNss,
         {},
         APIParameters(),
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});

    auto cursorId = cursorPin.getCursor()->cursorid();

    // Set up another client to kill the cursor.
    auto killCursorClientOwned = getGlobalServiceContext()->makeClient("killCursorClient");
    // Keep around a raw pointer for when we transfer ownership of killingClientOwned to the global
    // current client.
    Client* killCursorClient = killCursorClientOwned.get();

    // Need to swap the current client in order to make an operation context.
    auto pinningClient = Client::releaseCurrent();
    Client::setCurrent(std::move(killCursorClientOwned));

    auto killCursorOpCtx = killCursorClient->makeOperationContext();
    invariant(killCursorOpCtx);
    ASSERT_OK(cursorManager->killCursor(killCursorOpCtx.get(), cursorId));

    // The original operation should have been interrupted since the cursor was pinned.
    ASSERT_EQ(pinningOpCtx->checkForInterruptNoAssert(), ErrorCodes::CursorKilled);
}

/**
 * Test that client cursors time out and get deleted.
 */
TEST_F(CursorManagerTest, InactiveCursorShouldTimeout) {
    CursorManager* cursorManager = useCursorManager();
    auto clock = useClock();

    cursorManager->registerCursor(_opCtx.get(),
                                  {makeFakePlanExecutor(),
                                   NamespaceString{"test.collection"},
                                   {},
                                   APIParameters(),
                                   {},
                                   repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
                                   ReadPreferenceSetting(ReadPreference::PrimaryOnly),
                                   BSONObj(),
                                   PrivilegeVector()});

    ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), Date_t()));

    clock->advance(getDefaultCursorTimeoutMillis());
    ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
    ASSERT_EQ(0UL, cursorManager->numCursors());

    cursorManager->registerCursor(_opCtx.get(),
                                  {makeFakePlanExecutor(),
                                   NamespaceString{"test.collection"},
                                   {},
                                   APIParameters(),
                                   {},
                                   repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
                                   ReadPreferenceSetting(ReadPreference::PrimaryOnly),
                                   BSONObj(),
                                   PrivilegeVector()});
    ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), Date_t::max()));
    ASSERT_EQ(0UL, cursorManager->numCursors());
}

/**
 * Test that pinned cursors do not get timed out.
 */
TEST_F(CursorManagerTest, InactivePinnedCursorShouldNotTimeout) {
    CursorManager* cursorManager = useCursorManager();
    auto clock = useClock();

    auto cursorPin = cursorManager->registerCursor(
        _opCtx.get(),
        {makeFakePlanExecutor(),
         NamespaceString{"test.collection"},
         {},
         APIParameters(),
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});

    // The pin is still in scope, so it should not time out.
    clock->advance(getDefaultCursorTimeoutMillis());
    ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
}

/**
 * A cursor can be left in the CursorManager in a killed state when a pinned cursor is interrupted
 * with an unusual error code (a code other than ErrorCodes::Interrupted or
 * ErrorCodes::CursorKilled). Verify that such cursors get deregistered and deleted on an attempt to
 * pin.
 */
TEST_F(CursorManagerTest, MarkedAsKilledCursorsShouldBeDeletedOnCursorPin) {
    CursorManager* cursorManager = useCursorManager();

    auto cursorPin = cursorManager->registerCursor(
        _opCtx.get(),
        {makeFakePlanExecutor(),
         NamespaceString{"test.collection"},
         {},
         APIParameters(),
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});
    auto cursorId = cursorPin->cursorid();

    // A cursor will stay alive, but be marked as killed, if it is interrupted with a code other
    // than ErrorCodes::Interrupted or ErrorCodes::CursorKilled and then unpinned.
    _opCtx->markKilled(ErrorCodes::InternalError);
    cursorPin.release();

    // The cursor should still be present in the manager.
    ASSERT_EQ(1UL, cursorManager->numCursors());

    // Pinning the cursor should fail with the same error code that interrupted the OpCtx. The
    // cursor should no longer be present in the manager.
    ASSERT_EQ(cursorManager->pinCursor(_opCtx.get(), cursorId).getStatus(),
              ErrorCodes::InternalError);
    ASSERT_EQ(0UL, cursorManager->numCursors());
}

/**
 * Test that client cursors which have been marked as killed time out and get deleted.
 */
TEST_F(CursorManagerTest, InactiveKilledCursorsShouldTimeout) {
    CursorManager* cursorManager = useCursorManager();
    auto clock = useClock();

    auto cursorPin = cursorManager->registerCursor(
        _opCtx.get(),
        {makeFakePlanExecutor(),
         NamespaceString{"test.collection"},
         {},
         APIParameters(),
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});

    // A cursor will stay alive, but be marked as killed, if it is interrupted with a code other
    // than ErrorCodes::Interrupted or ErrorCodes::CursorKilled and then unpinned.
    _opCtx->markKilled(ErrorCodes::InternalError);
    cursorPin.release();

    // The cursor should still be present in the manager.
    ASSERT_EQ(1UL, cursorManager->numCursors());

    // Advance the clock to simulate time passing, and verify that the cursor times out.
    clock->advance(getDefaultCursorTimeoutMillis());
    ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
    ASSERT_EQ(0UL, cursorManager->numCursors());
}

/**
 * Test that using a cursor updates its time of last use.
 */
TEST_F(CursorManagerTest, UsingACursorShouldUpdateTimeOfLastUse) {
    CursorManager* cursorManager = useCursorManager();
    auto clock = useClock();

    // Register a cursor which we will look at again.
    auto cursorPin = cursorManager->registerCursor(
        _opCtx.get(),
        {makeFakePlanExecutor(),
         kTestNss,
         {},
         APIParameters(),
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});
    auto usedCursorId = cursorPin.getCursor()->cursorid();
    cursorPin.release();

    // Register a cursor to immediately forget about, to make sure it will time out on a normal
    // schedule.
    cursorManager->registerCursor(_opCtx.get(),
                                  {makeFakePlanExecutor(),
                                   kTestNss,
                                   {},
                                   APIParameters(),
                                   {},
                                   repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
                                   ReadPreferenceSetting(ReadPreference::PrimaryOnly),
                                   BSONObj(),
                                   PrivilegeVector()});

    // Advance the clock to simulate time passing.
    clock->advance(Milliseconds(1));

    // Touch the cursor with id 'usedCursorId' to advance its time of last use.
    cursorManager->pinCursor(_opCtx.get(), usedCursorId).status_with_transitional_ignore();

    // We should be able to time out the unused cursor, but the one we used should stay alive.
    ASSERT_EQ(2UL, cursorManager->numCursors());
    clock->advance(getDefaultCursorTimeoutMillis() - Milliseconds(1));
    ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
    ASSERT_EQ(1UL, cursorManager->numCursors());

    // We should be able to time out the used cursor after one more millisecond.
    clock->advance(Milliseconds(1));
    ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
    ASSERT_EQ(0UL, cursorManager->numCursors());
}

/**
 * Test that a cursor cannot be timed out while in use, and that it's time of last use is updated
 * when it is unpinned.
 */
TEST_F(CursorManagerTest, CursorShouldNotTimeOutUntilIdleForLongEnoughAfterBeingUnpinned) {
    CursorManager* cursorManager = useCursorManager();
    auto clock = useClock();

    // Register a cursor which we will look at again.
    auto cursorPin = cursorManager->registerCursor(
        _opCtx.get(),
        {makeFakePlanExecutor(),
         kTestNss,
         {},
         APIParameters(),
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});

    // Advance the clock to simulate time passing.
    clock->advance(getDefaultCursorTimeoutMillis() + Milliseconds(1));

    // Make sure the pinned cursor does not time out, before or after unpinning it.
    ASSERT_EQ(1UL, cursorManager->numCursors());
    ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
    ASSERT_EQ(1UL, cursorManager->numCursors());

    cursorPin.release();

    ASSERT_EQ(1UL, cursorManager->numCursors());
    ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
    ASSERT_EQ(1UL, cursorManager->numCursors());

    // Advance the clock to simulate more time passing, then assert that the now-inactive cursor
    // times out.
    clock->advance(getDefaultCursorTimeoutMillis() + Milliseconds(1));
    ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now()));
    ASSERT_EQ(0UL, cursorManager->numCursors());
}

/**
 * Test that a cursor correctly stores API parameters.
 */
TEST_F(CursorManagerTest, CursorStoresAPIParameters) {
    APIParameters apiParams = APIParameters();
    apiParams.setAPIVersion("2");
    apiParams.setAPIStrict(true);
    apiParams.setAPIDeprecationErrors(true);

    CursorManager* cursorManager = useCursorManager();
    auto cursorPin = cursorManager->registerCursor(
        _opCtx.get(),
        {makeFakePlanExecutor(),
         kTestNss,
         {},
         apiParams,
         {},
         repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern),
         ReadPreferenceSetting(ReadPreference::PrimaryOnly),
         BSONObj(),
         PrivilegeVector()});

    auto storedAPIParams = cursorPin->getAPIParameters();
    ASSERT_EQ("2", *storedAPIParams.getAPIVersion());
    ASSERT_TRUE(*storedAPIParams.getAPIStrict());
    ASSERT_TRUE(*storedAPIParams.getAPIDeprecationErrors());
}

/**
 * Test that cursors inherit the logical session id from their operation context
 */
TEST_F(CursorManagerTestCustomOpCtx, LogicalSessionIdOnOperationCtxTest) {
    // Cursors created on an op ctx without a session id have no session id.
    {
        auto opCtx = _queryServiceContext->makeOperationContext();
        auto pinned = makeCursor(opCtx.get());

        ASSERT_EQUALS(pinned.getCursor()->getSessionId(), boost::none);
    }

    // Cursors created on an op ctx with a session id have a session id.
    {
        auto lsid = makeLogicalSessionIdForTest();
        auto opCtx2 = _queryServiceContext->makeOperationContext(lsid);
        auto pinned2 = makeCursor(opCtx2.get());

        ASSERT_EQUALS(pinned2.getCursor()->getSessionId(), lsid);
    }
}

/**
 * Test that a manager whose cursors do not have sessions does not return them.
 */
TEST_F(CursorManagerTestCustomOpCtx, CursorsWithoutSessions) {
    // Add a cursor with no session to the cursor manager.
    auto opCtx = _queryServiceContext->makeOperationContext();
    auto pinned = makeCursor(opCtx.get());
    ASSERT_EQUALS(pinned.getCursor()->getSessionId(), boost::none);

    // Retrieve all sessions active in manager - set should be empty.
    LogicalSessionIdSet lsids;
    useCursorManager()->appendActiveSessions(&lsids);
    ASSERT(lsids.empty());
}

/**
 * Test a manager that has one cursor running inside of a session.
 */
TEST_F(CursorManagerTestCustomOpCtx, OneCursorWithASession) {
    // Add a cursor with a session to the cursor manager.
    auto lsid = makeLogicalSessionIdForTest();
    auto opCtx = _queryServiceContext->makeOperationContext(lsid);
    auto pinned = makeCursor(opCtx.get());

    // Retrieve all sessions active in manager - set should contain just lsid.
    LogicalSessionIdSet lsids;
    useCursorManager()->appendActiveSessions(&lsids);
    ASSERT_EQ(lsids.size(), size_t(1));
    ASSERT(lsids.find(lsid) != lsids.end());

    // Retrieve all cursors for this lsid - should be just ours.
    auto cursors = useCursorManager()->getCursorsForSession(lsid);
    ASSERT_EQ(cursors.size(), size_t(1));
    auto cursorId = pinned.getCursor()->cursorid();
    ASSERT(cursors.find(cursorId) != cursors.end());

    // Remove the cursor from the manager.
    pinned.release();
    ASSERT_OK(useCursorManager()->killCursor(opCtx.get(), cursorId));

    // There should be no more cursor entries by session id.
    LogicalSessionIdSet sessions;
    useCursorManager()->appendActiveSessions(&sessions);
    ASSERT(sessions.empty());
    ASSERT(useCursorManager()->getCursorsForSession(lsid).empty());
}

/**
 * Test a manager with multiple cursors running inside of the same session.
 */
TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsWithSameSession) {
    // Add two cursors on the same session to the cursor manager.
    auto lsid = makeLogicalSessionIdForTest();
    auto opCtx = _queryServiceContext->makeOperationContext(lsid);
    auto pinned = makeCursor(opCtx.get());
    auto pinned2 = makeCursor(opCtx.get());

    auto cursorId1 = pinned.getCursor()->cursorid();
    auto cursorId2 = pinned2.getCursor()->cursorid();

    // Retrieve all sessions - set should contain just lsid.
    stdx::unordered_set<LogicalSessionId, LogicalSessionIdHash> lsids;
    useCursorManager()->appendActiveSessions(&lsids);
    ASSERT_EQ(lsids.size(), size_t(1));
    ASSERT(lsids.find(lsid) != lsids.end());

    // Retrieve all cursors for session - should be both cursors.
    auto cursors = useCursorManager()->getCursorsForSession(lsid);
    ASSERT_EQ(cursors.size(), size_t(2));
    ASSERT(cursors.find(cursorId1) != cursors.end());
    ASSERT(cursors.find(cursorId2) != cursors.end());

    // Remove one cursor from the manager.
    pinned.release();
    ASSERT_OK(useCursorManager()->killCursor(opCtx.get(), cursorId1));

    // Should still be able to retrieve the session.
    lsids.clear();
    useCursorManager()->appendActiveSessions(&lsids);
    ASSERT_EQ(lsids.size(), size_t(1));
    ASSERT(lsids.find(lsid) != lsids.end());

    // Should still be able to retrieve remaining cursor by session.
    cursors = useCursorManager()->getCursorsForSession(lsid);
    ASSERT_EQ(cursors.size(), size_t(1));
    ASSERT(cursors.find(cursorId2) != cursors.end());
}

/**
 * Test a manager with multiple cursors running inside of different sessions.
 */
TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsMultipleSessions) {
    auto lsid1 = makeLogicalSessionIdForTest();
    auto lsid2 = makeLogicalSessionIdForTest();

    CursorId cursor1;
    CursorId cursor2;

    // Cursor with session 1.
    {
        auto opCtx1 = _queryServiceContext->makeOperationContext(lsid1);
        cursor1 = makeCursor(opCtx1.get()).getCursor()->cursorid();
    }

    // Cursor with session 2.
    {
        auto opCtx2 = _queryServiceContext->makeOperationContext(lsid2);
        cursor2 = makeCursor(opCtx2.get()).getCursor()->cursorid();
    }

    // Cursor with no session.
    {
        auto opCtx3 = _queryServiceContext->makeOperationContext();
        makeCursor(opCtx3.get()).getCursor();
    }

    // Retrieve all sessions - should be both lsids.
    LogicalSessionIdSet lsids;
    useCursorManager()->appendActiveSessions(&lsids);
    ASSERT_EQ(lsids.size(), size_t(2));
    ASSERT(lsids.find(lsid1) != lsids.end());
    ASSERT(lsids.find(lsid2) != lsids.end());

    // Retrieve cursors for each session - should be just one.
    auto cursors1 = useCursorManager()->getCursorsForSession(lsid1);
    ASSERT_EQ(cursors1.size(), size_t(1));
    ASSERT(cursors1.find(cursor1) != cursors1.end());

    auto cursors2 = useCursorManager()->getCursorsForSession(lsid2);
    ASSERT_EQ(cursors2.size(), size_t(1));
    ASSERT(cursors2.find(cursor2) != cursors2.end());
}

/**
 * Test that a CursorManager is registered with the global ServiceContext.
 */
TEST(CursorManagerTest, RegisteredWithGlobalServiceContext) {
    CursorManager* cursorManager = CursorManager::get(getGlobalServiceContext());
    ASSERT(cursorManager);
}

/**
 * Test that a CursorManager is registered with a custom ServiceContext.
 */
TEST_F(CursorManagerTest, RegisteredWithCustomServiceContext) {
    CursorManager* cursorManager = CursorManager::get(_queryServiceContext->getServiceContext());
    ASSERT(cursorManager);
}

/**
 * Test that a CursorManager is accessible via an OperationContext.
 */
TEST_F(CursorManagerTest, CanAccessFromOperationContext) {
    CursorManager* cursorManager = CursorManager::get(_opCtx.get());
    ASSERT(cursorManager);
}

TEST_F(CursorManagerTestCustomOpCtx, CursorsWithoutOperationKeys) {
    auto opCtx = _queryServiceContext->makeOperationContext();
    auto pinned = makeCursor(opCtx.get());
    ASSERT_EQUALS(pinned.getCursor()->getOperationKey(), boost::none);
}

TEST_F(CursorManagerTestCustomOpCtx, OneCursorWithAnOperationKey) {
    auto opKey = UUID::gen();
    auto opCtx = _queryServiceContext->makeOperationContext();
    opCtx->setOperationKey(opKey);
    auto pinned = makeCursor(opCtx.get());

    auto cursors = useCursorManager()->getCursorsForOpKeys({opKey});
    ASSERT_EQ(cursors.size(), size_t(1));
    auto cursorId = pinned.getCursor()->cursorid();
    ASSERT(cursors.find(cursorId) != cursors.end());

    // Remove the cursor from the manager and verify that we can't retrieve it.
    pinned.release();
    ASSERT_OK(useCursorManager()->killCursor(opCtx.get(), cursorId));
    ASSERT(useCursorManager()->getCursorsForOpKeys({opKey}).empty());
}

TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsMultipleOperationKeys) {
    auto opKey1 = UUID::gen();
    auto opKey2 = UUID::gen();

    CursorId cursor1;
    CursorId cursor2;

    // Cursor with operationKey 1.
    {
        auto opCtx1 = _queryServiceContext->makeOperationContext();
        opCtx1->setOperationKey(opKey1);
        cursor1 = makeCursor(opCtx1.get()).getCursor()->cursorid();
    }

    // Cursor with operationKey 2.
    {
        auto opCtx2 = _queryServiceContext->makeOperationContext();
        opCtx2->setOperationKey(opKey2);
        cursor2 = makeCursor(opCtx2.get()).getCursor()->cursorid();
    }

    // Cursor with no operation key.
    {
        auto opCtx3 = _queryServiceContext->makeOperationContext();
        makeCursor(opCtx3.get()).getCursor();
    }

    // Retrieve cursors for each operation key - should be one for each.
    auto cursors1 = useCursorManager()->getCursorsForOpKeys({opKey1});
    ASSERT_EQ(cursors1.size(), size_t(1));
    ASSERT(cursors1.find(cursor1) != cursors1.end());

    auto cursors2 = useCursorManager()->getCursorsForOpKeys({opKey2});
    ASSERT_EQ(cursors2.size(), size_t(1));
    ASSERT(cursors2.find(cursor2) != cursors2.end());

    // Retrieve cursors for both operation keys.
    auto cursors = useCursorManager()->getCursorsForOpKeys({opKey1, opKey2});
    ASSERT_EQ(cursors.size(), size_t(2));
    ASSERT(cursors.find(cursor1) != cursors.end());
    ASSERT(cursors.find(cursor2) != cursors.end());
}

TEST_F(CursorManagerTestCustomOpCtx, TimedOutCursorShouldNotBeReturnedForOpKeyLookup) {
    auto opKey = UUID::gen();
    auto opCtx = _queryServiceContext->makeOperationContext();
    opCtx->setOperationKey(opKey);
    auto clock = useClock();

    auto cursor = makeCursor(opCtx.get());

    ASSERT_EQ(1UL, useCursorManager()->numCursors());
    ASSERT_EQ(0UL, useCursorManager()->timeoutCursors(opCtx.get(), Date_t()));

    // Advance the clock and verify that the cursor times out.
    cursor.release();
    clock->advance(getDefaultCursorTimeoutMillis() + Milliseconds(1));
    ASSERT_EQ(1UL, useCursorManager()->timeoutCursors(opCtx.get(), clock->now()));
    ASSERT_EQ(0UL, useCursorManager()->numCursors());

    // Verify that the timed out cursor is not returned when looking up by OperationKey.
    auto cursors = useCursorManager()->getCursorsForOpKeys({opKey});
    ASSERT_EQ(cursors.size(), size_t(0));
}

TEST_F(CursorManagerTestCustomOpCtx, CursorsMarkedAsKilledAreReturnedForOpKeyLookup) {
    auto opKey = UUID::gen();
    auto opCtx = _queryServiceContext->makeOperationContext();
    opCtx->setOperationKey(opKey);

    auto cursor = makeCursor(opCtx.get());

    // Mark the OperationContext as killed.
    {
        stdx::lock_guard<Client> lkClient(*opCtx->getClient());
        // A cursor will stay alive, but be marked as killed, if it is interrupted with a code other
        // than ErrorCodes::Interrupted or ErrorCodes::CursorKilled and then unpinned.
        opCtx->getServiceContext()->killOperation(lkClient, opCtx.get(), ErrorCodes::InternalError);
    }
    cursor.release();

    // The cursor should still be present in the manager.
    ASSERT_EQ(1UL, useCursorManager()->numCursors());

    // Verify that the killed cursor is still returned when looking up by OperationKey.
    auto cursors = useCursorManager()->getCursorsForOpKeys({opKey});
    ASSERT_EQ(cursors.size(), size_t(1));
}
}  // namespace
}  // namespace mongo

max_stars_repo_name: benety/mongo

max_issues_repo_name: benety/mongo

max_forks_repo_name: benety/mongo


text: #include <boost/hana/less_equal.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
#include <Common/HighQueuePch.hpp>

#ifdef _WIN32 /// right now this test only works on windows.
#define BOOST_TEST_NO_MAIN HighQueueOtherTests
#include <boost/test/unit_test.hpp>

/// This is not a test of HighQueue.  It is a test of the operating system's dispatching algorithm.
/// The Windows dispatcher seems to be brain-dead.
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>

#include <Common/Stopwatch.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
#include <thread>
#include <atomic>
#include <vector>

#if 01
#   define YIELD() std::this_thread::yield()
#   define TECHNIQUE "std::this_thread::yield()"
#elif 0
#   define YIELD() ::SwitchToThread()
#   define TECHNIQUE "::SwitchToThread()"
#elif 0
#   define YIELD() ::Sleep(0)
#   define TECHNIQUE "::Sleep(0)"
#elif 0
#   define YIELD() std::this_thread::sleep_for(std::chrono::milliseconds(1))
#   define TECHNIQUE "std::this_thread::sleep_for(std::chrono::milliseconds(1))"
#elif 0
#   define YIELD() ::Sleep(1)
#   define TECHNIQUE "::Sleep(1)"
#elif 01
#   define YIELD() ::YieldProcessor()
#   define TECHNIQUE "::YieldProcessor()"
#else
#define YIELD() do{\
    size_t initial=position; \
    while(initial == position)\
    {\
        /*spin*/;\
    }\
    } while(false)
#define TECHNIQUE "SpinUntilPositionChanges"
#endif

namespace {
    const size_t entryCount = 1000000;
    uint8_t threadIds[entryCount];
    std::atomic<bool> go(false);
    std::atomic<size_t> threadsRunning(0);
    std::atomic<size_t> position(0);
    const uint64_t microsecondsPerSecond = 1000000;

    void threadFunction(uint8_t threadId)
    {
        ++threadsRunning;
        while (!go)
        {
            std::this_thread::yield();
        }
        while (true)
        {
            auto pos = position++;
            if (pos >= entryCount)
            {
                return;
            }
            threadIds[pos] = threadId;
            YIELD();
        }
    }
}

BOOST_AUTO_TEST_CASE(testOSThreadDispatcher)
{
    ///////////////
    // Capture data
    auto coreCount = std::thread::hardware_concurrency();
    uint32_t overbook = 2;
    uint8_t threadCount = uint8_t(coreCount + overbook);

    LARGE_INTEGER frequency;
    ::QueryPerformanceFrequency(&frequency);
    std::vector<std::thread> threads;
    for (uint8_t nth = 0; nth < threadCount; ++nth)
    {
        threads.emplace_back(std::bind(threadFunction, nth));
    }
    while (threadsRunning < threadCount)
    {
        std::this_thread::yield();
    }

    LARGE_INTEGER start;
    ::QueryPerformanceCounter(&start);
    go = true;
    for (auto & thread : threads)
    {
        thread.join();
    }
    LARGE_INTEGER stop;
    ::QueryPerformanceCounter(&stop);

    ///////////////
    // Analyze data
    std::vector<size_t> previous(threadCount, size_t(0));
    std::vector<size_t> minGaps(threadCount, std::numeric_limits<size_t>::max());
    std::vector<size_t> maxGaps(threadCount, 0);
    size_t overallMaxGap = 0;
    size_t startMaxGap = 0;
    size_t endMaxGap = 0;
    uint8_t threadIdMaxGap = 0;

    struct Bucket
    {
        size_t limit_;
        size_t count_;
        Bucket(size_t limit)
            : limit_(limit)
            , count_(0)
        {}
    };

    std::vector<Bucket> buckets;
    for (size_t limit = 1; limit < 10; ++limit)
    {
        buckets.emplace_back(limit);
    }
    for (size_t limit = 10; limit < 100; limit += 10)
    {
        buckets.emplace_back(limit);
    }
    for (size_t limit = 100; limit < 1000; limit += 100)
    {
        buckets.emplace_back(limit);
    }
    for (size_t limit = 1000; limit < 10000; limit += 1000)
    {
        buckets.emplace_back(limit);
    }
    buckets.emplace_back(std::numeric_limits<size_t>::max());

    for (size_t pos = 0; pos < entryCount; ++pos)
    {
        auto threadId = threadIds[pos];
        auto gap = pos - previous[threadId];
        if (gap < minGaps[threadId])
        {
            minGaps[threadId] = gap;
        }
        if (gap > maxGaps[threadId])
        {
            maxGaps[threadId] = gap;
        }
        if (gap > overallMaxGap)
        {
            overallMaxGap = gap;
            startMaxGap = previous[threadId];
            endMaxGap = pos;
            threadIdMaxGap = threadId;
        }

        for (auto & bucket : buckets)
        {
            if (gap < bucket.limit_)
            {
                ++bucket.count_;
                break;
            }
        }

        previous[threadId] = pos;

    }

    ///////////////////
    // Display results
    std::cout << "--------------------" << std::endl;
    std::cout << TECHNIQUE << std::endl;
    std::cout << uint16_t(threadCount) << " threads on " << coreCount << " cores." << std::endl;
    std::cout << "--------------------" << std::endl;
    for (size_t pos = 0; pos < threadCount; ++pos)
    {
        std::cout << pos << ": min = " << minGaps[pos] << " max = " << maxGaps[pos] << std::endl;
    }

    std::cout << "Overall max gap: [" << startMaxGap << ", " << endMaxGap << "] = " << overallMaxGap << " for thread " << uint16_t(threadIdMaxGap) << std::endl;
    auto microseconds = (stop.QuadPart - start.QuadPart) * microsecondsPerSecond / frequency.QuadPart;
    std::cout << microseconds << " uSec." << std::endl;

    size_t previousLimit = 0;
    for (auto & bucket : buckets)
    {
        if (bucket.count_ != 0)
        {
            std::cout << '[' << std::setw(4) << previousLimit << '-' << std::setw(4) << bucket.limit_ << ") : " << bucket.count_ << std::endl;
        }
        previousLimit = bucket.limit_;
    }

    std::cout << "===================================================================" << std::endl;

    BOOST_WARN_LT(overallMaxGap, 3*threadCount);
    BOOST_CHECK_LT(overallMaxGap, 3*threadCount);
}

#endif // _WIN32

ext: cpp

max_stars_repo_name: dale-wilson/HSQueue

max_issues_repo_name: dale-wilson/HighQueue

max_forks_repo_name: dale-wilson/HighQueue


text:
#ifndef IMGEN_HSL_HPP_
#define IMGEN_HSL_HPP_

/**
 * The HSL implementation from the Boost GIL Contributions
 * (http://gil-contributions.googlecode.com/svn/sandbox/boost/gil/extension/toolbox/hsl.hpp)
 * doesn't correctly convert from HSL to RGB so this is my own implementation.
 *
 * You can test this by the below code:
 *
 * hsl32f_pixel_t hsl(0.33, 0.56, 0.64);
 * rgb8_pixel_t rgb;
 *
 * color_convert(hsl, rgb);
 *
 * rgb will erroneously be (163, 163, 163) when it should infact be (114, 215,
 * 112)
 *
 * The algorithms implemented here have been taken from:
 * http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
 */

#include <boost/gil/gil_all.hpp>
#include <boost/mpl/vector_c.hpp>

#include <cstdlib>

namespace boost { namespace gil {

struct hue_t {};
struct saturation_t {};
struct lightness_t {};

typedef mpl::vector3<hue_t, saturation_t, lightness_t> hsl_t;
typedef layout<hsl_t> hsl_layout_t;

GIL_DEFINE_ALL_TYPEDEFS(32f, hsl)

namespace detail {

bits32f hue_to_rgb(bits32f p, bits32f q, bits32f t);

} // namespace detail

template <>
struct default_color_converter_impl<rgb_t, hsl_t> {
    template <typename P1>
    void operator()(const P1& src, hsl32f_pixel_t& dst) const {
        bits32f red = channel_convert<bits32f>(get_color(src, red_t()));
        bits32f green = channel_convert<bits32f>(get_color(src, green_t()));
        bits32f blue = channel_convert<bits32f>(get_color(src, blue_t()));
        bits32f hue, saturation, lightness;

        auto max = std::max(red, std::max(green, blue));
        auto min = std::min(red, std::min(green, blue));
        lightness = (max + min) / 2;

        if(max == min) {
            hue = 0.0f;
            saturation = 0.0f;
        } else {
            auto diff = max - min;

            if(lightness > 0.5f) {
                saturation = diff / (2 - max - min);
            } else {
                saturation = diff / (max + min);
            }

            if(red == max) {
                hue = (green - blue) / diff + (green < blue ? 6.0f : 0.0f);
            } else if(green == max) {
                hue = (blue - red) / diff + 2.0f;
            } else { // blue == max
                hue = (red - green) / diff + 4.0f;
            }

            hue /= 6.0f;
        }

        get_color(dst, hue_t()) = hue;
        get_color(dst, saturation_t()) = saturation;
        get_color(dst, lightness_t()) = lightness;
    }
};

template <>
struct default_color_converter_impl<hsl_t, rgb_t> {
    template <typename P2>
    void operator()(const hsl32f_pixel_t& src, P2& dst) const {
        bits32f hue = get_color(src, hue_t());
        bits32f saturation = get_color(src, saturation_t());
        bits32f lightness = get_color(src, lightness_t());
        bits32f red, green, blue;

        auto q = lightness < 0.5 ? lightness * (1 + saturation)
                                 : lightness + saturation - lightness * saturation;
        auto p = 2 * lightness - q;

        if(saturation == 0) {
            red = lightness;
            green = lightness;
            blue = lightness;
        } else {
            red   = detail::hue_to_rgb(p, q, hue + 1.0f/3.0f);
            green = detail::hue_to_rgb(p, q, hue);
            blue  = detail::hue_to_rgb(p, q, hue - 1.0/3.0);
        }

        get_color(dst, red_t()) =
            channel_convert<typename color_element_type<P2, red_t>::type>(red);

        get_color(dst, green_t()) =
            channel_convert<typename color_element_type<P2, green_t>::type>(green);

        get_color(dst, blue_t()) =
            channel_convert<typename color_element_type<P2, blue_t>::type>(blue);
    }
};

} } //namespace boost::gil

#endif // IMGEN_HSL_HPP_

max_stars_repo_name: maddisoj/wallgen

max_issues_repo_name: maddisoj/wallgen

max_forks_repo_name: maddisoj/wallgen


text:
/*
 * Copyright (c) 2013-2018 Masahide Kashiwagi (kashi@waseda.jp)
 */

#ifndef ODE_HPP
#define ODE_HPP

// ODE

#include <iostream>
#include <cmath>
#include <algorithm>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <kv/interval.hpp>
#include <kv/rdouble.hpp>
#include <kv/interval-vector.hpp>
#include <kv/make-candidate.hpp>
#include <kv/psa.hpp>
#include <kv/ode-param.hpp>

#ifndef ODE_FAST
#define ODE_FAST 1
#endif

#ifndef ODE_STEP_COMPONENT
#define ODE_STEP_COMPONENT 0
#endif

#ifndef ODE_RESTART_RATIO
#define ODE_RESTART_RATIO 1
#endif

#ifndef ODE_COEF_MID
#define ODE_CORF_MID 0
#endif

namespace kv {

namespace ub = boost::numeric::ublas;

template <class T, class F>
int
ode(F f, ub::vector< interval<T> >& init, const interval<T>& start, interval<T>& end, const ode_param<T> p = ode_param<T>(), ub::vector< psa< interval<T> > >* result_psa = NULL) {
	int n = init.size();
	int i, j;

	ub::vector< psa< interval<T> > > x, y;
	psa< interval<T> > torg;
	psa< interval<T> > t;

	ub::vector< psa< interval<T> > > z, w;

	psa< interval<T> > temp;
	T m;
	ub::vector<T> newton_step;

	bool flag, resized;

	interval<T> deltat;
	ub::vector< interval<T> > result;

	T radius, radius_tmp;

	#if ODE_STEP_COMPONENT == 1
	ub::vector<T> tolerance(n);
	#else
	T tolerance;
	#endif

	int n_rad;

	#if ODE_RESTART_RATIO == 1
	T max_ratio;
	#endif

	int ret_val;
	interval<T> end2;
	int restart;

	bool save_mode, save_uh, save_rh;

	#if ODE_STEP_COMPONENT == 1
	for (i=0; i<n; i++) {
		tolerance(i) = std::max(T(1.), norm(init(i))) * p.epsilon;
	}
	#else
	m = 1.;
	for (i=0; i<n; i++) {
		m = std::max(m, norm(init(i)));
	}
	tolerance = m * p.epsilon;
	#endif

	x = init;
	torg.v.resize(2);
	torg.v(0) = start; torg.v(1) = 1.;

	save_mode = psa< interval<T> >::mode();
	save_uh = psa< interval<T> >::use_history();
	save_rh = psa< interval<T> >::record_history();
	psa< interval<T> >::mode() = 1;
	psa< interval<T> >::use_history() = false;
	psa< interval<T> >::record_history() = false;
	#if ODE_FAST == 1
	psa< interval<T> >::record_history() = true;
	psa< interval<T> >::history().clear();
	#endif
	for (j=0; j<p.order; j++) {
		#if ODE_FAST == 1
		if (j == 1) psa< interval<T> >::use_history() = true;
		#endif
		t = setorder(torg, j);
		y = f(x, t);
		for (i=0; i<n; i++) {
			y(i) = integrate(y(i));
			// set order preparing for constant function
			y(i) = setorder(y(i), j+1);
		}
		x = init + y;
	}

	if (p.autostep) {
		// use two non-zero coefficients of higher order term
		#if ODE_STEP_COMPONENT == 1
		radius = std::numeric_limits<T>::infinity();
		for (i=0; i<n; i++) {
			radius_tmp = 0.;
			n_rad = 0;
			for (j = p.order; j>=1; j--) {
				#if ODE_COEF_MID == 1
				using std::abs;
				m = abs(mid(x(i).v(j)));
				#else
				m = norm(x(i).v(j));
				#endif
				if (m == 0.) continue;
				radius_tmp = std::max(radius_tmp, (T)std::pow((double)m, 1./j));
				n_rad++;
				if (n_rad == 2) break;
			}
			radius = std::min(radius, std::pow((double)(tolerance(i)), 1./p.order) / radius_tmp);
		}
		#else // ODE_STEP_COMPONENT
		radius = 0.;
		n_rad = 0;
		for (j = p.order; j>=1; j--) {
			m = 0.;
			for (i=0; i<n; i++) {
				#if ODE_COEF_MID == 1
				using std::abs;
				m = std::max(m, abs(mid(x(i).v(j))));
				#else
				m = std::max(m, norm(x(i).v(j)));
				#endif
			}
			if (m == 0.) continue;
			radius = std::max(radius, (T)std::pow((double)m, 1./j));
			n_rad++;
			if (n_rad == 2) break;
		}
		radius = std::pow((double)tolerance, 1./p.order) / radius;
		#endif // ODE_STEP_COMPONENT
	}

	psa< interval<T> >::mode() = 2;

	restart = 0;
	resized = false;

	while (true) {
		if (p.autostep) {
			end2 = mid(start + radius);
			if (end2 >= end.lower()) {
				end2 = end;
				radius = mid(end2 - start);
				ret_val = 2;
			} else {
				ret_val = 1;
			}
		} else {
			end2 = end;
			ret_val = 2;
		}
		deltat = end2 - start;

		psa< interval<T> >::domain() = interval<T>(0., deltat.upper());

		z = x;
		t = setorder(torg, p.order);

		try {
			w = f(z, t);
		}
		catch (std::domain_error& e) {
			if (p.autostep && restart < p.restart_max) {
				psa< interval<T> >::use_history() = false;
				if (p.verbose == 1) {
					std::cout << "ode: radius changed: " << radius;
				}
				radius *= 0.5;
				if (p.verbose == 1) {
					std::cout << " -> " << radius << "\n";
				}
				restart++;
				continue;
			} else {
				throw std::domain_error("ode: evaluation error");
			}
		}

		for (i=0; i<n; i++) {
			temp = integrate(w(i));
			w(i) = setorder(temp, p.order);
		}
		w = init + w;

		newton_step.resize(n);
		for (i=0; i<n; i++) {
			newton_step(i) = norm(w(i).v(p.order) - z(i).v(p.order));
		}
		make_candidate(newton_step);
		for (i=0; i<n; i++) {
			z(i).v(p.order) += newton_step(i) * interval<T>(-1., 1.);
		}

		if (p.autostep && ret_val != 2 && resized == false) {
			resized = true;
			m = (std::numeric_limits<T>::min)();
			#if ODE_STEP_COMPONENT == 1
			for (i=0; i<n; i++) {
				m = std::max(m, (rad(eval(z(i), deltat)) - rad(init(i))) / tolerance(i));
			}
			#else
			for (i=0; i<n; i++) {
				m = std::max(m, rad(eval(z(i), deltat)) - rad(init(i)));
			}
			m = m / tolerance;
			#endif
			radius_tmp = radius / std::pow((double)m, 1. / p.order);
			if (radius_tmp >= radius && restart > 0) {
				// do nothing, not continue
			} else {
				radius = radius_tmp;
				continue;
			}
		}

		w = f(z, t);
		for (i=0; i<n; i++) {
			temp = integrate(w(i));
			w(i) = setorder(temp, p.order);
		}
		w = init + w;

		flag = true;
		#if ODE_RESTART_RATIO == 1
		max_ratio = 0.;
		#endif
		for (i=0; i<n; i++) {
			#if ODE_RESTART_RATIO == 1
			max_ratio = std::max(max_ratio, width(w(i).v(p.order)) / width(z(i).v(p.order)));
			#endif
			flag = flag && subset(w(i).v(p.order), z(i).v(p.order));
		}
		if (flag) break;

		if (!p.autostep || restart >= p.restart_max) {
			ret_val = 0;
			break;
		}
		if (p.verbose == 1) {
			std::cout << "ode: radius changed: " << radius;
		}
		#if ODE_RESTART_RATIO == 1
		radius *= std::max(std::min((T)0.5, (T)0.5 / max_ratio), (T)0.125);
		#else
		radius *= 0.5;
		#endif
		if (p.verbose == 1) {
			std::cout << " -> " << radius << "\n";
		}
		restart++;
	}

	if (ret_val != 0) {
		for (j=0; j<p.iteration; j++) {
			z = w;
			w = f(z, t);
			for (i=0; i<n; i++) {
				temp = integrate(w(i));
				w(i) = setorder(temp, p.order);
			}
			w = init + w;
			for (i=0; i<n; i++) {
				w(i).v(p.order) = intersect(w(i).v(p.order), z(i).v(p.order));
			}
		}

		result.resize(n);
		for (i=0; i<n; i++) {
			result(i) = eval(w(i), deltat);
		}

		init = result;
		if (ret_val == 1) end = end2;
		if (result_psa != NULL) *result_psa = w;
	}

	psa< interval<T> >::mode() = save_mode;
	psa< interval<T> >::use_history() = save_uh;
	psa< interval<T> >::record_history() = save_rh;

	return ret_val;
}

template <class T, class F>
int
odelong(F f, ub::vector< interval<T> >& init, const interval<T>& start, interval<T>& end, ode_param<T> p = ode_param<T>()) {

	ub::vector< interval<T> > x;
	interval<T> t, t1;
	int r;
	int ret_val = 0;

	x = init;
	t = start;
	p.set_autostep(true);
	while (1) {
		t1 = end;

		r = ode(f, x, t, t1, p);
		if (r == 0) {
			if (ret_val == 1) {
				init = x;
				end = t;
			}
			return ret_val;
		}
		ret_val = 1;
		if (p.verbose == 1) {
			std::cout << "t: " << t1 << "\n";
			std::cout << x << "\n";
		}
		if (r == 2) {
			init = x;
			return 2;
		}
		t = t1;
	}
}

} // namespace kv

#endif // ODE_HPP

max_stars_repo_name: soonho-tri/kv

max_issues_repo_name: takafumihoriuchi/HyLaGI

max_forks_repo_name: takafumihoriuchi/HyLaGI


text:
/*
Copyright 2013-2015 Rogier van Dalen.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef RANGE_TAKE_HPP_INCLUDED
#define RANGE_TAKE_HPP_INCLUDED

#include <type_traits>
#include <utility>

#include <boost/utility/enable_if.hpp>

#include <boost/mpl/and.hpp>

#include "utility/overload_order.hpp"

#include "rime/core.hpp"
#include "rime/merge_types.hpp"
#include "rime/assert.hpp"
#include "rime/sign.hpp"
#include "rime/cast.hpp"
#include "rime/min.hpp"
#include "rime/call_if.hpp"

#include "core.hpp"
#include "helper/underlying.hpp"

namespace range {

/*
There are two aspects of the below code.
First, the logic that allows "take" to use drop (reverse (direction), n, range).
Second, if that is impossible, take_range, which wraps a view and restricts it
to a number of elements on the fly.
*/

/**
View of a range that cuts it off after a number of elements.
*/
template <class Underlying, class Limit, class Direction> class take_range;

template <class Underlying, class Limit, class Direction> inline
auto make_take_range (Underlying && underlying,
    Limit const & limit, Direction const & direction)
RETURNS (take_range <typename std::decay <Underlying>::type, Limit, Direction> (
    std::forward <Underlying> (underlying), limit, direction));

/* take_range, which limits the underlying range. */

template <class Underlying, class Limit, class Direction> class take_range {
public:
    static_assert (range::is_view <Underlying, Direction>::value,
        "Underlying range must be a view in Direction");

    typedef Underlying underlying_type;
    typedef Limit limit_type;
    typedef Direction direction_type;

private:
    Underlying underlying_;
    Limit limit_;
    Direction direction_;

    template <class Wrapper> friend class helper::callable::get_underlying;

public:
    template <class CVUnderlying>
    take_range (CVUnderlying && underlying, Limit const & limit,
        Direction const & direction)
    : underlying_ (std::forward <CVUnderlying> (underlying)),
        limit_ (limit), direction_ (direction) {}

    /// \brief Return unqualified limit.
    Limit limit() const { return limit_; }

    Direction const & direction() const { return direction_; }

private:
    friend class helper::member_access;

    auto default_direction() const
    RETURNS (range::default_direction (underlying_));

    auto empty (Direction const & direction) const
    RETURNS (rime::or_ (
        limit_ == rime::make_zero (limit_),
        range::empty (underlying_, direction)));

    template <class Underlying2 = Underlying, class Limit2 = Limit,
        class Result = decltype (range::chop_in_place (
            std::declval <Underlying2 &>(), std::declval <Direction>())),
        class Enable = typename std::enable_if <
            !rime::is_constant <Limit2>::value>::type>
    Result chop_in_place (Direction const & direction) {
        auto && result = range::chop_in_place (underlying_, direction);
        -- limit_;
        return static_cast <decltype (result)> (result);
    }
};

namespace take_operation {
    struct take_range_tag {};
} // namespace take_operation

template <class Underlying, class Limit, class Direction>
    struct tag_of_qualified <take_range <Direction, Limit, Underlying>>
{ typedef take_operation::take_range_tag type; };

namespace take_detail {

    /**
    Merge policy for types to do something sensible when a limits and sizes
    are of different types, e.g. int and unsigned, or constants.
    */
    struct limit_merge_policy
    : rime::merge_policy::constant <rime::merge_policy::common_type> {};

    typedef rime::callable::min <limit_merge_policy> min;

} // namespace take_detail

namespace helper {

    void implement_take (unusable);

} // namespace helper

namespace callable {

    namespace implementation {

        using helper::implement_take;

        /* The default implementation for "take". */

        struct implement_take_default {
        private:
            struct when_not_take_range {
                /* Implementation that just calls drop (back, ..., range). */
                // If size (range) <= limit: return range.
                // Range2 is Range but with an rvalue reference removed.
                template <class Range2, class Limit, class Direction,
                    class Size = typename result_of <
                        callable::size (Range2 const &, Direction)>::type,
                    class Enable2 = typename std::enable_if <
                        (Size::value <= Limit::value)>::type>
                Range2 operator() (Range2 && range,
                    Limit const & limit, Direction const & direction,
                    overload_order <1> *) const
                { return std::forward <Range2> (range); }

                // Otherwise: return drop (back, size - limit, range).
                // (Unless size <= limit; then return drop (back, 0, range).)
                // Range2 is necessary to delay the evaluation of result_of.
                template <class Range2, class Limit, class Direction,
                    class Size = typename result_of <
                        callable::size (Range2 const &, Direction)>::type,
                    class Difference = decltype (std::declval <Size>() -
                        take_detail::min() (
                            std::declval <Limit>(), std::declval <Size>())),
                    class Result = decltype (range::drop (
                        std::declval <Range2>(), std::declval <Difference>(),
                        direction::opposite (std::declval <Direction>())))>
                Result operator() (Range2 && range,
                    Limit const & limit, Direction const & direction,
                    overload_order <2> *) const
                {
                    auto size = range::size (range, direction);
                    return range::drop (std::forward <Range2> (range),
                        size - take_detail::min() (limit, size),
                        direction::opposite (direction));
                }

                /* Standard implementation. */
                template <class Range, class Limit, class Direction>
                auto operator() (Range && range, Limit const & limit,
                    Direction const & direction, overload_order <3> *)
                    const
                RETURNS (make_take_range (
                    std::forward <Range> (range), limit, direction));
            };

            /// Check that the limit is non-negative.
            // GCC 4.6 does not accept this if it is a static member function.
            struct check_limit_non_negative {
                template <class Limit>
                    Limit operator() (Limit const & limit) const
                {
                    rime::assert_ (
                        !rime::less_sign_safe (limit, rime::size_t <0>()));
                    return limit;
                }
            };

            struct when_take_range {
                template <class Underlying, class OriginalLimit, class Limit,
                    class Direction>
                auto operator() (
                    take_range <Underlying, OriginalLimit, Direction> const &
                        range,
                    Limit const & limit, Direction const & direction,
                    overload_order <1> *) const
                RETURNS (make_take_range (
                    range::helper::get_underlying <decltype (range)> (range),
                    rime::min_<take_detail::limit_merge_policy> (
                        limit, range.limit()),
                    direction));
            };

            struct is_take_range_with_same_direction {
                template <class Range2, class Direction>
                    rime::false_type operator() (
                        Range2 const &, Direction const &) const
                { return rime::false_; }

                template <class Underlying, class OriginalLimit,
                    class Direction>
                auto operator() (
                    take_range <Underlying, OriginalLimit, Direction>
                        const & range,
                    Direction const & direction) const
                RETURNS (direction == range.direction());
            };

        public:
            // Limit is unqualified; Range is a view.
            template <class Range, class Limit, class Direction>
            auto operator() (Range && range,
                Limit const & limit, Direction const & direction) const
            RETURNS (rime::call_if (
                is_take_range_with_same_direction() (range, direction),
                when_take_range(), when_not_take_range(),
                std::forward <Range> (range),
                check_limit_non_negative() (limit), direction,
                pick_overload()));
        };

        /* The main implementation of "take". */

        struct take {
        private:
            struct dispatch {
                // Call implement_take().
                template <class Range, class Limit, class Direction>
                    auto operator() (
                        Range && range, Limit const & limit,
                        Direction const & direction,
                        overload_order <1> *) const
                RETURNS (implement_take (typename tag_of <Range>::type(),
                    std::forward <Range> (range), limit, direction));

                // Default implementation
                template <class Range, class Limit, class Direction>
                    auto operator() (
                        Range && range, Limit const & limit,
                        Direction const & direction,
                        overload_order <2> *) const
                RETURNS (implement_take_default() (
                    std::forward <Range> (range), limit, direction));
            };

        public:
            // With direction and limit.
            template <class Range, class Limit, class Direction,
                class Enable = typename
                    std::enable_if <is_direction <Direction>::value>::type>
            auto operator() (
                Range && range, Limit const & limit,
                Direction const & direction) const
            RETURNS (dispatch() (
                range::view (std::forward <Range> (range), direction),
                limit, direction, pick_overload()));

            // With limit but without direction: use default direction.
            template <class Range, class Limit, class Enable =
                typename std::enable_if <
                    is_range <Range>::value && !is_direction <Limit>::value
                >::type>
            auto operator() (Range && range, Limit const & limit) const
            RETURNS (dispatch() (
                range::view (std::forward <Range> (range),
                    range::default_direction (range)),
                limit, range::default_direction (range), pick_overload()));
        };

    } // namespace implementation

    using implementation::take;

} // namespace callable

/**
Return a number of elements from a range, as a range.

The default direction is the same as the underlying range.
If take() is called with a different direction than the default direction, the
default direction will therefore often not actually work.

Calling take with the same direction multiple times will not lead
to a recursive type.
If size (direction, range) and drop (reverse (direction), n, range) are
available, then the latter is used directly.
take() will therefore often return exactly the same type as \c range.

\param range The range to take the elements from.
\param number The number of elements to take.
    If the underlying range has fewer elements, only those elements are taken.
\param direction (Optional) The direction to take the elements from.
*/
static auto const take = callable::take();

/* Operations on take_range. */
// These require take to be defined, so they are implemented down here.

namespace take_operation {

    template <class TakeRange, class Direction>
        inline auto implement_size (take_range_tag const &,
            TakeRange && r, Direction const & direction)
    RETURNS (rime::min_ <take_detail::limit_merge_policy> (
        r.limit(), range::size (range::helper::get_underlying <TakeRange> (r),
            direction)));

    template <class TakeRange, class Direction>
        inline auto implement_first (take_range_tag const &,
            TakeRange && r, Direction const & direction)
    -> decltype (range::first (range::helper::get_underlying <TakeRange> (r),
        direction))
    {
        rime::assert_ (direction == r.direction());
        rime::assert_ (!range::empty (r, direction));
        return range::first (range::helper::get_underlying <TakeRange> (r),
            direction);
    }

    template <class TakeRange, class Increment, class Direction>
        inline auto implement_drop (take_range_tag const &,
            TakeRange && r, Increment const & increment,
            Direction const & direction)
    -> decltype (range::take (
        range::drop (range::helper::get_underlying <TakeRange> (
                std::declval <TakeRange &>()),
            increment, direction),
        rime::cast_value <typename std::decay <TakeRange>::type::limit_type> (
            r.limit() - increment),
        direction))
    {
        rime::assert_ (direction == r.direction());
        rime::assert_ (!rime::less_sign_safe (
            increment, rime::size_t <0>()));
        rime::assert_ (!rime::less_sign_safe (r.limit(), increment));
        typedef typename std::decay <TakeRange>::type::limit_type
            limit_type;
        return range::take (
            range::drop (range::helper::get_underlying <TakeRange> (r),
                increment, direction),
            rime::cast_value <limit_type> (r.limit() - increment), direction);
    }

    // chop: enable only if this is implemented on the underlying range.
    // Otherwise, the generic implementation that uses "first" and "drop" should
    // work on the take_range, not on the underlying range.

    /*
    Dear reader: apologies for the following code.
    It calls "chop" on the underlying range, which returns a chopped<> with
    the first element (to be returned as-is) and the rest (to be wrapped in
    a new take_range).
    The best way to take this in is to read the body of the function, and
    only then convince yourself that the types make sense.
    */
    template <class TakeRange, class Direction,
        class Limit = typename std::decay <TakeRange>::type::limit_type,
        class NewLimit = decltype (
            rime::cast_value <Limit> (Limit()
                - callable::implementation::one_type())),
        class Underlying = typename helper::underlying_type <TakeRange>::type,
        class FirstAndUnderlyingRest = decltype (callable::chop_direct() (
            std::declval <Underlying>(), std::declval <Direction>(),
            pick_overload())),
        class NewTakeRange = take_range <
            typename FirstAndUnderlyingRest::rest_type, NewLimit, Direction>,
        class Result = chopped <
            typename FirstAndUnderlyingRest::first_type, NewTakeRange>>
    inline Result implement_chop (take_range_tag const &,
        TakeRange && r, Direction const & direction)
    {
        callable::implementation::one_type one;
        rime::assert_ (direction == r.direction());
        rime::assert_ (!rime::less_sign_safe (r.limit(), one));
        auto first_and_underlying_rest = callable::chop_direct() (
            range::helper::get_underlying <TakeRange> (r), direction,
            pick_overload());
        return Result (first_and_underlying_rest.forward_first(),
            range::take (first_and_underlying_rest.move_rest(),
                rime::cast_value <Limit> (r.limit() - one), direction));
    }

} // namespace take_operation

} // namespace range

#endif // RANGE_TAKE_HPP_INCLUDED

max_stars_repo_name: rogiervd/range

max_issues_repo_name: rogiervd/range

max_forks_repo_name: rogiervd/range


text:
/**
* Copyright (C) 2020 Xilinx, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may
* not use this file except in compliance with the License. A copy of the
* License is located at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef XF_BLAS_MATRIX_HPP
#define XF_BLAS_MATRIX_HPP

#include <iostream>
#include <fstream>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/gzip.hpp>
//#include <boost/iostreams/filter/bzip2.hpp>

class MtxRow {
   private:
    unsigned int m_Row, m_Col;
    double m_Val;

   public:
    MtxRow() : m_Row(0), m_Col(0), m_Val(0) {}
    MtxRow(double p_Val, unsigned int p_Row, unsigned int p_Col) : m_Row(p_Row), m_Col(p_Col), m_Val(p_Val) {}
    unsigned int getRow() { return m_Row; }
    unsigned int getCol() { return m_Col; }
    double getVal() { return m_Val; }
    void scan(std::istream& p_Is) {
        p_Is >> m_Row >> m_Col >> m_Val;
        if ((m_Row <= 0) || (m_Col <= 0)) {
            std::cerr << "  Error: invalid MTX file line row=" << m_Row << " col=" << m_Col << " val=" << m_Val << "\n";
            assert(0);
        }
        // Indices start from 1 in MTX; 0 locally
        m_Row--;
        m_Col--;
    }
    friend bool operator<(MtxRow& a, MtxRow& b) {
        if (a.getRow() < b.getRow()) {
            return (true);
        } else if (a.getRow() == b.getRow()) {
            if (a.getCol() < b.getCol()) {
                return (true);
            }
        }
        return (false);
    }
    struct compareCol {
        bool operator()(MtxRow& a, MtxRow& b) {
            if (a.getCol() < b.getCol()) {
                return (true);
            } else if (a.getCol() == b.getCol()) {
                if (a.getRow() < b.getRow()) {
                    return (true);
                }
            }
            return (false);
        }
    };

    void print(std::ostream& os) {
        os << std::setw(BLAS_FLOAT_WIDTH) << int(getRow()) << " " << std::setw(BLAS_FLOAT_WIDTH) << int(getCol())
           << "    " << std::setw(BLAS_FLOAT_WIDTH) << getVal();
    }
};
inline std::ostream& operator<<(std::ostream& os, MtxRow& p_Val) {
    p_Val.print(os);
    return (os);
}

// Regula Matrix descriptor with data itself stored in caller's space
template <typename T>
class DenseMat {
   private:
    unsigned int m_Rows, m_Cols, m_Ld;
    T* m_Addr;

   public:
    DenseMat() {}
    DenseMat(unsigned int p_Rows, unsigned int p_Cols, unsigned int p_Ld, T* p_Addr)
        : m_Rows(p_Rows), m_Cols(p_Cols), m_Ld(p_Ld), m_Addr(p_Addr) {}
    DenseMat& operator=(const DenseMat& p_Src) {
        assert(p_Src.rows() == rows());
        assert(p_Src.cols() == cols());
        for (unsigned int row = 0; row < m_Rows; ++row) {
            for (unsigned int col = 0; col < m_Cols; ++col) {
                m_Addr[row][col] = p_Src.getVal(row, col);
            }
        }
        return *this;
    }
    inline T& getVal(unsigned int p_Row, unsigned int p_Col) { return m_Addr[p_Row * ld() + p_Col]; }
    inline unsigned int rows() { return m_Rows; }
    inline unsigned int cols() { return m_Cols; }
    inline unsigned int ld() { return m_Ld; }
    void init(unsigned int p_Rows, unsigned int p_Cols, unsigned int p_Ld, T* p_Addr) {
        m_Rows = p_Rows;
        m_Cols = p_Cols;
        m_Ld = p_Ld;
        m_Addr = p_Addr;
    }
    void fillMod(T p_Max, T p_First = 0, bool p_small = false) {
        T l_val = p_First;
        for (unsigned int row = 0; row < m_Rows; ++row) {
            for (unsigned int col = 0; col < ld(); ++col) {
                getVal(row, col) = l_val;
                l_val++;
                l_val %= p_Max;
            }
        }
    }

    void fillModFromFile(std::string l_fs) {
        std::ifstream inputFile(l_fs);
        T l_val;
        if (inputFile.good()) {
            for (unsigned int row = 0; row < m_Rows; ++row) {
                for (unsigned int col = 0; col < ld(); ++col) {
                    inputFile >> l_val;
                    getVal(row, col) = l_val;
                }
            }
        } else {
            std::cout << "no file no loading!\n";
        }
    }

    void fillModFromBin(std::string l_fs) {
        std::ifstream inFile;
        inFile.open(l_fs);
        if (inFile.is_open()) {
            inFile.read((char*)m_Addr, sizeof(T) * m_Rows * ld());
            inFile.close();
        } else {
            std::cout << "no file no loading!\n";
        }
    }

    void fillFromFile(std::istream& p_Is) {
        T l_val;
        for (unsigned int row = 0; row < m_Rows; ++row) {
            for (unsigned int col = 0; col < ld(); ++col) {
                p_Is >> l_val;
                getVal(row, col) = l_val;
            }
        }
    }
#if BLAS_runTransp == 1
    // Golden comparsion functions for transp engine
    void transpose(DenseMat& p_A) {
        for (unsigned int row = 0; row < rows(); ++row) {
            for (unsigned int col = 0; col < cols(); ++col) {
                getVal(row, col) = p_A.getVal(col, row);
            }
        }
        std::swap(m_Rows, m_Cols);
    }
    void transposeGva(DenseMat& p_A, unsigned int p_rowEdgeWidth, unsigned int p_colEdgeWidth) {
        unsigned int l_pos = 0;
        for (unsigned int rowBlock = 0; rowBlock < p_A.rows() / p_rowEdgeWidth; ++rowBlock) {
            for (unsigned int colBlock = 0; colBlock < p_A.cols() / p_colEdgeWidth; ++colBlock) {
                for (unsigned int col = 0; col < p_colEdgeWidth; ++col) {
                    for (unsigned int row = 0; row < p_rowEdgeWidth; ++row) {
                        getVal(l_pos / cols(), l_pos % cols()) =
                            p_A.getVal(row + rowBlock * p_rowEdgeWidth, col + colBlock * p_colEdgeWidth);
                        l_pos++;
                    }
                    // std::cout << "DEBUG transposeGva step " << *this << "\n";
                }
            }
        }
        std::swap(m_Rows, m_Cols);
    }
#endif
    void print(std::ostream& os) {
        os << m_Rows << "x" << m_Cols << " Ld=" << m_Ld << "\n";
        unsigned int l_cols = cols(); // normal matrix
                                      // ld();; // parent matrix (within Ld
        for (unsigned int row = 0; row < rows(); ++row) {
            for (unsigned int col = 0; col < l_cols; ++col) {
                os << std::setw(BLAS_FLOAT_WIDTH) << int(getVal(row, col)) << " ";
            }
            os << "\n";
        }
    }
    bool cmp(float p_TolRel, float p_TolAbs, DenseMat& p_Ref) {
        bool ok = true;
        unsigned int l_numExactMatches = 0, l_numMismatches = 0;
        for (unsigned int row = 0; row < rows(); ++row) {
            for (unsigned int col = 0; col < cols(); ++col) {
                std::string l_Prefix = "      row " + std::to_string(row) + " col " + std::to_string(col);
                T v = getVal(row, col);
                T vRef = p_Ref.getVal(row, col);
                bool l_exactMatch = false;
                bool l_ok = xf::blas::cmpVal<T>(p_TolRel, p_TolAbs, vRef, v, l_Prefix, l_exactMatch, 1);
                ok = ok && l_ok;
                if (l_exactMatch) {
                    l_numExactMatches++;
                }
                if (!l_ok) {
                    l_numMismatches++;
                }
            }
        }
        unsigned int l_total = rows() * cols();
        unsigned int l_withinTolerance = l_total - l_numExactMatches - l_numMismatches;
        std::cout << "  Compared " << l_total << " values:"
                  << "  exact match " << l_numExactMatches << "  within tolerance " << l_withinTolerance
                  << "  mismatch " << l_numMismatches << "\n";
        return (ok);
    }
};
template <typename T1>
std::ostream& operator<<(std::ostream& os, DenseMat<T1>& p_Val) {
    p_Val.print(os);
    return (os);
}

/*Type define and float specialization*/

typedef DenseMat<float> MatType_ForFloat;

typedef DenseMat<BLAS_dataType> MatType;

template <>
void MatType_ForFloat::fillMod(float p_Max, float p_First, bool p_small) {
    if (p_small) {
        // for easier debug of matrices
        float l_val = p_First;
        for (unsigned int row = 0; row < m_Rows; ++row) {
            for (unsigned int col = 0; col < ld(); ++col) {
                getVal(row, col) = l_val;
                l_val += 0.01;
                if (l_val > p_Max) {
                    l_val -= p_Max;
                }
            }
        }
    } else {
        // for better float robustness of large matrices
        float l_val = 1.0;
        float l_step = 0.3;
        float l_drift = 0.00001;
        float l_sign = 1;
        for (unsigned int row = 0; row < m_Rows; ++row) {
            for (unsigned int col = 0; col < ld(); ++col) {
                getVal(row, col) = l_val;
                l_val += l_sign * l_step;
                l_step += l_drift;
                l_sign = -l_sign;
                if (l_val > p_Max) {
                    l_val -= p_Max;
                }
            }
        }
    }
}
template <>
void MatType_ForFloat::print(std::ostream& os) {
    os << m_Rows << "x" << m_Cols << " Ld=" << m_Ld << "\n";
    unsigned int l_cols = cols(); // normal matrix
                                  // ld();; // parent matrix (within Ld
    for (unsigned int row = 0; row < rows(); ++row) {
        for (unsigned int col = 0; col < l_cols; ++col) {
            os << std::setw(BLAS_FLOAT_WIDTH) << std::fixed << std::setprecision(3) << getVal(row, col) << " ";
        }
        os << "\n";
    }
}

#endif


text:
#include <NTL/tools.h>

#include <cctype>
#include <cstdio>

NTL_START_IMPL

NTL_CHEAP_THREAD_LOCAL void (*ErrorCallback)() = 0;
NTL_CHEAP_THREAD_LOCAL void (*ErrorMsgCallback)(const char *) = 0;

void TerminalError(const char *s)
{
   if (ErrorMsgCallback)
      (*ErrorMsgCallback)(s);
   else
      cerr << s << "\n";

   if (ErrorCallback) (*ErrorCallback)();
   abort();
}

// The following implementation of CharToIntVal is completely portable.

long CharToIntVal(long a)
{
   switch (a) {
      case '0': return 0;
      case '1': return 1;
      case '2': return 2;
      case '3': return 3;
      case '4': return 4;
      case '5': return 5;
      case '6': return 6;
      case '7': return 7;
      case '8': return 8;
      case '9': return 9;

      case 'A': return 10;
      case 'B': return 11;
      case 'C': return 12;
      case 'D': return 13;
      case 'E': return 14;
      case 'F': return 15;

      case 'a': return 10;
      case 'b': return 11;
      case 'c': return 12;
      case 'd': return 13;
      case 'e': return 14;
      case 'f': return 15;

      default:  return -1;
   }
}

// The following implementation of IntValToChar is completely portable.

char IntValToChar(long a)
{
   switch (a) {
      case 0: return '0';
      case 1: return '1';
      case 2: return '2';
      case 3: return '3';
      case 4: return '4';
      case 5: return '5';
      case 6: return '6';
      case 7: return '7';
      case 8: return '8';
      case 9: return '9';

      case 10: return 'a';
      case 11: return 'b';
      case 12: return 'c';
      case 13: return 'd';
      case 14: return 'e';
      case 15: return 'f';

      default: LogicError("IntValToChar: bad arg");
   }

   return 0;  // to supress warnings
}

long IsWhiteSpace(long a)
{
   if (a > NTL_MAX_INT || a < NTL_MIN_INT)
      return 0;

   int b = (int) a;

   if (isspace(b))
      return 1;
   else
      return 0;
}

long SkipWhiteSpace(istream& s)
{
   long c;

   c = s.peek();
   while (IsWhiteSpace(c)) {
      s.get();
      c = s.peek();
   }

   if (c == EOF)
      return 0;
   else
      return 1;
}

long IsEOFChar(long c)
{
   return c == EOF;
}

void PrintTime(ostream& s, double t)
{
   long hh, mm, ss;

   ss = long(t + 0.5);

   hh = ss/3600;
   ss = ss - hh*3600;
   mm = ss/60;
   ss = ss - mm*60;

   if (hh > 0)
      s << hh << ":";

   if (hh > 0 || mm > 0) {
      if (hh > 0 && mm < 10) s << "0";
      s << mm << ":";
   }

   if ((hh > 0 || mm > 0) && ss < 10) s << "0";
   s << ss;
}

NTL_END_IMPL


text:
/*******************************************************************************
Copyright(c) 2015-2020 Parker Hannifin Corp. All rights reserved.

MIT Licensed. See the included LICENSE.txt for a copy of the full MIT License.
*******************************************************************************/
#include "mscl/MicroStrain/MIP/MipDataField.h"
#include "mscl/MicroStrain/ResponseCollector.h"
#include "mscl/MicroStrain/Inertial/Commands/ContinuousDataStream.h"
#include "mscl/Exceptions.h"

#include <boost/test/unit_test.hpp>

using namespace std;
using namespace mscl;

BOOST_AUTO_TEST_SUITE(ContinuousDataStream_Test)

BOOST_AUTO_TEST_CASE(ContinuousDataStream_BuildCommand_Sensor)
{
    //create the "get" command
    ByteStream b = ContinuousDataStream::buildCommand_get(MipTypes::CLASS_GNSS);
    BOOST_CHECK_EQUAL(b.read_uint8(0), 0x75);
    BOOST_CHECK_EQUAL(b.read_uint8(1), 0x65);
    BOOST_CHECK_EQUAL(b.read_uint8(2), 0x0C);
    BOOST_CHECK_EQUAL(b.read_uint8(3), 0x05);
    BOOST_CHECK_EQUAL(b.read_uint8(4), 0x05);
    BOOST_CHECK_EQUAL(b.read_uint8(5), 0x11);
    BOOST_CHECK_EQUAL(b.read_uint8(6), 0x02);
    BOOST_CHECK_EQUAL(b.read_uint8(7), 0x02);
    BOOST_CHECK_EQUAL(b.read_uint8(8), 0x00);
    BOOST_CHECK_EQUAL(b.read_uint8(9), 0x05);
    BOOST_CHECK_EQUAL(b.read_uint8(10), 0x1e);

    //create the "set" command
    ByteStream b2 = ContinuousDataStream::buildCommand_set(MipTypes::CLASS_GNSS, true);
    BOOST_CHECK_EQUAL(b2.read_uint8(0), 0x75);
    BOOST_CHECK_EQUAL(b2.read_uint8(1), 0x65);
    BOOST_CHECK_EQUAL(b2.read_uint8(2), 0x0C);
    BOOST_CHECK_EQUAL(b2.read_uint8(3), 0x05);
    BOOST_CHECK_EQUAL(b2.read_uint8(4), 0x05);
    BOOST_CHECK_EQUAL(b2.read_uint8(5), 0x11);
    BOOST_CHECK_EQUAL(b2.read_uint8(6), 0x01);
    BOOST_CHECK_EQUAL(b2.read_uint8(7), 0x02);
    BOOST_CHECK_EQUAL(b2.read_uint8(8), 0x01);
    BOOST_CHECK_EQUAL(b2.read_uint8(9), 0x05);
    BOOST_CHECK_EQUAL(b2.read_uint8(10), 0x1c);
}

BOOST_AUTO_TEST_CASE(ContinuousDataStream_get_Match_Success)
{
    std::shared_ptr<ResponseCollector> rc(new ResponseCollector);
    ContinuousDataStream::Response response(rc, true, MipTypes::CLASS_GNSS);

    Bytes dataField;
    dataField.push_back(0x02);
    dataField.push_back(0x01);

    Bytes ackField;
    ackField.push_back(0x11);
    ackField.push_back(0x00);
    MipDataField field1(0x0CF1, ackField); //good ack field

    //check that the match fails
    BOOST_CHECK_EQUAL(response.match(field1), true);
    BOOST_CHECK_EQUAL(response.fullyMatched(), false);    //not yet fully matched

    MipDataField field2(0x0C85, dataField); //good data field

    //check that the match succeeds
    BOOST_CHECK_EQUAL(response.match(field2), true);
    BOOST_CHECK_EQUAL(response.fullyMatched(), true);    //we  fully matched because the device info is enough to match
    BOOST_CHECK_EQUAL(response.result().success(), true);
    BOOST_CHECK_EQUAL(response.result().errorCode(), mscl::MipPacket::MIP_ACK_NACK_ERROR_NONE);
    BOOST_CHECK_EQUAL(response.result().data().read_uint8(0), 2);
    BOOST_CHECK_EQUAL(response.result().data().read_uint8(1), 1);
}

BOOST_AUTO_TEST_CASE(ContinuousDataStream_set_Match_Success)
{
    std::shared_ptr<ResponseCollector> rc(new ResponseCollector);
    ContinuousDataStream::Response response(rc, false, MipTypes::CLASS_GNSS);

    Bytes ackField;
    ackField.push_back(0x11);
    ackField.push_back(0x00);
    MipDataField field1(0x0CF1, ackField); //good ack field

    //check that the match fails
    BOOST_CHECK_EQUAL(response.match(field1), true);
    BOOST_CHECK_EQUAL(response.fullyMatched(), true);    //fully matched
}

BOOST_AUTO_TEST_CASE(ContinuousDataStream_get_Match_Success_OnlyData)
{
    std::shared_ptr<ResponseCollector> rc(new ResponseCollector);
    ContinuousDataStream::Response response(rc, true, MipTypes::CLASS_GNSS);

    Bytes fieldData1;
    fieldData1.push_back(0x02);
    fieldData1.push_back(0x01);
    MipDataField field1(0x0C85, fieldData1);

    //check that the match fails
    BOOST_CHECK_EQUAL(response.match(field1), true);
}

BOOST_AUTO_TEST_CASE(ContinuousDataStream_get_Match_Fail_WrongDeviceSelector)
{
    std::shared_ptr<ResponseCollector> rc(new ResponseCollector);
    ContinuousDataStream::Response response(rc, true, MipTypes::CLASS_GNSS);

    Bytes fieldData1;
    fieldData1.push_back(0x01);    //invalid device selector (device category)
    fieldData1.push_back(0x01);
    MipDataField field1(0x0C85, fieldData1);

    //check that the match fails
    BOOST_CHECK_EQUAL(response.match(field1), false);
}

BOOST_AUTO_TEST_CASE(ContinuousDataStream_get_Match_Fail_SmallFieldData)
{
    std::shared_ptr<ResponseCollector> rc(new ResponseCollector);
    ContinuousDataStream::Response response(rc, true, MipTypes::CLASS_GNSS);

    Bytes fieldData1;
    fieldData1.push_back(0x02);    //missing a byte
    MipDataField field1(0x0C85, fieldData1);

    //check that the match fails
    BOOST_CHECK_EQUAL(response.match(field1), false);
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: contagon/MSCL

max_stars_repo_licenses: BSL-1.0 OpenSSL MIT

max_issues_repo_name: contagon/MSCL

max_issues_repo_licenses: BSL-1.0 OpenSSL MIT

max_forks_repo_name: contagon/MSCL

max_forks_repo_licenses: BSL-1.0 OpenSSL MIT


text: #include <boost/spirit/include/classic_distinct.hpp>