/*************************************************************************************
 *  Copyright (C) 2010 by Aleix Pol <aleixpol@kde.org>                               *
 *                                                                                   *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                   *
 *  This program 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                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                   *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "expressiontype.h"
#include <QStringList>
#include <QDebug>

using namespace Analitza;

QDebug operator<<(QDebug dbg, const ExpressionType &c);
namespace Analitza { void printAssumptions(const QString& prefix, const ExpressionType& current); }
QDebug operator<<(QDebug dbg, const QMap<int, Analitza::ExpressionType> &c);

bool ExpressionType::assumptionsMerge(QMap<QString, ExpressionType>& data, const QMap<QString, ExpressionType>& newmap)
{
    if(data.isEmpty() && newmap.isEmpty())
        return true;
    
//     static int i=0;
//     qDebug() << "merging!" << i++ << data << newmap;
    QMap<int, ExpressionType> stars;
    QMap<QString, ExpressionType>::const_iterator it=newmap.constBegin(), itEnd=newmap.constEnd();
    for(; it!=itEnd; ++it) {
        QMap<QString, ExpressionType>::iterator current = data.find(it.key());
        
        if(current!=data.end()) {
            if(!current->isError()) {
                ExpressionType t=ExpressionType::minimumType(*it, *current);
//                 qDebug() << "miiiiiiin" << it.key() << *it << *current << t << "|||" << it->assumptions() << current->assumptions() << t.assumptions();
                if(t.isError())
                    return false;
                
                stars=computeStars(stars, *it, *current);
                *current = t.starsToType(stars);
            }
        } else
            data.insert(it.key(), it.value());
    }
//     qDebug() << "merging_" << data;
    
    for(QMap<QString, ExpressionType>::iterator it=data.begin(); it!=data.end(); ++it) {
        *it=it->starsToType(stars);
    }
    
//     i--;
    return true;
}

void ExpressionType::assumptionsUnion(QMap<QString, ExpressionType>& data, const QMap<QString, ExpressionType>& newmap)
{
//     qDebug() << "-----------" << data << newmap;
    QMap<QString, ExpressionType>::const_iterator it=newmap.constBegin(), itEnd=newmap.constEnd();
    for(; it!=itEnd; ++it) {
        QMap<QString, ExpressionType>::iterator current = data.find(it.key());
        
        if(current!=data.end()) {
            if(current->canReduceTo(*it)) {
                data.insert(it.key(), minimumType(*current, *it));
            } else {
                bool correct=false;
                ExpressionType t(Many);
                ExpressionType t1(*it);            correct= t1.addAssumption(it.key(), *it);
                ExpressionType t2(*current);    correct|=t2.addAssumption(it.key(), *current);
                
                Q_ASSERT(correct);
                t.addAlternative(t1);
                t.addAlternative(t2);
                
                data.insert(it.key(), t);
            }
        } else
            data.insert(it.key(), it.value());
    }
    
//     qDebug() << "leeeeee" << data;
}

ExpressionType::ExpressionType(const ExpressionType& t)
    : m_type(t.m_type), m_contained(t.m_contained)
    , m_assumptions(t.m_assumptions), m_size(t.m_size), m_objectName(t.m_objectName)
{}

ExpressionType::ExpressionType(ExpressionType::Type t, int any)
    : m_type(t), m_any(any)
{Q_ASSERT(m_type==Any || m_type==Error || m_type==Value || m_type==Bool || m_type==Char || m_type==Many || m_type==Lambda); }

ExpressionType::ExpressionType(ExpressionType::Type t, const ExpressionType& contained, int s)
    : m_type(t), m_contained(QList<ExpressionType>() << contained), m_size(s)
{
    Q_ASSERT(m_type==List || m_type==Vector || m_type==Matrix || m_type==Any); // Any for variadic functions with same contained type
    Q_ASSERT(m_type!=Vector || m_size!=0);
    Q_ASSERT(m_type!=Matrix || contained.type()==Vector);
    m_assumptions=contained.assumptions();
}

ExpressionType::ExpressionType(const QString& objectName)
    : m_type(Object), m_size(-1), m_objectName(objectName)
{}

ExpressionType::ExpressionType(ExpressionType::Type t, const QList< ExpressionType >& alternatives)
    : m_type(Many), m_size(-1)
{
    Q_ASSERT(t==Many);
    foreach(const ExpressionType& t, alternatives)
        addAlternative(t);
}

QStringList typesToString(const QList<ExpressionType>& types)
{
    QStringList ret;
    foreach(const ExpressionType& t, types) {
        QString str=t.toString();
        if(t.type()==ExpressionType::Lambda)
            ret += '('+str+')';
        else
            ret += str;
    }
    return ret;
}

QString ExpressionType::toString() const
{
    QString ret;
    switch(m_type) {
        case ExpressionType::Value:ret=QStringLiteral("num");  break;
        case ExpressionType::Char: ret=QStringLiteral("char"); break;
        case ExpressionType::Bool: ret=QStringLiteral("bool"); break;
        case ExpressionType::List:
            ret='['+typesToString(m_contained).join(QStringLiteral("$"))+']';
            break;
        case ExpressionType::Vector:
            ret='<'+typesToString(m_contained).join(QStringLiteral("$"))+','+QString::number(m_size)+'>';
            break;
        case ExpressionType::Matrix:
            ret=QStringLiteral("{%1,%2x%3}").arg(contained().contained().toString()).arg(m_size).arg(contained().size());
            break;
        case ExpressionType::Error:
            ret=QStringLiteral("err");
            break;
        case ExpressionType::Lambda:
            ret=typesToString(m_contained).join(QStringLiteral(" -> "));
            break;
        case ExpressionType::Any: {
//             ret=QString(m_any, '*');
            QString id;
            for(int i=m_any; i>0; i/='z'-'a') {
                id.prepend(QChar('a'-1+(i%('z'-'a'))));
            }
            ret=id;
//             ret+=QString::number(m_any);
        }    break;
        case ExpressionType::Many:
            ret=/*"{"+*/typesToString(m_contained).join(QStringLiteral(" | "))/*+"}"*/;
            break;
        case ExpressionType::Object:
            ret="obj:"+m_objectName;
            break;
    }
    
    return ret;
}

