src/hbcore/layouts/hbanchorlayoutengine_p.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/layouts/hbanchorlayoutengine_p.cpp	Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,1034 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at developer.feedback@nokia.com.
+**
+****************************************************************************/
+
+#include "hbanchorlayoutengine_p.h"
+
+//Uncomment next define in order to get more debug prints.
+//Similar define exists also in the layout side.
+//#define HBANCHORLAYOUT_DEBUG
+
+
+#ifdef HBANCHORLAYOUT_DEBUG
+#include <QtDebug>
+#endif
+
+#define EPSILON 0.01f
+
+static inline bool myFuzzyCompare(double p1, double p2)
+{
+    return (qAbs(p1 - p2) <= 0.000000000001);
+}
+
+static inline bool myFuzzyCompare(float p1, float p2)
+{
+    return (qAbs(p1 - p2) <= 0.00001f);
+}
+
+/*!
+    \internal
+*/
+DataGrid::DataGrid() : mResult(), mSize( 0 )
+{
+}
+
+/*!
+    \internal
+*/
+DataGrid::~DataGrid()
+{
+    clear();
+}
+
+/*!
+    \internal
+*/
+void DataGrid::clear()
+{
+    mGrid.clear();
+    mResult.clear();
+    mSize = 0;
+}
+
+/*!
+    \internal
+*/
+void DataGrid::setExpression( const SimpleExpression ex, int i, int j )
+{
+    Expression e;
+    e.plusSimpleExpression( ex );
+    setExpression( e, i, j );
+}
+
+/*!
+    \internal
+*/
+void DataGrid::setExpression( const Expression ex, int i, int j )
+{
+    Expression ex2 = ex;
+    if ( mGrid[i].contains( j ) ) {
+        Equation eq( mGrid.value(i).value(j), ex );
+        if ( ( ! mResult.contains( eq ) ) && ( !eq.isTrivial() ) ) {
+            mResult.append( eq );
+#ifdef HBANCHORLAYOUT_DEBUG
+            qDebug( "Create extra equation on cell %d %d: %s", i, j, eq.print().toLatin1().data() );
+#endif
+            return;
+        }
+    }
+
+    mGrid[i][j] = ex2;
+    ex2.minus();
+    mGrid[j][i] = ex2;
+
+
+    if (mSize  < i + 1) {
+        mSize = i + 1;
+    }
+    if (mSize  < j + 1) {
+        mSize = j + 1;
+    }
+}
+
+/*!
+    \internal
+*/
+qreal DataGrid::value( int i, int j, Solution *solution, bool &ok )  const
+{
+    ok = true;
+    if( mGrid.value(i).contains( j ) ) {
+        return mGrid.value(i).value(j).value( solution );
+    }
+    ok = false;
+    return 0;
+}
+
+/*!
+    \internal
+*/
+QList<Equation> DataGrid::calculate()
+{
+
+#ifdef HBANCHORLAYOUT_DEBUG
+    qDebug( "\nbefore:" );
+
+    for( int j = 0; j < mSize; j++ ) {
+        QString line;
+        line.sprintf( "%d  ", j );
+        for( int i = 0; i < mSize; i++ ) {
+            if( mGrid.value(i).contains( j ) ) {
+                line.append( "X   " );
+            } else {
+                line.append( "_   " );
+            }
+        }
+        line.append( '\n' );
+        qDebug() << line;
+    }
+#endif // HBANCHORLAYOUT_DEBUG
+
+
+    for( int i = 1; i < mSize; i++ ) {
+        calculateLine( i );
+    }
+
+    for( int i = mSize - 1; i > 0; i-- ) {
+        if( ! mGrid.value( i ).contains( 0 ) ) {
+            calculateLine( i );
+        }
+    }
+
+    for( int i = 1; i < mSize; i++ ) {
+        if( ! mGrid.value( i ).contains( 0 ) ) {
+            calculateLine( i );
+        }
+    }
+
+#ifdef HBANCHORLAYOUT_DEBUG
+    qDebug( "\nafter:" );
+
+    for( int j = 0; j < mSize; j++ ) {
+        QString line;
+        line.sprintf( "%d  ", j );
+        for( int i = 0; i < mSize; i++ ) {
+            if( mGrid.value(i).contains( j ) ) {
+                line.append( "X   " );
+            } else {
+                line.append( "_   " );
+            }
+        }
+        line.append( '\n' );
+        qDebug() << line;
+    }
+
+    qDebug( "number of equations: %d", mResult.size() );
+    for( int i = 0; i < mResult.size(); i++ ) {
+        qDebug( "equation %d: %s", i, mResult.at(i).print().toLatin1().data() );
+    }
+#endif // HBANCHORLAYOUT_DEBUG
+
+    return mResult;
+}
+
+/*!
+    \internal
+*/
+void DataGrid::calculateLine( int x )
+{
+    QHash< int, Expression > vertical = mGrid.value( x );
+    bool flag = false;
+
+    QList<int>::const_iterator i;
+    QList<int> keys = vertical.keys();
+
+    for ( i = keys.constBegin(); i != keys.constEnd(); ++i ) {
+        if ( ( *i <= x ) && ( *i != 0 ) ) {
+            if ( checkLine( x, *i ) ) {
+                flag = true;
+            }
+        }
+    }
+
+    if ( flag ) {
+        return;
+    }
+
+    for ( i = keys.constBegin(); i != keys.constEnd(); ++i ) {
+        if ( *i > x ) {
+            checkLine( x, *i );
+        }
+    }
+}
+
+/*!
+    \internal
+*/
+bool DataGrid::checkLine( int x, int y )
+{
+    for ( int i = 0; i < mSize; i++ ) {
+        if ( ( mGrid.value(i).contains( y ) ) && ( mGrid.value(i).contains( 0 ) ) && ( i != x ) ) {
+            calculateRect( x, 0, i, y );
+            return true;
+        }
+    }
+    return false;
+}
+
+/*!
+    \internal
+*/
+void DataGrid::calculateRect( int ax, int ay, int cx, int cy )
+{
+    Expression exp;
+
+    /*
+    A ---  B
+    --------
+    D ---  C
+
+    A = B + D - C
+    */
+
+    exp.minusExpression( mGrid.value(cx).value(cy) );
+    exp.plusExpression( mGrid.value(cx).value(ay) );
+    exp.plusExpression( mGrid.value(ax).value(cy) );
+
+    if ( mGrid.value(ax).contains( ay ) ) {
+        Equation eq( mGrid.value(ax).value(ay), exp );
+        if ( ( ! mResult.contains( eq ) ) && ( !eq.isTrivial() ) ) {
+            mResult.append( eq );
+        }
+    } else {
+        mGrid[ax][ay] = exp;
+    }
+
+}
+
+/*!
+    \internal
+*/
+Equation::Equation( const Expression &exp1, const Expression &exp2 ) : mFormula()
+{
+    mFormula.plusExpression( exp1 );
+    mFormula.minusExpression( exp2 );
+}
+
+/*!
+    \internal
+*/
+Equation::~Equation()
+{
+}
+
+/*!
+    \internal
+*/
+bool Equation::isTrivial() const
+{
+    if ( mFormula.mExpression.count() == 0 ) {
+        return true;
+    }
+    return false;
+}
+
+/*!
+    \internal
+*/
+bool Equation::operator== ( const Equation &first ) const
+{
+    return EquationSolver::compareEquations( first, *this );
+}
+
+QString Equation::print() const
+{
+    return QString( mFormula.print() ) + QString( " = 0" );
+}
+
+
+static inline qreal minSlope(const Variable &var, qreal coef )
+{
+    return var.mPref + ( var.mMin - var.mPref )* coef;
+}
+
+static inline qreal maxSlope(const Variable &var, qreal coef )
+{
+    return var.mPref + ( var.mMax - var.mPref )* coef;
+}
+
+
+/*!
+    \internal
+*/
+bool EquationSolver::solveEquation( QList<Equation> el, VariableSet &varset, Solution *solution )
+{
+    enum State {
+        CheckEquationsNum,
+        FillRest,
+        SolveOne,
+        Result,
+        CheckEquation,
+        Validate,
+        FinalizeVariable,
+        ExcludeEquation,
+        SolveMany
+    } state;
+
+    qreal min;
+    qreal max;
+    qreal pref;
+    int currentEquationNum = 0;
+
+    state = CheckEquationsNum;
+    bool inloop = true;
+    bool result = false;
+
+    solution->clear();
+
+    for ( int i = 0; i < varset.mVarList.size(); i++ ) {
+        if ( varset.mVarList.at(i)->mFlags&Variable::FlagFixed ) {
+            solution->insert( *(varset.mVarList.at(i)), varset.mVarList.at(i)->mPref );
+#ifdef HBANCHORLAYOUT_DEBUG
+            qDebug( "Solved fixed variable: Id=%d value=%lf", varset.mVarList.at(i)->mId, varset.mVarList.at(i)->mPref );
+#endif // HBANCHORLAYOUT_DEBUG
+        }
+    }
+
+
+
+    while ( inloop ) {
+        switch ( state ) {
+            case CheckEquationsNum:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = CheckEquationsNum" );
+#endif // HBANCHORLAYOUT_DEBUG
+                if ( el.size() == 0 ) {
+                    state = FillRest;
+//                } else if ( el.size() == 1 ) {
+//                    state = SolveOne;
+                } else {
+                    state = CheckEquation;
+                }
+                break;
+            }
+            case FillRest:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = FillRest" );
+#endif // HBANCHORLAYOUT_DEBUG
+                for ( int i = 0; i < varset.mVarList.size(); i++ ) {
+                    if ( !solution->contains( *(varset.mVarList.at(i)) ) ) {
+                        solution->insert( *(varset.mVarList[i]), varset.mVarList.at(i)->mPref );
+                    }
+                }
+
+                state = Result;
+                result = true;
+                break;
+            }
+            case SolveOne:
+            {
+                currentEquationNum = el.size() - 1;
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = SolveOne" );
+#endif // HBANCHORLAYOUT_DEBUG
+                Equation currentEquation = el.at(currentEquationNum);
+
+                min = currentEquation.mFormula.minValue( solution );
+                max = currentEquation.mFormula.maxValue( solution );
+                pref = currentEquation.mFormula.prefValue( solution );
+                const qreal prefExpanded = currentEquation.mFormula.prefExpandedValue( solution );
+
+                qreal coef = 0;
+                
+                if ( ( ( min > 0 ) && !myFuzzyCompare(min, 0) ) ||
+                     ( ( max < 0 ) && !myFuzzyCompare(max, 0) ) ) {
+                    result = false;
+                    state = Result;
+                    break;
+                }
+
+                bool min_is_pref = false;
+                bool min_is_prefExp = false;
+                
+                if( pref < 0 ) {
+                    min_is_pref = true;
+                } else if ( prefExpanded < 0 ) {
+                    min_is_prefExp = true;
+                }
+                
+                if( myFuzzyCompare( pref, 0 ) ) {
+                    coef = 0;
+                } else if( min_is_pref ) {
+                    if( myFuzzyCompare( max, 0 ) ) {
+                        coef = 1;
+                    } else {
+                        coef = ( pref / ( pref - max ) );
+                    }
+                } else {
+                    if( myFuzzyCompare( min, 0 ) ) {
+                        coef = 1;
+                    } else {
+                        if ( min_is_prefExp ) {
+                            coef = ( pref / ( pref - prefExpanded ) );
+                        } else {
+                            coef = ( prefExpanded / ( prefExpanded - min ) );
+                        }
+                    }
+                }
+
+
+                for ( int i = 0; i < currentEquation.mFormula.mExpression.size(); i++ ) {
+                    SimpleExpression se = currentEquation.mFormula.mExpression[i];
+                    qreal value;
+
+                    if ( (se.mVar->mFlags&Variable::FlagFixed) || solution->contains( *( se.mVar ) ) ) {
+                        continue;
+                    }
+
+                    if( min_is_pref ) {
+
+                        if ( se.mCoef > 0 ) {
+                            value = maxSlope( *se.mVar, coef );
+                        } else {
+                            value = minSlope( *se.mVar, coef );
+                        }
+                    } else {
+                        if ( min_is_prefExp ) {
+                            if ( se.mVar->mFlags&Variable::FlagExpanding ){
+                                if ( se.mCoef > 0 ) {
+                                    value = minSlope( *se.mVar, coef );
+                                  } else {
+                                    value = maxSlope( *se.mVar, coef );
+                                } 
+                            }else {
+                                value = se.mVar->mPref;
+                            }
+
+                        } else {
+                            if ( se.mVar->mFlags&Variable::FlagExpanding ){
+                                if ( se.mCoef > 0 ) {
+                                    value = se.mVar->mMin;
+                                } else {
+                                    value = se.mVar->mMax;
+                                }
+                            } else {
+                                if ( se.mCoef > 0 ) {
+                                    value = minSlope( *se.mVar, coef );
+                                 } else {
+                                    value = maxSlope( *se.mVar, coef );
+                                }
+                            }
+                        }
+                    }
+
+                    solution->insert( *( se.mVar ), value );
+                }
+                result = true;
+                state = ExcludeEquation;
+
+                break;
+            }
+            case Result:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = Result" );
+#endif // HBANCHORLAYOUT_DEBUG
+                inloop = false;
+                break;
+            }
+            case CheckEquation:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = CheckEquation" );
+#endif // HBANCHORLAYOUT_DEBUG
+
+                bool real_break = false;
+
+                for ( int i = 0; i < el.size(); i++ ) {
+                    int num;
+                    num = numOfUnknownVars( &( el.at(i) ), solution );
+                    if ( num == 0 ) {
+                        currentEquationNum = i;
+                        state = Validate;
+                        real_break = true;
+                        break;
+                    }
+                    if ( num == 1 ) {
+                        currentEquationNum = i;
+                        state = FinalizeVariable;
+                        real_break = true;
+                        break;
+                    }
+                }
+
+                if ( real_break ) {
+                    break;
+                }
+
+                state = SolveMany;
+                break;
+            }
+            case Validate:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = Validate" );
+#endif // HBANCHORLAYOUT_DEBUG
+
+                if ( !myFuzzyCompare( el.at(currentEquationNum).mFormula.value( solution ), 0 ) ) {
+                    result = false;
+                    state = Result;
+                } else {
+                    state = ExcludeEquation;
+                }
+
+                break;
+            }
+            case FinalizeVariable:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = FinalizeVariable" );
+#endif // HBANCHORLAYOUT_DEBUG
+
+                Expression exp = el.at(currentEquationNum).mFormula;
+                SimpleExpression se;
+                se.mCoef = 0;
+                se.mVar = 0;
+
+                for ( int i = 0; i < exp.mExpression.size(); i++ ) {
+                    if ( !solution->contains( *( exp.mExpression.at(i).mVar ) ) ) {
+                        se = exp.mExpression.at(i);
+                        exp.mExpression.removeAt( i );
+                        break;
+                    }
+                }
+
+                qreal value = - exp.value( solution ) / se.mCoef;
+
+                if ( ( value - se.mVar->mMin < -EPSILON ) || ( value - se.mVar->mMax > EPSILON ) ) {
+#ifdef HBANCHORLAYOUT_DEBUG
+                    qDebug( "cannot solve: min=%lf, max=%lf, value=%lf", se.mVar->mMin, se.mVar->mMax, value );
+#endif
+                    result = false;
+                    state = Result;
+                    break;
+                }
+
+                solution->insert( *( se.mVar ), value );
+
+                state = ExcludeEquation;
+                break;
+            }
+            case ExcludeEquation:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = ExcludeEquation" );
+#endif // HBANCHORLAYOUT_DEBUG
+                el.removeAt( currentEquationNum );
+                state = CheckEquationsNum;
+                break;
+            }
+            case SolveMany:
+            {
+#ifdef HBANCHORLAYOUT_DEBUG
+                qDebug( "state = SolveMany" );
+#endif // HBANCHORLAYOUT_DEBUG
+                state = SolveOne;
+                break;
+            }
+        }
+    }
+
+#ifdef HBANCHORLAYOUT_DEBUG
+    qDebug( "result = %d\n", (int)result );
+#endif // HBANCHORLAYOUT_DEBUG
+    return result;
+}
+
+/*!
+    \internal
+*/
+int EquationSolver::numOfUnknownVars( const Equation *eq, const Solution *solution ) {
+
+    int result = 0;
+
+
+    for ( int i = 0; i < eq->mFormula.mExpression.size(); i++ ) {
+        if ( solution->contains( *(eq->mFormula.mExpression.at(i).mVar) ) ) {
+            continue;
+        }
+        result++;
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+bool EquationSolver::compareEquations( const Equation &equation1, const Equation &equation2 )
+{
+    QList<SimpleExpression> exp1 = equation1.mFormula.mExpression;
+    QList<SimpleExpression> exp2 = equation2.mFormula.mExpression;
+    if ( exp1.count() != exp2.count() ) {
+        return false;
+    }
+
+    if ( exp1.count() == 0 ) {
+        return true;
+    }
+
+    qreal global_coef = 1.0 * exp2.at(0).mCoef / exp1.at(0).mCoef;
+
+    for ( int i = 0; i < exp1.count(); i++ ) {
+        if ( ( !myFuzzyCompare( global_coef * exp1.at(i).mCoef, exp2.at(i).mCoef ) )
+            || ( exp1.at(i).mVar != exp2.at(i).mVar ) ) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/*!
+    \internal
+*/
+bool EquationSolver::compareExpressions( const Expression &expression1, const Expression &expression2 )
+{
+    QList<SimpleExpression> exp1 = expression1.mExpression;
+    QList<SimpleExpression> exp2 = expression2.mExpression;
+    if ( exp1.count() != exp2.count() ) {
+        return false;
+    }
+
+    for ( int i = 0; i < exp1.count(); i++ ) {
+        if ( ( !myFuzzyCompare( exp1.at(i).mCoef, exp2.at(i).mCoef ) )
+            || ( exp1.at(i).mVar != exp2.at(i).mVar ) ) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*!
+    \internal
+*/
+Expression::Expression() : mExpression()
+{
+}
+
+/*!
+    \internal
+*/
+Expression::~Expression()
+{
+}
+
+/*!
+    \internal
+*/
+void Expression::clear()
+{
+    mExpression.clear();
+}
+
+/*!
+    \internal
+*/
+void Expression::plusSimpleExpression( const SimpleExpression simp_exp )
+{
+    SimpleExpression *item;
+
+    if( myFuzzyCompare( simp_exp.mCoef, 0 ) ) {
+        return;
+    }
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        item = &(mExpression[i]);
+
+        if ( item->mVar->mId < simp_exp.mVar->mId ) {
+            continue;
+        }
+
+        if ( item->mVar->mId == simp_exp.mVar->mId ) {
+            item->mCoef += simp_exp.mCoef;
+            if ( myFuzzyCompare( item->mCoef, 0 ) ) {
+                mExpression.removeAt( i );
+            }
+            return;
+        } else {
+            mExpression.insert( i, simp_exp );
+            return;
+        }
+    }
+
+    mExpression.append( simp_exp );
+}
+
+/*!
+    \internal
+*/
+void Expression::plusExpression( const Expression exp )
+{
+    for ( int i = 0; i < exp.mExpression.size(); i++ ) {
+        plusSimpleExpression( exp.mExpression.at(i) );
+    }
+}
+
+/*!
+    \internal
+*/
+void Expression::minusSimpleExpression( const SimpleExpression simp_exp )
+{
+    SimpleExpression se = simp_exp;
+    se.mCoef = -se.mCoef;
+    plusSimpleExpression( se );
+}
+
+/*!
+    \internal
+*/
+void Expression::minusExpression( const Expression exp )
+{
+    for ( int i = 0; i < exp.mExpression.size(); i++ ) {
+        minusSimpleExpression( exp.mExpression.at(i) );
+    }
+}
+
+/*!
+    \internal
+*/
+void Expression::minus()
+{
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        mExpression[i].mCoef = -mExpression.at(i).mCoef;
+    }
+}
+
+/*!
+    \internal
+*/
+qreal Expression::value( Solution *solution ) const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        result += solution->value( *(mExpression.at(i).mVar) ) * mExpression.at(i).mCoef;
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+qreal Expression::minValue() const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        const SimpleExpression &currentExpression = mExpression.at(i);
+        if ( currentExpression.mVar->mFlags&Variable::FlagFixed ) {
+            result += currentExpression.mVar->mPref * currentExpression.mCoef;
+        } else {
+            if ( currentExpression.mCoef > 0 ) {
+                result += currentExpression.mVar->mMin * currentExpression.mCoef;
+            } else {
+                result += currentExpression.mVar->mMax * currentExpression.mCoef;
+            }
+        }
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+qreal Expression::prefValue() const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+         const SimpleExpression &currentExpression = mExpression.at(i);
+         result += currentExpression.mVar->mPref * currentExpression.mCoef;
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+qreal Expression::maxValue() const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        const SimpleExpression &currentExpression = mExpression.at(i);
+        if ( currentExpression.mVar->mFlags&Variable::FlagFixed ) {
+            result += currentExpression.mVar->mPref * currentExpression.mCoef;
+        } else {
+            if ( currentExpression.mCoef < 0 ) {
+                result += currentExpression.mVar->mMin * currentExpression.mCoef;
+            } else {
+                result += currentExpression.mVar->mMax * currentExpression.mCoef;
+            }
+        }
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+qreal Expression::minValue( Solution *solution ) const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        const SimpleExpression &currentExpression = mExpression.at(i);
+        if ( currentExpression.mVar->mFlags&Variable::FlagFixed ) {
+            result += currentExpression.mVar->mPref * currentExpression.mCoef;
+        } else if ( solution->contains( *( currentExpression.mVar ) ) ) {
+            result += solution->value( *( currentExpression.mVar ) ) * currentExpression.mCoef;
+        } else {
+            if ( currentExpression.mCoef > 0 ) {
+                result += currentExpression.mVar->mMin * currentExpression.mCoef;
+            } else {
+                result += currentExpression.mVar->mMax * currentExpression.mCoef;
+            }
+        }
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+qreal Expression::prefValue( Solution *solution ) const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        const SimpleExpression &currentExpression = mExpression.at(i);
+        if ( solution->contains( *( currentExpression.mVar ) ) ) {
+            result += solution->value( *( currentExpression.mVar ) ) * currentExpression.mCoef;
+        } else {
+            result += currentExpression.mVar->mPref * currentExpression.mCoef;
+        }
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+qreal Expression::prefExpandedValue( Solution *solution ) const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        const SimpleExpression &currentExpression = mExpression.at(i);
+        if ( currentExpression.mVar->mFlags&Variable::FlagExpanding ) {
+            // use the min value
+            if ( currentExpression.mVar->mFlags&Variable::FlagFixed ) {
+                result += currentExpression.mVar->mPref * currentExpression.mCoef;
+            } else if ( solution->contains( *( currentExpression.mVar ) ) ) {
+                result += solution->value( *( currentExpression.mVar ) ) * currentExpression.mCoef;
+            } else {
+                if ( currentExpression.mCoef > 0 ) {
+                    result += currentExpression.mVar->mMin * currentExpression.mCoef;
+                } else {
+                    result += currentExpression.mVar->mMax * currentExpression.mCoef;
+                }
+            }
+        }
+        else if ( solution->contains( *( currentExpression.mVar ) ) ) {
+            result += solution->value( *( currentExpression.mVar ) ) * currentExpression.mCoef;
+        } else {
+            result += currentExpression.mVar->mPref * currentExpression.mCoef;
+        }
+    }
+
+    return result;
+}
+
+/*!
+    \internal
+*/
+qreal Expression::maxValue( Solution *solution ) const
+{
+    qreal result = 0;
+
+    for ( int i = 0; i < mExpression.size(); i++ ) {
+        const SimpleExpression &currentExpression = mExpression.at(i);
+        if ( currentExpression.mVar->mFlags&Variable::FlagFixed ) {
+            result += currentExpression.mVar->mPref * currentExpression.mCoef;
+        } else if ( solution->contains( *( currentExpression.mVar ) ) ) {
+            result += solution->value( *( currentExpression.mVar ) ) * currentExpression.mCoef;
+        } else {
+            if ( currentExpression.mCoef < 0 ) {
+                result += currentExpression.mVar->mMin * currentExpression.mCoef;
+            } else {
+                result += currentExpression.mVar->mMax * currentExpression.mCoef;
+            }
+        }
+    }
+
+    return result;
+}
+
+QString Expression::print() const
+{
+    QString res;
+    for( int i = 0; i < mExpression.size(); i++ ) {
+        if( i > 0 ) {
+            res += " + ";
+        }
+        res += '(' + QString::number( mExpression.at(i).mCoef ) + ')' + "var[" + QString::number( mExpression.at(i).mVar->mId ) + ']';
+    }
+    return res;
+}
+
+/*!
+    \internal
+*/
+VariableSet::VariableSet() : mVarList()
+{
+    mCurr_id = 0;
+}
+
+/*!
+    \internal
+*/
+VariableSet::~VariableSet()
+{
+    qDeleteAll(mVarList);
+}
+
+/*!
+    \internal
+*/
+Variable *VariableSet::createVariable( void *ref )
+{
+    Variable *var = new Variable();
+    var->mId = mCurr_id;
+    var->mFlags &= ~Variable::FlagFixed;
+    var->mRef = ref;
+
+    mVarList.append( var );
+
+    mCurr_id++;
+
+    return var;
+}
+
+/*!
+    \internal
+*/
+bool VariableSet::removeVariable( Variable *var )
+{
+    if( var ) {
+        return mVarList.removeOne( var );
+    }
+    return false; 
+}
+
+/*!
+    \internal
+*/
+Variable *VariableSet::findVariable( void *ref ) const
+{
+    if( ! ref ) {
+        return 0;
+    }
+    
+    QList<Variable*>::const_iterator i;
+    for ( i = mVarList.constBegin(); i != mVarList.constEnd(); ++i ) {
+        if ( (*i)->mRef == ref ) return *i;
+    }
+    return 0;
+}
+
+
+/*!
+    \internal
+*/
+void VariableSet::clear()
+{
+    qDeleteAll( mVarList );
+    mVarList.clear();
+    mCurr_id = 0;
+}
+
+/*!
+    \internal
+*/
+uint qHash ( Variable key ) {
+    return key.mId;
+}