<commit_before>// Copyright (c) 2013 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#include "libmv/simple_pipeline/modal_solver.h"

#include "testing/testing.h"
#include "libmv/logging/logging.h"
#include "libmv/simple_pipeline/bundle.h"
#include "libmv/simple_pipeline/camera_intrinsics.h"

#include <stdio.h>

namespace libmv {

TEST(ModalSolver, SyntheticCubeSceneMotion) {
  CameraIntrinsics intrinsics;
  intrinsics.SetFocalLength(658.286, 658.286);
  intrinsics.SetPrincipalPoint(480.0, 270.0);
  intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);

  Marker markers[] = {
      {1, 0, 212.172775, 354.713538}, {2, 0, 773.468399, 358.735306},
      {1, 1, 62.415197, 287.905354},  {2, 1, 619.103336, 324.402537},
      {1, 2, 206.847939, 237.567925}, {2, 2, 737.496986, 247.881383},
      {1, 3, 351.743889, 316.415906}, {2, 3, 908.779621, 290.703617},
      {1, 4, 232.941413, 54.265443},  {2, 4, 719.444847, 63.062531},
      {1, 5, 96.391611, 119.283537},  {2, 5, 611.413136, 160.890715},
      {1, 6, 363.444958, 150.838144}, {2, 6, 876.374531, 114.916206},
    };
  int num_markers = sizeof(markers) / sizeof(Marker);

  Tracks tracks;
  for (int i = 0; i < num_markers; i++) {
    double x = markers[i].x, y = markers[i].y;
    intrinsics.InvertIntrinsics(x, y, &x, &y);
    tracks.Insert(markers[i].image, markers[i].track, x, y);
  }

  EuclideanReconstruction reconstruction;
  ModalSolver(tracks, &reconstruction);
  EuclideanBundleCommonIntrinsics(tracks,
                                  BUNDLE_NO_INTRINSICS,
                                  &reconstruction,
                                  &intrinsics,
                                  BUNDLE_NO_TRANSLATION);

  Mat3 expected_rotation;
  expected_rotation << 0.98215101299251, 0.17798357184544,  0.06083778292258,
                      -0.16875286001759, 0.97665299913606, -0.13293378620359,
                      -0.08307743323957, 0.12029450291547,  0.98925596922871;

  EXPECT_TRUE(Mat3::Identity().isApprox(reconstruction.CameraForImage(1)->R));
  EXPECT_TRUE(expected_rotation.isApprox(reconstruction.CameraForImage(2)->R));
}

}  // namespace libmv
<commit_msg>Use epsilon in modal solver test<commit_after>// Copyright (c) 2013 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#include "libmv/simple_pipeline/modal_solver.h"

#include "testing/testing.h"
#include "libmv/logging/logging.h"
#include "libmv/simple_pipeline/bundle.h"
#include "libmv/simple_pipeline/camera_intrinsics.h"

#include <stdio.h>

namespace libmv {

TEST(ModalSolver, SyntheticCubeSceneMotion) {
  double kTolerance = 1e-12;

  CameraIntrinsics intrinsics;
  intrinsics.SetFocalLength(658.286, 658.286);
  intrinsics.SetPrincipalPoint(480.0, 270.0);
  intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);

  Marker markers[] = {
      {1, 0, 212.172775, 354.713538}, {2, 0, 773.468399, 358.735306},
      {1, 1, 62.415197, 287.905354},  {2, 1, 619.103336, 324.402537},
      {1, 2, 206.847939, 237.567925}, {2, 2, 737.496986, 247.881383},
      {1, 3, 351.743889, 316.415906}, {2, 3, 908.779621, 290.703617},
      {1, 4, 232.941413, 54.265443},  {2, 4, 719.444847, 63.062531},
      {1, 5, 96.391611, 119.283537},  {2, 5, 611.413136, 160.890715},
      {1, 6, 363.444958, 150.838144}, {2, 6, 876.374531, 114.916206},
    };
  int num_markers = sizeof(markers) / sizeof(Marker);

  Tracks tracks;
  for (int i = 0; i < num_markers; i++) {
    double x = markers[i].x, y = markers[i].y;
    intrinsics.InvertIntrinsics(x, y, &x, &y);
    tracks.Insert(markers[i].image, markers[i].track, x, y);
  }

  EuclideanReconstruction reconstruction;
  ModalSolver(tracks, &reconstruction);
  EuclideanBundleCommonIntrinsics(tracks,
                                  BUNDLE_NO_INTRINSICS,
                                  &reconstruction,
                                  &intrinsics,
                                  BUNDLE_NO_TRANSLATION);

  Mat3 expected_rotation;
  expected_rotation << 0.98215101299251, 0.17798357184544,  0.06083778292258,
                      -0.16875286001759, 0.97665299913606, -0.13293378620359,
                      -0.08307743323957, 0.12029450291547,  0.98925596922871;

  Mat3 &first_camera_R = reconstruction.CameraForImage(1)->R;
  Mat3 &second_camera_R = reconstruction.CameraForImage(2)->R;

  EXPECT_TRUE(Mat3::Identity().isApprox(first_camera_R, kTolerance));
  EXPECT_TRUE(expected_rotation.isApprox(second_camera_R, kTolerance));
}

}  // namespace libmv
<|endoftext|>


<commit_before>// Copyright (c) 2018 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rcu.h"
#include "sync.h"

#include <chrono>
#include <condition_variable>

std::atomic<uint64_t> RCUInfos::revision{0};
thread_local RCUInfos RCUInfos::infos{};

/**
 * How many time a busy loop runs before yelding.
 */
static const int RCU_ACTIVE_LOOP_COUNT = 30;

/**
 * We maintain a linked list of all the RCUInfos for each active thread. Upon
 * start, a new thread adds itself to the head of the liked list and the node is
 * then removed when the threads shuts down.
 *
 * Insertion is fairly straightforward. The first step is to set the next
 * pointer of the node being inserted as the first node in the list as follow:
 *
 * threadInfos -> Node -> ...
 *                 ^
 *         Nadded -|
 *
 * The second step is to update threadInfos to point on the inserted node. This
 * is done using compare and swap. If the head of the list changed during this
 * process - for instance due to another insertion, CAS will fail and we can
 * start again.
 *
 * threadInfos    Node -> ...
 *     |           ^
 *     \-> Nadded -|
 *
 * Deletion is a slightly more complex process. The general idea is to go over
 * the list, find the parent of the item we want to remove and set it's next
 * pointer to jump over it.
 *
 * Nparent -> Ndelete -> Nchild
 *
 * Nparent    Ndelete -> Nchild
 *    |                    ^
 *    \--------------------|
 *
 * We run into problems when a nodes is deleted concurrently with another node
 * being inserted. Hopefully, we can solve that problem with CAS as well.
 *
 * threadInfos -> Ndelete -> Nchild
 *                   ^
 *           Nadded -|
 *
 * The insertion will try to update threadInfos to point to Nadded, while the
 * deletion will try to update it to point to Nchild. Whichever goes first will
 * cause the other to fail its CAS and restart its process.
 *
 * threadInfos    Ndelete -> Nchild
 *      |            ^
 *      \--> Nadded -|
 *
 * After a successful insertion, threadInfos now points to Nadded, and the CAS
 * to move it to Nchild will fail, causing the deletion process to restart from
 * scratch.
 *
 *      /----------------------|
 *      |                      V
 * threadInfos    Ndelete -> Nchild
 *                   ^
 *           Nadded -|
 *
 * After a succesful deletion, threadInfos now points to NChild and the CAS to
 * move it to Nadded will fail, causing the insertion process to fail.
 *
 * We also run into problems when several nodes are deleted concurrently.
 * Because it is not possible to read Ndelete->next and update Nparent->next
 * atomically, we may end up setting Nparent->next to a stale value if Nchild is
 * deleted.
 *
 *               /----------------------|
 *               |                      V
 * Nparent    Ndelete    Nchild -> Ngrandchild
 *    |                    ^
 *    \--------------------|
 *
 * This would cause Nchild to be 'resurrected', which is obviously a problem. In
 * order to avoid this problem, we make sure that no concurrent deletion takes
 * places using a good old mutex. Using a mutex for deletion also ensures we are
 * safe from the ABA problem.
 *
 * Once a node is deleted from the list, we cannot destroy it right away.
 * Readers do not hold the mutex and may still be using that node. We need to
 * leverage RCU to make sure all the readers have finished their work before
 * allowing the node to be destroyed. We need to keep the mutex during that
 * process, because we just removed our thread from the list of thread to wait
 * for. A concurrent deletion would not wait for us and may end up deleting data
 * we rely on as a result.
 */
static std::atomic<RCUInfos *> threadInfos{nullptr};
static CCriticalSection csThreadInfosDelete;

RCUInfos::RCUInfos() : next(nullptr), state(0) {
    RCUInfos *head = threadInfos.load();
    do {
        next.store(head);
    } while (!threadInfos.compare_exchange_weak(head, this));

    // Release the lock.
    readFree();
}

RCUInfos::~RCUInfos() {
    while (true) {
        LOCK(csThreadInfosDelete);

        std::atomic<RCUInfos *> *ptr;

        {
            RCULock lock(this);
            ptr = &threadInfos;
            while (true) {
                RCUInfos *current = ptr->load();
                if (current == this) {
                    break;
                }

                assert(current != nullptr);
                ptr = &current->next;
            }
        }

        /**
         * We have our node and the parent is ready to be updated.
         * NB: The CAS operation only checks for *ptr and not for next. This
         * would be a big problem in the general case, but because we only
         * insert at the tip of the list and cannot have concurrent deletion
         * thanks to the use of a mutex, we are safe.
         */
        RCUInfos *current = this;
        if (!ptr->compare_exchange_weak(current, next.load())) {
            continue;
        }

        /**
         * We now wait for possible readers to go past the synchronization
         * point. We need to do so while holding the lock as this operation
         * require us to a be a reader, but we just removed ourselves from the
         * list of reader to check and may therefore not be waited for.
         */
        synchronize();
        break;
    }
}

void RCUInfos::synchronize() {
    uint64_t syncRev = ++revision;

    // Loop a few time lock free.
    for (int i = 0; i < RCU_ACTIVE_LOOP_COUNT; i++) {
        if (hasSynced(syncRev)) {
            // Done!
            return;
        }
    }

    // It seems like we have some contention. Let's try to not starve the
    // system. Let's make sure threads that land here proceed one by one.
    // XXX: The best option long term is most likely to use a futex on one of
    // the thread causing synchronization delay so this thread can be waked up
    // at an apropriate time.
    static std::condition_variable cond;
    static CWaitableCriticalSection cs;
    WAIT_LOCK(cs, lock);

    do {
        cond.notify_one();
    } while (!cond.wait_for(lock, std::chrono::microseconds(1),
                            [&] { return hasSynced(syncRev); }));
}

bool RCUInfos::hasSynced(uint64_t syncRev) {
    // Go over the list and check all threads are past the synchronization
    // point.
    RCULock lock(this);
    RCUInfos *current = threadInfos.load();
    while (current != nullptr) {
        // If the current thread is not up to speed, bail.
        if (current->state < syncRev) {
            return false;
        }

        current = current->next.load();
    }

    return true;
}
<commit_msg>[RCU] Use strong CAS where apropriate<commit_after>// Copyright (c) 2018 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "rcu.h"
#include "sync.h"

#include <chrono>
#include <condition_variable>

std::atomic<uint64_t> RCUInfos::revision{0};
thread_local RCUInfos RCUInfos::infos{};

/**
 * How many time a busy loop runs before yelding.
 */
static const int RCU_ACTIVE_LOOP_COUNT = 30;

