Beispiel #1
0
        public override Operand EvaluationStep(Context context)
        {
            if (operandList.Count != 1)
            {
                throw new MathException($"Versor factor function expected exactly one argument; got {operandList.Count}.");
            }

            Operand operand = base.EvaluationStep(context);

            if (operand != null)
            {
                return(operand);
            }

            GeometricProduct factorization = Factor(operandList[0], context);

            factorization.freezeFlags |= FreezeFlag.DISTRIBUTION;

            // Provide a way to get at the individual factors.
            for (int i = 0; i < factorization.operandList.Count; i++)
            {
                context.operandStorage.SetStorage($"factor{i}", factorization.operandList[i].Copy());
            }

            return(factorization);
        }
        public override Operand Explode(ITranslator translator, Context context)
        {
            GeometricProduct geometricProduct = new GeometricProduct();

            geometricProduct.operandList.Add(translator.Translate(scalar.Copy(), context));

            foreach (Factor factor in factorList)
            {
                geometricProduct.operandList.Add(factor.Explode(translator, context));
            }

            return(geometricProduct);
        }
            public override Operand Explode(ITranslator translator, Context context)
            {
                GeometricProduct geometricProduct = new GeometricProduct();

                for (int i = 0; i < Math.Abs(exponent); i++)
                {
                    geometricProduct.operandList.Add(translator.Translate(new SymbolicScalarTerm(this.name), context));
                }

                if (exponent < 0)
                {
                    return(new Inverse(new List <Operand>()
                    {
                        geometricProduct
                    }));
                }

                return(geometricProduct);
            }
Beispiel #4
0
        public override Operand EvaluationStep(Context context)
        {
            if (operandList.Count != 1)
            {
                throw new MathException(string.Format("Inverse operation expects exactly one operand, got {0}.", operandList.Count));
            }

            if (operandList[0].IsAdditiveIdentity)
            {
                throw new MathException("Cannot invert the additive identity.");
            }

            if (operandList[0] is GeometricProduct oldGeometricProduct)
            {
                GeometricProduct newGeometricProduct = new GeometricProduct();

                for (int i = oldGeometricProduct.operandList.Count - 1; i >= 0; i--)
                {
                    newGeometricProduct.operandList.Add(new Inverse(new List <Operand>()
                    {
                        oldGeometricProduct.operandList[i]
                    }));
                }

                return(newGeometricProduct);
            }

            Operand operand = base.EvaluationStep(context);

            if (operand != null)
            {
                return(operand);
            }

            Operand inverse = operandList[0].Inverse(context);

            return(inverse);
        }
        public override Operand Inverse(Context context)
        {
            GeometricProduct geometricProduct = new GeometricProduct();

            // Without loss of generality, we can always write a blade in terms of an orthogonal basis.
            // It's then easy to realizing that a blade times its reverse is always a scalar.
            geometricProduct.operandList.Add(new Reverse(new List <Operand>()
            {
                this.Copy()
            }));
            geometricProduct.operandList.Add(new Inverse(new List <Operand>()
            {
                new InnerProduct(new List <Operand>()
                {
                    this.Copy(), new Reverse(new List <Operand>()
                    {
                        this.Copy()
                    })
                })
            }));

            return(geometricProduct);
        }
            public override Operand Explode(ITranslator translator, Context context)
            {
                GeometricProduct geometricProduct = new GeometricProduct();

                for (int i = 0; i < Math.Abs(exponent); i++)
                {
                    InnerProduct innerProduct = new InnerProduct();
                    innerProduct.operandList.Add(translator.Translate(new Blade(this.vectorNameA), context));
                    innerProduct.operandList.Add(translator.Translate(new Blade(this.vectorNameB), context));

                    geometricProduct.operandList.Add(innerProduct);
                }

                if (exponent < 0)
                {
                    return(new Inverse(new List <Operand>()
                    {
                        geometricProduct
                    }));
                }

                return(geometricProduct);
            }
        public override Operand EvaluationStep(Context context)
        {
            if (Grade == 0)
            {
                return(scalar);
            }

            // Handle an easy case of a linearly dependent set of vectors.
            for (int i = 0; i < vectorList.Count; i++)
            {
                for (int j = i + 1; j < vectorList.Count; j++)
                {
                    if (vectorList[i] == vectorList[j])
                    {
                        return(new NumericScalar(0.0));
                    }
                }
            }

            // The context may also have something to say about linear dependence.
            if (context.IsLinearlyDependentSet(vectorList))
            {
                return(new NumericScalar(0.0));
            }

            Operand operand = base.EvaluationStep(context);

            if (operand != null)
            {
                return(operand);
            }

            int  adjacentSwapCount = 0;
            bool keepGoing         = true;

            while (keepGoing)
            {
                keepGoing = false;
                for (int i = 0; i < vectorList.Count - 1; i++)
                {
                    string vectorNameA = vectorList[i];
                    string vectorNameB = vectorList[i + 1];

                    if (string.Compare(vectorNameA, vectorNameB) > 0)
                    {
                        vectorList[i]     = vectorNameB;
                        vectorList[i + 1] = vectorNameA;

                        adjacentSwapCount++;
                        keepGoing = true;
                    }
                }
            }

            if (adjacentSwapCount > 0)
            {
                if (adjacentSwapCount % 2 == 1)
                {
                    scalar = new GeometricProduct(new List <Operand>()
                    {
                        new NumericScalar(-1.0), scalar
                    });
                }

                return(this);
            }

            return(null);
        }
