<commit_before>//
// Created by Moritz Hoffmann on 18/02/16.
//
#include <readdy/Simulation.h>
#include <readdy/model/Kernel.h>
#include <readdy/plugin/KernelProvider.h>
#include <readdy/model/programs/Programs.h>
#include <readdy/model/potentials/PotentialsOrder2.h>
#include <readdy/model/potentials/PotentialsOrder1.h>

using namespace readdy;

using uuid_t = boost::uuids::uuid;

struct Simulation::Impl {
    std::unique_ptr<readdy::model::Kernel> kernel;
    std::vector<std::unique_ptr<readdy::model::ObservableBase>> foo {};
    std::unordered_map<uuid_t, std::unique_ptr<readdy::model::ObservableBase>, boost::hash<uuid_t>> observables {};
    std::unordered_map<uuid_t, boost::signals2::scoped_connection, boost::hash<uuid_t>> observableConnections {};
};

double Simulation::getKBT() const {
    ensureKernelSelected();
    return pimpl->kernel->getKernelContext().getKBT();

}

void Simulation::setKBT(double kBT) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setKBT(kBT);

}

void Simulation::setBoxSize(const readdy::model::Vec3 &boxSize) {
    setBoxSize(boxSize[0], boxSize[1], boxSize[2]);
}

void Simulation::setPeriodicBoundary(std::array<bool, 3> periodic) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setPeriodicBoundary(periodic[0], periodic[1], periodic[2]);

}

Simulation::Simulation() : pimpl(std::make_unique<Simulation::Impl>()) { }

readdy::model::Vec3 Simulation::getBoxSize() const {
    ensureKernelSelected();
    return readdy::model::Vec3(pimpl->kernel->getKernelContext().getBoxSize());

}

std::array<bool, 3> Simulation::getPeriodicBoundary() const {
    ensureKernelSelected();
    return pimpl->kernel->getKernelContext().getPeriodicBoundary();
}

void Simulation::run(const readdy::model::time_step_type steps, const double timeStep) {
    ensureKernelSelected();
    {
        BOOST_LOG_TRIVIAL(debug) << "available programs: ";
        for (auto &&p : pimpl->kernel->getAvailablePrograms()) {
            BOOST_LOG_TRIVIAL(debug) << "\t" << p;
        }
    }
    pimpl->kernel->getKernelContext().setTimeStep(timeStep);
    {
        auto &&diffuseProgram = pimpl->kernel->createProgram<readdy::model::programs::DiffuseProgram>();
        auto &&updateModelProgram = pimpl->kernel->createProgram<readdy::model::programs::UpdateStateModelProgram>();
        pimpl->kernel->getKernelContext().configure();
        for (readdy::model::time_step_type &&t = 0; t < steps; ++t) {
            diffuseProgram->execute();
            updateModelProgram->configure(t, true);
            updateModelProgram->execute();
        }
    }
}

void Simulation::setKernel(const std::string &kernel) {
    if (isKernelSelected()) {
        BOOST_LOG_TRIVIAL(debug) << "replacing kernel \"" << pimpl->kernel->getName() << "\" with \"" << kernel << "\"";
    }
    pimpl->kernel = readdy::plugin::KernelProvider::getInstance().create(kernel);
}

bool Simulation::isKernelSelected() const {
    return pimpl->kernel ? true : false;
}

const std::string &Simulation::getSelectedKernelType() const {
    ensureKernelSelected();
    return pimpl->kernel->getName();
}

void Simulation::addParticle(double x, double y, double z, const std::string &type) {
    ensureKernelSelected();
    const auto &&s = getBoxSize();
    if (0 <= x && x <= s[0] && 0 <= y && y <= s[1] && 0 <= z && z <= s[2]) {
        readdy::model::Particle p{x, y, z, pimpl->kernel->getKernelContext().getParticleTypeID(type)};
        pimpl->kernel->getKernelStateModel().addParticle(p);
    } else {
        BOOST_LOG_TRIVIAL(error) << "particle position was not in bounds of the simulation box!";
    }

}

void Simulation::registerParticleType(const std::string &name, const double diffusionCoefficient, const double radius) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setDiffusionConstant(name, diffusionCoefficient);
    pimpl->kernel->getKernelContext().setParticleRadius(name, radius);
}

const std::vector<readdy::model::Vec3> Simulation::getParticlePositions() const {
    ensureKernelSelected();
    return pimpl->kernel->getKernelStateModel().getParticlePositions();
}

void Simulation::registerPotentialOrder1(readdy::model::potentials::PotentialOrder1 const* const ptr, const std::string &type) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().registerOrder1Potential(ptr, type);
}

void Simulation::deregisterPotential(const uuid_t &uuid) {
    pimpl->kernel->getKernelContext().deregisterPotential(uuid);
};

boost::uuids::uuid Simulation::registerHarmonicRepulsionPotential(std::string particleTypeA, std::string particleTypeB, double forceConstant) {
    ensureKernelSelected();
    auto ptr = pimpl->kernel->createPotentialAs<readdy::model::potentials::HarmonicRepulsion>();
    ptr->setForceConstant(forceConstant);
    return pimpl->kernel->getKernelContext().registerOrder2Potential(ptr.get(), particleTypeA, particleTypeB);
}

boost::uuids::uuid Simulation::registerBoxPotential(std::string particleType, double forceConstant, readdy::model::Vec3 origin, readdy::model::Vec3 extent, bool considerParticleRadius) {
    ensureKernelSelected();
    auto ptr = pimpl->kernel->createPotentialAs<readdy::model::potentials::CubePotential>();
    ptr->setOrigin(origin);
    ptr->setExtent(extent);
    ptr->setConsiderParticleRadius(considerParticleRadius);
    ptr->setForceConstant(forceConstant);
    return pimpl->kernel->getKernelContext().registerOrder1Potential(ptr.get(), particleType);
}

void Simulation::registerPotentialOrder2(model::potentials::PotentialOrder2 const* const ptr, const std::string &type1, const std::string &type2) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().registerOrder2Potential(ptr, type1, type2);
}

void Simulation::ensureKernelSelected() const {
    if (!isKernelSelected()) {
        throw NoKernelSelectedException("No kernel was selected!");
    }
}

void Simulation::setBoxSize(double dx, double dy, double dz) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setBoxSize(dx, dy, dz);
}

uuid_t Simulation::registerObservable(readdy::model::ObservableBase &observable) {
    ensureKernelSelected();
    boost::uuids::random_generator uuid_gen;
    auto uuid = uuid_gen();
    auto&& connection = pimpl->kernel->connectObservable(&observable);
    pimpl->observableConnections.emplace(uuid, std::move(connection));
    return uuid;
}

void Simulation::deregisterObservable(const uuid_t uuid) {
    pimpl->observableConnections.erase(uuid);
    if(pimpl->observables.find(uuid) != pimpl->observables.end()) {
        pimpl->observables.erase(uuid);
    }
}

std::vector<std::string> Simulation::getAvailableObservables() {
    ensureKernelSelected();
    // TODO compile a list of observables
    return {"hallo"};
}

Simulation &Simulation::operator=(Simulation &&rhs) = default;

Simulation::Simulation(Simulation &&rhs) = default;

Simulation::~Simulation() {
}

template<typename T, typename... Args>
uuid_t Simulation::registerObservable(std::function<void(typename T::result_t)>&& callbackFun, unsigned int stride, Args... args) {
    ensureKernelSelected();
    boost::uuids::random_generator uuid_gen;
    auto uuid = uuid_gen();
    auto && obs = pimpl->kernel->createObservable<T>(stride, std::forward<Args>(args)...);
    obs->setCallback(std::move(callbackFun));
    pimpl->observables.emplace(uuid, std::move(obs));
    auto&& connection = pimpl->kernel->connectObservable(pimpl->observables[uuid].get());
    pimpl->observableConnections.emplace(uuid, std::move(connection));
    return uuid;
}
template uuid_t Simulation::registerObservable<readdy::model::ParticlePositionObservable>(std::function<void(typename readdy::model::ParticlePositionObservable::result_t)>&&, unsigned int);

NoKernelSelectedException::NoKernelSelectedException(const std::string &__arg) : runtime_error(__arg) { };

<commit_msg>[simulation] fix mac typedef clash<commit_after>//
// Created by Moritz Hoffmann on 18/02/16.
//
#include <readdy/Simulation.h>
#include <readdy/model/Kernel.h>
#include <readdy/plugin/KernelProvider.h>
#include <readdy/model/programs/Programs.h>
#include <readdy/model/potentials/PotentialsOrder2.h>
#include <readdy/model/potentials/PotentialsOrder1.h>

using namespace readdy;

using _sim_uuid_t = boost::uuids::uuid;

struct Simulation::Impl {
    std::unique_ptr<readdy::model::Kernel> kernel;
    std::vector<std::unique_ptr<readdy::model::ObservableBase>> foo {};
    std::unordered_map<_sim_uuid_t, std::unique_ptr<readdy::model::ObservableBase>, boost::hash<_sim_uuid_t>> observables {};
    std::unordered_map<_sim_uuid_t, boost::signals2::scoped_connection, boost::hash<_sim_uuid_t>> observableConnections {};
};

double Simulation::getKBT() const {
    ensureKernelSelected();
    return pimpl->kernel->getKernelContext().getKBT();

}

void Simulation::setKBT(double kBT) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setKBT(kBT);

}

void Simulation::setBoxSize(const readdy::model::Vec3 &boxSize) {
    setBoxSize(boxSize[0], boxSize[1], boxSize[2]);
}

void Simulation::setPeriodicBoundary(std::array<bool, 3> periodic) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setPeriodicBoundary(periodic[0], periodic[1], periodic[2]);

}

Simulation::Simulation() : pimpl(std::make_unique<Simulation::Impl>()) { }

readdy::model::Vec3 Simulation::getBoxSize() const {
    ensureKernelSelected();
    return readdy::model::Vec3(pimpl->kernel->getKernelContext().getBoxSize());

}

std::array<bool, 3> Simulation::getPeriodicBoundary() const {
    ensureKernelSelected();
    return pimpl->kernel->getKernelContext().getPeriodicBoundary();
}

void Simulation::run(const readdy::model::time_step_type steps, const double timeStep) {
    ensureKernelSelected();
    {
        BOOST_LOG_TRIVIAL(debug) << "available programs: ";
        for (auto &&p : pimpl->kernel->getAvailablePrograms()) {
            BOOST_LOG_TRIVIAL(debug) << "\t" << p;
        }
    }
    pimpl->kernel->getKernelContext().setTimeStep(timeStep);
    {
        auto &&diffuseProgram = pimpl->kernel->createProgram<readdy::model::programs::DiffuseProgram>();
        auto &&updateModelProgram = pimpl->kernel->createProgram<readdy::model::programs::UpdateStateModelProgram>();
        pimpl->kernel->getKernelContext().configure();
        for (readdy::model::time_step_type &&t = 0; t < steps; ++t) {
            diffuseProgram->execute();
            updateModelProgram->configure(t, true);
            updateModelProgram->execute();
        }
    }
}

void Simulation::setKernel(const std::string &kernel) {
    if (isKernelSelected()) {
        BOOST_LOG_TRIVIAL(debug) << "replacing kernel \"" << pimpl->kernel->getName() << "\" with \"" << kernel << "\"";
    }
    pimpl->kernel = readdy::plugin::KernelProvider::getInstance().create(kernel);
}

bool Simulation::isKernelSelected() const {
    return pimpl->kernel ? true : false;
}

const std::string &Simulation::getSelectedKernelType() const {
    ensureKernelSelected();
    return pimpl->kernel->getName();
}

void Simulation::addParticle(double x, double y, double z, const std::string &type) {
    ensureKernelSelected();
    const auto &&s = getBoxSize();
    if (0 <= x && x <= s[0] && 0 <= y && y <= s[1] && 0 <= z && z <= s[2]) {
        readdy::model::Particle p{x, y, z, pimpl->kernel->getKernelContext().getParticleTypeID(type)};
        pimpl->kernel->getKernelStateModel().addParticle(p);
    } else {
        BOOST_LOG_TRIVIAL(error) << "particle position was not in bounds of the simulation box!";
    }

}

void Simulation::registerParticleType(const std::string &name, const double diffusionCoefficient, const double radius) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setDiffusionConstant(name, diffusionCoefficient);
    pimpl->kernel->getKernelContext().setParticleRadius(name, radius);
}

const std::vector<readdy::model::Vec3> Simulation::getParticlePositions() const {
    ensureKernelSelected();
    return pimpl->kernel->getKernelStateModel().getParticlePositions();
}

void Simulation::registerPotentialOrder1(readdy::model::potentials::PotentialOrder1 const* const ptr, const std::string &type) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().registerOrder1Potential(ptr, type);
}

void Simulation::deregisterPotential(const _sim_uuid_t &uuid) {
    pimpl->kernel->getKernelContext().deregisterPotential(uuid);
};

