<commit_before>#include "boid.h"

/**
 * @brief Create a new boid
 *
 * Creates a new QQmlComponent using the main QQmlApplicationEngine of the program and
 * the Boid.qml file.
 * After the component is created and ready, the QObject is created and parented to the
 * canvas object.
 * Lastly we set the position randomly, but staying within the boundaries.
 */
Boid::Boid() {
    component = new QQmlComponent(getEngine(), QUrl(QStringLiteral("qrc:/Boid.qml")));
    if(component->status() == component->Ready) {
        object = component->create(getEngine()->rootContext());
        object->setProperty("parent", QVariant::fromValue(getCanvas()));
        QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
    }
    else
        qDebug() << component->errorString();

    setY(50 + ((double)rand()/(double)(RAND_MAX)) * (getCanvasHeight() - 100));
    setX(50 + ((double)rand()/(double)(RAND_MAX)) * (getCanvasWidth() - 100));

    lastVel = Vector2();
}

Boid::~Boid() {
    delete component;
    delete object;
}

/**
 * @brief Boid logic
 */
void Boid::update(){
    Vector2 v1 = Vector2();
    Vector2 v2 = Vector2();
    Vector2 v3 = Vector2();
    Vector2 v4 = Vector2();
    Vector2 center = Vector2();

    // Rule1: move to local center of mass ( center becomes average of surrounding boids)
    for(int i = 0; i < 3; i++){
        center = center + neighbours[i].position2;
    }
    center /= 3;

    v1 = center - position;

    // Rule2: Avoidance : if distance to next boid smaller than threshold T boid changes course.
    center = Vector2();
    for(int i = 0; i < 3; i++){
        if ((neighbours[i].position2 - position).getSqrMagnitude() < (getSize() + 10.0) * (getSize() + 10.0)){
            center = center - (neighbours[i].position2 - position);
        }
    }
    v2 = center;

    // Rule3: Match velocity to surrounding Boids
    for(int i = 0; i < 3; i++){
        v3 = v3 + neighbours[i].velocity2;
    }
    v3 = v3/3;

    // Rule 4: Follow mouse position
    Vector2 mp = getMousePosition();
    if(!(mp == Vector2(0, 0))) {
        if((mp - position).getSqrMagnitude() > 7500)
            v4 = mp - position;
    }

    // Rule 5: Stray away from the boundaries
    Vector2 v5 = Vector2();

    if(position.getX() < 80)
        v5.setX(1);
    else if(position.getX() >= getCanvasWidth() - 80)
        v5.setX(-1);

    if(position.getY() <  80)
        v5.setY(1);
    else if(position.getY() >= getCanvasHeight() - 80){
        v5.setY(-1);
    }

    //Replace position with mouse position and look through the force-parameter with the mouse
    double power = 0.0;
    double power2 = 0.0;
    if(position.getX() < 80)
        power = 100 - position.getX();
    else if(position.getX() >= getCanvasWidth() - 80)
        power = 100 + position.getX() - getCanvasWidth() ;
    else if(position.getY() < 80)
        power2 = 100 - position.getY();
    else if(position.getY() >= getCanvasHeight() - 80)
        power2 = 100 + position.getY() - getCanvasHeight();

    double force = 10*exp((power+ power2) *0.05);

    //qDebug << force;

    velocity = Vector2::lerp(lastVel,
                             velocity + v1.normalize()*getFlockingFactor() + v2.normalize()*getAvoidanceFactor() +
                             v3.normalize()*getVelocityMatchFactor() + v4.normalize()*getTargetFactor() + v5 * force,
                             0.016f);

    lastVel = velocity;
}
<commit_msg>Small Change: Fix due to Changing Values<commit_after>#include "boid.h"

/**
 * @brief Create a new boid
 *
 * Creates a new QQmlComponent using the main QQmlApplicationEngine of the program and
 * the Boid.qml file.
 * After the component is created and ready, the QObject is created and parented to the
 * canvas object.
 * Lastly we set the position randomly, but staying within the boundaries.
 */
Boid::Boid() {
    component = new QQmlComponent(getEngine(), QUrl(QStringLiteral("qrc:/Boid.qml")));
    if(component->status() == component->Ready) {
        object = component->create(getEngine()->rootContext());
        object->setProperty("parent", QVariant::fromValue(getCanvas()));
        QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
    }
    else
        qDebug() << component->errorString();

    setY(50 + ((double)rand()/(double)(RAND_MAX)) * (getCanvasHeight() - 100));
    setX(50 + ((double)rand()/(double)(RAND_MAX)) * (getCanvasWidth() - 100));

    lastVel = Vector2();
}

Boid::~Boid() {
    delete component;
    delete object;
}

/**
 * @brief Boid logic
 */
void Boid::update(){
    Vector2 v1 = Vector2();
    Vector2 v2 = Vector2();
    Vector2 v3 = Vector2();
    Vector2 v4 = Vector2();
    Vector2 center = Vector2();

    // Rule1: move to local center of mass ( center becomes average of surrounding boids)
    for(int i = 0; i < 3; i++){
        center = center + neighbours[i].position2;
    }
    center /= 3;

    v1 = center - position;

    // Rule2: Avoidance : if distance to next boid smaller than threshold T boid changes course.
    center = Vector2();
    for(int i = 0; i < 3; i++){
        if ((neighbours[i].position2 - position).getSqrMagnitude() < (getSize() + 10.0) * (getSize() + 10.0)){
            center = center - (neighbours[i].position2 - position);
        }
    }
    v2 = center;

    // Rule3: Match velocity to surrounding Boids
    for(int i = 0; i < 3; i++){
        v3 = v3 + neighbours[i].velocity2;
    }
    v3 = v3/3;

    // Rule 4: Follow mouse position
    Vector2 mp = getMousePosition();
    if(!(mp == Vector2(0, 0))) {
        if((mp - position).getSqrMagnitude() > 7500)
            v4 = mp - position;
    }

    // Rule 5: Stray away from the boundaries
    Vector2 v5 = Vector2();

    if(position.getX() < 80)
        v5.setX(1);
    else if(position.getX() >= getCanvasWidth() - 80)
        v5.setX(-1);

    if(position.getY() <  80)
        v5.setY(1);
    else if(position.getY() >= getCanvasHeight() - 80){
        v5.setY(-1);
    }

    //Replace position with mouse position and look through the force-parameter with the mouse
    double power = 0.0;
    double power2 = 0.0;
    if(position.getX() < 80)
        power = 80 - position.getX();
    else if(position.getX() >= getCanvasWidth() - 80)
        power = 80 + position.getX() - getCanvasWidth() ;
    else if(position.getY() < 80)
        power2 = 80 - position.getY();
    else if(position.getY() >= getCanvasHeight() - 80)
        power2 = 80 + position.getY() - getCanvasHeight();

    double force = 10*exp((power+ power2) *0.05);

    //qDebug << force;

    velocity = Vector2::lerp(lastVel,
                             velocity + v1.normalize()*getFlockingFactor() + v2.normalize()*getAvoidanceFactor() +
                             v3.normalize()*getVelocityMatchFactor() + v4.normalize()*getTargetFactor() + v5 * force,
                             0.016f);

    lastVel = velocity;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 Baldur Karlsson
 *
 * 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 "PipelineStateViewer.h"
#include "D3D11PipelineStateViewer.h"
#include "D3D12PipelineStateViewer.h"
#include "GLPipelineStateViewer.h"
#include "VulkanPipelineStateViewer.h"
#include "ui_PipelineStateViewer.h"

PipelineStateViewer::PipelineStateViewer(CaptureContext *ctx, QWidget *parent)
    : QFrame(parent), ui(new Ui::PipelineStateViewer), m_Ctx(ctx)
{
  ui->setupUi(this);

  m_D3D11 = NULL;
  m_D3D12 = NULL;
  m_GL = NULL;
  m_Vulkan = NULL;

  m_Current = NULL;

  m_Ctx->AddLogViewer(this);

  setToD3D11();
}

PipelineStateViewer::~PipelineStateViewer()
{
  reset();

  m_Ctx->windowClosed(this);
  m_Ctx->RemoveLogViewer(this);

  delete ui;
}

void PipelineStateViewer::OnLogfileLoaded()
{
  if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_D3D11)
    setToD3D11();
  else if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_D3D12)
    setToD3D12();
  else if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_OpenGL)
    setToGL();
  else if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_Vulkan)
    setToVulkan();

  if(m_Current)
    m_Current->OnLogfileLoaded();
}

void PipelineStateViewer::OnLogfileClosed()
{
  if(m_Current)
    m_Current->OnLogfileClosed();
}

void PipelineStateViewer::OnEventChanged(uint32_t eventID)
{
  if(m_Current)
    m_Current->OnEventChanged(eventID);
}

QVariant PipelineStateViewer::persistData()
{
  QVariantMap state;

  if(m_Current == m_D3D11)
    state["type"] = "D3D11";
  else if(m_Current == m_D3D12)
    state["type"] = "D3D12";
  else if(m_Current == m_GL)
    state["type"] = "GL";
  else if(m_Current == m_Vulkan)
    state["type"] = "Vulkan";
  else
    state["type"] = "";

  return state;
}

void PipelineStateViewer::setPersistData(const QVariant &persistData)
{
  QString str = persistData.toMap()["type"].toString();

  if(str == "D3D11")
    setToD3D11();
  else if(str == "D3D12")
    setToD3D12();
  else if(str == "GL")
    setToGL();
  else if(str == "Vulkan")
    setToVulkan();
}

void PipelineStateViewer::reset()
{
  delete m_D3D11;
  delete m_D3D12;
  delete m_GL;
  delete m_Vulkan;

  m_D3D11 = NULL;
  m_D3D12 = NULL;
  m_GL = NULL;
  m_Vulkan = NULL;

  m_Current = NULL;
}

void PipelineStateViewer::setToD3D11()
{
  if(m_D3D11)
    return;

  reset();

  m_D3D11 = new D3D11PipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_D3D11);
  m_Current = m_D3D11;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_D3D11;
}

void PipelineStateViewer::setToD3D12()
{
  if(m_D3D12)
    return;

  reset();

  m_D3D12 = new D3D12PipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_D3D12);
  m_Current = m_D3D12;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_D3D12;
}

void PipelineStateViewer::setToGL()
{
  if(m_GL)
    return;

  reset();

  m_GL = new GLPipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_GL);
  m_Current = m_GL;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_OpenGL;
}

void PipelineStateViewer::setToVulkan()
{
  if(m_Vulkan)
    return;

  reset();

  m_Vulkan = new VulkanPipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_Vulkan);
  m_Current = m_Vulkan;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_Vulkan;
}
<commit_msg>If LogfileLoaded is called out of order, ensure pipeline state is ready<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 Baldur Karlsson
 *
 * 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 "PipelineStateViewer.h"
#include "D3D11PipelineStateViewer.h"
#include "D3D12PipelineStateViewer.h"
#include "GLPipelineStateViewer.h"
#include "VulkanPipelineStateViewer.h"
#include "ui_PipelineStateViewer.h"