Beispiel #8
0
        public override Operand EvaluationStep(Context context)
        {
            Operand operand = base.EvaluationStep(context);

            if (operand != null)
            {
                return(operand);
            }

            // To avoid infinite evaluation looping, we must apply...
            //   1) vB = v.B + v^B, and
            //   2) v^B = vB - v.B,
            // ...according to rules that dictate when and where they're appropriate.
            // Also to avoid infinite looping, the distributive property must take
            // precedence over anything we do here.

            // All reduction cases must be eliminated before it is safe to handle the expansion cases.
            for (int i = 0; i < operandList.Count - 1; i++)
            {
                Blade bladeA = operandList[i] as Blade;
                Blade bladeB = operandList[i + 1] as Blade;

                if (bladeA != null && bladeB != null && bladeA.Grade > 1 && bladeB.Grade > 1)
                {
                    GeometricProduct geometricProduct;

                    if (context.useOperandCache)
                    {
                        geometricProduct = new GeometricProduct(new List <Operand>()
                        {
                            new Blade(new NumericScalar(1.0), bladeA.vectorList.ToList()), new Blade(new NumericScalar(1.0), bladeB.vectorList.ToList())
                        });
                        string  key          = geometricProduct.Print(Format.PARSEABLE, context);
                        Operand cachedResult = null;
                        if (!context.operandCache.GetStorage(key, ref cachedResult))
                        {
                            context.useOperandCache = false;
                            cachedResult            = Operand.ExhaustEvaluation(geometricProduct, context);
                            context.useOperandCache = true;
                            context.operandCache.SetStorage(key, cachedResult);
                        }

                        return(new GeometricProduct(new List <Operand>()
                        {
                            bladeA.scalar, bladeB.scalar, cachedResult
                        }));
                    }

                    // Here our choice of which blade to reduce is arbitrary from a stand-point of correctness.
                    // However, we might converge faster by choosing the blade with smaller grade.
                    // Note there is also something arbitrary about how we're reducing the blades.
                    int   j        = bladeA.Grade <= bladeB.Grade ? i : i + 1;
                    Blade blade    = operandList[j] as Blade;
                    Blade subBlade = blade.MakeSubBlade(0);
                    Blade vector   = new Blade(blade.vectorList[0]);
                    geometricProduct = new GeometricProduct(new List <Operand>()
                    {
                        vector, subBlade
                    });
                    InnerProduct innerProduct = new InnerProduct(new List <Operand>()
                    {
                        vector.Copy(), subBlade.Copy()
                    });
                    operandList[j] = new Sum(new List <Operand>()
                    {
                        geometricProduct, new GeometricProduct(new List <Operand>()
                        {
                            new NumericScalar(-1.0), innerProduct
                        })
                    });
                    return(this);
                }
            }

            // All reduction cases eliminated, it is now safe to handle some expansion cases.
            for (int i = 0; i < operandList.Count - 1; i++)
            {
                Blade bladeA = operandList[i] as Blade;
                Blade bladeB = operandList[i + 1] as Blade;

                if (bladeA == null || bladeB == null)
                {
                    continue;
                }

                if ((bladeA.Grade == 1 && bladeB.Grade > 1) || (bladeA.Grade > 1 && bladeB.Grade == 1))
                {
                    InnerProduct innerProduct = new InnerProduct(new List <Operand>()
                    {
                        bladeA, bladeB
                    });
                    OuterProduct outerProduct = new OuterProduct(new List <Operand>()
                    {
                        bladeA.Copy(), bladeB.Copy()
                    });
                    operandList[i] = new Sum(new List <Operand>()
                    {
                        innerProduct, outerProduct
                    });
                    operandList.RemoveAt(i + 1);
                    return(this);
                }
            }

            // It is now safe to handle the remaining expansion cases.
            for (int i = 0; i < operandList.Count - 1; i++)
            {
                Blade bladeA = operandList[i] as Blade;
                Blade bladeB = operandList[i + 1] as Blade;

                if (bladeA == null || bladeB == null)
                {
                    continue;
                }

                if (bladeA.Grade == 1 && bladeB.Grade == 1)
                {
                    operandList.RemoveAt(i + 1);
                    GeometricProduct innerProduct = new GeometricProduct(new List <Operand>()
                    {
                        bladeA.scalar, bladeB.scalar, context.BilinearForm(bladeA.vectorList[0], bladeB.vectorList[0])
                    });
                    Blade outerProduct = new Blade(new GeometricProduct(new List <Operand>()
                    {
                        bladeA.scalar.Copy(), bladeB.scalar.Copy()
                    }));
                    outerProduct.vectorList.Add(bladeA.vectorList[0]);
                    outerProduct.vectorList.Add(bladeB.vectorList[0]);
                    operandList[i] = new Sum(new List <Operand>()
                    {
                        innerProduct, outerProduct
                    });
                    return(this);
                }
            }

            return(null);
        }
        public override Operand EvaluationStep(Context context)
        {
            if (operandList.Count != 2)
            {
                throw new MathException(string.Format("Power function expected exactly 2 arguments, got {0}.", operandList.Count));
            }

            Operand operand = base.EvaluationStep(context);

            if (operand != null)
            {
                return(operand);
            }

            Operand          baseOperand      = operandList[0];
            Operand          exponentOperand  = operandList[1];
            GeometricProduct geometricProduct = new GeometricProduct();

            if (exponentOperand is Sum sumExponent)
            {
                for (int i = 0; i < sumExponent.operandList.Count; i++)
                {
                    geometricProduct.operandList.Add(new Power(new List <Operand>()
                    {
                        baseOperand.Copy(), sumExponent.operandList[i]
                    }));
                }

                return(geometricProduct);
            }

            if (exponentOperand is NumericScalar numericScalarExponent)
            {
                if (baseOperand is NumericScalar numericScalarBase)
                {
                    if (numericScalarBase.value < 0.0 && numericScalarExponent.value < 0.0)
                    {
                        throw new MathException("Imaginary root avoided in power computation.");
                    }

                    return(new NumericScalar(Math.Pow(numericScalarBase.value, numericScalarExponent.value)));
                }
                else if (Math.Round(numericScalarExponent.value) == numericScalarExponent.value)
                {
                    for (int i = 0; i < (int)Math.Abs(numericScalarExponent.value); i++)
                    {
                        geometricProduct.operandList.Add(baseOperand.Copy());
                    }

                    if (numericScalarExponent.value >= 0.0)
                    {
                        return(geometricProduct);
                    }

                    return(new Inverse(new List <Operand>()
                    {
                        geometricProduct
                    }));
                }
            }

            geometricProduct.operandList.Add(exponentOperand);
            geometricProduct.operandList.Add(new Logarithm(new List <Operand>()
            {
                baseOperand
            }));
            return(new Exponent(new List <Operand>()
            {
                geometricProduct
            }));
        }