/**
 * We maintain a linked list of all the RCUInfos for each active thread. Upon
 * start, a new thread adds itself to the head of the liked list and the node is
 * then removed when the threads shuts down.
 *
 * Insertion is fairly straightforward. The first step is to set the next
 * pointer of the node being inserted as the first node in the list as follow:
 *
 * threadInfos -> Node -> ...
 *                 ^
 *         Nadded -|
 *
 * The second step is to update threadInfos to point on the inserted node. This
 * is done using compare and swap. If the head of the list changed during this
 * process - for instance due to another insertion, CAS will fail and we can
 * start again.
 *
 * threadInfos    Node -> ...
 *     |           ^
 *     \-> Nadded -|
 *
 * Deletion is a slightly more complex process. The general idea is to go over
 * the list, find the parent of the item we want to remove and set it's next
 * pointer to jump over it.
 *
 * Nparent -> Ndelete -> Nchild
 *
 * Nparent    Ndelete -> Nchild
 *    |                    ^
 *    \--------------------|
 *
 * We run into problems when a nodes is deleted concurrently with another node
 * being inserted. Hopefully, we can solve that problem with CAS as well.
 *
 * threadInfos -> Ndelete -> Nchild
 *                   ^
 *           Nadded -|
 *
 * The insertion will try to update threadInfos to point to Nadded, while the
 * deletion will try to update it to point to Nchild. Whichever goes first will
 * cause the other to fail its CAS and restart its process.
 *
 * threadInfos    Ndelete -> Nchild
 *      |            ^
 *      \--> Nadded -|
 *
 * After a successful insertion, threadInfos now points to Nadded, and the CAS
 * to move it to Nchild will fail, causing the deletion process to restart from
 * scratch.
 *
 *      /----------------------|
 *      |                      V
 * threadInfos    Ndelete -> Nchild
 *                   ^
 *           Nadded -|
 *
 * After a succesful deletion, threadInfos now points to NChild and the CAS to
 * move it to Nadded will fail, causing the insertion process to fail.
 *
 * We also run into problems when several nodes are deleted concurrently.
 * Because it is not possible to read Ndelete->next and update Nparent->next
 * atomically, we may end up setting Nparent->next to a stale value if Nchild is
 * deleted.
 *
 *               /----------------------|
 *               |                      V
 * Nparent    Ndelete    Nchild -> Ngrandchild
 *    |                    ^
 *    \--------------------|
 *
 * This would cause Nchild to be 'resurrected', which is obviously a problem. In
 * order to avoid this problem, we make sure that no concurrent deletion takes
 * places using a good old mutex. Using a mutex for deletion also ensures we are
 * safe from the ABA problem.
 *
 * Once a node is deleted from the list, we cannot destroy it right away.
 * Readers do not hold the mutex and may still be using that node. We need to
 * leverage RCU to make sure all the readers have finished their work before
 * allowing the node to be destroyed. We need to keep the mutex during that
 * process, because we just removed our thread from the list of thread to wait
 * for. A concurrent deletion would not wait for us and may end up deleting data
 * we rely on as a result.
 */
static std::atomic<RCUInfos *> threadInfos{nullptr};
static CCriticalSection csThreadInfosDelete;

RCUInfos::RCUInfos() : next(nullptr), state(0) {
    RCUInfos *head = threadInfos.load();
    do {
        next.store(head);
    } while (!threadInfos.compare_exchange_weak(head, this));

    // Release the lock.
    readFree();
}

RCUInfos::~RCUInfos() {
    while (true) {
        LOCK(csThreadInfosDelete);

        std::atomic<RCUInfos *> *ptr;

        {
            RCULock lock(this);
            ptr = &threadInfos;
            while (true) {
                RCUInfos *current = ptr->load();
                if (current == this) {
                    break;
                }

                assert(current != nullptr);
                ptr = &current->next;
            }
        }

        /**
         * We have our node and the parent is ready to be updated.
         * NB: The CAS operation only checks for *ptr and not for next. This
         * would be a big problem in the general case, but because we only
         * insert at the tip of the list and cannot have concurrent deletion
         * thanks to the use of a mutex, we are safe.
         */
        RCUInfos *current = this;
        if (!ptr->compare_exchange_strong(current, next.load())) {
            continue;
        }

        /**
         * We now wait for possible readers to go past the synchronization
         * point. We need to do so while holding the lock as this operation
         * require us to a be a reader, but we just removed ourselves from the
         * list of reader to check and may therefore not be waited for.
         */
        synchronize();
        break;
    }
}

void RCUInfos::synchronize() {
    uint64_t syncRev = ++revision;

    // Loop a few time lock free.
    for (int i = 0; i < RCU_ACTIVE_LOOP_COUNT; i++) {
        if (hasSynced(syncRev)) {
            // Done!
            return;
        }
    }

    // It seems like we have some contention. Let's try to not starve the
    // system. Let's make sure threads that land here proceed one by one.
    // XXX: The best option long term is most likely to use a futex on one of
    // the thread causing synchronization delay so this thread can be waked up
    // at an apropriate time.
    static std::condition_variable cond;
    static CWaitableCriticalSection cs;
    WAIT_LOCK(cs, lock);

    do {
        cond.notify_one();
    } while (!cond.wait_for(lock, std::chrono::microseconds(1),
                            [&] { return hasSynced(syncRev); }));
}

bool RCUInfos::hasSynced(uint64_t syncRev) {
    // Go over the list and check all threads are past the synchronization
    // point.
    RCULock lock(this);
    RCUInfos *current = threadInfos.load();
    while (current != nullptr) {
        // If the current thread is not up to speed, bail.
        if (current->state < syncRev) {
            return false;
        }

        current = current->next.load();
    }

    return true;
}
<|endoftext|>


<commit_before>/* Copyright (c) 2017, EPL-Vizards
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the EPL-Vizards nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL EPL-Vizards BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*!
 * \file TimeLineModel.cpp
 */

#include "TimeLineModel.hpp"
#include "MainWindow.hpp"
#include "PlotCreator.hpp"
#include "QWTPlotWidget.hpp"
#include "TimeLineWidget.hpp"
using namespace EPL_Viz;
using namespace EPL_DataCollect;

TimeLineModel::TimeLineModel(MainWindow *mw, QwtPlot *widget) : QwtBaseModel(mw, widget) {
  (void)mw;
  (void)widget;
  scrollbar = mw->findChild<QScrollBar *>("scrBarTimeline");

  widget->enableAxis(QwtPlot::xBottom, false);

  widget->setAxisScale(QwtPlot::yLeft, 0, 10);
  widget->setAxisAutoScale(QwtPlot::yLeft, true);

  widget->setAxisScale(QwtPlot::xTop, 0, DEF_VIEWPORT_SIZE);
  widget->setAxisAutoScale(QwtPlot::xTop, true);

  magnifier = std::make_unique<TimeLineMagnifier>(scrollbar, this, &maxXValue, widget->canvas());
  magnifier->setAxisEnabled(QwtPlot::xTop, true);
  magnifier->setAxisEnabled(QwtPlot::yLeft, false);
  magnifier->setMouseButton(Qt::MouseButton::NoButton, Qt::KeyboardModifier::NoModifier);

  // Configure PlotPickers
  point = new QwtPlotPicker(plot->canvas());
  point->setAxis(QwtPlot::xTop, QwtPlot::yLeft);
  point->setStateMachine(new QwtPickerClickPointMachine());
  point->setMousePattern(QwtEventPattern::MousePatternCode::MouseSelect1, Qt::MouseButton::LeftButton);
  area = new QwtPlotPicker(plot->canvas());
  area->setAxis(QwtPlot::xTop, QwtPlot::yLeft);
  area->setStateMachine(new QwtPickerDragLineMachine());
  area->setMousePattern(QwtEventPattern::MousePatternCode::MouseSelect1, Qt::MouseButton::LeftButton);

  connect(point, SIGNAL(selected(QPointF)), this, SLOT(pointSelected(QPointF)));
  connect(area, SIGNAL(selected(QRectF)), window->findChild<QWTPlotWidget *>("tabGraph"), SLOT(changeArea(QRectF)));
}

TimeLineModel::~TimeLineModel() {
  delete point;
  delete area;
}

void TimeLineModel::pointSelected(const QPointF &pa) {
  uint32_t x = static_cast<uint32_t>(std::round(pa.x()));
  window->changeCycle(x);
  curCycleMarker.setXValue(x);
  replot();
}

void TimeLineModel::init() {
  markers.clear();

  curCycleMarker.setLineStyle(QwtPlotMarker::VLine);
  curCycleMarker.setLinePen(QColor(0, 0, 0), 2, Qt::PenStyle::DotLine);
  curCycleMarker.setXAxis(QwtPlot::xTop);
  curCycleMarker.setXValue(static_cast<double>(0));
  curCycleMarker.setLabel(QwtText("View"));
  curCycleMarker.setLabelAlignment(Qt::AlignTop | Qt::AlignLeft);
  curCycleMarker.attach(plot);

  newestCycleMarker.setLineStyle(QwtPlotMarker::VLine);
  newestCycleMarker.setLinePen(QColor(255, 0, 0), 2, Qt::PenStyle::DotLine);
  newestCycleMarker.setXAxis(QwtPlot::xTop);
  newestCycleMarker.setXValue(static_cast<double>(0));
  newestCycleMarker.setLabel(QwtText("Backend"));
  newestCycleMarker.setLabelAlignment(Qt::AlignLeft);
  newestCycleMarker.attach(plot);

  log   = getMainWindow()->getCaptureInstance()->getEventLog();
  appid = log->getAppID();

  QwtBaseModel::init();
}

void TimeLineModel::update(ProtectedCycle &cycle) {
  // Change cyclemarker position
  uint32_t cycleNum = window->getCycleNum();
  if (cycleNum == UINT32_MAX)
    cycleNum = cycle->getCycleNum();
  curCycleMarker.setXValue(static_cast<double>(cycleNum));

  // Set newest Cycle marker
  uint32_t newest = window->getCaptureInstance()->getCycleContainer()->pollCycle().getCycleNum();
  newestCycleMarker.setXValue(static_cast<double>(newest));
  maxXValue = newest;
  emit maxValueChanged(0, static_cast<int>(maxXValue - getViewportSize()));
  scrollbar->setMaximum(static_cast<int>(maxXValue));

  // Add new markers
  std::vector<EventBase *> nEvents = log->pollEvents(appid);
  if (nEvents.size() > 0)
    qDebug() << "Number of new Events adding to timeline: " + QString::number(nEvents.size());
  for (EventBase *ev : nEvents) {

    std::shared_ptr<QwtPlotMarker> marker =
          std::make_shared<QwtPlotMarker>(QString::fromStdString(ev->getDescription()));

    marker->setLineStyle(QwtPlotMarker::VLine);

    QColor col;
    switch (ev->getType()) {
      case EvType::ERROR: col   = QColor(200, 0, 0); break;
      case EvType::WARNING: col = QColor(250, 250, 0); break;
      case EvType::INFO: col    = QColor(0, 200, 0); break;
      default: col              = QColor(0, 0, 0);
    }
    marker->setLinePen(col);

    uint32_t x;
    ev->getCycleRange(&x);
    marker->setXValue(static_cast<double>(x));
    marker->setXAxis(QwtPlot::xTop);

    marker->attach(plot);
    markers.append(marker);
  }

  QwtBaseModel::update(cycle);
}

void TimeLineModel::updateViewport(int value) {
  QwtScaleDiv scale = plot->axisScaleDiv(QwtPlot::xTop);
  double      upper = scale.upperBound();
  double      lower = scale.lowerBound();

  double viewportSize = upper - lower;

  if (viewportSize < 1)
    return;

  double min = static_cast<double>(value);
  double max = min + viewportSize;

  plot->setAxisScale(QwtPlot::xTop, min, max);
  replot();
}