boost::uuids::uuid Simulation::registerHarmonicRepulsionPotential(std::string particleTypeA, std::string particleTypeB, double forceConstant) {
    ensureKernelSelected();
    auto ptr = pimpl->kernel->createPotentialAs<readdy::model::potentials::HarmonicRepulsion>();
    ptr->setForceConstant(forceConstant);
    return pimpl->kernel->getKernelContext().registerOrder2Potential(ptr.get(), particleTypeA, particleTypeB);
}

boost::uuids::uuid Simulation::registerBoxPotential(std::string particleType, double forceConstant, readdy::model::Vec3 origin, readdy::model::Vec3 extent, bool considerParticleRadius) {
    ensureKernelSelected();
    auto ptr = pimpl->kernel->createPotentialAs<readdy::model::potentials::CubePotential>();
    ptr->setOrigin(origin);
    ptr->setExtent(extent);
    ptr->setConsiderParticleRadius(considerParticleRadius);
    ptr->setForceConstant(forceConstant);
    return pimpl->kernel->getKernelContext().registerOrder1Potential(ptr.get(), particleType);
}

void Simulation::registerPotentialOrder2(model::potentials::PotentialOrder2 const* const ptr, const std::string &type1, const std::string &type2) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().registerOrder2Potential(ptr, type1, type2);
}

void Simulation::ensureKernelSelected() const {
    if (!isKernelSelected()) {
        throw NoKernelSelectedException("No kernel was selected!");
    }
}

void Simulation::setBoxSize(double dx, double dy, double dz) {
    ensureKernelSelected();
    pimpl->kernel->getKernelContext().setBoxSize(dx, dy, dz);
}

_sim_uuid_t Simulation::registerObservable(readdy::model::ObservableBase &observable) {
    ensureKernelSelected();
    boost::uuids::random_generator uuid_gen;
    auto uuid = uuid_gen();
    auto&& connection = pimpl->kernel->connectObservable(&observable);
    pimpl->observableConnections.emplace(uuid, std::move(connection));
    return uuid;
}

void Simulation::deregisterObservable(const _sim_uuid_t uuid) {
    pimpl->observableConnections.erase(uuid);
    if(pimpl->observables.find(uuid) != pimpl->observables.end()) {
        pimpl->observables.erase(uuid);
    }
}

std::vector<std::string> Simulation::getAvailableObservables() {
    ensureKernelSelected();
    // TODO compile a list of observables
    return {"hallo"};
}

Simulation &Simulation::operator=(Simulation &&rhs) = default;

Simulation::Simulation(Simulation &&rhs) = default;

Simulation::~Simulation() {
}

template<typename T, typename... Args>
_sim_uuid_t Simulation::registerObservable(std::function<void(typename T::result_t)>&& callbackFun, unsigned int stride, Args... args) {
    ensureKernelSelected();
    boost::uuids::random_generator uuid_gen;
    auto uuid = uuid_gen();
    auto && obs = pimpl->kernel->createObservable<T>(stride, std::forward<Args>(args)...);
    obs->setCallback(std::move(callbackFun));
    pimpl->observables.emplace(uuid, std::move(obs));
    auto&& connection = pimpl->kernel->connectObservable(pimpl->observables[uuid].get());
    pimpl->observableConnections.emplace(uuid, std::move(connection));
    return uuid;
}
template _sim_uuid_t Simulation::registerObservable<readdy::model::ParticlePositionObservable>(std::function<void(typename readdy::model::ParticlePositionObservable::result_t)>&&, unsigned int);

NoKernelSelectedException::NoKernelSelectedException(const std::string &__arg) : runtime_error(__arg) { };

<|endoftext|>


<commit_before>#include "MultiProperty.h"

#include "Config.h"
#include "PropertyConnector.h"
#include "Delegates/PropertyDelegateFactory.h"
#include "Delegates/Core/PropertyDelegateBool.h"

#include <QStyleOption>
#include <QKeyEvent>

QtnMultiProperty::QtnMultiProperty(const QMetaObject *propertyMetaObject, QObject *parent)
	: QtnProperty(parent)
	, propertyMetaObject(propertyMetaObject)
	, calculateMultipleValues(true)
	, multipleValues(false)
{

}

QtnMultiProperty::~QtnMultiProperty()
{
	for (auto property : properties)
	{
		QObject::disconnect(property, &QtnProperty::propertyValueAccept,
							this, &QtnMultiProperty::onPropertyValueAccept);
		QObject::disconnect(property, &QtnPropertyBase::propertyWillChange,
							this, &QtnMultiProperty::onPropertyWillChange);
		QObject::disconnect(property, &QtnPropertyBase::propertyDidChange,
							this, &QtnMultiProperty::onPropertyDidChange);
	}
}

void QtnMultiProperty::addProperty(QtnProperty *property, bool own)
{
	Q_ASSERT(nullptr != property);
	Q_ASSERT(nullptr != propertyMetaObject->cast(property));

	if (own)
		property->setParent(this);

	if (properties.end() != std::find(properties.begin(), properties.end(), property))
		return;

	properties.push_back(property);

	QObject::connect(property, &QtnProperty::propertyValueAccept,
					 this, &QtnMultiProperty::onPropertyValueAccept);
	QObject::connect(property, &QtnPropertyBase::propertyWillChange,
					 this, &QtnMultiProperty::onPropertyWillChange);
	QObject::connect(property, &QtnPropertyBase::propertyDidChange,
					 this, &QtnMultiProperty::onPropertyDidChange);
}

void QtnMultiProperty::resetValues()
{
	for (auto property : properties)
	{
		auto connector = property->getConnector();

		if (nullptr != connector)
			connector->resetPropertyValue();
	}

	updateMultipleState(true);
	emit propertyDidChange(QtnPropertyChangeReasonNewValue);
}

bool QtnMultiProperty::hasResettableValues() const
{
	for (auto property : properties)
	{
		auto connector = property->getConnector();

		if (nullptr != connector && connector->isResettablePropertyValue())
			return true;
	}

	return false;
}

void QtnMultiProperty::Register()
{
	qRegisterMetaType<QtnMultiVariant>();

	QtnPropertyDelegateFactory::staticInstance()
		.registerDelegateDefault(&QtnMultiProperty::staticMetaObject
		, &qtnCreateDelegate<QtnMultiPropertyDelegate, QtnMultiProperty>
		, "MultiProperty");
}

QString QtnMultiProperty::getMultiValuePlaceholder()
{
	return tr("(Multiple Values)");
}

QMetaProperty QtnMultiProperty::getMetaProperty() const
{
	Q_ASSERT(!properties.empty());

	auto connector = properties.at(0)->getConnector();
	Q_ASSERT(nullptr != connector);

	return connector->getMetaProperty();

}

bool QtnMultiProperty::hasMultipleValues() const
{
	if (calculateMultipleValues)
	{
		QString dummy;
		toStrImpl(dummy);
	}

	return multipleValues;
}

void QtnMultiProperty::onPropertyValueAccept(QtnPropertyValuePtr valueToAccept, bool *accept)
{
	emit propertyValueAccept(valueToAccept, accept);
}

void QtnMultiProperty::onPropertyWillChange(QtnPropertyChangeReason reason,
											QtnPropertyValuePtr newValue,
											int typeId)
{
	emit propertyWillChange(reason, newValue, typeId);
}

void QtnMultiProperty::onPropertyDidChange(QtnPropertyChangeReason reason)
{
	if (0 != (reason & QtnPropertyChangeReasonStateInherited)
	||	0 != (reason & QtnPropertyChangeReasonValue))
	{
		updateMultipleState(true);
	}

	emit propertyDidChange(reason);
}

bool QtnMultiProperty::loadImpl(QDataStream &stream)
{
	for (auto property : properties)
	{
		QByteArray propertyData;
		stream >> propertyData;
		QDataStream propertyStream(&propertyData, QIODevice::ReadOnly);
		if (!property->load(propertyStream))
			return false;
	}

	return true;
}

bool QtnMultiProperty::saveImpl(QDataStream &stream) const
{
	for (auto property : properties)
	{
		QByteArray propertyData;
		QDataStream propertyStream(&propertyData, QIODevice::WriteOnly);
		if (!property->save(propertyStream))
			return false;

		stream << propertyData;
	}

	return true;
}

bool QtnMultiProperty::fromStrImpl(const QString &str, bool edit)
{
	for (auto property : properties)
	{
		if (!property->fromStr(str, edit))
			return false;
	}

	return true;
}

bool QtnMultiProperty::toStrImpl(QString &str) const
{
	if (calculateMultipleValues)
	{
		auto thiz = const_cast<QtnMultiProperty *>(this);
		thiz->calculateMultipleValues = false;

		size_t sameCount = 0;
		QString temp;
		for (auto property : properties)
		{
			if (!property->toStr(str))
				str.clear();

			if (sameCount == 0)
				temp = str;

			if (sameCount == 0 || str == temp)
				sameCount++;
		}

		thiz->multipleValues = (sameCount != properties.size());
	} else if (!multipleValues)
	{
		if (properties.empty() || !properties.at(0)->toStr(str))
			str.clear();
	}

	if (multipleValues)
		str.clear();

	return true;
}

bool QtnMultiProperty::fromVariantImpl(const QVariant &var, bool edit)
{
	bool ok = true;
	if (var.type() == QVariant::UserType && var.userType() == qMetaTypeId<QtnMultiVariant>())
	{
		auto values = var.value<QtnMultiVariant>().values;
		auto count = properties.size();
		auto varCount = static_cast<size_t>(values.count());
		if (count != varCount)
			ok = false;

		if (count > varCount)
			count = varCount;

		for (size_t i = 0; i < count; i++)
		{
			QtnProperty *property = properties.at(i);
			if (!property->fromVariant(values.at(i), edit))
				ok = false;
		}
	} else
	{
		for (auto property : properties)
		{
			if (!property->fromVariant(var, edit))
				ok = false;
		}
	}

	return ok;
}

bool QtnMultiProperty::toVariantImpl(QVariant &var) const
{
	QtnMultiVariant multiVariant;

	for (auto property : properties)
	{
		if (!property->toVariant(var))
			var.clear();

		multiVariant.values.push_back(var);
	}

	var.setValue(multiVariant);

	return true;
}

void QtnMultiProperty::updateMultipleState(bool force)
{
	if (force)
		calculateMultipleValues = true;

	bool multipleValues = hasMultipleValues();
	switchState(QtnPropertyStateHiddenValue, multipleValues);
	properties.at(0)->switchState(QtnPropertyStateHiddenValue, multipleValues);
}

QtnMultiPropertyDelegate::QtnMultiPropertyDelegate(QtnMultiProperty &owner)
	: Inherited(owner)
{
	owner.updateMultipleState(true);

	int subPropertyCount = 0;
	for (auto property : owner.properties)
	{
		auto delegate = QtnPropertyDelegateFactory::staticInstance()
						.createDelegate(*property);
		superDelegates.push_back(DelegatePtr(delegate));
		subPropertyCount = delegate->subPropertyCount();
	}

	for (int i = 0; i < subPropertyCount; i++)
	{
		auto delegate = superDelegates.at(0).get();
		auto subProperty = delegate->subProperty(i);

		auto subMultiProperty = new QtnMultiProperty(subProperty->metaObject());
		subMultiProperty->setName(subProperty->name());
		subMultiProperty->setState(subProperty->stateLocal());
		subMultiProperty->setDescription(subProperty->description());
		subMultiProperty->setId(subProperty->id());

		for (auto &delegate : superDelegates)
		{
			subMultiProperty->addProperty(
				dynamic_cast<QtnProperty *>(delegate->subProperty(i)), false);
		}

		addSubProperty(subMultiProperty);
	}
}

QtnMultiPropertyDelegate::~QtnMultiPropertyDelegate()
{
	m_subProperties.clear();
}

void QtnMultiPropertyDelegate::onEditedPropertyWillChange(PropertyToEdit *data,
														  QtnPropertyChangeReason reason,
														  QtnPropertyValuePtr newValue,
														  int typeId)
{
	Q_ASSERT(nullptr != data);
	auto owner = data->owner;
	Q_ASSERT(nullptr != owner);

	emit owner->propertyWillChange(reason, newValue, typeId);
}

void QtnMultiPropertyDelegate::onEditedPropertyDidChange(PropertyToEdit *data, QtnPropertyChangeReason reason)
{
	Q_ASSERT(nullptr != data);
	auto owner = data->owner;
	Q_ASSERT(nullptr != owner);

	if (0 != (reason & QtnPropertyChangeReasonEditValue))
	{
		auto editedProperty = data->property;
		Q_ASSERT(nullptr != editedProperty);

		auto value = editedProperty->valueAsVariant();
		for (auto property : owner->properties)
		{
			if (property != editedProperty)
			{
				property->blockSignals(true);
				property->setValueAsVariant(value);
				property->blockSignals(false);
			}
		}
	}

	owner->updateMultipleState(true);

	emit owner->propertyDidChange(reason);
}

void QtnMultiPropertyDelegate::onEditedPropertyDestroyed(PropertyToEdit *data)
{
	Q_ASSERT(nullptr != data);
	data->property = nullptr;
	data->connections.clear();
}

void QtnMultiPropertyDelegate::onEditorDestroyed(PropertyToEdit *data)
{
	Q_ASSERT(nullptr != data);
	auto propertyToEdit = data->property;
	if (nullptr != propertyToEdit)
	{
		for (auto &connection : data->connections)
			QObject::disconnect(connection);
	}

	delete data;
}