bool ExpressionType::isError() const
{
    if(m_type==Error)
        return true;
    else if(m_type==Many && m_contained.isEmpty())
        return true;
    foreach (const ExpressionType& t, m_contained) {
        if(t.isError())
            return true;
    }
    return false;
}

ExpressionType ExpressionType::operator=(const ExpressionType& et)
{
    if(&et!=this) {
        m_type=et.m_type;
        m_contained=et.m_contained;
        m_size=et.m_size;
        m_assumptions=et.m_assumptions;
        m_objectName=et.m_objectName;
    }
    
    return *this;
}

bool ExpressionType::operator==(const ExpressionType& t) const
{
//     qDebug() <<"seeee" << m_type << t.m_type << t.m_contained << m_contained << t << *this;
    bool ret=t.m_type==m_type;
    ret=ret            && (m_type==ExpressionType::Any ? m_any==t.m_any : (m_size<1 || t.m_size<1 || m_size==t.m_size));
    ret=ret            && t.m_contained==m_contained;
    
    if(!ret && t.type()==ExpressionType::Many && t.alternatives().size()==1)
        ret=*this==t.alternatives().at(0);
    
    if(!ret &&   type()==ExpressionType::Many &&   alternatives().size()==1)
        ret=t==alternatives().at(0);
    
    ret &= m_objectName==t.m_objectName;
//     qDebug() << "++++++++" << t << *this << ret;
    return ret;
}

bool ExpressionType::addAssumption(const QString& bvar, const Analitza::ExpressionType& t)
{
    ExpressionType toadd=t;
    addAssumptions(t.assumptions());
    toadd.clearAssumptions();
    
    QMap< QString, ExpressionType >::iterator it=m_assumptions.find(bvar);
    if(it==m_assumptions.end())
        m_assumptions.insert(bvar, toadd);
    else {
        toadd=minimumType(toadd,*it);
        if(toadd.isError())
            return false;
        *it=toadd;
    }
    
    return true;
}

