Skip to content
Snippets Groups Projects
Select Git revision
  • eff85a174a6532fa1ac8d577944dbcc40d73e8cc
  • main default protected
  • edit_json
3 results

edit_json.py

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    Tests.fs 58.19 KiB
    module Tests
    
    #if INTERACTIVE
    #load "Interactive.fsx"
    #endif
    
    open NUnit.Framework
    open FsUnit
    open System.Collections.Generic
    open MathNet.Numerics
    open MathNet.Symbolics
    
    open Operators
    
    // Test: x should evaluate to expected
    let inline (-->) x expected = x |> should equal expected
    
    // Test: x should evaluate to the expected string when formatted *nicely*
    let inline (==>) x expected = Infix.format x |> should equal expected
    
    // Test: x should evaluate to the expected string when formatted *strictly* (not denormalized)
    let inline (===>) x expected = Infix.formatStrict x |> should equal expected
    
    // extra test helpers for tuples, list, arrays and hash-sets - maybe there's a better way?
    let inline (==|>) (x1, x2) expected = (Infix.format x1, Infix.format x2) |> should equal expected
    let inline (==||>) (x1, x2, x3) expected = (Infix.format x1, Infix.format x2, Infix.format x3) |> should equal expected
    let inline (==+>) x expected = List.iter2 (fun x e -> Infix.format x |> should equal e) x expected
    let inline (==->) x expected = Array.iter2 (fun x e -> Infix.format x |> should equal e) x expected
    let inline (==*>) (x:HashSet<Expression>) (expected:string list) = HashSet(expected).SetEquals(x |> Seq.map Infix.format) |> should be True
    
    // extra test helper for MathML (just normalizing XML, really)
    let inline (==/>) (x:string) expected = x |> should equal (Xml.normalizeString expected)
    
    // variables
    let x = symbol "x"
    let y = symbol "y"
    let z = symbol "z"
    let a = symbol "a"
    let b = symbol "b"
    let c = symbol "c"
    let d = symbol "d"
    let e = symbol "e"
    let f = symbol "f"
    
    [<Test>]
    let ``Number Expressions`` () =
    
        // equivalent:
        number 3 ==> "3"
        3Q ==> "3"
    
        // expressions are not comparable (NoComparison) to prevent errors,
        // but if the expressions are numbers we can use compareNumber:
        Numbers.compare 0Q 1Q --> -1
        Numbers.compare 1Q 1Q --> 0
        Numbers.compare 1Q 2Q --> -1
        Numbers.compare 0Q (1Q/2Q) --> -1
        Numbers.compare 1Q (1Q/2Q) --> 1
        Numbers.compare (1Q/2Q) 0Q --> 1
        Numbers.compare (1Q/2Q) 1Q --> -1
        Numbers.compare 1Q infinity --> -1
        Numbers.compare 1Q complexInfinity --> -1
        Numbers.compare 1Q negativeInfinity --> 1
        Numbers.compare infinity 1Q --> 1
        Numbers.compare complexInfinity 1Q --> 1
        Numbers.compare negativeInfinity 1Q --> -1
        Numbers.compare negativeInfinity infinity --> -1
        Numbers.compare infinity negativeInfinity --> 1
    
        Numbers.max [ 2Q; 4Q; 7Q/2 ] --> 4Q
        Numbers.max [ 2Q; 4Q; 9Q/2 ] --> 9Q/2
        Numbers.max [ -2Q; -4Q; -7Q/2 ] --> -2Q
    
        Numbers.gcd [ 4Q; 6Q; 10Q ] --> 2Q
        Numbers.lcm [ 4Q; 6Q; 10Q ] --> 60Q
    
        number 2 + number 5 ==> "7"
        2 * number 2 ==> "4"
    
    
    [<Test>]
    let ``Constant Expressions`` () =
    
        Expression.Pi ==> "π"
        Expression.E ==> "e"
        Expression.I ==> "j"
        Expression.Real(1.23) ==> "1.23"
        Expression.Real(-0.23) ==> "-0.23"
    
        real 1.1 + real 2.2 ==> "3.3"
        real 1.1 * real 2.2 ==> "2.42"
    
        2 * real 2.0 ==> "4"
    
        x + 2*x ==> "3*x"
        x + 2.2*x ==> "3.2*x"
    
    
    [<Test>]
    let ``Real Infinity Expressions`` () =
    
        infinity ==> "∞"
        -infinity ==> "-∞"
        infinity + infinity ==> "∞"
        infinity - infinity ==> "Undefined"
        -infinity - infinity ==> "-∞"
        2*infinity ==> "∞"
        -2*infinity ==> "-∞"
        infinity + 2Q ==> "∞"
        infinity*infinity ==> "∞"
        infinity*(-2*infinity) ==> "-∞"
    
    
    [<Test>]
    let ``Complex Infinity Expressions`` () =
    
        complexInfinity ==> "⧝"
        -complexInfinity ==> "⧝"
        complexInfinity + complexInfinity ==> "Undefined"
        complexInfinity - complexInfinity ==> "Undefined"
        2*complexInfinity ==> "⧝"
        complexInfinity + 2Q ==> "⧝"
        complexInfinity*complexInfinity ==> "⧝"
        complexInfinity*(-2*complexInfinity) ==> "⧝"
        complexInfinity+x*complexInfinity ==> "Undefined"
        complexInfinity*(x+complexInfinity) ==> "⧝"
    
        complexInfinity + infinity ==> "Undefined"
        complexInfinity - infinity ==> "Undefined"
    
    
    [<Test>]
    let ``Zero, One, Infinity, and ComplexInfinity`` () =
    
        // Operators.add
    
        0Q + undefined ==> "Undefined"
        0Q + infinity ==> "∞"
        0Q + negativeInfinity ==> "-∞"
        0Q + complexInfinity ==> "⧝"
    
        infinity + undefined ==> "Undefined"
        infinity + 1Q ==> "∞"
        infinity - 1Q ==> "∞"
        infinity + infinity ==> "∞"
        infinity - infinity ==> "Undefined"
        infinity + complexInfinity ==> "Undefined"
        infinity - complexInfinity ==> "Undefined"
    
        negativeInfinity + undefined ==> "Undefined"
        negativeInfinity + 1Q ==> "-∞"
        negativeInfinity - 1Q ==> "-∞"
        negativeInfinity + infinity ==> "Undefined"
        negativeInfinity - infinity ==> "-∞"
        negativeInfinity + complexInfinity ==> "Undefined"
        negativeInfinity-complexInfinity ==> "Undefined"    
    
        complexInfinity + undefined ==> "Undefined"
        complexInfinity + 1Q ==> "⧝"
        complexInfinity - 1Q ==> "⧝"
        complexInfinity + infinity ==> "Undefined"
        complexInfinity - infinity ==> "Undefined"
        complexInfinity + complexInfinity ==> "Undefined"
        complexInfinity - complexInfinity ==> "Undefined"
    
        // Operators.abs
    
        abs undefined ==> "Undefined"
        abs infinity ==> "∞"
        abs negativeInfinity ==> "∞"
        abs complexInfinity ==> "∞"
        abs Expression.I ==> "1"
    
        // Operators.invert
    
        invert undefined ==> "Undefined"
        invert 0Q ==> "⧝"
        invert infinity ==> "0"
        invert negativeInfinity ==> "0"
        invert complexInfinity ==> "0"    
    
        // Operators.multiply
    
        0Q*undefined ==> "Undefined"
        0Q*infinity ==> "Undefined"
        0Q*negativeInfinity ==> "Undefined"
        0Q*complexInfinity ==> "Undefined"
    
        1Q*undefined ==> "Undefined"
        1Q*infinity ==> "∞"
        1Q*negativeInfinity ==> "-∞"
        1Q*complexInfinity ==> "⧝"
    
        (-1Q)*undefined ==> "Undefined"
        (-1Q)*infinity ==> "-∞"
        (-1Q)*negativeInfinity ==> "∞"
        (-1Q)*complexInfinity ==> "⧝"
    
        infinity*undefined ==> "Undefined"
        infinity*0Q ==> "Undefined"
        infinity*1Q ==> "∞"
        infinity*(-1Q) ==> "-∞"
        infinity*infinity ==> "∞"
        infinity*negativeInfinity ==> "-∞"
        infinity*complexInfinity ==> "⧝"
    
        negativeInfinity*undefined ==> "Undefined"
        negativeInfinity*0Q ==> "Undefined"
        negativeInfinity*1Q ==> "-∞"
        negativeInfinity*(-1Q) ==> "∞"
        negativeInfinity*infinity ==> "-∞"
        negativeInfinity*negativeInfinity ==> "∞"
        negativeInfinity*complexInfinity ==> "⧝"
    
        complexInfinity*undefined ==> "Undefined"
        complexInfinity*0Q ==> "Undefined"
        complexInfinity*1Q ==> "⧝"
        complexInfinity*(-1Q) ==> "⧝"
        complexInfinity*infinity ==> "⧝"
        complexInfinity*negativeInfinity ==> "⧝"
        complexInfinity*complexInfinity ==> "⧝"
    
        // Operators.divide
    
        0Q/undefined ==> "Undefined"
        0Q/0Q ==> "Undefined"
        0Q/1Q ==> "0"
        0Q/infinity ==> "0"
        0Q/negativeInfinity ==> "0"
        0Q/complexInfinity ==> "0"    
    
        1Q/undefined ==> "Undefined"
        1Q/0Q ==> "⧝"
        1Q/1Q ==> "1"
        1Q/(-1Q) ==> "-1"
        1Q/infinity ==> "0"
        1Q/negativeInfinity ==> "0"
        1Q/complexInfinity ==> "0"
    
        (-1Q)/undefined ==> "Undefined"
        (-1Q)/0Q ==> "⧝"
        (-1Q)/1Q ==> "-1"
        (-1Q)/(-1Q) ==> "1"
        (-1Q)/infinity ==> "0"
        (-1Q)/negativeInfinity ==> "0"
        (-1Q)/complexInfinity ==> "0"
    
        infinity/undefined ==> "Undefined"
        infinity/0Q ==> "⧝"
        infinity/1Q ==> "∞"
        infinity/(-1Q) ==> "-∞"
        infinity/infinity ==> "Undefined"
        infinity/negativeInfinity ==> "Undefined"
        infinity/complexInfinity ==> "Undefined"
    
        negativeInfinity/undefined ==> "Undefined"
        negativeInfinity/0Q ==> "⧝"
        negativeInfinity/1Q ==> "-∞"
        negativeInfinity/(-1Q) ==> "∞"
        negativeInfinity/infinity ==> "Undefined"
        negativeInfinity/negativeInfinity ==> "Undefined"
        negativeInfinity/complexInfinity ==> "Undefined"    
    
        complexInfinity/undefined ==> "Undefined"
        complexInfinity/0Q ==> "⧝"
        complexInfinity/1Q ==> "⧝"
        complexInfinity/(-1Q) ==> "⧝"
        complexInfinity/infinity ==> "Undefined"
        complexInfinity/negativeInfinity ==> "Undefined"
        complexInfinity/complexInfinity ==> "Undefined"
    
        // Operators.pow
    
        0Q**undefined ==> "Undefined"
        0Q**0Q ==> "Undefined"
        0Q**1Q ==> "0"
        0Q**2Q ==> "0"
        0Q**(-1Q) ==> "⧝"
        0Q**(-2Q) ==> "⧝"
        0Q**infinity ==> "0"
        0Q**negativeInfinity ==> "⧝"
        0Q**complexInfinity ==> "0"
    
        1Q**undefined ==> "Undefined"
        1Q**0Q ==> "1"
        1Q**1Q ==> "1"
        1Q**2Q ==> "1"
        1Q**(-1Q) ==> "1"
        1Q**(-2Q) ==> "1"
        1Q**infinity ==> "Undefined"
        1Q**negativeInfinity ==> "Undefined"
        1Q**complexInfinity ==> "Undefined"
    
        2Q**undefined ==> "Undefined"
        2Q**0Q ==> "1"
        2Q**1Q ==> "2"
        2Q**2Q ==> "4"
        2Q**(-1Q) ==> "1/2"
        2Q**(-2Q) ==> "1/4"
        2Q**infinity ==> "∞"
        2Q**negativeInfinity ==> "0"
        2Q**complexInfinity ==> "Undefined"
    
        (-1Q)**undefined ==> "Undefined"
        (-1Q)**0Q ==> "1"
        (-1Q)**1Q ==> "-1"
        (-1Q)**2Q ==> "1"
        (-1Q)**(-1Q) ==> "-1"
        (-1Q)**(-2Q) ==> "1"
        (-1Q)**infinity ==> "Undefined"
        (-1Q)**negativeInfinity ==> "Undefined"
        (-1Q)**complexInfinity ==> "Undefined"
    
        (-2Q)**undefined ==> "Undefined"
        (-2Q)**0Q ==> "1"
        (-2Q)**1Q ==> "-2"
        (-2Q)**2Q ==> "4"
        (-2Q)**(-1Q) ==> "-1/2"
        (-2Q)**(-2Q) ==> "1/4"
        (-2Q)**infinity ==> "⧝"
        (-2Q)**negativeInfinity ==> "0"
        (-2Q)**complexInfinity ==> "Undefined"
    
        infinity**undefined ==> "Undefined"
        infinity**0Q ==> "Undefined"
        infinity**1Q ==> "∞"
        infinity**(-1Q) ==> "0"
        infinity**(-2Q) ==> "0"
        infinity**infinity ==> "⧝"
        infinity**negativeInfinity ==> "0"
        infinity**complexInfinity ==> "Undefined"
    
        negativeInfinity**undefined ==> "Undefined"
        negativeInfinity**0Q ==> "Undefined"
        negativeInfinity**1Q ==> "-∞" 
        negativeInfinity**2Q ==> "∞"
        negativeInfinity**(-1Q) ==> "0" 
        negativeInfinity**(-2Q) ==> "0" 
        negativeInfinity**infinity ==> "⧝"
        negativeInfinity**negativeInfinity ==> "0"
        negativeInfinity**complexInfinity ==> "Undefined"
    
        complexInfinity**undefined ==> "Undefined"
        complexInfinity**0Q ==> "Undefined"
        complexInfinity**1Q ==> "⧝"
        complexInfinity**(-1Q) ==> "0"
        complexInfinity**(-2Q) ==> "0"
        complexInfinity**infinity ==> "⧝"
        complexInfinity**negativeInfinity ==> "0"
        complexInfinity**complexInfinity ==> "Undefined"
    
        root 0Q x ==> "Undefined"
        root infinity x ==> "1"
        x**(1/infinity) ==>"1"
        root negativeInfinity x ==> "1"
        root complexInfinity x ==> "1"
        root infinity 0Q ==> "Undefined"
        root negativeInfinity 0Q ==> "Undefined"
        root complexInfinity 0Q ==> "Undefined" // In WolframAlpha, root(complexInfinity, 0) returns 0
        0Q**(1/complexInfinity) ==> "Undefined"
    
        // Operators.exp
    
        exp undefined ==> "Undefined"
        exp infinity ==> "∞"
        exp negativeInfinity ==> "0"
        exp complexInfinity ==> "Undefined"
        exp 0Q ==> "1"
        exp 1Q ==> "e"
    
        // Operators.ln
    
        ln undefined ==> "Undefined"
        ln infinity ==> "∞"
        ln negativeInfinity ==> "∞"
        ln complexInfinity ==> "∞"
        ln 0Q ==> "-∞"
        ln 1Q ==> "0"
        ln Expression.E ==> "1"
    
        // Operators.log10
    
        log10 undefined ==> "Undefined"
        log10 infinity ==> "∞"
        log10 negativeInfinity ==> "∞"
        log10 complexInfinity ==> "∞"
        log10 0Q ==> "-∞"
        log10 1Q ==> "0"
        log10 10Q ==> "1"
    
        // Todo - Imaginary related
        //Expression.I*infinity ==> "∞*j"
        //0Q**Expression.I ==> "Undefined"
        1Q**Expression.I ==> "1"
    
    
    [<Test>]
    let ``Expressions are always in auto-simplified form`` () =
    
        // readable output is F# interactive thanks to Text.format printer added in MathNet.Symbolics.fsx
    
        x + y ==> "x + y"
        y + x ==> "x + y"
        x + x ==> "2*x"
        x + 2*x ==> "3*x"
        x + x*2 ==> "3*x"
        2*x + 3*x ==> "5*x"
        a*x + 2*x ==> "2*x + a*x"
        a*x + x*b ==> "a*x + b*x"
        b*x + x*a ==> "a*x + b*x"
        1 + x + y ==> "1 + x + y"
        x + 1 + y ==> "1 + x + y"
        x + y + 1 ==> "1 + x + y"
    
        x*y ==> "x*y"
        y*x ==> "x*y"
        x**2*x ==> "x^3"
        x*y*x**2 ==> "x^3*y"
        y*x*y**2 ==> "x*y^3"
        2*x*y ==> "2*x*y"
        x*2*y ==> "2*x*y"
        x*y*2 ==> "2*x*y"
    
        2*(a*b) ==> "2*a*b"
        (a*b)*2 ==> "2*a*b"
        a*b + a*b ==> "2*a*b"
        a*b + b*a ==> "2*a*b"
    
        a + b + c + a*b + a*c + b*c ==> "a + b + a*b + c + a*c + b*c"
        c*b + c*a + b*a + c + b + a ==> "a + b + a*b + c + a*c + b*c"
    
        a**2 + b**2 ==> "a^2 + b^2"
        b**2 + a**2 ==> "a^2 + b^2"
    
        a**2 + a**3 ==> "a^2 + a^3"
        a**3 + a**2 ==> "a^2 + a^3"
    
        a**2 * b**2 ==> "a^2*b^2"
        b**2 * a**2 ==> "a^2*b^2"
    
        (a+c)**2 + (a+b)**2 ==> "(a + b)^2 + (a + c)^2"
        (a+b)**2 + (a+c)**2 ==> "(a + b)^2 + (a + c)^2"
    
        (a+c)**2 * (a+b)**2 ==> "(a + b)^2*(a + c)^2"
        (a+b)**2 * (a+c)**2 ==> "(a + b)^2*(a + c)^2"
    
        (a+c) * (a+b) ==> "(a + b)*(a + c)"
        (a+b) * (a+c) ==> "(a + b)*(a + c)"
    
        (1+x)**2 + (1+x)**3 + (1+y)**2 ==> "(1 + x)^2 + (1 + x)^3 + (1 + y)^2"
        (1+x)**3 + (1+y)**2 + (1+x)**2 ==> "(1 + x)^2 + (1 + x)^3 + (1 + y)^2"
        (1+y)**2 + (1+x)**2 + (1+x)**3 ==> "(1 + x)^2 + (1 + x)^3 + (1 + y)^2"
    
        (a+b)*x ==> "(a + b)*x"
        (a+b)*x*y ==> "(a + b)*x*y"
        (a+b)*y*x ==> "(a + b)*x*y"
        (a+b)*(x*y) ==> "(a + b)*x*y"
        (a+b)*(y*x) ==> "(a + b)*x*y"
    
        x*x ==> "x^2"
        x*x**2*x**3 ==> "x^6"
        (x**2)**3 ==> "x^6"
    
        2*(x*y)*z**2 ==> "2*x*y*z^2"
        1*x*y*z**2 ==> "x*y*z^2"
        2*x*y*z*z**2 ==> "2*x*y*z^3"
    
        (-2) + (-3)*x + 5*y ==> "-2 - 3*x + 5*y"
        (-2.0) + (-3.0)*x + 5.0*y ==> "-2 - 3*x + 5*y"
        (2*x)/(3*y) ==> "(2*x)/(3*y)"
        (1*x)/(3*y) ==> "x/(3*y)"
        (a*x)/(3*y) ==> "(a*x)/(3*y)"
        (2*x)/(1*y) ==> "(2*x)/y"
        (2*x)/(b*y) ==> "(2*x)/(b*y)"
        (2)/(3*b*y) ==> "2/(3*b*y)"
        (1)/(3*b*y) ==> "1/(3*b*y)"
        (2*a*x)/(3) ==> "2/3*a*x"
        (1*a*x)/(3) ==> "1/3*a*x"
        (-2*x)/(3*y) ==> "-(2*x)/(3*y)"
        (-1*x)/(3*y) ==> "-x/(3*y)"
        (-a*x)/(3*y) ==> "-(a*x)/(3*y)"
        (-2*x)/(1*y) ==> "-(2*x)/y"
        (-2*x)/(b*y) ==> "-(2*x)/(b*y)"
        (-2)/(3*b*y) ==> "-2/(3*b*y)"
        (-1)/(3*b*y) ==> "-1/(3*b*y)"
        (-2*a*x)/(3) ==> "-2/3*a*x"
        (-1*a*x)/(3) ==> "-1/3*a*x"
    
        // There is no subtraction, negation or division in simplified expressions (strict):
        1 / x ===> "x^(-1)" // strict
        1 / x ==> "1/x" // nice
        -x ===> "(-1)*x"
        -x ==> "-x"
        2 + 1/x - 1 ===> "1 + x^(-1)"
        2 + 1/x - 1 ==> "1 + 1/x"
        -(-x) ===> "x"
        -(-x) ==> "x"
        1 / (1 / x) ===> "x"
        1 / (1 / x) ==> "x"
    
        2*x*3 ==> "6*x"
        -x*y/3 ===> "(-1/3)*x*y"
        -x*y/3 ==> "-1/3*x*y"
    
        ((x*y)**(1Q/2)*z**2)**2 ==> "x*y*z^4"
        (a/b/(c*a))*(c*d/a)/d ===> "a^(-1)*b^(-1)" // strict
        (a/b/(c*a))*(c*d/a)/d ==> "1/(a*b)" // nice
        a**(3Q/2)*a**(1Q/2) ==> "a^2"
    
        x + ln x ==> "x + ln(x)"
        x + ln (x+1) ==> "x + ln(1 + x)"
        x + log10 (x+1) ==> "x + log(1 + x)"
        x + (log x (x+1)) ==> "x + log(x,1 + x)"
        2*abs x ==> "2*|x|"
        x + abs (-x) ==> "x + |x|"
        abs (-3Q) ==> "3"
        exp 0Q ==> "1"
    
        sin x ==> "sin(x)"
    
    [<Test>]
    let ``Convert Expression to VisualExpression`` () =
    
        let style = DefaultVisualStyle()
        let convert = VisualExpression.fromExpression style
    
        convert (1Q) --> VisualExpression.PositiveInteger (bigint 1)
        convert (0Q) --> VisualExpression.PositiveInteger (bigint 0)
        convert (-1Q) --> VisualExpression.Negative (VisualExpression.PositiveInteger (bigint 1))
    
        convert (1Q/2) --> VisualExpression.Fraction ((VisualExpression.PositiveInteger (bigint 1)), (VisualExpression.PositiveInteger (bigint 2)))
        convert (-1Q/2) --> VisualExpression.Negative (VisualExpression.Fraction ((VisualExpression.PositiveInteger (bigint 1)), (VisualExpression.PositiveInteger (bigint 2))))
    
        convert (real 1.0) --> VisualExpression.PositiveFloatingPoint 1.0
        convert (real 0.0) --> VisualExpression.PositiveFloatingPoint 0.0
        convert (real -1.0) --> VisualExpression.Negative (VisualExpression.PositiveFloatingPoint 1.0)
    
        convert (PositiveInfinity) --> VisualExpression.Infinity
        convert (NegativeInfinity) --> VisualExpression.Negative VisualExpression.Infinity
        convert (ComplexInfinity) --> VisualExpression.ComplexInfinity
        convert (Undefined) --> VisualExpression.Undefined
    
        convert (-x*y) --> VisualExpression.Negative (VisualExpression.Product [ VisualExpression.Symbol "x"; VisualExpression.Symbol "y"])
        convert (x*(1-y)) --> VisualExpression.Product [VisualExpression.Symbol "x"; VisualExpression.Parenthesis (VisualExpression.Sum [ VisualExpression.PositiveInteger (bigint 1); VisualExpression.Negative (VisualExpression.Symbol "y")])]
    
        convert (abs x) --> VisualExpression.Abs (VisualExpression.Symbol "x")
        convert (-(abs x)) --> VisualExpression.Negative (VisualExpression.Abs (VisualExpression.Symbol "x"))
        convert (1-(abs x)) --> VisualExpression.Sum [ VisualExpression.PositiveInteger (bigint 1); VisualExpression.Negative (VisualExpression.Abs (VisualExpression.Symbol "x"))]
        convert (-1-(abs x)) --> VisualExpression.Sum [ VisualExpression.Negative (VisualExpression.PositiveInteger (bigint 1)); VisualExpression.Negative (VisualExpression.Abs (VisualExpression.Symbol "x"))]
        convert (abs (2*x)) --> VisualExpression.Abs (VisualExpression.Product [ VisualExpression.PositiveInteger (bigint 2); VisualExpression.Symbol "x"])
    
        convert (x**(2Q)) --> VisualExpression.Power (VisualExpression.Symbol "x", VisualExpression.PositiveInteger (bigint 2))
        convert (x**(-1Q)) --> VisualExpression.Fraction (VisualExpression.PositiveInteger (bigint 1), VisualExpression.Symbol "x")
        convert (x**(-2Q)) --> VisualExpression.Fraction (VisualExpression.PositiveInteger (bigint 1), VisualExpression.Power (VisualExpression.Symbol "x", VisualExpression.PositiveInteger (bigint 2)))
    
    
    [<Test>]
    let ``Print infix and LaTeX expressions`` () =
    
        Infix.format (1/(a*b)) --> "1/(a*b)"
        Infix.formatStrict (1/(a*b)) --> "a^(-1)*b^(-1)"
    
    
    [<Test>]
    let ``Parse infix expressions`` () =
    
        Infix.parseOrUndefined "-3" ==> "-3"
        Infix.parseOrUndefined "1234567890123456789012345678901234567890" ==> "1234567890123456789012345678901234567890"
        Infix.parseOrUndefined "x" ==> "x"
        Infix.parseOrUndefined "-x" ==> "-x"
        Infix.parseOrUndefined "x-" ==> "Undefined"
        Infix.parseOrUndefined "y*x" ==> "x*y"
        Infix.parseOrUndefined "  y  *  x  " ==> "x*y"
    
        Infix.parseOrUndefined "sin(x)" ==> "sin(x)"
        Infix.parseOrUndefined " sin (x) " ==> "sin(x)"
        Infix.parseOrUndefined " sin ( - x ) " ==> "-sin(x)"
        Infix.parseOrUndefined "sin x" ==> "Undefined"
        Infix.parseOrUndefined "sin x-1" ==> "Undefined"
        Infix.parseOrUndefined "sin -x" ==> "sin - x"
        Infix.parseOrUndefined "sin" ==> "sin"
    
        Infix.parseOrUndefined "atan(x,y)" ==> "atan(x,y)"
        Infix.parseOrUndefined "atan ( x , y )"  ==> "atan(x,y)"
        Infix.parseOrUndefined " atan ( - x, - y ) " ==> "atan(-x,-y)"
    
        Infix.parseOrUndefined "log(x)" ==> "log(x)"
        Infix.parseOrUndefined "log(x,y)" ==> "log(x,y)"
        Infix.parseOrUndefined "log(x,10)" ==> "log(x,10)"
    
        Infix.parseOrThrow "1/(a*b)" ==> "1/(a*b)"
        Infix.parseOrThrow "exp(a)^exp(b)" ==> "(exp(a))^(exp(b))"
        Infix.parseOrThrow "a^b^c" ==> "a^(b^c)"
        Infix.parseOrThrow "|a-2|-1" ==> "-1 + |-2 + a|"
    
        Infix.parseOrThrow "(y-1)*10 + 2" ==> "2 + 10*(-1 + y)"
    
        Infix.parseOrThrow "2*x^(2*y) + e^(3*y)" ==> "e^(3*y) + 2*x^(2*y)"
    
        Infix.parseOrThrow "15" ==> "15"
        Infix.parseOrThrow "1.5" ==> "1.5"
        Infix.parseOrThrow "0.25" ==> "0.25"
        Infix.parseOrThrow "0.0250" ==> "0.025"
        Infix.parseOrThrow "2.25" ==> "2.25"
        Infix.parseOrThrow "2.250" ==> "2.25"
        Infix.parseOrThrow "0.001" ==> "0.001"
        Infix.parseOrThrow "2.00" ==> "2"
    
        Infix.parseOrThrow "1.5*a + o" ==> "1.5*a + o"
    
        Infix.parseOrThrow ".001" ==> "0.001"
        Infix.parseOrThrow ".001" --> Expression.Real(0.001)
        Infix.parseOrThrow "1." ==> "1"
        Infix.parseOrThrow "1." --> Expression.Real(1.0)
        Infix.parseOrThrow "1" ==> "1"
        Infix.parseOrThrow "1" --> Expression.FromInt32(1)
    
        Infix.parseOrThrow "pi" --> Expression.Pi
        Infix.parseOrThrow "π" --> Expression.Pi
        Infix.parseOrThrow "∞" --> Expression.PositiveInfinity
        Infix.parseOrThrow "inf" --> Expression.PositiveInfinity
        Infix.parseOrThrow "-∞" --> Expression.NegativeInfinity
        Infix.parseOrThrow "⧝" --> Expression.ComplexInfinity
    
    
    [<Test>]
    [<TestCase("en-US"); TestCase("tr-TR"); TestCase("de-DE");TestCase("de-CH");TestCase("he-IL")>]
    let ``Culture Invariant Infix Expressions`` (cultureName:string) =
        let original = System.Threading.Thread.CurrentThread.CurrentCulture
        try
            System.Threading.Thread.CurrentThread.CurrentCulture <- System.Globalization.CultureInfo(cultureName)
            Infix.parseOrThrow "0.25 + 0.1" ==> "0.35"
        finally
            System.Threading.Thread.CurrentThread.CurrentCulture <- original
    
    
    [<Test>]
    let ``Print LaTeX expressions`` () =
    
        LaTeX.format (1/(a*b)) --> """\frac{1}{ab}"""
        LaTeX.format Expression.MinusOne --> """-1"""
        LaTeX.format Expression.ComplexInfinity --> """\infty"""
        LaTeX.format Expression.Pi --> """\pi"""
        LaTeX.format (Expression.Real -0.23) --> string -0.23
        LaTeX.format (a**b) --> """{a}^{b}"""
        LaTeX.format (a**(b+c)) --> """{a}^{b + c}"""
        LaTeX.format ((a+b)**c) --> """{\left(a + b\right)}^{c}"""
        LaTeX.format (a**(b**c)) --> """{a}^{{b}^{c}}"""
        LaTeX.format ((a**b)**c) --> """{\left({a}^{b}\right)}^{c}"""
        LaTeX.format (a*b*(symbol "def")) --> """ab{def}"""
    
        LaTeX.format (3Q*2Q**x) --> """3\cdot{2}^{x}"""
        LaTeX.format (3.0*(real 2.0)**x) --> """3\cdot{2}^{x}"""
        LaTeX.format (5Q*x) --> """5x"""
        LaTeX.format (Expression.Pi * 10Q) --> """10\pi"""
        LaTeX.format (Expression.E * 2Q**(4Q*x)) --> """e\cdot{2}^{4x}"""
        LaTeX.format (4Q * Expression.E ** x) --> """4{e}^{x}"""
    
        LaTeX.format (log10 x) --> """\log_{10}{x}"""
        LaTeX.format (log10 (x+y)) --> """\log_{10}\left(x + y\right)"""
        LaTeX.format (log 8Q y) --> """\log_{8}{y}"""
        LaTeX.format (log 8Q (x+y)) --> """\log_{8}\left(x + y\right)"""
        LaTeX.format (log (sin x) (tanh y)) --> """\log_{\sin{x}}{\tanh{y}}"""
        LaTeX.format (arctan x) --> """\arctan{x}"""
        LaTeX.format (arctan2 x (3Q*y)) --> """\operatorname{atan2}\left({x}, {3y}\right)"""
    
        LaTeX.format (sin (x+y)) --> """\sin\left(x + y\right)"""
        LaTeX.format (sin ((x+y) ** 2)) --> """\sin{{\left(x + y\right)}^{2}}"""
        LaTeX.format ((sin (x+y)) ** 2) --> """{\left(\sin\left(x + y\right)\right)}^{2}"""
        LaTeX.format ((sin x)*(cos x)+(tan x)) --> """\sin{x}\cos{x} + \tan{x}"""
        LaTeX.format ((sin (x+y))*(cos (x+y))+(tan (x+y))) --> """\sin\left(x + y\right)\cos\left(x + y\right) + \tan\left(x + y\right)"""
    
        LaTeX.format (x**(1Q/2)) --> "\sqrt{x}"
        LaTeX.format (x**(1Q/3)) --> "\sqrt[3]{x}"
    
    
    [<Test>]
    let ``Format MathML3 Strict Content`` () =
    
        MathML.formatContentStrict 1Q ==/> """<cn>1</cn>"""
        MathML.formatContentStrict -1Q ==/> """<cn>-1</cn>"""
        MathML.formatContentStrict (1Q/2) ==/> """<apply><csymbol cd="nums1">rational</csymbol><cn>1</cn><cn>2</cn></apply>"""
        MathML.formatContentStrict x ==/> """<ci>x</ci>"""
        MathML.formatContentStrict -x ==/> """<apply><csymbol cd="arith1">unary_minus</csymbol><ci>x</ci></apply>"""
        MathML.formatContentStrict (-2*x) ==/> """<apply><csymbol cd="arith1">unary_minus</csymbol><apply><csymbol cd="arith1">times</csymbol><cn>2</cn><ci>x</ci></apply></apply>"""
        MathML.formatContentStrict pi ==/> """<csymbol cd="nums1">pi</csymbol>"""
        MathML.formatContentStrict (1/x) ==/> """<apply><csymbol cd="arith1">divide</csymbol><cn>1</cn><ci>x</ci></apply>"""
        MathML.formatContentStrict (1/(a*b)) ==/> """<apply><csymbol cd="arith1">divide</csymbol><cn>1</cn><apply><csymbol cd="arith1">times</csymbol><ci>a</ci><ci>b</ci></apply></apply>"""
        MathML.formatContentStrict (x**2) ==/> """<apply><csymbol cd="arith1">power</csymbol><ci>x</ci><cn>2</cn></apply>"""
        MathML.formatContentStrict (x**(1Q/2)) ==/> """<apply><csymbol cd="arith1">root</csymbol><ci>x</ci><cn>2</cn></apply>"""
        MathML.formatContentStrict (x**(1Q/3)) ==/> """<apply><csymbol cd="arith1">root</csymbol><ci>x</ci><cn>3</cn></apply>"""
    
    
    [<Test>]
    let ``Format MathML3 Strict Content with Annotations`` () =
    
        MathML.formatSemanticsAnnotated (1/x) ==/> """
          <semantics>
            <apply><csymbol cd="arith1">divide</csymbol><cn>1</cn><ci>x</ci></apply>
            <annotation encoding="application/x-tex">\frac{1}{x}</annotation>
            <annotation encoding="application/x-mathnet-infix">1/x</annotation>
          </semantics>"""
    
    
    [<Test>]
    let ``Parse MathML3 Strict Content`` () =
    
        MathML.parse """<ci>x</ci>""" ==> "x"
        MathML.parse """<cn>1</cn>""" ==> "1"
        MathML.parse """<csymbol cd="nums1">pi</csymbol>""" ==> "π"
        MathML.parse """<apply> <csymbol cd="nums1">rational</csymbol> <cn>1</cn> <cn>2</cn> </apply>""" ==> "1/2"
        MathML.parse """<apply><csymbol cd="arith1">divide</csymbol><cn>1</cn><ci>x</ci></apply>""" ==> "1/x"
        MathML.parse """<apply><csymbol cd="arith1">divide</csymbol><cn>1</cn><apply><csymbol cd="arith1">times</csymbol><ci>a</ci><ci>b</ci></apply></apply>""" ==> "1/(a*b)"
        MathML.parse """<apply><csymbol cd="arith1">power</csymbol><ci>x</ci><cn>2</cn></apply>""" ==> "x^2"
        MathML.parse """<apply><csymbol cd="arith1">root</csymbol><ci>x</ci><cn>2</cn></apply>""" ==> "sqrt(x)"
    
    
    [<Test>]
    let ``Parse MathML Non-Strict Content`` () =
    
        MathML.parse """<apply><divide/><cn>1</cn><ci>x</ci></apply>""" ==> "1/x"
        MathML.parse """<apply><divide/><cn>1</cn><apply><times/><ci>a</ci><ci>b</ci></apply></apply>""" ==> "1/(a*b)"
        MathML.parse """<apply><power/><ci>x</ci><cn>2</cn></apply>""" ==> "x^2"
        MathML.parse """<apply><root/><degree><cn>2</cn></degree><ci>x</ci></apply>""" ==> "sqrt(x)"
        MathML.parse """<apply><root/><degree><cn>3</cn></degree><ci>x</ci></apply>""" ==> "x^(1/3)"
    
    
    [<Test>]
    let ``Parse F# quotations`` () =
    
        Quotations.parse <@ 3 @> ==> "3"
        Quotations.parse <@ x @> ==> "x"
        Quotations.parse <@ fun x -> x @> ==> "x"
        Quotations.parse <@ 3/4 @> ==> "3/4"
        Quotations.parse <@ fun x -> 3/x @> ==> "3/x"
        Quotations.parse <@ -x*y/3 @> ==> "-1/3*x*y"
        Quotations.parse <@ fun x y -> -x*y/3 @> ==> "-1/3*x*y"
        Quotations.parse <@ fun (x, y) -> -x*y/3 @> ==> "-1/3*x*y"
    
    
    [<Test>]
    let ``Algebraic Expansion`` () =
    
        // Auto-simplification does not expand expressions:
        (a+b)-(a+b) ==> "a + b - (a + b)"
        (a+b)-(a+b) |> Algebraic.expand ==> "0"
        2*(a+b)-(a+b) ==> "a + b"
        (a+b)-2*(a+b) |> Algebraic.expand ===> "(-1)*a + (-1)*b"
        (a+b)-2*(a+b) |> Algebraic.expand ==> "-a - b"
    
        (a*b)/(b*a) ==> "1"
        (a*b)**2/(b*a) ==> "a*b"
        (a*b)/(b*a)**2 ==> "1/(a*b)"
    
        (a+b)/(b+a) ==> "1"
        (a+b)**2/(b+a) ==> "a + b"
        (a+b)/(b+a)**2 ==> "1/(a + b)"
    
        (x*(y+1)**(3Q/2)+1)*(x*(y+1)**(3Q/2)-1) ==> "(-1 + x*(1 + y)^(3/2))*(1 + x*(1 + y)^(3/2))"
        (x*(y+1)**(3Q/2)+1)*(x*(y+1)**(3Q/2)-1) |> Algebraic.expand |> Algebraic.expand ==> "-1 + x^2 + 3*x^2*y + 3*x^2*y^2 + x^2*y^3"
        sin(a*(x+y)) |> Algebraic.expand ==> "sin(a*(x + y))" // does not expand
        a/(b*(x+y)) |> Algebraic.expand ===> "a*b^(-1)*(x + y)^(-1)" // strict; does not expand
        a/(b*(x+y)) |> Algebraic.expand ==> "a/(b*(x + y))" // nice; does not expand
    
    
    [<Test>]
    let ``Structural Operators`` () =
    
        Structure.substitute 3Q 4Q (x**3) ==> "x^4"
        Structure.map (fun x -> -x) (x + y**2) ==> "-x - y^2"
        Structure.map (fun x -> -x) (x) ==> "x" // this is intended
    
        Structure.collectIdentifierSymbols (x*cos(y)) --> [ Symbol "x"; Symbol "y" ]
        Structure.collectIdentifiers (x*cos(y)) ==+> [ "x"; "y" ]
        Structure.collectIdentifiers (z/x*cos(y)**x) ==+> [ "x"; "y"; "z" ]
    
        Structure.collectFunctionTypes (x*cos(y)) --> [ Cos ]
        Structure.collectFunctions (x*cos(y)) ==+> [ "cos(y)" ]
    
        Structure.collectNumberValues (x*cos(2*y-4)/3) --> [ -4N; 1N/3N; 2N; ]
        Structure.collectNumbers (x*cos(2*y-4)/3) ==+> [ "-4"; "1/3"; "2" ]
    
        Structure.collect (function | Power _ as p -> Some p | _ -> None) ((x+y**z)**(a+b**c)+d) ==+> [ "(x + y^z)^(a + b^c)" ]
        Structure.collectPredicate (function | Power _ -> true | _ -> false) ((x+y**z)**(a+b**c)+d) ==+> [ "(x + y^z)^(a + b^c)" ]
        Structure.collectAll (function | Power _ as p -> Some p | _ -> None) ((x+y**z)**(a+b**c)+d) ==+> [ "b^c"; "y^z"; "(x + y^z)^(a + b^c)" ]
        Structure.collectAllPredicate (function | Power _ -> true | _ -> false) ((x+y**z)**(a+b**c)+d) ==+> [ "b^c"; "y^z"; "(x + y^z)^(a + b^c)" ]
        Structure.collectPowers ((x+y**z)**(a+b**c)+d) ==+> [ "b^c"; "y^z"; "(x + y^z)^(a + b^c)" ]
        Structure.collectSums ((x+y**z)**(a+b**c)+d) ==+> [ "a + b^c"; "x + y^z"; "d + (x + y^z)^(a + b^c)" ]
    
    [<Test>]
    let ``Algebaric Operators`` () =
        
        negate (x + y**2) ==> "-(x + y^2)"
    
        Algebraic.factors (b*cos(x)*ln(d)*x) ==+> ["b"; "x"; "ln(d)"; "cos(x)"]
        Algebraic.factors (b*cos(x)*log10(d)*x) ==+> ["b"; "x"; "log(d)"; "cos(x)"]
        Algebraic.factors (b*cos(x)*(log d (d*2))*x) ==+> ["b"; "x"; "log(d,2*d)"; "cos(x)"]
        Algebraic.factors (b+cos(x)) ==+> ["b + cos(x)"]
        Algebraic.summands (b+cos(x)+ln(d)+x) ==+> ["b"; "x"; "ln(d)"; "cos(x)"]
        Algebraic.summands (b+cos(x)+log10(d)+x) ==+> ["b"; "x"; "log(d)"; "cos(x)"]
        Algebraic.summands (b+cos(x)+(log d (d*2))+x) ==+> ["b"; "x"; "log(d,2*d)"; "cos(x)"]
        Algebraic.summands (b*cos(x)) ==+> ["b*cos(x)"]
    
        Algebraic.factorsInteger (2Q/3*b*cos(x)) --> (2I, [1Q/3; b; cos(x)])
    
        Algebraic.separateFactors x (b*cos(x)*ln(d)*x) ==|> ("b*ln(d)", "x*cos(x)")
        Algebraic.separateFactors x (b*cos(x)*log10(d)*x) ==|> ("b*log(d)", "x*cos(x)")
        Algebraic.separateFactors x (b*cos(x)*(log d (d*2))*x) ==|> ("b*log(d,2*d)", "x*cos(x)")
        Algebraic.separateFactors x (c*x*sin(x)/2) ==|> ("c/2", "x*sin(x)")
    
        Algebraic.expand ((x+1)*(x+3)) ==> "3 + 4*x + x^2"
        Algebraic.expand ((a+b)**2) ==> "a^2 + 2*a*b + b^2"
        Algebraic.expand ((a+b)**3) ==> "a^3 + 3*a^2*b + 3*a*b^2 + b^3"
        Algebraic.expand ((a+b)**4) ==> "a^4 + 4*a^3*b + 6*a^2*b^2 + 4*a*b^3 + b^4"
        Algebraic.expand ((a+b+c)**2) ==> "a^2 + 2*a*b + b^2 + 2*a*c + 2*b*c + c^2"
    
        Algebraic.expandMain (x*(2+(1+x)**2)) ==> "2*x + x*(1 + x)^2"
        Algebraic.expandMain ((x+(1+x)**2)**2) ==> "x^2 + 2*x*(1 + x)^2 + (1 + x)^4"
    
        Algebraic.expand ((a*x**2 + b*x + c)/(d*x + e)) ==> "c/(e + d*x) + (b*x)/(e + d*x) + (a*x^2)/(e + d*x)"
        let p = Algebraic.expand ((a*x**2 + b*x + c)*(d*x**2 + e*x + f))
        p ==> "c*f + c*e*x + b*f*x + c*d*x^2 + b*e*x^2 + a*f*x^2 + b*d*x^3 + a*e*x^3 + a*d*x^4"
        Polynomial.coefficients x p ==-> [|"c*f"; "c*e + b*f"; "c*d + b*e + a*f"; "b*d + a*e"; "a*d"|]
        Polynomial.leadingCoefficient x p ==> "a*d"
        Polynomial.collectTerms x p ==> "c*f + (c*e + b*f)*x + (c*d + b*e + a*f)*x^2 + (b*d + a*e)*x^3 + a*d*x^4"
        Polynomial.degree x p ==> "4"
        Polynomial.totalDegree p ==> "6"
        Polynomial.variables p ==*> ["a"; "b"; "c"; "d"; "e"; "f"; "x"]
    
        Exponential.expand (exp(2*x+y)) ==> "(exp(x))^2*exp(y)"
        Exponential.expand (exp(2*a*x + 3*y*z)) ==> "(exp(a*x))^2*(exp(y*z))^3"
        Exponential.expand (exp(2*(x+y))) ==> "(exp(x))^2*(exp(y))^2"
        Exponential.expand (1/(exp(2*x) - (exp(x))**2)) ==> "⧝"
        Exponential.expand (exp((x+y)*(x-y))) ==> "exp(x^2)/exp(y^2)"
        Exponential.expand (ln((c*x)**a) + ln(y**b*z)) ==> "a*ln(c) + a*ln(x) + b*ln(y) + ln(z)"
        Exponential.expand (log10((c*x)**a) + log10(y**b*z)) ==> "a*log(c) + a*log(x) + b*log(y) + log(z)"
        Exponential.expand ((log 5Q ((c*x)**a)) + (log 3Q (y**b*z))) ==> "a*log(5,c) + a*log(5,x) + b*log(3,y) + log(3,z)"
    
        Exponential.contract (exp(x)*exp(y)) ==> "exp(x + y)"
        Exponential.contract (exp(x)**a) ==> "exp(a*x)"
        Exponential.contract (exp(x)*(exp(x) + exp(y))) ==> "exp(2*x) + exp(x + y)"
        Exponential.contract ((exp(exp(x)))**exp(y)) ==> "exp(exp(x + y))"
    
        Exponential.simplify (1/(exp(x)*(exp(y)+exp(-x))) - (exp(x+y)-1)/((exp(x+y))**2-1)) ==> "0"
    
        Trigonometric.expand (sin(2*x)) ==> "2*sin(x)*cos(x)"
        Trigonometric.expand (sin(3*x)) ==> "-(sin(x))^3 + 3*sin(x)*(cos(x))^2"
        Trigonometric.expand (sin(a+x)) ==> "sin(x)*cos(a) + sin(a)*cos(x)"
        Trigonometric.expand (sin(2*x + 3*y)) ==> "(-(sin(x))^2 + (cos(x))^2)*(-(sin(y))^3 + 3*sin(y)*(cos(y))^2) + 2*sin(x)*cos(x)*(-3*(sin(y))^2*cos(y) + (cos(y))^3)"
        Trigonometric.expand (sin(2*(x+y))) ==> "2*sin(y)*(-(sin(x))^2 + (cos(x))^2)*cos(y) + 2*sin(x)*cos(x)*(-(sin(y))^2 + (cos(y))^2)"
        Trigonometric.expand (sin(2*(x+y))) |> Algebraic.expand ==> "-2*sin(x)*(sin(y))^2*cos(x) - 2*(sin(x))^2*sin(y)*cos(y) + 2*sin(y)*(cos(x))^2*cos(y) + 2*sin(x)*cos(x)*(cos(y))^2"
        Trigonometric.expand (cos(5*x)) ==> "5*(sin(x))^4*cos(x) - 10*(sin(x))^2*(cos(x))^3 + (cos(x))^5"    
        // TODO: should actually be Undefined
        Trigonometric.expand ((sin(2*x)-2*sin(x)*cos(x))/((sin(x))**2 + (cos(x))**2 - 1)) ==> "0"
        
        Trigonometric.contract (sin(a)*sin(b)) ==> "-cos(a + b)/2 + cos(a - b)/2"
        Trigonometric.contract (sin(a)*cos(b)) ==> "sin(a + b)/2 + sin(a - b)/2"
        Trigonometric.contract (cos(a)*sin(b)) ==> "sin(a + b)/2 + sin(-a + b)/2"
        Trigonometric.contract (cos(a)*cos(b)) ==> "cos(a + b)/2 + cos(a - b)/2"
        Trigonometric.contract (sin(x)**2 + cos(x)**2) ==> "1" // Pythagorean identity
        Trigonometric.simplify (sec(x)**2 - tan(x)**2 - 1) ==> "0"
        Trigonometric.simplify (csc(x)**2 - cot(x)**2 - 1) ==> "0"
        Trigonometric.contract ((sin(x) + cos(y))*cos(y)) ==> "1/2 + sin(x + y)/2 + sin(x - y)/2 + cos(2*y)/2"
        Trigonometric.contract (sin(x)**2*cos(x)**2) ==> "1/8 - cos(4*x)/8"
        Trigonometric.contract (cos(x)**4) ==> "3/8 + cos(2*x)/2 + cos(4*x)/8"
        Trigonometric.contract (sin(x/2)**2) ==> "1/2 - cos(x)/2"
        Trigonometric.contract (cos(x/2)**2) ==> "1/2 + cos(x)/2"
        Trigonometric.simplify ((cos(x)+sin(x))**4 + (cos(x)-sin(x))**4 + cos(4*x) - 3) ==> "0"
    
        Trigonometric.substitute (tan(x)) ==> "sin(x)/cos(x)"
        Trigonometric.substitute (cot(x)) ==> "cos(x)/sin(x)"
        Trigonometric.substitute (csc(x)) ==> "1/sin(x)"
        Trigonometric.substitute (sec(x)) ==> "1/cos(x)"
    
        Trigonometric.expand (sinh(2*x)) ==> "2*sinh(x)*cosh(x)"
        Trigonometric.expand (sinh(3*x)) ==> "(sinh(x))^3 + 3*sinh(x)*(cosh(x))^2"
        Trigonometric.expand (sinh(4*x)) ==> "4*(sinh(x))^3*cosh(x) + 4*sinh(x)*(cosh(x))^3"
        Trigonometric.expand (sinh(5*x)) ==> "(sinh(x))^5 + 10*(sinh(x))^3*(cosh(x))^2 + 5*sinh(x)*(cosh(x))^4"
        Trigonometric.expand (cosh(2*x)) ==> "(sinh(x))^2 + (cosh(x))^2"
        Trigonometric.expand (cosh(3*x)) ==> "3*(sinh(x))^2*cosh(x) + (cosh(x))^3"
        Trigonometric.expand (cosh(4*x)) ==> "(sinh(x))^4 + 6*(sinh(x))^2*(cosh(x))^2 + (cosh(x))^4"
        Trigonometric.expand (cosh(5*x)) ==> "5*(sinh(x))^4*cosh(x) + 10*(sinh(x))^2*(cosh(x))^3 + (cosh(x))^5"
    
        Trigonometric.contract (sinh(a)*sinh(b)) ==> "cosh(a + b)/2 - cosh(a - b)/2"
        Trigonometric.contract (sinh(a)*cosh(b)) ==> "sinh(a + b)/2 + sinh(a - b)/2"
        Trigonometric.contract (cosh(a)*sinh(b)) ==> "sinh(a + b)/2 + sinh(-a + b)/2"
        Trigonometric.contract (cosh(a)*cosh(b)) ==> "cosh(a + b)/2 + cosh(a - b)/2"
        Trigonometric.contract (sinh(x)**2) ==> "-1/2 + cosh(2*x)/2"
        Trigonometric.contract (sinh(x)**3) ==> "-3/4*sinh(x) + sinh(3*x)/4"
        Trigonometric.contract (sinh(x)**4) ==> "3/8 - cosh(2*x)/2 + cosh(4*x)/8"
        Trigonometric.contract (sinh(x)**5) ==> "5/8*sinh(x) - 5/16*sinh(3*x) + sinh(5*x)/16"
        Trigonometric.contract (cosh(x)**2) ==> "1/2 + cosh(2*x)/2"
        Trigonometric.contract (cosh(x)**3) ==> "3/4*cosh(x) + cosh(3*x)/4"
        Trigonometric.contract (cosh(x)**4) ==> "3/8 + cosh(2*x)/2 + cosh(4*x)/8"
        Trigonometric.contract (cosh(x)**5) ==> "5/8*cosh(x) + 5/16*cosh(3*x) + cosh(5*x)/16"
        Trigonometric.contract (sinh(x/2)**2) ==> "-1/2 + cosh(x)/2"
        Trigonometric.contract (cosh(x/2)**2) ==> "1/2 + cosh(x)/2"
        Trigonometric.contract (cosh(x)**2 - sinh(x)**2) ==> "1"
        Trigonometric.simplify (sech(x)**2 + tanh(x)**2 - 1) ==> "0"
        Trigonometric.simplify (csch(x)**2 - coth(x)**2 + 1) ==> "0"
        Trigonometric.simplify ((cosh(x)+sinh(x))**4 + (cosh(x)-sinh(x))**4 - 2*cosh(4*x)) ==> "0"
        
        Trigonometric.substitute (tanh(x)) ==> "sinh(x)/cosh(x)"
        Trigonometric.substitute (coth(x)) ==> "cosh(x)/sinh(x)"
        Trigonometric.substitute (csch(x)) ==> "1/sinh(x)"
        Trigonometric.substitute (sech(x)) ==> "1/cosh(x)"
    
        // TODO: expected: 0
        Trigonometric.simplify (sin(x) + sin(y) - 2*sin(x/2+y/2)*cos(x/2-y/2))
            ==> "sin(y) - sin(x - y)/2 - sin(x/2 - y/2 - (x/2 - y/2))/2 - sin(-x/2 + y/2 - (x/2 - y/2))/2 - sin(x/2 + y/2 - (x/2 - y/2))"
    
    [<Test>]
    let ``Differentiation and Taylor Series`` () =
    
        Calculus.differentiate x (a*x) ==> "a"
        Calculus.differentiate x (sin(x)) ==> "cos(x)"
        Calculus.differentiate x (x*sin(x)) ==> "sin(x) + x*cos(x)"
        Calculus.differentiate x (a*x**2) ==> "2*a*x"
        Calculus.differentiate x (a*x**b) ==> "a*b*x^(-1 + b)"
        Calculus.differentiate x (a*x**2 + b*x + c) ==> "b + 2*a*x"
        Calculus.differentiate x (1Q/x) ==> "-1/x^2"
        Calculus.differentiate x ((ln x) / (ln 10Q)) ==> "1/(x*ln(10))"
        Calculus.differentiate x (ln x) ==> "1/x"
        Calculus.differentiate x (ln (x**2)) ==> "2/x"
        Calculus.differentiate x (log10 x) ==> "1/(x*ln(10))"
        Calculus.differentiate x (log10 (x**2)) ==> "2/(x*ln(10))"
        Calculus.differentiate x (log 10Q x) ==> "1/(x*ln(10))"
        Calculus.differentiate x (log x (x**2)) ==> "2/(x*ln(x)) - ln(x^2)/(x*(ln(x))^2)"
    
        Calculus.differentiate x (arcsin(x)) ==> "(1 - x^2)^(-1/2)"
        Calculus.differentiate x (arccos(x)) ==> "-(1 - x^2)^(-1/2)"
        Calculus.differentiate x (arctan(x)) ==> "1/(1 + x^2)"
    
        Calculus.taylor 3 x 0Q (1/(1-x)) ==> "1 + x + x^2"
        Calculus.taylor 3 x 1Q (1/x) ==> "3 - 3*x + x^2"
        Calculus.taylor 3 x 1Q (ln(x)) ==> "-3/2 + 2*x - x^2/2"
        Calculus.taylor 4 x 1Q (ln(x)) ==> "-11/6 + 3*x - 3/2*x^2 + x^3/3"
        Calculus.taylor 3 x 0Q (sin(x)+cos(x)) ==> "1 + x - x^2/2"
        Calculus.taylor 4 x 0Q (sin(x)+cos(x)) ==> "1 + x - x^2/2 - x^3/6"
        (sin(x)+cos(x)) |> Calculus.taylor 4 x 0Q ==> "1 + x - x^2/2 - x^3/6"
    
    
    [<Test>]
    let ``Tangent and Normal Lines`` () =
    
        (1/z) |> Calculus.tangentLine z 3Q ==> "2/3 - z/9"
        (x**3 - 12*x**2 - c) |> Calculus.tangentLine x 1Q ==> "10 - c - 21*x"
    
        Calculus.normalLine z 3Q (1/z) ==> "-80/3 + 9*z"
        (1/z) |> Calculus.normalLine z 3Q ==> "-80/3 + 9*z"
    
    
    [<Test>]
    let ``Polynomial Division`` () =
    
        Polynomial.divide x (5*x**2 + 4*x + 1) (2*x + 3) ==|> ("-7/4 + 5/2*x", "25/4")
        Polynomial.divide x (x**3 - 2*x**2 - 4) (x-3) ==|> ("3 + x + x^2", "5")
        Polynomial.quot x (x**3 - 2*x**2 - 4) (x-3) ==> "3 + x + x^2"
        Polynomial.remainder x (x**3 - 2*x**2 - 4) (x-3) ==> "5"
    
        Polynomial.divide x (3*x**3 + x**2 + x + 5) (5*x**2 - 3*x + 1) ==|> ("14/25 + 3/5*x", "111/25 + 52/25*x")
        Polynomial.divide x (3*x**3 + x**2 + x + 5) (2Q) ==|> ("5/2 + x/2 + x^2/2 + 3/2*x^3", "0")
        Polynomial.pseudoDivide x (3*x**3 + x**2 + x + 5) (5*x**2 - 3*x + 1) ==||> ("14 + 15*x", "111 + 52*x", "25")
        Polynomial.pseudoDivide x (3*x**3 + x**2 + x + 5) (2Q) ==||> ("5 + x + x^2 + 3*x^3", "0", "2")
    
        // tangent of polynomial at x = 1?
        Polynomial.divide x (x**3 - 12*x**2 - c) (x**2-2*x+1) ==|> ("-10 + x", "10 - c - 21*x")
    
        /// Find tangent equation for x(symbol) at symbol=a
        let tangent symbol x a =
            let m = Calculus.differentiate symbol x |> Structure.substitute symbol a
            m*(symbol - a) + Structure.substitute symbol a x |> Algebraic.expand
    
        tangent x (x**3 - 12*x**2 - c) 1Q ==> "10 - c - 21*x"
        tangent z (1/z) 3Q ==> "2/3 - z/9"
    
    
    [<Test>]
    let ``Polynomial Expansion`` () =
    
        let ex = Polynomial.polynomialExpansion x y (x**5 + 11*x**4 + 51*x**3 + 124*x**2 + 159*x + 86) (x**2 + 4*x + 5)
        ex ==> "1 + x + (2 + x)*y + (3 + x)*y^2"
    
        let exs = ex |> Structure.substitute y (x**2 + 4*x + 5)
        exs ==> "1 + x + (2 + x)*(5 + 4*x + x^2) + (3 + x)*(5 + 4*x + x^2)^2"
    
        // get back to original polynomial:
        Algebraic.expand exs ==> "86 + 159*x + 124*x^2 + 51*x^3 + 11*x^4 + x^5"
    
    [<Test>]
    let ``Polynomial From Coefficients`` () =
        Polynomial.fromCoefficients x [1Q; 1Q; 1Q] ==> "1 + x + x^2"
        Polynomial.fromCoefficients x [1Q; 2Q; 3Q] ==> "1 + 2*x + 3*x^2"
        Polynomial.fromCoefficients x [a; b; c] ==> "a + b*x + c*x^2"
        Polynomial.fromCoefficients x [sin(y); cos(y)] ==> "sin(y) + x*cos(y)"
        Polynomial.fromCoefficients x [] ==> "0"
    
    [<Test>]
    let ``Polynomial Euclidean/GCD`` () =
    
        Polynomial.gcd x (x**7 - 4*x**5 - x**2 + 4) (x**5 - 4*x**3 - x**2 + 4) ==> "4 - 4*x - x^2 + x^3"
        Polynomial.gcd x (x**4 - 2*x**3 - 6*x**2 + 12*x + 15) (x**3 + x**2 - 4*x - 4) ==> "1 + x"
    
        Polynomial.extendedGcd x (x**7 - 4*x**5 - x**2 + 4) (x**5 - 4*x**3 - x**2 + 4)
            ==||> ("4 - 4*x - x^2 + x^3", "-x", "1 + x^3")
    
        // verify A*u+B*v = gcd = 4 - 4*x - x^2 + x^3:
        (-x)*(x**7 - 4*x**5 - x**2 + 4) + (1+x**3)*(x**5 - 4*x**3 - x**2 + 4) |> Algebraic.expand
            ==> "4 - 4*x - x^2 + x^3"
    
        let u = x**4 - 2*x**3 - 6*x**2 + 12*x + 15
        let v = x**3 + x**2 - 4*x - 4
        Polynomial.gcd x u v ==> "1 + x"
        Polynomial.halfExtendedGcd x u v ==|> ("1 + x", "3/5 - x/5")
        let g,a,b = Polynomial.extendedGcd x u v
        g ==> "1 + x"
        a ==> "3/5 - x/5"
        b ==> "2 - 6/5*x + x^2/5"
    
        // hence u*a + v*b = g ? indeed:
        u*a + v*b |> Algebraic.expand ==> "1 + x"
    
        // Let's try to find s, t such that s*u + t*v = x^2 - 1
        let s, t = Polynomial.diophantineGcd x (x**4 - 2*x**3 - 6*x**2 + 12*x + 15) (x**3 + x**2 - 4*x - 4) (x**2 - 1)
        s ==> "-3/5 + 4/5*x - x^2/5"
        t ==> "-2 + 16/5*x - 7/5*x^2 + x^3/5"
        s*u + t*v |> Algebraic.expand ==> "-1 + x^2"
    
        // (x^2 + 3*x)/((x + 1)*(x^2 - 2*x + 1)) --> (-1/2)/(x+1) + (1/2 + (3/2)*x)/(x^2-2*x+1)
        let a0, ax = Polynomial.partialFraction x (x**2+3*x) [x+1; x**2-2*x+1]
        a0 ==> "0"
        ax ==+> ["-1/2"; "1/2 + 3/2*x"]
    
    
    [<Test>]
    let ``Evaluate some expression to floating point numbers`` () =
    
        let symbols = Map.ofList ["a", FloatingPoint.Real 2.0; "b", FloatingPoint.Real 3.0; "c", FloatingPoint.Complex (complex 1.0 -1.0)]
        Evaluate.evaluate symbols (a) --> FloatingPoint.Real 2.0
        Evaluate.evaluate symbols (1Q/2) --> FloatingPoint.Real 0.5
        Evaluate.evaluate symbols (sin(a) + ln(b)) --> FloatingPoint.Real (System.Math.Sin(2.0) + System.Math.Log(3.0))
        Evaluate.evaluate symbols (a*x**2 + b*x + c |> Structure.substitute x (number 1/2)) --> FloatingPoint.Complex (complex 3.0 -1.0)
        Evaluate.evaluate symbols (1Q/0Q) --> FloatingPoint.ComplexInf
        (fun () -> Evaluate.evaluate symbols (f) |> ignore) |> should (throwWithMessage "Failed to find symbol: f") typeof<System.Exception>
    
        let (FloatingPoint.Complex c) = Evaluate.evaluate symbols (sqrt(-1Q))
        c.Real |> should (equalWithin 1e-15) 0.0
        c.Imaginary |> should (equalWithin 1e-15) 1.0
    
    
    [<Test>]
    let ``Primitive Equation Solver`` () =
    
        let solve x expr =
    
            let expr' = Rational.simplify x expr |> Algebraic.expand
    
            if Polynomial.isPolynomial x expr' then
                match Polynomial.coefficients x expr' with
                | [||] -> Undefined
                | [| a |] -> x
                | [| a; b |] -> -a/b
                | _ -> failwith "higher polynomials not supported"
    
            else failwith "only general polynomial expressions supported for now"
    
        // 2+3*x = 0 --> x =
        solve x (2+3*x) ==> "-2/3"
    
        // sin(a)+x*cos(b)+c = 0 --> x =
        solve x (sin(a)+x*cos(b)+c) ==> "-(c + sin(a))/cos(b)"
    
        // (x^2-1)/(x+1) = 0 --> x =
        solve x ((x**2-1)/(x+1)) ==> "1"
    
        /// Solve simple a=b line equations to y=f(x) form
        let solveLine x y a b =
            let z = solve y (a-b) |> Algebraic.expand |> Rational.simplify x
            let z' = z |> Algebraic.expand |> Polynomial.collectTerms x
            if z' <> Undefined then z' else z
    
        solveLine x y (x/2+y/3) 1Q ==> "3 - 3/2*x" //   -->  x/2 + y/3 = 1  ->  y = -3/2*x + 3
        solveLine x y (x/a) ((x+y)/b) ==> "(-1 + b/a)*x"
        solveLine x y ((y/x-2)/(1-3/x)) 6Q ==> "-18 + 8*x"
    
    
    [<Test>]
    let ``General Polynomial Expressions`` () =
    
        Polynomial.isMonomial x (a * x**2) --> true
        Polynomial.isMonomial x (ln(a) * x**2) --> true
        Polynomial.isMonomial x (log10(a) * x**2) --> true
        Polynomial.isMonomial x ((log a (a**2)) * x**2) --> true
        Polynomial.isMonomial x (x**2 + a) --> false
        Polynomial.isPolynomial x (x**2 + x**3) --> true
        Polynomial.isPolynomial x (x**2 + 2*x) --> true
        Polynomial.isPolynomial x ((x+1)*(x+3)) --> false
    
        Polynomial.isMonomialMV (Polynomial.symbols [x;y]) (a * x**2 * y**2) --> true
        Polynomial.isMonomialMV (Polynomial.symbols [x;y]) (ln(a) * x**2 * y**2) --> true
        Polynomial.isMonomialMV (Polynomial.symbols [x;y]) (log10(a) * x**2 * y**2) --> true
        Polynomial.isMonomialMV (Polynomial.symbols [x;y]) ((log a (a**2)) * x**2 * y**2) --> true
        Polynomial.isMonomialMV (Polynomial.symbols [x;y]) (x**2 + y**2) --> false
        Polynomial.isPolynomialMV (Polynomial.symbols [x;y]) (x**2 + y**2) --> true
        Polynomial.isPolynomialMV (Polynomial.symbols [x+1]) ((x+1)**2 + 2*(x+1)) --> true
        Polynomial.isPolynomialMV (Polynomial.symbols [x]) ((x+1)*(x+3)) --> false
    
        Polynomial.degreeMonomial x (a * x**2 * x * b**2) ==> "3"
        Polynomial.degree x (a*x**2 + b*x + c) ==> "2"
    
        Polynomial.degreeMonomialMV (Polynomial.symbols [x;y]) (a * x**2 * y * b**2) ==> "3" // (x:2 + y:1)
        Polynomial.degreeMV (Polynomial.symbols [x;y]) (a*x**2 + b*x + c) ==> "2"
        Polynomial.degreeMV (Polynomial.symbols [x;z]) (2*x**2*y**8*z**2 + a*x*z**6) ==> "7"
    
        Polynomial.variables (a * x**2 * y**2) ==*> ["a"; "x"; "y"]
        Polynomial.variables ((x+1)**2 + 2*(x+1)) ==*> ["1 + x"]
        Polynomial.variables ((x+1)*(x+3)) ==*> ["1 + x"; "3 + x"]
        Polynomial.variables ((x+1)*(x+3)*sin(x)) ==*> ["1 + x"; "3 + x"; "sin(x)"]
        Polynomial.totalDegree (2*x**2*y*z**2 + a*x*z**6) ==> "8"
    
        Polynomial.commonFactors (8*a*x + 6*a*x**2) ==> "2*a*x"
        Polynomial.commonFactors ((3Q/2)*x*y**2 + (5Q/8)*x**2*y + 7*x + (9Q/10)) ==> "1"
        Polynomial.commonFactors (512*x*y*z + 512*x**2*y*z + 3072*x*y**2*z + 3072*x**2*y**2*z + 1024*x*y**3*z) ==> "512*x*y*z"
    
        Polynomial.coefficientMonomial x (2*a * b * x**2) ==> "2*a*b"
    
        Polynomial.coefficientMonomialMV (Polynomial.symbols [x;y]) (2*a * b * x**2) ==> "2*a*b"
        Polynomial.coefficientMonomialMV (Polynomial.symbols [x;y]) (2*a * b * x**2 * y) ==> "2*a*b"
        Polynomial.coefficientMonomialMV (Polynomial.symbols [x;y]) (2*a * b * x**2 * y * z) ==> "2*a*b*z"
        Polynomial.coefficientMonomialMV (Polynomial.symbols [x;y;z;a;b]) (2*a * b * x**2 * y * z) ==> "2"
    
        Polynomial.coefficient x 2 (a*x**2 + b*x + c) ==> "a"
        Polynomial.coefficient x 2 (a*x*x + b*x + c) ==> "a"
        Polynomial.coefficient x 1 (3*x*y**2 + 5*x**2*y + 7*x + 9) ==> "7 + 3*y^2"
        Polynomial.coefficient x 3 (3*x*y**2 + 5*x**2*y + 7*x + 9) ==> "0"
        Polynomial.leadingCoefficient x (3*x*y**2 + 5*x**2*y + 7*x**2*y**3 + 9) ==> "5*y + 7*y^3"
        Polynomial.coefficients x (3*x*y**2 + 5*x**2*y + 7*x**2*y**3 + 9) ==-> [|"9"; "3*y^2"; "5*y + 7*y^3"|]
    
        Polynomial.collectTermsMonomial x (2*x*a) ==|> ("2*a", "x")
        Polynomial.collectTermsMonomial x (2*a*x*b*3) ==|> ("6*a*b", "x")
        Polynomial.collectTermsMonomial x (2*a*x**3*b*x*3) ==|> ("6*a*b", "x^4")
    
        Polynomial.collectTermsMonomialMV (Polynomial.symbols [x;y]) (2*x*a) ==|> ("2*a", "x")
        Polynomial.collectTermsMonomialMV (Polynomial.symbols [x;y]) (2*a*x*b*y*3) ==|> ("6*a*b", "x*y")
        Polynomial.collectTermsMonomialMV (Polynomial.symbols [x;y]) (2*a*x*b*y**3*x*3) ==|> ("6*a*b", "x^2*y^3")
    
        Polynomial.collectTerms x (2*x*a*y + 4*a*x + 3*x*y*b + 5*x*b) ==> "x*(4*a + 5*b + 2*a*y + 3*b*y)"
        Polynomial.collectTerms a (2*x*a*y + 4*a*x + 3*x*y*b + 5*x*b) ==> "5*b*x + 3*b*x*y + a*(4*x + 2*x*y)"
        Polynomial.collectTerms (ln(a)) (2*x*ln(a)*y + 4*x*ln(a) + 3*x*y*b + 5*x*b + c) ==> "c + 5*b*x + 3*b*x*y + (4*x + 2*x*y)*ln(a)"
        Polynomial.collectTerms (log10(a)) (2*x*log10(a)*y + 4*x*log10(a) + 3*x*y*b + 5*x*b + c) ==> "c + 5*b*x + 3*b*x*y + (4*x + 2*x*y)*log(a)"
        Polynomial.collectTerms (log a (a**2)) (2*x*(log a (a**2))*y + 4*x*(log a (a**2)) + 3*x*y*b + 5*x*b + c) ==> "c + 5*b*x + 3*b*x*y + (4*x + 2*x*y)*log(a,a^2)"
    
        Polynomial.collectTermsMV (Polynomial.symbols [x;y]) (2*x*a*y + 4*a*x + 3*x*y*b + 5*x*b) ==> "(4*a + 5*b)*x + (2*a + 3*b)*x*y"
        Polynomial.collectTermsMV (Polynomial.symbols [a;b]) (2*x*a*y + 4*a*x + 3*x*y*b + 5*x*b) ==> "a*(4*x + 2*x*y) + b*(5*x + 3*x*y)"
        Polynomial.collectTermsMV (Polynomial.symbols [x;ln(a)]) (2*x*ln(a)*y + 4*x*ln(a) + 3*x*y*b + 5*x*b + c) ==> "c + x*(5*b + 3*b*y) + x*(4 + 2*y)*ln(a)"
        Polynomial.collectTermsMV (Polynomial.symbols [x;log10(a)]) (2*x*log10(a)*y + 4*x*log10(a) + 3*x*y*b + 5*x*b + c) ==> "c + x*(5*b + 3*b*y) + x*(4 + 2*y)*log(a)"
        Polynomial.collectTermsMV (Polynomial.symbols [x;(log a (a**2))]) (2*x*(log a (a**2))*y + 4*x*(log a (a**2)) + 3*x*y*b + 5*x*b + c) ==> "c + x*(5*b + 3*b*y) + x*(4 + 2*y)*log(a,a^2)"
    
        Polynomial.isSquareFree x (x**3 + 1) --> true
        Polynomial.isSquareFree x (x**2 - 2) --> true
        Polynomial.isSquareFree x (8*x**3 + 12*x**2 + 6*x + 1) --> false
    
        Polynomial.factorSquareFree x (x**8 + 6*x**6 + 12*x**4 + 8*x**2) ==> "x^2*(2 + x^2)^3"
    
        let sf = Polynomial.factorSquareFree x (x**5 + 6*x**4 + 10*x**3 - 4*x**2 - 24*x - 16)
        sf ==> "(2 + x)^3*(-2 + x^2)"
        Algebraic.expand sf ==> "-16 - 24*x - 4*x^2 + 10*x^3 + 6*x^4 + x^5"
    
    
    [<Test>]
    let ``General Rational Expressions`` () =
    
        Rational.numerator (x/y) ==> "x"
        Rational.denominator (x/y) ==> "y"
        Rational.numerator (x**2/y**3) ==> "x^2"
        Rational.denominator (x**2/y**3) ==> "y^3"
    
        Rational.numerator (x**2) ==> "x^2"
        Rational.denominator (x**2) ==> "1"
        Rational.numerator (x**(-2)) ==> "1"
        Rational.denominator (x**(-2)) ==> "x^2"
    
        Rational.numerator (2Q/3*(x*(x+1))/(x+2)*y**a) ==> "2*x*(1 + x)*y^a"
        Rational.denominator (2Q/3*(x*(x+1))/(x+2)*y**a) ==> "3*(2 + x)"
    
        Rational.isRational x ((x**2+1)/(2*x+3)) --> true
        Rational.isRational x (1/x + 1/a) --> false
    
        Rational.variables ((2*x + 3*y)/(z + 4)) ==*> ["x"; "y"; "z"]
        Rational.variables (1/x + 1/y) ==*> ["1/x"; "1/y"]
        Rational.variables (a/x + b/y) ==*> ["a"; "1/x"; "b"; "1/y"]
    
        Rational.rationalize (a+1) ==> "1 + a"
        Rational.rationalize (a/b + c/d) ==> "(b*c + a*d)/(b*d)"
        Rational.rationalize (1+1/(1+1/x)) ==> "(1 + 2*x)/(1 + x)"
        Rational.rationalize (1/(1+1/x)**(1Q/2) + (1+1/x)**(3Q/2)) ==> "(x^2 + (1 + x)^2)/(x^2*sqrt((1 + x)/x))"
        Rational.rationalize ((1+1/x)**2) ==> "(1 + x)^2/x^2"
    
        Rational.rationalize (a/b + c/d + e/f) ==> "(b*d*e + (b*c + a*d)*f)/(b*d*f)"
        Rational.expand (a/b + c/d + e/f) ==> "(b*d*e + b*c*f + a*d*f)/(b*d*f)"
    
        Rational.rationalize (((1/((x+y)**2+1))**(1Q/2)+1)*((1/((x+y)**2+1))**(1Q/2)-1)/(x+1))
            ==> "((-1 + sqrt(1/(1 + (x + y)^2)))*(1 + sqrt(1/(1 + (x + y)^2))))/(1 + x)"
        Rational.expand (((1/((x+y)**2+1))**(1Q/2)+1)*((1/((x+y)**2+1))**(1Q/2)-1)/(x+1))
            ==> "(-x^2 - 2*x*y - y^2)/(1 + x + x^2 + x^3 + 2*x*y + 2*x^2*y + y^2 + x*y^2)"
    
        Rational.rationalize (1/(1/a + c/(a*b)) + (a*b*c + a*c**2)/(b+c)**2-a) |> Algebraic.expand ==> "0"
        Rational.expand (1/(1/a + c/(a*b)) + (a*b*c + a*c**2)/(b+c)**2-a) ==> "0"
    
        Rational.rationalize (x/z + y/z**2) ==> "(y*z + x*z^2)/z^3"
        Rational.simplify z (x/z + y/z**2) ==> "(y + x*z)/z^2"
    
        Rational.simplify x ((x**2-1)/(x+1)) ==> "-1 + x"
        Rational.simplify x ((x+1)/(x**2 - 1 - (x+1)*(x-1))) ==> "⧝"
        Rational.simplify x (1/(1+1/(x+1)) + 2/(x+2))  ==> "(3 + x)/(2 + x)"
    
        // http://stackoverflow.com/questions/32791138/extracting-common-terms-with-mathnet-symbolics
        let pn = (1Q/8)*x*y*z + (1Q/2)*x*(y**2)*z
        let pd = (1Q/8)*x*y*z + (1Q/8)*(x**2)*y*z + (3Q/4)*x*(y**2)*z + (3Q/4)*(x**2)*(y**2)*z + (1Q/4)*x*(y**3)*z
        Rational.expand (pn / pd) ==> "(1 + 4*y)/(1 + x + 6*y + 6*x*y + 2*y^2)"
    
    
    [<Test>]
    let ``Single Variable Polynomials`` () =
    
        SingleVariablePolynomial.isMonomialSV x (Quotations.parse <@ fun x -> 3*x @>) --> true
        SingleVariablePolynomial.isMonomialSV x (Quotations.parse <@ 3*x+2 @>) --> false
        SingleVariablePolynomial.isMonomialSV x (3*(x*x)) --> true
        SingleVariablePolynomial.isMonomialSV x (a*x) --> false
        SingleVariablePolynomial.isMonomialSV y (3*x) --> false
        SingleVariablePolynomial.degreeMonomialSV x 0Q ==> "-∞"
        SingleVariablePolynomial.degreeMonomialSV x 1Q ==> "0"
        SingleVariablePolynomial.degreeMonomialSV x (3*x) ==> "1"
        SingleVariablePolynomial.degreeMonomialSV x (3 * x*x) ==> "2"
        SingleVariablePolynomial.degreeMonomialSV x (3 * x*x * y) ==> "Undefined"
        SingleVariablePolynomial.degreeMonomialSV x (3 + x) ==> "Undefined"
    
        SingleVariablePolynomial.coefficientMonomialSV x 0Q ==> "0"
        SingleVariablePolynomial.coefficientMonomialSV x 1Q ==> "1"
        SingleVariablePolynomial.coefficientMonomialSV x (3 * x) ==> "3"
        SingleVariablePolynomial.coefficientMonomialSV x (3 * x*x) ==> "3"
        SingleVariablePolynomial.coefficientMonomialSV x (3 * x*x * y) ==> "Undefined"
        SingleVariablePolynomial.coefficientMonomialSV x (3 + x) ==> "Undefined"
        SingleVariablePolynomial.coefficientDegreeMonomialSV x 0Q ==|> ("0", "-∞")
        SingleVariablePolynomial.coefficientDegreeMonomialSV x 1Q ==|> ("1", "0")
        SingleVariablePolynomial.coefficientDegreeMonomialSV x (3*x) ==|> ("3", "1")
        SingleVariablePolynomial.coefficientDegreeMonomialSV x (3*x*x) ==|> ("3", "2")
    
        SingleVariablePolynomial.isPolynomialSV x (3*x) --> true
        SingleVariablePolynomial.isPolynomialSV x (3*x+2) --> true
        SingleVariablePolynomial.isPolynomialSV x (3*x*x+2) --> true
        SingleVariablePolynomial.degreeSV x (3*x*x + 2*x) ==> "2"
        SingleVariablePolynomial.degreeSV x (3*x*x + 2*x*x*x) ==> "3"
        SingleVariablePolynomial.degreeSV x (3*x + 2*x*(x**5) + 2*(x**3)) ==> "6"
    
        SingleVariablePolynomial.coefficientSV x 0 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "1"
        SingleVariablePolynomial.coefficientSV x 1 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "4"
        SingleVariablePolynomial.coefficientSV x 2 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "0"
        SingleVariablePolynomial.coefficientSV x 3 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "2"
        SingleVariablePolynomial.coefficientSV x 4 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "0"
        SingleVariablePolynomial.coefficientSV x 5 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "0"
        SingleVariablePolynomial.coefficientSV x 6 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "2"
        SingleVariablePolynomial.coefficientSV x 7 (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "0"
        SingleVariablePolynomial.leadingCoefficientSV x (3*x*x + 2*x) ==> "3"
        SingleVariablePolynomial.leadingCoefficientSV x (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==> "2"
        SingleVariablePolynomial.leadingCoefficientSV x 2Q ==> "2"
        SingleVariablePolynomial.leadingCoefficientSV x 0Q ==> "0"
        SingleVariablePolynomial.leadingCoefficientDegreeSV x (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==|> ("2", "6")
        SingleVariablePolynomial.coefficientsSV x (3*x*x + 2*x)  ==-> [|"0"; "2"; "3"|]
        SingleVariablePolynomial.coefficientsSV x (3*x + 2*x*(x**5) + 2*(x**3) + x + 1) ==-> [|"1"; "4"; "0"; "2"; "0"; "0"; "2"|]
    
    
    [<Test>]
    let ``Pseudo Function Test`` () =
    
        Infix.parseOrUndefined "sqrt(x)" ===> "x^(1/2)"
        Infix.parseOrUndefined "sqrt(x)" ==> "sqrt(x)"
        Infix.parseOrUndefined "pow(x,3)" ==> "x^3"
        Infix.parseOrUndefined "pow(3*x,10*sin(x))" ==> "(3*x)^(10*sin(x))"
        Infix.parseOrUndefined "sqrt(pow(x,1/2))" ===> "(x^(1/2))^(1/2)"
        Infix.parseOrUndefined "sqrt(pow(x,1/2))" ==> "sqrt(sqrt(x))"
    
    [<Test>]
    let ``Underscores in names`` () =
        let expr = Infix.parseOrUndefined "(TESTING_UNDER)*(2)"
        expr ==> "2*TESTING_UNDER"
        LaTeX.format expr --> """2{TESTING_{UNDER}}"""
    
        let expr2 = Infix.parseOrUndefined "(TESTING_UNDER_second)*(2)"
        expr2 ==> "2*TESTING_UNDER_second"
        LaTeX.format expr2 --> """2{TESTING_{UNDER_{second}}}"""
    
    [<Test>]
    let ``Test for other trigonometric function``() =
    
        let exrp = Infix.parseOrUndefined "tan(x)*25*csc(x)"
        exrp ==> "25*tan(x)*csc(x)"
    
        let expr2 = Operators.sec 32Q
        expr2 ==> "sec(32)"
    
        let exrp3 = Expression.Apply(Function.Cot, expr2)
        exrp3 ==> "cot(sec(32))"
    
        let expr4 = Infix.parseOrUndefined "25*x*sec(x)"
        Calculus.differentiate x expr4  ==> "25*(sec(x) + x*tan(x)*sec(x))"
    
    [<Test>]
    let ``Exponential notation parsing``() =
        let expr = Infix.parseOrUndefined "(-6.40869140625E-05)*x"
        expr ==> "(-6.40869140625E-05)*x"
    
        let expr2 = Infix.parseOrUndefined "1.5e7"
        expr2 ==> "15000000"
    
        let expr3 = Infix.parseOrUndefined "-.5e7"
        expr3 ==> "-5000000"
    
        let expr4 = Infix.parseOrUndefined "58E-3"
        expr4 ==> "0.058"
    
    [<Test>]
    let ``Expression to delegate compilation``() =
        
        let symX = Symbol "x"
        let symY = Symbol "y"
    
        let toComplex f = System.Numerics.Complex.Create(f, 0.0)
        
        let expr1 = x
        (Compile.compileExpression1OrThrow expr1 symX).Invoke(3.0) --> 3.0
    
        let expr2 = x*x
        (Compile.compileExpression1OrThrow expr2 symX).Invoke(3.0) --> 9.0
    
        let expr3 = x + y
        (Compile.compileExpression2OrThrow expr3 symX symY).Invoke(3.0, 3.0) --> 6.0
    
        let expr4 = ln x
        (Compile.compileExpression1OrThrow expr4 symX).Invoke(3.0) --> System.Math.Log(3.0)
    
        let expr5 = (Constant E) ** x
        (Compile.compileExpression1OrThrow expr5 symX).Invoke(3.0) --> System.Math.Exp(3.0)
    
        let expr6 = sqrt x
        (Compile.compileExpression1OrThrow expr6 symX).Invoke(12.5) --> System.Math.Sqrt(12.5)
    
        let expr7 = x ** y
        (Compile.compileExpression2OrThrow expr7 symX symY).Invoke(12.5, 5.7) --> System.Math.Pow(12.5, 5.7)
    
        let expr8 = abs x
        (Compile.compileExpression1OrThrow expr8 symX).Invoke(-14.0) --> 14.0
    
        let expr9 = x + 1
        (Compile.compileExpression1OrThrow expr9 symX).Invoke(1.0) --> 2.0
    
        let expr1' = x
        (Compile.compileComplexExpression1OrThrow expr1' symX).Invoke(toComplex 3.0) --> toComplex 3.0
    
        let expr2' = x*x
        (Compile.compileComplexExpression1OrThrow expr2' symX).Invoke(toComplex 3.0) --> toComplex 9.0
    
        let expr3' = x + y
        (Compile.compileComplexExpression2OrThrow expr3' symX symY).Invoke(toComplex 3.0, toComplex 3.0) --> toComplex 6.0
    
        let expr4' = ln x
        (Compile.compileComplexExpression1OrThrow expr4' symX).Invoke(toComplex 3.0) --> System.Numerics.Complex.Log(toComplex 3.0)
    
        let expr5' = (Constant E) ** x
        (Compile.compileComplexExpression1OrThrow expr5' symX).Invoke(toComplex 3.0) --> System.Numerics.Complex.Exp(toComplex 3.0)
    
        let expr6' = sqrt x
        (Compile.compileComplexExpression1OrThrow expr6' symX).Invoke(toComplex 12.5) --> System.Numerics.Complex.Sqrt(toComplex 12.5)
    
        let expr7' = x ** y
        (Compile.compileComplexExpression2OrThrow expr7' symX symY).Invoke(toComplex 12.5, toComplex 5.7) --> System.Numerics.Complex.Pow(toComplex 12.5, toComplex 5.7)
    
        let expr8' = abs x
        (Compile.compileComplexExpression1OrThrow expr8' symX).Invoke(System.Numerics.Complex.Create(-14.0, 0.0)) --> toComplex 14.0
    
        let expr9' = x + 1
        (Compile.compileComplexExpression1OrThrow expr9' symX).Invoke(System.Numerics.Complex.One) --> toComplex 2.0