/// <summary>
        /// Converts this collapsed product operation into a normal mathematical expression.
        /// </summary>
        public Expression ToExpression()
        {
            //If we have zero or less terms, we have no idea what the hell is going on
            if (this.Arity <= 0) throw new Exceptions.SimplexMathException("Unable to convert collapsed product operation to expression - invalid arity");

            //If we only have one term, just return that term
            if (this.Arity == 1) return this.ChildExpressions[0];

            //If we only have two terms, just return the product of those two terms
            if (this.Arity == 2)
            {
                if (!Propositions.IsNonValueInverse[this.ChildExpressions[0]] && Propositions.IsNonValueInverse[this.ChildExpressions[1]]) return (this.ChildExpressions[0] / (1 / this.ChildExpressions[1]));
                if (Propositions.IsNonValueInverse[this.ChildExpressions[0]] && !Propositions.IsNonValueInverse[this.ChildExpressions[1]]) return (this.ChildExpressions[1] / (1 / this.ChildExpressions[0]));
                if (Propositions.IsNonValueInverse[this.ChildExpressions[0]] && Propositions.IsNonValueInverse[this.ChildExpressions[1]]) return 1 / ((1 / this.ChildExpressions[0]) * (1 / this.ChildExpressions[1]));
                return (this.ChildExpressions[0] * this.ChildExpressions[1]);
            }

            //Otherwise, if we have more than two terms, we will return a new product of the first term
            //and the rest of the terms cast to a CPO which are converted back to an expression.
            var FirstTerm = this.ChildExpressions[0];
            var SecondTerm = new CollapsedProductOperation(this.ChildExpressions.GetRange(1, this.ChildExpressions.Count - 1).ToArray()).ToExpression();
            if (!Propositions.IsNonValueInverse[FirstTerm] && Propositions.IsNonValueInverse[SecondTerm]) return (FirstTerm / (1 / SecondTerm));
            if (Propositions.IsNonValueInverse[FirstTerm] && !Propositions.IsNonValueInverse[SecondTerm]) return (SecondTerm / (1 / FirstTerm));
            if (Propositions.IsNonValueInverse[FirstTerm] && Propositions.IsNonValueInverse[SecondTerm]) return 1 / ((1 / FirstTerm) * (1 / SecondTerm));
            return new Product(FirstTerm, SecondTerm);
        }
 /// <summary>
 /// Merges two collapsed product operations together into a single collapsed product operation.
 /// </summary>
 /// <param name="CPO1">The first collapsed product operation to merge</param>
 /// <param name="CPO2">The second collapsed product operation to merge</param>
 public static CollapsedProductOperation Merge(CollapsedProductOperation CPO1, CollapsedProductOperation CPO2)
 {
     return new CollapsedProductOperation(CPO1.ChildExpressions.Concat(CPO2.ChildExpressions).ToArray());
 }
        /// <summary>
        /// Tests the equality between two CPOs. Assumes associative and commutative rules.
        /// </summary>
        /// <param name="CPO1">The first CPO to test</param>
        /// <param name="CPO2">The second CPO to test</param>
        public static bool TestEquality(CollapsedProductOperation CPO1, CollapsedProductOperation CPO2)
        {
            //First, lets make sure our CPO's have the same number of terms (we will assume our CPOs are NOT reduced)
            if (CPO1.Arity != CPO2.Arity)
            {
                //If reducing our CPOs makes them have the same number of terms, test the reduced CPOs
                var CPO1r = CPO1.Reduce();
                var CPO2r = CPO2.Reduce();
                if (CPO1r.Arity == CPO2r.Arity) return TestEquality(CPO1r, CPO2r);
                else return false;
            }

            //We will use a dictionary object to store our records of matches (since product operations can contain multiples of the same expression)
            //This makes it so that the test for {x, y, x, 1} == {x, y, z, 1} would not pass.
            Dictionary<int, int> IndexMatches = new Dictionary<int, int>(CPO1.ChildExpressions.Count);

            //For each item in the first CPO:
            for (int i = 0; i < CPO1.ChildExpressions.Count; i++)
            {
                //For each item in the second CPO:
                for (int j = 0; j < CPO2.ChildExpressions.Count; j++)
                {
                    //If these two expressions are equal:
                    if (CPO1.ChildExpressions[i] == CPO2.ChildExpressions[j])
                    {
                        //If the index of the expression in CSO2 has not been matched to something in CSO1 AND this index hasn't been matched to anything yet:
                        if (!IndexMatches.ContainsValue(j) && !IndexMatches.ContainsKey(i)) IndexMatches.Add(i, j);
                    }
                }

                //When we are done going through CSO2, if we didn't find a unique match, then return false
                if (!IndexMatches.ContainsKey(i)) return false;
            }

            //If we didn't find any conflicts, return true
            return true;
        }