/****************************************************************************

 http://www.cocos2d-x.org

 ****************************************************************************/

#include "CCPUTextureAnimator.h"
#include "extensions/Particle3D/PU/CCPUParticleSystem3D.h"

NS_CC_BEGIN
// Constants
const float PUTextureAnimator::DEFAULT_TIME_STEP = 0.0f;
const unsigned short PUTextureAnimator::DEFAULT_TEXCOORDS_START = 0;
const unsigned short PUTextureAnimator::DEFAULT_TEXCOORDS_END = 0;
const PUTextureAnimator::TextureAnimationType PUTextureAnimator::DEFAULT_ANIMATION_TYPE = PUTextureAnimator::TAT_LOOP;
const bool PUTextureAnimator::DEFAULT_START_RANDOM = true;

PUTextureAnimator::PUTextureAnimator(void) :
    PUAffector(),
    _animationTimeStep(DEFAULT_TIME_STEP),
    _animationTimeStepCount(0.0f),
    _startRandom(DEFAULT_START_RANDOM),
    _animationTimeStepSet(false),
    _nextIndex(false),
    _textureAnimationType(DEFAULT_ANIMATION_TYPE),
    _textureCoordsStart(DEFAULT_TEXCOORDS_START),
    _textureCoordsEnd(DEFAULT_TEXCOORDS_END)
{
}
PUTextureAnimator::~PUTextureAnimator(void)
{
}
float PUTextureAnimator::getAnimationTimeStep(void) const
{
    return _animationTimeStep;
}
void PUTextureAnimator::setAnimationTimeStep(float animationTimeStep)
{
    _animationTimeStep = animationTimeStep;
    _animationTi

}
PUTextureAnimator::TextureAnimationType PUTextureAnimator::getTextureAnimationType(void) const
{
    return _textureAnimationType;
}
void PUTextureAnimator::setTextureAnimationType(PUTextureAnimator::TextureAnimationType textureAnimationType)
{
    _textureAnimationType = textureAnimationType;
}
unsigned short PUTextureAnimator::getTextureCoordsStart(void) const
{
    return _textureCoordsStart;
}
void PUTextureAnimator::setTextureCoordsStart(unsigned short textureCoordsStart)
{
    _textureCoordsStart = textureCoordsStart;
}
unsigned short PUTextureAnimator::getTextureCoordsEnd(void) const
{
    return _textureCoordsEnd;
}
void PUTextureAnimator::setTextureCoordsEnd(unsigned short textureCoordsEnd)
{
    _textureCoordsEnd = textureCoordsEnd;
}
bool PUTextureAnimator::isStartRandom(void) const
{
    return _startRandom;
}
void PUTextureAnimator::setStartRandom(bool startRandom)
{
    _startRandom = startRandom;
}
void PUTextureAnimator::initParticleForEmission(PUParticle3D* particle)
{
    //// Only continue if the particle is a visual particle

    // Set first image
    if (_startRandom)
    {
        particle->textureCoordsCurrent = (unsigned short)cocos2d::random((float)_textureCoordsStart, (float)_textureCoordsEnd + 0.999f);
    }
    else
    {
        particle->textureCoordsCurrent = _textureCoordsStart;
    }

    // Calculate the animationTimeStep
    if (!_animationTimeStepSet)
    {
        // Set the animation time step for each particle
        switch(_textureAnimationType)
        {
        case TAT_LOOP:
            {
                particle->textureAnimationTimeStep = particle->timeToLive / (_textureCoordsEnd - _textureCoordsStart + 1);
            }
            break;

        case TAT_UP_DOWN:
            {
                particle->textureAnimationTimeStep = particle->timeToLive / (2 * (_textureCoordsEnd - _textureCoordsStart) + 1);
            }
            break;

        case TAT_RANDOM:
            {
                particle->textureAnimationTimeStep = particle->timeToLive;
            }
            break;
        }
    }
}
void PUTextureAnimator::preUpdateAffector(float deltaTime)
{
    // Determine the next texture coords index (global)
    if (_animationTimeStepSet)
    {
        _nextIndex = false;
        _animationTimeStepCount += deltaTime;
        if (_animationTimeStepCount > _animationTimeStep)
        {
            _animationTimeStepCount -= _animationTimeStep;
            _nextIndex = true;
        }
    }
}
void PUTextureAnimator::determineNextTextureCoords(PUParticle3D* visualParticle)
{
    switch(_textureAnimationType)
    {
    case TAT_LOOP:
        {
            if (visualParticle->textureCoordsCurrent >= _textureCoordsEnd)
            {
                visualParticle->textureCoordsCurrent = _textureCoordsStart;
            }
            else
            {
                (visualParticle->textureCoordsCurrent)++;
            }
        }
        break;

    case TAT_UP_DOWN:
        {
            if (visualParticle->textureAnimationDirectionUp == true)
            {
                // Going up
                if (visualParticle->textureCoordsCurrent >= _textureCoordsEnd)
                {
                    (visualParticle->textureCoordsCurrent)--;
                    visualParticle->textureAnimationDirectionUp = false;
                }
                else
                {
                    (visualParticle->textureCoordsCurrent)++;
                }
            }
            else
            {
                // Going down
                if (visualParticle->textureCoordsCurrent <= _textureCoordsStart)
                {
                    (visualParticle->textureCoordsCurrent)++;
                    visualParticle->textureAnimationDirectionUp = true;
                }
                else
                {
                    (visualParticle->textureCoordsCurrent)--;
                }
            }
        }
        break;

    case TAT_RANDOM:
        {
            // Generate a random texcoord index
            visualParticle->textureCoordsCurrent = (unsigned short)cocos2d::random((float)_textureCoordsStart, (float)_textureCoordsEnd + 0.999f);
        }
        break;
    }
}

void PUTextureAnimator::updatePUAffector( PUParticle3D *particle, float deltaTime )
{
    //// Only continue if the particle is a visual particle

    {
        // Determine the next texture coords index
        if (_animationTimeStepSet)
        {
            if (_nextIndex)
            {
                // Use the global one for all particles
                determineNextTextureCoords(particle);
            }
        }
        else
        {
            particle->textureAnimationTimeStepCount += deltaTime;
            if (particle->textureAnimationTimeStepCount > particle->textureAnimationTimeStep)
            {
                particle->textureAnimationTimeStepCount -= particle->textureAnimationTimeStep;
                determineNextTextureCoords(particle);
            }
        }
    }
}

PUTextureAnimator* PUTextureAnimator::create()
{
    auto pta = new (std::nothrow) PUTextureAnimator();
    pta->autorelease();
    return pta;
}

void PUTextureAnimator::copyAttributesTo( PUAffector* affector )
{
    PUAffector::copyAttributesTo(affector);
    PUTextureAnimator* textureAnimator = static_cast<PUTextureAnimator*>(affector);
    textureAnimator->_animationTimeStep = _animationTimeStep;
    textureAnimator->_animationTimeStepSet = _animationTimeStepSet;
    textureAnimator->_textureAnimationType = _textureAnimationType;
    textureAnimator->_textureCoordsStart = _textureCoordsStart;
    textureAnimator->_textureCoordsEnd = _textureCoordsEnd;
    textureAnimator->_startRandom = _startRandom;
}

NS_CC_END


/****************************************************************************

 http://www.cocos2d-x.org

 ****************************************************************************/

#include "base/CCScriptSupport.h"

#if CC_ENABLE_SCRIPT_BINDING

#include "base/CCScheduler.h"
#include "2d/CCNode.h"

bool CC_DLL cc_assert_script_compatible(const char *msg)
{
    cocos2d::ScriptEngineProtocol* engine = cocos2d::ScriptEngineManager::getInstance()->getScriptEngine();
    if (engine && engine->handleAssert(msg))
    {
        return true;
    }
    return false;
}

NS_CC_BEGIN

ScriptHandlerEntry* ScriptHandlerEntry::create(int handler)
{
    ScriptHandlerEntry* entry = new (std::nothrow) ScriptHandlerEntry(handler);
    entry->autorelease();
    return entry;
}

ScriptHandlerEntry::~ScriptHandlerEntry()
{
    if (_handler != 0 )
    {
        ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptHandler(_handler);
        LUALOG("[LUA] Remove event handler: %d", _handler);
        _handler = 0;
    }
}

SchedulerScriptHandlerEntry* SchedulerScriptHandlerEntry::create(int handler, float interval, bool paused)
{
    SchedulerScriptHandlerEntry* entry = new (std::nothrow) SchedulerScriptHandlerEntry(handler);
    entry->init(interval, paused);
    entry->autorelease();
    return entry;
}

bool SchedulerScriptHandlerEntry::init(float interval, bool paused)
{
    _timer = new (std::nothrow) TimerScriptHandler();
    _timer->initWithScriptHandler(_handler, interval);
    _paused = paused;
    LUALOG("[LUA] ADD script schedule: %d, entryID: %d", _handler, _entryId);
    return true;
}

{
    _timer->release();
    LUALOG("[LUA] DEL script schedule %d, entryID: %d", _handler, _entryId);
}

TouchScriptHandlerEntry* TouchScriptHandlerEntry::create(int handler,
                                                             bool isMultiTouches,
                                                             int priority,
                                                             bool swallowsTouches)
{
    TouchScriptHandlerEntry* entry = new (std::nothrow) TouchScriptHandlerEntry(handler);
    entry->init(isMultiTouches, priority, swallowsTouches);
    entry->autorelease();
    return entry;
}

TouchScriptHandlerEntry::~TouchScriptHandlerEntry()
{
}

bool TouchScriptHandlerEntry::init(bool isMultiTouches, int priority, bool swallowsTouches)
{
    _isMultiTouches = isMultiTouches;
    _priority = priority;
    _swallowsTouches = swallowsTouches;

    return true;
}

static ScriptEngineManager* s_pSharedScriptEngineManager = nullptr;

ScriptEngineManager::~ScriptEngineManager()
{
    removeScriptEngine();
}

void ScriptEngineManager::setScriptEngine(ScriptEngineProtocol *scriptEngine)
{
	if (_scriptEngine != scriptEngine)
	{
		removeScriptEngine();
		_scriptEngine = scriptEngine;
	}
}

void ScriptEngineManager::removeScriptEngine()
{
    if (_scriptEngine)
    {
        delete _scriptEngine;
        _scriptEngine = nullptr;
    }
}

ScriptEngineManager* ScriptEngineManager::getInstance()
{
    if (!s_pSharedScriptEngineManager)
    {
        s_pSharedScriptEngineManager = new (std::nothrow) ScriptEngineManager();
    }
    return s_pSharedScriptEngineManager;
}

void ScriptEngineManager::destroyInstance()
{
    if (s_pSharedScriptEngineManager)
    {
        delete s_pSharedScriptEngineManager;
        s_pSharedScriptEngineManager = nullptr;
    }
}

bool ScriptEngineManager::sendActionEventToJS(Action* actionObject, int eventType, void* param)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    ActionObjectScriptData data(actionObject,(int*)&eventType, param);
    ScriptEvent scriptEvent(kScriptActionEvent,(void*)&data);
    if (scriptEngine->sendEvent(&scriptEvent))
        return true;

    return false;
}

bool ScriptEngineManager::sendNodeEventToJS(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    if (scriptEngine->isCalledFromScript())
    {
        // Should only be invoked at root class Node
        scriptEngine->setCalledFromScript(false);
    }
    else
    {
        BasicScriptData data(node,(void*)&action);
        ScriptEvent scriptEvent(kNodeEvent,(void*)&data);
        if (scriptEngine->sendEvent(&scriptEvent))
            return true;
    }

    return false;
}

bool ScriptEngineManager::sendNodeEventToJSExtended(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    if (!scriptEngine->isCalledFromScript())
    {
        BasicScriptData data(node,(void*)&action);
        ScriptEvent scriptEvent(kNodeEvent,(void*)&data);
        if (scriptEngine->sendEvent(&scriptEvent))
            return true;
    }

    return false;
}

void ScriptEngineManager::sendNodeEventToLua(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    BasicScriptData data(node,(void*)&action);
    ScriptEvent scriptEvent(kNodeEvent,(void*)&data);

    scriptEngine->sendEvent(&scriptEvent);
}

NS_CC_END

#endif // #if CC_ENABLE_SCRIPT_BINDING


{
    TouchScriptHandlerEntry* entry = new (std::nothrow) TouchScriptHandlerEntry(handler);
    entry->init(isMultiTouches, priority, swallowsTouches);
    entry->autorelease();
    return entry;
}

/****************************************************************************

 http://www.cocos2d-x.org

 ****************************************************************************/

#include "base/CCScriptSupport.h"

#if CC_ENABLE_SCRIPT_BINDING

#include "base/CCScheduler.h"
#include "2d/CCNode.h"

bool CC_DLL cc_assert_script_compatible(const char *msg)
{
    cocos2d::ScriptEngineProtocol* engine = cocos2d::ScriptEngineManager::getInstance()->getScriptEngine();
    if (engine && engine->handleAssert(msg))
    {
        return true;
    }
    return false;
}

NS_CC_BEGIN

ScriptHandlerEntry* ScriptHandlerEntry::create(int handler)
{
    ScriptHandlerEntry* entry = new (std::nothrow) ScriptHandlerEntry(handler);
    entry->autorelease();
    return entry;
}

