// This file is part of fityk program. Copyright (C) Marcin Wojdyr
// Licence: GNU General Public License version 2
// $Id: datatrans2.cpp 326 2007-08-01 05:54:03Z wojdyr $

/// big grammars in Spirit take a lot of time and memory to compile
/// so they must be splitted into separate compilation units
/// that's the only reason why this file is not a part of datatrans.cpp

#include "datatrans2.h"
#include "datatrans3.h"
#include "sum.h"
using namespace datatrans;

namespace datatrans {

void ParameterizedFunction::prepare_parameters(vector<Point> const& points)
{
    for (map<int, vector<int> >::const_iterator i = pcodes.begin(); 
            i != pcodes.end(); ++i) {
        vector<int> code_ = i->second;
        fp v = get_transform_expr_value(code_, points);
        assert(is_index(i->first, params));
        params[i->first] = v;
    }
    do_prepare();
}

//----------------------  Parameterized Functions -------------------------
class InterpolateFunction : public ParameterizedFunction
{
public:
    InterpolateFunction(vector<fp> const& params_, 
                        map<int, vector<int> > const& pcodes_) 
        : ParameterizedFunction(params_, pcodes_) {}

    void do_prepare() {
        for (int i = 0; i < size(params) - 1; i += 2)
            bb.push_back(PointQ(params[i], params[i+1]));
    }

    fp calculate(fp x) { return get_linear_interpolation(bb, x); }

private:
    std::vector<PointQ> bb;
};


class SplineFunction : public ParameterizedFunction
{
public:
    SplineFunction(vector<fp> const& params_, 
                   map<int, vector<int> > const& pcodes_) 
        : ParameterizedFunction(params_, pcodes_) {}

    void do_prepare() {
        for (int i = 0; i < size(params) - 1; i += 2)
            bb.push_back(PointQ(params[i], params[i+1]));
        prepare_spline_interpolation(bb);
    }

    fp calculate(fp x) { return get_spline_interpolation(bb, x); }

private:
    std::vector<PointQ> bb;
};

//----------------------   functors   -------------------------

void push_double::operator()(const double& n) const
{
    code.push_back(OP_NUMBER);
    code.push_back(size(numbers));
    numbers.push_back(n);
}

void push_op::push() const 
{ 
    code.push_back(op); 
    if (op2)
        code.push_back(op2); 
}

void parameterized_op::push() const
{ 
    //DT_DEBUG("PARAMETERIZED " + S(op))  
    typedef vector<int>::iterator viit;
    viit first = find(code.begin(), code.end(), OP_PLIST_BEGIN);
    viit last = find(code.begin(), code.end(), OP_PLIST_END) + 1;
    if (find(first+1, last, OP_PLIST_BEGIN) != last)
        throw ExecuteError("Parametrized functions can not be nested.");
    vector<fp> params;
    map<int, vector<int> > pcodes; //codes for parameters of eg. spline
    viit start = first+1;
    while (start != last) {
        viit finish = find(start, last, OP_PLIST_SEP);
        if (finish == last) 
            finish = last-1; // at OP_PLIST_END
        if (finish == start+3 && *start == OP_NUMBER) { //the most common case
            params.push_back(numbers[*(start+1)]);
        }
        else {
            pcodes[params.size()] = vector<int>(start, finish);
            params.push_back(0.); //placeholder
        }
        start = finish+1;
    }
    code.erase(first, last);
    code.push_back(OP_PARAMETERIZED); 
    code.push_back(size(parameterized));
    ParameterizedFunction *func = 0;
    switch (op) {
        case PF_INTERPOLATE:
            //TODO shared_ptr
            func = new InterpolateFunction(params, pcodes);
            break;
        case PF_SPLINE:
            func = new SplineFunction(params, pcodes);
            break;
        default:
            assert(0);
    }
    parameterized.push_back(func);
}

#ifndef STANDALONE_DATATRANS
void push_func::operator()(char const* a, char const* b) const
{ 
    string t(a, b);
    if (t[0] == '%') {
        string fstr = strip_string(string(t, 1, t.find_first_of("(,")-1));
        int n = AL->find_function_nr(fstr); 
        if (n == -1)
            throw ExecuteError("undefined function: %" + fstr);
        code.push_back(OP_FUNC); 
        code.push_back(n); 
    }
    else {
        int n = -1;
        if (t[0] == '@') {
            int dot = t.find('.');
            n = strtol(string(t,1,dot).c_str(), 0, 10);
            t = strip_string(string(t, dot+1));
        }
        if (t[0] == 'F')
            code.push_back(OP_SUM_F); 
        else if (t[0] == 'Z')
            code.push_back(OP_SUM_Z); 
        else
            assert(0);
        code.push_back(n); 
    }
}

void push_func_param::operator()(char const* a, char const* b) const 
{ 
    string t(a, b);
    int dot = t.rfind(".");
    string fstr = strip_string(string(t, 0, dot));
    string pstr = strip_string(string(t, dot+1));
    push_double::operator()(AL->find_function_any(fstr)->get_param_value(pstr));
}
#endif //not STANDALONE_DATATRANS

} //namespace

