private static Node Max(FunctionNode f, IReadOnlyList <Node> dargs)
        {
            dargs.Check(2);
            Node result = Node.Conditional(Node.GreaterThan(f.Arguments[0], f.Arguments[1]), dargs[0] ?? Node.Zero, dargs[1] ?? Node.Zero);

            return(result);
        }
        private static Node Limit(FunctionNode f, IReadOnlyList <Node> dargs)
        {
            dargs.Check(3);

            var x = f.Arguments[0];
            var y = f.Arguments[1];
            var z = f.Arguments[2];

            Node result = null;

            if (dargs[0] != null)
            {
                result = Node.Conditional(
                    Node.And(
                        Node.GreaterThan(x, Node.Function("min", y, z)),
                        Node.LessThan(x, Node.Function("max", y, z))),
                    dargs[0],
                    Node.Zero);
            }

            if (dargs[1] != null)
            {
                result += Node.Conditional(
                    Node.And(
                        Node.LessThanOrEqual(y, z),
                        Node.LessThanOrEqual(x, Node.Function("min", y, z))),
                    dargs[1],
                    Node.Zero);
            }

            if (dargs[2] != null)
            {
                result += Node.Conditional(
                    Node.And(
                        Node.LessThan(y, z),
                        Node.GreaterThanOrEqual(x, Node.Function("max", y, z))),
                    dargs[2],
                    Node.Zero);
            }

            return(result);
        }
        /// <summary>
        /// Derives the specified node to the variables.
        /// </summary>
        /// <param name="node">The nodes.</param>
        /// <returns>The derived nodes, or <c>null</c> if all derivatives are zero.</returns>
        public virtual Dictionary <VariableNode, Node> Derive(Node node)
        {
            if (Variables == null)
            {
                return(null);
            }

            // We can skip anything that is constant!
            if ((node.Properties & NodeProperties.Constant) != 0)
            {
                return(null);
            }

            Dictionary <VariableNode, Node> a = null, b = null;

            switch (node)
            {
            case UnaryOperatorNode un:
                if (un.NodeType == NodeTypes.Not)
                {
                    return(null);
                }
                a = Derive(un.Argument);
                switch (un.NodeType)
                {
                case NodeTypes.Plus: return(Apply(a, n => Node.Plus(n)));

                case NodeTypes.Minus: return(Apply(a, n => Node.Minus(n)));
                }
                break;

            case BinaryOperatorNode bn:
                switch (bn.NodeType)
                {
                case NodeTypes.And:
                case NodeTypes.Or:
                case NodeTypes.Xor:
                case NodeTypes.GreaterThan:
                case NodeTypes.GreaterThanOrEqual:
                case NodeTypes.LessThan:
                case NodeTypes.LessThanOrEqual:
                case NodeTypes.Equals:
                case NodeTypes.NotEquals:
                    return(null);
                }

                a = Derive(bn.Left);
                b = Derive(bn.Right);
                switch (bn.NodeType)
                {
                case NodeTypes.Add: return(Combine(a, b, n1 => n1, n2 => n2));

                case NodeTypes.Subtract: return(Combine(a, b, n1 => n1, n2 => Node.Minus(n2), (n1, n2) => Node.Subtract(n1, n2)));

                case NodeTypes.Multiply: return(Combine(a, b, n1 => n1 * bn.Right, n2 => bn.Left * n2));

                case NodeTypes.Divide:
                    return(Combine(a, b,
                                   n1 => n1 / bn.Right,
                                   n2 => - (n2 / Node.Power(bn.Right, Node.Two)),
                                   (n1, n2) => (n1 * bn.Right - bn.Left * n2) / Node.Power(bn.Right, Node.Two)));

                case NodeTypes.Pow:
                    return(Combine(a, b,
                                   n1 => n1 * bn.Right * Node.Power(bn.Left, bn.Right - Node.One),
                                   n2 => n2 * Node.Function("log", bn.Left) * bn));

                case NodeTypes.Modulo:
                    return(Combine(a, b,
                                   n1 => n1,
                                   n2 => n2 * -bn,
                                   (n1, n2) => n1 - n2 * bn));
                }
                break;

            case TernaryOperatorNode tn:
                a = Derive(tn.IfTrue);
                b = Derive(tn.IfFalse);
                return(Combine(a, b,
                               n1 => Node.Conditional(tn.Condition, n1, Node.Zero),
                               n2 => Node.Conditional(tn.Condition, Node.Zero, n2),
                               (n1, n2) => Node.Conditional(tn.Condition, n1, n2)));

            case FunctionNode fn:
                if (fn.Arguments.Count == 0)
                {
                    return(null);
                }

                // Build the derived arguments
                var lst = new Dictionary <VariableNode, Node[]>(Variables.Comparer);
                for (var i = 0; i < fn.Arguments.Count; i++)
                {
                    var darg = Derive(fn.Arguments[i]);
                    if (darg != null)
                    {
                        foreach (var pair in darg)
                        {
                            if (!lst.TryGetValue(pair.Key, out var dargs))
                            {
                                dargs = new Node[fn.Arguments.Count];
                                lst.Add(pair.Key, dargs);
                            }
                            dargs[i] = pair.Value;
                        }
                    }
                }

                // If there are no derivative, let's just stop here
                if (lst.Count == 0)
                {
                    return(null);
                }

                // Give a chance to our function rules
                if (FunctionRules == null || !FunctionRules.TryGetValue(fn.Name, out var rule))
                {
                    rule = (fn, darg) => ChainRule(fn, darg, "d{0}({1})");
                }
                a = new Dictionary <VariableNode, Node>(Variables.Comparer);
                foreach (var pair in lst)
                {
                    var df = rule(fn, pair.Value);
                    if (df != null)
                    {
                        a.Add(pair.Key, df);
                    }
                }

                // If all derivatives are zero, return null
                return(a.Count > 0 ? a : null);

            case VariableNode vn:
                if (Variables.Contains(vn))
                {
                    a = new Dictionary <VariableNode, Node>(Variables.Comparer)
                    {
                        { vn, Node.One }
                    };
                    return(a);
                }
                return(null);

            case PropertyNode _:
            case ConstantNode _:
                return(null);
            }

            throw new Exception("Could not derive expression node {0}".FormatString(node));
        }