ScriptHandlerEntry::~ScriptHandlerEntry()
{
    if (_handler != 0 )
    {
        ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptHandler(_handler);
        LUALOG("[LUA] Remove event handler: %d", _handler);
        _handler = 0;
    }
}

SchedulerScriptHandlerEntry* SchedulerScriptHandlerEntry::create(int handler, float interval, bool paused)
{
    SchedulerScriptHandlerEntry* entry = new (std::nothrow) SchedulerScriptHandlerEntry(handler);
    entry->init(interval, paused);
    entry->autorelease();
    return entry;
}

bool SchedulerScriptHandlerEntry::init(float interval, bool paused)
{
    _timer = new (std::nothrow) TimerScriptHandler();
    _timer->initWithScriptHandler(_handler, interval);
    _paused = paused;
    LUALOG("[LUA] ADD script schedule: %d, entryID: %d", _handler, _entryId);
    return true;
}

SchedulerScriptHandlerEntry::~SchedulerScriptHandlerEntry()
{
    _timer->release();
    LUALOG("[LUA] DEL script schedule %d, entryID: %d", _handler, _entryId);
}

TouchScriptHandlerEntry* TouchScriptHandlerEntry::create(int handler,
                                                             bool isMultiTouches,
                                                             int priority,
                                                             bool swallowsTouches)

TouchScriptHandlerEntry::~TouchScriptHandlerEntry()
{
}

bool TouchScriptHandlerEntry::init(bool isMultiTouches, int priority, bool swallowsTouches)
{
    _isMultiTouches = isMultiTouches;
    _priority = priority;
    _swallowsTouches = swallowsTouches;

    return true;
}

static ScriptEngineManager* s_pSharedScriptEngineManager = nullptr;

ScriptEngineManager::~ScriptEngineManager()
{
    removeScriptEngine();
}

void ScriptEngineManager::setScriptEngine(ScriptEngineProtocol *scriptEngine)
{
	if (_scriptEngine != scriptEngine)
	{
		removeScriptEngine();
		_scriptEngine = scriptEngine;
	}
}

void ScriptEngineManager::removeScriptEngine()
{
    if (_scriptEngine)
    {
        delete _scriptEngine;
        _scriptEngine = nullptr;
    }
}

ScriptEngineManager* ScriptEngineManager::getInstance()
{
    if (!s_pSharedScriptEngineManager)
    {
        s_pSharedScriptEngineManager = new (std::nothrow) ScriptEngineManager();
    }
    return s_pSharedScriptEngineManager;
}

void ScriptEngineManager::destroyInstance()
{
    if (s_pSharedScriptEngineManager)
    {
        delete s_pSharedScriptEngineManager;
        s_pSharedScriptEngineManager = nullptr;
    }
}

bool ScriptEngineManager::sendActionEventToJS(Action* actionObject, int eventType, void* param)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    ActionObjectScriptData data(actionObject,(int*)&eventType, param);
    ScriptEvent scriptEvent(kScriptActionEvent,(void*)&data);
    if (scriptEngine->sendEvent(&scriptEvent))
        return true;

    return false;
}

bool ScriptEngineManager::sendNodeEventToJS(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    if (scriptEngine->isCalledFromScript())
    {
        // Should only be invoked at root class Node
        scriptEngine->setCalledFromScript(false);
    }
    else
    {
        BasicScriptData data(node,(void*)&action);
        ScriptEvent scriptEvent(kNodeEvent,(void*)&data);
        if (scriptEngine->sendEvent(&scriptEvent))
            return true;
    }

    return false;
}

bool ScriptEngineManager::sendNodeEventToJSExtended(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    if (!scriptEngine->isCalledFromScript())
    {
        BasicScriptData data(node,(void*)&action);
        ScriptEvent scriptEvent(kNodeEvent,(void*)&data);
        if (scriptEngine->sendEvent(&scriptEvent))
            return true;
    }

    return false;
}

void ScriptEngineManager::sendNodeEventToLua(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    BasicScriptData data(node,(void*)&action);
    ScriptEvent scriptEvent(kNodeEvent,(void*)&data);

    scriptEngine->sendEvent(&scriptEvent);
}

NS_CC_END

#endif // #if CC_ENABLE_SCRIPT_BINDING


/****************************************************************************

 http://www.cocos2d-x.org

 ****************************************************************************/

#include "base/CCScriptSupport.h"

#if CC_ENABLE_SCRIPT_BINDING

#include "base/CCScheduler.h"
#include "2d/CCNode.h"

bool CC_DLL cc_assert_script_compatible(const char *msg)
{
    cocos2d::ScriptEngineProtocol* engine = cocos2d::ScriptEngineManager::getInstance()->getScriptEngine();
    if (engine && engine->handleAssert(msg))
    {
        return true;
    }
    return false;
}

NS_CC_BEGIN

ScriptHandlerEntry* ScriptHandlerEntry::create(int handler)
{
    ScriptHandlerEntry* entry = new (std::nothrow) ScriptHandlerEntry(handler);
    entry->autorelea

    return entry;
}

ScriptHandlerEntry::~ScriptHandlerEntry()
{
    if (_handler != 0 )
    {
        ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptHandler(_handler);
        LUALOG("[LUA] Remove event handler: %d", _handler);
        _handler = 0;
    }
}

SchedulerScriptHandlerEntry* SchedulerScriptHandlerEntry::create(int handler, float interval, bool paused)
{
    SchedulerScriptHandlerEntry* entry = new (std::nothrow) SchedulerScriptHandlerEntry(handler);
    entry->init(interval, paused);
    entry->autorelease();
    return entry;
}

bool SchedulerScriptHandlerEntry::init(float interval, bool paused)
{
    _timer = new (std::nothrow) TimerScriptHandler();
    _timer->initWithScriptHandler(_handler, interval);
    _paused = paused;
    LUALOG("[LUA] ADD script schedule: %d, entryID: %d", _handler, _entryId);
    return true;
}

SchedulerScriptHandlerEntry::~SchedulerScriptHandlerEntry()
{
    _timer->release();
    LUALOG("[LUA] DEL script schedule %d, entryID: %d", _handler, _entryId);
}

TouchScriptHandlerEntry* TouchScriptHandlerEntry::create(int handler,
                                                             bool isMultiTouches,
                                                             int priority,
                                                             bool swallowsTouches)
{
    TouchScriptHandlerEntry* entry = new (std::nothrow) TouchScriptHandlerEntry(handler);
    entry->init(isMultiTouches, priority, swallowsTouches);
    entry->autorelease();
    return entry;
}

TouchScriptHandlerEntry::~TouchScriptHandlerEntry()
{
}

bool TouchScriptHandlerEntry::init(bool isMultiTouches, int priority, bool swallowsTouches)
{
    _isMultiTouches = isMultiTouches;
    _priority = priority;
    _swallowsTouches = swallowsTouches;

    return true;
}

static ScriptEngineManager* s_pSharedScriptEngineManager = nullptr;

ScriptEngineManager::~ScriptEngineManager()
{
    removeScriptEngine();
}

void ScriptEngineManager::setScriptEngine(ScriptEngineProtocol *scriptEngine)
{
	if (_scriptEngine != scriptEngine)
	{
		removeScriptEngine();
		_scriptEngine = scriptEngine;
	}
}

void ScriptEngineManager::removeScriptEngine()
{
    if (_scriptEngine)
    {
        delete _scriptEngine;
        _scriptEngine = nullptr;
    }
}

ScriptEngineManager* ScriptEngineManager::getInstance()
{
    if (!s_pSharedScriptEngineManager)
    {
        s_pSharedScriptEngineManager = new (std::nothrow) ScriptEngineManager();
    }
    return s_pSharedScriptEngineManager;
}

void ScriptEngineManager::destroyInstance()
{
    if (s_pSharedScriptEngineManager)
    {
        delete s_pSharedScriptEngineManager;
        s_pSharedScriptEngineManager = nullptr;
    }
}

bool ScriptEngineManager::sendActionEventToJS(Action* actionObject, int eventType, void* param)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    ActionObjectScriptData data(actionObject,(int*)&eventType, param);
    ScriptEvent scriptEvent(kScriptActionEvent,(void*)&data);
    if (scriptEngine->sendEvent(&scriptEvent))
        return true;

    return false;
}

bool ScriptEngineManager::sendNodeEventToJS(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    if (scriptEngine->isCalledFromScript())
    {
        // Should only be invoked at root class Node
        scriptEngine->setCalledFromScript(false);
    }
    else
    {
        BasicScriptData data(node,(void*)&action);
        ScriptEvent scriptEvent(kNodeEvent,(void*)&data);
        if (scriptEngine->sendEvent(&scriptEvent))
            return true;
    }

    return false;
}

bool ScriptEngineManager::sendNodeEventToJSExtended(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    if (!scriptEngine->isCalledFromScript())
    {
        BasicScriptData data(node,(void*)&action);
        ScriptEvent scriptEvent(kNodeEvent,(void*)&data);
        if (scriptEngine->sendEvent(&scriptEvent))
            return true;
    }

    return false;
}

void ScriptEngineManager::sendNodeEventToLua(Node* node, int action)
{
    auto scriptEngine = getInstance()->getScriptEngine();

    BasicScriptData data(node,(void*)&action);
    ScriptEvent scriptEvent(kNodeEvent,(void*)&data);

    scriptEngine->sendEvent(&scriptEvent);
}

NS_CC_END

#endif // #if CC_ENABLE_SCRIPT_BINDING


#include "lcadviewerproxy.h"

using namespace lc::ui;

#include <QVBoxLayout>

using namespace lc::ui;

LCADViewerProxy::LCADViewerProxy(QWidget* parent=0) {
    auto gridLayout = new QGridLayout(this);
    gridLayout->setHorizontalSpacing(0);
    gridLayout->setVerticalSpacing(0);
    gridLayout->setObjectName(QStringLiteral("gridLayout"));
    gridLayout->setContentsMargins(0, 0, 0, 0);

    _tabWidget = new QTabWidget(this);

    _modelViewerImpl = new LCADModelViewerImpl(this);
    _modelViewerImpl->setObjectName(QStringLiteral("viewer"));
    _modelViewerImpl->setGeometry(QRect(50, 30, 581, 401));
    _modelViewerImpl->setAutoFillBackground(true);
    _modelViewerImpl->setContextMenuPolicy(Qt::CustomContextMenu);
    _modelViewerImpl->setFocusPolicy(Qt::StrongFocus);

    gridLayout->addWidget(_tabWidget, 0, 0, 1, 1);

    _tabWidget->addTab(_modelViewerImpl, tr("Model"));
    _tabWidget->addTab(_modelViewerImpl, tr("Model"));//Just for init, removed later
    _tabWidget->addTab(_modelViewerImpl, tr("Model"));

    _paperViewers = new LCADPaperViewerImpl(this);

    //Connections to receive active View
    connect(_modelViewerImpl, SIGNAL(setActiveView(LCADViewer*,bool)), this, SLOT(setActive(LCADViewer*,bool)));
    connect(_paperViewers, SIGNAL(setActiveView(LCADViewer*,bool)), this, SLOT(setActive(LCADViewer*,bool)));

    //Init connections now
    _isModel = true;
    _activeView = _modelViewerImpl->viewer();
    connect(_activeView, SIGNAL(mouseMoveEvent()), this, SIGNAL(mouseMoveEvent()));
    connect(_activeView, SIGNAL(mousePressEvent()), this, SIGNAL(mousePressEvent()));
    connect(_activeView, SIGNAL(mouseReleaseEvent()), this, SIGNAL(mouseReleaseEvent()));
    connect(_activeView, SIGNAL(selectionChangeEvent()), this, SIGNAL(selectionChangeEvent()));
    connect(_activeView, SIGNAL(keyPressEvent(int)), this, SIGNAL(keyPressEvent(int)));
}

{
    _modelViewerImpl->setDocument(document);
    _paperViewers->setDocument(document);

    _tabWidget->removeTab(1);
    _tabWidget->removeTab(1);

    //For each block create viewport
    auto view = document->blockByName("*Paper_Space");
    auto x = _paperViewers->getViewer();
    x->setDocument(document,view);
    _tabWidget->addTab(x,tr("Paper 1"));

    view = document->blockByName("*Paper_Space0");
    x = _paperViewers->getViewer();
    x->setDocument(document,view);
    _tabWidget->addTab(x,tr("Paper 2"));
}

void LCADViewerProxy::setActive(LCADViewer* view,bool isModel) {
    if(view!=_activeView) {
        _isModel = isModel;
        disconnect(_activeView, SIGNAL(mouseMoveEvent()), this, SIGNAL(mouseMoveEvent()));
        disconnect(_activeView, SIGNAL(mousePressEvent()), this, SIGNAL(mousePressEvent()));
        disconnect(_activeView, SIGNAL(mouseReleaseEvent()), this, SIGNAL(mouseReleaseEvent()));
        disconnect(_activeView, SIGNAL(selectionChangeEvent()), this, SIGNAL(selectionChangeEvent()));
        disconnect(_activeView, SIGNAL(keyPressEvent(int)), this, SIGNAL(keyPressEvent(int)));

        _activeView = view;
        connect(_activeView, SIGNAL(mouseMoveEvent()), this, SIGNAL(mouseMoveEvent()));
        connect(_activeView, SIGNAL(mousePressEvent()), this, SIGNAL(mousePressEvent()));
        connect(_activeView, SIGNAL(mouseReleaseEvent()), this, SIGNAL(mouseReleaseEvent()));
        connect(_activeView, SIGNAL(selectionChangeEvent()), this, SIGNAL(selectionChangeEvent()));
        connect(_activeView, SIGNAL(keyPressEvent(int)), this, SIGNAL(keyPressEvent(int)));
    }
}