void ExpressionType::removeAssumptions(const QStringList& bvarStrings)
{
    foreach(const QString& bvar, bvarStrings)
        m_assumptions.remove(bvar);
    
    QList<ExpressionType>::iterator it=m_contained.begin(), itEnd=m_contained.end();
    for(; it!=itEnd; ++it) {
        it->removeAssumptions(bvarStrings);
    }
}

void ExpressionType::addAssumptions(const QMap<QString, ExpressionType>& a)
{
//     qDebug() << "=====1" << m_assumptions << a;
    bool valid=assumptionsMerge(m_assumptions, a);
//     qDebug() << "=====2" << m_assumptions;
    Q_ASSERT(valid);
}

ExpressionType ExpressionType::starsToType(const QMap<int, ExpressionType>& info) const
{
#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
    for(QMap<int, ExpressionType>::const_iterator it=info.cbegin(); it!=info.cend(); ++it) {
        Q_ASSERT(it->type()!=ExpressionType::Any || it->anyValue()!=it.key());
    }
#endif
    
    ExpressionType ret;
//     static int deep=0;
//     qDebug() << qPrintable("hohohoho"+QString(++deep, '-')) << *this << info << &info;
    
//     qDebug() << ".........." << *this << info.keys();
    if(m_type==ExpressionType::Any && info.contains(m_any)) {
        ret=info.value(m_any);
        ret.m_assumptions=m_assumptions;
    } else if((m_type==ExpressionType::Vector || m_type==ExpressionType::Matrix) && m_size<0 && info.contains(m_size)) {
        ret=*this;
        QMap<int, ExpressionType> info2(info);
        ExpressionType sometype = info2.take(m_size);
        ret.m_size = sometype.m_size;
        ret.m_contained.first() = m_contained.first().starsToType(info2);
        ret.m_assumptions=m_assumptions;
    } else {
        ret=*this;
        
        bool hasMany=false;
        for(QList<ExpressionType>::iterator it=ret.m_contained.begin(), itEnd=ret.m_contained.end(); it!=itEnd; ++it) {
            *it=it->starsToType(info);
            hasMany|=(it->type()==ExpressionType::Many);
        }
        
        if(hasMany) {
            QList<ExpressionType> args=manyFromArgs(ret.m_contained);
            QList<ExpressionType> alts;
            foreach(const ExpressionType& t, args) {
                ExpressionType tt=ret;
                tt.m_contained=t.m_contained;
                
                bool b=assumptionsMerge(tt.m_assumptions, t.m_assumptions);
                if(b)
                    alts += tt;
            }
            ret=ExpressionType(ExpressionType::Many, alts);
        }
    }
    
    for(QMap<QString, ExpressionType>::iterator it=ret.m_assumptions.begin(), itEnd=ret.m_assumptions.end(); it!=itEnd; ++it) {
        *it=it->starsToType(info);
    }
    
//     qDebug() << qPrintable("fuuuuuuu"+QString(deep, '-')) << info << &info;
    
//     deep--;
//     qDebug() << "MMMMMMMM" << ret << ret.assumptions() << m_assumptions;
    
    return ret;
}

bool ExpressionType::canCompareTo(const ExpressionType& type) const
{
    bool ret=type==*this;
    if(!ret && type.m_type==m_type) {
        switch(m_type) {
            case ExpressionType::List:
                ret=contained().canCompareTo(type.contained());
                break;
            case ExpressionType::Matrix:
            case ExpressionType::Vector:
                ret=contained().canCompareTo(type.contained());
                if(ret && m_size>0 && type.m_size>0)
                    ret = m_size == type.m_size;
                break;
            case ExpressionType::Object:
                ret = m_objectName==type.m_objectName;
                break;
            case ExpressionType::Lambda:
                ret = m_contained.size()==type.m_contained.size();
                break;
            case ExpressionType::Error:
            case ExpressionType::Bool:
            case ExpressionType::Char:
            case ExpressionType::Value:
            case ExpressionType::Any:
            case ExpressionType::Many: //todo?
                ret=true;
                break;
        }
    } else if(!ret)
        ret = (m_type==Any || m_type==Error) || (type.m_type==Any || type.m_type==Error);
    return ret;
}