Beispiel #10
0
        public override Operand EvaluationStep(Context context)
        {
            if (operandList.Count != 1)
            {
                throw new MathException(string.Format("Logarithm function expected exactly 1 argument, got {0}.", operandList.Count));
            }

            Operand operand = operandList[0];

            if (operand is GeometricProduct geometricProduct)
            {
                Sum sum = new Sum();
                foreach (Operand factor in geometricProduct.operandList)
                {
                    sum.operandList.Add(new Logarithm(new List <Operand>()
                    {
                        factor
                    }));
                }

                return(sum);
            }

            operand = base.EvaluationStep(context);
            if (operand != null)
            {
                return(operand);
            }

            operand = operandList[0];

            if (operand is Collectable collectable)
            {
                Operand scalar = collectable.scalar;
                if (!scalar.IsMultiplicativeIdentity)
                {
                    collectable.scalar = new NumericScalar(1.0);
                    return(new Sum(new List <Operand>()
                    {
                        new Logarithm(new List <Operand>()
                        {
                            scalar
                        }), new Logarithm(new List <Operand>()
                        {
                            collectable
                        })
                    }));;
                }
            }

            if (operand is NumericScalar numericScalar)
            {
                return(new NumericScalar(Math.Log(numericScalar.value)));
            }

            if (operand is Inverse inverse && inverse.operandList.Count == 1)
            {
                geometricProduct = new GeometricProduct();
                geometricProduct.operandList.Add(new NumericScalar(-1.0));
                geometricProduct.operandList.Add(new Logarithm(new List <Operand>()
                {
                    inverse.operandList[0]
                }));
                return(geometricProduct);
            }

            // TODO: Solve e^x = y, for x in terms of y and e.
            return(null);
        }