bool QtnMultiPropertyDelegate::propertyValueToStr(QString &strValue) const
{
	if (owner().valueIsHidden())
	{
		strValue = QtnMultiProperty::getMultiValuePlaceholder();
		return true;
	}

	if (!superDelegates.empty())
		return superDelegates.at(0)->propertyValueToStr(strValue);

	strValue.clear();
	return false;
}

void QtnMultiPropertyDelegate::applyAttributesImpl(const QtnPropertyDelegateAttributes &attributes)
{
	for (auto &delegate : superDelegates)
	{
		delegate->applyAttributes(attributes);
	}
}

void QtnMultiPropertyDelegate::drawValueImpl(QStylePainter &painter, const QRect &rect,
											 const QStyle::State &state, bool *needTooltip) const
{
	if (owner().valueIsHidden())
	{
		Q_ASSERT(!superDelegates.empty());

		auto boolDelegate = dynamic_cast<QtnPropertyDelegateBoolCheck *>(superDelegates.at(0).get());
		if (nullptr != boolDelegate)
		{
			QStyleOptionButton opt;
			opt.rect = rect;
			opt.state = state | QStyle::State_NoChange;

			painter.drawControl(QStyle::CE_CheckBox, opt);
			return;
		}

		QPen oldPen = painter.pen();
		painter.setPen(Qt::darkGray);

		Inherited::drawValueImpl(painter, rect, state, needTooltip);
		painter.setPen(oldPen);
	} else
	if (!superDelegates.empty())
	{
		superDelegates.at(0)->drawValue(painter, rect, state, needTooltip);
	} else
	{
		Inherited::drawValueImpl(painter, rect, state, needTooltip);
	}
}

QString QtnMultiPropertyDelegate::toolTipImpl() const
{
	if (owner().valueIsHidden())
		return Inherited::toolTipImpl();

	if (!superDelegates.empty())
		return superDelegates.at(0)->toolTip();

	return QString();
}

bool QtnMultiPropertyDelegate::acceptKeyPressedForInplaceEditImpl(QKeyEvent *keyEvent) const
{
	if (!superDelegates.empty())
		return superDelegates.at(0)->acceptKeyPressedForInplaceEdit(keyEvent);

	return Inherited::acceptKeyPressedForInplaceEditImpl(keyEvent);
}

QWidget *QtnMultiPropertyDelegate::createValueEditorImpl(QWidget *parent,
														 const QRect &rect,
														 QtnInplaceInfo *inplaceInfo)
{
	if (!superDelegates.empty())
	{
		PropertyToEdit *data = nullptr;

		bool editable = owner().isEditableByUser();

		if (editable)
		{
			auto propertyToEdit = owner().properties.at(0);
			data = new PropertyToEdit;
			data->owner = &owner();
			data->property = propertyToEdit;

			using namespace std::placeholders;

			data->connections.push_back(QObject::connect(propertyToEdit, &QtnProperty::propertyWillChange,
							 std::bind(&QtnMultiPropertyDelegate::onEditedPropertyWillChange, data, _1, _2, _3)));
			data->connections.push_back(QObject::connect(propertyToEdit, &QtnProperty::propertyDidChange,
							 std::bind(&QtnMultiPropertyDelegate::onEditedPropertyDidChange, data, _1)));
			data->connections.push_back(QObject::connect(propertyToEdit, &QObject::destroyed,
							 std::bind(&QtnMultiPropertyDelegate::onEditedPropertyDestroyed, data)));
		}

		auto superDelegate = superDelegates.at(0).get();
		auto editor = superDelegate->createValueEditor(parent, rect, inplaceInfo);

		if (editable)
		{
			if (nullptr == editor)
			{
				onEditorDestroyed(data);
			} else
			{
				data->connections.push_back(QObject::connect(editor, &QObject::destroyed,
								 std::bind(&QtnMultiPropertyDelegate::onEditorDestroyed, data)));
			}
		}

		return editor;
	}

	return createValueEditorLineEdit(parent, rect, true, inplaceInfo);
}
<commit_msg>MultiProperty state update fix<commit_after>#include "MultiProperty.h"

#include "Config.h"
#include "PropertyConnector.h"
#include "Delegates/PropertyDelegateFactory.h"
#include "Delegates/Core/PropertyDelegateBool.h"

#include <QStyleOption>
#include <QKeyEvent>

QtnMultiProperty::QtnMultiProperty(const QMetaObject *propertyMetaObject, QObject *parent)
	: QtnProperty(parent)
	, propertyMetaObject(propertyMetaObject)
	, calculateMultipleValues(true)
	, multipleValues(false)
{

}

QtnMultiProperty::~QtnMultiProperty()
{
	for (auto property : properties)
	{
		QObject::disconnect(property, &QtnProperty::propertyValueAccept,
							this, &QtnMultiProperty::onPropertyValueAccept);
		QObject::disconnect(property, &QtnPropertyBase::propertyWillChange,
							this, &QtnMultiProperty::onPropertyWillChange);
		QObject::disconnect(property, &QtnPropertyBase::propertyDidChange,
							this, &QtnMultiProperty::onPropertyDidChange);
	}
}

void QtnMultiProperty::addProperty(QtnProperty *property, bool own)
{
	Q_ASSERT(nullptr != property);
	Q_ASSERT(nullptr != propertyMetaObject->cast(property));

	if (own)
		property->setParent(this);

	if (properties.end() != std::find(properties.begin(), properties.end(), property))
		return;

	properties.push_back(property);

	QObject::connect(property, &QtnProperty::propertyValueAccept,
					 this, &QtnMultiProperty::onPropertyValueAccept);
	QObject::connect(property, &QtnPropertyBase::propertyWillChange,
					 this, &QtnMultiProperty::onPropertyWillChange);
	QObject::connect(property, &QtnPropertyBase::propertyDidChange,
					 this, &QtnMultiProperty::onPropertyDidChange);
}

void QtnMultiProperty::resetValues()
{
	for (auto property : properties)
	{
		auto connector = property->getConnector();

		if (nullptr != connector)
			connector->resetPropertyValue();
	}

	updateMultipleState(true);
	emit propertyDidChange(QtnPropertyChangeReasonNewValue);
}

bool QtnMultiProperty::hasResettableValues() const
{
	for (auto property : properties)
	{
		auto connector = property->getConnector();

		if (nullptr != connector && connector->isResettablePropertyValue())
			return true;
	}

	return false;
}

void QtnMultiProperty::Register()
{
	qRegisterMetaType<QtnMultiVariant>();

	QtnPropertyDelegateFactory::staticInstance()
		.registerDelegateDefault(&QtnMultiProperty::staticMetaObject
		, &qtnCreateDelegate<QtnMultiPropertyDelegate, QtnMultiProperty>
		, "MultiProperty");
}

QString QtnMultiProperty::getMultiValuePlaceholder()
{
	return tr("(Multiple Values)");
}

QMetaProperty QtnMultiProperty::getMetaProperty() const
{
	Q_ASSERT(!properties.empty());

	auto connector = properties.at(0)->getConnector();
	Q_ASSERT(nullptr != connector);

	return connector->getMetaProperty();

}

bool QtnMultiProperty::hasMultipleValues() const
{
	if (calculateMultipleValues)
	{
		QString dummy;
		toStrImpl(dummy);
	}

	return multipleValues;
}

void QtnMultiProperty::onPropertyValueAccept(QtnPropertyValuePtr valueToAccept, bool *accept)
{
	emit propertyValueAccept(valueToAccept, accept);
}

void QtnMultiProperty::onPropertyWillChange(QtnPropertyChangeReason reason,
											QtnPropertyValuePtr newValue,
											int typeId)
{
	emit propertyWillChange(reason, newValue, typeId);
}

void QtnMultiProperty::onPropertyDidChange(QtnPropertyChangeReason reason)
{
	if (0 != (reason & (QtnPropertyChangeReasonState | QtnPropertyChangeReasonValue)))
	{
		updateMultipleState(true);
	}

	emit propertyDidChange(reason);
}

bool QtnMultiProperty::loadImpl(QDataStream &stream)
{
	for (auto property : properties)
	{
		QByteArray propertyData;
		stream >> propertyData;
		QDataStream propertyStream(&propertyData, QIODevice::ReadOnly);
		if (!property->load(propertyStream))
			return false;
	}

	return true;
}

bool QtnMultiProperty::saveImpl(QDataStream &stream) const
{
	for (auto property : properties)
	{
		QByteArray propertyData;
		QDataStream propertyStream(&propertyData, QIODevice::WriteOnly);
		if (!property->save(propertyStream))
			return false;

		stream << propertyData;
	}

	return true;
}

bool QtnMultiProperty::fromStrImpl(const QString &str, bool edit)
{
	for (auto property : properties)
	{
		if (!property->fromStr(str, edit))
			return false;
	}

	return true;
}

bool QtnMultiProperty::toStrImpl(QString &str) const
{
	if (calculateMultipleValues)
	{
		auto thiz = const_cast<QtnMultiProperty *>(this);
		thiz->calculateMultipleValues = false;

		size_t sameCount = 0;
		QString temp;
		for (auto property : properties)
		{
			if (!property->toStr(str))
				str.clear();

			if (sameCount == 0)
				temp = str;

			if (sameCount == 0 || str == temp)
				sameCount++;
		}

		thiz->multipleValues = (sameCount != properties.size());
	} else if (!multipleValues)
	{
		if (properties.empty() || !properties.at(0)->toStr(str))
			str.clear();
	}

	if (multipleValues)
		str.clear();

	return true;
}

bool QtnMultiProperty::fromVariantImpl(const QVariant &var, bool edit)
{
	bool ok = true;
	if (var.type() == QVariant::UserType && var.userType() == qMetaTypeId<QtnMultiVariant>())
	{
		auto values = var.value<QtnMultiVariant>().values;
		auto count = properties.size();
		auto varCount = static_cast<size_t>(values.count());
		if (count != varCount)
			ok = false;

		if (count > varCount)
			count = varCount;

		for (size_t i = 0; i < count; i++)
		{
			QtnProperty *property = properties.at(i);
			if (!property->fromVariant(values.at(i), edit))
				ok = false;
		}
	} else
	{
		for (auto property : properties)
		{
			if (!property->fromVariant(var, edit))
				ok = false;
		}
	}

	return ok;
}

bool QtnMultiProperty::toVariantImpl(QVariant &var) const
{
	QtnMultiVariant multiVariant;

	for (auto property : properties)
	{
		if (!property->toVariant(var))
			var.clear();

		multiVariant.values.push_back(var);
	}

	var.setValue(multiVariant);

	return true;
}

void QtnMultiProperty::updateMultipleState(bool force)
{
	if (force)
		calculateMultipleValues = true;

	bool multipleValues = hasMultipleValues();
	switchState(QtnPropertyStateHiddenValue, multipleValues);
	properties.at(0)->switchState(QtnPropertyStateHiddenValue, multipleValues);
}

QtnMultiPropertyDelegate::QtnMultiPropertyDelegate(QtnMultiProperty &owner)
	: Inherited(owner)
{
	owner.updateMultipleState(true);

	int subPropertyCount = 0;
	for (auto property : owner.properties)
	{
		auto delegate = QtnPropertyDelegateFactory::staticInstance()
						.createDelegate(*property);
		superDelegates.push_back(DelegatePtr(delegate));
		subPropertyCount = delegate->subPropertyCount();
	}

	for (int i = 0; i < subPropertyCount; i++)
	{
		auto delegate = superDelegates.at(0).get();
		auto subProperty = delegate->subProperty(i);

		auto subMultiProperty = new QtnMultiProperty(subProperty->metaObject());
		subMultiProperty->setName(subProperty->name());
		subMultiProperty->setState(subProperty->stateLocal());
		subMultiProperty->setDescription(subProperty->description());
		subMultiProperty->setId(subProperty->id());

		for (auto &delegate : superDelegates)
		{
			subMultiProperty->addProperty(
				dynamic_cast<QtnProperty *>(delegate->subProperty(i)), false);
		}

		addSubProperty(subMultiProperty);
	}
}

QtnMultiPropertyDelegate::~QtnMultiPropertyDelegate()
{
	m_subProperties.clear();
}

void QtnMultiPropertyDelegate::onEditedPropertyWillChange(PropertyToEdit *data,
														  QtnPropertyChangeReason reason,
														  QtnPropertyValuePtr newValue,
														  int typeId)
{
	Q_ASSERT(nullptr != data);
	auto owner = data->owner;
	Q_ASSERT(nullptr != owner);

	emit owner->propertyWillChange(reason, newValue, typeId);
}

void QtnMultiPropertyDelegate::onEditedPropertyDidChange(PropertyToEdit *data, QtnPropertyChangeReason reason)
{
	Q_ASSERT(nullptr != data);
	auto owner = data->owner;
	Q_ASSERT(nullptr != owner);

	if (0 != (reason & QtnPropertyChangeReasonEditValue))
	{
		auto editedProperty = data->property;
		Q_ASSERT(nullptr != editedProperty);

		auto value = editedProperty->valueAsVariant();
		for (auto property : owner->properties)
		{
			if (property != editedProperty)
			{
				property->blockSignals(true);
				property->setValueAsVariant(value);
				property->blockSignals(false);
			}
		}
	}

	owner->updateMultipleState(true);

	emit owner->propertyDidChange(reason);
}