bool ExpressionType::canReduceTo(const ExpressionType& type) const
{
    bool ret=false;
    
    if(type==*this || m_type==Any || isError())
        ret=true;
    else if(m_type==Many) {
        foreach(const ExpressionType& t, m_contained) {
            if(t.canReduceTo(type)) {
                ret=true;
                break;
            }
        }
    } else if(type.m_type==Many) {
        foreach(const ExpressionType& t, type.m_contained) {
            if(canReduceTo(t)) {
                ret=true;
                break;
            }
        }
    } else if(m_type==Lambda) {
        ret = m_contained.size()==type.m_contained.size();
        QMap<int, ExpressionType> reductionsApplied;
        for(int i=0; ret && i<m_contained.size(); i++) {
            ExpressionType a=m_contained[i].starsToType(reductionsApplied), b=type.m_contained[i].starsToType(reductionsApplied);
            ret&=a.canReduceTo(b);
            
            if(ret && a.type()==Any && a!=b) {
                b.clearAssumptions();
                reductionsApplied.insert(a.anyValue(), b);
            }
        }
    } else if(m_type==Vector && type.m_type==Vector) {
        ret  = m_size<0 || type.m_size<0 || m_size==type.m_size;
        ret &= contained().canReduceTo(type.contained());
    } else if(m_type==List && type.m_type==List) {
        ret = contained().canReduceTo(type.contained());
    } else if(m_type==Matrix && type.m_type==Matrix) {
        ret = contained().canReduceTo(type.contained());
    }
    
//     qDebug() << "OOOOOOOOOOOOO" << *this << type << ret;
    
    return ret;
}

QMap<QString, ExpressionType> ExpressionType::assumptions() const
{
//     printAssumptions(*this);
    return m_assumptions;
}

QMap< QString, ExpressionType >& ExpressionType::assumptions()
{
    return m_assumptions;
}

int ExpressionType::increaseStars(int stars)
{
    int ret=stars;
    
    if(m_type==ExpressionType::Any) {
        m_any=stars+m_any;
        if(m_any>ret) 
            ret=m_any+1;
    }
    
    for(QList<ExpressionType>::iterator it=m_contained.begin(), itEnd=m_contained.end(); it!=itEnd; ++it) {
        ret=qMax(it->increaseStars(stars), ret);
    }
    
    for(QMap<QString,ExpressionType>::iterator it=m_assumptions.begin(), itEnd=m_assumptions.end(); it!=itEnd; ++it) {
        ret=qMax(it->increaseStars(stars), ret);
    }
    
    return ret;
}

void ExpressionType::starsSimplification(ExpressionType& t, QMap<int, int>& reductions, int &next)
{
    switch(t.m_type) {
        case ExpressionType::Any:
            if(reductions.contains(t.m_any))
                t.m_any=reductions.value(t.m_any);
            else {
                reductions.insert(t.m_any, next);
                t.m_any=next++;
            }
            
            break;
        case ExpressionType::Many:
        case ExpressionType::Vector:
        case ExpressionType::Matrix:
        case ExpressionType::List:
        case ExpressionType::Lambda:
            for(QList<ExpressionType>::iterator it=t.m_contained.begin(), itEnd=t.m_contained.end(); it!=itEnd; ++it) {
                starsSimplification(*it, reductions, next);
            }
            break;
        case ExpressionType::Object:
        case ExpressionType::Value:
        case ExpressionType::Char:
        case ExpressionType::Bool:
        case ExpressionType::Error:
            break;
    }
}

ExpressionType& ExpressionType::simplifyStars()
{
    int next=1;
    QMap<int, int> map;
    starsSimplification(*this, map, next);
    return *this;
}

ExpressionType ExpressionType::returnValue() const
{
    ExpressionType ret;
    
    if(m_type==Analitza::ExpressionType::Many) {
        ret=ExpressionType(ExpressionType::Many);
        foreach(const ExpressionType& t, m_contained)
            ret.addAlternative(t.returnValue());
    } else if(m_type==Analitza::ExpressionType::Lambda)
        ret=m_contained.last();
    
    return ret;
}

