<commit_before>/*
 * manager.cpp
 *
 * Created on: 23/03/2015
 *
 * =========================================================================
 *  Copyright (C) 2015-, Daniele De Sensi (d.desensi.software@gmail.com)
 *
 *  This file is part of AdaptiveFastFlow.
 *
 *  AdaptiveFastFlow is free software: you can redistribute it and/or
 *  modify it under the terms of the Lesser GNU General Public
 *  License as published by the Free Software Foundation, either
 *  version 3 of the License, or (at your option) any later version.

 *  AdaptiveFastFlow is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  Lesser GNU General Public License for more details.
 *
 *  You should have received a copy of the Lesser GNU General Public
 *  License along with AdaptiveFastFlow.
 *  If not, see <http://www.gnu.org/licenses/>.
 *
 * =========================================================================
 */

#include "./manager.hpp"
#include "parameters.hpp"
#include "predictors.hpp"
#include "./node.hpp"
#include "utils.hpp"

#include <ff/farm.hpp>
#include <mammut/module.hpp>
#include <mammut/utils.hpp>
#include <mammut/mammut.hpp>

#include <cmath>
#include <iostream>
#include <limits>
#include <string>

#undef DEBUG
#undef DEBUGB

#ifdef DEBUG_MANAGER
#define DEBUG(x) do { cerr << "[Manager] " << x << endl; } while (0)
#define DEBUGB(x) do {x;} while(0)
#else
#define DEBUG(x)
#define DEBUGB(x)
#endif