#include "lcadviewerproxy.h"

using namespace lc::ui;

#include <QVBoxLayout>

using namespace lc::ui;

LCADViewerProxy::LCADViewerProxy(QWidget* parent=0) {
    auto gridLayout = new QGridLayout(this);
    gridLayou

    gridLayout->setVerticalSpacing(0);
    gridLayout->setObjectName(QStringLiteral("gridLayout"));
    gridLayout->setContentsMargins(0, 0, 0, 0);

    _tabWidget = new QTabWidget(this);

    _modelViewerImpl = new LCADModelViewerImpl(this);
    _modelViewerImpl->setObjectName(QStringLiteral("viewer"));
    _modelViewerImpl->setGeometry(QRect(50, 30, 581, 401));
    _modelViewerImpl->setAutoFillBackground(true);
    _modelViewerImpl->setContextMenuPolicy(Qt::CustomContextMenu);
    _modelViewerImpl->setFocusPolicy(Qt::StrongFocus);

    gridLayout->addWidget(_tabWidget, 0, 0, 1, 1);

    _tabWidget->addTab(_modelViewerImpl, tr("Model"));
    _tabWidget->addTab(_modelViewerImpl, tr("Model"));//Just for init, removed later
    _tabWidget->addTab(_modelViewerImpl, tr("Model"));

    _paperViewers = new LCADPaperViewerImpl(this);

    //Connections to receive active View
    connect(_modelViewerImpl, SIGNAL(setActiveView(LCADViewer*,bool)), this, SLOT(setActive(LCADViewer*,bool)));
    connect(_paperViewers, SIGNAL(setActiveView(LCADViewer*,bool)), this, SLOT(setActive(LCADViewer*,bool)));

    //Init connections now
    _isModel = true;
    _activeView = _modelViewerImpl->viewer();
    connect(_activeView, SIGNAL(mouseMoveEvent()), this, SIGNAL(mouseMoveEvent()));
    connect(_activeView, SIGNAL(mousePressEvent()), this, SIGNAL(mousePressEvent()));
    connect(_activeView, SIGNAL(mouseReleaseEvent()), this, SIGNAL(mouseReleaseEvent()));
    connect(_activeView, SIGNAL(selectionChangeEvent()), this, SIGNAL(selectionChangeEvent()));
    connect(_activeView, SIGNAL(keyPressEvent(int)), this, SIGNAL(keyPressEvent(int)));
}

void LCADViewerProxy::setDocument(std::shared_ptr<lc::storage::Document> document) {
    _modelViewerImpl->setDocument(document);
    _paperViewers->setDocument(document);

    _tabWidget->removeTab(1);
    _tabWidget->removeTab(1);

    //For each block create viewport
    auto view = document->blockByName("*Paper_Space");
    auto x = _paperViewers->getViewer();
    x->setDocument(document,view);
    _tabWidget->addTab(x,tr("Paper 1"));

    view = document->blockByName("*Paper_Space0");
    x = _paperViewers->getViewer();
    x->setDocument(document,view);
    _tabWidget->addTab(x,tr("Paper 2"));
}

void LCADViewerProxy::setActive(LCADViewer* view,bool isModel) {
    if(view!=_activeView) {
        _isModel = isModel;
        disconnect(_activeView, SIGNAL(mouseMoveEvent()), this, SIGNAL(mouseMoveEvent()));
        disconnect(_activeView, SIGNAL(mousePressEvent()), this, SIGNAL(mousePressEvent()));
        disconnect(_activeView, SIGNAL(mouseReleaseEvent()), this, SIGNAL(mouseReleaseEvent()));
        disconnect(_activeView, SIGNAL(selectionChangeEvent()), this, SIGNAL(selectionChangeEvent()));
        disconnect(_activeView, SIGNAL(keyPressEvent(int)), this, SIGNAL(keyPressEvent(int)));

        _activeView = view;
        connect(_activeView, SIGNAL(mouseMoveEvent()), this, SIGNAL(mouseMoveEvent()));
        connect(_activeView, SIGNAL(mousePressEvent()), this, SIGNAL(mousePressEvent()));
        connect(_activeView, SIGNAL(mouseReleaseEvent()), this, SIGNAL(mouseReleaseEvent()));
        connect(_activeView, SIGNAL(selectionChangeEvent()), this, SIGNAL(selectionChangeEvent()));
        connect(_activeView, SIGNAL(keyPressEvent(int)), this, SIGNAL(keyPressEvent(int)));
    }
}


#include "testing/testing.hpp"

#include "platform/platform_tests_support/scoped_mwm.cpp"

#include "indexer/indexer_tests/test_mwm_set.hpp"

#include "indexer/mwm_set.hpp"

#include "base/macros.hpp"

#include <initializer_list>
#include <unordered_map>

using namespace std;
using platform::CountryFile;
using platform::LocalCountryFile;
using tests::TestMwmSet;
using namespace platform::tests_support;

using MwmsInfo = unordered_map<string, shared_ptr<MwmInfo>>;

namespace
{
void GetMwmsInfo(MwmSet const & mwmSet, MwmsInfo & mwmsInfo)
{
  vector<shared_ptr<MwmInfo>> mwmsInfoList;
  mwmSet.GetMwmsInfo(mwmsInfoList);

  mwmsInfo.clear();
  for (shared_ptr<MwmInfo> const & info : mwmsInfoList)
    mwmsInfo[info->GetCountryName()] = info;
}

void TestFilesPresence(MwmsInfo const & mwmsInfo, initializer_list<string> const & expectedNames)
{
  TEST_EQUAL(expectedNames.size(), mwmsInfo.size(), ());
  for (string const & countryFileName : expectedNames)
    TEST_EQUAL(1, mwmsInfo.count(countryFileName), (countryFileName));
}
}  // namespace

UNIT_TEST(MwmSetSmokeTest)
{
  TestMwmSet mwmSet;
  MwmsInfo mwmsInfo;

  ScopedMwm mwm0("0.mwm");
  ScopedMwm mwm1("1.mwm");
  ScopedMwm mwm2("2.mwm");
  ScopedMwm mwm3("3.mwm");
  ScopedMwm mwm4("4.mwm");
  ScopedMwm mwm5("5.mwm");

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("0")));
  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("1")));
  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("2")));
  mwmSet.Deregister(CountryFile("1"));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  {
    MwmSet::MwmHandle const handle0 = mwmSet.GetMwmHandleByCountryFile(CountryFile("0"));
    MwmSet::MwmHandle const handle1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("1"));
    TEST(handle0.IsAlive(), ());
    TEST(!handle1.IsAlive(), ());
  }

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("3")));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "3"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["3"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["3"]->m_maxScale, 3, ());

  {
    MwmSet::MwmHandle const handle1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("1"));
    TEST(!handle1.IsAlive(), ());
    mwmSet.Deregister(CountryFile("3"));
    UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("4")));
  }

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "4"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["4"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["4"]->m_maxScale, 4, ());

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("5")));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "4", "5"});

  TEST_EQUAL(mwmsInfo.size(), 4, ());
  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["4"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["4"]->m_maxScale, 4, ());
  TEST(mwmsInfo["5"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["5"]->m_maxScale, 5, ());
}

{
  ScopedMwm mwm3("3.mwm");

  TestMwmSet mwmSet;
  TEST_EQUAL(MwmSet::RegResult::Success,
             mwmSet.Register(LocalCountryFile::MakeForTesting("3")).second, ());

  MwmSet::MwmId const id0 = mwmSet.GetMwmHandleByCountryFile(CountryFile("3")).GetId();
  MwmSet::MwmId const id1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("3")).GetId();

  TEST(id0.IsAlive(), ());
  TEST(id1.IsAlive(), ());
  TEST_EQUAL(id0.GetInfo().get(), id1.GetInfo().get(), ());
  TEST_EQUAL(MwmInfo::STATUS_REGISTERED, id0.GetInfo()->GetStatus(), ());

  TEST(mwmSet.Deregister(CountryFile("3")), ());

  // Test that both id's are sour now.
  TEST(!id0.IsAlive(), ());
  TEST(!id1.IsAlive(), ());
  TEST_EQUAL(id0.GetInfo().get(), id1.GetInfo().get(), ());
  TEST_EQUAL(MwmInfo::STATUS_DEREGISTERED, id0.GetInfo()->GetStatus(), ());
}

UNIT_TEST(MwmSetLockAndIdTest)
{
  ScopedMwm mwm4("4.mwm");
  TestMwmSet mwmSet;
  MwmSet::MwmId id;

  {
    auto p = mwmSet.Register(LocalCountryFile::MakeForTesting("4"));
    MwmSet::MwmHandle handle = mwmSet.GetMwmHandleById(p.first);
    TEST(handle.IsAlive(), ());
    TEST_EQUAL(MwmSet::RegResult::Success, p.second, ("Can't register test mwm 4"));
    TEST_EQUAL(MwmInfo::STATUS_REGISTERED, handle.GetInfo()->GetStatus(), ());

    TEST(!mwmSet.Deregister(CountryFile("4")), ());  // It's not possible to remove mwm 4 right now.
    TEST(handle.IsAlive(), ());
    TEST_EQUAL(MwmInfo::STATUS_MARKED_TO_DEREGISTER, handle.GetInfo()->GetStatus(), ());
    id = handle.GetId();
    TEST(id.IsAlive(), ());
  }

  TEST(!id.IsAlive(), ());       // Mwm is not alive, so id is sour now.
  TEST(id.GetInfo().get(), ());  // But it's still possible to get an MwmInfo.
  TEST_EQUAL(MwmInfo::STATUS_DEREGISTERED, id.GetInfo()->GetStatus(), ());
  TEST_EQUAL(4, id.GetInfo()->m_maxScale, ());

  // It is not possible to lock mwm 4 because it is already deleted,
  // and it is not possible to get to it's info from mwmSet.
  MwmSet::MwmHandle handle = mwmSet.GetMwmHandleByCountryFile(CountryFile("4"));
  TEST(!handle.IsAlive(), ());
  TEST(!handle.GetId().IsAlive(), ());
  TEST(!handle.GetId().GetInfo().get(), ());
}


{
  vector<shared_ptr<MwmInfo>> mwmsInfoList;
  mwmSet.GetMwmsInfo(mwmsInfoList);

  mwmsInfo.clear();
  for (shared_ptr<MwmInfo> const & info : mwmsInfoList)
    mwmsInfo[info->GetCountryName()] = info;
}

#include "testing/testing.hpp"

#include "platform/platform_tests_support/scoped_mwm.cpp"

#include "indexer/indexer_tests/test_mwm_set.hpp"

#include "indexer/mwm_set.hpp"

#include "base/macros.hpp"

#include <initializer_list>
#include <unordered_map>

using namespace std;
using platform::CountryFile;
using platform::LocalCountryFile;
using tests::TestMwmSet;
using namespace platform::tests_support;

using MwmsInfo = unordered_map<string, shared_ptr<MwmInfo>>;

namespace
{
void GetMwmsInfo(MwmSet const & mwmSet, MwmsInfo & mwmsInfo)

void TestFilesPresence(MwmsInfo const & mwmsInfo, initializer_list<string> const & expectedNames)
{
  TEST_EQUAL(expectedNames.size(), mwmsInfo.size(), ());
  for (string const & countryFileName : expectedNames)
    TEST_EQUAL(1, mwmsInfo.count(countryFileName), (countryFileName));
}
}  // namespace

UNIT_TEST(MwmSetSmokeTest)
{
  TestMwmSet mwmSet;
  MwmsInfo mwmsInfo;

  ScopedMwm mwm0("0.mwm");
  ScopedMwm mwm1("1.mwm");
  ScopedMwm mwm2("2.mwm");
  ScopedMwm mwm3("3.mwm");
  ScopedMwm mwm4("4.mwm");
  ScopedMwm mwm5("5.mwm");

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("0")));
  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("1")));
  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("2")));
  mwmSet.Deregister(CountryFile("1"));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  {
    MwmSet::MwmHandle const handle0 = mwmSet.GetMwmHandleByCountryFile(CountryFile("0"));
    MwmSet::MwmHandle const handle1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("1"));
    TEST(handle0.IsAlive(), ());
    TEST(!handle1.IsAlive(), ());
  }

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("3")));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "3"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["3"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["3"]->m_maxScale, 3, ());

  {
    MwmSet::MwmHandle const handle1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("1"));
    TEST(!handle1.IsAlive(), ());
    mwmSet.Deregister(CountryFile("3"));
    UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("4")));
  }

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "4"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["4"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["4"]->m_maxScale, 4, ());

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("5")));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "4", "5"});

  TEST_EQUAL(mwmsInfo.size(), 4, ());
  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["4"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["4"]->m_maxScale, 4, ());
  TEST(mwmsInfo["5"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["5"]->m_maxScale, 5, ());
}