Beispiel #11
0
        public override Operand EvaluationStep(Context context)
        {
            if (operandList.Count != 1)
            {
                throw new MathException(string.Format("Exponential function expected exactly 1 argument, got {0}.", operandList.Count));
            }

            Operand operand = base.EvaluationStep(context);

            if (operand != null)
            {
                return(operand);
            }

            Operand exponentOperand = operandList[0];

            if (exponentOperand is Sum sumExponent)
            {
                GeometricProduct geometricProduct = new GeometricProduct();

                for (int i = 0; i < sumExponent.operandList.Count; i++)
                {
                    geometricProduct.operandList.Add(new Exponent(new List <Operand>()
                    {
                        sumExponent.operandList[i]
                    }));
                }

                return(geometricProduct);
            }

            if (exponentOperand is NumericScalar numericScalar)
            {
                return(new NumericScalar(Math.Exp(numericScalar.value)));
            }
            else if (exponentOperand is Blade blade)
            {
                Blade        basisBlade   = new Blade(new NumericScalar(1.0), blade.vectorList.ToList());
                InnerProduct innerProduct = new InnerProduct();
                innerProduct.operandList.Add(new NumericScalar(-1.0));
                innerProduct.operandList.Add(basisBlade.Copy());
                innerProduct.operandList.Add(basisBlade.Copy());
                Operand result = Operand.ExhaustEvaluation(innerProduct, context);
                if (result.IsMultiplicativeIdentity)
                {
                    Sum sum = new Sum();
                    sum.operandList.Add(new Cosine(new List <Operand>()
                    {
                        blade.scalar.Copy()
                    }));
                    sum.operandList.Add(new GeometricProduct(new List <Operand>()
                    {
                        basisBlade.Copy(), new Sine(new List <Operand>()
                        {
                            blade.scalar.Copy()
                        })
                    }));
                    return(sum);
                }
                else if (result.IsAdditiveIdentity)
                {
                    // What if it's a null blade?
                }
                else
                {
                    // Hyperbolic cosine/sine?
                }
            }

            return(null);
        }