void QtnMultiPropertyDelegate::onEditedPropertyDestroyed(PropertyToEdit *data)
{
	Q_ASSERT(nullptr != data);
	data->property = nullptr;
	data->connections.clear();
}

void QtnMultiPropertyDelegate::onEditorDestroyed(PropertyToEdit *data)
{
	Q_ASSERT(nullptr != data);
	auto propertyToEdit = data->property;
	if (nullptr != propertyToEdit)
	{
		for (auto &connection : data->connections)
			QObject::disconnect(connection);
	}

	delete data;
}

bool QtnMultiPropertyDelegate::propertyValueToStr(QString &strValue) const
{
	if (owner().valueIsHidden())
	{
		strValue = QtnMultiProperty::getMultiValuePlaceholder();
		return true;
	}

	if (!superDelegates.empty())
		return superDelegates.at(0)->propertyValueToStr(strValue);

	strValue.clear();
	return false;
}

void QtnMultiPropertyDelegate::applyAttributesImpl(const QtnPropertyDelegateAttributes &attributes)
{
	for (auto &delegate : superDelegates)
	{
		delegate->applyAttributes(attributes);
	}
}

void QtnMultiPropertyDelegate::drawValueImpl(QStylePainter &painter, const QRect &rect,
											 const QStyle::State &state, bool *needTooltip) const
{
	if (owner().valueIsHidden())
	{
		Q_ASSERT(!superDelegates.empty());

		auto boolDelegate = dynamic_cast<QtnPropertyDelegateBoolCheck *>(superDelegates.at(0).get());
		if (nullptr != boolDelegate)
		{
			QStyleOptionButton opt;
			opt.rect = rect;
			opt.state = state | QStyle::State_NoChange;

			painter.drawControl(QStyle::CE_CheckBox, opt);
			return;
		}

		QPen oldPen = painter.pen();
		painter.setPen(Qt::darkGray);

		Inherited::drawValueImpl(painter, rect, state, needTooltip);
		painter.setPen(oldPen);
	} else
	if (!superDelegates.empty())
	{
		superDelegates.at(0)->drawValue(painter, rect, state, needTooltip);
	} else
	{
		Inherited::drawValueImpl(painter, rect, state, needTooltip);
	}
}

QString QtnMultiPropertyDelegate::toolTipImpl() const
{
	if (owner().valueIsHidden())
		return Inherited::toolTipImpl();

	if (!superDelegates.empty())
		return superDelegates.at(0)->toolTip();

	return QString();
}

bool QtnMultiPropertyDelegate::acceptKeyPressedForInplaceEditImpl(QKeyEvent *keyEvent) const
{
	if (!superDelegates.empty())
		return superDelegates.at(0)->acceptKeyPressedForInplaceEdit(keyEvent);

	return Inherited::acceptKeyPressedForInplaceEditImpl(keyEvent);
}

QWidget *QtnMultiPropertyDelegate::createValueEditorImpl(QWidget *parent,
														 const QRect &rect,
														 QtnInplaceInfo *inplaceInfo)
{
	if (!superDelegates.empty())
	{
		PropertyToEdit *data = nullptr;

		bool editable = owner().isEditableByUser();

		if (editable)
		{
			auto propertyToEdit = owner().properties.at(0);
			data = new PropertyToEdit;
			data->owner = &owner();
			data->property = propertyToEdit;

			using namespace std::placeholders;

			data->connections.push_back(QObject::connect(propertyToEdit, &QtnProperty::propertyWillChange,
							 std::bind(&QtnMultiPropertyDelegate::onEditedPropertyWillChange, data, _1, _2, _3)));
			data->connections.push_back(QObject::connect(propertyToEdit, &QtnProperty::propertyDidChange,
							 std::bind(&QtnMultiPropertyDelegate::onEditedPropertyDidChange, data, _1)));
			data->connections.push_back(QObject::connect(propertyToEdit, &QObject::destroyed,
							 std::bind(&QtnMultiPropertyDelegate::onEditedPropertyDestroyed, data)));
		}

		auto superDelegate = superDelegates.at(0).get();
		auto editor = superDelegate->createValueEditor(parent, rect, inplaceInfo);

		if (editable)
		{
			if (nullptr == editor)
			{
				onEditorDestroyed(data);
			} else
			{
				data->connections.push_back(QObject::connect(editor, &QObject::destroyed,
								 std::bind(&QtnMultiPropertyDelegate::onEditorDestroyed, data)));
			}
		}

		return editor;
	}

	return createValueEditorLineEdit(parent, rect, true, inplaceInfo);
}
<|endoftext|>


<commit_before>#include <im_str/im_str.hpp>

#include "include_catch.hpp"

#include <memory>
#include <memory_resource>
#include <string>
#include <vector>

namespace {

// Test allocator for debugging purposes - DO NOT USE IN PRODUCTION
struct TestAlloc : std::pmr::memory_resource {
	static constexpr std::size_t chunk_size = 1024;

	struct Alloc {
		void*       data;
		std::size_t start;
		std::size_t size;
	};

	std::vector<Alloc>                   allocs;
	std::vector<Alloc>                   all_allocs;
	std::vector<std::unique_ptr<char[]>> data;

	std::size_t next = 0;

	static std::size_t roundUp( std::size_t numToRound, std::size_t multiple )
	{
		if( multiple == 0 ) return numToRound;

		std::size_t remainder = numToRound % multiple;
		if( remainder == 0 ) return numToRound;

		return numToRound + multiple - remainder;
	}

	void* do_allocate( std::size_t bytes, std::size_t alignment ) override
	{
		if( bytes > chunk_size ) { throw std::bad_alloc{}; }

		const auto start = roundUp( next, alignment );

		auto chunk  = start / chunk_size;
		auto offset = start % chunk_size;

		if( ( offset + bytes ) > chunk_size ) {
			chunk++;
			offset = 0;
		}
		while( chunk >= data.size() ) {
			data.push_back( std::make_unique<char[]>( chunk_size ) );
		}

		void* ptr = data[chunk].get() + offset;
		Alloc alloc{ ptr, chunk * chunk_size + offset, bytes };
		allocs.push_back( alloc );
		all_allocs.push_back( alloc );

		next = chunk * chunk_size + offset + bytes;

		return ptr;
	}
	void do_deallocate( void* p, std::size_t bytes, std::size_t /*alignment*/ ) override
	{
		auto pos = std::find_if( allocs.begin(), allocs.end(), [p]( const Alloc& a ) { return a.data == p; } );
		assert( pos != allocs.end() );
		assert( pos->size == bytes );

		// NOTE: for this test allocator, the memory isn't actually reclaimed
		allocs.erase( pos );
	}
	bool do_is_equal( const std::pmr::memory_resource& other ) const noexcept override { return this == &other; }
};

TestAlloc alloc;
} // namespace

TEST_CASE( "custom_alloc", "[im_str]" )
{

	{ 	// construction from string causes single allocation
		std::string s{ "Hello World" };
		CHECK( alloc.allocs.size() == 0 );
		mba::im_zstr str( s, &alloc );
		CHECK( alloc.allocs.size() == 1 );

		// move construction keeps allocation the same
		mba::im_zstr str2 = std::move( str );
		CHECK( alloc.allocs.size() == 1 );
		CHECK( str2[1] == 'e' );

		// second construction causes second allocation
		mba::im_zstr str3( std::string_view("Hello World3"), &alloc );
		CHECK( alloc.allocs.size() == 2 );

		// move assignment causes deallocation of original memory
		str3 = str2;
		CHECK( alloc.allocs.size() == 1 );

		// explicit construction without allocation allocation
		mba::im_zstr str4( std::string_view( "Hello World3" ), mba::im_str::trust_me_this_is_from_a_string_litteral );
		CHECK( alloc.allocs.size() == 1 );
	}

	// after all strings have been destructed, all memory is deallocated
	CHECK( alloc.allocs.size() == 0 );

	// in total there where two allocations happening
	CHECK( alloc.all_allocs.size() == 2 );
}
<commit_msg>[Tests] Disable alloc test on unsupported toolchains<commit_after>#include <im_str/im_str.hpp>

#include "include_catch.hpp"

#if IM_STR_USE_ALLOC

#include <memory>
#include <memory_resource>
#include <string>
#include <vector>

namespace {

// Test allocator for debugging purposes - DO NOT USE IN PRODUCTION
struct TestAlloc : std::pmr::memory_resource {
	static constexpr std::size_t chunk_size = 1024;

	struct Alloc {
		void*       data;
		std::size_t start;
		std::size_t size;
	};

	std::vector<Alloc>                   allocs;
	std::vector<Alloc>                   all_allocs;
	std::vector<std::unique_ptr<char[]>> data;

	std::size_t next = 0;

	static std::size_t roundUp( std::size_t numToRound, std::size_t multiple )
	{
		if( multiple == 0 ) return numToRound;

		std::size_t remainder = numToRound % multiple;
		if( remainder == 0 ) return numToRound;

		return numToRound + multiple - remainder;
	}

	void* do_allocate( std::size_t bytes, std::size_t alignment ) override
	{
		if( bytes > chunk_size ) { throw std::bad_alloc{}; }

		const auto start = roundUp( next, alignment );

		auto chunk  = start / chunk_size;
		auto offset = start % chunk_size;

		if( ( offset + bytes ) > chunk_size ) {
			chunk++;
			offset = 0;
		}
		while( chunk >= data.size() ) {
			data.push_back( std::make_unique<char[]>( chunk_size ) );
		}

		void* ptr = data[chunk].get() + offset;
		Alloc alloc{ ptr, chunk * chunk_size + offset, bytes };
		allocs.push_back( alloc );
		all_allocs.push_back( alloc );

		next = chunk * chunk_size + offset + bytes;

		return ptr;
	}
	void do_deallocate( void* p, std::size_t bytes, std::size_t /*alignment*/ ) override
	{
		auto pos = std::find_if( allocs.begin(), allocs.end(), [p]( const Alloc& a ) { return a.data == p; } );
		assert( pos != allocs.end() );
		assert( pos->size == bytes );

		// NOTE: for this test allocator, the memory isn't actually reclaimed
		allocs.erase( pos );
	}
	bool do_is_equal( const std::pmr::memory_resource& other ) const noexcept override { return this == &other; }
};

TestAlloc alloc;
} // namespace

#endif

TEST_CASE( "custom_alloc", "[im_str]" )
{
#if IM_STR_USE_ALLOC
	{ 	// construction from string causes single allocation
		std::string s{ "Hello World" };
		CHECK( alloc.allocs.size() == 0 );
		mba::im_zstr str( s, &alloc );
		CHECK( alloc.allocs.size() == 1 );

		// move construction keeps allocation the same
		mba::im_zstr str2 = std::move( str );
		CHECK( alloc.allocs.size() == 1 );
		CHECK( str2[1] == 'e' );

		// second construction causes second allocation
		mba::im_zstr str3( std::string_view("Hello World3"), &alloc );
		CHECK( alloc.allocs.size() == 2 );

		// move assignment causes deallocation of original memory
		str3 = str2;
		CHECK( alloc.allocs.size() == 1 );

		// explicit construction without allocation allocation
		mba::im_zstr str4( std::string_view( "Hello World3" ), mba::im_str::trust_me_this_is_from_a_string_litteral );
		CHECK( alloc.allocs.size() == 1 );
	}

	// after all strings have been destructed, all memory is deallocated
	CHECK( alloc.allocs.size() == 0 );

	// in total there where two allocations happening
	CHECK( alloc.all_allocs.size() == 2 );
#endif
}
<|endoftext|>


<commit_before>#include <reflection/reflection.h>
#include <smartref/smartref.h>

// #include <vector>