void ExpressionType::reduce(const Analitza::ExpressionType& type)
{
    if(m_type == Many) {
        QList<ExpressionType> newcontained;
        
        foreach(const ExpressionType& t, m_contained) {
            if(t.canReduceTo(type))
                newcontained.append(t);
        }
        
        if(newcontained.size()==1) {
            bool b=assumptionsMerge(newcontained.first().assumptions(), m_assumptions);
            Q_ASSERT(b);
            *this = newcontained.first();
        } else if(newcontained.isEmpty())
            ;
        else
            m_contained = newcontained;
        
    }
    
    if(m_type == Many) {
        QList<ExpressionType>::iterator it=m_contained.begin(), itEnd=m_contained.end();
        for(; it!=itEnd; ) {
            bool b = assumptionsMerge(it->assumptions(), type.assumptions());
            
//             qDebug() << "LLLLL" << b << *it << it->assumptions();
            if(b)
                ++it;
            else
                it=m_contained.erase(it);
        }
    } else {
        bool b = assumptionsMerge(m_assumptions, type.assumptions());
        if(!b)
            *this = ExpressionType(Error);
        else if(m_type==ExpressionType::Any && *this!=type) {
            QMap<int, ExpressionType> t;
            t.insert(m_any, type);
            
            QMap<QString, ExpressionType> resassumptions=assumptions();
            *this = type.starsToType(t);
            addAssumptions(resassumptions);
        }
    }
    
    if(m_type==Lambda && type.m_type==Lambda && canReduceTo(type)) {
        QList< ExpressionType >::iterator it1=m_contained.begin();
        QList< ExpressionType >::const_iterator it2=type.m_contained.constBegin();
        
        for(; it1!=m_contained.end(); ++it1, ++it2) {
            *it1=minimumType(*it1, *it2);
        }
    }
}

ExpressionType ExpressionType::minimumType(const ExpressionType& t1, const ExpressionType& t2)
{
    if(t1.type()==ExpressionType::Many && t2.type()==ExpressionType::Many) {
        QList<ExpressionType> alts;
//         qDebug() << "IIIIIIIIII" << t1 << t2;
        foreach(const ExpressionType& alt1, t1.alternatives()) {
            foreach(const ExpressionType& alt2, t2.alternatives()) {
                if(alt1.canReduceTo(alt2)) {
                    ExpressionType alt(alt1);
                    alt.reduce(alt2);
                    
                    if(!alt.isError()) {
                        alts+=alt;
                    }
                }
            }
        }
        
        if(alts.isEmpty())
            return ExpressionType(ExpressionType::Error);
        else {
//             qDebug() << "aaaaaaaaaaaaaaaaaaaaaaaaaaa" << alts << "\n\t --- " << alts.first().assumptions() << "\n\t --- " << alts.last().assumptions()
//                         << "\n\t --- " << t1.assumptions() << t2.assumptions();
            ExpressionType ret=ExpressionType(ExpressionType::Many, alts);
            ret.addAssumptions(t1.assumptions());
            ret.addAssumptions(t2.assumptions());
            return ret;
        }
    }
    else if(t2.isError())
        return t1;
    else if(t1.isError())
        return t2;
    else if(t1.type()==ExpressionType::Any && t2.type()==ExpressionType::Any)
        return t1.anyValue()>t2.anyValue() ? t1 : t2;
    else if(t1.type()==ExpressionType::Lambda && t2.type()==ExpressionType::Lambda && t1.parameters().size()==t2.parameters().size()) {
        ExpressionType t(ExpressionType::Lambda);
        for(int i=0; i<t1.parameters().size(); i++) {
            t.addParameter(minimumType(t1.parameters().at(i), t2.parameters().at(i)));
        }
        if(!t.isError()) {
            t.addAssumptions(t1.assumptions());
            t.addAssumptions(t2.assumptions());
        }
        
        return t;
    } else if(t1.canReduceTo(t2)) {
//         qDebug() << "zas" << t1 << t2 << "|||" << t1.assumptions() << t2.assumptions();
        ExpressionType t(t2);
        t.reduce(t1);
//         qDebug() << "zus" << t << t.assumptions();
        return t;
    } else if(t2.canReduceTo(t1)) {
        ExpressionType t(t1);
        t.reduce(t2);
        return t;
    }
    
    return ExpressionType();
}

ExpressionType& ExpressionType::addParameter(const ExpressionType& t)
{
    Q_ASSERT(m_type==Lambda);
    m_contained.append(t);
    return *this;
}