PipelineStateViewer::PipelineStateViewer(CaptureContext *ctx, QWidget *parent)
    : QFrame(parent), ui(new Ui::PipelineStateViewer), m_Ctx(ctx)
{
  ui->setupUi(this);

  m_D3D11 = NULL;
  m_D3D12 = NULL;
  m_GL = NULL;
  m_Vulkan = NULL;

  m_Current = NULL;

  m_Ctx->AddLogViewer(this);

  setToD3D11();
}

PipelineStateViewer::~PipelineStateViewer()
{
  reset();

  m_Ctx->windowClosed(this);
  m_Ctx->RemoveLogViewer(this);

  delete ui;
}

void PipelineStateViewer::OnLogfileLoaded()
{
  if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_D3D11)
    setToD3D11();
  else if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_D3D12)
    setToD3D12();
  else if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_OpenGL)
    setToGL();
  else if(m_Ctx->APIProps().pipelineType == eGraphicsAPI_Vulkan)
    setToVulkan();

  if(m_Current)
    m_Current->OnLogfileLoaded();
}

void PipelineStateViewer::OnLogfileClosed()
{
  if(m_Current)
    m_Current->OnLogfileClosed();
}

void PipelineStateViewer::OnEventChanged(uint32_t eventID)
{
  if(m_Ctx->CurPipelineState.DefaultType != m_Ctx->APIProps().pipelineType)
    OnLogfileLoaded();

  if(m_Current)
    m_Current->OnEventChanged(eventID);
}

QVariant PipelineStateViewer::persistData()
{
  QVariantMap state;

  if(m_Current == m_D3D11)
    state["type"] = "D3D11";
  else if(m_Current == m_D3D12)
    state["type"] = "D3D12";
  else if(m_Current == m_GL)
    state["type"] = "GL";
  else if(m_Current == m_Vulkan)
    state["type"] = "Vulkan";
  else
    state["type"] = "";

  return state;
}

void PipelineStateViewer::setPersistData(const QVariant &persistData)
{
  QString str = persistData.toMap()["type"].toString();

  if(str == "D3D11")
    setToD3D11();
  else if(str == "D3D12")
    setToD3D12();
  else if(str == "GL")
    setToGL();
  else if(str == "Vulkan")
    setToVulkan();
}

void PipelineStateViewer::reset()
{
  delete m_D3D11;
  delete m_D3D12;
  delete m_GL;
  delete m_Vulkan;

  m_D3D11 = NULL;
  m_D3D12 = NULL;
  m_GL = NULL;
  m_Vulkan = NULL;

  m_Current = NULL;
}

void PipelineStateViewer::setToD3D11()
{
  if(m_D3D11)
    return;

  reset();

  m_D3D11 = new D3D11PipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_D3D11);
  m_Current = m_D3D11;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_D3D11;
}

void PipelineStateViewer::setToD3D12()
{
  if(m_D3D12)
    return;

  reset();

  m_D3D12 = new D3D12PipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_D3D12);
  m_Current = m_D3D12;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_D3D12;
}

void PipelineStateViewer::setToGL()
{
  if(m_GL)
    return;

  reset();

  m_GL = new GLPipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_GL);
  m_Current = m_GL;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_OpenGL;
}

void PipelineStateViewer::setToVulkan()
{
  if(m_Vulkan)
    return;

  reset();

  m_Vulkan = new VulkanPipelineStateViewer(m_Ctx, this);
  ui->layout->addWidget(m_Vulkan);
  m_Current = m_Vulkan;
  m_Ctx->CurPipelineState.DefaultType = eGraphicsAPI_Vulkan;
}
<|endoftext|>


<commit_before>#ifndef MJOLNIR_CORE_EXCLUSION_LIST_HPP
#define MJOLNIR_CORE_EXCLUSION_LIST_HPP
#include <mjolnir/core/System.hpp>
#include <mjolnir/util/range.hpp>
#include <mjolnir/util/logger.hpp>
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>

namespace mjolnir
{

// In some cases, like excluded volume interaction between particles that
// are connected by a bond, some particular pairs of particles are excluded
// from interacting pairs.
// This class constructs a list that contains a list of pairs that are excluded
// from interacting pairs using information in a topology and a potential class.
class ExclusionList
{
  public:
    using topology_type        = Topology;
    using molecule_id_type     = topology_type::molecule_id_type;
    using group_id_type        = topology_type::group_id_type;
    using connection_kind_type = topology_type::connection_kind_type;

  public:

    ExclusionList() = default;
    ~ExclusionList() = default;
    ExclusionList(const ExclusionList&) = default;
    ExclusionList(ExclusionList&&)      = default;
    ExclusionList& operator=(const ExclusionList&) = default;
    ExclusionList& operator=(ExclusionList&&)      = default;

    // check an interaction exists between i-th and j-th particles.
    //
    // It searches a small list of indices and check j is found in a exclusion-
    // list of i-th particle.
    bool is_excluded(const std::size_t i, const std::size_t j) const
    {
        // assuming all the lists are enough small (< 20 or so)
        {
            const auto ign_grps = this->ignored_grp_of(this->grp_ids_[i]);
            const auto grp_of_j = this->grp_ids_[j];
            if(std::binary_search(ign_grps.begin(), ign_grps.end(), grp_of_j))
            {
                // if found, the pair is ignored. return true.
                return true;
            }
        }

        // check molecule ids...
        const auto mol_of_j = this->mol_ids_[j];
        for(const auto& ignoring_mol : this->ignored_mol_of(this->mol_ids_[i]))
        {
            // already sorted like ignoring_mol = [4,5,6]
            if     (mol_of_j <  ignoring_mol) {break;}
            else if(mol_of_j == ignoring_mol) {return true;}
        }
        // check distance on topology...
        for(const auto& ignoring_idx : this->ignored_idx_of(i))
        {
            if     (ignoring_idx >  j) {break;}
            else if(ignoring_idx == j) {return true;}
        }
        return false;
    }

    // construct exclusion lists from topology and potential.
    //
    // This function may take a bit long time.
    template<typename traitsT, typename PotentialT>
    void make(const System<traitsT>& sys, const PotentialT& pot)
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();
        MJOLNIR_LOG_INFO("potential = ", pot.name());

        const auto& topol = sys.topology();
        const std::size_t N = sys.size();

        // count groups and assign indices
        this->grp_list_.clear();
        for(std::size_t i=0; i<N; ++i)
        {
            if(std::find(this->grp_list_.begin(), this->grp_list_.end(),
                         topol.group_of(i)) == this->grp_list_.end())
            {
                // if not found, push it to the list
                this->grp_list_.push_back(topol.group_of(i));
                MJOLNIR_LOG_INFO("group ", topol.group_of(i), " found. ",
                                 this->grp_list_.size(), "-th group is ",
                                 topol.group_of(i));
            }
        }
        MJOLNIR_LOG_INFO("all groups are found {", this->grp_list_, "}.");

        // Here, the list of all groups is constructed.
        // Next, construct grp_ids_. assign corresponding group-ids
        this->grp_ids_.resize(N);
        for(std::size_t i=0; i<N; ++i)
        {
            const auto found = std::find(grp_list_.begin(), grp_list_.end(),
                                         topol.group_of(i));
            assert(found != grp_list_.end()); // should be found.
            this->grp_ids_.at(i) = std::distance(grp_list_.begin(), found);
            MJOLNIR_LOG_DEBUG("particle ", i, " belongs to ", *found, " and "
                              "the corresponding group_idx is ", grp_ids_.at(i),
                              " and the value is ", grp_list_.at(grp_ids_.at(i)));
        }

        // Next, construct exclusion list for group.
        {
            const std::size_t Ngrps = this->grp_list_.size();

            std::size_t idx = 0; // the current index in the exclusion list
            for(std::size_t i=0; i<Ngrps; ++i)
            {
                const std::size_t first = idx; // first index of the sub-list

                const auto& grp_name_i = this->grp_list_.at(i);
                for(std::size_t j=0; j<Ngrps; ++j)
                {
                    const auto& grp_name_j = this->grp_list_.at(j);
                    if(pot.is_ignored_group(grp_name_i, grp_name_j))
                    {
                        MJOLNIR_LOG_INFO("group ", grp_name_i, " and ", grp_name_j,
                                         " ignores each other on ", pot.name());
                        this->ignored_grps_.push_back(i);
                        ++idx;
                    }
                }
                const auto beg = ignored_grps_.begin();
                // sort this sub-range [first, idx)
                std::sort(beg + first, beg + idx);
                this->grp_ranges_.emplace_back(first, idx);
            }
        }

        // copy molecule_ids from topol to this
        this->mol_ids_.resize(N);
        for(std::size_t i=0; i<N; ++i)
        {
            this->mol_ids_[i] = topol.molecule_of(i);
            MJOLNIR_LOG_DEBUG("particle ", i, " is belonging molecule ",
                              topol.molecule_of(i));
        }

        // make ignored_molecule_idxs
        {
            const std::size_t Nmols = topol.number_of_molecules();
            std::size_t idx = 0;
            for(std::size_t i=0; i<Nmols; ++i)
            {
                const std::size_t first = idx;
                for(std::size_t j=0; j<Nmols; ++j)
                {
                    if(pot.is_ignored_molecule(i, j))
                    {
                        MJOLNIR_LOG_INFO("molecule ", i, " and molecule ", j,
                            " will ignore each other on ", pot.name());
                        this->ignored_mols_.push_back(j);
                        ++idx;
                    }
                }
                const auto beg = ignored_mols_.begin();
                std::sort(beg + first, beg + idx);
                this->mol_ranges_.emplace_back(first, idx);
            }
        }

        // make ignored_particle_idxs
        // excluded_connection := pair{connection kind, distance}
        {
            std::size_t idx = 0;
            for(std::size_t i=0; i<N; ++i)
            {
                const std::size_t first = idx;
                std::vector<std::size_t> ignored_particles{i};
                for(const auto& connection : pot.ignore_within())
                {
                    const std::size_t dist = connection.second;
                    for(const auto j :
                        topol.list_adjacent_within(i, dist, connection.first))
                    {
                        ignored_particles.push_back(j);
                    }
                }
                std::sort(ignored_particles.begin(), ignored_particles.end());
                const auto last = std::unique(ignored_particles.begin(),
                                              ignored_particles.end());
                ignored_particles.erase(last, ignored_particles.end());
                MJOLNIR_LOG_INFO("particle ", i, " ignores ", ignored_particles);

                for(const auto j : ignored_particles)
                {
                    this->ignored_idxs_.push_back(j);
                    ++idx;
                }
                this->idx_ranges_.emplace_back(first, idx);
            }
        }
        return;
    }

  private:

    range<typename std::vector<std::size_t>::const_iterator>
    ignored_idx_of(const std::size_t i) const noexcept
    {
        return range<typename std::vector<std::size_t>::const_iterator>{
            this->ignored_idxs_.begin() + this->idx_ranges_[i].first,
            this->ignored_idxs_.begin() + this->idx_ranges_[i].second
        };
    }
    range<typename std::vector<std::size_t>::const_iterator>
    ignored_mol_of(const std::size_t i) const noexcept
    {
        return range<typename std::vector<std::size_t>::const_iterator>{
            this->ignored_mols_.begin() + this->mol_ranges_[i].first,
            this->ignored_mols_.begin() + this->mol_ranges_[i].second
        };
    }
    range<typename std::vector<std::size_t>::const_iterator>
    ignored_grp_of(const std::size_t i) const noexcept
    {
        return range<typename std::vector<std::size_t>::const_iterator>{
            this->ignored_grps_.begin() + this->grp_ranges_[i].first,
            this->ignored_grps_.begin() + this->grp_ranges_[i].second
        };
    }

  private:

    // It contains the same infromation as {topol.nodes_.molecule_id};
    std::vector<molecule_id_type> mol_ids_;

    // Here, ExclusionList counts a number of groups and assign a unique index
    // for each group. `grp_ids` contains the index, not std::string itself.
    // It would be changed by calling ExclusionList::make() because it counts
    // groups and and assigns IDs.
    std::vector<group_id_type>    grp_list_; // list of the group names
    std::vector<std::size_t>      grp_ids_;  // the index of the above list

    // ignored mol_id...
    std::vector<std::size_t> ignored_mols_;
    std::vector<std::pair<std::size_t, std::size_t>> mol_ranges_;

    // grp_idx...
    std::vector<std::size_t> ignored_grps_;
    std::vector<std::pair<std::size_t, std::size_t>> grp_ranges_;

    // normal particle idx
    std::vector<std::size_t> ignored_idxs_;
    std::vector<std::pair<std::size_t, std::size_t>> idx_ranges_;
};

} // mjolnir
#endif// MJOLNIR_NEIGHBOR_LIST_HPP
<commit_msg>fix: list corect group id<commit_after>#ifndef MJOLNIR_CORE_EXCLUSION_LIST_HPP
#define MJOLNIR_CORE_EXCLUSION_LIST_HPP
#include <mjolnir/core/System.hpp>
#include <mjolnir/util/range.hpp>
#include <mjolnir/util/logger.hpp>
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>

namespace mjolnir
{

// In some cases, like excluded volume interaction between particles that
// are connected by a bond, some particular pairs of particles are excluded
// from interacting pairs.
// This class constructs a list that contains a list of pairs that are excluded
// from interacting pairs using information in a topology and a potential class.
class ExclusionList
{
  public:
    using topology_type        = Topology;
    using molecule_id_type     = topology_type::molecule_id_type;
    using group_id_type        = topology_type::group_id_type;
    using connection_kind_type = topology_type::connection_kind_type;

  public:

    ExclusionList() = default;
    ~ExclusionList() = default;
    ExclusionList(const ExclusionList&) = default;
    ExclusionList(ExclusionList&&)      = default;
    ExclusionList& operator=(const ExclusionList&) = default;
    ExclusionList& operator=(ExclusionList&&)      = default;

    // check an interaction exists between i-th and j-th particles.
    //
    // It searches a small list of indices and check j is found in a exclusion-
    // list of i-th particle.
    bool is_excluded(const std::size_t i, const std::size_t j) const
    {
        // assuming all the lists are enough small (< 20 or so)
        {
            const auto ign_grps = this->ignored_grp_of(this->grp_ids_[i]);
            const auto grp_of_j = this->grp_ids_[j];
            if(std::binary_search(ign_grps.begin(), ign_grps.end(), grp_of_j))
            {
                // if found, the pair is ignored. return true.
                return true;
            }
        }

        // check molecule ids...
        const auto mol_of_j = this->mol_ids_[j];
        for(const auto& ignoring_mol : this->ignored_mol_of(this->mol_ids_[i]))
        {
            // already sorted like ignoring_mol = [4,5,6]
            if     (mol_of_j <  ignoring_mol) {break;}
            else if(mol_of_j == ignoring_mol) {return true;}
        }
        // check distance on topology...
        for(const auto& ignoring_idx : this->ignored_idx_of(i))
        {
            if     (ignoring_idx >  j) {break;}
            else if(ignoring_idx == j) {return true;}
        }
        return false;
    }

    // construct exclusion lists from topology and potential.
    //
    // This function may take a bit long time.
    template<typename traitsT, typename PotentialT>
    void make(const System<traitsT>& sys, const PotentialT& pot)
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();
        MJOLNIR_LOG_INFO("potential = ", pot.name());

        const auto& topol = sys.topology();
        const std::size_t N = sys.size();

        // count groups and assign indices
        this->grp_list_.clear();
        for(std::size_t i=0; i<N; ++i)
        {
            if(std::find(this->grp_list_.begin(), this->grp_list_.end(),
                         topol.group_of(i)) == this->grp_list_.end())
            {
                // if not found, push it to the list
                this->grp_list_.push_back(topol.group_of(i));
                MJOLNIR_LOG_INFO("group ", topol.group_of(i), " found. ",
                                 this->grp_list_.size(), "-th group is ",
                                 topol.group_of(i));
            }
        }
        MJOLNIR_LOG_INFO("all groups are found {", this->grp_list_, "}.");

        // Here, the list of all groups is constructed.
        // Next, construct grp_ids_. assign corresponding group-ids
        this->grp_ids_.resize(N);
        for(std::size_t i=0; i<N; ++i)
        {
            const auto found = std::find(grp_list_.begin(), grp_list_.end(),
                                         topol.group_of(i));
            assert(found != grp_list_.end()); // should be found.
            this->grp_ids_.at(i) = std::distance(grp_list_.begin(), found);
            MJOLNIR_LOG_DEBUG("particle ", i, " belongs to ", *found, " and "
                              "the corresponding group_idx is ", grp_ids_.at(i),
                              " and the value is ", grp_list_.at(grp_ids_.at(i)));
        }

        // Next, construct exclusion list for group.
        {
            const std::size_t Ngrps = this->grp_list_.size();

            std::size_t idx = 0; // the current index in the exclusion list
            for(std::size_t i=0; i<Ngrps; ++i)
            {
                const std::size_t first = idx; // first index of the sub-list

                const auto& grp_name_i = this->grp_list_.at(i);
                for(std::size_t j=0; j<Ngrps; ++j)
                {
                    const auto& grp_name_j = this->grp_list_.at(j);
                    if(pot.is_ignored_group(grp_name_i, grp_name_j))
                    {
                        MJOLNIR_LOG_INFO("group ", grp_name_i, " and ", grp_name_j,
                                         " ignores each other on ", pot.name());
                        this->ignored_grps_.push_back(j);
                        ++idx;
                    }
                }
                const auto beg = ignored_grps_.begin();
                // sort this sub-range [first, idx)
                std::sort(beg + first, beg + idx);
                this->grp_ranges_.emplace_back(first, idx);
            }
        }

        // copy molecule_ids from topol to this
        this->mol_ids_.resize(N);
        for(std::size_t i=0; i<N; ++i)
        {
            this->mol_ids_[i] = topol.molecule_of(i);
            MJOLNIR_LOG_DEBUG("particle ", i, " is belonging molecule ",
                              topol.molecule_of(i));
        }

        // make ignored_molecule_idxs
        {
            const std::size_t Nmols = topol.number_of_molecules();
            std::size_t idx = 0;
            for(std::size_t i=0; i<Nmols; ++i)
            {
                const std::size_t first = idx;
                for(std::size_t j=0; j<Nmols; ++j)
                {
                    if(pot.is_ignored_molecule(i, j))
                    {
                        MJOLNIR_LOG_INFO("molecule ", i, " and molecule ", j,
                            " will ignore each other on ", pot.name());
                        this->ignored_mols_.push_back(j);
                        ++idx;
                    }
                }
                const auto beg = ignored_mols_.begin();
                std::sort(beg + first, beg + idx);
                this->mol_ranges_.emplace_back(first, idx);
            }
        }

        // make ignored_particle_idxs
        // excluded_connection := pair{connection kind, distance}
        {
            std::size_t idx = 0;
            for(std::size_t i=0; i<N; ++i)
            {
                const std::size_t first = idx;
                std::vector<std::size_t> ignored_particles{i};
                for(const auto& connection : pot.ignore_within())
                {
                    const std::size_t dist = connection.second;
                    for(const auto j :
                        topol.list_adjacent_within(i, dist, connection.first))
                    {
                        ignored_particles.push_back(j);
                    }
                }
                std::sort(ignored_particles.begin(), ignored_particles.end());
                const auto last = std::unique(ignored_particles.begin(),
                                              ignored_particles.end());
                ignored_particles.erase(last, ignored_particles.end());
                MJOLNIR_LOG_INFO("particle ", i, " ignores ", ignored_particles);

                for(const auto j : ignored_particles)
                {
                    this->ignored_idxs_.push_back(j);
                    ++idx;
                }
                this->idx_ranges_.emplace_back(first, idx);
            }
        }
        return;
    }

  private:

    range<typename std::vector<std::size_t>::const_iterator>
    ignored_idx_of(const std::size_t i) const noexcept
    {
        return range<typename std::vector<std::size_t>::const_iterator>{
            this->ignored_idxs_.begin() + this->idx_ranges_[i].first,
            this->ignored_idxs_.begin() + this->idx_ranges_[i].second
        };
    }
    range<typename std::vector<std::size_t>::const_iterator>
    ignored_mol_of(const std::size_t i) const noexcept
    {
        return range<typename std::vector<std::size_t>::const_iterator>{
            this->ignored_mols_.begin() + this->mol_ranges_[i].first,
            this->ignored_mols_.begin() + this->mol_ranges_[i].second
        };
    }
    range<typename std::vector<std::size_t>::const_iterator>
    ignored_grp_of(const std::size_t i) const noexcept
    {
        return range<typename std::vector<std::size_t>::const_iterator>{
            this->ignored_grps_.begin() + this->grp_ranges_[i].first,
            this->ignored_grps_.begin() + this->grp_ranges_[i].second
        };
    }

  private:

    // It contains the same infromation as {topol.nodes_.molecule_id};
    std::vector<molecule_id_type> mol_ids_;

    // Here, ExclusionList counts a number of groups and assign a unique index
    // for each group. `grp_ids` contains the index, not std::string itself.
    // It would be changed by calling ExclusionList::make() because it counts
    // groups and and assigns IDs.
    std::vector<group_id_type>    grp_list_; // list of the group names
    std::vector<std::size_t>      grp_ids_;  // the index of the above list

    // ignored mol_id...
    std::vector<std::size_t> ignored_mols_;
    std::vector<std::pair<std::size_t, std::size_t>> mol_ranges_;

    // grp_idx...
    std::vector<std::size_t> ignored_grps_;
    std::vector<std::pair<std::size_t, std::size_t>> grp_ranges_;

    // normal particle idx
    std::vector<std::size_t> ignored_idxs_;
    std::vector<std::pair<std::size_t, std::size_t>> idx_ranges_;
};

} // mjolnir
#endif// MJOLNIR_NEIGHBOR_LIST_HPP
<|endoftext|>


<commit_before><commit_msg>🛠 Avoid shadowing warning<commit_after><|endoftext|>