namespace tests_containers {

// template <typename F>
// class reflect_member_function : public reflection::reflect_base<reflection::reflected_kind::member_function> {
// private:
//     template <typename... ExplicitArgs, typename... Args>
//     decltype(auto) indirect(Args&&... args)
//     {
//         auto f = [](auto& obj, auto&&... args) { if constexpr (sizeof...(ExplicitArgs) == 0) return obj.operator=(std::forward<Args>(args)...); else if constexpr (utils::always_true<Args...>) return obj.template operator=<ExplicitArgs...>( std::forward<Args>(args)...); };
//         return F{}(*this, f, std::forward<Args>(args)...);
//     }
// public:
//     template <typename... ExplicitArgs, typename... Args>
//     auto operator=(Args&&... args) -> decltype(indirect<ExplicitArgs...>(std::forward<Args>(args)...)) { return indirect<ExplicitArgs...>(std::forward<Args>(args)...); }
// };
// template<typename T>
struct Ref : smartref::reflect_member_function<Ref>
// struct Ref : smartref::using_<T>
{
  int ref;

  operator int &()
  {
    return ref;
  }

  // Ref() = default;
  // Ref(int arg) : ref{arg} {}

  using Base = smartref::reflect_member_function<Ref>;
  using Base::operator=;

  // int &operator=(int arg)
  // {
  //   return ref = arg;
  // }

  // using smartref::using_<T>::using_;
  // using smartref::using_<T>::operator=;

  // Ref() = default;

  // Ref(const Ref &) = default;
  Ref &operator=(const Ref &) = default;
  // Ref(Ref &&) = default;
  // Ref &operator=(Ref &&) = default;
};

// struct uiop {
//   auto operator=(int) {}
// };
// struct qwerty : uiop {
//   using uiop::operator=;
// };
template<typename T>
auto test = []{
  T u;
  // T v{};
  // T w = {};
  // T x = 0;
  // T y = {0};

  u = 0;
  // u = v;
  // u = v = 0;
  // u = v = w;
  // u = (v = 0);
  // u = (v = w);
  // (u = v) = 0;
  // (u = v) = w;

  // TODO: test both initialized ({} and assignment) and uninitialized construction
  // Ref<int> x = 4;
  // Ref<int> x;
  // Ref<int> x{};
  // qwerty x;
  // x = 7;
};

auto test_int = test<int>;
auto test_ref = test<Ref>;

// using T = int;
// using T = std::vector<int>;

// using reflection::reflect;

// //! Member-types
// constexpr auto is_valid = [](auto expression)
// {
//   // auto expected = reflect<decltype(expression(std::declval<    T >(), std::declval<    T >()))>;
//   auto actual   = reflect<decltype(expression(std::declval<Ref<T>>(), std::declval<    T >()))>;

//   return true;
//   // return actual == expected;
// };

// #define IS_VALID(expression) is_valid([](auto &&_, auto &&__) {return expression;})

// TODO:
// - constructing a smartref (e.g. Ref<int> x;)

// static_assert(reflect<Ref<T>::value_type>             == reflect<T::value_type>);
// static_assert(reflect<Ref<T>::allocator_type>         == reflect<T::allocator_type>);
// static_assert(reflect<Ref<T>::size_type>              == reflect<T::size_type>);
// static_assert(reflect<Ref<T>::difference_type>        == reflect<T::difference_type>);
// static_assert(reflect<Ref<T>::reference>              == reflect<T::reference>);
// static_assert(reflect<Ref<T>::const_reference>        == reflect<T::const_reference>);
// static_assert(reflect<Ref<T>::pointer>                == reflect<T::pointer>);
// static_assert(reflect<Ref<T>::const_pointer>          == reflect<T::const_pointer>);
// static_assert(reflect<Ref<T>::iterator>               == reflect<T::iterator>);
// static_assert(reflect<Ref<T>::const_iterator>         == reflect<T::const_iterator>);
// static_assert(reflect<Ref<T>::reverse_iterator>       == reflect<T::reverse_iterator>);
// static_assert(reflect<Ref<T>::const_reverse_iterator> == reflect<T::const_reverse_iterator>);

// //! Member functions
// // TODO: Test all overloads
// // TODO: (constructor)
// // TODO: Why does operator= work? What does it do?
// // static_assert(IS_VALID(_.operator=(_)));
// // static_assert(IS_VALID(_.operator=(__)));
// static_assert(IS_VALID(_ = __));
// static_assert(IS_VALID(_.assign(0, 0)));
// static_assert(IS_VALID(_.assign(begin(_), end(_))));
// static_assert(IS_VALID(_.get_allocator()));
// //! Element access
// static_assert(IS_VALID(_.at(T::size_type{})));
// static_assert(IS_VALID(_.operator[](0)));
// static_assert(IS_VALID(_[0]));
// static_assert(IS_VALID(_.front()));
// static_assert(IS_VALID(_.back()));
// static_assert(IS_VALID(_.data()));

// //! Iterators
// static_assert(IS_VALID(_.begin()));
// static_assert(IS_VALID(_.cbegin()));
// static_assert(IS_VALID(_.end()));
// static_assert(IS_VALID(_.cend()));
// static_assert(IS_VALID(_.rbegin()));
// static_assert(IS_VALID(_.crbegin()));
// static_assert(IS_VALID(_.rend()));
// static_assert(IS_VALID(_.crend()));

// //! Capacity
// static_assert(IS_VALID(_.empty()));
// static_assert(IS_VALID(_.size()));
// static_assert(IS_VALID(_.max_size()));
// static_assert(IS_VALID(_.reserve(T::size_type{})));
// static_assert(IS_VALID(_.capacity()));
// static_assert(IS_VALID(_.shrink_to_fit()));

// //! Modifiers
// static_assert(IS_VALID(_.clear()));
// static_assert(IS_VALID(_.insert(begin(_), 0)));
// static_assert(IS_VALID(_.emplace(begin(_), 0)));
// static_assert(IS_VALID(_.template emplace<int>(begin(_), 0)));
// static_assert(IS_VALID(_.erase(begin(_))));
// static_assert(IS_VALID(_.push_back(0)));
// static_assert(IS_VALID(_.emplace_back(0)));
// static_assert(IS_VALID(_.template emplace_back<int>(0)));
// static_assert(IS_VALID(_.pop_back()));
// static_assert(IS_VALID(_.resize(T::size_type{})));
// static_assert(IS_VALID(_.swap(_)));

// //! Non-member functions
// // static_assert(IS_VALID(_ == _);
// // TODO: operator!=
// // TODO: operator<
// // TODO: operator<=
// // TODO: operator>
// // TODO: operator>=
// // TODO: std::swap

// // TODO: Deduction guides

} // namespace tests_containers
<commit_msg>Re-enabled previously failing unit-test<commit_after>#include <reflection/reflection.h>
#include <smartref/smartref.h>

// #include <vector>

namespace tests_containers {

// template <typename F>
// class reflect_member_function : public reflection::reflect_base<reflection::reflected_kind::member_function> {
// private:
//     template <typename... ExplicitArgs, typename... Args>
//     decltype(auto) indirect(Args&&... args)
//     {
//         auto f = [](auto& obj, auto&&... args) { if constexpr (sizeof...(ExplicitArgs) == 0) return obj.operator=(std::forward<Args>(args)...); else if constexpr (utils::always_true<Args...>) return obj.template operator=<ExplicitArgs...>( std::forward<Args>(args)...); };
//         return F{}(*this, f, std::forward<Args>(args)...);
//     }
// public:
//     template <typename... ExplicitArgs, typename... Args>
//     auto operator=(Args&&... args) -> decltype(indirect<ExplicitArgs...>(std::forward<Args>(args)...)) { return indirect<ExplicitArgs...>(std::forward<Args>(args)...); }
// };
// template<typename T>
struct Ref : smartref::reflect_member_function<Ref>
// struct Ref : smartref::using_<T>
{
  int ref;

  operator int &()
  {
    return ref;
  }

  // Ref() = default;
  // Ref(int arg) : ref{arg} {}

  using Base = smartref::reflect_member_function<Ref>;
  using Base::operator=;

  // int &operator=(int arg)
  // {
  //   return ref = arg;
  // }

  // using smartref::using_<T>::using_;
  // using smartref::using_<T>::operator=;

  // Ref() = default;

  // Ref(const Ref &) = default;
  Ref &operator=(const Ref &) = default;
  // Ref(Ref &&) = default;
  // Ref &operator=(Ref &&) = default;
};

// struct uiop {
//   auto operator=(int) {}
// };
// struct qwerty : uiop {
//   using uiop::operator=;
// };
template<typename T>
auto test = []{
  T u;
  T v{};
  T w = {};
  // T x = 0;
  // T y = {0};

  u = 0;
  // u = v;
  // u = v = 0;
  // u = v = w;
  // u = (v = 0);
  // u = (v = w);
  // (u = v) = 0;
  // (u = v) = w;

  // TODO: test both initialized ({} and assignment) and uninitialized construction
  // Ref<int> x = 4;
  // Ref<int> x;
  // Ref<int> x{};
  // qwerty x;
  // x = 7;
};

auto test_int = test<int>;
auto test_ref = test<Ref>;

// using T = int;
// using T = std::vector<int>;

// using reflection::reflect;

// //! Member-types
// constexpr auto is_valid = [](auto expression)
// {
//   // auto expected = reflect<decltype(expression(std::declval<    T >(), std::declval<    T >()))>;
//   auto actual   = reflect<decltype(expression(std::declval<Ref<T>>(), std::declval<    T >()))>;

//   return true;
//   // return actual == expected;
// };

// #define IS_VALID(expression) is_valid([](auto &&_, auto &&__) {return expression;})

// TODO:
// - constructing a smartref (e.g. Ref<int> x;)

// static_assert(reflect<Ref<T>::value_type>             == reflect<T::value_type>);
// static_assert(reflect<Ref<T>::allocator_type>         == reflect<T::allocator_type>);
// static_assert(reflect<Ref<T>::size_type>              == reflect<T::size_type>);
// static_assert(reflect<Ref<T>::difference_type>        == reflect<T::difference_type>);
// static_assert(reflect<Ref<T>::reference>              == reflect<T::reference>);
// static_assert(reflect<Ref<T>::const_reference>        == reflect<T::const_reference>);
// static_assert(reflect<Ref<T>::pointer>                == reflect<T::pointer>);
// static_assert(reflect<Ref<T>::const_pointer>          == reflect<T::const_pointer>);
// static_assert(reflect<Ref<T>::iterator>               == reflect<T::iterator>);
// static_assert(reflect<Ref<T>::const_iterator>         == reflect<T::const_iterator>);
// static_assert(reflect<Ref<T>::reverse_iterator>       == reflect<T::reverse_iterator>);
// static_assert(reflect<Ref<T>::const_reverse_iterator> == reflect<T::const_reverse_iterator>);

// //! Member functions
// // TODO: Test all overloads
// // TODO: (constructor)
// // TODO: Why does operator= work? What does it do?
// // static_assert(IS_VALID(_.operator=(_)));
// // static_assert(IS_VALID(_.operator=(__)));
// static_assert(IS_VALID(_ = __));
// static_assert(IS_VALID(_.assign(0, 0)));
// static_assert(IS_VALID(_.assign(begin(_), end(_))));
// static_assert(IS_VALID(_.get_allocator()));
// //! Element access
// static_assert(IS_VALID(_.at(T::size_type{})));
// static_assert(IS_VALID(_.operator[](0)));
// static_assert(IS_VALID(_[0]));
// static_assert(IS_VALID(_.front()));
// static_assert(IS_VALID(_.back()));
// static_assert(IS_VALID(_.data()));

// //! Iterators
// static_assert(IS_VALID(_.begin()));
// static_assert(IS_VALID(_.cbegin()));
// static_assert(IS_VALID(_.end()));
// static_assert(IS_VALID(_.cend()));
// static_assert(IS_VALID(_.rbegin()));
// static_assert(IS_VALID(_.crbegin()));
// static_assert(IS_VALID(_.rend()));
// static_assert(IS_VALID(_.crend()));

// //! Capacity
// static_assert(IS_VALID(_.empty()));
// static_assert(IS_VALID(_.size()));
// static_assert(IS_VALID(_.max_size()));
// static_assert(IS_VALID(_.reserve(T::size_type{})));
// static_assert(IS_VALID(_.capacity()));
// static_assert(IS_VALID(_.shrink_to_fit()));

// //! Modifiers
// static_assert(IS_VALID(_.clear()));
// static_assert(IS_VALID(_.insert(begin(_), 0)));
// static_assert(IS_VALID(_.emplace(begin(_), 0)));
// static_assert(IS_VALID(_.template emplace<int>(begin(_), 0)));
// static_assert(IS_VALID(_.erase(begin(_))));
// static_assert(IS_VALID(_.push_back(0)));
// static_assert(IS_VALID(_.emplace_back(0)));
// static_assert(IS_VALID(_.template emplace_back<int>(0)));
// static_assert(IS_VALID(_.pop_back()));
// static_assert(IS_VALID(_.resize(T::size_type{})));
// static_assert(IS_VALID(_.swap(_)));

// //! Non-member functions
// // static_assert(IS_VALID(_ == _);
// // TODO: operator!=
// // TODO: operator<
// // TODO: operator<=
// // TODO: operator>
// // TODO: operator>=
// // TODO: std::swap

// // TODO: Deduction guides

} // namespace tests_containers
<|endoftext|>


<commit_before>#include <reflection/reflection.h>
#include <smartref/smartref.h>

// #include <vector>