Beispiel #12
0
        // The algorithm implemented here comes straight out of Christian Perwass' book.
        // TODO: Also, as noted by Perwass, this algorithm doesn't work for null versors.
        public static GeometricProduct Factor(Operand operand, Context context)
        {
            Operand          currentVersor       = operand.Copy();
            GeometricProduct versorFactorization = new GeometricProduct();

            while (true)
            {
                HashSet <int> gradeSet = DiscoverGrades(currentVersor, context);
                int           maxGrade = gradeSet.ToList().Aggregate(0, (currentMaxGrade, grade) => grade > currentMaxGrade ? grade : currentMaxGrade);
                if (maxGrade <= 0)
                {
                    break;
                }

                Operand blade = Operand.ExhaustEvaluation(new GradePart(new List <Operand>()
                {
                    currentVersor.Copy(), new NumericScalar(maxGrade)
                }), context);
                OuterProduct bladeFactorization = null;

                try
                {
                    bladeFactorization = FactorBlade.Factor(blade, context);
                }
                catch (MathException exc)
                {
                    throw new MathException("Highest-grade-part of multivector does not factor as a blade.", exc);
                }

                Operand nonNullVector = null, magnitude = null;

                foreach (Operand vector in bladeFactorization.operandList)
                {
                    if (vector.Grade == 1)
                    {
                        magnitude = Operand.ExhaustEvaluation(new Trim(new List <Operand>()
                        {
                            new Magnitude(new List <Operand>()
                            {
                                vector.Copy()
                            })
                        }), context);
                        if (!magnitude.IsAdditiveIdentity)
                        {
                            nonNullVector = vector;
                            break;
                        }
                    }
                }

                if (nonNullVector == null)
                {
                    throw new MathException("Failed to find non-null vector in blade factorization.");
                }

                Operand unitVector = Operand.ExhaustEvaluation(new Normalize(new List <Operand>()
                {
                    nonNullVector
                }), context);

                versorFactorization.operandList.Insert(0, unitVector);

                currentVersor = Operand.ExhaustEvaluation(new Trim(new List <Operand>()
                {
                    new GeometricProduct(new List <Operand>()
                    {
                        currentVersor, unitVector.Copy()
                    })
                }), context);
            }

            versorFactorization.operandList.Add(currentVersor);

            return(versorFactorization);
        }
