Exemplo n.º 1
0
        public (object, KalkExpression) Canonical(KalkExpression value)
        {
            Collect(value);
            SquashPowers();

            // Compute the final canonical value
            KalkExpression leftValue = null;

            if (_powers != null)
            {
                for (var i = 0; i < _powers.Count; i++)
                {
                    var power      = _powers[i];
                    var powerValue = _context.ToObject <double>(_context.CurrentSpan, power.Unit);
                    if (powerValue < 0)
                    {
                        powerValue = -powerValue;
                        power.Unit = powerValue;
                        object left = leftValue ?? (object)1.0;
                        leftValue = new KalkBinaryExpression(left, ScriptBinaryOperator.Divide, KalkValue.AlmostEqual(powerValue, 1.0) ? power.Value : power);
                    }
                    else
                    {
                        var nextMul = KalkValue.AlmostEqual(powerValue, 1.0) ? (KalkUnit)power.Value : (KalkExpression)power;
                        leftValue = leftValue == null ? nextMul : new KalkBinaryExpression(leftValue, ScriptBinaryOperator.Multiply, nextMul);
                    }
                }
            }


            return(_value, leftValue);
        }
Exemplo n.º 2
0
        private int SortPowerPerSymbol(KalkBinaryExpression left, KalkBinaryExpression right)
        {
            var leftSymbol  = (KalkUnit)left.Value;
            var rightSymbol = (KalkUnit)right.Value;

            Debug.Assert(leftSymbol.Value == null);
            Debug.Assert(rightSymbol.Value == null);

            return(string.Compare(leftSymbol.Symbol, rightSymbol.Symbol, StringComparison.Ordinal));
        }
Exemplo n.º 3
0
        private int SortPowerPerPower(KalkBinaryExpression left, KalkBinaryExpression right)
        {
            var leftPowerValue  = _context.ToObject <double>(_context.CurrentSpan, left.Unit);
            var rightPowerValue = _context.ToObject <double>(_context.CurrentSpan, right.Unit);

            var comparePower = rightPowerValue.CompareTo(leftPowerValue);

            if (comparePower != 0)
            {
                return(comparePower);
            }

            // Then compare per symbol
            return(SortPowerPerSymbol(left, right));
        }
Exemplo n.º 4
0
        private void SquashPowers()
        {
            if (_powers == null)
            {
                return;
            }
            _powers.Sort(SortPowerPerSymbol);

            // Compress the powers: a^x * a^y = a^(x+y)
            KalkBinaryExpression previousPower = null;

            for (var i = 0; i < _powers.Count; i++)
            {
                var power = _powers[i];
                if (previousPower != null && ReferenceEquals(power.Value, previousPower.Value))
                {
                    var powerResult = ScriptBinaryExpression.Evaluate(_context, _context.CurrentSpan, ScriptBinaryOperator.Add, power.Unit, previousPower.Unit);
                    _powers[i - 1] = new KalkBinaryExpression(power.Value, ScriptBinaryOperator.Power, powerResult);
                    _powers.RemoveAt(i);
                    i--;
                    continue;
                }

                previousPower = power;
            }

            // Remove any powers a^0.0
            for (var i = 0; i < _powers.Count; i++)
            {
                var power      = _powers[i];
                var powerValue = _context.ToObject <double>(_context.CurrentSpan, power.Unit);
                if (Math.Abs(powerValue) < (float.Epsilon * 4))
                {
                    _powers.RemoveAt(i);
                    i--;
                }
            }

            // Sort power per Power first and symbol after
            // From largest to smallest
            _powers.Sort(SortPowerPerPower);
        }