void TimeLineModel::reset() {
  for (std::shared_ptr<QwtPlotMarker> marker : markers) {
    marker->detach();
  }
  markers.clear();
  curCycleMarker.detach();
  newestCycleMarker.detach();
  magnifier = std::make_unique<TimeLineMagnifier>(scrollbar, this, &maxXValue, plot->canvas());
  QwtBaseModel::reset();
}
<commit_msg>Fixed small reset bug (Timeline)<commit_after>/* Copyright (c) 2017, EPL-Vizards
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the EPL-Vizards nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL EPL-Vizards BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*!
 * \file TimeLineModel.cpp
 */

#include "TimeLineModel.hpp"
#include "MainWindow.hpp"
#include "PlotCreator.hpp"
#include "QWTPlotWidget.hpp"
#include "TimeLineWidget.hpp"
using namespace EPL_Viz;
using namespace EPL_DataCollect;

TimeLineModel::TimeLineModel(MainWindow *mw, QwtPlot *widget) : QwtBaseModel(mw, widget) {
  (void)mw;
  (void)widget;
  scrollbar = mw->findChild<QScrollBar *>("scrBarTimeline");

  widget->enableAxis(QwtPlot::xBottom, false);

  widget->setAxisScale(QwtPlot::yLeft, 0, 10);
  widget->setAxisAutoScale(QwtPlot::yLeft, true);

  widget->setAxisScale(QwtPlot::xTop, 0, DEF_VIEWPORT_SIZE);
  widget->setAxisAutoScale(QwtPlot::xTop, true);

  magnifier = std::make_unique<TimeLineMagnifier>(scrollbar, this, &maxXValue, widget->canvas());
  magnifier->setAxisEnabled(QwtPlot::xTop, true);
  magnifier->setAxisEnabled(QwtPlot::yLeft, false);
  magnifier->setMouseButton(Qt::MouseButton::NoButton, Qt::KeyboardModifier::NoModifier);

  // Configure PlotPickers
  point = new QwtPlotPicker(plot->canvas());
  point->setAxis(QwtPlot::xTop, QwtPlot::yLeft);
  point->setStateMachine(new QwtPickerClickPointMachine());
  point->setMousePattern(QwtEventPattern::MousePatternCode::MouseSelect1, Qt::MouseButton::LeftButton);
  area = new QwtPlotPicker(plot->canvas());
  area->setAxis(QwtPlot::xTop, QwtPlot::yLeft);
  area->setStateMachine(new QwtPickerDragLineMachine());
  area->setMousePattern(QwtEventPattern::MousePatternCode::MouseSelect1, Qt::MouseButton::LeftButton);

  connect(point, SIGNAL(selected(QPointF)), this, SLOT(pointSelected(QPointF)));
  connect(area, SIGNAL(selected(QRectF)), window->findChild<QWTPlotWidget *>("tabGraph"), SLOT(changeArea(QRectF)));
}

TimeLineModel::~TimeLineModel() {
  delete point;
  delete area;
}

void TimeLineModel::pointSelected(const QPointF &pa) {
  uint32_t x = static_cast<uint32_t>(std::round(pa.x()));
  window->changeCycle(x);
  curCycleMarker.setXValue(x);
  replot();
}

void TimeLineModel::init() {
  markers.clear();

  curCycleMarker.setLineStyle(QwtPlotMarker::VLine);
  curCycleMarker.setLinePen(QColor(0, 0, 0), 2, Qt::PenStyle::DotLine);
  curCycleMarker.setXAxis(QwtPlot::xTop);
  curCycleMarker.setXValue(static_cast<double>(0));
  curCycleMarker.setLabel(QwtText("View"));
  curCycleMarker.setLabelAlignment(Qt::AlignTop | Qt::AlignLeft);
  curCycleMarker.attach(plot);

  newestCycleMarker.setLineStyle(QwtPlotMarker::VLine);
  newestCycleMarker.setLinePen(QColor(255, 0, 0), 2, Qt::PenStyle::DotLine);
  newestCycleMarker.setXAxis(QwtPlot::xTop);
  newestCycleMarker.setXValue(static_cast<double>(0));
  newestCycleMarker.setLabel(QwtText("Backend"));
  newestCycleMarker.setLabelAlignment(Qt::AlignLeft);
  newestCycleMarker.attach(plot);

  log   = getMainWindow()->getCaptureInstance()->getEventLog();
  appid = log->getAppID();

  QwtBaseModel::init();
}

void TimeLineModel::update(ProtectedCycle &cycle) {
  // Change cyclemarker position
  uint32_t cycleNum = window->getCycleNum();
  if (cycleNum == UINT32_MAX)
    cycleNum = cycle->getCycleNum();
  curCycleMarker.setXValue(static_cast<double>(cycleNum));

  // Set newest Cycle marker
  uint32_t newest = window->getCaptureInstance()->getCycleContainer()->pollCycle().getCycleNum();
  newestCycleMarker.setXValue(static_cast<double>(newest));
  maxXValue = newest;
  emit maxValueChanged(0, static_cast<int>(maxXValue - getViewportSize()));
  scrollbar->setMaximum(static_cast<int>(maxXValue));

  // Add new markers
  std::vector<EventBase *> nEvents = log->pollEvents(appid);
  if (nEvents.size() > 0)
    qDebug() << "Number of new Events adding to timeline: " + QString::number(nEvents.size());
  for (EventBase *ev : nEvents) {

    std::shared_ptr<QwtPlotMarker> marker =
          std::make_shared<QwtPlotMarker>(QString::fromStdString(ev->getDescription()));

    marker->setLineStyle(QwtPlotMarker::VLine);

    QColor col;
    switch (ev->getType()) {
      case EvType::ERROR: col   = QColor(200, 0, 0); break;
      case EvType::WARNING: col = QColor(250, 250, 0); break;
      case EvType::INFO: col    = QColor(0, 200, 0); break;
      default: col              = QColor(0, 0, 0);
    }
    marker->setLinePen(col);

    uint32_t x;
    ev->getCycleRange(&x);
    marker->setXValue(static_cast<double>(x));
    marker->setXAxis(QwtPlot::xTop);

    marker->attach(plot);
    markers.append(marker);
  }

  QwtBaseModel::update(cycle);
}

void TimeLineModel::updateViewport(int value) {
  QwtScaleDiv scale = plot->axisScaleDiv(QwtPlot::xTop);
  double      upper = scale.upperBound();
  double      lower = scale.lowerBound();

  double viewportSize = upper - lower;

  if (viewportSize < 1)
    return;

  double min = static_cast<double>(value);
  double max = min + viewportSize;

  plot->setAxisScale(QwtPlot::xTop, min, max);
  replot();
}

void TimeLineModel::reset() {
  for (std::shared_ptr<QwtPlotMarker> marker : markers) {
    marker->detach();
  }
  markers.clear();
  curCycleMarker.detach();
  newestCycleMarker.detach();
  QwtBaseModel::reset();
}
<|endoftext|>


<commit_before>#include "heapsort.hpp"
#include "heap.hpp"
#include "swap.hpp"

void heapsort(int *data, const int size_of_data)
{
    binary_max_heap(data, size_of_data - 1);

    do_heapsort(data, size_of_data - 1);
}

void do_heapsort(int *data, const int end)
{
    if(end <= 0) return;

    int k = 0;
    swap(data[k], data[end]); /* data[end] has been sorted and from now, should NOT touch */

    while(true)
    {
        if(2 * k + 2 < end) /* Have two child nodes */
        {
            if(data[k] < data[2 * k + 1] && data[2 * k + 1] >= data[2 * k + 2])
            {
                swap(data[k], data[2 * k + 1]);
                k = 2 * k + 1;
            }
            else if(data[k] < data[2 * k + 2])
            {
                swap(data[k], data[2 * k + 2]);
                k = 2 * k + 2;
            }
            else break;
        }
        else if(2 * k + 1 < end && data[k] < data[2 * k + 1]) /* Have one leaf node */
        {
            swap(data[k], data[2 * k + 1]);
            k = 2 * k + 1;
        }
        else break;
    }

    return do_heapsort(data, end - 1);
}
<commit_msg>Fix the not matching function declaration<commit_after>#include "heapsort.hpp"
#include "heap.hpp"
#include "swap.hpp"

void heapsort(int *data, const int size_of_data)
{
    convert_to_binary_max_heap(data, size_of_data);
    do_heapsort(data, size_of_data - 1);
}

void do_heapsort(int *data, const int end)
{
    if(end <= 0) return;

    int k = 0;
    swap(data[k], data[end]); /* data[end] has been sorted and from now, should NOT touch */

    while(true)
    {
        if(2 * k + 2 < end) /* Have two child nodes */
        {
            if(data[k] < data[2 * k + 1] && data[2 * k + 1] >= data[2 * k + 2])
            {
                swap(data[k], data[2 * k + 1]);
                k = 2 * k + 1;
            }
            else if(data[k] < data[2 * k + 2])
            {
                swap(data[k], data[2 * k + 2]);
                k = 2 * k + 2;
            }
            else break;
        }
        else if(2 * k + 1 < end && data[k] < data[2 * k + 1]) /* Have one leaf node */
        {
            swap(data[k], data[2 * k + 1]);
            k = 2 * k + 1;
        }
        else break;
    }

    return do_heapsort(data, end - 1);
}
<|endoftext|>


<commit_before>//
// Copyright Copyright 2009-2021, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    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.
//

#pragma once

#include "entity/entity.hpp"
#include "device_model/device.hpp"
#include "device_model/data_item/data_item.hpp"
#include "ruby_type.hpp"

#include <rice/rice.hpp>
#include <rice/stl.hpp>
#include <ruby/thread.h>

namespace mtconnect::ruby {
  using namespace mtconnect;
  using namespace device_model;
  using namespace data_item;
  using namespace entity;
  using namespace std;
  using namespace Rice;

  struct RubyEntity {
    void create(Rice::Module &module)
    {
      m_properties = define_class_under<Properties>(module, "Properties");
      m_propertyKey = define_class_under<PropertyKey>(module, "PropertyKey");
      m_entity = define_class_under<Entity>(module, "Entity");
      c_Entity = m_entity.value();
      m_component = define_class_under<Component, Entity>(module, "Component");
      m_device = define_class_under<device_model::Device, device_model::Component>(module, "Device");
      m_dataItem = define_class_under<data_item::DataItem, entity::Entity>(module, "DataItem");
    }

    void methods()
    {
      m_entity.define_method("value", [](entity::EntityPtr entity) { return entity->getValue(); }).
        define_method("property", [](entity::EntityPtr entity, std::string name) {
          return entity->getProperty(name);
        }, Arg("name"));

      m_dataItem.define_method("name", [](data_item::DataItem *di) { return di->getName(); }).
        define_method("observation_name", [](data_item::DataItem *di) { return di->getObservationName().str(); }).
        define_method("id", [](data_item::DataItem *di) { return di->getId(); }).
        define_method("type", [](data_item::DataItem *di) { return di->getType(); }).
        define_method("sub_type", [](data_item::DataItem *di) { return di->getSubType(); });

      m_component.define_method("component_name", [](Component *c) { return c->getComponentName(); }).
        define_method("uuid", [](Component *c) { return c->getUuid(); }).
        define_method("id", [](Component *c) { return c->getId(); }).
        define_method("children", [](Component *c) {
          Rice::Array ary;
          const auto &list = c->getChildren();
          if (list)
          {
            for (auto const &s : *list)
            {
              auto cp = dynamic_cast<Component*>(s.get());
              ary.push(cp);
            }
          }
          return ary;
        }).
      define_method("data_items", [](Component *c) {
          Rice::Array ary;
          const auto &list = c->getDataItems();
          if (list)
          {
            for (auto const &s : *list)
            {
              auto di = dynamic_cast<data_item::DataItem*>(s.get());
              ary.push(di);
            }
          }
          return ary;
        });

      m_propertyKey.define_constructor(Constructor<PropertyKey, const string>(), Arg("key")).
        define_method("to_s", [](PropertyKey *key) { return key->str(); }).
        define_method("ns?", [](PropertyKey *key) { return key->hasNs(); });

    }

