public BladeDecomposition AnalyzeBlade(Operand operand, Context context)
        {
            BladeDecomposition decomposition;

            decomposition.weight       = null;
            decomposition.center       = null;
            decomposition.radius       = null;
            decomposition.normal       = null;
            decomposition.analysisList = new List <string>();

            using (var pushPopper = new ContextPushPopper(context))
            {
                int grade = operand.Grade;
                if (grade < 0)
                {
                    decomposition.analysisList.Add("Could not identify grade of given element.");
                }
                else
                {
                    OuterProduct blade = null;

                    try
                    {
                        blade = FactorBlade.Factor(operand, context);
                    }
                    catch (MathException)
                    {
                    }

                    if (blade == null)
                    {
                        decomposition.analysisList.Add("The given element was not a blade.");
                    }
                    else
                    {
                        context.operandStorage.SetStorage("__blade__", operand);
                        Evaluate("del(@weight, @center, @radius, @normal)", context);

                        switch (grade)
                        {
                        case 0:
                        {
                            if (operand.IsAdditiveIdentity)
                            {
                                decomposition.analysisList.Add("The blade is all of space.");
                            }
                            else
                            {
                                decomposition.analysisList.Add("The blade is the empty set.");
                            }
                            break;
                        }

                        case 1:
                        {
                            decomposition.weight = Evaluate("@weight = mag(ni . @__blade__)", context).output;
                            if (decomposition.weight.IsAdditiveIdentity)
                            {
                                decomposition.analysisList.Add("The blade is a plane.");
                                decomposition.weight = Evaluate("@weight = mag(no . ni ^ @__blade__)", context).output;
                                Evaluate("@__blade__ = @__blade__ / @weight", context);
                                decomposition.normal = Evaluate("@normal = -no . ni ^ @__blade__", context).output;
                                decomposition.center = Evaluate("@center = (-no . @__blade__) * @normal", context).output;
                            }
                            else
                            {
                                Evaluate("@__blade__ = @__blade__ / @weight", context);
                                decomposition.center = Evaluate("@center = ni^no . ni^no ^ @__blade__", context).output;
                                Operand squareRadius = Evaluate("@__square_radius__ = @__blade__ . @__blade__", context).output;
                                if (squareRadius.IsAdditiveIdentity)
                                {
                                    decomposition.analysisList.Add("The blade is a point.");
                                }
                                else if (!(squareRadius is NumericScalar numericScalar))
                                {
                                    decomposition.analysisList.Add("The blade is a sphere.");
                                    decomposition.radius = Evaluate("@radius = sqrt(@__square_radius__)", context).output;
                                }
        public VersorDecomposition AnalyzeVersor(Operand operand, Context context)
        {
            VersorDecomposition decomposition;

            decomposition.analysisList = new List <string>();

            using (var pushPopper = new ContextPushPopper(context))
            {
                int grade = operand.Grade;
                if (grade == 0)
                {
                    if (operand.IsAdditiveIdentity)
                    {
                        decomposition.analysisList.Add("Zero is not a versor because it is not invertible.");
                    }
                    else
                    {
                        decomposition.analysisList.Add("Non-zero scalars act as the identity transformation.");
                    }
                }
                else
                {
                    GeometricProduct versor = null;

                    try
                    {
                        versor = FactorVersor.Factor(operand, context);
                    }
                    catch (MathException)
                    {
                    }

                    if (versor == null)
                    {
                        decomposition.analysisList.Add("The given element was not a versor.");
                    }
                    else
                    {
                        // The reflections and rotations of conformal space give us the conformal transformations in the embedded 3D euclidean sub-space.
                        int i = versor.operandList.Count - 1;
                        while (i >= 0)
                        {
                            if (i - 1 >= 0 && AnalyzeRotation(versor.operandList[i - 1], versor.operandList[i], ref decomposition, context))
                            {
                                i -= 2;
                            }
                            else if (AnalyzeReflection(versor.operandList[i], ref decomposition, context))
                            {
                                i--;
                            }
                            else
                            {
                                decomposition.analysisList.Add("Failed to recognize transformation performed by vector.");
                                i--;
                            }
                        }
                    }
                }
            }

            return(decomposition);
        }