namespace adpff{

class Parameters;

using namespace std;
using namespace ff;
using namespace mammut::cpufreq;
using namespace mammut::energy;
using namespace mammut::task;
using namespace mammut::topology;
using namespace mammut::utils;

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::setDomainToHighestFrequency(const Domain* domain){
    if(!domain->setGovernor(GOVERNOR_PERFORMANCE)){
        if(!domain->setGovernor(GOVERNOR_USERSPACE) ||
           !domain->setHighestFrequencyUserspace()){
            throw runtime_error("AdaptivityManagerFarm: Fatal error while "
                                "setting highest frequency for sensitive "
                                "emitter/collector. Try to run it without "
                                "sensitivity parameters.");
        }
    }
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getPrimaryValue(const MonitoredSample& sample) const{
    switch(_p.contractType){
        case CONTRACT_PERF_UTILIZATION:{
            return sample.utilization;
        }break;
        case CONTRACT_PERF_BANDWIDTH:
        case CONTRACT_PERF_COMPLETION_TIME:{
            return sample.bandwidth;
        }break;
        case CONTRACT_POWER_BUDGET:{
            return sample.watts;
        }break;
        default:{
            return 0;
        }break;
    }
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getSecondaryValue(const MonitoredSample& sample) const{
    switch(_p.contractType){
        case CONTRACT_PERF_UTILIZATION:
        case CONTRACT_PERF_BANDWIDTH:
        case CONTRACT_PERF_COMPLETION_TIME:{
            return sample.watts;
        }break;
        case CONTRACT_POWER_BUDGET:{
            return sample.bandwidth;
        }break;
        default:{
            return 0;
        }break;
    }
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getPrimaryValue() const{
    return getPrimaryValue(_samples->average());
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getSecondaryValue() const{
    return getSecondaryValue(_samples->average());
}

template <typename lb_t, typename gt_t>
bool ManagerFarm<lb_t, gt_t>::terminated(){
    /**
     * We do not need to wait if the emitter is terminated.
     * Indeed, if the workers terminated, the emitter surely terminated
     * too.
     */

    for(size_t i = 0; i < _activeWorkers.size(); i++){
        if(!_activeWorkers.at(i)->isTerminated()){
            return false;
        }else{
            DEBUG("Worker " << i << " terminated.");
        }
    }

    if(_collector &&
       !_collector->isTerminated()){
        return false;
    }else{
        DEBUG("Collector terminated.");
    }

    return true;
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::changeKnobs(){
    KnobsValues values = _calibrator->getNextKnobsValues(getPrimaryValue(),
                                                         getSecondaryValue(),
                                                         _remainingTasks);
    if(values != _configuration.getRealValues()){
        _configuration.setValues(values);
        _activeWorkers = dynamic_cast<const KnobWorkers*>(_configuration.getKnob(KNOB_TYPE_WORKERS))->getActiveWorkers();

        /****************** Clean state ******************/
        _lastStoredSampleMs = getMillisecondsTime();
        _samples->reset();
        if(_counter){
            _counter->reset();
        }
        _totalTasks = 0;
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::observe(){
    if(_p.observer){
        const KnobMapping* kMapping = dynamic_cast<const KnobMapping*>(_configuration.getKnob(KNOB_TYPE_MAPPING));
        _p.observer->observe(_lastStoredSampleMs,
                             _configuration.getRealValue(KNOB_TYPE_WORKERS),
                             _configuration.getRealValue(KNOB_TYPE_FREQUENCY),
                             kMapping->getEmitterVirtualCore(),
                             kMapping->getWorkersVirtualCore(),
                             kMapping->getCollectorVirtualCore(),
                             _samples->getLastSample().bandwidth,
                             _samples->average().bandwidth,
                             _samples->coefficientVariation().bandwidth,
                             _samples->average().utilization,
                             _samples->average().watts);
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::askForWorkersSamples(){
    for(size_t i = 0; i < _activeWorkers.size(); i++){
        _activeWorkers.at(i)->askForSample();
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::getWorkersSamples(WorkerSample& sample){
    AdaptiveNode* w;
    uint numActiveWorkers = _activeWorkers.size();
    sample = WorkerSample();

    for(size_t i = 0; i < numActiveWorkers; i++){
        WorkerSample tmp;
        w = _activeWorkers.at(i);
        w->getSampleResponse(tmp, _p.strategyPolling,
                             _samples->average().latency);
        sample += tmp;
    }
    sample.loadPercentage /= numActiveWorkers;
    sample.latency /= numActiveWorkers;
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::storeNewSample(){
    MonitoredSample sample;
    WorkerSample ws;
    Joules joules = 0.0;

    askForWorkersSamples();
    getWorkersSamples(ws);

    _totalTasks += ws.tasksCount;
    if(_p.contractType == CONTRACT_PERF_COMPLETION_TIME){
        if(_remainingTasks > ws.tasksCount){
            _remainingTasks -= ws.tasksCount;
        }else{
            _remainingTasks = 0;
        }
    }

    if(_counter){
        switch(_counter->getType()){
            case COUNTER_CPUS:{
                joules = ((CounterCpus*) _counter)->getJoulesCoresAll();
            }break;
            default:{
                joules = _counter->getJoules();
            }break;
        }
    }

    double now = getMillisecondsTime();
    double durationSecs = (now - _lastStoredSampleMs) / 1000.0;
    _lastStoredSampleMs = now;

    sample.watts = joules / durationSecs;
    sample.utilization = ws.loadPercentage;
    // ATTENTION: Bandwidth is not the number of task since the
    //            last observation but the number of expected
    //            tasks that will be processed in 1 second.
    //            For this reason, if we sum all the bandwidths in
    //            the result observation file, we may have an higher
    //            number than the number of tasks.
    sample.bandwidth = ws.bandwidthTotal;
    sample.latency = ws.latency;

    if(_counter){
        _counter->reset();
    }
    _samples->add(sample);

    DEBUGB(samplesFile << *_samples << "\n");
}

template <typename lb_t, typename gt_t>
bool ManagerFarm<lb_t, gt_t>::persist() const{
    bool r = false;
    switch(_p.strategyPersistence){
        case STRATEGY_PERSISTENCE_SAMPLES:{
            r = _samples->size() < _p.persistenceValue;
        }break;
        case STRATEGY_PERSISTENCE_TASKS:{
            r = _totalTasks < _p.persistenceValue;
        }break;
        case STRATEGY_PERSISTENCE_VARIATION:{
            const MonitoredSample& variation =
                    _samples->coefficientVariation();
            r = getPrimaryValue(variation) < _p.persistenceValue &&
                getSecondaryValue(variation) < _p.persistenceValue;
        }break;
    }
    return r;
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::initPredictors(){
    if(_p.strategyCalibration == STRATEGY_CALIBRATION_RANDOM){
        ; //CREARE TODO: Ci deve sempre essere un calibratore
    }else{
        switch(_p.strategyPrediction){
            case STRATEGY_PREDICTION_SIMPLE:{
                _calibrator = new CalibratorDummy(_p, _configuration, _samples);
            }break;
            case STRATEGY_PREDICTION_REGRESSION_LINEAR:{
                _calibrator = new CalibratorLowDiscrepancy(_p, _configuration, _samples);
            }break;
        }
    }
}

static Parameters& validate(Parameters& p){
    ParametersValidation apv = p.validate();
    if(apv != VALIDATION_OK){
        throw runtime_error("Invalid adaptivity parameters: " + std::to_string(apv));
    }
    return p;
}

static std::vector<AdaptiveNode*> convertWorkers(svector<ff_node*> w){
    std::vector<AdaptiveNode*> r;
    for(size_t i = 0; i < w.size(); i++){
        r.push_back(dynamic_cast<AdaptiveNode*>(w[i]));
    }
    return r;
}

template <typename lb_t, typename gt_t>
ManagerFarm<lb_t, gt_t>::ManagerFarm(ff_farm<lb_t, gt_t>* farm, Parameters parameters):
        _farm(farm),
        _p(validate(parameters)),
        _startTimeMs(0),
        _cpufreq(_p.mammut.getInstanceCpuFreq()),
        _counter(_p.mammut.getInstanceEnergy()->getCounter()),
        _task(_p.mammut.getInstanceTask()),
        _topology(_p.mammut.getInstanceTopology()),
        _emitter(dynamic_cast<AdaptiveNode*>(_farm->getEmitter())),
        _collector(dynamic_cast<AdaptiveNode*>(_farm->getCollector())),
        _activeWorkers(convertWorkers(_farm->getWorkers())),
        _configuration(_p, _emitter, _collector, _farm->getgt(), _activeWorkers),
        _samples(NULL),
        _totalTasks(0),
        _remainingTasks(0),
        _deadline(0),
        _lastStoredSampleMs(0),
        _calibrator(NULL){

    _samples = NULL;
    switch(_p.strategySmoothing){
    case STRATEGY_SMOOTHING_MOVING_AVERAGE:{
        _samples = new MovingAverageSimple<MonitoredSample>(_p.smoothingFactor);
    }break;
    case STRATEGY_SMOOTHING_EXPONENTIAL:{
        _samples = new MovingAverageExponential<MonitoredSample>(_p.smoothingFactor);
    }break;
    }

    DEBUGB(samplesFile.open("samples.csv"));
}

template <typename lb_t, typename gt_t>
ManagerFarm<lb_t, gt_t>::~ManagerFarm(){
    delete _samples;
    if(_calibrator){
        delete _calibrator;
    }
    DEBUGB(samplesFile.close());
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::initNodesPreRun() {
    for (size_t i = 0; i < _activeWorkers.size(); i++) {
        _activeWorkers.at(i)->initPreRun(_p.mammut, _p.archData.ticksPerNs, NODE_TYPE_WORKER);
    }
    if (_emitter) {
        _emitter->initPreRun(_p.mammut, _p.archData.ticksPerNs, NODE_TYPE_EMITTER);
    } else {
        throw runtime_error("Emitter is needed to use the manager.");
    }
    if (_collector) {
        _collector->initPreRun(_p.mammut, _p.archData.ticksPerNs, NODE_TYPE_COLLECTOR);
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::initNodesPostRun() {
    for (size_t i = 0; i < _activeWorkers.size(); i++) {
        _activeWorkers.at(i)->initPostRun();
    }
    _emitter->initPostRun();
    if (_collector) {
        _collector->initPostRun();
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::cleanNodes() {
    for (size_t i = 0; i < _activeWorkers.size(); i++) {
        _activeWorkers.at(i)->clean();
    }
    if (_emitter) {
        _emitter->clean();
    }
    if (_collector) {
        _collector->clean();
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::run(){
    initNodesPreRun();
    _farm->run_then_freeze();
    initNodesPostRun();

    if(_p.contractType != CONTRACT_NONE){
        _configuration.maxAllKnobs();
    }

    _startTimeMs = getMillisecondsTime();
    if(_counter){
        _counter->reset();
    }
    _lastStoredSampleMs = _startTimeMs;
    if(_p.observer){
        _p.observer->_startMonitoringMs = _lastStoredSampleMs;
    }

    if(_p.contractType == CONTRACT_PERF_COMPLETION_TIME){
        _remainingTasks = _p.expectedTasksNumber;
        _deadline = getMillisecondsTime()/1000.0 + _p.requiredCompletionTime;
    }

    initPredictors();

    double microsecsSleep = 0;
    if(_p.contractType == CONTRACT_NONE){
        _farm->wait();
        storeNewSample();
        observe();
    }else{
        /* Force the first calibration point. **/
        assert(_calibrator);
        changeKnobs();

        double startSample = getMillisecondsTime();

        while(!terminated()){
            double overheadMs = getMillisecondsTime() - startSample;
            microsecsSleep = ((double)_p.samplingInterval - overheadMs)*
                              (double)MAMMUT_MICROSECS_IN_MILLISEC;
            if(microsecsSleep < 0){
                microsecsSleep = 0;
            }
            usleep(microsecsSleep);
            startSample = getMillisecondsTime();

            storeNewSample();
            DEBUG("New sample stored.");

            if(_p.contractType == CONTRACT_PERF_COMPLETION_TIME){
                double now = getMillisecondsTime()/1000.0;
                if(now >= _deadline){
                    _p.requiredBandwidth = numeric_limits<double>::max();
                }else{
                    _p.requiredBandwidth = _remainingTasks /
                                           (_deadline - now);
                }
            }

            observe();

            if(!persist()){
                assert(_calibrator);
                changeKnobs();
                startSample = getMillisecondsTime();
            }
        }
        DEBUG("Terminated.");
    }

    uint duration = getMillisecondsTime() - _startTimeMs;
    if(_p.observer){
        vector<CalibrationStats> cs;
        if(_calibrator){
            cs = _calibrator->getCalibrationsStats();
            _p.observer->calibrationStats(cs, duration);
        }
        _p.observer->summaryStats(cs, duration);
    }

    cleanNodes();
}

}
<commit_msg>[FIX] Fixed execution time calculation when contract is NONE.<commit_after>/*
 * manager.cpp
 *
 * Created on: 23/03/2015
 *
 * =========================================================================
 *  Copyright (C) 2015-, Daniele De Sensi (d.desensi.software@gmail.com)
 *
 *  This file is part of AdaptiveFastFlow.
 *
 *  AdaptiveFastFlow is free software: you can redistribute it and/or
 *  modify it under the terms of the Lesser GNU General Public
 *  License as published by the Free Software Foundation, either
 *  version 3 of the License, or (at your option) any later version.

 *  AdaptiveFastFlow is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  Lesser GNU General Public License for more details.
 *
 *  You should have received a copy of the Lesser GNU General Public
 *  License along with AdaptiveFastFlow.
 *  If not, see <http://www.gnu.org/licenses/>.
 *
 * =========================================================================
 */

#include "./manager.hpp"
#include "parameters.hpp"
#include "predictors.hpp"
#include "./node.hpp"
#include "utils.hpp"

#include <ff/farm.hpp>
#include <mammut/module.hpp>
#include <mammut/utils.hpp>
#include <mammut/mammut.hpp>

#include <cmath>
#include <iostream>
#include <limits>
#include <string>

#undef DEBUG
#undef DEBUGB

#ifdef DEBUG_MANAGER
#define DEBUG(x) do { cerr << "[Manager] " << x << endl; } while (0)
#define DEBUGB(x) do {x;} while(0)
#else
#define DEBUG(x)
#define DEBUGB(x)
#endif

namespace adpff{

class Parameters;

using namespace std;
using namespace ff;
using namespace mammut::cpufreq;
using namespace mammut::energy;
using namespace mammut::task;
using namespace mammut::topology;
using namespace mammut::utils;

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::setDomainToHighestFrequency(const Domain* domain){
    if(!domain->setGovernor(GOVERNOR_PERFORMANCE)){
        if(!domain->setGovernor(GOVERNOR_USERSPACE) ||
           !domain->setHighestFrequencyUserspace()){
            throw runtime_error("AdaptivityManagerFarm: Fatal error while "
                                "setting highest frequency for sensitive "
                                "emitter/collector. Try to run it without "
                                "sensitivity parameters.");
        }
    }
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getPrimaryValue(const MonitoredSample& sample) const{
    switch(_p.contractType){
        case CONTRACT_PERF_UTILIZATION:{
            return sample.utilization;
        }break;
        case CONTRACT_PERF_BANDWIDTH:
        case CONTRACT_PERF_COMPLETION_TIME:{
            return sample.bandwidth;
        }break;
        case CONTRACT_POWER_BUDGET:{
            return sample.watts;
        }break;
        default:{
            return 0;
        }break;
    }
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getSecondaryValue(const MonitoredSample& sample) const{
    switch(_p.contractType){
        case CONTRACT_PERF_UTILIZATION:
        case CONTRACT_PERF_BANDWIDTH:
        case CONTRACT_PERF_COMPLETION_TIME:{
            return sample.watts;
        }break;
        case CONTRACT_POWER_BUDGET:{
            return sample.bandwidth;
        }break;
        default:{
            return 0;
        }break;
    }
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getPrimaryValue() const{
    return getPrimaryValue(_samples->average());
}

template <typename lb_t, typename gt_t>
double ManagerFarm<lb_t, gt_t>::getSecondaryValue() const{
    return getSecondaryValue(_samples->average());
}

template <typename lb_t, typename gt_t>
bool ManagerFarm<lb_t, gt_t>::terminated(){
    /**
     * We do not need to wait if the emitter is terminated.
     * Indeed, if the workers terminated, the emitter surely terminated
     * too.
     */

    for(size_t i = 0; i < _activeWorkers.size(); i++){
        if(!_activeWorkers.at(i)->isTerminated()){
            return false;
        }else{
            DEBUG("Worker " << i << " terminated.");
        }
    }

    if(_collector &&
       !_collector->isTerminated()){
        return false;
    }else{
        DEBUG("Collector terminated.");
    }

    return true;
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::changeKnobs(){
    KnobsValues values = _calibrator->getNextKnobsValues(getPrimaryValue(),
                                                         getSecondaryValue(),
                                                         _remainingTasks);
    if(values != _configuration.getRealValues()){
        _configuration.setValues(values);
        _activeWorkers = dynamic_cast<const KnobWorkers*>(_configuration.getKnob(KNOB_TYPE_WORKERS))->getActiveWorkers();

        /****************** Clean state ******************/
        _lastStoredSampleMs = getMillisecondsTime();
        _samples->reset();
        if(_counter){
            _counter->reset();
        }
        _totalTasks = 0;
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::observe(){
    if(_p.observer){
        const KnobMapping* kMapping = dynamic_cast<const KnobMapping*>(_configuration.getKnob(KNOB_TYPE_MAPPING));
        _p.observer->observe(_lastStoredSampleMs,
                             _configuration.getRealValue(KNOB_TYPE_WORKERS),
                             _configuration.getRealValue(KNOB_TYPE_FREQUENCY),
                             kMapping->getEmitterVirtualCore(),
                             kMapping->getWorkersVirtualCore(),
                             kMapping->getCollectorVirtualCore(),
                             _samples->getLastSample().bandwidth,
                             _samples->average().bandwidth,
                             _samples->coefficientVariation().bandwidth,
                             _samples->average().utilization,
                             _samples->average().watts);
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::askForWorkersSamples(){
    for(size_t i = 0; i < _activeWorkers.size(); i++){
        _activeWorkers.at(i)->askForSample();
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::getWorkersSamples(WorkerSample& sample){
    AdaptiveNode* w;
    uint numActiveWorkers = _activeWorkers.size();
    sample = WorkerSample();

    for(size_t i = 0; i < numActiveWorkers; i++){
        WorkerSample tmp;
        w = _activeWorkers.at(i);
        w->getSampleResponse(tmp, _p.strategyPolling,
                             _samples->average().latency);
        sample += tmp;
    }
    sample.loadPercentage /= numActiveWorkers;
    sample.latency /= numActiveWorkers;
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::storeNewSample(){
    MonitoredSample sample;
    WorkerSample ws;
    Joules joules = 0.0;

    askForWorkersSamples();
    getWorkersSamples(ws);

    _totalTasks += ws.tasksCount;
    if(_p.contractType == CONTRACT_PERF_COMPLETION_TIME){
        if(_remainingTasks > ws.tasksCount){
            _remainingTasks -= ws.tasksCount;
        }else{
            _remainingTasks = 0;
        }
    }

    if(_counter){
        switch(_counter->getType()){
            case COUNTER_CPUS:{
                joules = ((CounterCpus*) _counter)->getJoulesCoresAll();
            }break;
            default:{
                joules = _counter->getJoules();
            }break;
        }
    }

    double now = getMillisecondsTime();
    double durationSecs = (now - _lastStoredSampleMs) / 1000.0;
    _lastStoredSampleMs = now;

    sample.watts = joules / durationSecs;
    sample.utilization = ws.loadPercentage;
    // ATTENTION: Bandwidth is not the number of task since the
    //            last observation but the number of expected
    //            tasks that will be processed in 1 second.
    //            For this reason, if we sum all the bandwidths in
    //            the result observation file, we may have an higher
    //            number than the number of tasks.
    sample.bandwidth = ws.bandwidthTotal;
    sample.latency = ws.latency;

    if(_counter){
        _counter->reset();
    }
    _samples->add(sample);

    DEBUGB(samplesFile << *_samples << "\n");
}

template <typename lb_t, typename gt_t>
bool ManagerFarm<lb_t, gt_t>::persist() const{
    bool r = false;
    switch(_p.strategyPersistence){
        case STRATEGY_PERSISTENCE_SAMPLES:{
            r = _samples->size() < _p.persistenceValue;
        }break;
        case STRATEGY_PERSISTENCE_TASKS:{
            r = _totalTasks < _p.persistenceValue;
        }break;
        case STRATEGY_PERSISTENCE_VARIATION:{
            const MonitoredSample& variation =
                    _samples->coefficientVariation();
            r = getPrimaryValue(variation) < _p.persistenceValue &&
                getSecondaryValue(variation) < _p.persistenceValue;
        }break;
    }
    return r;
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::initPredictors(){
    if(_p.strategyCalibration == STRATEGY_CALIBRATION_RANDOM){
        ; //CREARE TODO: Ci deve sempre essere un calibratore
    }else{
        switch(_p.strategyPrediction){
            case STRATEGY_PREDICTION_SIMPLE:{
                _calibrator = new CalibratorDummy(_p, _configuration, _samples);
            }break;
            case STRATEGY_PREDICTION_REGRESSION_LINEAR:{
                _calibrator = new CalibratorLowDiscrepancy(_p, _configuration, _samples);
            }break;
        }
    }
}

static Parameters& validate(Parameters& p){
    ParametersValidation apv = p.validate();
    if(apv != VALIDATION_OK){
        throw runtime_error("Invalid adaptivity parameters: " + std::to_string(apv));
    }
    return p;
}

static std::vector<AdaptiveNode*> convertWorkers(svector<ff_node*> w){
    std::vector<AdaptiveNode*> r;
    for(size_t i = 0; i < w.size(); i++){
        r.push_back(dynamic_cast<AdaptiveNode*>(w[i]));
    }
    return r;
}

template <typename lb_t, typename gt_t>
ManagerFarm<lb_t, gt_t>::ManagerFarm(ff_farm<lb_t, gt_t>* farm, Parameters parameters):
        _farm(farm),
        _p(validate(parameters)),
        _startTimeMs(0),
        _cpufreq(_p.mammut.getInstanceCpuFreq()),
        _counter(_p.mammut.getInstanceEnergy()->getCounter()),
        _task(_p.mammut.getInstanceTask()),
        _topology(_p.mammut.getInstanceTopology()),
        _emitter(dynamic_cast<AdaptiveNode*>(_farm->getEmitter())),
        _collector(dynamic_cast<AdaptiveNode*>(_farm->getCollector())),
        _activeWorkers(convertWorkers(_farm->getWorkers())),
        _configuration(_p, _emitter, _collector, _farm->getgt(), _activeWorkers),
        _samples(NULL),
        _totalTasks(0),
        _remainingTasks(0),
        _deadline(0),
        _lastStoredSampleMs(0),
        _calibrator(NULL){

    _samples = NULL;
    switch(_p.strategySmoothing){
    case STRATEGY_SMOOTHING_MOVING_AVERAGE:{
        _samples = new MovingAverageSimple<MonitoredSample>(_p.smoothingFactor);
    }break;
    case STRATEGY_SMOOTHING_EXPONENTIAL:{
        _samples = new MovingAverageExponential<MonitoredSample>(_p.smoothingFactor);
    }break;
    }

    DEBUGB(samplesFile.open("samples.csv"));
}

template <typename lb_t, typename gt_t>
ManagerFarm<lb_t, gt_t>::~ManagerFarm(){
    delete _samples;
    if(_calibrator){
        delete _calibrator;
    }
    DEBUGB(samplesFile.close());
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::initNodesPreRun() {
    for (size_t i = 0; i < _activeWorkers.size(); i++) {
        _activeWorkers.at(i)->initPreRun(_p.mammut, _p.archData.ticksPerNs, NODE_TYPE_WORKER);
    }
    if (_emitter) {
        _emitter->initPreRun(_p.mammut, _p.archData.ticksPerNs, NODE_TYPE_EMITTER);
    } else {
        throw runtime_error("Emitter is needed to use the manager.");
    }
    if (_collector) {
        _collector->initPreRun(_p.mammut, _p.archData.ticksPerNs, NODE_TYPE_COLLECTOR);
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::initNodesPostRun() {
    for (size_t i = 0; i < _activeWorkers.size(); i++) {
        _activeWorkers.at(i)->initPostRun();
    }
    _emitter->initPostRun();
    if (_collector) {
        _collector->initPostRun();
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::cleanNodes() {
    for (size_t i = 0; i < _activeWorkers.size(); i++) {
        _activeWorkers.at(i)->clean();
    }
    if (_emitter) {
        _emitter->clean();
    }
    if (_collector) {
        _collector->clean();
    }
}

template <typename lb_t, typename gt_t>
void ManagerFarm<lb_t, gt_t>::run(){
    initNodesPreRun();
    _farm->run_then_freeze();
    initNodesPostRun();

    if(_p.contractType != CONTRACT_NONE){
        _configuration.maxAllKnobs();
    }

    _startTimeMs = getMillisecondsTime();
    if(_counter){
        _counter->reset();
    }
    _lastStoredSampleMs = _startTimeMs;
    if(_p.observer){
        _p.observer->_startMonitoringMs = _lastStoredSampleMs;
    }

    if(_p.contractType == CONTRACT_PERF_COMPLETION_TIME){
        _remainingTasks = _p.expectedTasksNumber;
        _deadline = getMillisecondsTime()/1000.0 + _p.requiredCompletionTime;
    }

    initPredictors();

    double microsecsSleep = 0;
    double startSample = getMillisecondsTime();

    if(_p.contractType == CONTRACT_NONE){
        _farm->wait();
        storeNewSample();
        observe();
    }else{
        /* Force the first calibration point. **/
        assert(_calibrator);
        changeKnobs();

        while(!terminated()){
            double overheadMs = getMillisecondsTime() - startSample;
            microsecsSleep = ((double)_p.samplingInterval - overheadMs)*
                              (double)MAMMUT_MICROSECS_IN_MILLISEC;
            if(microsecsSleep < 0){
                microsecsSleep = 0;
            }
            usleep(microsecsSleep);
            startSample = getMillisecondsTime();

            storeNewSample();
            DEBUG("New sample stored.");

            if(_p.contractType == CONTRACT_PERF_COMPLETION_TIME){
                double now = getMillisecondsTime()/1000.0;
                if(now >= _deadline){
                    _p.requiredBandwidth = numeric_limits<double>::max();
                }else{
                    _p.requiredBandwidth = _remainingTasks /
                                           (_deadline - now);
                }
            }

            observe();

            if(!persist()){
                assert(_calibrator);
                changeKnobs();
                startSample = getMillisecondsTime();
            }
        }
        DEBUG("Terminated.");
    }

    uint duration = getMillisecondsTime() - _startTimeMs;
    if(_p.observer){
        vector<CalibrationStats> cs;
        if(_calibrator){
            cs = _calibrator->getCalibrationsStats();
            _p.observer->calibrationStats(cs, duration);
        }
        _p.observer->summaryStats(cs, duration);
    }

    cleanNodes();
}

}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Matthew Harvey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "time_log.hpp"
#include "atomic_writer.hpp"
#include "file_utilities.hpp"
#include "stint.hpp"
#include "stream_utilities.hpp"
#include "string_utilities.hpp"
#include "time_point.hpp"
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <ios>
#include <iostream>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

using std::cerr;
using std::endl;
using std::getline;
using std::ifstream;
using std::isspace;
using std::ios;
using std::move;
using std::ofstream;
using std::ostringstream;
using std::regex;
using std::regex_search;
using std::remove;
using std::runtime_error;
using std::size_t;
using std::string;
using std::stringstream;
using std::tm;
using std::upper_bound;
using std::vector;

namespace chrono = std::chrono;

namespace swx
{

namespace
{
	string::size_type expected_time_stamp_length
	(	string const& p_time_format,
		unsigned int p_formatted_buf_len
	)
	{
		static string const time_format =
			time_point_to_stamp(now(), p_time_format, p_formatted_buf_len);
		return time_format.length();
	}

}  // end anonymous namespace

TimeLog::TimeLog
(	string const& p_filepath,
	string const& p_time_format,
	unsigned int p_formatted_buf_len
):
	m_is_loaded(false),
	m_formatted_buf_len(p_formatted_buf_len),
	m_filepath(p_filepath),
	m_time_format(p_time_format)
{
	assert (m_entries.empty());
	assert (m_activities.empty());
}

TimeLog::~TimeLog() = default;

void
TimeLog::append_entry(string const& p_activity)
{
	AtomicWriter writer(m_filepath);
	mark_cache_as_stale();
	writer.append(time_point_to_stamp(now(), m_time_format, m_formatted_buf_len));
	if (!p_activity.empty())
	{
		writer.append(" ");
		writer.append(p_activity);
	}
	writer.append("\n");
	writer.commit();
	mark_cache_as_stale();
	return;
}

vector<Stint>
TimeLog::get_stints
(	string const* p_sought_activity,
	TimePoint const* p_begin,
	TimePoint const* p_end,
	bool p_use_regex
)
{
	load();
	regex reg;
	if (!p_sought_activity) p_use_regex = false;
	if (p_use_regex)
	{
		assert (p_sought_activity);
		reg = regex(*p_sought_activity, regex::extended | regex::optimize);
	}
	vector<Stint> ret;
	auto const e = m_entries.end();
	auto it = (p_begin? find_entry_just_before(*p_begin): m_entries.begin());
	auto const n = now();
	auto const sought_id =
		(p_sought_activity? register_activity(*p_sought_activity): 0);
	for ( ; (it != e) && (!p_end || (it->time_point < *p_end)); ++it)
	{
		ActivityId const activity_id = it->activity_id;
		if (!p_sought_activity || (sought_id == activity_id) || p_use_regex)
		{
			auto const& activity = id_to_activity(activity_id);
			if (!p_use_regex || regex_search(activity, reg))
			{
				auto tp = it->time_point;
				if (p_begin && (tp < *p_begin)) tp = *p_begin;
				auto const next_it = it + 1;
				auto const done = (next_it == e);
				auto next_tp = (done? (n > tp? n: tp): next_it->time_point);
				if (p_end && (next_tp > *p_end)) next_tp = *p_end;
				assert (next_tp >= tp);
				assert (!p_begin || (tp >= *p_begin));
				assert (!p_end || (next_tp <= *p_end));
				auto const duration = next_tp - tp;
				auto const seconds = chrono::duration_cast<Seconds>(duration);
				Interval const interval(tp, seconds, done);
				ret.push_back(Stint(activity, interval));
			}
		}
	}
	return ret;
}

string
TimeLog::last_activity_to_match(string const& p_regex)
{
	load();
	regex const reg(p_regex, regex::extended | regex::optimize);
	typedef decltype(m_entries)::const_reverse_iterator RevIter;
	auto const empty_activity_id = register_activity("");
	for (RevIter rit = m_entries.rbegin(); rit != m_entries.rend(); ++rit)
	{
		auto const id = rit->activity_id;
		if (id != empty_activity_id)
		{
			auto const& activity = id_to_activity(id);
			if (regex_search(activity, reg))
			{
				return activity;
			}
		}
	}
	return string();
}

vector<string>
TimeLog::last_activities(size_t p_num)
{
	load();
	vector<string> ret;
	if (m_entries.empty())
	{
		return ret;
	}
	assert (m_entries.size() >= 1);
	typedef decltype(m_entries)::const_reverse_iterator RevIter;
	for (RevIter it = m_entries.rbegin(); it != m_entries.rend(); ++it)
	{
		if (ret.size() == p_num)
		{
			break;
		}
		auto const& activity = id_to_activity(it->activity_id);
		if (!activity.empty() && (ret.empty() || (activity != ret.back())))
		{
			ret.push_back(activity);
		}
	}
	assert (ret.size() <= p_num);
	assert (ret.size() <= m_entries.size());
	return ret;
}

bool
TimeLog::is_active()
{
	load();
	if (m_entries.empty())
	{
		return false;
	}
	return !id_to_activity(m_entries.back().activity_id).empty();
}

bool
TimeLog::has_activity(string const& p_activity)
{
	load();
	return m_activities.find(p_activity) != m_activities.end();
}

void
TimeLog::clear_cache()
{
	m_entries.clear();
	m_activities.clear();
	mark_cache_as_stale();
	return;
}

void
TimeLog::mark_cache_as_stale()
{
	m_is_loaded = false;
}

void
TimeLog::load()
{
	if (!m_is_loaded)
	{
		clear_cache();
		if (file_exists_at(m_filepath))
		{
			ifstream infile(m_filepath.c_str());
			enable_exceptions(infile);
			string line;
			size_t line_number = 1;
			while (infile.peek() != EOF)
			{
				getline(infile, line);
				load_entry(line, line_number);
				++line_number;
			}
			if (!m_entries.empty() && (m_entries.back().time_point > now()))
			{
				throw runtime_error
				(	"The final entry in the time log is future-dated. "
				    "Future dated entries are not supported."
				);
			}
		}
		m_is_loaded = true;
	}
	return;
}

TimeLog::ActivityId
TimeLog::register_activity(string const& p_activity)
{
	return &*(m_activities.insert(p_activity).first);
}

void
TimeLog::load_entry(string const& p_entry_string, size_t p_line_number)
{
	if (p_entry_string.size() < expected_time_stamp_length(m_time_format, m_formatted_buf_len))
	{
		ostringstream oss;
		enable_exceptions(oss);
		oss << "Error parsing the time log at line "
		    << p_line_number << '.';
		throw runtime_error(oss.str());
	}
	auto it = p_entry_string.begin() + expected_time_stamp_length(m_time_format, m_formatted_buf_len);
	assert (it > p_entry_string.begin());
	string const time_stamp(p_entry_string.begin(), it);
	auto const activity = trim(string(it, p_entry_string.end()));
	auto const activity_id = register_activity(activity);
	auto const time_point = time_stamp_to_point(time_stamp, m_time_format);
	Entry entry(activity_id, time_point);
	if (!m_entries.empty())
	{
		auto const last_time_point = m_entries.back().time_point;
		if (entry.time_point < last_time_point)
		{
			ostringstream oss;
			enable_exceptions(oss);
			oss << "Time log entries out of order at line "
			    << p_line_number << '.';
			throw runtime_error(oss.str());
		}
	}
	m_entries.push_back(entry);
	return;
}

string const&
TimeLog::id_to_activity(ActivityId p_activity_id)
{
	load();
	return *p_activity_id;
}

vector<TimeLog::Entry>::const_iterator
TimeLog::find_entry_just_before(TimePoint const& p_time_point)
{
	load();
	auto const comp = [](Entry const& lhs, Entry const& rhs)
	{
		return lhs.time_point < rhs.time_point;
	};
	auto const b = m_entries.begin(), e = m_entries.end();
	Entry const dummy(0, p_time_point);
	auto it = upper_bound(b, e, dummy, comp);
	for ( ; (it != b) && ((it == e) || (it->time_point > p_time_point)); --it)
	{
	}
	return it;
}

TimeLog::Entry::Entry(ActivityId p_activity_id, TimePoint const& p_time_point):
	activity_id(p_activity_id),
	time_point(p_time_point)
{
}

}  // namespace swx
<commit_msg>Remove unused dependencies from time_log.cpp.<commit_after>/*
 * Copyright 2014 Matthew Harvey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "time_log.hpp"
#include "atomic_writer.hpp"
#include "file_utilities.hpp"
#include "stint.hpp"
#include "stream_utilities.hpp"
#include "string_utilities.hpp"
#include "time_point.hpp"
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <ios>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

using std::getline;
using std::ifstream;
using std::isspace;
using std::ios;
using std::move;
using std::ofstream;
using std::ostringstream;
using std::regex;
using std::regex_search;
using std::remove;
using std::runtime_error;
using std::size_t;
using std::string;
using std::stringstream;
using std::tm;
using std::upper_bound;
using std::vector;

namespace chrono = std::chrono;

namespace swx
{

namespace
{
	string::size_type expected_time_stamp_length
	(	string const& p_time_format,
		unsigned int p_formatted_buf_len
	)
	{
		static string const time_format =
			time_point_to_stamp(now(), p_time_format, p_formatted_buf_len);
		return time_format.length();
	}

}  // end anonymous namespace

TimeLog::TimeLog
(	string const& p_filepath,
	string const& p_time_format,
	unsigned int p_formatted_buf_len
):
	m_is_loaded(false),
	m_formatted_buf_len(p_formatted_buf_len),
	m_filepath(p_filepath),
	m_time_format(p_time_format)
{
	assert (m_entries.empty());
	assert (m_activities.empty());
}

TimeLog::~TimeLog() = default;

void
TimeLog::append_entry(string const& p_activity)
{
	AtomicWriter writer(m_filepath);
	mark_cache_as_stale();
	writer.append(time_point_to_stamp(now(), m_time_format, m_formatted_buf_len));
	if (!p_activity.empty())
	{
		writer.append(" ");
		writer.append(p_activity);
	}
	writer.append("\n");
	writer.commit();
	mark_cache_as_stale();
	return;
}

vector<Stint>
TimeLog::get_stints
(	string const* p_sought_activity,
	TimePoint const* p_begin,
	TimePoint const* p_end,
	bool p_use_regex
)
{
	load();
	regex reg;
	if (!p_sought_activity) p_use_regex = false;
	if (p_use_regex)
	{
		assert (p_sought_activity);
		reg = regex(*p_sought_activity, regex::extended | regex::optimize);
	}
	vector<Stint> ret;
	auto const e = m_entries.end();
	auto it = (p_begin? find_entry_just_before(*p_begin): m_entries.begin());
	auto const n = now();
	auto const sought_id =
		(p_sought_activity? register_activity(*p_sought_activity): 0);
	for ( ; (it != e) && (!p_end || (it->time_point < *p_end)); ++it)
	{
		ActivityId const activity_id = it->activity_id;
		if (!p_sought_activity || (sought_id == activity_id) || p_use_regex)
		{
			auto const& activity = id_to_activity(activity_id);
			if (!p_use_regex || regex_search(activity, reg))
			{
				auto tp = it->time_point;
				if (p_begin && (tp < *p_begin)) tp = *p_begin;
				auto const next_it = it + 1;
				auto const done = (next_it == e);
				auto next_tp = (done? (n > tp? n: tp): next_it->time_point);
				if (p_end && (next_tp > *p_end)) next_tp = *p_end;
				assert (next_tp >= tp);
				assert (!p_begin || (tp >= *p_begin));
				assert (!p_end || (next_tp <= *p_end));
				auto const duration = next_tp - tp;
				auto const seconds = chrono::duration_cast<Seconds>(duration);
				Interval const interval(tp, seconds, done);
				ret.push_back(Stint(activity, interval));
			}
		}
	}
	return ret;
}

string
TimeLog::last_activity_to_match(string const& p_regex)
{
	load();
	regex const reg(p_regex, regex::extended | regex::optimize);
	typedef decltype(m_entries)::const_reverse_iterator RevIter;
	auto const empty_activity_id = register_activity("");
	for (RevIter rit = m_entries.rbegin(); rit != m_entries.rend(); ++rit)
	{
		auto const id = rit->activity_id;
		if (id != empty_activity_id)
		{
			auto const& activity = id_to_activity(id);
			if (regex_search(activity, reg))
			{
				return activity;
			}
		}
	}
	return string();
}

vector<string>
TimeLog::last_activities(size_t p_num)
{
	load();
	vector<string> ret;
	if (m_entries.empty())
	{
		return ret;
	}
	assert (m_entries.size() >= 1);
	typedef decltype(m_entries)::const_reverse_iterator RevIter;
	for (RevIter it = m_entries.rbegin(); it != m_entries.rend(); ++it)
	{
		if (ret.size() == p_num)
		{
			break;
		}
		auto const& activity = id_to_activity(it->activity_id);
		if (!activity.empty() && (ret.empty() || (activity != ret.back())))
		{
			ret.push_back(activity);
		}
	}
	assert (ret.size() <= p_num);
	assert (ret.size() <= m_entries.size());
	return ret;
}

bool
TimeLog::is_active()
{
	load();
	if (m_entries.empty())
	{
		return false;
	}
	return !id_to_activity(m_entries.back().activity_id).empty();
}

bool
TimeLog::has_activity(string const& p_activity)
{
	load();
	return m_activities.find(p_activity) != m_activities.end();
}

void
TimeLog::clear_cache()
{
	m_entries.clear();
	m_activities.clear();
	mark_cache_as_stale();
	return;
}

void
TimeLog::mark_cache_as_stale()
{
	m_is_loaded = false;
}

void
TimeLog::load()
{
	if (!m_is_loaded)
	{
		clear_cache();
		if (file_exists_at(m_filepath))
		{
			ifstream infile(m_filepath.c_str());
			enable_exceptions(infile);
			string line;
			size_t line_number = 1;
			while (infile.peek() != EOF)
			{
				getline(infile, line);
				load_entry(line, line_number);
				++line_number;
			}
			if (!m_entries.empty() && (m_entries.back().time_point > now()))
			{
				throw runtime_error
				(	"The final entry in the time log is future-dated. "
				    "Future dated entries are not supported."
				);
			}
		}
		m_is_loaded = true;
	}
	return;
}

TimeLog::ActivityId
TimeLog::register_activity(string const& p_activity)
{
	return &*(m_activities.insert(p_activity).first);
}

void
TimeLog::load_entry(string const& p_entry_string, size_t p_line_number)
{
	if (p_entry_string.size() < expected_time_stamp_length(m_time_format, m_formatted_buf_len))
	{
		ostringstream oss;
		enable_exceptions(oss);
		oss << "Error parsing the time log at line "
		    << p_line_number << '.';
		throw runtime_error(oss.str());
	}
	auto it = p_entry_string.begin() + expected_time_stamp_length(m_time_format, m_formatted_buf_len);
	assert (it > p_entry_string.begin());
	string const time_stamp(p_entry_string.begin(), it);
	auto const activity = trim(string(it, p_entry_string.end()));
	auto const activity_id = register_activity(activity);
	auto const time_point = time_stamp_to_point(time_stamp, m_time_format);
	Entry entry(activity_id, time_point);
	if (!m_entries.empty())
	{
		auto const last_time_point = m_entries.back().time_point;
		if (entry.time_point < last_time_point)
		{
			ostringstream oss;
			enable_exceptions(oss);
			oss << "Time log entries out of order at line "
			    << p_line_number << '.';
			throw runtime_error(oss.str());
		}
	}
	m_entries.push_back(entry);
	return;
}

string const&
TimeLog::id_to_activity(ActivityId p_activity_id)
{
	load();
	return *p_activity_id;
}

vector<TimeLog::Entry>::const_iterator
TimeLog::find_entry_just_before(TimePoint const& p_time_point)
{
	load();
	auto const comp = [](Entry const& lhs, Entry const& rhs)
	{
		return lhs.time_point < rhs.time_point;
	};
	auto const b = m_entries.begin(), e = m_entries.end();
	Entry const dummy(0, p_time_point);
	auto it = upper_bound(b, e, dummy, comp);
	for ( ; (it != b) && ((it == e) || (it->time_point > p_time_point)); --it)
	{
	}
	return it;
}

TimeLog::Entry::Entry(ActivityId p_activity_id, TimePoint const& p_time_point):
	activity_id(p_activity_id),
	time_point(p_time_point)
{
}

}  // namespace swx
<|endoftext|>


<commit_before>///
/// @file      calculator.hpp
/// @brief     calculator::eval(const std::string&) evaluates an integer
///            arithmetic expression and returns the result. If an error
///            occurs a calculator::error exception is thrown.
///            <https://github.com/kimwalisch/calculator>
/// @author    Kim Walisch, <kim.walisch@gmail.com>
/// @copyright Copyright (C) 2013 Kim Walisch
/// @date      November 30, 2013
/// @license   BSD 2-Clause, http://opensource.org/licenses/BSD-2-Clause
/// @version   1.0 patched: `^' is raise to power instead of XOR.
///
/// == Supported operators ==
///
/// OPERATOR    NAME                     ASSOCIATIVITY    PRECEDENCE
///
/// |           Bitwise Inclusive OR    Left               4
/// &           Bitwise AND             Left               6
/// <<          Shift Left              Left               9
/// >>          Shift Right             Left               9
/// +           Addition                Left              10
/// -           Subtraction             Left              10
/// *           Multiplication          Left              20
/// /           Division                Left              20
/// %           Modulo                  Left              20
/// ^, **       Raise to power          Right             30
/// e, E        Scientific notation     Right             40
/// ~           Unary complement        Left              99
///
/// The operator precedence has been set according to (uses the C and
/// C++ operator precedence): http://en.wikipedia.org/wiki/Order_of_operations
/// Operators with higher precedence are evaluated before operators
/// with relatively lower precedence. Unary operators are set to have
/// the highest precedence, this is not strictly correct for the power
/// operator e.g. "-3**2" = 9 but a lot of software tools (Bash shell,
/// Microsoft Excel, GNU bc, ...) use the same convention.
///
/// == Examples of valid expressions ==
///
/// "65536 >> 15"                       = 2
/// "2**16"                             = 65536
/// "(0 + 0xDf234 - 1000)*3/2%999"      = 828
/// "-(2**2**2**2)"                     = -65536
/// "(0 + ~(0xDF234 & 1000) *3) /-2"    = 817
/// "(2**16) + (1 << 16) >> 0X5"        = 4096
/// "5*-(2**(9+7))/3+5*(1 & 0xFf123)"   = -109221
///
/// == About the algorithm used ==
///
/// calculator::eval(std::string&) relies on the ExpressionParser
/// class which is a simple C++ operator precedence parser with infix
/// notation for integer arithmetic expressions.
/// ExpressionParser has its roots in a JavaScript parser published
/// at: http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence/114961#114961
/// The same author has also published an article about his operator
/// precedence algorithm at PerlMonks:
/// http://www.perlmonks.org/?node_id=554516
///

#ifndef CALCULATOR_HPP
#define CALCULATOR_HPP

#include <stdexcept>
#include <string>
#include <sstream>
#include <stack>
#include <cstddef>
#include <cctype>

namespace calculator
{

/// calculator::eval() throws a calculator::error if it fails
/// to evaluate the expression string.
///
class error : public std::runtime_error
{
public:
  error(const std::string& expr, const std::string& message)
    : std::runtime_error(message),
      expr_(expr)
  { }
#if __cplusplus >= 201103L
  ~error() { }
#else
  ~error() throw() { }
#endif
  std::string expression() const
  {
    return expr_;
  }
private:
  std::string expr_;
};

template <typename T>
class ExpressionParser
{
public:
  /// Evaluate an integer arithmetic expression and return its result.
  /// @throw error if parsing fails.
  ///
  T eval(const std::string& expr)
  {
    T result = 0;
    index_ = 0;
    expr_ = expr;
    try
    {
      result = parseExpr();
      if (!isEnd())
        unexpected();
    }
    catch (const calculator::error&)
    {
      while(!stack_.empty())
        stack_.pop();
      throw;
    }
    return result;
  }

  /// Get the integer value of a character.
  T eval(char c)
  {
    std::string expr(1, c);
    return eval(expr);
  }

private:
  enum
  {
    OPERATOR_NULL,
    OPERATOR_BITWISE_OR,     /// |
    OPERATOR_BITWISE_XOR,    /// ^
    OPERATOR_BITWISE_AND,    /// &
    OPERATOR_BITWISE_SHL,    /// <<
    OPERATOR_BITWISE_SHR,    /// >>
    OPERATOR_ADDITION,       /// +
    OPERATOR_SUBTRACTION,    /// -
    OPERATOR_MULTIPLICATION, /// *
    OPERATOR_DIVISION,       /// /
    OPERATOR_MODULO,         /// %
    OPERATOR_POWER,          /// **
    OPERATOR_EXPONENT        /// e, E
  };

  struct Operator
  {
    /// Operator, one of the OPERATOR_* enum definitions
    int op;
    int precedence;
    /// 'L' = left or 'R' = right
    int associativity;
    Operator(int opr, int prec, int assoc) :
      op(opr),
      precedence(prec),
      associativity(assoc)
    { }
  };

  struct OperatorValue
  {
    Operator op;
    T value;
    OperatorValue(const Operator& opr, T val) :
      op(opr),
      value(val)
    { }
    int getPrecedence() const
    {
      return op.precedence;
    }
    bool isNull() const
    {
      return op.op == OPERATOR_NULL;
    }
  };

  /// Expression string
  std::string expr_;
  /// Current expression index, incremented whilst parsing
  std::size_t index_;
  /// The current operator and its left value
  /// are pushed onto the stack if the operator on
  /// top of the stack has lower precedence.
  std::stack<OperatorValue> stack_;

  /// Exponentiation by squaring, x^n.
  static T pow(T x, T n)
  {
    T res = 1;
    while (n != 0)
    {
      if (n % 2 != 0)
      {
        res *= x;
        n -= 1;
      }
      x *= x;
      n /= 2;
    }
    return res;
  }

  T checkZero(T value) const
  {
    if (value == 0)
    {
      std::string divOperators("/%");
      std::size_t division = expr_.find_last_of(divOperators, index_ - 2);
      std::ostringstream msg;
      msg << "Parser error: division by 0";
      if (division != std::string::npos)
        msg << " (error token is \""
            << expr_.substr(division, expr_.size() - division)
            << "\")";
      throw calculator::error(expr_, msg.str());
    }
    return value;
  }

  T calculate(T v1, T v2, const Operator& op) const
  {
    switch (op.op)
    {
      case OPERATOR_BITWISE_OR:     return v1 | v2;
      case OPERATOR_BITWISE_XOR:    return v1 ^ v2;
      case OPERATOR_BITWISE_AND:    return v1 & v2;
      case OPERATOR_BITWISE_SHL:    return v1 << v2;
      case OPERATOR_BITWISE_SHR:    return v1 >> v2;
      case OPERATOR_ADDITION:       return v1 + v2;
      case OPERATOR_SUBTRACTION:    return v1 - v2;
      case OPERATOR_MULTIPLICATION: return v1 * v2;
      case OPERATOR_DIVISION:       return v1 / checkZero(v2);
      case OPERATOR_MODULO:         return v1 % checkZero(v2);
      case OPERATOR_POWER:          return pow(v1, v2);
      case OPERATOR_EXPONENT:       return v1 * pow(10, v2);
      default:                      return 0;
    }
  }

  bool isEnd() const
  {
    return index_ >= expr_.size();
  }

  /// Returns the character at the current expression index or
  /// 0 if the end of the expression is reached.
  ///
  char getCharacter() const
  {
    if (!isEnd())
      return expr_[index_];
    return 0;
  }

  /// Parse str at the current expression index.
  /// @throw error if parsing fails.
  ///
  void expect(const std::string& str)
  {
    if (expr_.compare(index_, str.size(), str) != 0)
      unexpected();
    index_ += str.size();
  }

  void unexpected() const
  {
    std::ostringstream msg;
    msg << "Syntax error: unexpected token \""
        << expr_.substr(index_, expr_.size() - index_)
        << "\" at index "
        << index_;
    throw calculator::error(expr_, msg.str());
  }

  /// Eat all white space characters at the
  /// current expression index.
  ///
  void eatSpaces()
  {
    while (std::isspace(getCharacter()) != 0)
      index_++;
  }

  /// Parse a binary operator at the current expression index.
  /// @return Operator with precedence and associativity.
  ///
  Operator parseOp()
  {
    eatSpaces();
    switch (getCharacter())
    {
      case '|': index_++;     return Operator(OPERATOR_BITWISE_OR,      4, 'L');
      case '&': index_++;     return Operator(OPERATOR_BITWISE_AND,     6, 'L');
      case '<': expect("<<"); return Operator(OPERATOR_BITWISE_SHL,     9, 'L');
      case '>': expect(">>"); return Operator(OPERATOR_BITWISE_SHR,     9, 'L');
      case '+': index_++;     return Operator(OPERATOR_ADDITION,       10, 'L');
      case '-': index_++;     return Operator(OPERATOR_SUBTRACTION,    10, 'L');
      case '/': index_++;     return Operator(OPERATOR_DIVISION,       20, 'L');
      case '%': index_++;     return Operator(OPERATOR_MODULO,         20, 'L');
      case '*': index_++; if (getCharacter() != '*')
                              return Operator(OPERATOR_MULTIPLICATION, 20, 'L');
                index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case '^': index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case 'e': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      case 'E': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      default :               return Operator(OPERATOR_NULL,            0, 'L');
    }
  }

  static T toInteger(char c)
  {
    if (c >= '0' && c <= '9') return c -'0';
    if (c >= 'a' && c <= 'f') return c -'a' + 0xa;
    if (c >= 'A' && c <= 'F') return c -'A' + 0xa;
    T noDigit = 0xf + 1;
    return noDigit;
  }

  T getInteger() const
  {
    return toInteger(getCharacter());
  }

  T parseDecimal()
  {
    T value = 0;
    for (T d; (d = getInteger()) <= 9; index_++)
      value = value * 10 + d;
    return value;
  }

  T parseHex()
  {
    index_ = index_ + 2;
    T value = 0;
    for (T h; (h = getInteger()) <= 0xf; index_++)
      value = value * 0x10 + h;
    return value;
  }

  bool isHex() const
  {
    if (index_ + 2 < expr_.size())
    {
      char x = expr_[index_ + 1];
      char h = expr_[index_ + 2];
      return (std::tolower(x) == 'x' && toInteger(h) <= 0xf);
    }
    return false;
  }

  /// Parse an integer value at the current expression index.
  /// The unary `+', `-' and `~' operators and opening
  /// parentheses `(' cause recursion.
  ///
  T parseValue()
  {
    T val = 0;
    eatSpaces();
    switch (getCharacter())
    {
      case '0': if (isHex())
                  val = parseHex();
                else
                  val = parseDecimal();
                break;
      case '1': case '2': case '3': case '4': case '5':
      case '6': case '7': case '8': case '9':
                val = parseDecimal();
                break;
      case '(': index_++;
                val = parseExpr();
                eatSpaces();
                if (getCharacter() != ')')
                {
                  if (!isEnd())
                    unexpected();
                  throw calculator::error(expr_, "Syntax error: `)' expected at end of expression");
                }
                index_++; break;
      case '~': index_++; val = ~parseValue(); break;
      case '+': index_++; val =  parseValue(); break;
      case '-': index_++; val =  parseValue() * static_cast<T>(-1);
                break;
      default : if (!isEnd())
                  unexpected();
                throw calculator::error(expr_, "Syntax error: value expected at end of expression");
    }
    return val;
  }

  /// Parse all operations of the current parenthesis
  /// level and the levels above, when done
  /// return the result (value).
  ///
  T parseExpr()
  {
    stack_.push(OperatorValue(Operator(OPERATOR_NULL, 0, 'L'), 0));
    // first parse value on the left
    T value = parseValue();

    while (!stack_.empty())
    {
      // parse an operator (+, -, *, ...)
      Operator op(parseOp());
      while (op.precedence  < stack_.top().getPrecedence() || (
             op.precedence == stack_.top().getPrecedence() &&
             op.associativity == 'L'))
      {
        // end reached
        if (stack_.top().isNull())
        {
          stack_.pop();
          return value;
        }
        // do the calculation ("reduce"), producing a new value
        value = calculate(stack_.top().value, value, stack_.top().op);
        stack_.pop();
      }

      // store on stack_ and continue parsing ("shift")
      stack_.push(OperatorValue(op, value));
      // parse value on the right
      value = parseValue();
    }
    return 0;
  }
};

template <typename T>
inline T eval(const std::string& expression)
{
  ExpressionParser<T> parser;
  return parser.eval(expression);
}

template <typename T>
inline T eval(char c)
{
  ExpressionParser<T> parser;
  return parser.eval(c);
}

inline int eval(const std::string& expression)
{
  return eval<int>(expression);
}

inline int eval(char c)
{
  return eval<int>(c);
}

} // namespace calculator

#endif
<commit_msg>Update copyright year<commit_after>///
/// @file      calculator.hpp
/// @brief     calculator::eval(const std::string&) evaluates an integer
///            arithmetic expression and returns the result. If an error
///            occurs a calculator::error exception is thrown.
///            <https://github.com/kimwalisch/calculator>
/// @author    Kim Walisch, <kim.walisch@gmail.com>
/// @copyright Copyright (C) 2017 Kim Walisch
/// @license   BSD 2-Clause, http://opensource.org/licenses/BSD-2-Clause
/// @version   1.1 patched: `^' is raise to power instead of XOR.
///
/// == Supported operators ==
///
/// OPERATOR    NAME                     ASSOCIATIVITY    PRECEDENCE
///
/// |           Bitwise Inclusive OR    Left               4
/// &           Bitwise AND             Left               6
/// <<          Shift Left              Left               9
/// >>          Shift Right             Left               9
/// +           Addition                Left              10
/// -           Subtraction             Left              10
/// *           Multiplication          Left              20
/// /           Division                Left              20
/// %           Modulo                  Left              20
/// ^, **       Raise to power          Right             30
/// e, E        Scientific notation     Right             40
/// ~           Unary complement        Left              99
///
/// The operator precedence has been set according to (uses the C and
/// C++ operator precedence): http://en.wikipedia.org/wiki/Order_of_operations
/// Operators with higher precedence are evaluated before operators
/// with relatively lower precedence. Unary operators are set to have
/// the highest precedence, this is not strictly correct for the power
/// operator e.g. "-3**2" = 9 but a lot of software tools (Bash shell,
/// Microsoft Excel, GNU bc, ...) use the same convention.
///
/// == Examples of valid expressions ==
///
/// "65536 >> 15"                       = 2
/// "2**16"                             = 65536
/// "(0 + 0xDf234 - 1000)*3/2%999"      = 828
/// "-(2**2**2**2)"                     = -65536
/// "(0 + ~(0xDF234 & 1000) *3) /-2"    = 817
/// "(2**16) + (1 << 16) >> 0X5"        = 4096
/// "5*-(2**(9+7))/3+5*(1 & 0xFf123)"   = -109221
///
/// == About the algorithm used ==
///
/// calculator::eval(std::string&) relies on the ExpressionParser
/// class which is a simple C++ operator precedence parser with infix
/// notation for integer arithmetic expressions.
/// ExpressionParser has its roots in a JavaScript parser published
/// at: http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence/114961#114961
/// The same author has also published an article about his operator
/// precedence algorithm at PerlMonks:
/// http://www.perlmonks.org/?node_id=554516
///

#ifndef CALCULATOR_HPP
#define CALCULATOR_HPP

#include <stdexcept>
#include <string>
#include <sstream>
#include <stack>
#include <cstddef>
#include <cctype>

namespace calculator
{

/// calculator::eval() throws a calculator::error if it fails
/// to evaluate the expression string.
///
class error : public std::runtime_error
{
public:
  error(const std::string& expr, const std::string& message)
    : std::runtime_error(message),
      expr_(expr)
  { }
#if __cplusplus >= 201103L
  ~error() { }
#else
  ~error() throw() { }
#endif
  std::string expression() const
  {
    return expr_;
  }
private:
  std::string expr_;
};

template <typename T>
class ExpressionParser
{
public:
  /// Evaluate an integer arithmetic expression and return its result.
  /// @throw error if parsing fails.
  ///
  T eval(const std::string& expr)
  {
    T result = 0;
    index_ = 0;
    expr_ = expr;
    try
    {
      result = parseExpr();
      if (!isEnd())
        unexpected();
    }
    catch (const calculator::error&)
    {
      while(!stack_.empty())
        stack_.pop();
      throw;
    }
    return result;
  }

  /// Get the integer value of a character.
  T eval(char c)
  {
    std::string expr(1, c);
    return eval(expr);
  }

private:
  enum
  {
    OPERATOR_NULL,
    OPERATOR_BITWISE_OR,     /// |
    OPERATOR_BITWISE_XOR,    /// ^
    OPERATOR_BITWISE_AND,    /// &
    OPERATOR_BITWISE_SHL,    /// <<
    OPERATOR_BITWISE_SHR,    /// >>
    OPERATOR_ADDITION,       /// +
    OPERATOR_SUBTRACTION,    /// -
    OPERATOR_MULTIPLICATION, /// *
    OPERATOR_DIVISION,       /// /
    OPERATOR_MODULO,         /// %
    OPERATOR_POWER,          /// **
    OPERATOR_EXPONENT        /// e, E
  };

  struct Operator
  {
    /// Operator, one of the OPERATOR_* enum definitions
    int op;
    int precedence;
    /// 'L' = left or 'R' = right
    int associativity;
    Operator(int opr, int prec, int assoc) :
      op(opr),
      precedence(prec),
      associativity(assoc)
    { }
  };

  struct OperatorValue
  {
    Operator op;
    T value;
    OperatorValue(const Operator& opr, T val) :
      op(opr),
      value(val)
    { }
    int getPrecedence() const
    {
      return op.precedence;
    }
    bool isNull() const
    {
      return op.op == OPERATOR_NULL;
    }
  };

  /// Expression string
  std::string expr_;
  /// Current expression index, incremented whilst parsing
  std::size_t index_;
  /// The current operator and its left value
  /// are pushed onto the stack if the operator on
  /// top of the stack has lower precedence.
  std::stack<OperatorValue> stack_;

  /// Exponentiation by squaring, x^n.
  static T pow(T x, T n)
  {
    T res = 1;
    while (n != 0)
    {
      if (n % 2 != 0)
      {
        res *= x;
        n -= 1;
      }
      x *= x;
      n /= 2;
    }
    return res;
  }

  T checkZero(T value) const
  {
    if (value == 0)
    {
      std::string divOperators("/%");
      std::size_t division = expr_.find_last_of(divOperators, index_ - 2);
      std::ostringstream msg;
      msg << "Parser error: division by 0";
      if (division != std::string::npos)
        msg << " (error token is \""
            << expr_.substr(division, expr_.size() - division)
            << "\")";
      throw calculator::error(expr_, msg.str());
    }
    return value;
  }

  T calculate(T v1, T v2, const Operator& op) const
  {
    switch (op.op)
    {
      case OPERATOR_BITWISE_OR:     return v1 | v2;
      case OPERATOR_BITWISE_XOR:    return v1 ^ v2;
      case OPERATOR_BITWISE_AND:    return v1 & v2;
      case OPERATOR_BITWISE_SHL:    return v1 << v2;
      case OPERATOR_BITWISE_SHR:    return v1 >> v2;
      case OPERATOR_ADDITION:       return v1 + v2;
      case OPERATOR_SUBTRACTION:    return v1 - v2;
      case OPERATOR_MULTIPLICATION: return v1 * v2;
      case OPERATOR_DIVISION:       return v1 / checkZero(v2);
      case OPERATOR_MODULO:         return v1 % checkZero(v2);
      case OPERATOR_POWER:          return pow(v1, v2);
      case OPERATOR_EXPONENT:       return v1 * pow(10, v2);
      default:                      return 0;
    }
  }

  bool isEnd() const
  {
    return index_ >= expr_.size();
  }

  /// Returns the character at the current expression index or
  /// 0 if the end of the expression is reached.
  ///
  char getCharacter() const
  {
    if (!isEnd())
      return expr_[index_];
    return 0;
  }

  /// Parse str at the current expression index.
  /// @throw error if parsing fails.
  ///
  void expect(const std::string& str)
  {
    if (expr_.compare(index_, str.size(), str) != 0)
      unexpected();
    index_ += str.size();
  }

  void unexpected() const
  {
    std::ostringstream msg;
    msg << "Syntax error: unexpected token \""
        << expr_.substr(index_, expr_.size() - index_)
        << "\" at index "
        << index_;
    throw calculator::error(expr_, msg.str());
  }

  /// Eat all white space characters at the
  /// current expression index.
  ///
  void eatSpaces()
  {
    while (std::isspace(getCharacter()) != 0)
      index_++;
  }

  /// Parse a binary operator at the current expression index.
  /// @return Operator with precedence and associativity.
  ///
  Operator parseOp()
  {
    eatSpaces();
    switch (getCharacter())
    {
      case '|': index_++;     return Operator(OPERATOR_BITWISE_OR,      4, 'L');
      case '&': index_++;     return Operator(OPERATOR_BITWISE_AND,     6, 'L');
      case '<': expect("<<"); return Operator(OPERATOR_BITWISE_SHL,     9, 'L');
      case '>': expect(">>"); return Operator(OPERATOR_BITWISE_SHR,     9, 'L');
      case '+': index_++;     return Operator(OPERATOR_ADDITION,       10, 'L');
      case '-': index_++;     return Operator(OPERATOR_SUBTRACTION,    10, 'L');
      case '/': index_++;     return Operator(OPERATOR_DIVISION,       20, 'L');
      case '%': index_++;     return Operator(OPERATOR_MODULO,         20, 'L');
      case '*': index_++; if (getCharacter() != '*')
                              return Operator(OPERATOR_MULTIPLICATION, 20, 'L');
                index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case '^': index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case 'e': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      case 'E': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      default :               return Operator(OPERATOR_NULL,            0, 'L');
    }
  }

  static T toInteger(char c)
  {
    if (c >= '0' && c <= '9') return c -'0';
    if (c >= 'a' && c <= 'f') return c -'a' + 0xa;
    if (c >= 'A' && c <= 'F') return c -'A' + 0xa;
    T noDigit = 0xf + 1;
    return noDigit;
  }

  T getInteger() const
  {
    return toInteger(getCharacter());
  }

  T parseDecimal()
  {
    T value = 0;
    for (T d; (d = getInteger()) <= 9; index_++)
      value = value * 10 + d;
    return value;
  }

  T parseHex()
  {
    index_ = index_ + 2;
    T value = 0;
    for (T h; (h = getInteger()) <= 0xf; index_++)
      value = value * 0x10 + h;
    return value;
  }

  bool isHex() const
  {
    if (index_ + 2 < expr_.size())
    {
      char x = expr_[index_ + 1];
      char h = expr_[index_ + 2];
      return (std::tolower(x) == 'x' && toInteger(h) <= 0xf);
    }
    return false;
  }

  /// Parse an integer value at the current expression index.
  /// The unary `+', `-' and `~' operators and opening
  /// parentheses `(' cause recursion.
  ///
  T parseValue()
  {
    T val = 0;
    eatSpaces();
    switch (getCharacter())
    {
      case '0': if (isHex())
                  val = parseHex();
                else
                  val = parseDecimal();
                break;
      case '1': case '2': case '3': case '4': case '5':
      case '6': case '7': case '8': case '9':
                val = parseDecimal();
                break;
      case '(': index_++;
                val = parseExpr();
                eatSpaces();
                if (getCharacter() != ')')
                {
                  if (!isEnd())
                    unexpected();
                  throw calculator::error(expr_, "Syntax error: `)' expected at end of expression");
                }
                index_++; break;
      case '~': index_++; val = ~parseValue(); break;
      case '+': index_++; val =  parseValue(); break;
      case '-': index_++; val =  parseValue() * static_cast<T>(-1);
                break;
      default : if (!isEnd())
                  unexpected();
                throw calculator::error(expr_, "Syntax error: value expected at end of expression");
    }
    return val;
  }

  /// Parse all operations of the current parenthesis
  /// level and the levels above, when done
  /// return the result (value).
  ///
  T parseExpr()
  {
    stack_.push(OperatorValue(Operator(OPERATOR_NULL, 0, 'L'), 0));
    // first parse value on the left
    T value = parseValue();

    while (!stack_.empty())
    {
      // parse an operator (+, -, *, ...)
      Operator op(parseOp());
      while (op.precedence  < stack_.top().getPrecedence() || (
             op.precedence == stack_.top().getPrecedence() &&
             op.associativity == 'L'))
      {
        // end reached
        if (stack_.top().isNull())
        {
          stack_.pop();
          return value;
        }
        // do the calculation ("reduce"), producing a new value
        value = calculate(stack_.top().value, value, stack_.top().op);
        stack_.pop();
      }

      // store on stack_ and continue parsing ("shift")
      stack_.push(OperatorValue(op, value));
      // parse value on the right
      value = parseValue();
    }
    return 0;
  }
};

template <typename T>
inline T eval(const std::string& expression)
{
  ExpressionParser<T> parser;
  return parser.eval(expression);
}

template <typename T>
inline T eval(char c)
{
  ExpressionParser<T> parser;
  return parser.eval(c);
}

inline int eval(const std::string& expression)
{
  return eval<int>(expression);
}

inline int eval(char c)
{
  return eval<int>(c);
}

} // namespace calculator

#endif
<|endoftext|>


<commit_before>#include "../ris_lib/ris_resources_from_file.h"
#include "../ris_lib/ris_bundle_compression.h"
#include "../ris_lib/ris_writing_files.h"
#include "../ris_lib/template.h"
#include "../ris_lib/ris_resource_loader.h"
#include "../ris_lib/ris_default_or_from_file.h"
#include "../ris_lib/ris_resource_snapshot.h"
#include "../ris_lib/ris_late_context.h"
#include "../ris_lib/ris_late.h"
#include "../ris_lib/ris_resource_loader.h"

#include <iostream>
#include <string>
#include <stdexcept>
#include <unordered_map>

#include <boost/filesystem.hpp>

void print_usage() {
    std::cout
        << "---      ris v0.2.0            ---" << std::endl
        << "a simple resource compiler for c++" << std::endl
        << "https://github.com/d-led/ris" << std::endl
        << "USAGE:" << std::endl
        << "       ris <path_to>/<resources>.[json/yml/yaml] [<template_overrides>.[json/yml/yaml]]" << std::endl
        ;
}

template <typename TResource>
std::string member_name(TResource const& res) {
    if (!res.member_name.empty())
        return res.member_name;
    return res.name;
}

void process(std::string const& path, std::string const& source_template) {
    auto full_path = absolute(boost::filesystem::path(path));
    full_path.make_preferred();
    std::cout << "reading " << full_path.generic_string() << std::endl;

    auto user_resources = ris::load_resources_from_file(path);
    auto lookup = user_resources.to_lookup();
    std::cout << "read " << user_resources.resources().resources.size() << " resources" << std::endl;

    auto compression = ris::bundle_compression();
    auto ris_res = ris::default_or_from_file<ris::Resource>(source_template);
    auto template_snapshot = ris::resource_snapshot(ris_res);
    bool any_with_compression = std::any_of(std::begin(user_resources.resources().resources),
        std::end(user_resources.resources().resources),
        [&compression](ris::resource const& res) {
        return compression.is_legal(res.compression);
    });

    ris::write_to_temp_first_then_move header([&ris_res, &template_snapshot, &user_resources,&lookup](std::ostream& s) {
        template_snapshot["namespace_name"] = user_resources.namespace_();
        template_snapshot["class_name"] = user_resources.class_();
        auto lazy = ris::get_context(template_snapshot);
        lazy
            .lazy("header", [&lazy, &ris_res](std::ostream& s) {
                ris::render(ris_res.Get("header"), lazy, s);
            })
            .lazy("header_declarations", [&ris_res,&user_resources,&lazy](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy.lazy("resource_member_name", [&resource](std::ostream& s){
                        s << member_name(resource);
                    });
                    ris::render(ris_res.Get("header_single_declaration"), lazy, s);
                }
            })
            .lazy("header_resource_names", [&ris_res, &user_resources, &lazy](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy.lazy("resource_name", [&resource](std::ostream& s){
                        s << resource.name;
                    });
                    ris::render(ris_res.Get("header_single_resource_name"), lazy, s);
                }
            })
        ;
        ris::render("{{header}}", lazy, s);
    }, user_resources.header());
    header.start();

    ris::write_to_temp_first_then_move source([&](std::ostream& s) {
        template_snapshot["namespace_name"] = user_resources.namespace_();
        template_snapshot["class_name"] = user_resources.class_();
        auto lazy = ris::get_context(template_snapshot);
        lazy
            .lazy("source", [&lazy, &ris_res](std::ostream& s) {
                ris::render(ris_res.Get("source"), lazy, s);
            })
            .lazy("optional_compression_header", [&](std::ostream& s) {
                if (any_with_compression)
                    s << "#include <bundle.hpp>\n";
            })
            .lazy("source_default_include", [&](std::ostream& s) {
                s << "#include \"" << boost::filesystem::path(user_resources.header()).filename().generic_string() << "\"";
            })
            .lazy("source_definitions", [&](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy
                    .lazy("resource_member_name", [&](std::ostream& s){
                        s << member_name(resource);
                    })
                    .lazy("source_literal_bytes", [&](std::ostream& s){
                        static const unsigned MAX_IN_ONE_LINE = 100;
                        std::string data = ris::resource_loader(resource, user_resources.base_path()).get();

                        auto raw_size = data.size();

                        if (compression.is_legal(resource.compression)) {
                            data = compression.pack(resource.compression, data);
                            auto new_size = data.size();
                            std::cout << "[" << resource.compression << "] "
                                << resource.name << ": "
                                << new_size << "/" << raw_size
                                << " (" << ((double)new_size / raw_size*100.0) << "%)" << std::endl;
                        }

                        int count = 0;

                        for (char c : data) {
                            if (count > MAX_IN_ONE_LINE - 1) {
                                count = 0;
                            }

                            if (count == 0) {
                                s << "\n        ";
                            }

                            s << static_cast<short>(c) << ", ";

                            count++;
                        }
                    })
                    .lazy("source_return_literal", [&](std::ostream& s) {
                        if (compression.is_legal(resource.compression)) {
                            ris::render(ris_res.Get("source_return_compressed_literal"), lazy, s);
                        }
                        else {
                            ris::render(ris_res.Get("source_return_plain_literal"), lazy, s);
                        }
                    })
                    ;
                    ris::render(ris_res.Get("source_single_definition"), lazy, s);
                }
            })
            .lazy("source_getters", [&](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy
                    .lazy("resource_name", [&resource](std::ostream& s){
                        s << resource.name;
                    })
                    .lazy("resource_member_name", [&](std::ostream& s){
                        s << member_name(resource);
                    })
                    ;
                    ris::render(ris_res.Get("source_single_getter"), lazy, s);
                }
            })
        ;
        ris::render("{{source}}", lazy, s);
    }, user_resources.source());
    source.start();
}

int main(int argc, char ** argv) {
    std::string source_template;

    switch (argc) {
        case 3:
            source_template = argv[2];
            // fall through
        case 2:
            try {
                process(argv[1], source_template);
            }
            catch (std::exception& e) {
                std::cerr << "Error: " << e.what() << std::endl;
                return 1;
            }
            break;
        default:
            print_usage();
    }
}
<commit_msg>bump version [ci skip]<commit_after>#include "../ris_lib/ris_resources_from_file.h"
#include "../ris_lib/ris_bundle_compression.h"
#include "../ris_lib/ris_writing_files.h"
#include "../ris_lib/template.h"
#include "../ris_lib/ris_resource_loader.h"
#include "../ris_lib/ris_default_or_from_file.h"
#include "../ris_lib/ris_resource_snapshot.h"
#include "../ris_lib/ris_late_context.h"
#include "../ris_lib/ris_late.h"
#include "../ris_lib/ris_resource_loader.h"

#include <iostream>
#include <string>
#include <stdexcept>
#include <unordered_map>

#include <boost/filesystem.hpp>

void print_usage() {
    std::cout
        << "---      ris v0.3.0            ---" << std::endl
        << "a simple resource compiler for c++" << std::endl
        << "https://github.com/d-led/ris" << std::endl
        << "USAGE:" << std::endl
        << "       ris <path_to>/<resources>.[json/yml/yaml] [<template_overrides>.[json/yml/yaml]]" << std::endl
        ;
}

template <typename TResource>
std::string member_name(TResource const& res) {
    if (!res.member_name.empty())
        return res.member_name;
    return res.name;
}

void process(std::string const& path, std::string const& source_template) {
    auto full_path = absolute(boost::filesystem::path(path));
    full_path.make_preferred();
    std::cout << "reading " << full_path.generic_string() << std::endl;

    auto user_resources = ris::load_resources_from_file(path);
    auto lookup = user_resources.to_lookup();
    std::cout << "read " << user_resources.resources().resources.size() << " resources" << std::endl;

    auto compression = ris::bundle_compression();
    auto ris_res = ris::default_or_from_file<ris::Resource>(source_template);
    auto template_snapshot = ris::resource_snapshot(ris_res);
    bool any_with_compression = std::any_of(std::begin(user_resources.resources().resources),
        std::end(user_resources.resources().resources),
        [&compression](ris::resource const& res) {
        return compression.is_legal(res.compression);
    });

    ris::write_to_temp_first_then_move header([&ris_res, &template_snapshot, &user_resources,&lookup](std::ostream& s) {
        template_snapshot["namespace_name"] = user_resources.namespace_();
        template_snapshot["class_name"] = user_resources.class_();
        auto lazy = ris::get_context(template_snapshot);
        lazy
            .lazy("header", [&lazy, &ris_res](std::ostream& s) {
                ris::render(ris_res.Get("header"), lazy, s);
            })
            .lazy("header_declarations", [&ris_res,&user_resources,&lazy](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy.lazy("resource_member_name", [&resource](std::ostream& s){
                        s << member_name(resource);
                    });
                    ris::render(ris_res.Get("header_single_declaration"), lazy, s);
                }
            })
            .lazy("header_resource_names", [&ris_res, &user_resources, &lazy](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy.lazy("resource_name", [&resource](std::ostream& s){
                        s << resource.name;
                    });
                    ris::render(ris_res.Get("header_single_resource_name"), lazy, s);
                }
            })
        ;
        ris::render("{{header}}", lazy, s);
    }, user_resources.header());
    header.start();

    ris::write_to_temp_first_then_move source([&](std::ostream& s) {
        template_snapshot["namespace_name"] = user_resources.namespace_();
        template_snapshot["class_name"] = user_resources.class_();
        auto lazy = ris::get_context(template_snapshot);
        lazy
            .lazy("source", [&lazy, &ris_res](std::ostream& s) {
                ris::render(ris_res.Get("source"), lazy, s);
            })
            .lazy("optional_compression_header", [&](std::ostream& s) {
                if (any_with_compression)
                    s << "#include <bundle.hpp>\n";
            })
            .lazy("source_default_include", [&](std::ostream& s) {
                s << "#include \"" << boost::filesystem::path(user_resources.header()).filename().generic_string() << "\"";
            })
            .lazy("source_definitions", [&](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy
                    .lazy("resource_member_name", [&](std::ostream& s){
                        s << member_name(resource);
                    })
                    .lazy("source_literal_bytes", [&](std::ostream& s){
                        static const unsigned MAX_IN_ONE_LINE = 100;
                        std::string data = ris::resource_loader(resource, user_resources.base_path()).get();

                        auto raw_size = data.size();

                        if (compression.is_legal(resource.compression)) {
                            data = compression.pack(resource.compression, data);
                            auto new_size = data.size();
                            std::cout << "[" << resource.compression << "] "
                                << resource.name << ": "
                                << new_size << "/" << raw_size
                                << " (" << ((double)new_size / raw_size*100.0) << "%)" << std::endl;
                        }

                        int count = 0;

                        for (char c : data) {
                            if (count > MAX_IN_ONE_LINE - 1) {
                                count = 0;
                            }

                            if (count == 0) {
                                s << "\n        ";
                            }

                            s << static_cast<short>(c) << ", ";

                            count++;
                        }
                    })
                    .lazy("source_return_literal", [&](std::ostream& s) {
                        if (compression.is_legal(resource.compression)) {
                            ris::render(ris_res.Get("source_return_compressed_literal"), lazy, s);
                        }
                        else {
                            ris::render(ris_res.Get("source_return_plain_literal"), lazy, s);
                        }
                    })
                    ;
                    ris::render(ris_res.Get("source_single_definition"), lazy, s);
                }
            })
            .lazy("source_getters", [&](std::ostream& s) {
                for (auto& resource : user_resources.resources().resources) {
                    lazy
                    .lazy("resource_name", [&resource](std::ostream& s){
                        s << resource.name;
                    })
                    .lazy("resource_member_name", [&](std::ostream& s){
                        s << member_name(resource);
                    })
                    ;
                    ris::render(ris_res.Get("source_single_getter"), lazy, s);
                }
            })
        ;
        ris::render("{{source}}", lazy, s);
    }, user_resources.source());
    source.start();
}

int main(int argc, char ** argv) {
    std::string source_template;

    switch (argc) {
        case 3:
            source_template = argv[2];
            // fall through
        case 2:
            try {
                process(argv[1], source_template);
            }
            catch (std::exception& e) {
                std::cerr << "Error: " << e.what() << std::endl;
                return 1;
            }
            break;
        default:
            print_usage();
    }
}
<|endoftext|>