UNIT_TEST(MwmSetIdTest)
{
  ScopedMwm mwm3("3.mwm");

  TestMwmSet mwmSet;
  TEST_EQUAL(MwmSet::RegResult::Success,
             mwmSet.Register(LocalCountryFile::MakeForTesting("3")).second, ());

  MwmSet::MwmId const id0 = mwmSet.GetMwmHandleByCountryFile(CountryFile("3")).GetId();
  MwmSet::MwmId const id1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("3")).GetId();

  TEST(id0.IsAlive(), ());
  TEST(id1.IsAlive(), ());
  TEST_EQUAL(id0.GetInfo().get(), id1.GetInfo().get(), ());
  TEST_EQUAL(MwmInfo::STATUS_REGISTERED, id0.GetInfo()->GetStatus(), ());

  TEST(mwmSet.Deregister(CountryFile("3")), ());

  // Test that both id's are sour now.
  TEST(!id0.IsAlive(), ());
  TEST(!id1.IsAlive(), ());
  TEST_EQUAL(id0.GetInfo().get(), id1.GetInfo().get(), ());
  TEST_EQUAL(MwmInfo::STATUS_DEREGISTERED, id0.GetInfo()->GetStatus(), ());
}

UNIT_TEST(MwmSetLockAndIdTest)
{
  ScopedMwm mwm4("4.mwm");
  TestMwmSet mwmSet;
  MwmSet::MwmId id;

  {
    auto p = mwmSet.Register(LocalCountryFile::MakeForTesting("4"));
    MwmSet::MwmHandle handle = mwmSet.GetMwmHandleById(p.first);
    TEST(handle.IsAlive(), ());
    TEST_EQUAL(MwmSet::RegResult::Success, p.second, ("Can't register test mwm 4"));
    TEST_EQUAL(MwmInfo::STATUS_REGISTERED, handle.GetInfo()->GetStatus(), ());

    TEST(!mwmSet.Deregister(CountryFile("4")), ());  // It's not possible to remove mwm 4 right now.
    TEST(handle.IsAlive(), ());
    TEST_EQUAL(MwmInfo::STATUS_MARKED_TO_DEREGISTER, handle.GetInfo()->GetStatus(), ());
    id = handle.GetId();
    TEST(id.IsAlive(), ());
  }

  TEST(!id.IsAlive(), ());       // Mwm is not alive, so id is sour now.
  TEST(id.GetInfo().get(), ());  // But it's still possible to get an MwmInfo.
  TEST_EQUAL(MwmInfo::STATUS_DEREGISTERED, id.GetInfo()->GetStatus(), ());
  TEST_EQUAL(4, id.GetInfo()->m_maxScale, ());

  // It is not possible to lock mwm 4 because it is already deleted,
  // and it is not possible to get to it's info from mwmSet.
  MwmSet::MwmHandle handle = mwmSet.GetMwmHandleByCountryFile(CountryFile("4"));
  TEST(!handle.IsAlive(), ());
  TEST(!handle.GetId().IsAlive(), ());
  TEST(!handle.GetId().GetInfo().get(), ());
}


#include "testing/testing.hpp"

#include "platform/platform_tests_support/scoped_mwm.cpp"

#include "indexer/indexer_tests/test_mwm_set.hpp"

#include "indexer/mwm_set.hpp"

#include "base/macros.hpp"

#include <initializer_list>
#include <unordered_map>

using namespace std;
using platform::CountryFile;
using platform::LocalCountryFile;
using tests::TestMwmSet;
using namespace platform::tests_support;

using MwmsInfo = unordered_map<string, shared_ptr<MwmInfo>>;

namespace
{
void GetMwmsInfo(MwmSet const & mwmSet, MwmsInfo & mwmsInfo)
{
  vector<shared_ptr<MwmInfo>> mwmsInfoList;
  mwmSet.GetMwmsInfo(mwmsInfoList);

  mwmsInfo.clear();
  for (shared_ptr<MwmInfo> const & info : mwmsInfoList)
    mwmsInfo[info->GetCountryName()] = info;
}

void TestFilesPresence(MwmsInfo const & mwmsInfo, initializer_list<string> const & expectedNames)
{
  TEST_EQUAL(expectedNames.size(), mwmsInfo.size(), ());
  for (string const & countryFileName : expectedNames)
    TEST_EQUAL(1, mwmsInfo.count(countryFileName), (countryFileName));
}
}  // namespace

UNIT_TEST(MwmSetSmokeTest)
{
  TestMwmSet mwmSet;
  MwmsInfo mwmsInfo;

  ScopedMwm mwm0("0.mwm");
  ScopedMwm mwm1("1.mwm");
  ScopedMwm mwm2("2.mwm");
  ScopedMwm mwm3("3.mwm");
  ScopedMwm mwm4("4.mwm");
  ScopedMwm mwm5("5.mwm");

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("0")));
  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("1")));
  UNUSED_VALUE(mwmSet.Register(

  mwmSet.Deregister(CountryFile("1"));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  {
    MwmSet::MwmHandle const handle0 = mwmSet.GetMwmHandleByCountryFile(CountryFile("0"));
    MwmSet::MwmHandle const handle1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("1"));
    TEST(handle0.IsAlive(), ());
    TEST(!handle1.IsAlive(), ());
  }

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("3")));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "3"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["3"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["3"]->m_maxScale, 3, ());

  {
    MwmSet::MwmHandle const handle1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("1"));
    TEST(!handle1.IsAlive(), ());
    mwmSet.Deregister(CountryFile("3"));
    UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("4")));
  }

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "4"});

  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["4"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["4"]->m_maxScale, 4, ());

  UNUSED_VALUE(mwmSet.Register(LocalCountryFile::MakeForTesting("5")));

  GetMwmsInfo(mwmSet, mwmsInfo);
  TestFilesPresence(mwmsInfo, {"0", "2", "4", "5"});

  TEST_EQUAL(mwmsInfo.size(), 4, ());
  TEST(mwmsInfo["0"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["0"]->m_maxScale, 0, ());
  TEST(mwmsInfo["2"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["2"]->m_maxScale, 2, ());
  TEST(mwmsInfo["4"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["4"]->m_maxScale, 4, ());
  TEST(mwmsInfo["5"]->IsUpToDate(), ());
  TEST_EQUAL(mwmsInfo["5"]->m_maxScale, 5, ());
}

UNIT_TEST(MwmSetIdTest)
{
  ScopedMwm mwm3("3.mwm");

  TestMwmSet mwmSet;
  TEST_EQUAL(MwmSet::RegResult::Success,
             mwmSet.Register(LocalCountryFile::MakeForTesting("3")).second, ());

  MwmSet::MwmId const id0 = mwmSet.GetMwmHandleByCountryFile(CountryFile("3")).GetId();
  MwmSet::MwmId const id1 = mwmSet.GetMwmHandleByCountryFile(CountryFile("3")).GetId();

  TEST(id0.IsAlive(), ());
  TEST(id1.IsAlive(), ());
  TEST_EQUAL(id0.GetInfo().get(), id1.GetInfo().get(), ());
  TEST_EQUAL(MwmInfo::STATUS_REGISTERED, id0.GetInfo()->GetStatus(), ());

  TEST(mwmSet.Deregister(CountryFile("3")), ());

  // Test that both id's are sour now.
  TEST(!id0.IsAlive(), ());
  TEST(!id1.IsAlive(), ());
  TEST_EQUAL(id0.GetInfo().get(), id1.GetInfo().get(), ());
  TEST_EQUAL(MwmInfo::STATUS_DEREGISTERED, id0.GetInfo()->GetStatus(), ());
}

UNIT_TEST(MwmSetLockAndIdTest)
{
  ScopedMwm mwm4("4.mwm");
  TestMwmSet mwmSet;
  MwmSet::MwmId id;

  {
    auto p = mwmSet.Register(LocalCountryFile::MakeForTesting("4"));
    MwmSet::MwmHandle handle = mwmSet.GetMwmHandleById(p.first);
    TEST(handle.IsAlive(), ());
    TEST_EQUAL(MwmSet::RegResult::Success, p.second, ("Can't register test mwm 4"));
    TEST_EQUAL(MwmInfo::STATUS_REGISTERED, handle.GetInfo()->GetStatus(), ());

    TEST(!mwmSet.Deregister(CountryFile("4")), ());  // It's not possible to remove mwm 4 right now.
    TEST(handle.IsAlive(), ());
    TEST_EQUAL(MwmInfo::STATUS_MARKED_TO_DEREGISTER, handle.GetInfo()->GetStatus(), ());
    id = handle.GetId();
    TEST(id.IsAlive(), ());
  }

  TEST(!id.IsAlive(), ());       // Mwm is not alive, so id is sour now.
  TEST(id.GetInfo().get(), ());  // But it's still possible to get an MwmInfo.
  TEST_EQUAL(MwmInfo::STATUS_DEREGISTERED, id.GetInfo()->GetStatus(), ());
  TEST_EQUAL(4, id.GetInfo()->m_maxScale, ());

  // It is not possible to lock mwm 4 because it is already deleted,
  // and it is not possible to get to it's info from mwmSet.
  MwmSet::MwmHandle handle = mwmSet.GetMwmHandleByCountryFile(CountryFile("4"));
  TEST(!handle.IsAlive(), ());
  TEST(!handle.GetId().IsAlive(), ());
  TEST(!handle.GetId().GetInfo().get(), ());
}


// includes

#include "stdpch.h"

#include "game_share/small_string_manager.h"

using namespace R2;

uint32 CSmallStringManager::registerString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator found(_StringToId.find(phrase));
	//new text entered
	uint32 id;
	if (found == _StringToId.end())
	{
		id = getNewId();
		_StringToId.insert(std::pair<std::string, uint32>(phrase, id));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(id, std::pair<std::string, uint32>(phrase, nb) ) );
	}
	else //already registered text, increment counter
	{
		id = found->second;
		std::map<uint32, std::pair<std::string, uint32> >::iterator  found2	(_IdToString.find(found->second));
		found2->second.second += nb;
	}
	//return the id of the text
	return id;
}

void CSmallStringManager::unregisterString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator  found(_StringToId.find(phrase));
	nlassert(found != _StringToId.end());

	uint32 id = found->second;
	unregisterStringById(id, nb);

}

void CSmallStringManager::unregisterStringById(uint32 id, uint32 nb)
{
	std::map<uint32, std::pair<std::string, uint32> >::iterator  found2(_IdToString.find(id));
	if (found2 == _IdToString.end())
	{
		nlwarning("try to unload a unregistered table");
		return;
	}
	uint32& count = found2->second.second;
	if (count < nb) { count = 0; }
	count -= nb;
	if (count == 0)
	{
		std::map<std::string, uint32>::iterator found = _StringToId.find(found2->second.first);
		if (found != _StringToId.end()) { _StringToId.erase(found); }

		_FreeIds.insert(found2->first);
		_IdToString.erase(found2);
	}

}

{
	//TMP
	if (!_FreeIds.empty())
	{
		std::set< uint32 >::iterator it = _FreeIds.begin();
		uint32 id = *it;
		_FreeIds.erase( it );
		return id;
	}
	_MaxId = _MaxId +1;
	return _MaxId-1;
}

const std::string& CSmallStringManager::getString(uint32 id) const
{
	static const std::string empty("Wrong id");
	std::map<uint32,std::pair<std::string,uint32> >::const_iterator  found(_IdToString.find(id));
	if (found == _IdToString.end()){ nlwarning("Try to access un accessible string"); return empty;}
	return found->second.first;
}

CSmallStringManager::CSmallStringManager()
{
	_MaxId=0;
}

CSmallStringManager::CSmallStringManager(CObject* textManager)
{
	if(textManager==NULL)
	{
		_MaxId = 0;
		return;
	}
	CObject* texts = textManager->getAttr("Texts");
	uint32 maxId = static_cast<uint32>(textManager->getAttr("MaxId")->toInteger());
	CObject* unused = textManager->getAttr("UnusedIds");
	uint32 max = unused->getSize();

	//_MaxId
	_MaxId = maxId;

	//unused ids
	for(uint32 i=0;i<max;i++)
	{
		uint32 id = static_cast<uint32>(unused->getValueAtPos(i)->toInteger());
		_FreeIds.insert(id);
	}

	//texts
	max = texts->getSize();
	for(uint32 i=0;i<max;i++)
	{
		CObject* entry = texts->getValueAtPos(i);
		std::string text = entry->getAttr("Text")->toString();
		uint32 textId = static_cast<uint32>(entry->getAttr("TextId")->toInteger());
		uint32 textCount = static_cast<uint32>(entry->getAttr("Count")->toInteger());
		_StringToId.insert(std::pair<std::string, uint32>(text, textId));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(textId, std::pair<std::string, uint32>(text, textCount) ) );
	}
}