<commit_before>#include "cfilelistmodel.h"
#include "shell/cshell.h"
#include "ccontroller.h"
#include "../../../cmainwindow.h"
#include "../../columns.h"

DISABLE_COMPILER_WARNINGS
#include <QMimeData>
#include <QUrl>
RESTORE_COMPILER_WARNINGS

#include <set>

CFileListModel::CFileListModel(QTreeView * treeView, QObject *parent) :
	QStandardItemModel(0, NumberOfColumns, parent),
	_controller(CController::get()),
	_tree(treeView),
	_panel(UnknownPanel)
{
}

// Sets the position (left or right) of a panel that this model represents
void CFileListModel::setPanelPosition(Panel p)
{
	assert_r(_panel == UnknownPanel); // Doesn't make sense to call this method more than once
	_panel = p;
}

Panel CFileListModel::panelPosition() const
{
	return _panel;
}

QTreeView *CFileListModel::treeView() const
{
	return _tree;
}

QVariant CFileListModel::data(const QModelIndex & index, int role /*= Qt::DisplayRole*/) const
{
	if (role == Qt::ToolTipRole)
	{
		if (!index.isValid())
			return QString();

		const CFileSystemObject item = _controller.itemByHash(_panel, itemHash(index));
		return QString(item.fullName() % "\n\n" % QString::fromStdWString(CShell::toolTip(item.fullAbsolutePath().toStdWString())));
	}
	else if (role == Qt::EditRole)
	{
		return _controller.itemByHash(_panel, itemHash(index)).fullName();
	}
	else if (role == FullNameRole)
	{
		return _controller.itemByHash(_panel, itemHash(index)).fullName();
	}
	else
		return QStandardItemModel::data(index, role);
}

bool CFileListModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
	if (role == Qt::EditRole)
	{
		const qulonglong hash = itemHash(index);
		emit itemEdited(hash, value.toString());
		return false;
	}
	else
		return QStandardItemModel::setData(index, value, role);
}

Qt::ItemFlags CFileListModel::flags(const QModelIndex & idx) const
{
	const Qt::ItemFlags flags = QStandardItemModel::flags(idx);
	if (!idx.isValid())
		return flags;

	const qulonglong hash = itemHash(idx);
	const CFileSystemObject item = _controller.itemByHash(_panel, hash);
	return item.exists() ? flags | Qt::ItemIsEditable : flags;
}

bool CFileListModel::canDropMimeData(const QMimeData * data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex & /*parent*/) const
{
	return data->hasUrls();
}

QStringList CFileListModel::mimeTypes() const
{
	return QStringList("text/uri-list");
}

bool CFileListModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex & parent)
{
	if (action == Qt::IgnoreAction)
		return true;
	else if (!data->hasUrls())
		return false;

	CFileSystemObject dest = parent.isValid() ? _controller.itemByHash(_panel, itemHash(parent)) : CFileSystemObject(_controller.panel(_panel).currentDirPathNative());
	if (dest.isFile())
		dest = CFileSystemObject(dest.parentDirPath());
	assert_and_return_r(dest.exists() && dest.isDir(), false);

	const QList<QUrl> urls = data->urls();
	std::vector<CFileSystemObject> objects;
	for(const QUrl& url: urls)
		objects.emplace_back(url.toLocalFile());

	if (objects.empty())
		return false;

	if (action == Qt::CopyAction)
		return CMainWindow::get()->copyFiles(objects, dest.fullAbsolutePath());
	else if (action == Qt::MoveAction)
		return CMainWindow::get()->moveFiles(objects, dest.fullAbsolutePath());
	else
		return false;
}

QMimeData *CFileListModel::mimeData(const QModelIndexList & indexes) const
{
	QMimeData * mime = new QMimeData();
	QList<QUrl> urls;
	std::set<int> rows;
	for(const auto& idx: indexes)
	{
		if (idx.isValid() && rows.count(idx.row()) == 0)
		{
			const QString path = _controller.itemByHash(_panel, itemHash(index(idx.row(), 0))).fullAbsolutePath();
			if (!path.isEmpty())
			{
				rows.insert(idx.row());
				urls.push_back(QUrl::fromLocalFile(path));
			}
		}
	}

	mime->setUrls(urls);
	return mime;
}

qulonglong CFileListModel::itemHash(const QModelIndex & index) const
{
	QStandardItem * itm = item(index.row(), 0);
	if (!itm)
		return 0;

	bool ok = false;
	const qulonglong hash = itm->data(Qt::UserRole).toULongLong(&ok);
	assert_and_return_r(ok, 0);
	return hash;
}
<commit_msg>Fixed #108<commit_after>#include "cfilelistmodel.h"
#include "shell/cshell.h"
#include "ccontroller.h"
#include "../../../cmainwindow.h"
#include "../../columns.h"

DISABLE_COMPILER_WARNINGS
#include <QMimeData>
#include <QUrl>
RESTORE_COMPILER_WARNINGS

#include <set>

CFileListModel::CFileListModel(QTreeView * treeView, QObject *parent) :
	QStandardItemModel(0, NumberOfColumns, parent),
	_controller(CController::get()),
	_tree(treeView),
	_panel(UnknownPanel)
{
}

// Sets the position (left or right) of a panel that this model represents
void CFileListModel::setPanelPosition(Panel p)
{
	assert_r(_panel == UnknownPanel); // Doesn't make sense to call this method more than once
	_panel = p;
}

Panel CFileListModel::panelPosition() const
{
	return _panel;
}

QTreeView *CFileListModel::treeView() const
{
	return _tree;
}

QVariant CFileListModel::data(const QModelIndex & index, int role /*= Qt::DisplayRole*/) const
{
	if (role == Qt::ToolTipRole)
	{
		if (!index.isValid())
			return QString();

		const CFileSystemObject item = _controller.itemByHash(_panel, itemHash(index));
		return QString(item.fullName() % "\n\n" % QString::fromStdWString(CShell::toolTip(item.fullAbsolutePath().toStdWString())));
	}
	else if (role == Qt::EditRole)
	{
		return _controller.itemByHash(_panel, itemHash(index)).fullName();
	}
	else if (role == FullNameRole)
	{
		return _controller.itemByHash(_panel, itemHash(index)).fullName();
	}
	else
		return QStandardItemModel::data(index, role);
}

bool CFileListModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
	if (role == Qt::EditRole)
	{
		const qulonglong hash = itemHash(index);
		emit itemEdited(hash, value.toString());
		return false;
	}
	else
		return QStandardItemModel::setData(index, value, role);
}

Qt::ItemFlags CFileListModel::flags(const QModelIndex & idx) const
{
	const Qt::ItemFlags flags = QStandardItemModel::flags(idx);
	if (!idx.isValid())
		return flags;

	const qulonglong hash = itemHash(idx);
	const CFileSystemObject item = _controller.itemByHash(_panel, hash);
	return item.exists() && !item.isCdUp() ? flags | Qt::ItemIsEditable : flags;
}

bool CFileListModel::canDropMimeData(const QMimeData * data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex & /*parent*/) const
{
	return data->hasUrls();
}

QStringList CFileListModel::mimeTypes() const
{
	return QStringList("text/uri-list");
}

bool CFileListModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex & parent)
{
	if (action == Qt::IgnoreAction)
		return true;
	else if (!data->hasUrls())
		return false;

	CFileSystemObject dest = parent.isValid() ? _controller.itemByHash(_panel, itemHash(parent)) : CFileSystemObject(_controller.panel(_panel).currentDirPathNative());
	if (dest.isFile())
		dest = CFileSystemObject(dest.parentDirPath());
	assert_and_return_r(dest.exists() && dest.isDir(), false);

	const QList<QUrl> urls = data->urls();
	std::vector<CFileSystemObject> objects;
	for(const QUrl& url: urls)
		objects.emplace_back(url.toLocalFile());

	if (objects.empty())
		return false;

	if (action == Qt::CopyAction)
		return CMainWindow::get()->copyFiles(objects, dest.fullAbsolutePath());
	else if (action == Qt::MoveAction)
		return CMainWindow::get()->moveFiles(objects, dest.fullAbsolutePath());
	else
		return false;
}

QMimeData *CFileListModel::mimeData(const QModelIndexList & indexes) const
{
	QMimeData * mime = new QMimeData();
	QList<QUrl> urls;
	std::set<int> rows;
	for(const auto& idx: indexes)
	{
		if (idx.isValid() && rows.count(idx.row()) == 0)
		{
			const QString path = _controller.itemByHash(_panel, itemHash(index(idx.row(), 0))).fullAbsolutePath();
			if (!path.isEmpty())
			{
				rows.insert(idx.row());
				urls.push_back(QUrl::fromLocalFile(path));
			}
		}
	}

	mime->setUrls(urls);
	return mime;
}

qulonglong CFileListModel::itemHash(const QModelIndex & index) const
{
	QStandardItem * itm = item(index.row(), 0);
	if (!itm)
		return 0;

	bool ok = false;
	const qulonglong hash = itm->data(Qt::UserRole).toULongLong(&ok);
	assert_and_return_r(ok, 0);
	return hash;
}
<|endoftext|>


