A new handmade online tool to differentiate to any order any common expression :
It's here : Click -> DIFFERENTIATOR
It uses Scala and especially its functionnal aspect : pattern matching, high order functions, Options ( equivalent to MayBe in Haskell )
Scala.Js allows to create dynamic webpages. The web part is rather short:
Scala
package example import org.scalajs.dom import dom.document import dom.html import scalajs.js.annotation.JSExport import scalatags.JsDom.all._ //import org.scalajs.jquery.jQuery import scala.scalajs.js.annotation.JSExportTopLevel import scala.scalajs.js import utils.Func import utils.Rpn // reading of Rpn expressions import utils.Op // manipulations of expression object TutorialApp { // a function to append a text on the page def appendPar(targetNode: dom.Node, text: String): Unit = { val parNode = document.createElement("p") val textNode = document.createTextNode(text) parNode.appendChild(textNode) targetNode.appendChild(parNode) } // Javascript interactions @JSExportTopLevel("addClickedMessage") def addClickedMessage(): Unit = { val expr:Option[utils.Func] = Rpn.rpnToFunc(document.getElementById("nodeExpr").asInstanceOf[html.Input].value.toString) val order:Int = document.getElementById("nodeOrder").asInstanceOf[html.Input].value.toInt val valev:Double = document.getElementById("nodeVal").asInstanceOf[html.Input].value.toDouble expr match { case Some(e) => appendPar(document.body, "The expression is $" + Op.show(e) + "$") appendPar(document.body, "The derivative of order " + order.toString + " is " + "$" + Op.show(Op.derivn(e, order, Func.Var("x"))) + "$") Op.eval(Op.derivn(e, order, Func.Var("x")), valev) match { case Some(v) => appendPar(document.body, "Whose value in " + valev.toString + " is " + "$" + v + "$") case _ => appendPar(document.body, "Expression unevaluable ") } case None => appendPar(document.body, "Expression mistyped ") } // reload MathJax for new expressions js.eval("MathJax.Hub.Queue([\"Typeset\",MathJax.Hub])") } // we need a main def main(args: Array[String]): Unit = { } }
Assiocated to a html file which could have been a lot shorter if created from the Scala file...TODO ;)
HTML
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Differentiator</title> <script type="text/x-mathjax-config"> MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}}); </script> <script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script> </head> <body> <h1>DIFFERENTIATOR</h1> <h2>Α simple formal "differentiator" written in Scala.Js</h2> <img src="https://www.scala-js.org/assets/badges/scalajs-1.0.0-M2.svg"> <button onclick="window.location='http://informathix.tuxfamily.org/?q=node/192'">Back to Informathix</button> <br><br><br><br> <form method="post"> Your expression to be differentiated (RPN form, variable is x): <br><br> <input type="text" placeholder="x 2 ^ 3 + log gives : log(x^2 + 3)" name="expr" id="nodeExpr" size="40"> <br><br> Order of differentiation:<br><br> <input type="text" name="order" id="nodeOrder" placeholder="2 to get the 2nd derivative" value="1"> <br><br> Evaluated in:<br><br> <input type="text" name="valev" id="nodeVal" placeholder="1 to evaluate in 1" value="1"> <br><br> <input type="button" onclick="addClickedMessage()" value="Differentiate!" id="click-me-button"> <input type="reset" value="Reset" onClick="window.location.reload()"> </form> <!-- Include Scala.js compiled code here --> <!-- Include JavaScript dependencies --> <script type="text/javascript" src="./target/scala-2.12/scala-js-tutorial-fastopt.js"></script> </body> </html>
The RPN tools using Scala's Stack structure:
Scala
package utils import java.io.{FileNotFoundException, PrintWriter, StringWriter} import Func._ import scala.util.Try object Rpn { val op2 = Map("+" -> Add, "*" -> Mul, "^" -> Pow, "frac" -> Frac, "-" -> Sub) val op1 = Map("log" -> Log, "exp" -> Exp, "cos" -> Cos, "sin" -> Sin, "neg" -> Neg, "sqrt" -> Sqrt) def parseString(s: String): Option[Double] = Try { s.toDouble }.toOption def norm[T](v: T):Func = v match { case s if (s == "x") => Var("x") case f: Func => f case n: String => parseString(n) match { case Some(d) => Const(d) case None => Param(n) } } def rpnToFunc(typed:String):Option[Func] = { val vals = typed.split(" ").filterNot(_ == "") val pile = new scala.collection.mutable.Stack[Func] for (value <- vals) { if (op1 contains value){ val op = op1(value) val n = norm(pile.pop) pile.push(op(n)) } else if (op2 contains value){ val op = op2(value) val n2 = norm(pile.pop) val n1 = norm(pile.pop) pile.push(op(n1,n2)) } else{ pile.push(norm(value)) } } val res = pile.pop if (pile.isEmpty) {Some(res)} else {None} }
And now, the big tool: how to deal with algebraic expressions...still need improvement!
Scala
package utils sealed trait Func object Func{ case class Const(value: Double) extends Func case class Param(value: String) extends Func case class Var(value: String) extends Func case class Add(f1: Func, f2: Func) extends Func case class Mul(f1: Func, f2: Func) extends Func case class Pow(f1: Func, f2: Func) extends Func case class Log(f: Func) extends Func case class Exp(f: Func) extends Func case class Cos(f: Func) extends Func case class Sin(f: Func) extends Func case class Frac(f1:Func, f2:Func) extends Func case class Sub(f1:Func, f2:Func) extends Func case class Neg(f:Func) extends Func case class Sqrt(f:Func) extends Func } object Op { import Func._ import scala.util.Try def add(f1:Func, f2: Func): Func = (f1, f2) match { case (Const(a), Const(b)) => Const(a + b) case (Add(Const(k1), f), Const(k2)) => add(Const(k1 + k2), f) case (Add(f, Const(k1)), Const(k2)) => add(Const(k1 + k2), f) case (Const(k2), Add(Const(k1), f)) => add(Const(k1 + k2), f) case (Const(k2), Add(f, Const(k1))) => add(Const(k1 + k2 ), f) case (Mul(u,v), Mul(w,z)) if v == z => mul(add(u,w),v) case (Mul(u,v), Mul(w,z)) if u == w => mul(u,add(v,z)) case (Mul(u,v), Mul(w,z)) if u == z => mul(u,add(v,w)) case (Mul(u,v), Neg(Mul(w,z))) if v == z => mul(add(u,neg(w)),v) case (Mul(u,v), Neg(Mul(w,z))) if u == w => mul(u,add(v,neg(z))) case (Mul(u,v), Neg(Mul(w,z))) if u == z => mul(u,add(v,neg(w))) case (Log(u),Log(v)) => log(mul(u,v)) case (Frac(a,b),Frac(c,d)) => frac(add(mul(a,d),mul(b,c)), mul(b,d)) case (Frac(a,b),Const(k)) => frac(add(a,mul(Const(k),b)), b) case (Const(k), Frac(a,b)) => frac(add(a,mul(Const(k),b)), b) case (f, Const(0)) => f case (Const(0), f) => f case (Const(a), Neg(Const(b))) => Const(a-b) case (Neg(Const(a)), (Const(b))) => Const(-a+b) case (u,v) if u == v => mul(Const(2),u) case _ => Add(simp(f1), simp(f2)) } def mul(f1:Func, f2: Func): Func = (f1, f2) match { case (Const(l), Const(k)) => Const(k*l) case (Const(1), f) => f case (Const(0), _) => Const(0) case (f, Const(1)) => f case (_, Const(0)) => Const(0) case (Neg(u),Neg(v)) => mul(u,v) case (Const(k2), Mul(Const(k1), f)) => mul(Const(k1 * k2), f) case (Mul(Const(k1), f), Const(k2)) => mul(Const(k1 * k2), f) case (Mul(Const(k1),u), Mul(Const(k2),v)) => mul(Const(k1*k2),mul(u,v)) case (u, Mul(Const(k), v)) => mul(Const(k), mul(u,v)) case (Mul(Const(k), v),u) => mul(Const(k), mul(u,v)) case (Mul(v, Const(k)),u) => mul(Const(k), mul(u,v)) case (u,Mul(v, Const(k))) => mul(Const(k), mul(u,v)) case (Frac(Const(1),b), Frac(Const(1),d)) => frac(Const(1),mul(b,d)) case (Const(k), Frac(Const(1),Mul(Const(l),u))) => mul(frac(Const(k),Const(l)), frac(Const(1),u)) //case (Frac(a,b),Const(k)) => frac(mul(Const(k),a),b) //case (Const(k),Frac(a,b)) => frac(mul(Const(k),a),b) case (Pow(u,n),Pow(v,k)) if u == v => pow(u,add(n,k)) case (Pow(u,Const(n)),Frac(Const(1),Pow(v,Const(k)))) if u == v =>{ if (n >= k) {pow(u,add(Const(n),neg(Const(k))))} else {frac(Const(1),pow(u,add(Const(k),neg(Const(n)))))} } case (Frac(Const(1),Pow(v,Const(k))),Pow(u,Const(n))) if u == v =>{ if (n >= k) {pow(u,add(Const(n),neg(Const(k))))} else {frac(Const(1),pow(u,add(Const(k),neg(Const(n)))))} } case (u,Frac(Const(1),Pow(v,Const(k)))) if u == v =>{ frac(Const(1),pow(u,add(Const(k),neg(Const(1))))) } case (Frac(Const(1),v),Pow(u,Const(n))) if u == v =>{ pow(u,add(Const(n),neg(Const(1)))) } case (Frac(Const(1),Pow(v,Const(k))),u) if u == v =>{ frac(Const(1),pow(u,add(Const(k),neg(Const(1))))) } case (Pow(u,Const(n)),Frac(Const(1),v)) if u == v =>{ pow(u,add(Const(n),neg(Const(1)))) } case (Pow(u,n),v) if u == v => pow(u,add(n,Const(1))) case (v,Pow(u,n)) if u == v => pow(u,add(n,Const(1))) case (v,u) if u == v => pow(u,Const(2)) case (Exp(u), Exp(v)) => exp(add(u,v)) case (Sqrt(u), Sqrt(v)) => sqrt(mul(u,v)) case (u, Neg(v)) => neg(mul(u,v)) case (Neg(u),v) => neg(mul(u,v)) case (u,Const(k)) => mul(Const(k),u) case (u,Frac(Const(k),Const(l))) => mul(frac(Const(k),Const(l)),u) case _ => Mul(simp(f1), simp(f2)) } def pow(f1:Func, f2: Func): Func = (f1, f2) match { case (_, Const(0)) => Const(1) case (_, Const(1)) => f1 case (Const(a), Const(b)) => Const(math.pow(a,b)) case (Pow(u,n),p) => pow(u,mul(n,p)) case (u, Const(k)) if k <0 => frac(Const(1),pow(u,Const(-k))) case (Mul(Const(k),u),Const(n)) => mul(Const(math.pow(k,n)),pow(u,Const(n))) case (Mul(u,Const(k)),Const(n)) => mul(Const(math.pow(k,n)),pow(u,Const(n))) //case (Frac(a,b),n) => frac(pow(a,n),pow(b,n)) case _ => Pow((f1), (f2)) } def isInt(a:Double):Boolean = a.toInt == a def gcd(a:Double, b:Double):Double = { if (b == 0.0) {a} else {gcd(math.abs(b), (math.abs(a).toInt % math.abs(b).toInt).toDouble)} } def frac(f1: Func, f2:Func): Func = (f1, f2) match { case (u,v) if u == v => Const(1) case (u, Const(1)) => u case (Const(a),Const(b)) if isInt(a) & isInt(b) => {var g = gcd(a,b) if (g > 1) {Frac(Const(a/g),Const(b/g))} else {Frac(f1,f2)} } case (Neg(u),Neg(v)) => frac(u,v) // case (u, Frac(a,b)) => frac(mul(u,b),a) // case (Frac(a,b),u) => frac(a,mul(u,b)) case (Pow(u,n), Pow(v,k)) if u == v => pow(u, add(n, mul(Const(-1),k))) case (u, Pow(v,k)) if u == v => pow(u, add(Const(1), mul(Const(-1),k))) case (Pow(v,k),u) if u == v => pow(u, add(k, Const(-1))) case (Mul(Const(k),Pow(u,n)), Pow(v,l)) if u == v => mul(Const(k),pow(u, add(n, mul(Const(-1),l)))) case (Mul(Const(k),Pow(u,n)), v) if u == v => mul(Const(k),pow(u, add(n, Const(-1)))) case (Pow(u,n), Mul(Const(k),Pow(v,l))) if u == v => frac(Const(1),mul(Const(k), pow(u, add(l, mul(Const(-1),n))))) case (Mul(Const(k),u), Pow(v,l)) if u == v => frac(Const(k),pow(u, add(l, Const(-1)))) case (u, Mul(Const(k),Pow(v,l))) if u == v => frac(Const(1),mul(Const(k), pow(u, add(l, Const(-1))))) case (Neg(u),v) => neg(frac(u,v)) case (u,Neg(v)) => neg(frac(u,v)) // // //case (Mul(Const(k),u),v) => mul(Const(k),frac(u,v)) case (Const(k),Mul(Const(l),v)) if k != 1 => mul(frac(Const(k),Const(l)),frac(Const(1),v)) case (Const(k),v) if k != 1 => mul(Const(k),frac(Const(1),v)) case (u,Mul(v,w)) if u == v => frac(Const(1),w) case (u,Mul(v,w)) if u == w => frac(Const(1),v) case (Mul(v,w),u) if u == v => w case (Mul(v,w),u) if u == w => v //case (Frac(Const(1),u),Mul(Const(k),Frac(Const(1),v))) => // mul(frac(Const(1),Const(k)),frac(v,u)) // //case (u,v) => mul(u,pow(v,Const(-1))) */ case (Mul(Const(k),u),Mul(Const(l),v)) => mul(frac(Const(k),Const(l)),frac(u,v)) case (Mul(Const(k),u),v) => mul(Const(k),frac(u,v)) case (Frac(a,b),Frac(c,d)) => frac(mul(a,d),mul(b,c)) case _ => Frac(simp(f1),simp(f2)) } def log(f:Func): Func = f match { case Exp(u) => u case Pow(u,n) => mul(n,log(u)) case Frac(Const(1),v) => neg(log(v)) // case Mul(Const(k),v) => add(log(v),log(Const(k))) case _ => Log(simp(f)) } def exp(f:Func): Func = f match { case Log(u) => u case _ => Exp(f) } def cos(f:Func): Func = Cos(f) def sin(f:Func): Func = Sin(f) def sqrt(f:Func): Func = f match { case Const(1) => Const(1) case Mul(Const(k),Var(x)) => mul(sqrt(Const(k)),pow(Var(x),Const(0.5))) case Frac(Const(k),Var(x)) => frac(sqrt(Const(k)),pow(Var(x),Const(0.5))) case _ => Sqrt(simp(f)) } def neg(f:Func): Func = f match { //case Frac(u,v) => frac(neg(u),v) case Const(0) => Const(0) case Neg(u) => u case Mul(Neg(u),v) => mul(u,v) case Mul(v,Neg(u)) => mul(v,u) //case Const(k) => Const(-k) case _ => Neg((f)) } def simp(f:Func): Func = f match{ case Const(v) => Const(v) case Param(v) => Param(v) case Var(v) => Var(v) case Add(f1, f2) => add(f1,f2) case Mul(f1, f2) => mul(f1,f2) case Pow(f1, f2) => pow(f1,f2) case Log(f) => log(f) case Exp(f) => exp(f) case Cos(f) => cos(f) case Sin(f) => sin(f) case Frac(f1, f2) => frac(f1,f2) case Sub(f1, f2) => add(f1, neg(f2)) case Neg(f) => neg(f) case Sqrt(f) => sqrt(f) } def deriv(f:Func, x:Func): Func = simp(f) match { case Neg(u) => neg(deriv(u,x)) case Var(_) => Const(1) case Const(_) | Param(_) => Const(0) case Add(u,v) => add(deriv(u,x), deriv(v,x)) case Sub(u,v) => add(deriv(u,x), neg(deriv(v,x))) case Mul(Const(k),v) => mul(Const(k),deriv(v,x)) case Mul(v,Const(k)) => mul(Const(k),deriv(v,x)) case Mul(u,v) => add(mul(deriv(u,x),v), mul(deriv(v,x),u)) case Pow(u,n) => mul(n, mul(deriv(u,x), pow(u, add(n, Const(-1))))) case Frac(u,v) => // deriv(mul(u, pow(v,Const(-1))),x) (u,v) match { case (z,Const(k)) => mul(frac(Const(1),Const(k)),deriv(z,x)) case (Const(1),w) => neg(frac(deriv(w,x),pow(w,Const(2)))) case _ => frac(add(mul(deriv(u,x),v), neg(mul(deriv(v,x),u))), pow(v,Const(2))) } case Log(u) => mul(deriv(u,x), pow(u, Const(-1))) case Exp(u) => mul(deriv(u,x), exp(u)) case Cos(u) => neg( mul(deriv(u,x), Sin(u))) case Sin(u) => mul(deriv(u,x), Cos(u)) case Sqrt(u) => frac(deriv(u,x),mul(Const(2),Sqrt(u))) //case Var(y), Var(t)) => if (t == y) {Const(1)} else {Const(0)} } def derivn(f: Func, n: Int, x: Func): Func = if (n == 0) {simp(f)} else {derivn(simp(deriv(f,x)), n - 1, x)} def parseDouble(s: Double): Option[Double] = Try { s.toDouble }.toOption def parseOp2(o1:Option[Double], o2:Option[Double], op: (Double,Double) => Double): Option[Double] = (o1,o2) match { case (Some(d1), Some(d2)) => parseDouble(op(d1,d2)) case _ => None } def parseOp1(o1:Option[Double], op: Double => Double): Option[Double] = o1 match { case Some(d1) => parseDouble(op(d1)) case _ => None } def eval(f: Func, t: Double): Option[Double] = simp(f) match { case Param(_) => None case Var(_) => parseDouble(t) case Const(k) => parseDouble(k) case Mul(u, v) => parseOp2(eval(u,t),eval(v,t),(x,y)=>x*y) case Add(u, v) => parseOp2(eval(u,t),eval(v,t),(x,y)=>x+y) case Sub(u, v) => parseOp2(eval(u,t),eval(v,t),(x,y)=>x-y) case Frac(u, v)=> parseOp2(eval(u,t),eval(v,t),(x,y)=>x/y) case Pow(u, n) => parseOp2(eval(u,t),eval(n,t),math.pow) case Log(u) => parseOp1(eval(u,t), math.log) case Cos(u) => parseOp1(eval(u,t), math.cos) case Sin(u) => parseOp1(eval(u,t), math.sin) case Exp(u) => parseOp1(eval(u,t), math.exp) case Sqrt(u) => parseOp1(eval(u,t), math.sqrt) case Neg(u) => parseOp1(eval(u,t), x => -x) case _ => None } def show(f: Func): String = simp(f) match { case Const(k) => k.toString() case Var(x) => x case Param(k) => k case Mul(Const(-1),u) => "-" + show(u) case Mul(u,Const(-1)) => "-" + show(u) case Add(u,Neg(v)) => "\\left(" + show(u) + "-" + show(v) + "\\right)" case Add(u,Mul(Neg(v),w)) => "\\left(" + show(u) + "-" + show(mul(v,w)) + "\\right)" case Add(u,Mul(Mul(Const(-1),v),w)) => "\\left(" + show(u) + "-" + show(mul(v,w)) + "\\right)" case Add(u,v) => "\\left(" + show(u) + "+" + show(v) + "\\right)" case Sub(u,v) => "\\left(" + show(u) + " - " + show(v) + "\\right)" case Mul(u,v) => show(u) + "\\cdot " + show(v) case Log(u) => u match { case Exp(v) => show(v) case _ => "\\log\\left(" + show(u) + "\\right)"} case Exp(u) => u match { case Log(v) => show(v) case _ => "{\\rm e}^{" + show(u) + "}"} case Cos(u) => "\\cos\\left(" + show(u) + "\\right)" case Sin(u) => "\\sin\\left(" + show(u) + "\\right)" case Sqrt(u) => u match { case Pow(v,Const(2)) => "|" + show(v) + "|" case _ => "\\sqrt{" + show(u) + "}"} case Neg(u) => "=\\left(" + show(u) + "\\right)" case Frac(u,v) => "\\dfrac{" + show(u) + "}{" + show(v) + "}" case Pow(u,v) => (u,v) match { case (w,Const(0.5)) => w match { case Pow(z,Const(2)) => "|" + show(z) + "|" case _ => "\\sqrt{" + show(u) + "}"} case (w,Const(2)) => w match { case Sqrt(z) => show(z) case Pow(y,Const(0.5)) => show(y) case Const(_)|Var(_)|Param(_) => show(u) + "^2" case _ => "\\left(" + show(u) + "\\right)^{2}"} case (Const(_)|Var(_)|Param(_),_) => show(u) + "^{" + show(v) + "}" case _ => "\\left(" + show(u) + "\\right)^{" + show(v) + "}" } } }