void CSmallStringManager::serial(NLMISC::IStream& stream)
{
	//serialize maxId
	stream.serial(_MaxId);
	//serialize the number of free ids
	uint32 tmp = (uint32)_FreeIds.size();
	stream.serial(tmp);
	//serialize the free ids
	std::set< uint32 >::const_iterator first(_FreeIds.begin()),last(_FreeIds.end());
	for(;first!=last;++first)
	{
		tmp = *first;
		stream.serial(tmp);
	}
	tmp = (uint32)_IdToString.size();
	nlassert(tmp==_StringToId.size());

	//serialize the number of entries
	stream.serial(tmp);

	//serialize the entries
	std::map<uint32, std::pair<std::string, uint32> >::const_iterator first2(_IdToString.begin()),last2(_IdToString.end());
	for(;first2!=last2;++first2)
	{
		//serialize the entry's id
		tmp = first2->first;
		stream.serial(tmp);
		//serialize the entry's text
		std::string tmp2 = first2->second.first;
		stream.serial(tmp2);
		//serialize the entry's count
		tmp = first2->second.second;
		stream.serial(tmp);
	}
}

void CSmallStringManager::release()
{

	_StringToId.clear();

	_IdToString.clear();

	_FreeIds.clear();;

	_MaxId = 0;

}

CSmallStringManager::~CSmallStringManager()
{
	release();

}

void CStringTableManager::createTable(uint32 tableId)
{
	TLocalTable *locTable = new TLocalTable();
	if ( !_LocalTables.insert( std::make_pair(tableId, locTable)).second )
	{
		nlwarning("Table already exist %u", tableId);
		delete locTable;
		return;
	}
}

//set the new value for an entry of a local table
void CStringTableManager::setValue(uint32 tableId,const std::string& localId, const std::string& value)
{
	TLocalTable * localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		nlwarning("unknown scenario Id or no string table for this scenario! : %d",tableId);
		return;
	}

	TLocalTable::iterator found2 = localTable->find(localId);
	if(found2!=localTable->end())
	{
		std::string oldValue = _StringMgr.getString(found2->second);
		if (oldValue.empty()) nlwarning("error! no string %u in string manager! ",found2->second);
		_StringMgr.unregisterString(oldValue);
		found2->second = _StringMgr.registerString(value);
	}
	else
	{
		uint32 key = _StringMgr.registerString(value);
		localTable->insert(std::make_pair(localId, key));
	}

}

std::string CStringTableManager::getValue(uint32 tableId, const std::string& localId) const
{
	// Search the table
	TLocalTable* localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		return localId;
	}

	// Searche the key in the table
	TLocalTable::const_iterator found2 = localTable->find(localId);
	if(found2 == localTable->end())
	{
		return localId;
	}

	//get the value
	return _StringMgr.getString(found2->second);

}

CStringTableManager::TLocalTable* CStringTableManager::getLocalTable(uint32 tableId) const
{
	TLocalTables::const_iterator found = _LocalTables.find(tableId);
	if(found != _LocalTables.end()) { return found->second; }
	return NULL;
}

CStringTableManager::~CStringTableManager()
{
	release();
}

void CStringTableManager::release()
{
	_StringMgr.release();

	TLocalTables::iterator first(_LocalTables.begin()), 	last(_LocalTables.end());

	for( ; first != last; ++first)
	{

		TLocalTable* table = first->second;
		delete table;
	}
	_LocalTables.clear();

}

void CStringTableManager::releaseTable(uint32 tableId)
{

	TLocalTables::iterator found( _LocalTables.find(tableId));
	if ( found != _LocalTables.end())
	{
		TLocalTable* table = found->second;
		if (table)
		{
			TLocalTable::const_iterator first(table->begin()), last(table->end());
			for (; first != last; ++first)
			{
				_StringMgr.unregisterStringById( first->second);
			}
			delete table;
		}

		_LocalTables.erase(found);
	}
}


// includes

#include "stdpch.h"

#include "game_share/small_string_manager.h"

using namespace R2;

uint32 CSmallStringManager::registerString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator found(_StringToId.find(phrase));
	//new text entered
	uint32 id;
	if (found == _StringToId.end())
	{
		id = getNewId();
		_StringToId.insert(std::pair<std::string, uint32>(phrase, id));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(id, std::pair<std::string, uint32>(phrase, nb) ) );
	}
	else //already registered text, increment counter
	{
		id = found->second;
		std::map<uint32, std::pair<std::string, uint32> >::iterator  found2	(_IdToString.find(found->second));
		found2->second.second += nb;
	}
	//return the id of the text
	return id;
}

void CSmallStringManager::unregisterString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator  found(_StringToId.find(phrase));
	nlassert(found != _StringToId.end());

	uint32 id = found->second;
	unregisterStringById(id, nb);

}

void CSmallStringManager::unregisterStringById(uint32 id, uint32 nb)
{
	std::map<uint32, std::pair<std::string, uint32> >::iterator  found2(_IdToString.find(id));
	if (found2 == _IdToString.end())
	{
		nlwarning("try to unload a unregistered table");
		return;
	}
	uint32& count = found2->second.second;
	if (count < nb) { count = 0; }
	count -= nb;
	if (count == 0)
	{
		std::map<std::string, uint32>::iterator found = _StringToId.find(found2->second.first);
		if (found != _StringToId.end()) { _StringToId.erase(found); }

		_FreeIds.insert(found2->first);
		_IdToString.erase(found2);
	}

}

uint32 CSmallStringManager::getNewId()
{
	//TMP
	if (!_FreeIds.empty())
	{
		std::set< uint32 >::iterator it = _FreeIds.begin();
		uint32 id = *it;
		_FreeIds.erase( it );
		return id;
	}
	_MaxId = _MaxId +1;
	return _MaxId-1;
}

const std::string& CSmallStringManager::getString(uint32 id) const
{
	static const std::string empty("Wrong id");
	std::map<uint32,std::pair<std::string,uint32> >::const_iterator  found(_IdToString.find(id));
	if (found == _IdToString.end()){ nlwarning("Try to access un accessible string"); return empty;}
	return found->second.first;
}

CSmallStringManager::CSmallStringManager()

CSmallStringManager::CSmallStringManager(CObject* textManager)
{
	if(textManager==NULL)
	{
		_MaxId = 0;
		return;
	}
	CObject* texts = textManager->getAttr("Texts");
	uint32 maxId = static_cast<uint32>(textManager->getAttr("MaxId")->toInteger());
	CObject* unused = textManager->getAttr("UnusedIds");
	uint32 max = unused->getSize();

	//_MaxId
	_MaxId = maxId;

	//unused ids
	for(uint32 i=0;i<max;i++)
	{
		uint32 id = static_cast<uint32>(unused->getValueAtPos(i)->toInteger());
		_FreeIds.insert(id);
	}

	//texts
	max = texts->getSize();
	for(uint32 i=0;i<max;i++)
	{
		CObject* entry = texts->getValueAtPos(i);
		std::string text = entry->getAttr("Text")->toString();
		uint32 textId = static_cast<uint32>(entry->getAttr("TextId")->toInteger());
		uint32 textCount = static_cast<uint32>(entry->getAttr("Count")->toInteger());
		_StringToId.insert(std::pair<std::string, uint32>(text, textId));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(textId, std::pair<std::string, uint32>(text, textCount) ) );
	}
}

void CSmallStringManager::serial(NLMISC::IStream& stream)
{
	//serialize maxId
	stream.serial(_MaxId);
	//serialize the number of free ids
	uint32 tmp = (uint32)_FreeIds.size();
	stream.serial(tmp);
	//serialize the free ids
	std::set< uint32 >::const_iterator first(_FreeIds.begin()),last(_FreeIds.end());
	for(;first!=last;++first)
	{
		tmp = *first;
		stream.serial(tmp);
	}
	tmp = (uint32)_IdToString.size();
	nlassert(tmp==_StringToId.size());

	//serialize the number of entries
	stream.serial(tmp);

	//serialize the entries
	std::map<uint32, std::pair<std::string, uint32> >::const_iterator first2(_IdToString.begin()),last2(_IdToString.end());
	for(;first2!=last2;++first2)
	{
		//serialize the entry's id
		tmp = first2->first;
		stream.serial(tmp);
		//serialize the entry's text
		std::string tmp2 = first2->second.first;
		stream.serial(tmp2);
		//serialize the entry's count
		tmp = first2->second.second;
		stream.serial(tmp);
	}
}

void CSmallStringManager::release()
{

	_StringToId.clear();

	_IdToString.clear();

	_FreeIds.clear();;

	_MaxId = 0;

}

CSmallStringManager::~CSmallStringManager()
{
	release();

}

void CStringTableManager::createTable(uint32 tableId)
{
	TLocalTable *locTable = new TLocalTable();
	if ( !_LocalTables.insert( std::make_pair(tableId, locTable)).second )
	{
		nlwarning("Table already exist %u", tableId);
		delete locTable;
		return;
	}
}

//set the new value for an entry of a local table
void CStringTableManager::setValue(uint32 tableId,const std::string& localId, const std::string& value)
{
	TLocalTable * localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		nlwarning("unknown scenario Id or no string table for this scenario! : %d",tableId);
		return;
	}

	TLocalTable::iterator found2 = localTable->find(localId);
	if(found2!=localTable->end())
	{
		std::string oldValue = _StringMgr.getString(found2->second);
		if (oldValue.empty()) nlwarning("error! no string %u in string manager! ",found2->second);
		_StringMgr.unregisterString(oldValue);
		found2->second = _StringMgr.registerString(value);
	}
	else
	{
		uint32 key = _StringMgr.registerString(value);
		localTable->insert(std::make_pair(localId, key));
	}

}

std::string CStringTableManager::getValue(uint32 tableId, const std::string& localId) const
{
	// Search the table
	TLocalTable* localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		return localId;
	}

	// Searche the key in the table
	TLocalTable::const_iterator found2 = localTable->find(localId);
	if(found2 == localTable->end())
	{
		return localId;
	}

	//get the value
	return _StringMgr.getString(found2->second);

}

CStringTableManager::TLocalTable* CStringTableManager::getLocalTable(uint32 tableId) const
{
	TLocalTables::const_iterator found = _LocalTables.find(tableId);
	if(found != _LocalTables.end()) { return found->second; }
	return NULL;
}

CStringTableManager::~CStringTableManager()
{
	release();
}

void CStringTableManager::release()
{
	_StringMgr.release();

	TLocalTables::iterator first(_LocalTables.begin()), 	last(_LocalTables.end());

	for( ; first != last; ++first)
	{

		TLocalTable* table = first->second;
		delete table;
	}
	_LocalTables.clear();

}

void CStringTableManager::releaseTable(uint32 tableId)
{

	TLocalTables::iterator found( _LocalTables.find(tableId));
	if ( found != _LocalTables.end())
	{
		TLocalTable* table = found->second;
		if (table)
		{
			TLocalTable::const_iterator first(table->begin()), last(table->end());
			for (; first != last; ++first)
			{
				_StringMgr.unregisterStringById( first->second);
			}
			delete table;
		}

		_LocalTables.erase(found);
	}
}


// includes

#include "stdpch.h"

#include "game_share/small_string_manager.h"

using namespace R2;

uint32 CSmallStringManager::registerString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator found(_StringToId.find(phrase));
	//new text entered
	uint32 id;
	if (found == _StringToId.end())
	{
		id = getNewId();
		_StringToId.insert(std::pair<std::string, uint32>(phrase, id));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(id, std::pair<std::string, uint32>(phrase, nb) ) );
	}
	else //already registered text, increment counter
	{
		id = found->second;
		std::map<uint32, std::pair<std::string, uint32> >::iterator  found2	(_IdToString.find(found->second));
		found2->second.second += nb;
	}
	//return the id of the text
	return id;
}

void CSmallStringManager::unregisterString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator  found(_StringToId.find(phrase));
	nlassert(found != _StringToId.end());

	uint32 id = found->second;
	unregisterStringById(id, nb);

}

void CSmallStringManager::unregisterStringById(uint32 id, uint32 nb)
{
	std::map<uint32, std::pair<std::string, uint32> >::iterator  found2(_IdToString.find(id));
	if (found2 == _IdToString.end())
	{
		nlwarning("try to unload a unregistered table");
		return;
	}
	uint32& count = found2->second.second;
	if (count < nb) { count = 0; }
	count -= nb;
	if (count == 0)
	{
		std::map<std::string, uint32>::iterator found = _StringToId.find(found2->second.first);
		if (found != _StringToId.end()) { _StringToId.erase(found); }

		_FreeIds.insert(found2->first);
		_IdToString.erase(found2);
	}

}

uint32 CSmallStringManager::getNewId()
{
	//TMP
	if (!_FreeIds.empty())
	{
		std::set< uint32 >::iterator it = _FreeIds.begin();
		uint32 id = *it;
		_FreeIds.erase( it );
		return id;
	}
	_MaxId = _MaxId +1;
	return _MaxId-1;
}

const std::string& CSmallStringManager::getString(uint32 id) const
{
	static const std::string empty("Wrong id");
	std::map<uint32,std::pair<std::string,uint32> >::const_iterator  found(_IdToString.find(id));
	if (found == _IdToString.end()){ nlwarning("Try to access un accessible string"); return empty;}
	return found->second.first;
}

CSmallStringManager::CSmallStringManager()
{
	_MaxId=0;
}

