/*
         * (a @ (b @ c)) => (a @@ b @@ c)
         * (a @@ (b @ c)) => (a @@ b @@ c)
         * (a @@ (b @@ c)) => (a @@ b @@ c)
         * Same with flatten function.
         */
        private Node BasicMultiNodeConverter(Node node)
        {
            BiNode bin = node.TryToGetAsBiNode;

            if (bin != null)
            {
                if (bin.IsAssociative)
                {
                    return(BasicMultiNodeConverter(bin.ToMultiNode));
                }
            }
            MultiNode mul = node.TryToGetAsMultiNode;

            if (mul != null)
            {
                foreach (Node element in mul.Elements.ToArray())
                {
                    bin = element.TryToGetAsBiNode;
                    if (bin != null)
                    {
                        if (bin.Signature == mul.Signature)
                        {
                            mul.Elements.Remove(element);
                            mul.Elements.AddRange(new List <Node>()
                            {
                                bin.Left, bin.Right
                            });
                        }
                    }
                    MultiNode eMul = element.TryToGetAsMultiNode;
                    if (eMul != null)
                    {
                        if (eMul.Signature == mul.Signature)
                        {
                            mul.Elements.Remove(element);
                            mul.Elements.AddRange(eMul.Elements);
                        }
                    }
                }
            }
            return(node);
        }
        /*
         * (5 * 4 * a * 3) => (60 * a)
         */
        private Node BasicMultiMultiplicationConverter(Node node)
        {
            MultiNode mul = node.TryToGetAsMultiNode;

            if (mul != null)
            {
                if (mul.Signature == new MultiMultiplication(null).Signature)
                {
                    List <Node> elements   = new List <Node>();
                    double      multiplier = 1;

                    foreach (Node element in mul.Elements)
                    {
                        Value val = element.TryToGetAsValue;
                        if (val != null)
                        {
                            multiplier *= val.GetValue;
                        }
                        else
                        {
                            elements.Add(element);
                        }
                    }
                    if (multiplier == 0)
                    {
                        return(new Value(0));
                    }
                    else if (multiplier != 1)
                    {
                        elements.Add(new Value(multiplier));
                    }
                    return(new MultiMultiplication(elements));
                }
            }
            return(node);
        }
        /*
         * ((2 * a) + (3 * a) + (4 * a) + b) => ((9 * a) + b) (for all values and any number of elements)
         */
        private Node BasicMultiplicatorComposeConverter(Node node)
        {
            MultiNode mul = node.TryToGetAsMultiNode;

            // This converter converts only multisums
            if (mul == null)
            {
                return(node);
            }
            if (mul.Signature == new MultiSum(null).Signature)
            {
                // List of nodes that will appear in returned node as elements
                List <Node> components = new List <Node>();
                // Assistant list to avoid any changes in original list
                List <Node> elements = new List <Node>(mul.Elements);
                // As the elements list is changed during loop operation it is problematic to use foreach or for loop
                // This loop checks for duplicates and multiplied by some value duplicates in the list
                // The multipliers of the duplicates are sumed so all the duplicates can be replaced by one occurance with sumed multiplier
                // Every found duplicate and original are removed from the list, so, after checking whole list it should be empty
                while (elements.Count > 0)
                {
                    Node element = elements[0];
                    elements.Remove(element);
                    MultiNode submul     = element.TryToGetAsMultiNode;
                    double    multiplier = 1;
                    Node      value;
                    if (submul != null)
                    {
                        List <Node> submul_elements = new List <Node>(submul.Elements);
                        if (submul.Signature == new MultiMultiplication(null).Signature)
                        {
                            value = submul_elements.Find((x) => x.TryToGetAsValue != null);
                            if (value != null)
                            {
                                submul_elements.Remove(value);
                                multiplier = value.TryToGetAsValue.GetValue;
                                submul     = new MultiMultiplication(submul_elements);
                            }
                        }
                        element = submul;
                    }
                    for (int i = 0; i < elements.Count; i++)
                    {
                        Node      relement = elements[i];
                        MultiNode rsubmul  = relement.TryToGetAsMultiNode;
                        double    mult     = 1;
                        if (rsubmul != null)
                        {
                            List <Node> rsubmul_elements = new List <Node>(rsubmul.Elements);
                            if (rsubmul.Signature == new MultiMultiplication(null).Signature)
                            {
                                value = rsubmul_elements.Find((x) => x.TryToGetAsValue != null);
                                if (value != null)
                                {
                                    rsubmul_elements.Remove(value);
                                    mult    = value.TryToGetAsValue.GetValue;
                                    rsubmul = new MultiMultiplication(rsubmul_elements);
                                }
                                if (element.Compare(rsubmul))
                                {
                                    multiplier += mult;
                                    elements.Remove(relement);
                                    i--;
                                }
                            }
                        }
                        else if (element.Compare(relement))
                        {
                            multiplier += mult;
                            elements.Remove(relement);
                            i--;
                        }
                    }
                    if (multiplier != 1)
                    {
                        if (submul != null)
                        {
                            submul.Elements.Add(new Value(multiplier));
                            element = submul;
                        }
                        else
                        {
                            element = new MultiMultiplication(new List <Node>()
                            {
                                new Value(multiplier), element
                            });
                        }
                    }
                    components.Add(element);
                }
                return(new MultiSum(components));
            }
            return(node);
        }