Exemplo n.º 5
0
        public bool TryEvaluate(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, SourceSpan leftSpan, object leftValue, SourceSpan rightSpan, object rightValue, out object result)
        {
            var leftExpr = leftValue as KalkExpression;

            if (leftExpr is null && !KalkValue.IsNumber(leftValue))
            {
                throw new ScriptRuntimeException(leftSpan, "Expecting a number, vector or matrix");
            }
            var rightExpr = rightValue as KalkExpression;

            if (rightExpr is null && !KalkValue.IsNumber(rightValue))
            {
                throw new ScriptRuntimeException(rightSpan, "Expecting a number, vector or matrix");
            }

            result = null;
            switch (op)
            {
            case ScriptBinaryOperator.CompareEqual:
            case ScriptBinaryOperator.CompareNotEqual:
            case ScriptBinaryOperator.CompareLessOrEqual:
            case ScriptBinaryOperator.CompareGreaterOrEqual:
            case ScriptBinaryOperator.CompareLess:
            case ScriptBinaryOperator.CompareGreater:
                if (leftExpr != null && rightExpr != null)
                {
                    var leftSimplifier = new KalkExpressionSimplifier(context);
                    var(leftValueMultiplier, newLeftExpr) = leftSimplifier.Canonical(leftExpr);

                    var rightSimplifier = new KalkExpressionSimplifier(context);
                    var(rightValueMultiplier, newRightExpr) = rightSimplifier.Canonical(rightExpr);

                    var exprEquals = Equals(context, newLeftExpr, newRightExpr);
                    if (exprEquals)
                    {
                        var almostEqual = KalkValue.AlmostEqual(leftValueMultiplier, rightValueMultiplier);
                        switch (op)
                        {
                        case ScriptBinaryOperator.CompareEqual:
                            result = almostEqual;
                            break;

                        case ScriptBinaryOperator.CompareNotEqual:
                            result = !almostEqual;
                            break;

                        case ScriptBinaryOperator.CompareLessOrEqual:
                            result = almostEqual || ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v1 && v1;
                            break;

                        case ScriptBinaryOperator.CompareGreaterOrEqual:
                            result = almostEqual || ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v2 && v2;
                            break;

                        case ScriptBinaryOperator.CompareLess:
                            result = ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v3 && v3;
                            break;

                        case ScriptBinaryOperator.CompareGreater:
                            result = ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v4 && v4;
                            break;
                        }
                    }
                    else
                    {
                        result = op == ScriptBinaryOperator.CompareNotEqual;
                    }
                }
                else
                {
                    if (op == ScriptBinaryOperator.CompareEqual || op == ScriptBinaryOperator.CompareNotEqual)
                    {
                        result = op == ScriptBinaryOperator.CompareNotEqual;
                    }
                    else
                    {
                        throw NotMatching(leftSpan, leftExpr, rightSpan, rightExpr);
                    }
                }
                return(true);

            case ScriptBinaryOperator.Multiply:
            case ScriptBinaryOperator.Divide:
            case ScriptBinaryOperator.Power:
            {
                if (op != ScriptBinaryOperator.Power || rightExpr == null)
                {
                    var simplifier = new KalkExpressionSimplifier(context);
                    var(valueMul, valueExpr) = simplifier.Canonical(new KalkBinaryExpression(leftValue, op, rightValue));
                    var valueBinExpr = valueExpr as KalkBinaryExpression;

                    result = KalkValue.AlmostEqual(valueMul, 1.0) && valueBinExpr == null ? valueExpr ?? (object)1.0 : valueExpr == null ? valueMul : (object)new KalkBinaryExpression(valueMul, ScriptBinaryOperator.Multiply, valueExpr)
                    {
                        OriginalExpression = new KalkBinaryExpression(leftValue, op, rightValue)
                    };
                    return(true);
                }
                else
                {
                    throw new ScriptRuntimeException(rightSpan, "Cannot use a unit as an exponent.");
                }
            }

            case ScriptBinaryOperator.Add:
            case ScriptBinaryOperator.Substract:

                if (leftExpr != null && rightExpr != null)
                {
                    var leftSimplifier = new KalkExpressionSimplifier(context);
                    var(leftValueMultiplier, newLeftExpr) = leftSimplifier.Canonical(leftExpr);

                    var rightSimplifier = new KalkExpressionSimplifier(context);
                    var(rightValueMultiplier, newRightExpr) = rightSimplifier.Canonical(rightExpr);

                    if (!Equals(context, newLeftExpr, newRightExpr))
                    {
                        throw new ScriptRuntimeException(span, $"Cannot {(op == ScriptBinaryOperator.Add ? "add" : "subtract")} the expression. Units are not matching. The left expression with unit `{newLeftExpr}` is not matching the right expression with unit `{newRightExpr}`.");
                    }

                    result = new KalkBinaryExpression(ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier), ScriptBinaryOperator.Multiply, newLeftExpr)
                    {
                        OriginalExpression = new KalkBinaryExpression(leftValue, op, rightValue)
                    };
                }
                else
                {
                    throw NotMatching(leftSpan, leftExpr, rightSpan, rightExpr);
                }

                return(true);
            }

            return(false);
        }