CSmallStringManager::CSmallStringManager(CObject* textManager)
{
	if(textManager==NULL)
	{
		_MaxId = 0;
		return;
	}
	CObject* texts = textManager->getAttr("Texts");
	uint32 maxId = static_cast<uint32>(textManager->getAttr("MaxId")->toInteger());
	CObject* unused = textManager->getAttr("UnusedIds");
	uint32 max = unused->getSize();

	//_MaxId
	_MaxId = maxId;

	//unused ids
	for(uint32 i=0;i<max;i++)
	{
		uint32 id = static_cast<uint32>(unused->getValueAtPos(i)->toInteger());
		_FreeIds.insert(id);
	}

	//texts
	max = texts->getSize();
	for(uint32 i=0;i<max;i++)
	{
		CObject* entry = texts->getValueAtPos(i);
		std::string text = entry->getAttr("Text")->toString();
		uint32 textId = static_cast<uint32>(entry->getAttr("TextId")->toInteger());
		uint32 textCount = static_cast<uint32>(entry->getAttr("Count")->toInteger());
		_StringToId.insert(std::pair<std::string, uint32>(text, textId));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(textId, std::pair<std::string, uint32>(text, textCount) ) );
	}
}

void CSmallStringManager::serial(NLMISC::IStream& stream)
{
	//serialize maxId
	stream.serial(_MaxId);
	//serialize the number of free ids
	uint32 tmp = (uint32)_FreeIds.size();
	stream.serial(tmp);
	//serialize the free ids
	std::set< uint32 >::const_iterator first(_FreeIds.begin()),last(_FreeIds.end());
	for(;first!=last;++first)
	{
		tmp = *first;
		stream.serial(tmp);
	}
	tmp = (uint32)_IdToString.size();
	nlassert(tmp==_StringToId.size());

	//serialize the number of entries
	stream.serial(tmp);

	//serialize the entries
	std::map<uint32, std::pair<std::string, uint32> >::const_it

	for(;first2!=last2;++first2)
	{
		//serialize the entry's id
		tmp = first2->first;
		stream.serial(tmp);
		//serialize the entry's text
		std::string tmp2 = first2->second.first;
		stream.serial(tmp2);
		//serialize the entry's count
		tmp = first2->second.second;
		stream.serial(tmp);
	}
}

void CSmallStringManager::release()
{

	_StringToId.clear();

	_IdToString.clear();

	_FreeIds.clear();;

	_MaxId = 0;

}

CSmallStringManager::~CSmallStringManager()
{
	release();

}

void CStringTableManager::createTable(uint32 tableId)
{
	TLocalTable *locTable = new TLocalTable();
	if ( !_LocalTables.insert( std::make_pair(tableId, locTable)).second )
	{
		nlwarning("Table already exist %u", tableId);
		delete locTable;
		return;
	}
}

//set the new value for an entry of a local table
void CStringTableManager::setValue(uint32 tableId,const std::string& localId, const std::string& value)
{
	TLocalTable * localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		nlwarning("unknown scenario Id or no string table for this scenario! : %d",tableId);
		return;
	}

	TLocalTable::iterator found2 = localTable->find(localId);
	if(found2!=localTable->end())
	{
		std::string oldValue = _StringMgr.getString(found2->second);
		if (oldValue.empty()) nlwarning("error! no string %u in string manager! ",found2->second);
		_StringMgr.unregisterString(oldValue);
		found2->second = _StringMgr.registerString(value);
	}
	else
	{
		uint32 key = _StringMgr.registerString(value);
		localTable->insert(std::make_pair(localId, key));
	}

}

std::string CStringTableManager::getValue(uint32 tableId, const std::string& localId) const
{
	// Search the table
	TLocalTable* localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		return localId;
	}

	// Searche the key in the table
	TLocalTable::const_iterator found2 = localTable->find(localId);
	if(found2 == localTable->end())
	{
		return localId;
	}

	//get the value
	return _StringMgr.getString(found2->second);

}

CStringTableManager::TLocalTable* CStringTableManager::getLocalTable(uint32 tableId) const
{
	TLocalTables::const_iterator found = _LocalTables.find(tableId);
	if(found != _LocalTables.end()) { return found->second; }
	return NULL;
}

CStringTableManager::~CStringTableManager()
{
	release();
}

void CStringTableManager::release()
{
	_StringMgr.release();

	TLocalTables::iterator first(_LocalTables.begin()), 	last(_LocalTables.end());

	for( ; first != last; ++first)
	{

		TLocalTable* table = first->second;
		delete table;
	}
	_LocalTables.clear();

}

void CStringTableManager::releaseTable(uint32 tableId)
{

	TLocalTables::iterator found( _LocalTables.find(tableId));
	if ( found != _LocalTables.end())
	{
		TLocalTable* table = found->second;
		if (table)
		{
			TLocalTable::const_iterator first(table->begin()), last(table->end());
			for (; first != last; ++first)
			{
				_StringMgr.unregisterStringById( first->second);
			}
			delete table;
		}

		_LocalTables.erase(found);
	}
}


for (; first != last; ++first)
			{
				_StringMgr.unregisterStringById( first->second);
			}

// includes

#include "stdpch.h"

#include "game_share/small_string_manager.h"

using namespace R2;

uint32 CSmallStringManager::registerString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator found(_StringToId.find(phrase));
	//new text entered
	uint32 id;
	if (found == _StringToId.end())
	{
		id = getNewId();
		_StringToId.insert(std::pair<std::string, uint32>(phrase, id));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(id, std::pair<std::string, uint32>(phrase, nb) ) );
	}
	else //already registered text, increment counter
	{
		id = found->second;
		std::map<uint32, std::pair<std::string, uint32> >::iterator  found2	(_IdToString.find(found->second));
		found2->second.second += nb;
	}
	//return the id of the text
	return id;
}

void CSmallStringManager::unregisterString(const std::string& phrase,uint32 nb)
{
	std::map<std::string, uint32>::iterator  found(_StringToId.find(phrase));
	nlassert(found != _StringToId.end());

	uint32 id = found->second;
	unregisterStringById(id, nb);

}

void CSmallStringManager::unregisterStringById(uint32 id, uint32 nb)
{
	std::map<uint32, std::pair<std::string, uint32> >::iterator  found2(_IdToString.find(id));
	if (found2 == _IdToString.end())
	{
		nlwarning("try to unload a unregistered table");
		return;
	}
	uint32& count = found2->second.second;
	if (count < nb) { count = 0; }
	count -= nb;
	if (count == 0)
	{
		std::map<std::string, uint32>::iterator found = _StringToId.find(found2->second.first);
		if (found != _StringToId.end()) { _StringToId.erase(found); }

		_FreeIds.insert(found2->first);
		_IdToString.erase(found2);
	}

}

uint32 CSmallStringManager::getNewId()
{
	//TMP
	if (!_FreeIds.empty())
	{
		std::set< uint32 >::iterator it = _FreeIds.begin();
		uint32 id = *it;
		_FreeIds.erase( it );
		return id;
	}
	_MaxId = _MaxId +1;
	return _MaxId-1;
}

const std::string& CSmallStringManager::getString(uint32 id) const
{
	static const std::string empty("Wrong id");
	std::map<uint32,std::pair<std::string,uint32> >::const_iterator  found(_IdToString.find(id));
	if (found == _IdToString.end()){ nlwarning("Try to access un accessible string"); return empty;}
	return found->second.first;
}

CSmallStringManager::CSmallStringManager()
{
	_MaxId=0;
}

CSmallStringManager::CSmallStringManager(CObject* textManager)
{
	if(textManager==NULL)
	{
		_MaxId = 0;
		return;
	}
	CObject* texts = textManager->getAttr("Texts");
	uint32 maxId = static_cast<uint32>(textManager->getAttr("MaxId")->toInteger());
	CObject* unused = textManager->getAttr("UnusedIds");
	uint32 max = unused->getSize();

	//_MaxId
	_MaxId = maxId;

	//unused ids
	for(uint32 i=0;i<max;i++)
	{
		uint32 id = static_cast<uint32>(unused->getValueAtPos(i)->toInteger());
		_FreeIds.insert(id);
	}

	//texts
	max = texts->getSize();
	for(uint32 i=0;i<max;i++)
	{
		CObject* entry = texts->getValueAtPos(i);
		std::string text = entry->getAttr("Text")->toString();
		uint32 textId = static_cast<uint32>(entry->getAttr("TextId")->toInteger());
		uint32 textCount = static_cast<uint32>(entry->getAttr("Count")->toInteger());
		_StringToId.insert(std::pair<std::string, uint32>(text, textId));
		_IdToString.insert( std::pair<uint32, std::pair<std::string, uint32> >(textId, std::pair<std::string, uint32>(text, textCount) ) );
	}
}

void CSmallStringManager::serial(NLMISC::IStream& stream)
{
	//serialize maxId
	stream.serial(_MaxId);
	//serialize the number of free ids
	uint32 tmp = (uint32)_FreeIds.size();
	stream.serial(tmp);
	//serialize the free ids
	std::set< uint32 >::const_iterator first(_FreeIds.begin()),last(_FreeIds.end());
	for(;first!=last;++first)
	{
		tmp = *first;
		stream.serial(tmp);
	}
	tmp = (uint32)_IdToString.size();
	nlassert(tmp==_StringToId.size());

	//serialize the number of entries
	stream.serial(tmp);

	//serialize the entries
	std::map<uint32, std::pair<std::string, uint32> >::const_iterator first2(_IdToString.begin()),last2(_IdToString.end());
	for(;first2!=last2;++first2)
	{
		//serialize the entry's id
		tmp = first2->first;
		stream.serial(tmp);
		//serialize the entry's text
		std::string tmp2 = first2->second.first;
		stream.serial(tmp2);
		//serialize the entry's count
		tmp = first2->second.second;
		stream.serial(tmp);
	}
}

void CSmallStringManager::release()
{

	_StringToId.clear();

	_IdToString.clear();

	_FreeIds.clear();;

	_MaxId = 0;

}

CSmallStringManager::~CSmallStringManager()
{
	release();

}

void CStringTableManager::createTable(uint32 tableId)
{
	TLocalTable *locTable = new TLocalTable();
	if ( !_LocalTables.insert( std::make_pair(tableId, locTable)).second )
	{
		nlwarning("Table already exist %u", tableId);
		delete locTable;
		return;
	}
}

//set the new value for an entry of a local table
void CStringTableManager::setValue(uint32 tableId,const std::string& localId, const std::string& value)
{
	TLocalTable * localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		nlwarning("unknown scenario Id or no string table for this scenario! : %d",tableId);
		return;
	}

	TLocalTable::iterator found2 = localTable->find(localId);
	if(found2!=localTable->end())
	{
		std::string oldValue = _StringMgr.getString(found2->second);
		if (oldValue.empty()) nlwarning("error! no string %u in string manager! ",found2->second);
		_StringMgr.unregisterString(oldValue);
		found2->second = _StringMgr.registerString(value);
	}
	else
	{
		uint32 key = _StringMgr.registerString(value);
		localTable->insert(std::make_pair(localId, key));
	}

}

std::string CStringTableManager::getValue(uint32 tableId, const std::string& localId) const
{
	// Search the table
	TLocalTable* localTable = getLocalTable(tableId);
	if(localTable == NULL)
	{
		return localId;
	}

	// Searche the key in the table
	TLocalTable::const_iterator found2 = localTable->find(localId);
	if(found2 == localTable->end())
	{
		return localId;
	}

	//get the value
	return _StringMgr.getString(found2->second);

}

CStringTableManager::TLocalTable* CStringTableManager::getLocalTable(uint32 tableId) const
{
	TLocalTables::const_iterator found = _LocalTables.find(tableId);
	if(found != _LocalTables.end()) { return found->second; }
	return NULL;
}

CStringTableManager::~CStringTableManager()
{
	release();
}

void CStringTableManager::release()
{
	_StringMgr.release();

	TLocalTables::iterator first(_LocalTables.begin()), 	last(_LocalTables.end());

	for( ; first != last; ++first)
	{

		TLocalTable* table = first->second;
		delete table;
	}
	_LocalTables.clear();

}

void CStringTableManager::releaseTable(uint32 tableId)
{

	TLocalTables::iterator found( _LocalTables.find(tableId));
	if ( found != _LocalTables.end())
	{
		TLocalTable* table = found->second;
		if (table)
		{
			TLocalTable::const_iterator first(table->begin()), last(table->end());

			delete table;
		}

		_LocalTables.erase(found);
	}
}


{
    QApplication app(argv, args);
    Window window;
    window.show();
    return app.exec();
}


UnitVector3 interpolate(UnitVector3& u0, UnitVector3& u1, double t)

#include "geometrycentral/utilities/unit_vector3.h"

namespace geometrycentral {

{
  double theta = angle(u0, u1);
  Vector3 w = cross(u0, u1);

  Vector3 e1 = u0;
  Vector3 e2 = cross(w, e1);

  Vector3 r = cos(t * theta) * e1 + sin(t * theta) * e2;

  return UnitVector3(r);
}

} // namespace geometrycentral


{
  double theta = angle(u0, u1);
  Vector3 w = cross(u0, u1);

  Vector3 e1 = u0;
  Vector3 e2 = cross(w, e1);

  Vector3 r = cos(t * theta) * e1 + sin(t * theta) * e2;

  return UnitVector3(r);
}

#include "geometrycentral/utilities/unit_vector3.h"