    Data_Type<Properties> m_properties;
    Data_Type<PropertyKey> m_propertyKey;
    Data_Type<Entity> m_entity;
    Data_Type<Component> m_component;
    Data_Type<Device> m_device;
    Data_Type<DataItem> m_dataItem;
  };
}

namespace Rice::detail {
  template<>
  struct Type<Properties>
  {
    static bool verify()
    {
      return true;
    }
  };

  template<>
  struct From_Ruby<Properties>
  {
    From_Ruby() = default;

    explicit From_Ruby(Arg * arg) : m_arg(arg)
    {
    }

    auto &convert(VALUE value)
    {
      if (TYPE(value) == RUBY_T_HASH)
      {
        Rice::Hash hash(value);
        for (const auto &p : hash)
        {
          PropertyKey key;
          auto pt = TYPE(p.first.value());
          if (pt == RUBY_T_SYMBOL)
            key = Symbol(p.first.value()).str();
          else if (pt == RUBY_T_STRING)
            key = From_Ruby<string>().convert(p.first);
          else
            key = From_Ruby<string>().convert(p.first.call(Identifier("to_s")));
          Value value { From_Ruby<Value>().convert(p.second.value()) };

          m_converted.emplace(key, value);
        }
      }
      return m_converted;
    }

    Arg* m_arg = nullptr;
    Properties m_converted;
  };

  template<>
  struct To_Ruby<Properties>
  {
    auto convert(const Properties &value)
    {
      return Qnil;
    }
  };

}
<commit_msg>Added property -> hash conversion<commit_after>//
// Copyright Copyright 2009-2021, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    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.
//

#pragma once

#include "entity/entity.hpp"
#include "device_model/device.hpp"
#include "device_model/data_item/data_item.hpp"
#include "ruby_type.hpp"

#include <rice/rice.hpp>
#include <rice/stl.hpp>
#include <ruby/thread.h>

namespace mtconnect::ruby {
  using namespace mtconnect;
  using namespace device_model;
  using namespace data_item;
  using namespace entity;
  using namespace std;
  using namespace Rice;

  struct RubyEntity {
    void create(Rice::Module &module)
    {
      m_properties = define_class_under<Properties>(module, "Properties");
      m_propertyKey = define_class_under<PropertyKey>(module, "PropertyKey");
      m_entity = define_class_under<Entity>(module, "Entity");
      c_Entity = m_entity.value();
      m_component = define_class_under<Component, Entity>(module, "Component");
      m_device = define_class_under<device_model::Device, device_model::Component>(module, "Device");
      m_dataItem = define_class_under<data_item::DataItem, entity::Entity>(module, "DataItem");
    }

    void methods()
    {
      m_entity.define_method("value", [](entity::EntityPtr entity) { return entity->getValue(); }).
        define_method("property", [](entity::EntityPtr entity, std::string name) {
          return entity->getProperty(name);
        }, Arg("name"));

      m_dataItem.define_method("name", [](data_item::DataItem *di) { return di->getName(); }).
        define_method("observation_name", [](data_item::DataItem *di) { return di->getObservationName().str(); }).
        define_method("id", [](data_item::DataItem *di) { return di->getId(); }).
        define_method("type", [](data_item::DataItem *di) { return di->getType(); }).
        define_method("sub_type", [](data_item::DataItem *di) { return di->getSubType(); });

      m_component.define_method("component_name", [](Component *c) { return c->getComponentName(); }).
        define_method("uuid", [](Component *c) { return c->getUuid(); }).
        define_method("id", [](Component *c) { return c->getId(); }).
        define_method("children", [](Component *c) {
          Rice::Array ary;
          const auto &list = c->getChildren();
          if (list)
          {
            for (auto const &s : *list)
            {
              auto cp = dynamic_cast<Component*>(s.get());
              ary.push(cp);
            }
          }
          return ary;
        }).
      define_method("data_items", [](Component *c) {
          Rice::Array ary;
          const auto &list = c->getDataItems();
          if (list)
          {
            for (auto const &s : *list)
            {
              auto di = dynamic_cast<data_item::DataItem*>(s.get());
              ary.push(di);
            }
          }
          return ary;
        });

      m_propertyKey.define_constructor(Constructor<PropertyKey, const string>(), Arg("key")).
        define_method("to_s", [](PropertyKey *key) { return key->str(); }).
        define_method("ns?", [](PropertyKey *key) { return key->hasNs(); });

    }

    Data_Type<Properties> m_properties;
    Data_Type<PropertyKey> m_propertyKey;
    Data_Type<Entity> m_entity;
    Data_Type<Component> m_component;
    Data_Type<Device> m_device;
    Data_Type<DataItem> m_dataItem;
  };
}

namespace Rice::detail {
  template<>
  struct Type<Properties>
  {
    static bool verify()
    {
      return true;
    }
  };

  template<>
  struct From_Ruby<Properties>
  {
    From_Ruby() = default;

    explicit From_Ruby(Arg * arg) : m_arg(arg)
    {
    }

    auto &convert(VALUE value)
    {
      if (TYPE(value) == RUBY_T_HASH)
      {
        Rice::Hash hash(value);
        for (const auto &p : hash)
        {
          PropertyKey key;
          auto pt = TYPE(p.first.value());
          if (pt == RUBY_T_SYMBOL)
            key = Symbol(p.first.value()).str();
          else if (pt == RUBY_T_STRING)
            key = From_Ruby<string>().convert(p.first);
          else
            key = From_Ruby<string>().convert(p.first.call(Identifier("to_s")));
          Value val { From_Ruby<Value>().convert(p.second.value()) };

          m_converted.emplace(key, val);
        }
      }
      else
      {
        Value val { From_Ruby<Value>().convert(value) };
        m_converted.emplace("VALUE", val);
      }
      return m_converted;
    }

    Arg* m_arg = nullptr;
    Properties m_converted;
  };

  template<>
  struct To_Ruby<Properties>
  {
    VALUE convert(const Properties &props)
    {
      Rice::Hash hash;
      for (const auto &[key, value] : props)
      {
        hash[key.str()] = To_Ruby<Value>().convert(value);
      }

      return hash;
    }
  };

}
<|endoftext|>


<commit_before>/*
 * =====================================================================================
 *
 *       Filename:  3.cpp
 *
 *    Description:  Solution to the third problem of the `evens' section.
 *
 *        Version:  1.0
 *        Created:  28.01.2015  6,00,07
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Radoslav Georgiev (sid), rgeorgiev583@gmail.com
 *        Company:  Faculty of Mathematics and Informatics at Sofia University
 *
 * =====================================================================================
 */

#include <iostream>
#include <cstring>

using namespace std;

const int MAX_SIZE = 1000;
const char diff = 'a' - 'A';

bool isWordChar(char c)
{
    return c == '(' || c == ')' || c == '-' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}

const char* skipWhitespaceReverse(const char* s)
{
    while (*s == ' ')
        s--;

    return s;
}

const char* goToEnd(const char* s)
{
    while (*s)
        s++;

    return s;
}

char toUpper(char c)
{
    return c >= 'a' && c <= 'z' ? c - diff : c;
}

char toLower(char c)
{
    return c >= 'A' && c <= 'Z' ? c + diff : c;
}

void swap(char* a, char* b)
{
    char x = *a;
    *a = *b;
    *b = x;
}

// Ако изречението е невалидно, връщаме NULL
char* reverseSentence(char* rev, const char* s)
{
    if (!s)
        return NULL;

    const char* end = goToEnd(s);
    end--;

    if (end < s || *end != '!' && *end != '.' && *end != '?')
        return NULL;

    end--;

    do
    {
        const char* beg = end;

        while (beg >= s && isWordChar(*beg))
            beg--;

        if (beg == end)
            return NULL;

        if (beg++ < s)
        {
            char first[2];
            strncat(first, beg, 1);
            *first = toLower(*first);
            strcat(rev, first);
            strncat(rev, beg + 1, end - beg);
            end = beg - 1;
        }
        else
        {
            strncat(rev, beg, end - beg + 1);
            end = beg - 1;

            if (*end != ' ')
                return NULL;

            end--;

            if (end < s)
                return NULL;

            if (*end == ',' || *end == ':' || *end == ';')
                strncat(rev, end, 1);

            strncat(rev, end + 1, 1);
            end--;
        }
    }
    while (end >= s);

    *rev = toUpper(*rev);
    char *p = rev, *lp = NULL;
    int b = 0;

    while (p)
    {
        if (*p == '(')
        {
            b++;

            if (lp && b == 0)
            {
                swap(p, lp);
                lp = NULL;
            }
        }
        else if (*p == ')')
        {
            b--;

            if (b == -1)
                lp = p;
        }

        p++;
    }

    return rev;
}

int main()
{
    char s[MAX_SIZE], rev[MAX_SIZE];
    cin.getline(s, MAX_SIZE);

    cout << reverseSentence(rev, s) << endl;

    return 0;
}
<commit_msg>fixed incorrect decrease of end pointer<commit_after>/*
 * =====================================================================================
 *
 *       Filename:  3.cpp
 *
 *    Description:  Solution to the third problem of the `evens' section.
 *
 *        Version:  1.0
 *        Created:  28.01.2015  6,00,07
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Radoslav Georgiev (sid), rgeorgiev583@gmail.com
 *        Company:  Faculty of Mathematics and Informatics at Sofia University
 *
 * =====================================================================================
 */

#include <iostream>
#include <cstring>

using namespace std;

const int MAX_SIZE = 1000;
const char diff = 'a' - 'A';

bool isWordChar(char c)
{
    return c == '(' || c == ')' || c == '-' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}

const char* skipWhitespaceReverse(const char* s)
{
    while (*s == ' ')
        s--;

    return s;
}

const char* goToEnd(const char* s)
{
    while (*s)
        s++;

    return s;
}

char toUpper(char c)
{
    return c >= 'a' && c <= 'z' ? c - diff : c;
}

char toLower(char c)
{
    return c >= 'A' && c <= 'Z' ? c + diff : c;
}

void swap(char* a, char* b)
{
    char x = *a;
    *a = *b;
    *b = x;
}

// Ако изречението е невалидно, връщаме NULL
char* reverseSentence(char* rev, const char* s)
{
    if (!s)
        return NULL;

    const char* end = goToEnd(s);
    end--;

    if (end < s || *end != '!' && *end != '.' && *end != '?')
        return NULL;

    end--;

    do
    {
        const char* beg = end;

        while (beg >= s && isWordChar(*beg))
            beg--;

        if (beg == end)
            return NULL;

        if (beg++ < s)
        {
            char first[2];
            strncat(first, beg, 1);
            *first = toLower(*first);
            strcat(rev, first);
            strncat(rev, beg + 1, end - beg);
            end = beg - 1;
        }
        else
        {
            strncat(rev, beg, end - beg + 1);
            end = beg - 1;

            if (*end != ' ')
                return NULL;

            end--;

            if (end < s)
                return NULL;

            if (*end == ',' || *end == ':' || *end == ';')
                strncat(rev, end, 1);

            strncat(rev, end + 1, 1);
        }
    }
    while (end >= s);

    *rev = toUpper(*rev);
    char *p = rev, *lp = NULL;
    int b = 0;

    while (p)
    {
        if (*p == '(')
        {
            b++;

            if (lp && b == 0)
            {
                swap(p, lp);
                lp = NULL;
            }
        }
        else if (*p == ')')
        {
            b--;

            if (b == -1)
                lp = p;
        }

        p++;
    }

    return rev;
}