Beispiel #13
0
        // Note that here that we do not consider unary operator precedence.
        // So for example, if we have -1~, we don't try to choose between (-1)~ and -(1~),
        // though both are the same in this particular case.  Also, we don't recognize unary
        // operator stacking.  E.g., -~1 will not parse as -(~1) would.  In short, working with
        // unary operators will sometimes requires parenthesis.
        public Operand BuildOperandTree(List <Token> tokenList)
        {
            while (tokenList.Count > 0)
            {
                int count = tokenList.Count;

                if (tokenList[0].kind == Token.Kind.LEFT_PARAN && tokenList[0].paranType == Token.ParanType.ROUND)
                {
                    int i = FindMatchingParan(tokenList, 0);
                    if (i == tokenList.Count - 1)
                    {
                        tokenList.RemoveAt(0);
                        tokenList.RemoveAt(tokenList.Count - 1);
                    }
                }

                if (tokenList.Count == count)
                {
                    break;
                }
            }

            if (tokenList.Count == 0)
            {
                throw new ParseException("Encountered empty token list.");
            }

            if (tokenList.Count == 1)
            {
                Token token = tokenList[0];
                switch (token.kind)
                {
                case Token.Kind.SYMBOL:
                {
                    if (token.data[0] == '@')
                    {
                        return(new Variable(token.data.Substring(1)));
                    }

                    if (token.data[0] == '$')
                    {
                        return(new SymbolicScalarTerm(token.data.Substring(1)));
                    }

                    string vectorName    = token.data;
                    bool   isBasisVector = false;

                    List <string> basisVectorList = context.ReturnBasisVectors();
                    if (basisVectorList != null)
                    {
                        isBasisVector = basisVectorList.Contains(vectorName);
                        if (basisVectorsOnly && !isBasisVector)
                        {
                            Sum sum = new Sum();

                            foreach (string basisVectorName in basisVectorList)
                            {
                                InnerProduct dot = new InnerProduct(new List <Operand>()
                                    {
                                        new Blade(vectorName), new Blade(basisVectorName)
                                    });
                                sum.operandList.Add(new GeometricProduct(new List <Operand>()
                                    {
                                        dot, new Blade(basisVectorName)
                                    }));
                            }

                            return(sum);
                        }
                    }

                    generatedSymbolicVector = !isBasisVector;
                    return(new Blade(vectorName));
                }

                case Token.Kind.NUMBER:
                {
                    double value;
                    if (!double.TryParse(token.data, out value))
                    {
                        throw new ParseException(string.Format("Encountered non-parsable number ({0}).", token.data));
                    }

                    return(new NumericScalar(value));
                }

                default:
                {
                    throw new ParseException(string.Format("Encountered lone token ({0}) that isn't handled.", token.data));
                }
                }
            }
            else if (tokenList[0].kind == Token.Kind.OPERATOR && (ParansMatch(tokenList, 1, tokenList.Count - 1) || tokenList.Count == 2 || IsFunctionPattern(tokenList.Skip(1).ToList())))
            {
                Token token = tokenList[0];

                if (token.data == "-")
                {
                    return(new GeometricProduct(new List <Operand>()
                    {
                        new Blade(-1.0), BuildOperandTree(tokenList.Skip(1).ToList())
                    }));
                }

                throw new ParseException(string.Format("Encounterd unary operator ({0}) that isn't recognized on the left.", token.data));
            }
            else if (tokenList[tokenList.Count - 1].kind == Token.Kind.OPERATOR && (ParansMatch(tokenList, 0, tokenList.Count - 2) || tokenList.Count == 2 || IsFunctionPattern(tokenList.Take(tokenList.Count - 1).ToList())))
            {
                Token token = tokenList[tokenList.Count - 1];

                if (token.data == "~")
                {
                    return(new Reverse(new List <Operand>()
                    {
                        BuildOperandTree(tokenList.Take(tokenList.Count - 1).ToList())
                    }));
                }

                throw new ParseException(string.Format("Encountered unary operator ({0}) that isn't recognized on the right.", token.data));
            }
            else if (IsFunctionPattern(tokenList))
            {
                Token token = tokenList[0];

                Operation operation = context.CreateFunction(token.data);
                if (operation == null)
                {
                    throw new ParseException(string.Format("Encountered unknown function \"{0}\".", token.data));
                }

                List <List <Token> > argumentList = ParseListOfTokenLists(tokenList.Skip(2).Take(tokenList.Count - 3).ToList());
                foreach (List <Token> subTokenList in argumentList)
                {
                    operation.operandList.Add(BuildOperandTree(subTokenList));
                }

                return(operation);
            }
            else if (tokenList[0].paranType == Token.ParanType.SQUARE && ParansMatch(tokenList, 0, tokenList.Count - 1))
            {
                List <List <Operand> > listOfOperandLists = new List <List <Operand> >();

                List <List <Token> > rowList = ParseListOfTokenLists(tokenList.Skip(1).Take(tokenList.Count - 2).ToList());
                foreach (List <Token> rowTokenList in rowList)
                {
                    listOfOperandLists.Add(new List <Operand>());

                    List <List <Token> > colList;
                    if (rowTokenList[0].paranType == Token.ParanType.SQUARE && ParansMatch(rowTokenList, 0, rowTokenList.Count - 1))
                    {
                        colList = ParseListOfTokenLists(rowTokenList.Skip(1).Take(rowTokenList.Count - 2).ToList());
                    }
                    else
                    {
                        colList = new List <List <Token> >()
                        {
                            rowTokenList
                        }
                    };

                    foreach (List <Token> subTokenList in colList)
                    {
                        listOfOperandLists[listOfOperandLists.Count - 1].Add(BuildOperandTree(subTokenList));
                    }
                }

                return(new Matrix(listOfOperandLists));
            }
            else
            {
                // Our goal here is to find an operator of lowest precedence.  It will never be
                // at the very beginning or end of the entire token sequence.
                List <Token> opTokenList = null;
                foreach (Token token in WalkTokensSkipSubexpressions(tokenList))
                {
                    if (token.kind != Token.Kind.OPERATOR)
                    {
                        continue;
                    }

                    // Only unary operators can be at the start or end of the token list.
                    if (tokenList.IndexOf(token) == 0 || tokenList.IndexOf(token) == tokenList.Count - 1)
                    {
                        continue;
                    }

                    // Ignore unary operators on left.
                    if (token.data == "-" && tokenList[tokenList.IndexOf(token) - 1].kind == Token.Kind.OPERATOR)
                    {
                        continue;
                    }

                    // Ignore unary operators on right.
                    if (token.data == "~" && tokenList[tokenList.IndexOf(token) + 1].kind == Token.Kind.OPERATOR)
                    {
                        continue;
                    }

                    // At this point we should be reasonably sure it's a binary operator we're looking at.
                    if (opTokenList == null || PrecedenceLevel(opTokenList[0].data) > PrecedenceLevel(token.data))
                    {
                        opTokenList = new List <Token>()
                        {
                            token
                        }
                    }
                    ;
                    else if (opTokenList != null && PrecedenceLevel(opTokenList[0].data) == PrecedenceLevel(token.data))
                    {
                        opTokenList.Add(token);
                    }
                }

                if (opTokenList == null)
                {
                    throw new ParseException("Did not encounter binary operator token.");
                }

                Token operatorToken = null;
                switch (OperatorAssociativity(opTokenList[0].data))
                {
                case Associativity.LEFT_TO_RIGHT:
                    operatorToken = opTokenList[opTokenList.Count - 1];
                    break;

                case Associativity.RIGHT_TO_LEFT:
                    operatorToken = opTokenList[0];
                    break;
                }

                Operation operation = null;

                if (operatorToken.data == ";")
                {
                    operation = new Sequence();
                }
                else if (operatorToken.data == "+" || operatorToken.data == "-")
                {
                    operation = new Sum();
                }
                else if (operatorToken.data == "*" || operatorToken.data == "/")
                {
                    operation = new GeometricProduct();
                }
                else if (operatorToken.data == ".")
                {
                    operation = new InnerProduct();
                }
                else if (operatorToken.data == "^")
                {
                    operation = new OuterProduct();
                }
                else if (operatorToken.data == "=")
                {
                    operation = new Assignment();
                }
                else if (operatorToken.data == ":=")
                {
                    operation = new Assignment(false);
                }

                if (operation == null)
                {
                    throw new ParseException(string.Format("Did not recognized operator token ({0}).", operatorToken.data));
                }

                int i = tokenList.IndexOf(operatorToken);

                Operand leftOperand  = BuildOperandTree(tokenList.Take(i).ToList());
                Operand rightOperand = BuildOperandTree(tokenList.Skip(i + 1).Take(tokenList.Count - 1 - i).ToList());

                operation.operandList.Add(leftOperand);

                if (operatorToken.data == "-")
                {
                    operation.operandList.Add(new GeometricProduct(new List <Operand>()
                    {
                        new NumericScalar(-1.0), rightOperand
                    }));
                }
                else if (operatorToken.data == "/")
                {
                    operation.operandList.Add(new Inverse(new List <Operand>()
                    {
                        rightOperand
                    }));
                }
                else
                {
                    operation.operandList.Add(rightOperand);
                }

                return(operation);
            }
        }