<commit_before>/*
-------------------------------------------------------------------------------------------------
The MIT License (MIT)

Copyright (c) 2014-2019 Kim Kulling

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 <cppcore/Container/TArray.h>
#include <cppcore/CPPCoreCommon.h>

#include "gtest/gtest.h"

#include <sstream>
#include <iostream>

using namespace CPPCore;

static const size_t ArraySize = 4;

static const float ArrayData[ ArraySize ] = {
	0.0f,
	1.0f,
	2.0f,
	3.0f
};

//-------------------------------------------------------------------------------------------------
///	@class		TArrayTest
///	@ingroup	UnitTest
///
///	@brief	The array tests.
//-------------------------------------------------------------------------------------------------
class TArrayTest : public testing::Test {
protected:
    void createArray( const float *pOrig, size_t numItems, TArray<float> &arrayInstance ) {
		for ( size_t i=0; i<numItems; ++i ) {
			arrayInstance.add( pOrig[ i ] );
		}
	}
};

TEST_F( TArrayTest, constructTest ) {
    TArray<float> arrayInstance;
	EXPECT_EQ( true, arrayInstance.isEmpty() );
    EXPECT_EQ( 0U, arrayInstance.size() );
    EXPECT_EQ( arrayInstance.begin(), arrayInstance.end() );
}

TEST_F( TArrayTest, constructWithSizeTest) {
    TArray<float> arrayInstance( 4 );
	EXPECT_EQ( 4u, arrayInstance.size() );
}

TEST_F( TArrayTest, addTest) {
    TArray<float> arrayInstance;
	arrayInstance.add( 0.0f );
	arrayInstance.add( 1.0f );

	EXPECT_EQ( 2u, arrayInstance.size() );
	EXPECT_EQ( 0.0f, arrayInstance[ 0 ] );
	EXPECT_EQ( 1.0f, arrayInstance[ 1 ] );
}

TEST_F( TArrayTest, addItemsTest ) {
    TArray<float> arrayInstance;
    arrayInstance.add( 0.0f );

    float data[2] = { 0, 1 };
    arrayInstance.add( data, 2 );
    EXPECT_EQ( 3u, arrayInstance.size() );
    EXPECT_EQ( 0.0f, arrayInstance[ 0 ] );
    EXPECT_EQ( 0.0f, arrayInstance[ 1 ] );
    EXPECT_EQ( 1.0f, arrayInstance[ 2 ] );

    arrayInstance.add( nullptr, 0 );
    EXPECT_EQ( 3u, arrayInstance.size() );
}

TEST_F( TArrayTest, accessTest) {
    TArray<float> arrayInstance;
	arrayInstance.add( 0.0f );
	arrayInstance.add( 1.0f );

	EXPECT_EQ( 0.0f, arrayInstance[ 0 ] );
	EXPECT_EQ( 1.0f, arrayInstance[ 1 ] );
}

TEST_F( TArrayTest, backTest ) {
    float item( 0.0f );
    TArray<float> arrayInstance;
    arrayInstance.add( 1.0f );
    item = arrayInstance.back();
    EXPECT_FLOAT_EQ( 1.0f, item );

    arrayInstance.add( 2.0f );
    item = arrayInstance.back();
    EXPECT_FLOAT_EQ( 2.0f, item );
}

struct foo {
    float m_item;
    foo() : m_item( 1.0f ) {}
};

TEST_F( TArrayTest, constBackTest ) {
    TArray<foo> fooArrayInstance;
    foo foo1;
    foo1.m_item = 1.0f;
    fooArrayInstance.add( foo1 );
    const foo &res1 = fooArrayInstance.back();
    EXPECT_FLOAT_EQ( 1.0f, res1.m_item );

    foo foo2;
    foo2.m_item = 2.0f;
    fooArrayInstance.add( foo2 );
    const foo &res2 = fooArrayInstance.back();
    EXPECT_FLOAT_EQ( 2.0f, res2.m_item );

}

TEST_F( TArrayTest, removeTest) {
    TArray<float> arrayInstance;
	createArray( ArrayData, ArraySize, arrayInstance );

	std::stringstream stream;
	static const size_t Size = 3;
	arrayInstance.remove( 1 );
	EXPECT_EQ( Size, arrayInstance.size() );
	float expectedResult[ Size ] = { 0.0f, 2.0f, 3.0f };
	bool equal = true;
	for ( size_t i=0; i<Size; ++i ) {
		if ( arrayInstance[ i ] != expectedResult[ i ] ) {
			stream << "error in index " << i << std::endl;
			equal = false;
			break;
		}
	}
    EXPECT_TRUE( equal ) << stream.str();
}

TEST_F( TArrayTest, removeItTest) {
    TArray<float> arrayInstance;
    arrayInstance.add( 1.0f );
    EXPECT_EQ( 1u, arrayInstance.size() );
    TArray<float>::Iterator it = arrayInstance.find( 1.0f );
    EXPECT_NE( arrayInstance.end(), it );

    arrayInstance.remove( it );
    EXPECT_EQ( 0u, arrayInstance.size() );
}

TEST_F( TArrayTest, removeBackTest) {
    TArray<float> arrayInstance;
	createArray( ArrayData, ArraySize, arrayInstance );

	arrayInstance.removeBack();
	EXPECT_EQ( 3u, arrayInstance.size() );
	EXPECT_EQ( 2.0f, arrayInstance[ 2 ] );
}

TEST_F( TArrayTest, resizeTest ) {
    TArray<float> arrayInstance;
	EXPECT_EQ( 0u, arrayInstance.size() );

	arrayInstance.resize( 5 );
	EXPECT_EQ( 5u, arrayInstance.size() );
}

TEST_F( TArrayTest, moveTest ) {
    TArray<float> arrayInstance;
    arrayInstance.add( 1.0f );
    arrayInstance.add( 2.0f );
    arrayInstance.add( 3.0f );
    arrayInstance.move( 1, 2 );
}

TEST_F( TArrayTest, reserveTest ) {
    TArray<float> arrayInstance;
	EXPECT_EQ( 0u, arrayInstance.capacity() );

	arrayInstance.reserve( 5 );
	EXPECT_EQ( 5u, arrayInstance.capacity() );

    static const size_t NewSize = 2000;
	arrayInstance.reserve(NewSize);
	EXPECT_EQ( NewSize, arrayInstance.capacity() );
}

TEST_F( TArrayTest, resize_with_init_Test ) {
    TArray<float> arrayInstance;
    EXPECT_EQ( 0u, arrayInstance.capacity() );

    arrayInstance.resize( 10, 1.0f );
    EXPECT_EQ( 10u, arrayInstance.size() );
    for ( size_t i = 0; i < 10; ++i ) {
        EXPECT_FLOAT_EQ( 1.0f, arrayInstance[ i ] );
    }
}

TEST_F( TArrayTest, iterateTest ) {
    TArray<float> arrayInstance;
	createArray( ArrayData, ArraySize, arrayInstance );

	size_t i( 0 );
    for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); ++it ) {
        ++i;
	}
	EXPECT_EQ( i, arrayInstance.size() );
}

TEST_F( TArrayTest, preIncIterateTest ) {
    TArray<float> arrayInstance;
	createArray( ArrayData, ArraySize, arrayInstance );

	bool ok = true;
	try {
		size_t i( 0 );
        for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); ++it ) {
            float tmp = *it;
			EXPECT_EQ( tmp, ArrayData[ i ] );
			++i;
		}
	} catch (...)	{
		ok = false;
	}
	EXPECT_TRUE( ok );
}

TEST_F( TArrayTest, postIncIterateTest ) {
	TArray<float> arrayInstance;
	createArray( ArrayData, ArraySize, arrayInstance );

	bool ok = true;
	try {
		size_t i(0);
        for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); it++ ) {
            float tmp = *it;
			EXPECT_EQ( tmp, ArrayData[ i ] );
			++i;
		}
	} catch (...) {
		ok = false;
	}
	EXPECT_TRUE( ok );
}

TEST_F( TArrayTest, findTest )	{
    TArray<float> arrayInstance;
	arrayInstance.add( 0.0f );
	arrayInstance.add( 1.0f );
	arrayInstance.add( 2.0f );
	arrayInstance.add( 3.0f );
	EXPECT_EQ( 4u, arrayInstance.size() );

    TArray<float>::Iterator it = arrayInstance.find( 1.0f );
	EXPECT_NE( it, arrayInstance.end() );
	EXPECT_EQ( *it, 1.0f );
}

TEST_F( TArrayTest, ContainerClearTest ) {
    TArray<float*> arrayInstance;
    ContainerClear( arrayInstance );
    EXPECT_TRUE( arrayInstance.isEmpty() );

    arrayInstance.add( new float( 0.0f ) );
    arrayInstance.add( new float( 1.0f ) );
    arrayInstance.add( new float( 2.0f ) );

    EXPECT_EQ( arrayInstance.size(), 3U );
    ContainerClear( arrayInstance );
    EXPECT_EQ( arrayInstance.size(), 0U );
    EXPECT_TRUE( arrayInstance.isEmpty() );
}

//---------------------------------------------------------------------------------------------
//	Crash when iterating an empty array.
//---------------------------------------------------------------------------------------------
TEST_F( TArrayTest, bug_IterateEmptyListTest )	{
    TArray<float> arrayInstance;
	bool ok = true;
	try {
        for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); ++it );
	} catch ( ... ) {
		ok = false;
	}
	EXPECT_TRUE( ok );
}

//---------------------------------------------------------------------------------------------
//	Heap corruption after grow.
//---------------------------------------------------------------------------------------------
TEST_F( TArrayTest, bug_AddHeapCorruptTest ) {
    TArray<float> arrayInstance;
	for ( size_t i=0; i<50; ++i ) {
        arrayInstance.add( ( float ) i );
	}
}
<commit_msg>Update TArrayTest.cpp<commit_after>/*
-------------------------------------------------------------------------------------------------
The MIT License (MIT)

Copyright (c) 2014-2019 Kim Kulling

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 <cppcore/Container/TArray.h>
#include <cppcore/CPPCoreCommon.h>

#include "gtest/gtest.h"

#include <sstream>
#include <iostream>

using namespace CPPCore;

static const size_t ArraySize = 4;

static const float ArrayData[ ArraySize ] = {
	0.0f,
	1.0f,
	2.0f,
	3.0f
};

//-------------------------------------------------------------------------------------------------
///	@class		TArrayTest
///	@ingroup	UnitTest
///
///	@brief	The array tests, includes showcases how to add remove and access data.
//-------------------------------------------------------------------------------------------------
class TArrayTest : public testing::Test {
protected:
    void createArray( const float *pOrig, size_t numItems, TArray<float> &arrayInstance ) {
        for ( size_t i=0; i<numItems; ++i ) {
	    arrayInstance.add( pOrig[ i ] );
	}
    }
};

TEST_F( TArrayTest, constructTest ) {
    TArray<float> arrayInstance;
    EXPECT_EQ( true, arrayInstance.isEmpty() );
    EXPECT_EQ( 0U, arrayInstance.size() );
    EXPECT_EQ( arrayInstance.begin(), arrayInstance.end() );
}

TEST_F( TArrayTest, constructWithSizeTest) {
    TArray<float> arrayInstance( 4 );
    EXPECT_EQ( 4u, arrayInstance.size() );
    for ( size_t i=0; i<4; ++i ) {
        const float f = arrayInstance[i];
	EXPECT_EQ(0, f );
    }
}

TEST_F( TArrayTest, addTest) {
    TArray<float> arrayInstance;
    arrayInstance.add( 0.0f );
    arrayInstance.add( 1.0f );

    EXPECT_EQ( 2u, arrayInstance.size() );
    EXPECT_EQ( 0.0f, arrayInstance[ 0 ] );
    EXPECT_EQ( 1.0f, arrayInstance[ 1 ] );
}

TEST_F( TArrayTest, addItemsTest ) {
    TArray<float> arrayInstance;
    arrayInstance.add( 0.0f );

    float data[2] = { 0, 1 };
    arrayInstance.add( data, 2 );
    EXPECT_EQ( 3u, arrayInstance.size() );
    EXPECT_EQ( 0.0f, arrayInstance[ 0 ] );
    EXPECT_EQ( 0.0f, arrayInstance[ 1 ] );
    EXPECT_EQ( 1.0f, arrayInstance[ 2 ] );

    arrayInstance.add( nullptr, 0 );
    EXPECT_EQ( 3u, arrayInstance.size() );
}

TEST_F( TArrayTest, accessTest) {
    TArray<float> arrayInstance;
    arrayInstance.add( 0.0f );
    arrayInstance.add( 1.0f );

    EXPECT_EQ( 0.0f, arrayInstance[ 0 ] );
    EXPECT_EQ( 1.0f, arrayInstance[ 1 ] );
}

TEST_F( TArrayTest, backTest ) {
    float item( 0.0f );
    TArray<float> arrayInstance;
    arrayInstance.add( 1.0f );
    item = arrayInstance.back();
    EXPECT_FLOAT_EQ( 1.0f, item );

    arrayInstance.add( 2.0f );
    item = arrayInstance.back();
    EXPECT_FLOAT_EQ( 2.0f, item );
}

struct foo {
    float m_item;
    foo() : m_item( 1.0f ) {}
};

TEST_F( TArrayTest, constBackTest ) {
    TArray<foo> fooArrayInstance;
    foo foo1;
    foo1.m_item = 1.0f;
    fooArrayInstance.add( foo1 );
    const foo &res1 = fooArrayInstance.back();
    EXPECT_FLOAT_EQ( 1.0f, res1.m_item );

    foo foo2;
    foo2.m_item = 2.0f;
    fooArrayInstance.add( foo2 );
    const foo &res2 = fooArrayInstance.back();
    EXPECT_FLOAT_EQ( 2.0f, res2.m_item );
}

TEST_F( TArrayTest, removeTest) {
    TArray<float> arrayInstance;
    createArray( ArrayData, ArraySize, arrayInstance );

    std::stringstream stream;
    static const size_t Size = 3;
    arrayInstance.remove( 1 );
    EXPECT_EQ( Size, arrayInstance.size() );
    float expectedResult[ Size ] = { 0.0f, 2.0f, 3.0f };
    bool equal = true;
    for ( size_t i=0; i<Size; ++i ) {
        if ( arrayInstance[ i ] != expectedResult[ i ] ) {
            stream << "error in index " << i << std::endl;
            equal = false;
            break;
        }
    }
    EXPECT_TRUE( equal ) << stream.str();
}

TEST_F( TArrayTest, removeItTest) {
    TArray<float> arrayInstance;
    arrayInstance.add( 1.0f );
    EXPECT_EQ( 1u, arrayInstance.size() );
    TArray<float>::Iterator it = arrayInstance.find( 1.0f );
    EXPECT_NE( arrayInstance.end(), it );

    arrayInstance.remove( it );
    EXPECT_EQ( 0u, arrayInstance.size() );
}

TEST_F( TArrayTest, removeBackTest) {
    TArray<float> arrayInstance;
    createArray( ArrayData, ArraySize, arrayInstance );

    arrayInstance.removeBack();
    EXPECT_EQ( 3u, arrayInstance.size() );
    EXPECT_EQ( 2.0f, arrayInstance[ 2 ] );
}

TEST_F( TArrayTest, resizeTest ) {
    TArray<float> arrayInstance;
    EXPECT_EQ( 0u, arrayInstance.size() );

    arrayInstance.resize( 5 );
    EXPECT_EQ( 5u, arrayInstance.size() );
}

TEST_F( TArrayTest, moveTest ) {
    TArray<float> arrayInstance;
    arrayInstance.add( 1.0f );
    arrayInstance.add( 2.0f );
    arrayInstance.add( 3.0f );
    arrayInstance.move( 1, 2 );
}

TEST_F( TArrayTest, reserveTest ) {
    TArray<float> arrayInstance;
    EXPECT_EQ( 0u, arrayInstance.capacity() );

    arrayInstance.reserve( 5 );
    EXPECT_EQ( 5u, arrayInstance.capacity() );

    static const size_t NewSize = 2000;
    arrayInstance.reserve(NewSize);
    EXPECT_EQ( NewSize, arrayInstance.capacity() );
}

TEST_F( TArrayTest, resize_with_init_Test ) {
    TArray<float> arrayInstance;
    EXPECT_EQ( 0u, arrayInstance.capacity() );

    arrayInstance.resize( 10, 1.0f );
    EXPECT_EQ( 10u, arrayInstance.size() );
    for ( size_t i = 0; i < 10; ++i ) {
        EXPECT_FLOAT_EQ( 1.0f, arrayInstance[ i ] );
    }
}

TEST_F( TArrayTest, iterateTest ) {
    TArray<float> arrayInstance;
    createArray( ArrayData, ArraySize, arrayInstance );

    size_t i( 0 );
    for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); ++it ) {
        ++i;
    }
    EXPECT_EQ( i, arrayInstance.size() );
}

TEST_F( TArrayTest, preIncIterateTest ) {
    TArray<float> arrayInstance;
    createArray( ArrayData, ArraySize, arrayInstance );

    bool ok = true;
    try {
 	size_t i( 0 );
        for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); ++it ) {
            float tmp = *it;
			EXPECT_EQ( tmp, ArrayData[ i ] );
			++i;
		}
	} catch (...)	{
		ok = false;
	}
	EXPECT_TRUE( ok );
}

TEST_F( TArrayTest, postIncIterateTest ) {
	TArray<float> arrayInstance;
	createArray( ArrayData, ArraySize, arrayInstance );

	bool ok = true;
	try {
		size_t i(0);
        for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); it++ ) {
            float tmp = *it;
			EXPECT_EQ( tmp, ArrayData[ i ] );
			++i;
		}
	} catch (...) {
		ok = false;
	}
	EXPECT_TRUE( ok );
}

TEST_F( TArrayTest, findTest )	{
    TArray<float> arrayInstance;
	arrayInstance.add( 0.0f );
	arrayInstance.add( 1.0f );
	arrayInstance.add( 2.0f );
	arrayInstance.add( 3.0f );
	EXPECT_EQ( 4u, arrayInstance.size() );

    TArray<float>::Iterator it = arrayInstance.find( 1.0f );
	EXPECT_NE( it, arrayInstance.end() );
	EXPECT_EQ( *it, 1.0f );
}

TEST_F( TArrayTest, ContainerClearTest ) {
    TArray<float*> arrayInstance;
    ContainerClear( arrayInstance );
    EXPECT_TRUE( arrayInstance.isEmpty() );

    arrayInstance.add( new float( 0.0f ) );
    arrayInstance.add( new float( 1.0f ) );
    arrayInstance.add( new float( 2.0f ) );

    EXPECT_EQ( arrayInstance.size(), 3U );
    ContainerClear( arrayInstance );
    EXPECT_EQ( arrayInstance.size(), 0U );
    EXPECT_TRUE( arrayInstance.isEmpty() );
}

//---------------------------------------------------------------------------------------------
//	Crash when iterating an empty array.
//---------------------------------------------------------------------------------------------
TEST_F( TArrayTest, bug_IterateEmptyListTest )	{
    TArray<float> arrayInstance;
	bool ok = true;
	try {
        for( TArray<float>::Iterator it = arrayInstance.begin( ); it != arrayInstance.end( ); ++it );
	} catch ( ... ) {
		ok = false;
	}
	EXPECT_TRUE( ok );
}

//---------------------------------------------------------------------------------------------
//	Heap corruption after grow.
//---------------------------------------------------------------------------------------------
TEST_F( TArrayTest, bug_AddHeapCorruptTest ) {
    TArray<float> arrayInstance;
	for ( size_t i=0; i<50; ++i ) {
        arrayInstance.add( ( float ) i );
	}
}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <entt/signal/delegate.hpp>

int delegate_function(const int &i) {
    return i*i;
}

int curried_function(const int *i, int j) {
    return *i+j;
}

struct delegate_functor {
    int operator()(int i) {
        return i+i;
    }

    int identity(int i) const {
        return i;
    }

    static const int static_value = 3;
    const int data_member = 42;
};

struct const_nonconst_noexcept {
    void f() { ++cnt; }
    void g() noexcept { ++cnt; }
    void h() const { ++cnt; }
    void i() const noexcept { ++cnt; }
    mutable int cnt{0};
};

TEST(Delegate, Functionalities) {
    entt::delegate<int(int)> ff_del;
    entt::delegate<int(int)> mf_del;
    delegate_functor functor;

    ASSERT_FALSE(ff_del);
    ASSERT_FALSE(mf_del);
    ASSERT_EQ(ff_del, mf_del);

    ff_del.connect<&delegate_function>();
    mf_del.connect<&delegate_functor::operator()>(&functor);

    ASSERT_TRUE(ff_del);
    ASSERT_TRUE(mf_del);

    ASSERT_EQ(ff_del(3), 9);
    ASSERT_EQ(mf_del(3), 6);

    ff_del.reset();

    ASSERT_FALSE(ff_del);
    ASSERT_TRUE(mf_del);

    ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
    ASSERT_NE(mf_del, entt::delegate<int(int)>{});
    ASSERT_NE(ff_del, mf_del);

    mf_del.reset();

    ASSERT_FALSE(ff_del);
    ASSERT_FALSE(mf_del);

    ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
    ASSERT_EQ(mf_del, entt::delegate<int(int)>{});
    ASSERT_EQ(ff_del, mf_del);
}

TEST(Delegate, DataMembers) {
    entt::delegate<double()> delegate;
    delegate_functor functor;

    delegate.connect<&delegate_functor::data_member>(&functor);

    ASSERT_EQ(delegate(), 42);
}

TEST(Delegate, Comparison) {
    entt::delegate<int(int)> lhs;
    entt::delegate<int(int)> rhs;
    delegate_functor functor;
    delegate_functor other;
    const int value = 0;

    ASSERT_EQ(lhs, entt::delegate<int(int)>{});
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&delegate_function>();

    ASSERT_EQ(lhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.connect<&delegate_function>();

    ASSERT_EQ(rhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&curried_function>(&value);

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, &value}));
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.connect<&curried_function>(&value);

    ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, &value}));
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&delegate_functor::operator()>(&functor);

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &functor}));
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.connect<&delegate_functor::operator()>(&functor);

    ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &functor}));
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&delegate_functor::operator()>(&other);

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &other}));
    ASSERT_NE(lhs.instance(), rhs.instance());
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.reset();

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{}));
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.reset();

    ASSERT_EQ(rhs, (entt::delegate<int(int)>{}));
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);
}

TEST(Delegate, ConstNonConstNoExcept) {
    entt::delegate<void()> delegate;
    const_nonconst_noexcept functor;

    delegate.connect<&const_nonconst_noexcept::f>(&functor);
    delegate();

    delegate.connect<&const_nonconst_noexcept::g>(&functor);
    delegate();

    delegate.connect<&const_nonconst_noexcept::h>(&functor);
    delegate();

    delegate.connect<&const_nonconst_noexcept::i>(&functor);
    delegate();

    ASSERT_EQ(functor.cnt, 4);
}

TEST(Delegate, DeducedGuidelines) {
    const_nonconst_noexcept functor;
    const int value = 0;

    entt::delegate func_deduced{entt::connect_arg<&delegate_function>};
    entt::delegate curried_func_deduced{entt::connect_arg<&curried_function>, &value};
    entt::delegate member_f_deduced{entt::connect_arg<&const_nonconst_noexcept::f>, &functor};
    entt::delegate member_g_deduced{entt::connect_arg<&const_nonconst_noexcept::g>, &functor};
    entt::delegate member_h_deduced{entt::connect_arg<&const_nonconst_noexcept::h>, &functor};
    entt::delegate member_i_deduced{entt::connect_arg<&const_nonconst_noexcept::i>, &functor};

    static_assert(std::is_same_v<typename decltype(func_deduced)::function_type, int(const int &)>);
    static_assert(std::is_same_v<typename decltype(curried_func_deduced)::function_type, int(int)>);
    static_assert(std::is_same_v<typename decltype(member_f_deduced)::function_type, void()>);
    static_assert(std::is_same_v<typename decltype(member_g_deduced)::function_type, void()>);
    static_assert(std::is_same_v<typename decltype(member_h_deduced)::function_type, void()>);
    static_assert(std::is_same_v<typename decltype(member_i_deduced)::function_type, void()>);

    ASSERT_TRUE(func_deduced);
    ASSERT_TRUE(curried_func_deduced);
    ASSERT_TRUE(member_f_deduced);
    ASSERT_TRUE(member_g_deduced);
    ASSERT_TRUE(member_h_deduced);
    ASSERT_TRUE(member_i_deduced);
}

TEST(Delegate, ConstInstance) {
    entt::delegate<int(int)> delegate;
    const delegate_functor functor;

    ASSERT_FALSE(delegate);

    delegate.connect<&delegate_functor::identity>(&functor);

    ASSERT_TRUE(delegate);
    ASSERT_EQ(delegate(3), 3);

    delegate.reset();

    ASSERT_FALSE(delegate);
    ASSERT_EQ(delegate, entt::delegate<int(int)>{});
}

TEST(Delegate, CurriedFunction) {
    entt::delegate<int(int)> delegate;
    const auto value = 3;

    delegate.connect<&curried_function>(&value);

    ASSERT_TRUE(delegate);
    ASSERT_EQ(delegate(1), 4);
}

TEST(Delegate, Constructors) {
    delegate_functor functor;
    const auto value = 2;

    entt::delegate<int(int)> empty{};
    entt::delegate<int(int)> func{entt::connect_arg<&delegate_function>};
    entt::delegate<int(int)> curr{entt::connect_arg<&curried_function>, &value};
    entt::delegate<int(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};

    ASSERT_FALSE(empty);

    ASSERT_TRUE(func);
    ASSERT_EQ(9, func(3));

    ASSERT_TRUE(curr);
    ASSERT_EQ(5, curr(3));

    ASSERT_TRUE(member);
    ASSERT_EQ(6, member(3));
}

TEST(Delegate, VoidVsNonVoidReturnType) {
    delegate_functor functor;

    entt::delegate<void(int)> func{entt::connect_arg<&delegate_function>};
    entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
    entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, &std::as_const(functor)};

    ASSERT_TRUE(func);
    ASSERT_TRUE(member);
    ASSERT_TRUE(cmember);
}
<commit_msg>fixed typo<commit_after>#include <gtest/gtest.h>
#include <entt/signal/delegate.hpp>

int delegate_function(const int &i) {
    return i*i;
}

int curried_function(const int *i, int j) {
    return *i+j;
}

struct delegate_functor {
    int operator()(int i) {
        return i+i;
    }

    int identity(int i) const {
        return i;
    }

    static const int static_value = 3;
    const int data_member = 42;
};

struct const_nonconst_noexcept {
    void f() { ++cnt; }
    void g() noexcept { ++cnt; }
    void h() const { ++cnt; }
    void i() const noexcept { ++cnt; }
    mutable int cnt{0};
};

TEST(Delegate, Functionalities) {
    entt::delegate<int(int)> ff_del;
    entt::delegate<int(int)> mf_del;
    delegate_functor functor;

    ASSERT_FALSE(ff_del);
    ASSERT_FALSE(mf_del);
    ASSERT_EQ(ff_del, mf_del);

    ff_del.connect<&delegate_function>();
    mf_del.connect<&delegate_functor::operator()>(&functor);

    ASSERT_TRUE(ff_del);
    ASSERT_TRUE(mf_del);

    ASSERT_EQ(ff_del(3), 9);
    ASSERT_EQ(mf_del(3), 6);

    ff_del.reset();

    ASSERT_FALSE(ff_del);
    ASSERT_TRUE(mf_del);

    ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
    ASSERT_NE(mf_del, entt::delegate<int(int)>{});
    ASSERT_NE(ff_del, mf_del);

    mf_del.reset();

    ASSERT_FALSE(ff_del);
    ASSERT_FALSE(mf_del);

    ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
    ASSERT_EQ(mf_del, entt::delegate<int(int)>{});
    ASSERT_EQ(ff_del, mf_del);
}

TEST(Delegate, DataMembers) {
    entt::delegate<double()> delegate;
    delegate_functor functor;

    delegate.connect<&delegate_functor::data_member>(&functor);

    ASSERT_EQ(delegate(), 42);
}

TEST(Delegate, Comparison) {
    entt::delegate<int(int)> lhs;
    entt::delegate<int(int)> rhs;
    delegate_functor functor;
    delegate_functor other;
    const int value = 0;

    ASSERT_EQ(lhs, entt::delegate<int(int)>{});
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&delegate_function>();

    ASSERT_EQ(lhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.connect<&delegate_function>();

    ASSERT_EQ(rhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&curried_function>(&value);

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, &value}));
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.connect<&curried_function>(&value);

    ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, &value}));
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&delegate_functor::operator()>(&functor);

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &functor}));
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.connect<&delegate_functor::operator()>(&functor);

    ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &functor}));
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.connect<&delegate_functor::operator()>(&other);

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &other}));
    ASSERT_NE(lhs.instance(), rhs.instance());
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);

    lhs.reset();

    ASSERT_EQ(lhs, (entt::delegate<int(int)>{}));
    ASSERT_TRUE(lhs != rhs);
    ASSERT_FALSE(lhs == rhs);
    ASSERT_NE(lhs, rhs);

    rhs.reset();

    ASSERT_EQ(rhs, (entt::delegate<int(int)>{}));
    ASSERT_FALSE(lhs != rhs);
    ASSERT_TRUE(lhs == rhs);
    ASSERT_EQ(lhs, rhs);
}

TEST(Delegate, ConstNonConstNoExcept) {
    entt::delegate<void()> delegate;
    const_nonconst_noexcept functor;

    delegate.connect<&const_nonconst_noexcept::f>(&functor);
    delegate();

    delegate.connect<&const_nonconst_noexcept::g>(&functor);
    delegate();

    delegate.connect<&const_nonconst_noexcept::h>(&functor);
    delegate();

    delegate.connect<&const_nonconst_noexcept::i>(&functor);
    delegate();

    ASSERT_EQ(functor.cnt, 4);
}

TEST(Delegate, DeducedGuide) {
    const_nonconst_noexcept functor;
    const int value = 0;

    entt::delegate func_deduced{entt::connect_arg<&delegate_function>};
    entt::delegate curried_func_deduced{entt::connect_arg<&curried_function>, &value};
    entt::delegate member_f_deduced{entt::connect_arg<&const_nonconst_noexcept::f>, &functor};
    entt::delegate member_g_deduced{entt::connect_arg<&const_nonconst_noexcept::g>, &functor};
    entt::delegate member_h_deduced{entt::connect_arg<&const_nonconst_noexcept::h>, &functor};
    entt::delegate member_i_deduced{entt::connect_arg<&const_nonconst_noexcept::i>, &functor};

    static_assert(std::is_same_v<typename decltype(func_deduced)::function_type, int(const int &)>);
    static_assert(std::is_same_v<typename decltype(curried_func_deduced)::function_type, int(int)>);
    static_assert(std::is_same_v<typename decltype(member_f_deduced)::function_type, void()>);
    static_assert(std::is_same_v<typename decltype(member_g_deduced)::function_type, void()>);
    static_assert(std::is_same_v<typename decltype(member_h_deduced)::function_type, void()>);
    static_assert(std::is_same_v<typename decltype(member_i_deduced)::function_type, void()>);

    ASSERT_TRUE(func_deduced);
    ASSERT_TRUE(curried_func_deduced);
    ASSERT_TRUE(member_f_deduced);
    ASSERT_TRUE(member_g_deduced);
    ASSERT_TRUE(member_h_deduced);
    ASSERT_TRUE(member_i_deduced);
}

TEST(Delegate, ConstInstance) {
    entt::delegate<int(int)> delegate;
    const delegate_functor functor;

    ASSERT_FALSE(delegate);

    delegate.connect<&delegate_functor::identity>(&functor);

    ASSERT_TRUE(delegate);
    ASSERT_EQ(delegate(3), 3);

    delegate.reset();

    ASSERT_FALSE(delegate);
    ASSERT_EQ(delegate, entt::delegate<int(int)>{});
}

TEST(Delegate, CurriedFunction) {
    entt::delegate<int(int)> delegate;
    const auto value = 3;

    delegate.connect<&curried_function>(&value);

    ASSERT_TRUE(delegate);
    ASSERT_EQ(delegate(1), 4);
}

TEST(Delegate, Constructors) {
    delegate_functor functor;
    const auto value = 2;

    entt::delegate<int(int)> empty{};
    entt::delegate<int(int)> func{entt::connect_arg<&delegate_function>};
    entt::delegate<int(int)> curr{entt::connect_arg<&curried_function>, &value};
    entt::delegate<int(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};

    ASSERT_FALSE(empty);

    ASSERT_TRUE(func);
    ASSERT_EQ(9, func(3));

    ASSERT_TRUE(curr);
    ASSERT_EQ(5, curr(3));

    ASSERT_TRUE(member);
    ASSERT_EQ(6, member(3));
}

TEST(Delegate, VoidVsNonVoidReturnType) {
    delegate_functor functor;

    entt::delegate<void(int)> func{entt::connect_arg<&delegate_function>};
    entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
    entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, &std::as_const(functor)};

    ASSERT_TRUE(func);
    ASSERT_TRUE(member);
    ASSERT_TRUE(cmember);
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
#include "test/util.h"

namespace {

const int kMaxErrorFrames = 8;
const int kMaxDroppableFrames = 8;

class ErrorResilienceTest : public ::libvpx_test::EncoderTest,
    public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
 protected:
  ErrorResilienceTest() : EncoderTest(GET_PARAM(0)),
                          psnr_(0.0),
                          nframes_(0),
                          mismatch_psnr_(0.0),
                          mismatch_nframes_(0),
                          encoding_mode_(GET_PARAM(1)) {
    Reset();
  }

  virtual ~ErrorResilienceTest() {}

  void Reset() {
    error_nframes_ = 0;
    droppable_nframes_ = 0;
  }

  virtual void SetUp() {
    InitializeConfig();
    SetMode(encoding_mode_);
  }

  virtual void BeginPassHook(unsigned int /*pass*/) {
    psnr_ = 0.0;
    nframes_ = 0;
    mismatch_psnr_ = 0.0;
    mismatch_nframes_ = 0;
  }

  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
    psnr_ += pkt->data.psnr.psnr[0];
    nframes_++;
  }

  virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video) {
    frame_flags_ &= ~(VP8_EFLAG_NO_UPD_LAST |
                      VP8_EFLAG_NO_UPD_GF |
                      VP8_EFLAG_NO_UPD_ARF);
    if (droppable_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < droppable_nframes_; ++i) {
        if (droppable_frames_[i] == video->frame()) {
          std::cout << "             Encoding droppable frame: "
                    << droppable_frames_[i] << "\n";
          frame_flags_ |= (VP8_EFLAG_NO_UPD_LAST |
                           VP8_EFLAG_NO_UPD_GF |
                           VP8_EFLAG_NO_UPD_ARF);
          return;
        }
      }
    }
  }

  double GetAveragePsnr() const {
    if (nframes_)
      return psnr_ / nframes_;
    return 0.0;
  }

  double GetAverageMismatchPsnr() const {
    if (mismatch_nframes_)
      return mismatch_psnr_ / mismatch_nframes_;
    return 0.0;
  }

  virtual bool DoDecode() const {
    if (error_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < error_nframes_; ++i) {
        if (error_frames_[i] == nframes_ - 1) {
          std::cout << "             Skipping decoding frame: "
                    << error_frames_[i] << "\n";
          return 0;
        }
      }
    }
    return 1;
  }

  virtual void MismatchHook(const vpx_image_t *img1,
                            const vpx_image_t *img2) {
    double mismatch_psnr = compute_psnr(img1, img2);
    mismatch_psnr_ += mismatch_psnr;
    ++mismatch_nframes_;
    // std::cout << "Mismatch frame psnr: " << mismatch_psnr << "\n";
  }

  void SetErrorFrames(int num, unsigned int *list) {
    if (num > kMaxErrorFrames)
      num = kMaxErrorFrames;
    else if (num < 0)
      num = 0;
    error_nframes_ = num;
    for (unsigned int i = 0; i < error_nframes_; ++i)
      error_frames_[i] = list[i];
  }

  void SetDroppableFrames(int num, unsigned int *list) {
    if (num > kMaxDroppableFrames)
      num = kMaxDroppableFrames;
    else if (num < 0)
      num = 0;
    droppable_nframes_ = num;
    for (unsigned int i = 0; i < droppable_nframes_; ++i)
      droppable_frames_[i] = list[i];
  }

  unsigned int GetMismatchFrames() {
    return mismatch_nframes_;
  }

 private:
  double psnr_;
  unsigned int nframes_;
  unsigned int error_nframes_;
  unsigned int droppable_nframes_;
  double mismatch_psnr_;
  unsigned int mismatch_nframes_;
  unsigned int error_frames_[kMaxErrorFrames];
  unsigned int droppable_frames_[kMaxDroppableFrames];
  libvpx_test::TestMode encoding_mode_;
};