int main()
{
    char s[MAX_SIZE], rev[MAX_SIZE];
    cin.getline(s, MAX_SIZE);

    cout << reverseSentence(rev, s) << endl;

    return 0;
}
<|endoftext|>


<commit_before>#include <math.h>

#include "str.hh"
#include "MllrTrainer.hh"

MllrTrainer::MllrTrainer(HmmSet &model, FeatureGenerator &feagen) :
  m_model(model),
  m_feagen(feagen)
{
  m_dim = m_model.dim();

  // create vector and matrix arrays
  k_array.resize(m_dim);
  G_array.resize(m_dim);
  for (int i = 0; i < m_dim; i++)
  {
    k_array[i] = new Vector(m_dim+1);
    G_array[i] = new Matrix(m_dim+1, m_dim+1);
  }

  // set statistics to zero
  clear_stats();
}

MllrTrainer::~MllrTrainer()
{
  // Free allocated memory
  for (int i = 0; i < m_dim; i++)
  {
    delete k_array[i];
    delete G_array[i];
  }
}

void MllrTrainer::find_probs(double prior, HmmState *state,
                             const FeatureVec &feature)
{
  // get the mixture
  Mixture *mixture = m_model.get_emission_pdf(state->emission_pdf);
  int gaussiancount = mixture->size();

  double probs[gaussiancount];
  double prob_sum = 0;

  // check dim
  if (m_dim != m_feagen.dim())
    throw std::string("MllrTrainer: Model and feature dimensions disagree");

  // get probabilities
  for (int g = 0; g < gaussiancount; g++)
  {
    Gaussian *gaussian = dynamic_cast< Gaussian* > (mixture->get_base_pdf(g));
    if (gaussian == NULL)
      throw std::string("Warning: MLLR not supported for non-Gaussian models");

    probs[g] = prior*gaussian->compute_likelihood(*feature.get_vector());
    prob_sum += probs[g];
  }

  if (prob_sum != 0)
    for (int g = 0; g < gaussiancount; g++)
      probs[g] = probs[g]/prob_sum;
  else
    m_zero_prob_count++;

  update_stats(state, probs, feature);
}

void MllrTrainer::calculate_transform(LinTransformModule *mllr_mod)
{
  int i, j; // rows, columns
  Matrix A(m_dim, m_dim);
  double detA = 1;
  LaVectorLongInt pivots(m_dim);
  LaVectorLongInt pivots2(m_dim+1);
  Matrix G(m_dim+1, m_dim+1);
  Matrix G_inv(m_dim+1, m_dim+1);
  Vector p(m_dim+1);
  Vector w(m_dim+1);
  Matrix trans(m_dim, m_dim+1);
  double alpha;

  // check that we have probabilities
  if (m_beta == 0)
    throw std::string("ERROR: No probabilities. See if .seg-file empty.");

  if (m_zero_prob_count > 0)
    printf("Warning: There were %i zero probability sample(s)\n",
           m_zero_prob_count);

  // Initialize the transform with identity matrix
  trans=0;
  for (i = 0; i < m_dim; i++)
    trans(i,i+1) = 1;

  // calculate inverse of G
  for (i = 0; i < m_dim; i++)
  {
    G.copy(*(G_array[i]));
    LUFactorizeIP(G, pivots2);
    LaLUInverseIP(G, pivots2);
    (*(G_array[i])).copy(G);
  }

  // calculate transformation
  int row = 0;
  for (int round = 0; round < 20 * m_dim; round++)
  {
    if (row == m_dim)
      row = 0; // next iteration

    // matrix A
    detA = 1;
    for (i = 0; i < m_dim; i++)
    {
      for (j = 0; j < m_dim; j++)
	A(j, i) = trans(i, j+1);  // Transposed
    }

    // calculate cofactors
    LUFactorizeIP(A, pivots);

    for (i = 0; i < m_dim; i++)
      detA *= A(i, i);

    LaLUInverseIP(A, pivots);
    Blas_Scale(detA, A);

    // cofactor vector
    p(0) = 0;
    for (j = 0; j < m_dim; j++)
      p(j+1) = A(row,j);

    // alpha parameter
    alpha = calculate_alpha(*(G_array[row]), p, *(k_array[row]), m_beta);

    // calculate ith row of transformation matrix
    Blas_Scale(alpha, p);
    Blas_Add_Mult(p, 1.0, *(k_array[row]));
    Blas_Mat_Trans_Vec_Mult(*(G_array[row]), p, w);

    for(j = 0; j < m_dim+1; j++)
      trans(row, j) = w(j);

    row++; // next row
  }

  // Accumulate the new matrix with the previous one
  Matrix A1(m_dim, m_dim);
  Vector b1(m_dim);
  Matrix A2(m_dim, m_dim);
  Vector b2(m_dim);
  const std::vector<float> *bias = mllr_mod->get_transformation_bias();
  const std::vector<float> *matrix = mllr_mod->get_transformation_matrix();

  for (i = 0; i < m_dim; i++)
  {
    b1(i) = (*bias)[i];
    b2(i) = trans(i, 0);
    for (j = 0; j < m_dim; j++)
    {
      A1(i, j) = (*matrix)[i*m_dim + j];
      A2(i, j) = trans(i, j + 1);
    }
  }

  // calculate A x + b =  A2 (A1 x + b1) + b2
  Vector b(m_dim);
  b=0;
  A=0;
  Blas_Mat_Mat_Mult(A2, A1, A, 1.0, 0.0);
  Blas_Mat_Vec_Mult(A2, b1, b);
  Blas_Add_Mult(b, 1.0, b2);

  // Set the transformation
  std::vector<float> new_bias;
  std::vector<float> new_matrix;
  new_bias.resize(m_dim, 0);
  new_matrix.resize(m_dim*m_dim, 0);
  for (i = 0; i < m_dim; i++)
  {
    new_bias[i] = b(i);
    for (j = 0; j < m_dim; j++)
    {
      new_matrix[i*m_dim+j] = A(i, j);
    }
  }

  mllr_mod->set_transformation_matrix(new_matrix);
  mllr_mod->set_transformation_bias(new_bias);

  clear_stats();
}

void MllrTrainer::restore_identity(LinTransformModule *mllr_mod)
{
  std::vector<float> new_bias;
  std::vector<float> new_matrix;
  int dim = mllr_mod->dim();
  new_bias.resize(dim, 0);
  new_matrix.resize(dim * dim, 0);
  for (int i = 0; i < dim; i++)
  {
    new_bias[i] = 0;
    new_matrix[i*dim+i] = 1;
  }
  mllr_mod->set_transformation_matrix(new_matrix);
  mllr_mod->set_transformation_bias(new_bias);
}

void MllrTrainer::clear_stats()
{
  for(int i = 0; i < m_dim; i++)
  {
    *(k_array[i]) = 0;
    *(G_array[i]) = 0;
  }
  m_beta = 0;
  m_zero_prob_count = 0;
}

void MllrTrainer::update_stats(HmmState *state, double *probs,
                              const FeatureVec &feature)
{
  int i; // rows, columns
  int gaussiancount;

  Matrix matrix(m_dim+1, m_dim+1);

  // extended feature vector
  Vector fea_vector(m_dim+1); fea_vector(0) = 1;
  for (i = 1; i < m_dim+1; i++) fea_vector(i) = feature[i-1];

  // k array
  Mixture *mixture = m_model.get_emission_pdf(state->emission_pdf);
  gaussiancount = mixture->size();

  for (int g = 0; g < gaussiancount; g++)
  {
    Gaussian *gaussian = dynamic_cast< Gaussian* > (mixture->get_base_pdf(g));
    if (gaussian == NULL)
      throw std::string("Warning: MLLR not supported for non-Gaussian models");

    // get gaussian statistics
    Vector g_mean;
    Vector g_var;
    gaussian->get_mean(g_mean);
    gaussian->get_covariance(g_var);

    // update k(i)
    for (int i = 0; i < m_dim; i++)
      Blas_Add_Mult(*(k_array[i]), g_mean(i)/g_var(i)*probs[g], fea_vector);
  }

  // G array
  matrix=0;
  Blas_R1_Update(matrix, fea_vector, fea_vector);

  for (int g = 0; g < gaussiancount; g++)
  {
    Gaussian *gaussian = dynamic_cast< Gaussian* > (mixture->get_base_pdf(g));
    if (gaussian == NULL)
      fprintf(stderr, "Warning: MLLR not supported for non-Gaussian models");

    // get gaussian statistics
    Vector g_var;
    gaussian->get_covariance(g_var);

    for (int i = 0; i < m_dim; i++)
      Blas_Add_Mat_Mult(*(G_array[i]), 1/g_var(i) * probs[g], matrix);
  }

  for (int g = 0; g < gaussiancount; g++)
    m_beta += probs[g];
}

double MllrTrainer::calculate_alpha(Matrix &Gi, Vector &p,
                                    Vector &k, double beta)
{

  double c2 = quadratic(p, Gi, p);
  double c1 = quadratic(p, Gi, k);

  // solve quadratic equation c2 x^2 + c1 x - beta

  double a1 = (-c1 + sqrt(c1*c1 + 4*c2*beta))/(2*c2);
  double a2 = (-c1 - sqrt(c1*c1 + 4*c2*beta))/(2*c2);

  double m1 = beta*log(fabs(a1*c2 + c1))-(c2/2)*a1*a1;
  double m2 = beta*log(fabs(a2*c2 + c1))-(c2/2)*a2*a2;

  // select the maximizing value

  if (m1 > m2)
    return a1;

  return a2;

}

double MllrTrainer::quadratic(Vector &x, Matrix &A, Vector &y)
{
  double sum = 0;
  LaVectorDouble t(x.size());
  Blas_Mat_Vec_Mult(A, y, t);
  sum = Blas_Dot_Prod(x, t);
  return sum;
}
<commit_msg>fixed a prior bug in find_probs<commit_after>#include <math.h>

#include "str.hh"
#include "MllrTrainer.hh"

MllrTrainer::MllrTrainer(HmmSet &model, FeatureGenerator &feagen) :
  m_model(model),
  m_feagen(feagen)
{
  m_dim = m_model.dim();

  // create vector and matrix arrays
  k_array.resize(m_dim);
  G_array.resize(m_dim);
  for (int i = 0; i < m_dim; i++)
  {
    k_array[i] = new Vector(m_dim+1);
    G_array[i] = new Matrix(m_dim+1, m_dim+1);
  }

  // set statistics to zero
  clear_stats();
}

MllrTrainer::~MllrTrainer()
{
  // Free allocated memory
  for (int i = 0; i < m_dim; i++)
  {
    delete k_array[i];
    delete G_array[i];
  }
}

void MllrTrainer::find_probs(double prior, HmmState *state,
                             const FeatureVec &feature)
{
  // get the mixture
  Mixture *mixture = m_model.get_emission_pdf(state->emission_pdf);
  int gaussiancount = mixture->size();

  double probs[gaussiancount];
  double prob_sum = 0;

  // check dim
  if (m_dim != m_feagen.dim())
    throw std::string("MllrTrainer: Model and feature dimensions disagree");

  // get probabilities
  for (int g = 0; g < gaussiancount; g++)
  {
    Gaussian *gaussian = dynamic_cast< Gaussian* > (mixture->get_base_pdf(g));
    if (gaussian == NULL)
      throw std::string("Warning: MLLR not supported for non-Gaussian models");

    probs[g] = gaussian->compute_likelihood(*feature.get_vector());
    prob_sum += probs[g];
  }

  if (prob_sum != 0)
    for (int g = 0; g < gaussiancount; g++)
      probs[g] = prior*probs[g]/prob_sum;
  else
    m_zero_prob_count++;

  update_stats(state, probs, feature);
}

