Пример #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);
        }
Пример #2
0
 private static void CheckElementType(KalkValue x, KalkValue y)
 {
     if (x == null)
     {
         throw new ArgumentNullException(nameof(x));
     }
     if (y == null)
     {
         throw new ArgumentNullException(nameof(y));
     }
     if (x.ElementType != y.ElementType)
     {
         throw new ArgumentException($"Unsupported type for matrix multiplication. The combination of {x.GetType().ScriptPrettyName()} * {y.GetType().ScriptPrettyName()} is not supported.", nameof(x));
     }
 }
Пример #3
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);
        }
Пример #4
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);
            }
        }