namespace tests_containers {

// template <typename F>
// class reflect_member_function : public reflection::reflect_base<reflection::reflected_kind::member_function> {
// private:
//     template <typename... ExplicitArgs, typename... Args>
//     decltype(auto) indirect(Args&&... args)
//     {
//         auto f = [](auto& obj, auto&&... args) { if constexpr (sizeof...(ExplicitArgs) == 0) return obj.operator=(std::forward<Args>(args)...); else if constexpr (utils::always_true<Args...>) return obj.template operator=<ExplicitArgs...>( std::forward<Args>(args)...); };
//         return F{}(*this, f, std::forward<Args>(args)...);
//     }
// public:
//     template <typename... ExplicitArgs, typename... Args>
//     auto operator=(Args&&... args) -> decltype(indirect<ExplicitArgs...>(std::forward<Args>(args)...)) { return indirect<ExplicitArgs...>(std::forward<Args>(args)...); }
// };
// template<typename T>
struct Ref : smartref::reflect_member_function<Ref>
// struct Ref : smartref::using_<T>
{
  int ref;

  operator int &()
  {
    return ref;
  }

  Ref() = default;
  Ref(int arg) : ref{arg} {}

  using Base = smartref::reflect_member_function<Ref>;
  using Base::operator=;

  // int &operator=(int arg)
  // {
  //   return ref = arg;
  // }

  // using smartref::using_<T>::using_;
  // using smartref::using_<T>::operator=;

  // Ref() = default;

  // Ref(const Ref &) = default;
  Ref &operator=(const Ref &) = default;
  // Ref(Ref &&) = default;
  // Ref &operator=(Ref &&) = default;
};

// struct uiop {
//   auto operator=(int) {}
// };
// struct qwerty : uiop {
//   using uiop::operator=;
// };
template<typename T>
auto test = []{
  T u;
  T v{};
  T w = {};
  T x = 0;
  T y = {0};
  T z = u;
  T s = {u};

  u = 0;
  // u = v;
  // u = v = 0;
  // u = v = w;
  // u = (v = 0);
  // u = (v = w);
  // (u = v) = 0;
  // (u = v) = w;

  // TODO: test both initialized ({} and assignment) and uninitialized construction
  // Ref<int> x = 4;
  // Ref<int> x;
  // Ref<int> x{};
  // qwerty x;
  // x = 7;
};

auto test_int = test<int>;
auto test_ref = test<Ref>;

// using T = int;
// using T = std::vector<int>;

// using reflection::reflect;

// //! Member-types
// constexpr auto is_valid = [](auto expression)
// {
//   // auto expected = reflect<decltype(expression(std::declval<    T >(), std::declval<    T >()))>;
//   auto actual   = reflect<decltype(expression(std::declval<Ref<T>>(), std::declval<    T >()))>;

//   return true;
//   // return actual == expected;
// };

// #define IS_VALID(expression) is_valid([](auto &&_, auto &&__) {return expression;})

// TODO:
// - constructing a smartref (e.g. Ref<int> x;)

// static_assert(reflect<Ref<T>::value_type>             == reflect<T::value_type>);
// static_assert(reflect<Ref<T>::allocator_type>         == reflect<T::allocator_type>);
// static_assert(reflect<Ref<T>::size_type>              == reflect<T::size_type>);
// static_assert(reflect<Ref<T>::difference_type>        == reflect<T::difference_type>);
// static_assert(reflect<Ref<T>::reference>              == reflect<T::reference>);
// static_assert(reflect<Ref<T>::const_reference>        == reflect<T::const_reference>);
// static_assert(reflect<Ref<T>::pointer>                == reflect<T::pointer>);
// static_assert(reflect<Ref<T>::const_pointer>          == reflect<T::const_pointer>);
// static_assert(reflect<Ref<T>::iterator>               == reflect<T::iterator>);
// static_assert(reflect<Ref<T>::const_iterator>         == reflect<T::const_iterator>);
// static_assert(reflect<Ref<T>::reverse_iterator>       == reflect<T::reverse_iterator>);
// static_assert(reflect<Ref<T>::const_reverse_iterator> == reflect<T::const_reverse_iterator>);

// //! Member functions
// // TODO: Test all overloads
// // TODO: (constructor)
// // TODO: Why does operator= work? What does it do?
// // static_assert(IS_VALID(_.operator=(_)));
// // static_assert(IS_VALID(_.operator=(__)));
// static_assert(IS_VALID(_ = __));
// static_assert(IS_VALID(_.assign(0, 0)));
// static_assert(IS_VALID(_.assign(begin(_), end(_))));
// static_assert(IS_VALID(_.get_allocator()));
// //! Element access
// static_assert(IS_VALID(_.at(T::size_type{})));
// static_assert(IS_VALID(_.operator[](0)));
// static_assert(IS_VALID(_[0]));
// static_assert(IS_VALID(_.front()));
// static_assert(IS_VALID(_.back()));
// static_assert(IS_VALID(_.data()));

// //! Iterators
// static_assert(IS_VALID(_.begin()));
// static_assert(IS_VALID(_.cbegin()));
// static_assert(IS_VALID(_.end()));
// static_assert(IS_VALID(_.cend()));
// static_assert(IS_VALID(_.rbegin()));
// static_assert(IS_VALID(_.crbegin()));
// static_assert(IS_VALID(_.rend()));
// static_assert(IS_VALID(_.crend()));

// //! Capacity
// static_assert(IS_VALID(_.empty()));
// static_assert(IS_VALID(_.size()));
// static_assert(IS_VALID(_.max_size()));
// static_assert(IS_VALID(_.reserve(T::size_type{})));
// static_assert(IS_VALID(_.capacity()));
// static_assert(IS_VALID(_.shrink_to_fit()));

// //! Modifiers
// static_assert(IS_VALID(_.clear()));
// static_assert(IS_VALID(_.insert(begin(_), 0)));
// static_assert(IS_VALID(_.emplace(begin(_), 0)));
// static_assert(IS_VALID(_.template emplace<int>(begin(_), 0)));
// static_assert(IS_VALID(_.erase(begin(_))));
// static_assert(IS_VALID(_.push_back(0)));
// static_assert(IS_VALID(_.emplace_back(0)));
// static_assert(IS_VALID(_.template emplace_back<int>(0)));
// static_assert(IS_VALID(_.pop_back()));
// static_assert(IS_VALID(_.resize(T::size_type{})));
// static_assert(IS_VALID(_.swap(_)));

// //! Non-member functions
// // static_assert(IS_VALID(_ == _);
// // TODO: operator!=
// // TODO: operator<
// // TODO: operator<=
// // TODO: operator>
// // TODO: operator>=
// // TODO: std::swap

// // TODO: Deduction guides

} // namespace tests_containers
<commit_msg>Renamed variables<commit_after>#include <reflection/reflection.h>
#include <smartref/smartref.h>

// #include <vector>

namespace tests_containers {

// template <typename F>
// class reflect_member_function : public reflection::reflect_base<reflection::reflected_kind::member_function> {
// private:
//     template <typename... ExplicitArgs, typename... Args>
//     decltype(auto) indirect(Args&&... args)
//     {
//         auto f = [](auto& obj, auto&&... args) { if constexpr (sizeof...(ExplicitArgs) == 0) return obj.operator=(std::forward<Args>(args)...); else if constexpr (utils::always_true<Args...>) return obj.template operator=<ExplicitArgs...>( std::forward<Args>(args)...); };
//         return F{}(*this, f, std::forward<Args>(args)...);
//     }
// public:
//     template <typename... ExplicitArgs, typename... Args>
//     auto operator=(Args&&... args) -> decltype(indirect<ExplicitArgs...>(std::forward<Args>(args)...)) { return indirect<ExplicitArgs...>(std::forward<Args>(args)...); }
// };
// template<typename T>
struct Ref : smartref::reflect_member_function<Ref>
// struct Ref : smartref::using_<T>
{
  int ref;

  operator int &()
  {
    return ref;
  }

  Ref() = default;
  Ref(int arg) : ref{arg} {}

  using Base = smartref::reflect_member_function<Ref>;
  using Base::operator=;

  // int &operator=(int arg)
  // {
  //   return ref = arg;
  // }

  // using smartref::using_<T>::using_;
  // using smartref::using_<T>::operator=;

  // Ref() = default;

  // Ref(const Ref &) = default;
  Ref &operator=(const Ref &) = default;
  // Ref(Ref &&) = default;
  // Ref &operator=(Ref &&) = default;
};

// struct uiop {
//   auto operator=(int) {}
// };
// struct qwerty : uiop {
//   using uiop::operator=;
// };
template<typename T>
auto test = []{
  T a;
  T b{};
  T c = {};
  T d = 0;
  T e = {0};
  T f = a;
  T g = {a};

  a = 0;
  // a = b;
  // a = b = 0;
  // a = b = c;
  // a = (b = 0);
  // a = (b = c);
  // (a = b) = 0;
  // (a = b) = c;

  // TODO: test both initialized ({} and assignment) and uninitialized construction
  // Ref<int> x = 4;
  // Ref<int> x;
  // Ref<int> x{};
  // qwerty x;
  // x = 7;
};

auto test_int = test<int>;
auto test_ref = test<Ref>;

// using T = int;
// using T = std::vector<int>;

// using reflection::reflect;

// //! Member-types
// constexpr auto is_valid = [](auto expression)
// {
//   // auto expected = reflect<decltype(expression(std::declval<    T >(), std::declval<    T >()))>;
//   auto actual   = reflect<decltype(expression(std::declval<Ref<T>>(), std::declval<    T >()))>;

//   return true;
//   // return actual == expected;
// };

// #define IS_VALID(expression) is_valid([](auto &&_, auto &&__) {return expression;})

// TODO:
// - constructing a smartref (e.g. Ref<int> x;)

// static_assert(reflect<Ref<T>::value_type>             == reflect<T::value_type>);
// static_assert(reflect<Ref<T>::allocator_type>         == reflect<T::allocator_type>);
// static_assert(reflect<Ref<T>::size_type>              == reflect<T::size_type>);
// static_assert(reflect<Ref<T>::difference_type>        == reflect<T::difference_type>);
// static_assert(reflect<Ref<T>::reference>              == reflect<T::reference>);
// static_assert(reflect<Ref<T>::const_reference>        == reflect<T::const_reference>);
// static_assert(reflect<Ref<T>::pointer>                == reflect<T::pointer>);
// static_assert(reflect<Ref<T>::const_pointer>          == reflect<T::const_pointer>);
// static_assert(reflect<Ref<T>::iterator>               == reflect<T::iterator>);
// static_assert(reflect<Ref<T>::const_iterator>         == reflect<T::const_iterator>);
// static_assert(reflect<Ref<T>::reverse_iterator>       == reflect<T::reverse_iterator>);
// static_assert(reflect<Ref<T>::const_reverse_iterator> == reflect<T::const_reverse_iterator>);

// //! Member functions
// // TODO: Test all overloads
// // TODO: (constructor)
// // TODO: Why does operator= work? What does it do?
// // static_assert(IS_VALID(_.operator=(_)));
// // static_assert(IS_VALID(_.operator=(__)));
// static_assert(IS_VALID(_ = __));
// static_assert(IS_VALID(_.assign(0, 0)));
// static_assert(IS_VALID(_.assign(begin(_), end(_))));
// static_assert(IS_VALID(_.get_allocator()));
// //! Element access
// static_assert(IS_VALID(_.at(T::size_type{})));
// static_assert(IS_VALID(_.operator[](0)));
// static_assert(IS_VALID(_[0]));
// static_assert(IS_VALID(_.front()));
// static_assert(IS_VALID(_.back()));
// static_assert(IS_VALID(_.data()));

// //! Iterators
// static_assert(IS_VALID(_.begin()));
// static_assert(IS_VALID(_.cbegin()));
// static_assert(IS_VALID(_.end()));
// static_assert(IS_VALID(_.cend()));
// static_assert(IS_VALID(_.rbegin()));
// static_assert(IS_VALID(_.crbegin()));
// static_assert(IS_VALID(_.rend()));
// static_assert(IS_VALID(_.crend()));

// //! Capacity
// static_assert(IS_VALID(_.empty()));
// static_assert(IS_VALID(_.size()));
// static_assert(IS_VALID(_.max_size()));
// static_assert(IS_VALID(_.reserve(T::size_type{})));
// static_assert(IS_VALID(_.capacity()));
// static_assert(IS_VALID(_.shrink_to_fit()));

// //! Modifiers
// static_assert(IS_VALID(_.clear()));
// static_assert(IS_VALID(_.insert(begin(_), 0)));
// static_assert(IS_VALID(_.emplace(begin(_), 0)));
// static_assert(IS_VALID(_.template emplace<int>(begin(_), 0)));
// static_assert(IS_VALID(_.erase(begin(_))));
// static_assert(IS_VALID(_.push_back(0)));
// static_assert(IS_VALID(_.emplace_back(0)));
// static_assert(IS_VALID(_.template emplace_back<int>(0)));
// static_assert(IS_VALID(_.pop_back()));
// static_assert(IS_VALID(_.resize(T::size_type{})));
// static_assert(IS_VALID(_.swap(_)));

// //! Non-member functions
// // static_assert(IS_VALID(_ == _);
// // TODO: operator!=
// // TODO: operator<
// // TODO: operator<=
// // TODO: operator>
// // TODO: operator>=
// // TODO: std::swap

// // TODO: Deduction guides

} // namespace tests_containers
<|endoftext|>


<commit_before>#ifndef MJOLNIR_LOCAL_FORCE_FIELD
#define MJOLNIR_LOCAL_FORCE_FIELD
#include "BondLengthInteraction.hpp"
#include "BondAngleInteraction.hpp"
#include "DihedralAngleInteraction.hpp"
#include "LocalPotentialBase.hpp"
#include "ParticleContainer.hpp"
#include <utility>
#include <vector>
#include <array>
#include <memory>