TEST_P(ErrorResilienceTest, OnVersusOff) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 2000;
  cfg_.g_lag_in_frames = 10;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode OFF.
  cfg_.g_error_resilient = 0;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_off = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_off, 25.0);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_on = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_on, 25.0);

  // Test that turning on error resilient mode hurts by 10% at most.
  if (psnr_resilience_off > 0.0) {
    const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
    EXPECT_GE(psnr_ratio, 0.9);
    EXPECT_LE(psnr_ratio, 1.1);
  }
}

TEST_P(ErrorResilienceTest, DropFramesWithoutRecovery) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 500;
  // FIXME(debargha): Fix this to work for any lag.
  // Currently this test only works for lag = 0
  cfg_.g_lag_in_frames = 0;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;

  // Set an arbitrary set of error frames same as droppable frames
  unsigned int num_droppable_frames = 2;
  unsigned int droppable_frame_list[] = {5, 16};
  SetDroppableFrames(num_droppable_frames, droppable_frame_list);
  SetErrorFrames(num_droppable_frames, droppable_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that no mismatches have been found
  std::cout << "             Mismatch frames: "
            << GetMismatchFrames() << "\n";
  EXPECT_EQ(GetMismatchFrames(), (unsigned int) 0);

  // reset previously set error/droppable frames
  Reset();

#if 0
  // TODO(jkoleszar): This test is disabled for the time being as too
  // sensitive. It's not clear how to set a reasonable threshold for
  // this behavior.

  // Now set an arbitrary set of error frames that are non-droppable
  unsigned int num_error_frames = 3;
  unsigned int error_frame_list[] = {3, 10, 20};
  SetErrorFrames(num_error_frames, error_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));

  // Test that dropping an arbitrary set of inter frames does not hurt too much
  // Note the Average Mismatch PSNR is the average of the PSNR between
  // decoded frame and encoder's version of the same frame for all frames
  // with mismatch.
  const double psnr_resilience_mismatch = GetAverageMismatchPsnr();
  std::cout << "             Mismatch PSNR: "
            << psnr_resilience_mismatch << "\n";
  EXPECT_GT(psnr_resilience_mismatch, 20.0);