template <typename ScannerT>
DataExpressionGrammar::definition<ScannerT>::definition(
                                          DataExpressionGrammar const& /*self*/)
{
    rprec5 
        =   DataE2G 
            >> *(
                  ('^' >> DataE2G) [push_op(OP_POW)]
                )
        ;

    rprec4
        =   ('-' >> rprec5) [push_op(OP_NEG)]
        |   (!ch_p('+') >> rprec5)
        ;
        
    rprec3 
        =   rprec4
            >> *(   ('*' >> rprec4)[push_op(OP_MUL)]
                |   ('/' >> rprec4)[push_op(OP_DIV)]
                |   ('%' >> rprec4)[push_op(OP_MOD)]
                )
        ;

    rprec2 
        =   rprec3
            >> *(   ('+' >> rprec3) [push_op(OP_ADD)]
                |   ('-' >> rprec3) [push_op(OP_SUB)]
                )
        ;

    rbool
    // a hack for handling 10 < x < 20 
    // how it works:
    //  3 < 5.2
    //    op:    OP_NUMBER(3)    OP_NUMBER(5.2)   OP_LT
    //    stack:    ... 3         ... 3 5.2      ... 1 5.2
    //    stack end:    ^                 ^          ^
    //  3 < 5.2 < 4 (continued previous example)
    //    op:     OP_AND     OP_NCMP_HACK  OP_NUMBER(4) OP_LT  OP_AFTER_AND
    //    stack: ... 1 5.2   ... 5.2       ... 5.2 4   ... 0 4     (flag)
    //    s. end:  ^               ^               ^       ^  
        = rprec2
           >> !(( ("<=" >> rprec2) [push_op(OP_LE)]
                | (">=" >> rprec2) [push_op(OP_GE)]
                | (str_p("==") >> rprec2)  [push_op(OP_EQ)]
                | ((str_p("!=")|"<>") >> rprec2)  [push_op(OP_NEQ)]
                | ('<' >> rprec2)  [push_op(OP_LT)]
                | ('>' >> rprec2)  [push_op(OP_GT)]
                )
                >> *( (str_p("<=")[push_op(OP_AND, OP_NCMP_HACK)] 
                        >> rprec2) [push_op(OP_LE, OP_AFTER_AND)]
                    | (str_p(">=")[push_op(OP_AND, OP_NCMP_HACK)] 
                        >> rprec2) [push_op(OP_GE, OP_AFTER_AND)]
                    | (str_p("==")[push_op(OP_AND, OP_NCMP_HACK)] 
                        >> rprec2) [push_op(OP_EQ, OP_AFTER_AND)]
                    | ((str_p("!=")|"<>")[push_op(OP_AND, OP_NCMP_HACK)] 
                        >> rprec2)  [push_op(OP_NEQ, OP_AFTER_AND)]
                    | (ch_p('<')[push_op(OP_AND, OP_NCMP_HACK)] 
                        >> rprec2) [push_op(OP_LT, OP_AFTER_AND)]
                    | (ch_p('>')[push_op(OP_AND, OP_NCMP_HACK)] 
                        >> rprec2) [push_op(OP_GT, OP_AFTER_AND)]
                    )
               )
        ;

    rbool_not
        =  (as_lower_d["not"] >> rbool) [push_op(OP_NOT)]
        |  rbool
        ;

    rbool_and
        =  rbool_not
           >> *(
                as_lower_d["and"] [push_op(OP_AND)] 
                >> rbool_not
               ) [push_op(OP_AFTER_AND)]
        ;

    rbool_or  
        =  rbool_and 
           >> *(
                as_lower_d["or"] [push_op(OP_OR)] 
                >> rbool_and
               ) [push_op(OP_AFTER_OR)]
        ;  


    rprec1
        =  rbool_or 
           >> !(ch_p('?') [push_op(OP_TERNARY)] 
                >> rbool_or 
                >> ch_p(':') [push_op(OP_TERNARY_MID)] 
                >> rbool_or
               ) [push_op(OP_AFTER_TERNARY)]
        ;
}

// explicit template instantiations 
template DataExpressionGrammar::definition<scanner<char const*, scanner_policies<skipper_iteration_policy<iteration_policy>, match_policy, no_actions_action_policy<action_policy> > > >::definition(DataExpressionGrammar const&);

template DataExpressionGrammar::definition<scanner<char const*, scanner_policies<skipper_iteration_policy<iteration_policy>, match_policy, no_actions_action_policy<no_actions_action_policy<action_policy> > > > >::definition(DataExpressionGrammar const&);

template DataExpressionGrammar::definition<scanner<char const*, scanner_policies<skipper_iteration_policy<iteration_policy>, match_policy, action_policy> > >::definition(DataExpressionGrammar const&);

DataExpressionGrammar DataExpressionG;




syntax highlighted by Code2HTML, v. 0.9.1