namespace mjolnir
{

template<typename traitsT>
class LocalForceField
{
  public:
    typedef traitsT traits_type;
    typedef typename traits_type::time_type       time_type;
    typedef typename traits_type::real_type       real_type;
    typedef typename traits_type::coordinate_type coordinate_type;
    typedef Particle<coordinate_type>             particle_type;
    typedef ParticleContainer<traits_type>        particle_container_type;
    typedef LocalPotentialBase<traits_type>       potential_base;
    typedef std::unique_ptr<potential_base>       potential_ptr;

    template<std::size_t N>
    struct local_forcefield_type
    {
        typedef LocalInteractionBase<traitsT, N>         interaction_type;
        typedef std::unique_ptr<interaction_type>        interaction_ptr;
        typedef typename interaction_type::particle_ptrs particle_ptrs;

        interaction_ptr interaction;
        std::vector<std::pair<particle_ptrs, potential_ptr>> potentials;
    };

  public:

    LocalForceField() = default;
    ~LocalForceField() = default;
    LocalForceField(LocalForceField const&) = delete;
    LocalForceField(LocalForceField&&)      = default;
    LocalForceField& operator=(LocalForceField const&) = delete;
    LocalForceField& operator=(LocalForceField&&)      = default;

//     void initialize(const particle_container_type& pcon, const time_type dt);

    void      calc_force(particle_container_type& pcon);
    real_type calc_energy(const particle_container_type& pcon) const;

//     void emplace_bond(std::size_t i, std::size_t j, potential_ptr&& pot);
//     void emplace_angle(std::size_t i, std::size_t j, std::size_t k,
//                        potential_ptr&& pot);
//     void emplace_dihedral(std::size_t i, std::size_t j, std::size_t k,
//                           std::size_t l, potential_ptr&& pot);

    local_forcefield_type<2>& body2_at(std::size_t i){return body2.at(i);}
    local_forcefield_type<3>& body3_at(std::size_t i){return body3.at(i);}
    local_forcefield_type<4>& body4_at(std::size_t i){return body4.at(i);}

  private:

    std::vector<local_forcefield_type<2>> body2;
    std::vector<local_forcefield_type<3>> body3;
    std::vector<local_forcefield_type<4>> body4;
};

// template<typename traitsT>
// void LocalForceField<traitsT>::emplace_bond(
//         std::size_t i, std::size_t j, potential_ptr&& pot)
// {
//     bond_potentials.emplace_back(
//             i, j, std::forward<potential_ptr>(pot));
//     return ;
// }
//
// template<typename traitsT>
// void LocalForceField<traitsT>::emplace_angle(
//         std::size_t i, std::size_t j, std::size_t k, potential_ptr&& pot)
// {
//     angle_potentials.emplace_back(i, j, k,
//             std::forward<potential_ptr>(pot));
//     return ;
// }
//
// template<typename traitsT>
// void LocalForceField<traitsT>::emplace_dihedral(
//         std::size_t i, std::size_t j, std::size_t k, std::size_t l,
//         potential_ptr&& pot)
// {
//     dihd_potentials.emplace_back(i, j, k, l,
//             std::forward<potential_ptr>(pot));
//     return ;
// }

template<typename traitsT>
void LocalForceField<traitsT>::calc_force(particle_container_type& pcon)
{
    for(auto ff = body2.cbegin(); ff != body2.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            ff->interaction.calc_force(iter->first, *(iter->second));
    }

    for(auto ff = body3.cbegin(); ff != body3.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            ff->interaction.calc_force(iter->first, *(iter->second));
    }

    for(auto ff = body4.cbegin(); ff != body4.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            ff->interaction.calc_force(iter->first, *(iter->second));
    }
    return;
}

template<typename traitsT>
typename LocalForceField<traitsT>::real_type
LocalForceField<traitsT>::calc_energy(const particle_container_type& pcon) const
{
    real_type energy = 0.0;

    for(auto ff = body2.cbegin(); ff != body2.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            energy += ff->interaction.calc_energy(iter->first, *(iter->second));
    }

    for(auto ff = body3.cbegin(); ff != body3.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            energy += ff->interaction.calc_energy(iter->first, *(iter->second));
    }

    for(auto ff = body4.cbegin(); ff != body4.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            energy += ff->interaction.calc_energy(iter->first, *(iter->second));
    }
    return energy;
}

} // mjolnir
#endif /* MJOLNIR_LOCAL_FORCE_FIELD */
<commit_msg>add ctor of local_forcefield_type<commit_after>#ifndef MJOLNIR_LOCAL_FORCE_FIELD
#define MJOLNIR_LOCAL_FORCE_FIELD
#include "BondLengthInteraction.hpp"
#include "BondAngleInteraction.hpp"
#include "DihedralAngleInteraction.hpp"
#include "LocalPotentialBase.hpp"
#include "ParticleContainer.hpp"
#include <utility>
#include <vector>
#include <array>
#include <memory>

namespace mjolnir
{

template<typename traitsT>
class LocalForceField
{
  public:
    typedef traitsT traits_type;
    typedef typename traits_type::time_type       time_type;
    typedef typename traits_type::real_type       real_type;
    typedef typename traits_type::coordinate_type coordinate_type;
    typedef Particle<coordinate_type>             particle_type;
    typedef ParticleContainer<traits_type>        particle_container_type;
    typedef LocalPotentialBase<traits_type>       potential_base;
    typedef std::unique_ptr<potential_base>       potential_ptr;

    template<std::size_t N>
    struct local_forcefield_type
    {
        typedef LocalInteractionBase<traitsT, N>         interaction_type;
        typedef std::unique_ptr<interaction_type>        interaction_ptr;
        typedef typename interaction_type::particle_ptrs particle_ptrs;
        typedef std::pair<particle_ptrs, potential_ptr>  potential_index_pair;
        typedef std::vector<potential_index_pair>        potential_array;

        local_forcefield_type(interaction_ptr&& i, potential_array&& ps)
            : interaction(std::forward<interaction_ptr>(i)),
              potentials(std::forward<potential_array>(ps))
        {}
        interaction_ptr interaction;
        potential_array potentials;
    };

    typedef local_forcefield_type<2> forcefield_2body;
    typedef local_forcefield_type<3> forcefield_3body;
    typedef local_forcefield_type<4> forcefield_4body;

  public:

    LocalForceField() = default;
    ~LocalForceField() = default;
    LocalForceField(LocalForceField const&) = delete;
    LocalForceField(LocalForceField&&)      = default;
    LocalForceField& operator=(LocalForceField const&) = delete;
    LocalForceField& operator=(LocalForceField&&)      = default;

//     void initialize(const particle_container_type& pcon, const time_type dt);

    void      calc_force(particle_container_type& pcon);
    real_type calc_energy(const particle_container_type& pcon) const;

//     void emplace_bond(std::size_t i, std::size_t j, potential_ptr&& pot);
//     void emplace_angle(std::size_t i, std::size_t j, std::size_t k,
//                        potential_ptr&& pot);
//     void emplace_dihedral(std::size_t i, std::size_t j, std::size_t k,
//                           std::size_t l, potential_ptr&& pot);

    std::vector<local_forcefield_type<2>>& body2(){return body2_;}
    std::vector<local_forcefield_type<3>>& body3(){return body3_;}
    std::vector<local_forcefield_type<4>>& body4(){return body4_;}

  private:

    std::vector<local_forcefield_type<2>> body2_;
    std::vector<local_forcefield_type<3>> body3_;
    std::vector<local_forcefield_type<4>> body4_;
};

// template<typename traitsT>
// void LocalForceField<traitsT>::emplace_bond(
//         std::size_t i, std::size_t j, potential_ptr&& pot)
// {
//     bond_potentials.emplace_back(
//             i, j, std::forward<potential_ptr>(pot));
//     return ;
// }
//
// template<typename traitsT>
// void LocalForceField<traitsT>::emplace_angle(
//         std::size_t i, std::size_t j, std::size_t k, potential_ptr&& pot)
// {
//     angle_potentials.emplace_back(i, j, k,
//             std::forward<potential_ptr>(pot));
//     return ;
// }
//
// template<typename traitsT>
// void LocalForceField<traitsT>::emplace_dihedral(
//         std::size_t i, std::size_t j, std::size_t k, std::size_t l,
//         potential_ptr&& pot)
// {
//     dihd_potentials.emplace_back(i, j, k, l,
//             std::forward<potential_ptr>(pot));
//     return ;
// }

template<typename traitsT>
void LocalForceField<traitsT>::calc_force(particle_container_type& pcon)
{
    for(auto ff = body2_.cbegin(); ff != body2_.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            ff->interaction->calc_force(iter->first, *(iter->second));
    }

    for(auto ff = body3_.cbegin(); ff != body3_.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            ff->interaction->calc_force(iter->first, *(iter->second));
    }

    for(auto ff = body4_.cbegin(); ff != body4_.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            ff->interaction->calc_force(iter->first, *(iter->second));
    }
    return;
}

template<typename traitsT>
typename LocalForceField<traitsT>::real_type
LocalForceField<traitsT>::calc_energy(const particle_container_type& pcon) const
{
    real_type energy = 0.0;

    for(auto ff = body2_.cbegin(); ff != body2_.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            energy += ff->interaction->calc_energy(iter->first, *(iter->second));
    }

    for(auto ff = body3_.cbegin(); ff != body3_.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            energy += ff->interaction->calc_energy(iter->first, *(iter->second));
    }

    for(auto ff = body4_.cbegin(); ff != body4_.cend(); ++ff)
    {
        for(auto iter = ff->potentials.cbegin();
                iter != ff->potentials.cend(); ++iter)
            energy += ff->interaction->calc_energy(iter->first, *(iter->second));
    }
    return energy;
}

} // mjolnir
#endif /* MJOLNIR_LOCAL_FORCE_FIELD */
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2013 Soumyajit De
 */

#include <gtest/gtest.h>

#include <shogun/lib/common.h>
#include <shogun/lib/SGVector.h>
#include <shogun/lib/SGSparseVector.h>
#include <shogun/lib/SGSparseMatrix.h>
#include <shogun/io/LibSVMFile.h>
#include <shogun/lib/SGMatrix.h>
#include <shogun/mathematics/Random.h>

using namespace shogun;

#ifdef HAVE_EIGEN3
#include <shogun/mathematics/eigen3.h>
using namespace Eigen;

//function to create sparse matrix
template<typename T>
SGSparseMatrix<T> GenerateSparseMatrix(const int32_t numberOfFeatures,
                                       const int32_t numberOfVectors,
                                       const float64_t sparseLevel)
{
    CRandom randGenerator(0);
    SGSparseMatrix<T> matrix(numberOfFeatures, numberOfVectors);
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
        {
            float64_t randomNumber=randGenerator.random(0.0,1.0);
            if (randomNumber<=sparseLevel)
                matrix(vecIndex,featIndex)=randomNumber;
        }
    return matrix;
}

TEST(SGSparseMatrix, multiply_float64_int32)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	SGSparseMatrix<float64_t> m(size, size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<float64_t>(num_feat);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<float64_t> entry;
			entry.feat_index=(j+1)*2;
			entry.entry=0.5;
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	SGVector<int32_t> v(size);
	v.set_const(2);

	SGVector<float64_t> result=m*v;
	Map<VectorXd> r(result.vector, result.vlen);

	EXPECT_NEAR(r.norm(), 12.64911064067351809115, 1E-16);
}

TEST(SGSparseMatrix, multiply_complex128_int32)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	SGSparseMatrix<complex128_t> m(size, size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<complex128_t>(num_feat);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<complex128_t> entry;
			entry.feat_index=(j+1)*2;
			entry.entry=complex128_t(0.5, 0.75);
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	SGVector<int32_t> v(size);
	v.set_const(2);

	SGVector<complex128_t> result=m*v;
	Map<VectorXcd> r(result.vector, result.vlen);

	EXPECT_NEAR(r.norm(), 22.80350850198275836078, 1E-16);
}

TEST(SGSparseMatrix, multiply_complex128_float64)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	SGSparseMatrix<complex128_t> m(size, size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<complex128_t>(num_feat);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<complex128_t> entry;
			entry.feat_index=(j+1)*2;
			entry.entry=complex128_t(0.5, 0.75);
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	SGVector<float64_t> v(size);
	v.set_const(2);

	SGVector<complex128_t> result=m*v;
	Map<VectorXcd> r(result.vector, result.vlen);

	EXPECT_NEAR(r.norm(), 22.80350850198275836078, 1E-16);
}
#endif // HAVE_EIGEN3

TEST(SGSparseMatrix, access_by_index)
{
	const index_t size=2;

	SGSparseMatrix<int32_t> m(size, size);
	for (index_t i=0; i<size; ++i)
		m(i,i)=i+1;
	m.sort_features();

	for (index_t i=0; i<size; ++i)
		EXPECT_EQ(m(i,i), i+1);
}