namespace geometrycentral {

UnitVector3 interpolate(UnitVector3& u0, UnitVector3& u1, double t)


#include "geometrycentral/utilities/unit_vector3.h"

namespace geometrycentral {

UnitVector3 interpolate(UnitVector3& u0, UnitVector3& u1, double t) {
  double theta = angle(u0, u1);
  Vector3 w =

  Vector3 e1 = u0;
  Vector3 e2 = cross(w, e1);

  Vector3 r = cos(t * theta) * e1 + sin(t * theta) * e2;

  return UnitVector3(r);
}

} // namespace geometrycentral


//                     The LLVM Compiler Infrastructure

#include "BenchmarkResult.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

using ::testing::AllOf;
using ::testing::Eq;
using ::testing::get;
using ::testing::Pointwise;
using ::testing::Property;

namespace exegesis {

bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) {
  return std::tie(A.Key, A.Value) == std::tie(B.Key, B.Value);
}

static std::string Dump(const llvm::MCInst &McInst) {
  std::string Buffer;
  llvm::raw_string_ostream OS(Buffer);
  McInst.print(OS);
  return Buffer;
}

MATCHER(EqMCInst, "") {
  const std::string Lhs = Dump(get<0>(arg));
  const std::string Rhs = Dump(get<1>(arg));
  if (Lhs != Rhs) {
    *result_listener << Lhs << " <=> " << Rhs;
    return false;
  }
  return true;
}

namespace {

static constexpr const unsigned kInstrId = 5;
static constexpr const char kInstrName[] = "Instruction5";
static constexpr const unsigned kReg1Id = 1;
static constexpr const char kReg1Name[] = "Reg1";
static constexpr const unsigned kReg2Id = 2;
static constexpr const char kReg2Name[] = "Reg2";

{
  llvm::ExitOnError ExitOnErr;
  BenchmarkResultContext Ctx;
  Ctx.addInstrEntry(kInstrId, kInstrName);
  Ctx.addRegEntry(kReg1Id, kReg1Name);
  Ctx.addRegEntry(kReg2Id, kReg2Name);

  InstructionBenchmark ToDisk;

  ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(kInstrId)
                                        .addReg(kReg1Id)
                                        .addReg(kReg2Id)
                                        .addImm(123)
                                        .addFPImm(0.5));
  ToDisk.Key.Config = "config";
  ToDisk.Mode = InstructionBenchmark::Latency;
  ToDisk.CpuName = "cpu_name";
  ToDisk.LLVMTriple = "llvm_triple";
  ToDisk.NumRepetitions = 1;
  ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, "debug a"});
  ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, ""});
  ToDisk.Error = "error";
  ToDisk.Info = "info";

  llvm::SmallString<64> Filename;
  std::error_code EC;
  EC = llvm::sys::fs::createUniqueDirectory("BenchmarkResultTestDir", Filename);
  ASSERT_FALSE(EC);
  llvm::sys::path::append(Filename, "data.yaml");
  ExitOnErr(ToDisk.writeYaml(Ctx, Filename));

  {
    // One-element version.
    const auto FromDisk =
        ExitOnErr(InstructionBenchmark::readYaml(Ctx, Filename));

    EXPECT_THAT(FromDisk.Key.Instructions,
                Pointwise(EqMCInst(), ToDisk.Key.Instructions));
    EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
    EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
    EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
    EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
    EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
    EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
    EXPECT_THAT(FromDisk.Error, ToDisk.Error);
    EXPECT_EQ(FromDisk.Info, ToDisk.Info);
  }
  {
    // Vector version.
    const auto FromDiskVector =
        ExitOnErr(InstructionBenchmark::readYamls(Ctx, Filename));
    ASSERT_EQ(FromDiskVector.size(), size_t{1});
    const auto FromDisk = FromDiskVector[0];
    EXPECT_THAT(FromDisk.Key.Instructions,
                Pointwise(EqMCInst(), ToDisk.Key.Instructions));
    EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
    EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
    EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
    EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
    EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
    EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
    EXPECT_THAT(FromDisk.Error, ToDisk.Error);
    EXPECT_EQ(FromDisk.Info, ToDisk.Info);
  }
}

TEST(BenchmarkResultTest, BenchmarkMeasureStats) {
  BenchmarkMeasureStats Stats;
  Stats.push(BenchmarkMeasure{"a", 0.5, "debug a"});
  Stats.push(BenchmarkMeasure{"a", 1.5, "debug a"});
  Stats.push(BenchmarkMeasure{"a", -1.0, "debug a"});
  Stats.push(BenchmarkMeasure{"a", 0.0, "debug a"});
  EXPECT_EQ(Stats.min(), -1.0);
  EXPECT_EQ(Stats.max(), 1.5);
  EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4
}
} // namespace
} // namespace exegesis


{
  std::string Buffer;
  llvm::raw_string_ostream OS(Buffer);
  McInst.print(OS);
  return Buffer;
}

//                     The LLVM Compiler Infrastructure

#include "BenchmarkResult.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

using ::testing::AllOf;
using ::testing::Eq;
using ::testing::get;
using ::testing::Pointwise;
using ::testing::Property;

namespace exegesis {

bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) {
  return std::tie(A.Key, A.Value) == std::tie(B.Key, B.Value);
}

static std::string Dump(const llvm::MCInst &McInst)

MATCHER(EqMCInst, "") {
  const std::string Lhs = Dump(get<0>(arg));
  const std::string Rhs = Dump(get<1>(arg));
  if (Lhs != Rhs) {
    *result_listener << Lhs << " <=> " << Rhs;
    return false;
  }
  return true;
}

namespace {

static constexpr const unsigned kInstrId = 5;
static constexpr const char kInstrName[] = "Instruction5";
static constexpr const unsigned kReg1Id = 1;
static constexpr const char kReg1Name[] = "Reg1";
static constexpr const unsigned kReg2Id = 2;
static constexpr const char kReg2Name[] = "Reg2";

TEST(BenchmarkResultTest, WriteToAndReadFromDisk) {
  llvm::ExitOnError ExitOnErr;
  BenchmarkResultContext Ctx;
  Ctx.addInstrEntry(kInstrId, kInstrName);
  Ctx.addRegEntry(kReg1Id, kReg1Name);
  Ctx.addRegEntry(kReg2Id, kReg2Name);

  InstructionBenchmark ToDisk;

  ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(kInstrId)
                                        .addReg(kReg1Id)
                                        .addReg(kReg2Id)
                                        .addImm(123)
                                        .addFPImm(0.5));
  ToDisk.Key.Config = "config";
  ToDisk.Mode = InstructionBenchmark::Latency;
  ToDisk.CpuName = "cpu_name";
  ToDisk.LLVMTriple = "llvm_triple";
  ToDisk.NumRepetitions = 1;
  ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, "debug a"});
  ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, ""});
  ToDisk.Error = "error";
  ToDisk.Info = "info";

  llvm::SmallString<64> Filename;
  std::error_code EC;
  EC = llvm::sys::fs::createUniqueDirectory("BenchmarkResultTestDir", Filename);
  ASSERT_FALSE(EC);
  llvm::sys::path::append(Filename, "data.yaml");
  ExitOnErr(ToDisk.writeYaml(Ctx, Filename));

  {
    // One-element version.
    const auto FromDisk =
        ExitOnErr(InstructionBenchmark::readYaml(Ctx, Filename));

    EXPECT_THAT(FromDisk.Key.Instructions,
                Pointwise(EqMCInst(), ToDisk.Key.Instructions));
    EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
    EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
    EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
    EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
    EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
    EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
    EXPECT_THAT(FromDisk.Error, ToDisk.Error);
    EXPECT_EQ(FromDisk.Info, ToDisk.Info);
  }
  {
    // Vector version.
    const auto FromDiskVector =
        ExitOnErr(InstructionBenchmark::readYamls(Ctx, Filename));
    ASSERT_EQ(FromDiskVector.size(), size_t{1});
    const auto FromDisk = FromDiskVector[0];
    EXPECT_THAT(FromDisk.Key.Instructions,
                Pointwise(EqMCInst(), ToDisk.Key.Instructions));
    EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
    EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
    EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
    EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
    EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
    EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
    EXPECT_THAT(FromDisk.Error, ToDisk.Error);
    EXPECT_EQ(FromDisk.Info, ToDisk.Info);
  }
}

TEST(BenchmarkResultTest, BenchmarkMeasureStats) {
  BenchmarkMeasureStats Stats;
  Stats.push(BenchmarkMeasure{"a", 0.5, "debug a"});
  Stats.push(BenchmarkMeasure{"a", 1.5, "debug a"});
  Stats.push(BenchmarkMeasure{"a", -1.0, "debug a"});
  Stats.push(BenchmarkMeasure{"a", 0.0, "debug a"});
  EXPECT_EQ(Stats.min(), -1.0);
  EXPECT_EQ(Stats.max(), 1.5);
  EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4
}
} // namespace
} // namespace exegesis


//                     The LLVM Compiler Infrastructure

#include "BenchmarkResult.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

using ::testing::AllOf;
using ::testing::Eq;
using ::testing::get;
using ::testing::Pointwise;
using ::testing::Property;

namespace exegesis {

bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) {
  return std::tie(A.Key, A.Value) == std::tie(B.Key, B.Value);
}

static std::string Dump(const llvm::MCInst &McInst) {
  std::string Buffer;
  llvm::raw_string_ostream OS(Buffer);
  McInst.print(OS);
  return Buffer;
}

MATCHER(EqMCInst, "") {
  const std::string Lhs = Dump(get<0>(arg));
  const std::string Rhs = Dump(get<1>(arg));
  if (Lhs != Rhs) {
    *result_listener << Lhs << " <=> " << Rhs;
    return false;
  }
  return true;
}

namespace {

static constexpr const unsigned kInstrId = 5;
static constexpr const char kInstrName[] = "Instruction5";
static constexpr const unsigned kReg1Id = 1;
static constexpr const char kReg1Name[] = "Reg1";
static constexpr const unsigned kReg2Id = 2;
static constexpr const char kReg2Name[] = "Reg2";

TEST(BenchmarkResultTest, WriteToAndReadFromDisk) {
  llvm::ExitOnError ExitOnErr;
  BenchmarkResultContext Ctx;
  Ctx.addInstrEntry(kInstrId, kInstrName);
  Ctx.addRegEntry(kReg1Id, kReg1Name);
  Ctx.addRegEntry(kReg2Id, kReg2Name);

  InstructionBenchmark ToDisk;

  ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(kInstrId)
                                        .addReg(kReg1Id)
                                        .addReg(kReg2Id)
                                        .addImm(123)
                                        .addFPImm(0.5));
  ToDisk.Key.Config = "config";
  ToDisk.Mode = InstructionBenchmark::Latency;
  ToDisk.CpuName = "cpu_name";
  ToDisk.LLVMTriple = "llvm_triple";
  ToDisk.NumRepetitions = 1;
  ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1

  ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, ""});
  ToDisk.Error = "error";
  ToDisk.Info = "info";

  llvm::SmallString<64> Filename;
  std::error_code EC;
  EC = llvm::sys::fs::createUniqueDirectory("BenchmarkResultTestDir", Filename);
  ASSERT_FALSE(EC);
  llvm::sys::path::append(Filename, "data.yaml");
  ExitOnErr(ToDisk.writeYaml(Ctx, Filename));

  {
    // One-element version.
    const auto FromDisk =
        ExitOnErr(InstructionBenchmark::readYaml(Ctx, Filename));

    EXPECT_THAT(FromDisk.Key.Instructions,
                Pointwise(EqMCInst(), ToDisk.Key.Instructions));
    EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
    EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
    EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
    EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
    EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
    EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
    EXPECT_THAT(FromDisk.Error, ToDisk.Error);
    EXPECT_EQ(FromDisk.Info, ToDisk.Info);
  }
  {
    // Vector version.
    const auto FromDiskVector =
        ExitOnErr(InstructionBenchmark::readYamls(Ctx, Filename));
    ASSERT_EQ(FromDiskVector.size(), size_t{1});
    const auto FromDisk = FromDiskVector[0];
    EXPECT_THAT(FromDisk.Key.Instructions,
                Pointwise(EqMCInst(), ToDisk.Key.Instructions));
    EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);
    EXPECT_EQ(FromDisk.Mode, ToDisk.Mode);
    EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);
    EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple);
    EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions);
    EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements);
    EXPECT_THAT(FromDisk.Error, ToDisk.Error);
    EXPECT_EQ(FromDisk.Info, ToDisk.Info);
  }
}

TEST(BenchmarkResultTest, BenchmarkMeasureStats) {
  BenchmarkMeasureStats Stats;
  Stats.push(BenchmarkMeasure{"a", 0.5, "debug a"});
  Stats.push(BenchmarkMeasure{"a", 1.5, "debug a"});
  Stats.push(BenchmarkMeasure{"a", -1.0, "debug a"});
  Stats.push(BenchmarkMeasure{"a", 0.0, "debug a"});
  EXPECT_EQ(Stats.min(), -1.0);
  EXPECT_EQ(Stats.max(), 1.5);
  EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4
}
} // namespace
} // namespace exegesis


for (HighsInt iCol = 0; iCol < num_col; iCol++)
    basis.col_status.push_back(HighsBasisStatus::kLower);

#include "Highs.h"
#include "catch.hpp"

const double inf = kHighsInf;
const bool dev_run = false;
const double double_equal_tolerance = 1e-5;