void ExpressionType::addAlternative(const ExpressionType& t)
{
    Q_ASSERT(m_type==Many);
    /*int i=m_contained.indexOf(t);
    if(i>=0) {
        m_contained[i].addAssumptions(t.assumptions());//TODO??
    } else */
    if(t.m_type==Many) {
        foreach(const ExpressionType& tt, t.alternatives()) 
            addAlternative(tt);
        
        addAssumptions(t.m_assumptions);
//         m_contained=t.m_contained;
    } else
        m_contained.append(t);
}

void ExpressionType::clearAssumptions()
{
    m_assumptions.clear();
    QList< ExpressionType >::iterator it=m_contained.begin(), itEnd=m_contained.end();
    for(; it!=itEnd; ++it) {
        it->clearAssumptions();
    }
}

QMap<int, ExpressionType> ExpressionType::processContained(const QMap<int, ExpressionType>& initial, const ExpressionType& candidate, const ExpressionType& type)
{
    auto ret=computeStars(initial, candidate.contained(), type.contained());

    if(candidate.size()<0) {
        ExpressionType t=type;
        t.m_contained.first()=t.m_contained.first().starsToType(ret);
        ret[candidate.size()] = t;
    }
    return ret;
}

QMap<int, ExpressionType> ExpressionType::computeStars(const QMap<int, ExpressionType>& initial, const ExpressionType& candidate, const ExpressionType& type)
{
    QMap<int, ExpressionType> ret(initial);
    
    switch(candidate.type()) {
        case ExpressionType::Any: {
            int stars=candidate.anyValue();
            
            if(ret.contains(stars)) {
                ExpressionType t=ret.take(stars);
                ret=computeStars(ret, t, type);
                
                t=t.starsToType(ret);
                if(t.type()!=ExpressionType::Any || t.m_any!=stars)
                    ret.insert(stars, t);
            } else if(type.type()!=ExpressionType::Any || type.m_any!=stars) {
                ret[stars]=type;
            }
        }    break;
        case ExpressionType::List:
            if(type.type()==ExpressionType::List) { // remove? assert?
                ret=computeStars(initial, candidate.contained(), type.contained());
            }
            break;
        case ExpressionType::Matrix:
            if(type.type()==ExpressionType::Matrix) { // remove? assert?
                ret = processContained(initial, candidate, type);
            }
            break;
        case ExpressionType::Vector:
            if(type.type()==ExpressionType::Vector) { // remove?
                ret = processContained(initial, candidate, type);
            }
            break;
        case ExpressionType::Lambda:
            if(type.type()!=ExpressionType::Lambda)
                break;
            Q_ASSERT(type.parameters().size()==candidate.parameters().size());
            for(int i=0; i<type.parameters().size(); i++) {
                ret=computeStars(ret, candidate.parameters().at(i), type.parameters().at(i));
            }
            break;
        case ExpressionType::Many:
        case ExpressionType::Object:
        case ExpressionType::Value:
        case ExpressionType::Bool:
        case ExpressionType::Char:
        case ExpressionType::Error:
//             Q_ASSERT(false && "bffff");
            break;
    }
    
    for(QMap<int, ExpressionType>::iterator it=ret.begin(), itEnd=ret.end(); it!=itEnd; ++it) {
        ExpressionType t=it->starsToType(ret);
        if(t.type()!=ExpressionType::Any || t.m_any!=it.key()) {
            *it=t;
        }
    }
    
//     qDebug() << ";;;;;;;" << candidate.type() << candidate << type << ret;
//     bool b = matchAssumptions(&ret, type.assumptions(), candidate.assumptions());
//     qDebug() << "feeeee" << ret;
    
    return ret;
}