TEST(SGSparseMatrix, io_libsvm)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	CLibSVMFile* fin;
	CLibSVMFile* fout;

	SGSparseMatrix<float64_t> m(size, size);
	SGSparseMatrix<float64_t> m_from_file;
	SGVector<float64_t> labels(size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<float64_t>(num_feat);
		labels.vector[i]=(float64_t) (i%2);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<float64_t> entry;
			entry.feat_index=(j+1)*2 - 1;
			entry.entry=0.5;
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	fout=new CLibSVMFile("SGSparseMatrix_io_libsvm_output.txt",'w', NULL);
	m.save_with_labels(fout, labels);
	SG_UNREF(fout);

	fin=new CLibSVMFile("SGSparseMatrix_io_libsvm_output.txt",'r', NULL);
	SGVector<float64_t> labels_from_file=m_from_file.load_with_labels(fin, false);
	SG_UNREF(fin);

	for (int32_t i=0; i<size; i++)
	{
		EXPECT_EQ(labels[i], labels_from_file[i]);
		for (index_t j=0; j<num_feat; ++j)
		{
			EXPECT_EQ(m[i].features[j].feat_index, m_from_file[i].features[j].feat_index);
			EXPECT_NEAR(m[i].features[j].entry, m_from_file[i].features[j].entry, 1E-14);
		}
	}

    //remove file generated by test
	unlink("SGSparseMatrix_io_libsvm_output.txt");
}

TEST(SGSparseMatrix, access_by_index_non_square)
{
    //GeneratSparseMatrix function cannot used in this test since it uses () operator
    CRandom randGenerator(0);
    float64_t sparseLevel=0.1;
    const index_t numberOfFeatures=50;
    const index_t numberOfVectors=100;

    SGSparseMatrix<float64_t> matrix(numberOfFeatures, numberOfVectors);

    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
        {
            float64_t randomNumber=randGenerator.random(0.0,1.0);
            if (randomNumber<=sparseLevel)
                matrix(vecIndex,featIndex)=randomNumber;

            if (randomNumber<=sparseLevel)
                EXPECT_EQ(matrix(vecIndex,featIndex), randomNumber);
            else
                EXPECT_EQ(matrix(vecIndex,featIndex), 0.0);
        }
}

TEST(SGSparseMatrix, get_transposed_more_features)
{
    CRandom randGenerator(0);
    const float64_t sparseLevel=0.1;

    const index_t numberOfFeatures=100;
    const index_t numberOfVectors=50;
    SGSparseMatrix<float64_t> sparseMatrix
        =GenerateSparseMatrix<float64_t>(numberOfFeatures, numberOfVectors, sparseLevel);

    SGSparseMatrix<float64_t> sparseMatrixT=sparseMatrix.get_transposed();

    //check dimension
    EXPECT_EQ(sparseMatrix.num_features, sparseMatrixT.num_vectors);
    EXPECT_EQ(sparseMatrix.num_vectors, sparseMatrixT.num_features);

    //check contents
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
            EXPECT_EQ(sparseMatrix(vecIndex,featIndex), sparseMatrixT(featIndex,vecIndex));
}

TEST(SGSparseMatrix, get_transposed_more_vectors)
{
    CRandom randGenerator(0);
    const float64_t sparseLevel=0.1;

    const index_t numberOfFeatures=50;
    const index_t numberOfVectors=100;
    SGSparseMatrix<float64_t> sparseMatrix
        =GenerateSparseMatrix<float64_t>(numberOfFeatures, numberOfVectors, sparseLevel);

    SGSparseMatrix<float64_t> sparseMatrixT=sparseMatrix.get_transposed();

    //check dimension
    EXPECT_EQ(sparseMatrix.num_features, sparseMatrixT.num_vectors);
    EXPECT_EQ(sparseMatrix.num_vectors, sparseMatrixT.num_features);

    //check contents
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
            EXPECT_EQ(sparseMatrix(vecIndex,featIndex), sparseMatrixT(featIndex,vecIndex));
}

TEST(SGSparseMatrix, from_dense)
{
    //GenerateSparseMatrix cannot templated to generate dense matrix,
    //since () operator are defined differently for sparse and dense matrix
    CRandom randGenerator(0);
    float64_t sparseLevel=0.1;

    const index_t numberOfFeatures=50;
    const index_t numberOfVectors=100;
    SGMatrix<float64_t> denseMatrix(numberOfFeatures, numberOfVectors);
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
        {
            float64_t randomNumber=randGenerator.random(0.0,1.0);
            if (randomNumber<=sparseLevel)
                denseMatrix(featIndex,vecIndex)=randomNumber;
        }

    SGSparseMatrix<float64_t> sparseMatrix;
    sparseMatrix.from_dense(denseMatrix);

    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
            EXPECT_EQ(sparseMatrix(vecIndex,featIndex), denseMatrix(featIndex,vecIndex));
}
<commit_msg>Fixed compilation error in SGSparseMatrix unittet in absence of Eigen3<commit_after>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2013 Soumyajit De
 */

#include <gtest/gtest.h>

#include <shogun/lib/common.h>
#include <shogun/lib/SGVector.h>
#include <shogun/lib/SGSparseVector.h>
#include <shogun/lib/SGSparseMatrix.h>
#include <shogun/io/LibSVMFile.h>
#include <shogun/lib/SGMatrix.h>
#include <shogun/mathematics/Random.h>

using namespace shogun;

#ifdef HAVE_EIGEN3
#include <shogun/mathematics/eigen3.h>
using namespace Eigen;
#endif // HAVE_EIGEN3

//function to create sparse matrix
template<typename T>
SGSparseMatrix<T> GenerateSparseMatrix(const int32_t numberOfFeatures,
                                       const int32_t numberOfVectors,
                                       const float64_t sparseLevel)
{
    CRandom randGenerator(0);
    SGSparseMatrix<T> matrix(numberOfFeatures, numberOfVectors);
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
        {
            float64_t randomNumber=randGenerator.random(0.0,1.0);
            if (randomNumber<=sparseLevel)
                matrix(vecIndex,featIndex)=randomNumber;
        }
    return matrix;
}

#ifdef HAVE_EIGEN3
TEST(SGSparseMatrix, multiply_float64_int32)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	SGSparseMatrix<float64_t> m(size, size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<float64_t>(num_feat);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<float64_t> entry;
			entry.feat_index=(j+1)*2;
			entry.entry=0.5;
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	SGVector<int32_t> v(size);
	v.set_const(2);

	SGVector<float64_t> result=m*v;
	Map<VectorXd> r(result.vector, result.vlen);

	EXPECT_NEAR(r.norm(), 12.64911064067351809115, 1E-16);
}

TEST(SGSparseMatrix, multiply_complex128_int32)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	SGSparseMatrix<complex128_t> m(size, size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<complex128_t>(num_feat);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<complex128_t> entry;
			entry.feat_index=(j+1)*2;
			entry.entry=complex128_t(0.5, 0.75);
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	SGVector<int32_t> v(size);
	v.set_const(2);

	SGVector<complex128_t> result=m*v;
	Map<VectorXcd> r(result.vector, result.vlen);

	EXPECT_NEAR(r.norm(), 22.80350850198275836078, 1E-16);
}

TEST(SGSparseMatrix, multiply_complex128_float64)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	SGSparseMatrix<complex128_t> m(size, size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<complex128_t>(num_feat);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<complex128_t> entry;
			entry.feat_index=(j+1)*2;
			entry.entry=complex128_t(0.5, 0.75);
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	SGVector<float64_t> v(size);
	v.set_const(2);

	SGVector<complex128_t> result=m*v;
	Map<VectorXcd> r(result.vector, result.vlen);

	EXPECT_NEAR(r.norm(), 22.80350850198275836078, 1E-16);
}
#endif // HAVE_EIGEN3

TEST(SGSparseMatrix, access_by_index)
{
	const index_t size=2;

	SGSparseMatrix<int32_t> m(size, size);
	for (index_t i=0; i<size; ++i)
		m(i,i)=i+1;
	m.sort_features();

	for (index_t i=0; i<size; ++i)
		EXPECT_EQ(m(i,i), i+1);
}

TEST(SGSparseMatrix, io_libsvm)
{
	const int32_t size=10;
	const int32_t num_feat=size/2;

	CLibSVMFile* fin;
	CLibSVMFile* fout;

	SGSparseMatrix<float64_t> m(size, size);
	SGSparseMatrix<float64_t> m_from_file;
	SGVector<float64_t> labels(size);
	for (index_t i=0; i<size; ++i)
	{
		m.sparse_matrix[i]=SGSparseVector<float64_t>(num_feat);
		labels.vector[i]=(float64_t) (i%2);
		for (index_t j=0; j<num_feat; ++j)
		{
			SGSparseVectorEntry<float64_t> entry;
			entry.feat_index=(j+1)*2 - 1;
			entry.entry=0.5;
			m.sparse_matrix[i].features[j]=entry;
		}
	}

	fout=new CLibSVMFile("SGSparseMatrix_io_libsvm_output.txt",'w', NULL);
	m.save_with_labels(fout, labels);
	SG_UNREF(fout);

	fin=new CLibSVMFile("SGSparseMatrix_io_libsvm_output.txt",'r', NULL);
	SGVector<float64_t> labels_from_file=m_from_file.load_with_labels(fin, false);
	SG_UNREF(fin);

	for (int32_t i=0; i<size; i++)
	{
		EXPECT_EQ(labels[i], labels_from_file[i]);
		for (index_t j=0; j<num_feat; ++j)
		{
			EXPECT_EQ(m[i].features[j].feat_index, m_from_file[i].features[j].feat_index);
			EXPECT_NEAR(m[i].features[j].entry, m_from_file[i].features[j].entry, 1E-14);
		}
	}

    //remove file generated by test
	unlink("SGSparseMatrix_io_libsvm_output.txt");
}

TEST(SGSparseMatrix, access_by_index_non_square)
{
    //GeneratSparseMatrix function cannot used in this test since it uses () operator
    CRandom randGenerator(0);
    float64_t sparseLevel=0.1;
    const index_t numberOfFeatures=50;
    const index_t numberOfVectors=100;

    SGSparseMatrix<float64_t> matrix(numberOfFeatures, numberOfVectors);

    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
        {
            float64_t randomNumber=randGenerator.random(0.0,1.0);
            if (randomNumber<=sparseLevel)
                matrix(vecIndex,featIndex)=randomNumber;

            if (randomNumber<=sparseLevel)
                EXPECT_EQ(matrix(vecIndex,featIndex), randomNumber);
            else
                EXPECT_EQ(matrix(vecIndex,featIndex), 0.0);
        }
}

TEST(SGSparseMatrix, get_transposed_more_features)
{
    CRandom randGenerator(0);
    const float64_t sparseLevel=0.1;

    const index_t numberOfFeatures=100;
    const index_t numberOfVectors=50;
    SGSparseMatrix<float64_t> sparseMatrix
        =GenerateSparseMatrix<float64_t>(numberOfFeatures, numberOfVectors, sparseLevel);

    SGSparseMatrix<float64_t> sparseMatrixT=sparseMatrix.get_transposed();

    //check dimension
    EXPECT_EQ(sparseMatrix.num_features, sparseMatrixT.num_vectors);
    EXPECT_EQ(sparseMatrix.num_vectors, sparseMatrixT.num_features);

    //check contents
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
            EXPECT_EQ(sparseMatrix(vecIndex,featIndex), sparseMatrixT(featIndex,vecIndex));
}

TEST(SGSparseMatrix, get_transposed_more_vectors)
{
    CRandom randGenerator(0);
    const float64_t sparseLevel=0.1;

    const index_t numberOfFeatures=50;
    const index_t numberOfVectors=100;
    SGSparseMatrix<float64_t> sparseMatrix
        =GenerateSparseMatrix<float64_t>(numberOfFeatures, numberOfVectors, sparseLevel);

    SGSparseMatrix<float64_t> sparseMatrixT=sparseMatrix.get_transposed();

    //check dimension
    EXPECT_EQ(sparseMatrix.num_features, sparseMatrixT.num_vectors);
    EXPECT_EQ(sparseMatrix.num_vectors, sparseMatrixT.num_features);

    //check contents
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
            EXPECT_EQ(sparseMatrix(vecIndex,featIndex), sparseMatrixT(featIndex,vecIndex));
}

TEST(SGSparseMatrix, from_dense)
{
    //GenerateSparseMatrix cannot templated to generate dense matrix,
    //since () operator are defined differently for sparse and dense matrix
    CRandom randGenerator(0);
    float64_t sparseLevel=0.1;

    const index_t numberOfFeatures=50;
    const index_t numberOfVectors=100;
    SGMatrix<float64_t> denseMatrix(numberOfFeatures, numberOfVectors);
    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
        {
            float64_t randomNumber=randGenerator.random(0.0,1.0);
            if (randomNumber<=sparseLevel)
                denseMatrix(featIndex,vecIndex)=randomNumber;
        }

    SGSparseMatrix<float64_t> sparseMatrix;
    sparseMatrix.from_dense(denseMatrix);

    for (index_t featIndex=0; featIndex<numberOfFeatures; ++featIndex)
        for (index_t vecIndex=0; vecIndex<numberOfVectors; ++vecIndex)
            EXPECT_EQ(sparseMatrix(vecIndex,featIndex), denseMatrix(featIndex,vecIndex));
}
<|endoftext|>