Exemplo n.º 6
0
        private void Collect(object value)
        {
            if (value == null)
            {
                return;
            }

            if (value is KalkUnit symbol)
            {
                if (symbol.Value != null)
                {
                    Collect(symbol.Value);
                }
                else
                {
                    if (_powers == null)
                    {
                        _powers = new List <KalkBinaryExpression>();
                    }
                    _powers.Add(new KalkBinaryExpression(symbol, ScriptBinaryOperator.Power, 1));
                }
            }
            else if (value is KalkBinaryExpression binary)
            {
                if (binary.Operator == ScriptBinaryOperator.Multiply)
                {
                    var leftSimplifier = new KalkExpressionSimplifier(_context);
                    leftSimplifier.Collect(binary.Value);
                    Collect(leftSimplifier);

                    var rightSimplifier = new KalkExpressionSimplifier(_context);
                    rightSimplifier.Collect(binary.Unit);
                    Collect(rightSimplifier);
                }
                else if (binary.Operator == ScriptBinaryOperator.Divide)
                {
                    Collect(binary.Value);
                    if (binary.Unit is KalkExpression)
                    {
                        Collect(new KalkBinaryExpression(binary.Unit, ScriptBinaryOperator.Power, -1));
                    }
                    else
                    {
                        var result = ScriptBinaryExpression.Evaluate(_context, _context.CurrentSpan, ScriptBinaryOperator.Divide, 1, binary.Unit);
                        SquashValue(result);
                    }
                }
                else
                {
                    Debug.Assert(binary.Operator == ScriptBinaryOperator.Power);
                    if (_powers == null)
                    {
                        _powers = new List <KalkBinaryExpression>();
                    }

                    // (a * b^2) ^ 5
                    // a ^ 5 * b ^ 10
                    var subSimplifier = new KalkExpressionSimplifier(_context);
                    subSimplifier.Collect(binary.Value);

                    var subValue = _context.ToObject <double>(_context.CurrentSpan, subSimplifier._value);

                    if (!KalkValue.AlmostEqual(subValue, 1.0))
                    {
                        var result = ScriptBinaryExpression.Evaluate(_context, _context.CurrentSpan, ScriptBinaryOperator.Power, subSimplifier._value, binary.Unit);
                        SquashValue(result);
                    }

                    if (subSimplifier._powers != null)
                    {
                        foreach (var powerExpression in subSimplifier._powers)
                        {
                            var powerResult = ScriptBinaryExpression.Evaluate(_context, _context.CurrentSpan, ScriptBinaryOperator.Multiply, powerExpression.Unit, binary.Unit);
                            var newPower    = new KalkBinaryExpression(powerExpression.Value, ScriptBinaryOperator.Power, powerResult);
                            _powers.Add(newPower);
                        }
                    }
                }
            }
            else
            {
                SquashValue(value);
            }
        }