void MllrTrainer::calculate_transform(LinTransformModule *mllr_mod)
{
  int i, j; // rows, columns
  Matrix A(m_dim, m_dim);
  double detA = 1;
  LaVectorLongInt pivots(m_dim);
  LaVectorLongInt pivots2(m_dim+1);
  Matrix G(m_dim+1, m_dim+1);
  Matrix G_inv(m_dim+1, m_dim+1);
  Vector p(m_dim+1);
  Vector w(m_dim+1);
  Matrix trans(m_dim, m_dim+1);
  double alpha;

  // check that we have probabilities
  if (m_beta == 0)
    throw std::string("ERROR: No probabilities. See if .seg-file empty.");

  if (m_zero_prob_count > 0)
    printf("Warning: There were %i zero probability sample(s)\n",
           m_zero_prob_count);

  // Initialize the transform with identity matrix
  trans=0;
  for (i = 0; i < m_dim; i++)
    trans(i,i+1) = 1;

  // calculate inverse of G
  for (i = 0; i < m_dim; i++)
  {
    G.copy(*(G_array[i]));
    LUFactorizeIP(G, pivots2);
    LaLUInverseIP(G, pivots2);
    (*(G_array[i])).copy(G);
  }

  // calculate transformation
  int row = 0;
  for (int round = 0; round < 20 * m_dim; round++)
  {
    if (row == m_dim)
      row = 0; // next iteration

    // matrix A
    detA = 1;
    for (i = 0; i < m_dim; i++)
    {
      for (j = 0; j < m_dim; j++)
	A(j, i) = trans(i, j+1);  // Transposed
    }

    // calculate cofactors
    LUFactorizeIP(A, pivots);

    for (i = 0; i < m_dim; i++)
      detA *= A(i, i);

    LaLUInverseIP(A, pivots);
    Blas_Scale(detA, A);

    // cofactor vector
    p(0) = 0;
    for (j = 0; j < m_dim; j++)
      p(j+1) = A(row,j);

    // alpha parameter
    alpha = calculate_alpha(*(G_array[row]), p, *(k_array[row]), m_beta);

    // calculate ith row of transformation matrix
    Blas_Scale(alpha, p);
    Blas_Add_Mult(p, 1.0, *(k_array[row]));
    Blas_Mat_Trans_Vec_Mult(*(G_array[row]), p, w);

    for(j = 0; j < m_dim+1; j++)
      trans(row, j) = w(j);

    row++; // next row
  }

  // Accumulate the new matrix with the previous one
  Matrix A1(m_dim, m_dim);
  Vector b1(m_dim);
  Matrix A2(m_dim, m_dim);
  Vector b2(m_dim);
  const std::vector<float> *bias = mllr_mod->get_transformation_bias();
  const std::vector<float> *matrix = mllr_mod->get_transformation_matrix();

  for (i = 0; i < m_dim; i++)
  {
    b1(i) = (*bias)[i];
    b2(i) = trans(i, 0);
    for (j = 0; j < m_dim; j++)
    {
      A1(i, j) = (*matrix)[i*m_dim + j];
      A2(i, j) = trans(i, j + 1);
    }
  }

  // calculate A x + b =  A2 (A1 x + b1) + b2
  Vector b(m_dim);
  b=0;
  A=0;
  Blas_Mat_Mat_Mult(A2, A1, A, 1.0, 0.0);
  Blas_Mat_Vec_Mult(A2, b1, b);
  Blas_Add_Mult(b, 1.0, b2);

  // Set the transformation
  std::vector<float> new_bias;
  std::vector<float> new_matrix;
  new_bias.resize(m_dim, 0);
  new_matrix.resize(m_dim*m_dim, 0);
  for (i = 0; i < m_dim; i++)
  {
    new_bias[i] = b(i);
    for (j = 0; j < m_dim; j++)
    {
      new_matrix[i*m_dim+j] = A(i, j);
    }
  }

  mllr_mod->set_transformation_matrix(new_matrix);
  mllr_mod->set_transformation_bias(new_bias);

  clear_stats();
}

void MllrTrainer::restore_identity(LinTransformModule *mllr_mod)
{
  std::vector<float> new_bias;
  std::vector<float> new_matrix;
  int dim = mllr_mod->dim();
  new_bias.resize(dim, 0);
  new_matrix.resize(dim * dim, 0);
  for (int i = 0; i < dim; i++)
  {
    new_bias[i] = 0;
    new_matrix[i*dim+i] = 1;
  }
  mllr_mod->set_transformation_matrix(new_matrix);
  mllr_mod->set_transformation_bias(new_bias);
}

void MllrTrainer::clear_stats()
{
  for(int i = 0; i < m_dim; i++)
  {
    *(k_array[i]) = 0;
    *(G_array[i]) = 0;
  }
  m_beta = 0;
  m_zero_prob_count = 0;
}

void MllrTrainer::update_stats(HmmState *state, double *probs,
                              const FeatureVec &feature)
{
  int i; // rows, columns
  int gaussiancount;

  Matrix matrix(m_dim+1, m_dim+1);

  // extended feature vector
  Vector fea_vector(m_dim+1); fea_vector(0) = 1;
  for (i = 1; i < m_dim+1; i++) fea_vector(i) = feature[i-1];

  // k array
  Mixture *mixture = m_model.get_emission_pdf(state->emission_pdf);
  gaussiancount = mixture->size();

  for (int g = 0; g < gaussiancount; g++)
  {
    Gaussian *gaussian = dynamic_cast< Gaussian* > (mixture->get_base_pdf(g));
    if (gaussian == NULL)
      throw std::string("Warning: MLLR not supported for non-Gaussian models");

    // get gaussian statistics
    Vector g_mean;
    Vector g_var;
    gaussian->get_mean(g_mean);
    gaussian->get_covariance(g_var);

    // update k(i)
    for (int i = 0; i < m_dim; i++)
      Blas_Add_Mult(*(k_array[i]), g_mean(i)/g_var(i)*probs[g], fea_vector);
  }

  // G array
  matrix=0;
  Blas_R1_Update(matrix, fea_vector, fea_vector);

  for (int g = 0; g < gaussiancount; g++)
  {
    Gaussian *gaussian = dynamic_cast< Gaussian* > (mixture->get_base_pdf(g));
    if (gaussian == NULL)
      fprintf(stderr, "Warning: MLLR not supported for non-Gaussian models");

    // get gaussian statistics
    Vector g_var;
    gaussian->get_covariance(g_var);

    for (int i = 0; i < m_dim; i++)
      Blas_Add_Mat_Mult(*(G_array[i]), 1/g_var(i) * probs[g], matrix);
  }

  for (int g = 0; g < gaussiancount; g++)
    m_beta += probs[g];
}

double MllrTrainer::calculate_alpha(Matrix &Gi, Vector &p,
                                    Vector &k, double beta)
{

  double c2 = quadratic(p, Gi, p);
  double c1 = quadratic(p, Gi, k);

  // solve quadratic equation c2 x^2 + c1 x - beta

  double a1 = (-c1 + sqrt(c1*c1 + 4*c2*beta))/(2*c2);
  double a2 = (-c1 - sqrt(c1*c1 + 4*c2*beta))/(2*c2);

  double m1 = beta*log(fabs(a1*c2 + c1))-(c2/2)*a1*a1;
  double m2 = beta*log(fabs(a2*c2 + c1))-(c2/2)*a2*a2;

  // select the maximizing value

  if (m1 > m2)
    return a1;

  return a2;

}

double MllrTrainer::quadratic(Vector &x, Matrix &A, Vector &y)
{
  double sum = 0;
  LaVectorDouble t(x.size());
  Blas_Mat_Vec_Mult(A, y, t);
  sum = Blas_Dot_Prod(x, t);
  return sum;
}
<|endoftext|>


<commit_before>#include <cryptopp/dh.h>
#include "I2PEndian.h"
#include "CryptoConst.h"
#include "Tunnel.h"
#include "NetDb.h"
#include "Timestamp.h"
#include "RouterContext.h"
#include "Garlic.h"
#include "TunnelPool.h"

namespace i2p
{
namespace tunnel
{
	TunnelPool::TunnelPool (i2p::data::LocalDestination * localDestination, int numTunnels):
		m_LocalDestination (localDestination), m_NumTunnels (numTunnels), m_LastOutboundTunnel (nullptr)
	{
		CryptoPP::AutoSeededRandomPool rnd;
		CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
		dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
	}

	TunnelPool::~TunnelPool ()
	{
		for (auto it: m_InboundTunnels)
			it->SetTunnelPool (nullptr);
		for (auto it: m_OutboundTunnels)
			it->SetTunnelPool (nullptr);
	}

	void TunnelPool::TunnelCreated (InboundTunnel * createdTunnel)
	{
		m_InboundTunnels.insert (createdTunnel);
		if (m_LocalDestination)
			m_LocalDestination->UpdateLeaseSet ();
	}

	void TunnelPool::TunnelExpired (InboundTunnel * expiredTunnel)
	{
		m_InboundTunnels.erase (expiredTunnel);
		if (m_LocalDestination)
			m_LocalDestination->UpdateLeaseSet ();
	}

	void TunnelPool::TunnelCreated (OutboundTunnel * createdTunnel)
	{
		m_OutboundTunnels.insert (createdTunnel);
	}

	void TunnelPool::TunnelExpired (OutboundTunnel * expiredTunnel)
	{
		m_OutboundTunnels.erase (expiredTunnel);
		if (expiredTunnel == m_LastOutboundTunnel)
			m_LastOutboundTunnel = nullptr;
	}

	std::vector<InboundTunnel *> TunnelPool::GetInboundTunnels (int num) const
	{
		std::vector<InboundTunnel *> v;
		int i = 0;
		for (auto it : m_InboundTunnels)
		{
			if (i >= num) break;
			v.push_back (it);
			i++;
		}
		return v;
	}

	OutboundTunnel * TunnelPool::GetNextOutboundTunnel ()
	{
		if (m_OutboundTunnels.empty ()) return nullptr;
		auto tunnel = *m_OutboundTunnels.begin ();
		if (m_LastOutboundTunnel && tunnel == m_LastOutboundTunnel)
		{
			for (auto it: m_OutboundTunnels)
				if (it != m_LastOutboundTunnel)
				{
					tunnel = it;
					break;
				}
		}
		m_LastOutboundTunnel = tunnel;
		return tunnel;
	}

	void TunnelPool::CreateTunnels ()
	{
		int num = m_InboundTunnels.size ();
		for (int i = num; i < m_NumTunnels; i++)
			CreateInboundTunnel ();
		num = m_OutboundTunnels.size ();
		for (int i = num; i < m_NumTunnels; i++)
			CreateOutboundTunnel ();
	}

	void TunnelPool::TestTunnels ()
	{
		auto& rnd = i2p::context.GetRandomNumberGenerator ();
		for (auto it: m_Tests)
		{
			LogPrint ("Tunnel test ", (int)it.first, " failed");
			// both outbound and inbound tunnels considered as invalid
			TunnelExpired (it.second.first);
			TunnelExpired (it.second.second);
		}
		m_Tests.clear ();
		auto it1 = m_OutboundTunnels.begin ();
		auto it2 = m_InboundTunnels.begin ();
		while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
		{
			uint32_t msgID = rnd.GenerateWord32 ();
			m_Tests[msgID] = std::make_pair (*it1, *it2);
			(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
				CreateDeliveryStatusMsg (msgID));
			it1++; it2++;
		}
	}

