/*
         * Differentiates one-variable functions with the substitution rule, which works as follows:
         * First it computes the outer derivative.
         * This is the derivative of the function with the argument substituted for a single variable.
         * Then it multiplies this with the derivative of the substituted argument.
         */
        public static string DifferentiateFunction(string functionName, string argument)
        {
            var outerDerivative = functionName switch
            {
                "sin" => StringMath.ToPrefixFormat(argument, "cos"),
                "cos" => StringMath.MultiplyStrings("-1", StringMath.ToPrefixFormat(argument, "sin")),
                "tan" => StringMath.DivideStrings("1", StringMath.PowerStrings(StringMath.ToPrefixFormat(argument, "cos"), "2")),
                "ln" => StringMath.DivideStrings("1", argument),
                "exp" => StringMath.ToPrefixFormat(argument, "exp"),
                _ => throw new FormatException(),
            };

            return(StringMath.MultiplyStrings(Differentiate(argument), outerDerivative));
        }
        /*
         * Differentiates an operator expression. Uses rules for operands being general diffentiable functions.
         * +/-: Differentiation linear over +/-
         * *: Chain rule
         *./: https://www.wolframalpha.com/input/?i=differentiate+f%28x%29%2Fg%28x%29
         * ^: https://www.wolframalpha.com/input/?i=differentiate+f%28x%29%5Eg%28x%29 (using expanded form)
         */
        public static string DifferentiateOperator(string operatorSymbol, string arguments)
        {
            string[] operands = DifferentiationTokenizer.SplitArguments(arguments);

            string firstDerivative  = Differentiate(operands[0]);
            string secondDerivative = Differentiate(operands[1]);

            switch (operatorSymbol[0])
            {
            case '+':
            case '-':
                return(StringMath.AddStrings(firstDerivative, secondDerivative, operatorSymbol[0]));

            case '*':
                return(StringMath.AddStrings(StringMath.MultiplyStrings(firstDerivative, operands[1]), StringMath.MultiplyStrings(secondDerivative, operands[0])));

            case '/':
                return(StringMath.DivideStrings(
                           StringMath.AddStrings(
                               StringMath.MultiplyStrings(firstDerivative, operands[1]),
                               StringMath.MultiplyStrings(secondDerivative, operands[0]), '-'),
                           StringMath.PowerStrings(operands[1], "2")));

            case '^':
                return(StringMath.AddStrings(
                           StringMath.MultiplyStrings(
                               StringMath.MultiplyStrings(firstDerivative, operands[1]),
                               StringMath.PowerStrings(operands[0], StringMath.AddStrings(operands[1], "1", '-'))),
                           StringMath.MultiplyStrings(
                               StringMath.MultiplyStrings(secondDerivative, StringMath.LogarithmString(operands[0])),
                               StringMath.PowerStrings(operands[0], operands[1])
                               )
                           ));

            default:
                throw new FormatException();
            }
        }