bool ExpressionType::matchAssumptions(QMap< int, ExpressionType >* stars,
                                                const QMap< QString, ExpressionType >& assum1,
                                                const QMap< QString, ExpressionType >& assum2)
{
    bool ret=true;
    QMap<QString, ExpressionType>::const_iterator it=assum1.constBegin(), itEnd=assum1.constEnd();
    QMap<QString, ExpressionType>::const_iterator itFind, itFindEnd=assum2.constEnd();
    
    for(; ret && it!=itEnd; ++it) {
        itFind=assum2.find(it.key());
        
        if(itFind!=itFindEnd && *itFind!=*it) {
//             qDebug() << "sih" << it.key() << *it << *itFind;
            if(itFind->canReduceTo(*it))
                *stars=ExpressionType::computeStars(*stars, *itFind, *it);
            else if(it->canReduceTo(*itFind))
                *stars=ExpressionType::computeStars(*stars, *it, *itFind);
            else
                ret=false;
//             qDebug() << "seh" << *stars << ret;
        }
    }
    
    return ret;
}

QStringList ExpressionType::wrongAssumptions(const QMap< QString, ExpressionType >& assum1, const QMap< QString, ExpressionType >& assum2)
{
    QStringList ret;
    QMap<QString, ExpressionType>::const_iterator it=assum1.constBegin(), itEnd=assum1.constEnd();
    QMap<QString, ExpressionType>::const_iterator itFind, itFindEnd=assum2.constEnd();
    
    for(; it!=itEnd; ++it) {
        itFind=assum2.find(it.key());
        
        if(itFind!=itFindEnd && *itFind!=*it && !itFind->canReduceTo(*it) && !it->canReduceTo(*itFind)) {
            ret += it.key();
        }
    }
    
    return ret;
}

QList<ExpressionType> ExpressionType::manyFromArgs(const QList<ExpressionType>& args)
{
    QList<ExpressionType> funcs = QList<ExpressionType>() << ExpressionType(ExpressionType::Many);
    
    foreach(const ExpressionType& e, args) {
        QList<ExpressionType> alts = e.type()==ExpressionType::Many ? e.alternatives() : QList<ExpressionType>() << e;
        QList<ExpressionType> newfuncs;
        foreach(const ExpressionType& rr, alts) {
            foreach(const ExpressionType& f, funcs) {
                newfuncs += f;
                
                QMap<int, ExpressionType> stars;
//                 qDebug() << "&&&&&&&" << rr << newfuncs.last().assumptions() << rr.assumptions();
                bool m=ExpressionType::assumptionsMerge(newfuncs.last().assumptions(), rr.assumptions());
                m|=ExpressionType::matchAssumptions(&stars, newfuncs.last().assumptions(), rr.assumptions());
//                 qDebug() << "falala" << m;
                
                if(m)
                    newfuncs.last().addAlternative(rr.starsToType(stars));
                else
                    newfuncs.removeLast();
            }
        }
        
        funcs = newfuncs;
    }
    
    return funcs;
}

QList<ExpressionType> ExpressionType::lambdaFromArgs(const QList<ExpressionType>& args)
{
    QList<ExpressionType> funcs = QList<ExpressionType>() << ExpressionType(ExpressionType::Lambda);
    
    foreach(const ExpressionType& e, args) {
        QList<ExpressionType> alts = e.type()==ExpressionType::Many ? e.alternatives() : QList<ExpressionType>() << e;
        QList<ExpressionType> newfuncs;
        foreach(const ExpressionType& rr, alts) {
            foreach(const ExpressionType& f, funcs) {
                newfuncs += f;
                
                QMap<int, ExpressionType> stars;
//                 qDebug() << "&&&&&&&" << rr << newfuncs.last().assumptions() << rr.assumptions();
                bool m=ExpressionType::assumptionsMerge(newfuncs.last().assumptions(), rr.assumptions());
                m|=matchAssumptions(&stars, newfuncs.last().assumptions(), rr.assumptions());
//                 qDebug() << "falala" << m;
                
                if(m)
                    newfuncs.last().addParameter(rr.starsToType(stars));
                else
                    newfuncs.removeLast();
            }
        }
        
        funcs = newfuncs;
    }
    
    return funcs;
}

QString ExpressionType::objectName() const
{
    return m_objectName;
}

bool ExpressionType::hasContained() const
{
    Q_ASSERT(m_type==Vector || m_type==List || m_type==Matrix || m_type==Any);
    return !m_contained.isEmpty();
}

ExpressionType ExpressionType::contained() const
{
    Q_ASSERT(m_type==Vector || m_type==List || m_type==Matrix || m_type==Any);
    Q_ASSERT(m_contained.size()==1);
    return m_contained.first();
}