	void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg)
	{
		I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
		auto it = m_Tests.find (be32toh (deliveryStatus->msgID));
		if (it != m_Tests.end ())
		{
			LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - be64toh (deliveryStatus->timestamp), " milliseconds");
			m_Tests.erase (it);
		}
		else
			i2p::garlic::routing.HandleDeliveryStatusMessage (msg->GetPayload (), msg->GetLength ()); // TODO:
		DeleteI2NPMessage (msg);
	}

	void TunnelPool::CreateInboundTunnel ()
	{
		OutboundTunnel * outboundTunnel = m_OutboundTunnels.size () > 0 ?
			*m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel ();
		LogPrint ("Creating destination inbound tunnel...");
		auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel ? outboundTunnel->GetEndpointRouter () : nullptr);
		auto secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
		auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (
			new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
				{
			        firstHop,
					secondHop
					// TODO: switch to 3-hops later
					/*i2p::data::netdb.GetRandomRouter (secondHop) */
	            }),
			outboundTunnel);
		tunnel->SetTunnelPool (this);
	}

	void TunnelPool::CreateOutboundTunnel ()
	{
		InboundTunnel * inboundTunnel = m_InboundTunnels.size () > 0 ?
			*m_InboundTunnels.begin () : tunnels.GetNextInboundTunnel ();
		if (inboundTunnel)
		{
			LogPrint ("Creating destination outbound tunnel...");
			auto firstHop = i2p::data::netdb.GetRandomRouter (&i2p::context.GetRouterInfo ());
			auto secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
			auto * tunnel = tunnels.CreateTunnel<OutboundTunnel> (
				new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
					{
					    firstHop,
						secondHop
			        },
					inboundTunnel->GetTunnelConfig ()));
			tunnel->SetTunnelPool (this);
		}
	}
}
}
<commit_msg>exclude failed tunnel from pool<commit_after>#include <cryptopp/dh.h>
#include "I2PEndian.h"
#include "CryptoConst.h"
#include "Tunnel.h"
#include "NetDb.h"
#include "Timestamp.h"
#include "RouterContext.h"
#include "Garlic.h"
#include "TunnelPool.h"

namespace i2p
{
namespace tunnel
{
	TunnelPool::TunnelPool (i2p::data::LocalDestination * localDestination, int numTunnels):
		m_LocalDestination (localDestination), m_NumTunnels (numTunnels), m_LastOutboundTunnel (nullptr)
	{
		CryptoPP::AutoSeededRandomPool rnd;
		CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
		dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
	}

	TunnelPool::~TunnelPool ()
	{
		for (auto it: m_InboundTunnels)
			it->SetTunnelPool (nullptr);
		for (auto it: m_OutboundTunnels)
			it->SetTunnelPool (nullptr);
	}

	void TunnelPool::TunnelCreated (InboundTunnel * createdTunnel)
	{
		m_InboundTunnels.insert (createdTunnel);
		if (m_LocalDestination)
			m_LocalDestination->UpdateLeaseSet ();
	}

	void TunnelPool::TunnelExpired (InboundTunnel * expiredTunnel)
	{
		if (expiredTunnel)
		{
			expiredTunnel->SetTunnelPool (nullptr);
			m_InboundTunnels.erase (expiredTunnel);
		}
		if (m_LocalDestination)
			m_LocalDestination->UpdateLeaseSet ();
	}

	void TunnelPool::TunnelCreated (OutboundTunnel * createdTunnel)
	{
		m_OutboundTunnels.insert (createdTunnel);
	}

	void TunnelPool::TunnelExpired (OutboundTunnel * expiredTunnel)
	{
		if (expiredTunnel)
		{
			expiredTunnel->SetTunnelPool (nullptr);
			m_OutboundTunnels.erase (expiredTunnel);
		}
		if (expiredTunnel == m_LastOutboundTunnel)
			m_LastOutboundTunnel = nullptr;
	}

	std::vector<InboundTunnel *> TunnelPool::GetInboundTunnels (int num) const
	{
		std::vector<InboundTunnel *> v;
		int i = 0;
		for (auto it : m_InboundTunnels)
		{
			if (i >= num) break;
			v.push_back (it);
			i++;
		}
		return v;
	}

	OutboundTunnel * TunnelPool::GetNextOutboundTunnel ()
	{
		if (m_OutboundTunnels.empty ()) return nullptr;
		auto tunnel = *m_OutboundTunnels.begin ();
		if (m_LastOutboundTunnel && tunnel == m_LastOutboundTunnel)
		{
			for (auto it: m_OutboundTunnels)
				if (it != m_LastOutboundTunnel)
				{
					tunnel = it;
					break;
				}
		}
		m_LastOutboundTunnel = tunnel;
		return tunnel;
	}

	void TunnelPool::CreateTunnels ()
	{
		int num = m_InboundTunnels.size ();
		for (int i = num; i < m_NumTunnels; i++)
			CreateInboundTunnel ();
		num = m_OutboundTunnels.size ();
		for (int i = num; i < m_NumTunnels; i++)
			CreateOutboundTunnel ();
	}

	void TunnelPool::TestTunnels ()
	{
		auto& rnd = i2p::context.GetRandomNumberGenerator ();
		for (auto it: m_Tests)
		{
			LogPrint ("Tunnel test ", (int)it.first, " failed");
			// both outbound and inbound tunnels considered as invalid
			TunnelExpired (it.second.first);
			TunnelExpired (it.second.second);
		}
		m_Tests.clear ();
		auto it1 = m_OutboundTunnels.begin ();
		auto it2 = m_InboundTunnels.begin ();
		while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
		{
			uint32_t msgID = rnd.GenerateWord32 ();
			m_Tests[msgID] = std::make_pair (*it1, *it2);
			(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
				CreateDeliveryStatusMsg (msgID));
			it1++; it2++;
		}
	}

	void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg)
	{
		I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
		auto it = m_Tests.find (be32toh (deliveryStatus->msgID));
		if (it != m_Tests.end ())
		{
			LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - be64toh (deliveryStatus->timestamp), " milliseconds");
			m_Tests.erase (it);
		}
		else
			i2p::garlic::routing.HandleDeliveryStatusMessage (msg->GetPayload (), msg->GetLength ()); // TODO:
		DeleteI2NPMessage (msg);
	}

	void TunnelPool::CreateInboundTunnel ()
	{
		OutboundTunnel * outboundTunnel = m_OutboundTunnels.size () > 0 ?
			*m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel ();
		LogPrint ("Creating destination inbound tunnel...");
		auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel ? outboundTunnel->GetEndpointRouter () : nullptr);
		auto secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
		auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (
			new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
				{
			        firstHop,
					secondHop
					// TODO: switch to 3-hops later
					/*i2p::data::netdb.GetRandomRouter (secondHop) */
	            }),
			outboundTunnel);
		tunnel->SetTunnelPool (this);
	}

	void TunnelPool::CreateOutboundTunnel ()
	{
		InboundTunnel * inboundTunnel = m_InboundTunnels.size () > 0 ?
			*m_InboundTunnels.begin () : tunnels.GetNextInboundTunnel ();
		if (inboundTunnel)
		{
			LogPrint ("Creating destination outbound tunnel...");
			auto firstHop = i2p::data::netdb.GetRandomRouter (&i2p::context.GetRouterInfo ());
			auto secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
			auto * tunnel = tunnels.CreateTunnel<OutboundTunnel> (
				new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
					{
					    firstHop,
						secondHop
			        },
					inboundTunnel->GetTunnelConfig ()));
			tunnel->SetTunnelPool (this);
		}
	}
}
}
<|endoftext|>


<commit_before>/*=========================================================================

Program:   Visualization Toolkit
Module:    TestSQLDatabaseSchema.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*----------------------------------------------------------------------------
  Copyright (c) Sandia Corporation
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
  ----------------------------------------------------------------------------*/
// .SECTION Thanks
// Thanks to Philippe Pebay and David Thompson from Sandia National Laboratories
// for implementing this test.

#include "vtkSQLDatabaseSchema.h"

#include "vtkStdString.h"

#include <vtkstd/set>