#endif
}

VP8_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);
VP9_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);

}  // namespace
<commit_msg>Add consective frame loss to error_resilience test.<commit_after>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
#include "test/util.h"

namespace {

const int kMaxErrorFrames = 12;
const int kMaxDroppableFrames = 12;

class ErrorResilienceTest : public ::libvpx_test::EncoderTest,
    public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
 protected:
  ErrorResilienceTest() : EncoderTest(GET_PARAM(0)),
                          psnr_(0.0),
                          nframes_(0),
                          mismatch_psnr_(0.0),
                          mismatch_nframes_(0),
                          encoding_mode_(GET_PARAM(1)) {
    Reset();
  }

  virtual ~ErrorResilienceTest() {}

  void Reset() {
    error_nframes_ = 0;
    droppable_nframes_ = 0;
  }

  virtual void SetUp() {
    InitializeConfig();
    SetMode(encoding_mode_);
  }

  virtual void BeginPassHook(unsigned int /*pass*/) {
    psnr_ = 0.0;
    nframes_ = 0;
    mismatch_psnr_ = 0.0;
    mismatch_nframes_ = 0;
  }

  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
    psnr_ += pkt->data.psnr.psnr[0];
    nframes_++;
  }

  virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video) {
    frame_flags_ &= ~(VP8_EFLAG_NO_UPD_LAST |
                      VP8_EFLAG_NO_UPD_GF |
                      VP8_EFLAG_NO_UPD_ARF);
    if (droppable_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < droppable_nframes_; ++i) {
        if (droppable_frames_[i] == video->frame()) {
          std::cout << "             Encoding droppable frame: "
                    << droppable_frames_[i] << "\n";
          frame_flags_ |= (VP8_EFLAG_NO_UPD_LAST |
                           VP8_EFLAG_NO_UPD_GF |
                           VP8_EFLAG_NO_UPD_ARF);
          return;
        }
      }
    }
  }

  double GetAveragePsnr() const {
    if (nframes_)
      return psnr_ / nframes_;
    return 0.0;
  }

  double GetAverageMismatchPsnr() const {
    if (mismatch_nframes_)
      return mismatch_psnr_ / mismatch_nframes_;
    return 0.0;
  }

  virtual bool DoDecode() const {
    if (error_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < error_nframes_; ++i) {
        if (error_frames_[i] == nframes_ - 1) {
          std::cout << "             Skipping decoding frame: "
                    << error_frames_[i] << "\n";
          return 0;
        }
      }
    }
    return 1;
  }

  virtual void MismatchHook(const vpx_image_t *img1,
                            const vpx_image_t *img2) {
    double mismatch_psnr = compute_psnr(img1, img2);
    mismatch_psnr_ += mismatch_psnr;
    ++mismatch_nframes_;
    // std::cout << "Mismatch frame psnr: " << mismatch_psnr << "\n";
  }

  void SetErrorFrames(int num, unsigned int *list) {
    if (num > kMaxErrorFrames)
      num = kMaxErrorFrames;
    else if (num < 0)
      num = 0;
    error_nframes_ = num;
    for (unsigned int i = 0; i < error_nframes_; ++i)
      error_frames_[i] = list[i];
  }

  void SetDroppableFrames(int num, unsigned int *list) {
    if (num > kMaxDroppableFrames)
      num = kMaxDroppableFrames;
    else if (num < 0)
      num = 0;
    droppable_nframes_ = num;
    for (unsigned int i = 0; i < droppable_nframes_; ++i)
      droppable_frames_[i] = list[i];
  }

  unsigned int GetMismatchFrames() {
    return mismatch_nframes_;
  }

 private:
  double psnr_;
  unsigned int nframes_;
  unsigned int error_nframes_;
  unsigned int droppable_nframes_;
  double mismatch_psnr_;
  unsigned int mismatch_nframes_;
  unsigned int error_frames_[kMaxErrorFrames];
  unsigned int droppable_frames_[kMaxDroppableFrames];
  libvpx_test::TestMode encoding_mode_;
};