TEST_CASE("FreezeBasis", "[highs_test_freeze_basis]") {
  std::string filename;
  filename = std::string(HIGHS_DIR) + "/check/instances/avgas.mps";

  Highs highs;
  if (!dev_run) highs.setOptionValue("output_flag", false);
  highs.readModel(filename);
  const HighsLp& lp = highs.getLp();
  const HighsInt num_col = lp.num_col_;
  const HighsInt num_row = lp.num_row_;
  const HighsInt from_col = 0;
  const HighsInt to_col = num_col - 1;
  const HighsInfo& info = highs.getInfo();

  vector<double> original_col_lower = lp.col_lower_;
  vector<double> original_col_upper = lp.col_upper_;

  // Get the continuous solution and iteration count from a logical basis
  highs.run();
  HighsInt continuous_iteration_count = info.simplex_iteration_count;
  double continuous_objective = info.objective_function_value;

  // Get the integer solution to provide bound tightenings
  vector<HighsVarType> integrality;
  integrality.assign(num_col, HighsVarType::kInteger);
  highs.changeColsIntegrality(from_col, to_col, &integrality[0]);

  highs.setOptionValue("output_flag", false);
  highs.run();
  if (dev_run) highs.setOptionValue("output_flag", true);

  vector<double> integer_solution = highs.getSolution().col_value;
  vector<double> local_col_lower;
  vector<double> local_col_upper;

  // Now restore the original integrality and set an explicit logical
  // basis to force reinversion
  integrality.assign(num_col, HighsVarType::kContinuous);
  highs.changeColsIntegrality(from_col, to_col, &integrality[0]);
  HighsBasis basis;

  for (HighsInt iRow = 0; iRow < num_row; iRow++)
    basis.row_status.push_back(HighsBasisStatus::kBasic);
  highs.setBasis(basis);

  HighsInt frozen_basis_id0;
  // Cannot freeze a basis when there's no INVERT
  REQUIRE(highs.freezeBasis(frozen_basis_id0) == HighsStatus::kError);

  // Get the basic variables to force INVERT with a logical basis
  vector<HighsInt> basic_variables;
  basic_variables.resize(lp.num_row_);
  highs.getBasicVariables(&basic_variables[0]);

  // Can freeze a basis now!
  REQUIRE(highs.freezeBasis(frozen_basis_id0) == HighsStatus::kOk);

  if (dev_run) {
    highs.setOptionValue("output_flag", true);
    highs.setOptionValue("log_dev_level", 2);
    highs.setOptionValue("highs_debug_level", 3);
  }
  highs.run();

  // Now freeze the current basis and add the integer solution as lower bounds
  HighsInt frozen_basis_id1;
  REQUIRE(highs.freezeBasis(frozen_basis_id1) == HighsStatus::kOk);
  if (dev_run) printf("\nSolving with bounds (integer solution, upper)\n");
  local_col_lower = integer_solution;
  local_col_upper = original_col_upper;
  highs.changeColsBounds(from_col, to_col, &local_col_lower[0],
                         &local_col_upper[0]);
  if (dev_run) highs.setOptionValue("output_flag", true);
  highs.run();
  double semi_continuous_objective = info.objective_function_value;

  // Now freeze the current basis and add the integer solution as upper bounds
  HighsInt frozen_basis_id2;
  REQUIRE(highs.freezeBasis(frozen_basis_id2) == HighsStatus::kOk);
  if (dev_run)
    printf("\nSolving with bounds (integer solution, integer solution)\n");
  local_col_lower = integer_solution;
  local_col_upper = integer_solution;
  highs.changeColsBounds(from_col, to_col, &local_col_lower[0],
                         &local_col_upper[0]);
  if (dev_run) highs.setOptionValue("output_flag", true);
  highs.run();

  // Now test unfreeze
  // Restore the upper bounds and unfreeze frozen_basis_id2 (semi-continuous
  // optimal basis)
  if (dev_run)
    printf(
        "\nSolving with bounds (integer solution, upper) after unfreezing "
        "basis %d\n",
        (int)frozen_basis_id2);
  if (dev_run) printf("Change column bounds to (integer solution, upper)\n");
  local_col_lower = integer_solution;
  local_col_upper = original_col_upper;
  highs.changeColsBounds(from_col, to_col, &local_col_lower[0],
                         &local_col_upper[0]);
  if (dev_run) printf("Unfreeze basis %d\n", (int)frozen_basis_id2);
  REQUIRE(highs.unfreezeBasis(frozen_basis_id2) == HighsStatus::kOk);
  // Solving the LP should require no iterations
  if (dev_run) highs.setOptionValue("output_flag", true);
  highs.run();
  REQUIRE(info.simplex_iteration_count == 0);
  double dl_objective =
      fabs(semi_continuous_objective - info.objective_function_value);
  REQUIRE(dl_objective < double_equal_tolerance);
  REQUIRE(info.simplex_iteration_count == 0);
  // Restore the lower bounds and unfreeze frozen_basis_id1 (continuous optimal
  if (dev_run)
    printf("\nSolving with bounds (lower, upper) after unfreezing basis %d\n",
           (int)frozen_basis_id1);
  if (dev_run) printf("Change column bounds to (lower, upper)\n");
  local_col_lower = original_col_lower;
  local_col_upper = original_col_upper;
  highs.changeColsBounds(from_col, to_col, &local_col_lower[0],
                         &local_col_upper[0]);
  if (dev_run) printf("Unfreeze basis %d\n", (int)frozen_basis_id1);
  REQUIRE(highs.unfreezeBasis(frozen_basis_id1) == HighsStatus::kOk);
  // Solving the LP should require no iterations
  if (dev_run) highs.setOptionValue("output_flag", true);
  highs.run();
  dl_objective = fabs(continuous_objective - info.objective_function_value);
  REQUIRE(dl_objective < double_equal_tolerance);
  REQUIRE(info.simplex_iteration_count == 0);
  // Unfreeze frozen_basis_id0
  if (dev_run)
    printf("\nSolving with bounds (lower, upper) after unfreezing basis %d\n",
           (int)frozen_basis_id0);
  if (dev_run) printf("Unfreeze basis %d\n", (int)frozen_basis_id0);
  REQUIRE(highs.unfreezeBasis(frozen_basis_id0) == HighsStatus::kOk);
  // Solving the LP should require the same number of iterations as before
  if (dev_run) highs.setOptionValue("output_flag", true);
  highs.run();
  dl_objective = fabs(continuous_objective - info.objective_function_value);
  REQUIRE(dl_objective < double_equal_tolerance);
  REQUIRE(info.simplex_iteration_count == continuous_iteration_count);

  REQUIRE(highs.frozenBasisAllDataClear() == HighsStatus::kOk);
}


static void thread_func(void* /* unused */)

/******************************************************************************
 * $Id$
 *
 * Project:  GDAL Core
 * Purpose:  Test block cache under multi-threading
 * Author:   Even Rouault, <even dot rouault at spatialys dot com>
 *
 ******************************************************************************
 *
 ****************************************************************************/

#include "cpl_conv.h"
#include "cpl_multiproc.h"
#include "cpl_string.h"
#include "gdal_priv.h"
#include "gdal.h"

#include "test_data.h"

template<typename T> void check(const T& x, const char* msg)
{
    if( !x )
    {
        fprintf(stderr, "CHECK(%s) failed\n", msg);
        exit(1);
    }
}

#define STRINGIFY(x) #x
#define CHECK(x) check((x), STRINGIFY(x))

{
    printf("begin thread %p\n", (void*)CPLGetPID());
    CPLSetThreadLocalConfigOption("GDAL_RB_INTERNALIZE_SLEEP_AFTER_DROP_LOCK", "0.6");
    GDALDatasetH hDS = GDALOpen(GCORE_DATA_DIR "byte.tif", GA_ReadOnly);
    char buf[20*20];
    CPL_IGNORE_RET_VAL(GDALRasterIO(GDALGetRasterBand(hDS, 1), GF_Read, 0, 0, 20, 20, buf, 20, 20, GDT_Byte, 0, 0));
    CPLSetThreadLocalConfigOption("GDAL_RB_INTERNALIZE_SLEEP_AFTER_DROP_LOCK", "0");
    GDALClose(hDS);
    printf("end of thread\n\n");
}

static void thread_func2(void* /* unused */)
{
    printf("begin thread %p\n", (void*)CPLGetPID());
    CPLSetThreadLocalConfigOption("GDAL_RB_FLUSHBLOCK_SLEEP_AFTER_DROP_LOCK", "0.6");
    GDALFlushCacheBlock();
    CPLSetThreadLocalConfigOption("GDAL_RB_FLUSHBLOCK_SLEEP_AFTER_DROP_LOCK", "0");
    printf("end of thread\n\n");
}

static void thread_func3(void* /* unused */)
{
    printf("begin thread %p\n", (void*)CPLGetPID());
    CPLSleep(0.3);
    printf("begin GDALFlushCacheBlock\n");
    GDALFlushCacheBlock();
    printf("end of thread\n\n");
}

static void thread_func4(void* /* unused */)
{
    printf("begin thread %p\n", (void*)CPLGetPID());
    CPLSetThreadLocalConfigOption("GDAL_RB_FLUSHBLOCK_SLEEP_AFTER_RB_LOCK", "0.6");
    GDALFlushCacheBlock();
    CPLSetThreadLocalConfigOption("GDAL_RB_FLUSHBLOCK_SLEEP_AFTER_RB_LOCK", "0");
    printf("end of thread\n\n");
}

int main(int argc, char* argv[])
{
    CPLJoinableThread* hThread;

    printf("main thread %p\n", (void*)CPLGetPID());

    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );

    CPLSetConfigOption("GDAL_CACHEMAX", "0");
    CPLSetConfigOption("GDAL_DEBUG_BLOCK_CACHE", "ON");
    GDALAllRegister();

    GDALDatasetH hDS = GDALOpen(GCORE_DATA_DIR "byte.tif", GA_ReadOnly);

    char buf[20*20];
    printf("cache fill\n");
    CPL_IGNORE_RET_VAL(GDALRasterIO(GDALGetRasterBand(hDS, 1), GF_Read, 0, 0, 20, 20, buf, 20, 20, GDT_Byte, 0, 0));
    printf("end of cache fill\n");
    printf("buf[0]=%d\n\n", (int)buf[0]);

    hThread = CPLCreateJoinableThread(thread_func, nullptr);
    CPLSleep(0.3);

    printf("re read block\n");
    CPL_IGNORE_RET_VAL(GDALRasterIO(GDALGetRasterBand(hDS, 1), GF_Read, 0, 0, 20, 20, buf, 20, 20, GDT_Byte, 0, 0));
    printf("end of re read block\n");
    printf("buf[0]=%d\n", (int)buf[0]);
    CPLJoinThread(hThread);

    hThread = CPLCreateJoinableThread(thread_func2, nullptr);
    CPLSleep(0.3);

    printf("re read block\n");
    CPL_IGNORE_RET_VAL(GDALRasterIO(GDALGetRasterBand(hDS, 1), GF_Read, 0, 0, 20, 20, buf, 20, 20, GDT_Byte, 0, 0));
    printf("end of re read block\n");
    printf("buf[0]=%d\n", (int)buf[0]);
    CPLJoinThread(hThread);

    hThread = CPLCreateJoinableThread(thread_func3, nullptr);

    printf("re read block\n");
    CPLSetThreadLocalConfigOption("GDAL_RB_TRYGET_SLEEP_AFTER_TAKE_LOCK", "0.6");
    CPL_IGNORE_RET_VAL(GDALRasterIO(GDALGetRasterBand(hDS, 1), GF_Read, 0, 0, 20, 20, buf, 20, 20, GDT_Byte, 0, 0));
    CPLSetThreadLocalConfigOption("GDAL_RB_TRYGET_SLEEP_AFTER_TAKE_LOCK", "0");
    printf("end of re read block\n");
    printf("buf[0]=%d\n", (int)buf[0]);
    CPLJoinThread(hThread);

    hThread = CPLCreateJoinableThread(thread_func2, nullptr);
    CPLSleep(0.3);
    printf("before GDALFlushRasterCache\n");
    GDALFlushRasterCache(GDALGetRasterBand(hDS, 1));
    printf("after GDALFlushRasterCache\n");

    CPLJoinThread(hThread);
    CHECK( GDALGetCacheUsed64() == 0 );

    CPL_IGNORE_RET_VAL(GDALRasterIO(GDALGetRasterBand(hDS, 1), GF_Read, 0, 0, 20, 20, buf, 20, 20, GDT_Byte, 0, 0));
    hThread = CPLCreateJoinableThread(thread_func2, nullptr);
    CPLSleep(0.3);
    GDALClose(hDS);
    CPLJoinThread(hThread);

    hDS = GDALOpen(GCORE_DATA_DIR "byte.tif", GA_ReadOnly);
    CPL_IGNORE_RET_VAL(GDALRasterIO(GDALGetRasterBand(hDS, 1), GF_Read, 0, 0, 20, 20, buf, 20, 20, GDT_Byte, 0, 0));
    hThread = CPLCreateJoinableThread(thread_func4, nullptr);
    CPLSleep(0.3);
    GDALClose(hDS);
    CPLJoinThread(hThread);

    GDALDestroyDriverManager();
    CSLDestroy( argv );

    return 0;
}