int TestSQLDatabaseSchema( int /*argc*/, char* /*argv*/[] )
{
  bool status = true;
  int tblHandle = 0;
  vtkSQLDatabaseSchema* schema = vtkSQLDatabaseSchema::New();

  schema->SetName( "TestSchema" );
  tblHandle = schema->AddTableMultipleArguments( "StrangeTable",
    vtkSQLDatabaseSchema::COLUMN_TOKEN, vtkSQLDatabaseSchema::SERIAL,  "TableKey",  0, "",
    vtkSQLDatabaseSchema::COLUMN_TOKEN, vtkSQLDatabaseSchema::VARCHAR, "SomeName", 11, "NOT NULL",
    vtkSQLDatabaseSchema::COLUMN_TOKEN, vtkSQLDatabaseSchema::BIGINT,  "SomeNmbr", 17, "DEFAULT 0",
    vtkSQLDatabaseSchema::INDEX_TOKEN,  vtkSQLDatabaseSchema::PRIMARY_KEY, "BigKey",
    vtkSQLDatabaseSchema::INDEX_COLUMN_TOKEN, "TableKey",
    vtkSQLDatabaseSchema::END_INDEX_TOKEN,
    vtkSQLDatabaseSchema::INDEX_TOKEN,  vtkSQLDatabaseSchema::UNIQUE, "ReverseLookup",
    vtkSQLDatabaseSchema::INDEX_COLUMN_TOKEN, "SomeName",
    vtkSQLDatabaseSchema::INDEX_COLUMN_TOKEN, "SomeNmbr",
    vtkSQLDatabaseSchema::END_INDEX_TOKEN,
    vtkSQLDatabaseSchema::TRIGGER_TOKEN,  vtkSQLDatabaseSchema::AFTER_INSERT,
      "InsertTrigger", "INSERT INTO OtherTable ( Value ) VALUES NEW.SomeNmbr",
    vtkSQLDatabaseSchema::END_TABLE_TOKEN
  );

  if ( tblHandle < 0 )
    {
    cerr << "Could not create test schema.\n";
    status = false;
    }

  // Define the correct (reference) columns and types
  vtkstd::set<vtkStdString> colNames;
  colNames.insert( vtkStdString ( "SomeNmbr" ) );
  colNames.insert( vtkStdString ( "SomeName" ) );
  colNames.insert( vtkStdString ( "TableKey" ) );
  vtkstd::set<int> colTypes;
  colTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::BIGINT ) );
  colTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::SERIAL ) );
  colTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::VARCHAR ) );

  // Loop over all columns of the previously created table
  int numCol = schema->GetNumberOfColumnsInTable( tblHandle );
  if ( numCol != 3 )
    {
    cerr << "Read " << numCol << " != 3 columns in test schema.\n";
    status = false;
    }

  for ( int colHandle = 0; colHandle < numCol; ++ colHandle )
    {
    vtkStdString colName = schema->GetColumnNameFromHandle( tblHandle, colHandle );
    cerr << "Column name: "
         << colName
         << "\n";

    vtkstd::set<vtkStdString>::iterator sit = colNames.find( colName );
    if ( sit != colNames.end() )
      {
      colNames.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve column name " << colName  << " from test schema.\n";
      status = false;
      }

    int colType = schema->GetColumnTypeFromHandle( tblHandle, colHandle );
    cerr << "Column type: "
         << colType
         << "\n";

    vtkstd::set<int>::iterator iit = colTypes.find( colType );
    if ( iit != colTypes.end() )
      {
      colTypes.erase ( iit );
      }
    else
      {
      cerr << "Could not retrieve column type " << colType  << " from test schema.\n";
      status = false;
      }
    }

  // Define the correct (reference) indices and types
  vtkstd::set<vtkStdString> idxNames;
  idxNames.insert( vtkStdString ( "BigKey" ) );
  idxNames.insert( vtkStdString ( "ReverseLookup" ) );
  vtkstd::set<int> idxTypes;
  idxTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::PRIMARY_KEY ) );
  idxTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::UNIQUE ) );

  // Loop over all indices of the previously created table
  int numIdx = schema->GetNumberOfIndicesInTable( tblHandle );
  if ( numIdx != 2 )
    {
    cerr << "Read " << numIdx << " != 2 indices in test schema.\n";
    status = false;
    }

  for ( int idxHandle = 0; idxHandle < numIdx; ++ idxHandle )
    {
    vtkStdString idxName = schema->GetIndexNameFromHandle( tblHandle, idxHandle );
    cerr << "Index name: "
         << idxName
         << "\n";

    vtkstd::set<vtkStdString>::iterator sit = idxNames.find( idxName );
    if ( sit != idxNames.end() )
      {
      idxNames.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve index name " << idxName  << " from test schema.\n";
      status = false;
      }

    int idxType = schema->GetIndexTypeFromHandle( tblHandle, idxHandle );
    cerr << "Index type: "
         << idxType
         << "\n";

    vtkstd::set<int>::iterator iit = idxTypes.find( idxType );
    if ( iit != idxTypes.end() )
      {
      idxTypes.erase ( iit );
      }
    else
      {
      cerr << "Could not retrieve index type " << idxType  << " from test schema.\n";
      status = false;
      }
    }

  // Define the correct (reference) triggers and types
  vtkstd::set<vtkStdString> trgNames;
  trgNames.insert( vtkStdString ( "InsertTrigger" ) );
  vtkstd::set<int> trgTypes;
  trgTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::AFTER_INSERT ) );
  vtkstd::set<vtkStdString> trgActions;
  trgActions.insert( vtkStdString( "INSERT INTO OtherTable ( Value ) VALUES NEW.SomeNmbr" ) );

  // Loop over all triggers of the previously created table
  int numTrg = schema->GetNumberOfTriggersInTable( tblHandle );
  if ( numTrg != 1 )
    {
    cerr << "Read " << numTrg << " != 2 triggers in test schema.\n";
    status = false;
    }

  for ( int trgHandle = 0; trgHandle < numTrg; ++ trgHandle )
    {
    vtkStdString trgName = schema->GetTriggerNameFromHandle( tblHandle, trgHandle );
    cerr << "Trigger name: "
         << trgName
         << "\n";

    vtkstd::set<vtkStdString>::iterator sit = trgNames.find( trgName );
    if ( sit != trgNames.end() )
      {
      trgNames.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve trigger name " << trgName  << " from test schema.\n";
      status = false;
      }

    int trgType = schema->GetTriggerTypeFromHandle( tblHandle, trgHandle );
    cerr << "Trigger type: "
         << trgType
         << "\n";

    vtkstd::set<int>::iterator iit = trgTypes.find( trgType );
    if ( iit != trgTypes.end() )
      {
      trgTypes.erase ( iit );
      }
    else
      {
      cerr << "Could not retrieve trigger type " << trgType  << " from test schema.\n";
      status = false;
      }

    vtkStdString trgAction = schema->GetTriggerActionFromHandle( tblHandle, trgHandle );
    cerr << "Trigger action: "
         << trgAction
         << "\n";

    sit = trgActions.find( trgAction );
    if ( sit != trgActions.end() )
      {
      trgActions.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve trigger action " << trgAction  << " from test schema.\n";
      status = false;
      }
    }

  schema->Delete();

  return status ? 0 : 1;
}
<commit_msg>ENH: modified the DB schema test to reflect the updates to the trigger      support for some SQL backends.<commit_after>/*=========================================================================

Program:   Visualization Toolkit
Module:    TestSQLDatabaseSchema.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*----------------------------------------------------------------------------
  Copyright (c) Sandia Corporation
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
  ----------------------------------------------------------------------------*/
// .SECTION Thanks
// Thanks to Philippe Pebay and David Thompson from Sandia National Laboratories
// for implementing this test.

#include "vtkSQLDatabaseSchema.h"

#include "vtkStdString.h"

#include <vtkstd/set>

int TestSQLDatabaseSchema( int /*argc*/, char* /*argv*/[] )
{
  bool status = true;
  int tblHandle = 0;
  vtkSQLDatabaseSchema* schema = vtkSQLDatabaseSchema::New();

  schema->SetName( "TestSchema" );
  tblHandle = schema->AddTableMultipleArguments( "ATable",
    vtkSQLDatabaseSchema::COLUMN_TOKEN, vtkSQLDatabaseSchema::SERIAL,  "TableKey",  0, "",
    vtkSQLDatabaseSchema::COLUMN_TOKEN, vtkSQLDatabaseSchema::VARCHAR, "SomeName", 11, "NOT NULL",
    vtkSQLDatabaseSchema::COLUMN_TOKEN, vtkSQLDatabaseSchema::BIGINT,  "SomeNmbr", 17, "DEFAULT 0",
    vtkSQLDatabaseSchema::INDEX_TOKEN,  vtkSQLDatabaseSchema::PRIMARY_KEY, "BigKey",
    vtkSQLDatabaseSchema::INDEX_COLUMN_TOKEN, "TableKey",
    vtkSQLDatabaseSchema::END_INDEX_TOKEN,
    vtkSQLDatabaseSchema::INDEX_TOKEN,  vtkSQLDatabaseSchema::UNIQUE, "ReverseLookup",
    vtkSQLDatabaseSchema::INDEX_COLUMN_TOKEN, "SomeName",
    vtkSQLDatabaseSchema::INDEX_COLUMN_TOKEN, "SomeNmbr",
    vtkSQLDatabaseSchema::END_INDEX_TOKEN,
    vtkSQLDatabaseSchema::TRIGGER_TOKEN,  vtkSQLDatabaseSchema::AFTER_INSERT,
      "InsertTrigger", "INSERT INTO BTable SET SomeValue = NEW.SomeNmbr",
    vtkSQLDatabaseSchema::END_TABLE_TOKEN
  );

  if ( tblHandle < 0 )
    {
    cerr << "Could not create test schema.\n";
    status = false;
    }

  // Define the correct (reference) columns and types
  vtkstd::set<vtkStdString> colNames;
  colNames.insert( vtkStdString ( "SomeNmbr" ) );
  colNames.insert( vtkStdString ( "SomeName" ) );
  colNames.insert( vtkStdString ( "TableKey" ) );
  vtkstd::set<int> colTypes;
  colTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::BIGINT ) );
  colTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::SERIAL ) );
  colTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::VARCHAR ) );

  // Loop over all columns of the previously created table
  int numCol = schema->GetNumberOfColumnsInTable( tblHandle );
  if ( numCol != 3 )
    {
    cerr << "Read " << numCol << " != 3 columns in test schema.\n";
    status = false;
    }

  for ( int colHandle = 0; colHandle < numCol; ++ colHandle )
    {
    vtkStdString colName = schema->GetColumnNameFromHandle( tblHandle, colHandle );
    cerr << "Column name: "
         << colName
         << "\n";

    vtkstd::set<vtkStdString>::iterator sit = colNames.find( colName );
    if ( sit != colNames.end() )
      {
      colNames.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve column name " << colName  << " from test schema.\n";
      status = false;
      }

    int colType = schema->GetColumnTypeFromHandle( tblHandle, colHandle );
    cerr << "Column type: "
         << colType
         << "\n";

    vtkstd::set<int>::iterator iit = colTypes.find( colType );
    if ( iit != colTypes.end() )
      {
      colTypes.erase ( iit );
      }
    else
      {
      cerr << "Could not retrieve column type " << colType  << " from test schema.\n";
      status = false;
      }
    }

  // Define the correct (reference) indices and types
  vtkstd::set<vtkStdString> idxNames;
  idxNames.insert( vtkStdString ( "BigKey" ) );
  idxNames.insert( vtkStdString ( "ReverseLookup" ) );
  vtkstd::set<int> idxTypes;
  idxTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::PRIMARY_KEY ) );
  idxTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::UNIQUE ) );

  // Loop over all indices of the previously created table
  int numIdx = schema->GetNumberOfIndicesInTable( tblHandle );
  if ( numIdx != 2 )
    {
    cerr << "Read " << numIdx << " != 2 indices in test schema.\n";
    status = false;
    }

  for ( int idxHandle = 0; idxHandle < numIdx; ++ idxHandle )
    {
    vtkStdString idxName = schema->GetIndexNameFromHandle( tblHandle, idxHandle );
    cerr << "Index name: "
         << idxName
         << "\n";

    vtkstd::set<vtkStdString>::iterator sit = idxNames.find( idxName );
    if ( sit != idxNames.end() )
      {
      idxNames.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve index name " << idxName  << " from test schema.\n";
      status = false;
      }

    int idxType = schema->GetIndexTypeFromHandle( tblHandle, idxHandle );
    cerr << "Index type: "
         << idxType
         << "\n";

    vtkstd::set<int>::iterator iit = idxTypes.find( idxType );
    if ( iit != idxTypes.end() )
      {
      idxTypes.erase ( iit );
      }
    else
      {
      cerr << "Could not retrieve index type " << idxType  << " from test schema.\n";
      status = false;
      }
    }

  // Define the correct (reference) triggers and types
  vtkstd::set<vtkStdString> trgNames;
  trgNames.insert( vtkStdString ( "InsertTrigger" ) );
  vtkstd::set<int> trgTypes;
  trgTypes.insert( static_cast<int>( vtkSQLDatabaseSchema::AFTER_INSERT ) );
  vtkstd::set<vtkStdString> trgActions;
  trgActions.insert( vtkStdString( "INSERT INTO BTable SET SomeValue = NEW.SomeNmbr" ) );

  // Loop over all triggers of the previously created table
  int numTrg = schema->GetNumberOfTriggersInTable( tblHandle );
  if ( numTrg != 1 )
    {
    cerr << "Read " << numTrg << " != 2 triggers in test schema.\n";
    status = false;
    }

  for ( int trgHandle = 0; trgHandle < numTrg; ++ trgHandle )
    {
    vtkStdString trgName = schema->GetTriggerNameFromHandle( tblHandle, trgHandle );
    cerr << "Trigger name: "
         << trgName
         << "\n";

    vtkstd::set<vtkStdString>::iterator sit = trgNames.find( trgName );
    if ( sit != trgNames.end() )
      {
      trgNames.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve trigger name " << trgName  << " from test schema.\n";
      status = false;
      }

    int trgType = schema->GetTriggerTypeFromHandle( tblHandle, trgHandle );
    cerr << "Trigger type: "
         << trgType
         << "\n";

    vtkstd::set<int>::iterator iit = trgTypes.find( trgType );
    if ( iit != trgTypes.end() )
      {
      trgTypes.erase ( iit );
      }
    else
      {
      cerr << "Could not retrieve trigger type " << trgType  << " from test schema.\n";
      status = false;
      }

    vtkStdString trgAction = schema->GetTriggerActionFromHandle( tblHandle, trgHandle );
    cerr << "Trigger action: "
         << trgAction
         << "\n";

    sit = trgActions.find( trgAction );
    if ( sit != trgActions.end() )
      {
      trgActions.erase ( sit );
      }
    else
      {
      cerr << "Could not retrieve trigger action " << trgAction  << " from test schema.\n";
      status = false;
      }
    }

  schema->Delete();

  return status ? 0 : 1;
}
<|endoftext|>