TEST_P(ErrorResilienceTest, OnVersusOff) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 2000;
  cfg_.g_lag_in_frames = 10;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode OFF.
  cfg_.g_error_resilient = 0;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_off = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_off, 25.0);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_on = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_on, 25.0);

  // Test that turning on error resilient mode hurts by 10% at most.
  if (psnr_resilience_off > 0.0) {
    const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
    EXPECT_GE(psnr_ratio, 0.9);
    EXPECT_LE(psnr_ratio, 1.1);
  }
}

// Check for successful decoding and no encoder/decoder mismatch
// if we lose (i.e., drop before decoding) a set of droppable
// frames (i.e., frames that don't update any reference buffers).
// Check both isolated and consecutive loss.
TEST_P(ErrorResilienceTest, DropFramesWithoutRecovery) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 500;
  // FIXME(debargha): Fix this to work for any lag.
  // Currently this test only works for lag = 0
  cfg_.g_lag_in_frames = 0;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 40);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
  cfg_.kf_mode = VPX_KF_DISABLED;

  // Set an arbitrary set of error frames same as droppable frames.
  // In addition to isolated loss/drop, add a long consecutive series
  // (of size 9) of dropped frames.
  unsigned int num_droppable_frames = 11;
  unsigned int droppable_frame_list[] = {5, 16, 22, 23, 24, 25, 26, 27, 28,
                                         29, 30};
  SetDroppableFrames(num_droppable_frames, droppable_frame_list);
  SetErrorFrames(num_droppable_frames, droppable_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that no mismatches have been found
  std::cout << "             Mismatch frames: "
            << GetMismatchFrames() << "\n";
  EXPECT_EQ(GetMismatchFrames(), (unsigned int) 0);

  // Reset previously set of error/droppable frames.
  Reset();

#if 0
  // TODO(jkoleszar): This test is disabled for the time being as too
  // sensitive. It's not clear how to set a reasonable threshold for
  // this behavior.

  // Now set an arbitrary set of error frames that are non-droppable
  unsigned int num_error_frames = 3;
  unsigned int error_frame_list[] = {3, 10, 20};
  SetErrorFrames(num_error_frames, error_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));

  // Test that dropping an arbitrary set of inter frames does not hurt too much
  // Note the Average Mismatch PSNR is the average of the PSNR between
  // decoded frame and encoder's version of the same frame for all frames
  // with mismatch.
  const double psnr_resilience_mismatch = GetAverageMismatchPsnr();
  std::cout << "             Mismatch PSNR: "
            << psnr_resilience_mismatch << "\n";
  EXPECT_GT(psnr_resilience_mismatch, 20.0);
#endif
}

VP8_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);
VP9_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);

}  // namespace
<|endoftext|>