http://mathling.com/core/functions  library module

http://mathling.com/core/functions


Functions that we need to perform analytical operations on, e.g.
arithmetic ops, derivative(), etc.
A unifying API for things like polynomials, complex polynomials, polynomial
quotients, etc.

Provides for polymorphism over a certain class of functions in the same way
that geo/euclidean provides polymorphism over a class of geometric objects.

Kinds of functions supported:
constants (core/complex)
real polynomials over x, sin(x), and cos(x) (types/polynomial)
complex polynomials over x, sin(x), and cos(x) (types/cpolynomial)
adhoc functions with certain operators determined by function keys (limited)

Note: limitations on some quotient operators right now

Copyright© Mary Holstege 2022-2025
CC-BY (https://creativecommons.org/licenses/by/4.0/)

April 2023
Status: Bleeding edge

Function Index

Imports

http://mathling.com/type/polynomial/quotient
import module namespace quotient="http://mathling.com/type/polynomial/quotient"
       at "../types/quotient.xqy"
http://mathling.com/core/utilities
import module namespace util="http://mathling.com/core/utilities"
       at "../core/utilities.xqy"
http://mathling.com/type/polynomial
import module namespace poly="http://mathling.com/type/polynomial"
       at "../types/polynomial.xqy"
http://mathling.com/core/complex
import module namespace z="http://mathling.com/core/complex"
       at "../core/complex.xqy"
http://mathling.com/core/complex/vector
import module namespace zv="http://mathling.com/core/complex/vector"
       at "../core/vcomplex.xqy"
http://mathling.com/type/adhoc-function
import module namespace adhoc="http://mathling.com/type/adhoc-function"
       at "../types/adhoc-function.xqy"
http://mathling.com/core/config
import module namespace config="http://mathling.com/core/config"
       at "../core/config.xqy"
http://mathling.com/type/polynomial/complex
import module namespace zpoly="http://mathling.com/type/polynomial/complex"
       at "../types/cpolynomial.xqy"
http://mathling.com/core/errors
import module namespace errors="http://mathling.com/core/errors"
       at "../core/errors.xqy"

Functions

Function: value
declare function value($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*)


value()
The value of f(x) for a real-valued x. Returns a complex value:
use z:as-real() if you need to.

Params
  • f as map(xs:string,item()*): an object representing a certain kind of function
  • x as xs:double: double value
Returns
  • map(xs:string,item()*): : complex value f(x)
declare function this:value(
  $f as map(xs:string,item()*),
  $x as xs:double
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then $f
    else if ($kind="polynomial") then poly:value($f, $x)=>z:as-complex()
    else if ($kind="complex-polynomial") then zpoly:value($f, $x)
    else if ($kind="polynomial-quotient") then (
      this:value(quotient:u($f), $x)=>z:divide( this:value(quotient:v($f), $x) )
    ) else if ($f("value") instance of function(*)) then (
      $f("value")($f, $x)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: zvalue
declare function zvalue($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*)


zvalue()
The value of f(z) for a complex value z

Params
  • f as map(xs:string,item()*): an object representing a certain kind of function
  • z as map(xs:string,item()*): complex value
Returns
  • map(xs:string,item()*): : a complex value f(z)
declare function this:zvalue(
  $f as map(xs:string,item()*),
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then $f
    else if ($kind="polynomial") then poly:zvalue($f, $z)
    else if ($kind="complex-polynomial") then zpoly:zvalue($f, $z)
    else if ($kind="polynomial-quotient") then (
      this:zvalue(quotient:u($f), $z)=>z:divide( this:zvalue(quotient:v($f), $z) )
    ) else if ($f("zvalue") instance of function(*)) then (
      $f("zvalue")($f, $z)
    ) else if ($f("zvaluev") instance of function(*)) then (
      $f("zvaluev")($f, z:as-vector($z))=>z:vector()
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: zvaluev
declare function zvaluev($f as map(xs:string,item()*), $z as xs:double*) as xs:double*


zvaluev()
The value of f(z) for a complex value z vector

Params
  • f as map(xs:string,item()*): an object representing a certain kind of function
  • z as xs:double*: complex value vector
Returns
  • xs:double*: : a complex value f(z) vector
declare function this:zvaluev(
  $f as map(xs:string,item()*),
  $z as xs:double*
) as xs:double*
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then $f=>z:as-vector()
    else if ($kind="polynomial") then poly:zvaluev($f, $z)
    else if ($kind="complex-polynomial") then zpoly:zvaluev($f, $z)
    else if ($kind="polynomial-quotient") then (
      this:zvaluev(quotient:u($f), $z)=>zv:divide( this:zvaluev(quotient:v($f), $z) )
    ) else if ($f("zvaluev") instance of function(*)) then (
      $f("zvaluev")($f, $z)
    ) else if ($f("zvalue") instance of function(*)) then (
      $f("zvalue")($f, z:vector($z))=>z:as-vector()
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: derivative
declare function derivative($f as map(xs:string,item()*)) as map(xs:string,item()*)


derivative()
Compute the derivative of the function.

Params
  • f as map(xs:string,item()*): an object representing a certain kind of function
Returns
  • map(xs:string,item()*): : an object representing the function that is the derivative of f
declare function this:derivative(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then (
      z:as-complex(0)
    ) else if ($kind="polynomial") then (
      poly:derivative($f)
    ) else if ($kind="complex-polynomial") then (
      zpoly:derivative($f)
    ) else if ($kind="polynomial-quotient") then (
      (: d(u/v) = (v du - u dv) / v² :)
      let $u := quotient:u($f)
      let $v := quotient:v($f)
      let $du := this:derivative($u)
      let $dv := this:derivative($v)
      return (
        quotient:quotient(
          (this:multiply($v, $du)=>this:sub( this:multiply($u, $dv) )),
          this:multiply($v, $v)
        )
      )
    ) else if ($f("derivative") instance of function(*)) then (
      $f("derivative")($f)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: antiderivative
declare function antiderivative($f as map(xs:string,item()*)) as map(xs:string,item()*)


antiderivative()
Compute the anti-derivative of the function.

Params
  • f as map(xs:string,item()*): an object representing a certain kind of function
Returns
  • map(xs:string,item()*): : an object representing the function that is the anti-derivative of f
declare function this:antiderivative(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then (
      if (z:eq($f, 0)) then $f else zpoly:polynomial( ($f, z:as-complex(0)) )
    ) else if ($kind="polynomial") then (
      poly:antiderivative($f)
    ) else if ($kind="complex-polynomial") then (
      zpoly:antiderivative($f)
    ) else if ($kind="polynomial-quotient") then (
      (: Not easy:
       : ∫u'/v = u/v + ∫(uv'/v²) so
       : ∫u/v = (∫u)/v + ∫((∫u)v'/v²)
       : Which in theory we could keep going on until the right hand bit gets
       : down to a v' that is 0, but skip this for now
       :)
      errors:error("UTIL-NOTIMPLEMENTED", ("anti-derivative", $f))
    ) else if ($f("antiderivative") instance of function(*)) then (
      $f("antiderivative")($f)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: definite-integral
declare function definite-integral($f as map(xs:string,item()*), $a as xs:double, $b as xs:double) as xs:double


definite-integral()
Definite integral of a function (exact)
Note: if the result includes an imaginary part, this will raise
an error.

Params
  • f as map(xs:string,item()*): the function
  • a as xs:double: start of range of integration
  • b as xs:double: end of range of integration
Returns
  • xs:double
declare function this:definite-integral(
  $f as map(xs:string,item()*),
  $a as xs:double,
  $b as xs:double
) as xs:double
{
  let $anti := this:antiderivative($f)
  return ($anti=>this:value($b)=>z:sub( $anti=>this:value($a) ) )=>z:as-real()
}

Function: describe
declare function describe($f as map(xs:string,item()*)) as xs:string


describe()
Describe the function

Params
  • f as map(xs:string,item()*): the function
Returns
  • xs:string
declare function this:describe(
  $f as map(xs:string,item()*)
) as xs:string
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then z:quote($f)
    else if ($kind="polynomial") then poly:describe($f)
    else if ($kind="complex-polynomial") then zpoly:describe($f)
    else if ($kind="polynomial-quotient") then (
      "("||this:describe(quotient:u($f))||")/("||this:describe(quotient:v($f))||")"
    ) else if ($f("describe") instance of function(*)) then (
      $f("describe")($f)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: function
declare function function($f as map(xs:string,item()*)) as function(xs:double) as xs:double


function()
Return a function that takes values of the function, e.g. for curve plotting

Params
  • f as map(xs:string,item()*): the function
Returns
  • function(xs:double)asxs:double
declare function this:function(
  $f as map(xs:string,item()*)
) as function(xs:double) as xs:double
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then
      function($v as xs:double) as xs:double {
        z:as-real($f)
      }
    else if ($kind="polynomial") then poly:function($f)
    else if ($kind="complex-polynomial") then zpoly:function($f)
    else if ($kind="polynomial-quotient") then (
      let $uf := this:function(quotient:u($f))
      let $vf := this:function(quotient:v($f))
      return (
        function($v as xs:double) as xs:double {
          $uf($v) div $vf($v)
        }
      )
    ) else if ($f("function") instance of function(*)) then (
      $f("function")($f)
    ) else if ($f("value") instance of function(*)) then (
      function ($v as xs:double) as xs:double {
        $f("value")($f, $v)
      }
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: zfunction
declare function zfunction($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*)


zfunction()
Return a function over complex values that takes values of the function, e.g. for curve plotting

Params
  • f as map(xs:string,item()*): the function
Returns
  • function(map(xs:string,item()*))asmap(xs:string,item()*)
declare function this:zfunction(
  $f as map(xs:string,item()*)
) as function(map(xs:string,item()*)) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then
      function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
        $f
      }
    else if ($kind="polynomial") then poly:zfunction($f)
    else if ($kind="complex-polynomial") then zpoly:zfunction($f)
    else if ($kind="polynomial-quotient") then (
      let $uf := this:zfunction(quotient:u($f))
      let $vf := this:zfunction(quotient:v($f))
      return (
        function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
          $uf($v)=>z:divide( $vf($v) )
        }
      )
    ) else if ($f("zfunction") instance of function(*)) then (
      $f("zfunction")($f)
    ) else if ($f("zvalue") instance of function(*)) then (
      function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
        $f("zvalue")($f, $v)
      }
    ) else if ($f("zvaluev") instance of function(*)) then (
      function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
        $f("zvaluev")($f, z:as-vector($v))=>z:vector()
      }
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: zfunctionv
declare function zfunctionv($f as map(xs:string,item()*)) as function(xs:double*) as xs:double*


zfunctionv()
Return a function over complex values (vector form) that takes values of the function, e.g. for curve plotting

Params
  • f as map(xs:string,item()*): the function
Returns
  • function(xs:double*)asxs:double*
declare function this:zfunctionv(
  $f as map(xs:string,item()*)
) as function(xs:double*) as xs:double*
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then
      function($v as xs:double*) as xs:double* {
        z:as-vector($f)
      }
    else if ($kind="polynomial") then poly:zfunctionv($f)
    else if ($kind="complex-polynomial") then zpoly:zfunctionv($f)
    else if ($kind="polynomial-quotient") then (
      let $uf := this:zfunctionv(quotient:u($f))
      let $vf := this:zfunctionv(quotient:v($f))
      return (
        function($v as xs:double*) as xs:double* {
          $uf($v)=>zv:divide( $vf($v) )
        }
      )
    ) else if ($f("zfunctionv") instance of function(*)) then (
      $f("zfunctionv")($f)
    ) else if ($f("zfunction") instance of function(*)) then (
      let $zf := $f("zfunction")($f)
      return (
        function ($v as xs:double*) as xs:double* {
          $zf(z:vector($v))=>z:as-vector()
        }
      )
    ) else if ($f("zvaluev") instance of function(*)) then (
      function($v as xs:double*) as xs:double* {
        $f("zvaluev")($f, $v)
      }
    ) else if ($f("zvalue") instance of function(*)) then (
      function($v as xs:double*) as xs:double* {
        $f("zvalue")($f, z:vector($v))=>z:as-vector()
      }
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: add
declare function add($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


add()
Add two functions

Params
  • f1 as map(xs:string,item()*): one function
  • f2 as map(xs:string,item()*): another function
Returns
  • map(xs:string,item()*): object representing f1+f2
declare function this:add(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex") then $f1=>z:add($f2) else this:add($f2, $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        zpoly:as-polynomial($f1)=>zpoly:add( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (poly:op($f1)=poly:op($f2))
        then $f1=>poly:add($f2)
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then zpoly:as-polynomial($f1)=>zpoly:add( $f2 )
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add($f2, $f1)
      ) else (
        this:adhoc-add($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        $f1=>zpoly:add( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (zpoly:op($f1)=poly:op($f2))
        then $f1=>zpoly:add( zpoly:as-polynomial($f2) )
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then $f1=>zpoly:add($f2)
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add($f2, $f1)
      ) else (
        this:adhoc-add($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: b/c + a = (b + ac) / c :)
        quotient:quotient(
          this:add( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient(
          this:add( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b + c/d = (ad + cb) / (bd) :)
        return (
          quotient:quotient(
            this:add( this:multiply($u1, $v2), this:multiply($u2, $v1) ),
            this:multiply( $v1, $v2 )
          )
        )
      ) else (
        this:adhoc-add($f1, $f2)
      )
    ) else (
      this:adhoc-add($f1, $f2)
    )
  )
}

Function: sub
declare function sub($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


sub()
Subtract two functions

Params
  • f1 as map(xs:string,item()*): one function
  • f2 as map(xs:string,item()*): another function
Returns
  • map(xs:string,item()*): object representing f1-f2
declare function this:sub(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex") then $f1=>z:sub($f2) else this:add(this:minus($f2), $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        zpoly:as-polynomial($f1)=>zpoly:sub( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (poly:op($f1)=poly:op($f2))
        then $f1=>poly:sub($f2)
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then zpoly:as-polynomial($f1)=>zpoly:sub( $f2 )
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add(this:minus($f2), $f1)
      ) else (
        this:adhoc-sub($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        $f1=>zpoly:sub( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (zpoly:op($f1)=poly:op($f2))
        then $f1=>zpoly:sub( zpoly:as-polynomial($f2) )
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then $f1=>zpoly:sub($f2)
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add(this:minus($f2), $f1)
      ) else (
        this:adhoc-sub($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: b/c - a = (b - ac) / c :)
        quotient:quotient(
          this:sub( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="complex-polynomial") then (
        (: b/c - a = (b + ac) / c :)
        quotient:quotient(
          this:sub( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b - c/d = (ad - cb) / (bd) :)
        return (
          quotient:quotient(
            this:sub( this:multiply($u1, $v2), this:multiply($u2, $v1) ),
            this:multiply( $v1, $v2 )
          )
        )
      ) else (
        this:adhoc-sub($f1, $f2)
      )
    ) else (
      this:adhoc-sub($f1, $f2)
    )
  )
}

Function: multiply
declare function multiply($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


multiply()
Multiply two functions

Params
  • f1 as map(xs:string,item()*): one function
  • f2 as map(xs:string,item()*): another function
Returns
  • map(xs:string,item()*): object representing f1 * f2
declare function this:multiply(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex") then $f1=>z:multiply($f2) else this:multiply($f2, $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(z:as-real($f2))
        else zpoly:as-polynomial($f1)=>zpoly:multiply( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (poly:op($f1)=poly:op($f2))
        then $f1=>poly:multiply($f2)
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (poly:op($f1)=zpoly:op($f2))
        then zpoly:as-polynomial($f1)=>zpoly:multiply( $f2 )
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:multiply($f2, $f1)
      ) else (
        this:adhoc-multiply($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(z:as-real($f2))
        else $f1=>zpoly:multiply( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (zpoly:op($f1)=poly:op($f2))
        then $f1=>zpoly:multiply( zpoly:as-polynomial($f2) )
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then $f1=>zpoly:multiply($f2)
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:multiply($f2, $f1)
      ) else (
        this:adhoc-multiply($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: b/c * a = ba / c :)
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b * c/d = (ac) / (bd) :)
        return (
          quotient:quotient(
            this:multiply( $u1, $u1 ),
            this:multiply( $v1, $v2 )
          )
        )
      ) else (
        this:adhoc-multiply($f1, $f2)
      )
    ) else (
      this:adhoc-multiply($f1, $f2)
    )
  )
}

Function: divide
declare function divide($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


divide()
Divide two functions

Params
  • f1 as map(xs:string,item()*): one function
  • f2 as map(xs:string,item()*): another function
Returns
  • map(xs:string,item()*): object representing f1/f2
declare function this:divide(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex")
      then $f1=>z:divide($f2)
      else this:multiply(z:reciprocal($f2), $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(1 div z:as-real($f2))
        else zpoly:as-polynomial($f1)=>zpoly:ztimes( z:reciprocal($f2) )
      ) else if ($kind2="polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        (: a / (b/c) =  ac / b :)
        quotient:quotient(
          this:multiply($f1, quotient:v($f2)),
          quotient:u($f2)
        )
      ) else (
        this:adhoc-divide($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(1 div z:as-real($f2))
        else $f1=>zpoly:ztimes( z:reciprocal($f2) )
      ) else if ($kind2="polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        (: a / (b/c) =  ac / b :)
        quotient:quotient(
          this:multiply($f1, quotient:v($f2)),
          quotient:u($f2)
        )
      ) else (
        this:adhoc-divide($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), z:reciprocal($f2)),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: (b/c) / a = b / ca :)
        quotient:quotient(
          quotient:u($f1),
          this:multiply(quotient:v($f1), $f2)
        )
      ) else if ($kind2="complex-polynomial") then (
        (: (b/c) / a = b / ca :)
        quotient:quotient(
          quotient:u($f1),
          this:multiply(quotient:v($f1), $f2)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b / c/d = a/b * d/c = ad/bc :)
        return (
          quotient:quotient(
            this:multiply( $u1, $v2),
            this:multiply( $v1, $u2 )
          )
        )
      ) else (
        this:adhoc-divide($f1, $f2)
      )
    ) else (
      this:adhoc-divide($f1, $f2)
    )
  )
}

Function: pow
declare function pow($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


pow()
Raise one function to another

Params
  • f1 as map(xs:string,item()*): one function
  • f2 as map(xs:string,item()*): another function
Returns
  • map(xs:string,item()*): object representing f1^f2
declare function this:pow(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:adhoc-pow($f1, $f2)
}

Function: log
declare function log($f1 as map(xs:string,item()*)) as map(xs:string,item()*)


log()
Log of a function

Params
  • f1 as map(xs:string,item()*): one function
Returns
  • map(xs:string,item()*): object representing log(f1)
declare function this:log(
  $f1 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:adhoc-log($f1)
}

Function: times
declare function times($f as map(xs:string,item()*), $k as xs:double) as map(xs:string,item()*)


times()
Multiply function by constant

Params
  • f as map(xs:string,item()*): the function
  • k as xs:double: constant
Returns
  • map(xs:string,item()*): object representing k*f
declare function this:times(
  $f as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then z:times($f, $k)
    else if ($kind="polynomial") then poly:times($f, $k)
    else if ($kind="complex-polynomial") then zpoly:times($f, $k)
    else if ($kind="polynomial-quotient") then (
      quotient:quotient(
        this:times(quotient:u($f), $k),
        quotient:v($f)
      )
    ) else (
      this:adhoc-times($f, $k)
    )
  )
}

Function: minus
declare function minus($f as map(xs:string,item()*)) as map(xs:string,item()*)


minus()
Negate the function

Params
  • f as map(xs:string,item()*): the function
Returns
  • map(xs:string,item()*): object representing -f
declare function this:minus(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then z:minus($f)
    else if ($kind="polynomial") then poly:times($f, -1)
    else if ($kind="complex-polynomial") then zpoly:times($f, -1)
    else if ($kind="polynomial-quotient") then (
      quotient:quotient(
        this:minus(quotient:u($f)),
        quotient:v($f)
      )
    ) else (
      this:adhoc-minus($f)
    )
  )
}

Function: order
declare function order($f as map(xs:string,item()*)) as xs:integer


order()
Order of the function. Raises an error for ad hoc functions with no
defined order.

Params
  • f as map(xs:string,item()*): the function
Returns
  • xs:integer: polynomial order of the function (if there is a defined order)
declare function this:order(
  $f as map(xs:string,item()*)
) as xs:integer
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then 0
    else if ($kind="polynomial") then poly:order($f)
    else if ($kind="complex-polynomial") then zpoly:order($f)
    else if ($kind="polynomial-quotient") then (
      abs(this:order(quotient:u($f)) - this:order(quotient:v($f)))
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
}

Function: derivative-add
declare function derivative-add($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


derivative-add()
Return a function object representing the derivative of the sum of two
functions, suitable for using with adhoc:set-derivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing d(f1+f2)
declare function this:derivative-add(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f + g) = df + dg :)
  this:adhoc-add(this:derivative($f1), this:derivative($f2))
}

Function: derivative-sub
declare function derivative-sub($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


derivative-sub()
Return a function object representing the derivative of the difference of two
functions, suitable for using with adhoc:set-derivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing d(f1-f2)
declare function this:derivative-sub(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f - g) = df - dg :)
  this:adhoc-sub(this:derivative($f1), this:derivative($f2))
}

Function: derivative-multiply
declare function derivative-multiply($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


derivative-multiply()
Return a function object representing the derivative of the product of two
functions, suitable for using with adhoc:set-derivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing d(f1*f2)
declare function this:derivative-multiply(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f * g) = f*dg + g*df :)
  this:adhoc-add(
    this:adhoc-multiply($f1, this:derivative($f2)),
    this:adhoc-multiply($f2, this:derivative($f1))
  )
}

Function: derivative-divide
declare function derivative-divide($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


derivative-divide()
Return a function object representing the derivative of the quotient of two
functions, suitable for using with adhoc:set-derivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing d(f1/f2)
declare function this:derivative-divide(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f / g) = (df*g + dg*f)/g*g :)
  this:adhoc-sub(
    this:adhoc-multiply(this:derivative($f1), $f2),
    this:adhoc-multiply(this:derivative($f2), $f1)
  )=>this:adhoc-divide(
    this:adhoc-multiply($f2, $f2)
  )
}

Function: derivative-pow
declare function derivative-pow($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


derivative-pow()
Return a function object representing the derivative of the raising of
one function to the other, suitable for using with adhoc:set-derivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing d(f1^f2)
declare function this:derivative-pow(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f^g) = (f^g)(df (g/f) + dg ln(f) ) :)
  this:adhoc-multiply(
    this:adhoc-pow($f1, $f2),
    this:adhoc-add(
      this:adhoc-multiply(
        this:derivative($f1),
        this:adhoc-divide($f2, $f1)
      ),
      this:adhoc-multiply(
        this:derivative($f2),
        this:adhoc-log($f1)
      )
    )
  )
}

Function: derivative-log
declare function derivative-log($f1 as map(xs:string,item()*)) as map(xs:string,item()*)


derivative-log()
Return a function object representing the derivative of taking the
log of a function.

Params
  • f1 as map(xs:string,item()*): function object for first function
Returns
  • map(xs:string,item()*): object representing d(log(f1))
declare function this:derivative-log(
  $f1 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(log(f)) = d(f)/f :)
  this:adhoc-divide( this:derivative($f1), $f1 )
}

Function: derivative-times
declare function derivative-times($f as map(xs:string,item()*), $k as xs:double) as map(xs:string,item()*)


derivative-times()
Return a function object representing the derivative of the scaling of
a function by a constant, suitable for using with adhoc:set-derivative().

Params
  • f as map(xs:string,item()*): function object for function
  • k as xs:double: constant
Returns
  • map(xs:string,item()*): object representing d(k*f)
declare function this:derivative-times(
  $f as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  (: d(k*f) = k*df :)
  this:adhoc-times(this:derivative($f), $k)
}

Function: derivative-minus
declare function derivative-minus($f as map(xs:string,item()*)) as map(xs:string,item()*)


derivative-minus()
Return a function object representing the derivative of the negation of
a function, suitable for using with adhoc:set-derivative().

Params
  • f as map(xs:string,item()*): function object for function
Returns
  • map(xs:string,item()*): object representing d(-f)
declare function this:derivative-minus(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(-f) = -df :)
  this:adhoc-minus(this:derivative($f))
}

Function: antiderivative-add
declare function antiderivative-add($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


antiderivative-add()
Return a function object representing the antiderivative of the sum of two
functions, suitable for using with adhoc:set-antiderivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing ∫(f1+f2)
declare function this:antiderivative-add(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f + g) = ∫f + ∫g :)
  this:adhoc-add(this:antiderivative($f1), this:antiderivative($f2))
}

Function: antiderivative-sub
declare function antiderivative-sub($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


antiderivative-sub()
Return a function object representing the antiderivative of the difference
of two functions, suitable for using with adhoc:set-antiderivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing ∫(f1-f2)
declare function this:antiderivative-sub(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f - g) = ∫f - ∫g :)
  this:adhoc-sub(this:antiderivative($f1), this:antiderivative($f2))
}

Function: antiderivative-multiply
declare function antiderivative-multiply($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


antiderivative-multiply()
Return a function object representing the antiderivative of the product of two
functions, suitable for using with adhoc:set-antiderivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing ∫(f1*f2)
declare function this:antiderivative-multiply(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f * g) = f*∫g - ∫(df*∫g) :)
  this:adhoc-sub(
    this:adhoc-multiply($f1, this:antiderivative($f2)),
    this:antiderivative(
      this:adhoc-multiply(
        this:derivative($f1),
        this:antiderivative($f2)
      )
    )
  )
}

Function: antiderivative-divide
declare function antiderivative-divide($f1 as map(xs:string,item()*), $f2 as map(xs:string,item()*)) as map(xs:string,item()*)


antiderivative-divide()
Return a function object representing the antiderivative of the quotient
of two functions, suitable for using with adhoc:set-antiderivative().

Params
  • f1 as map(xs:string,item()*): function object for first function
  • f2 as map(xs:string,item()*): function object for second function
Returns
  • map(xs:string,item()*): object representing ∫(f1/f2)
declare function this:antiderivative-divide(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f / g) = f / g + ∫[(f*dg) / (g*g)] :)
  this:adhoc-add(
    this:adhoc-divide($f1, $f2),
    this:antiderivative(
      this:adhoc-divide(
        this:adhoc-multiply($f1, this:derivative($f2)),
        this:adhoc-multiply($f2, $f2)
      )
    )
  )
}

Function: antiderivative-times
declare function antiderivative-times($f as map(xs:string,item()*), $k as xs:double) as map(xs:string,item()*)


antiderivative-times()
Return a function object representing the antiderivative of the scaling of
a function by a constant, suitable for using with adhoc:set-antiderivative().

Params
  • f as map(xs:string,item()*): function object
  • k as xs:double: constant
Returns
  • map(xs:string,item()*): object representing ∫(k*f)
declare function this:antiderivative-times(
  $f as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  (: ∫(k*f) = k*∫f :)
  this:adhoc-times(this:antiderivative($f), $k)
}

Function: antiderivative-minus
declare function antiderivative-minus($f as map(xs:string,item()*)) as map(xs:string,item()*)


antiderivative-minus()
Return a function object representing the antiderivative of the negation of
a function, suitable for using with adhoc:set-antiderivative().

Params
  • f as map(xs:string,item()*): function object
Returns
  • map(xs:string,item()*): object representing ∫(-f)
declare function this:antiderivative-minus(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(-f) = -∫f :)
  this:adhoc-minus(this:antiderivative($f))
}

Original Source Code

xquery version "3.1";
(:~
 : Functions that we need to perform analytical operations on, e.g.
 : arithmetic ops, derivative(), etc.
 : A unifying API for things like polynomials, complex polynomials, polynomial
 : quotients, etc.
 :
 : Provides for polymorphism over a certain class of functions in the same way
 : that geo/euclidean provides polymorphism over a class of geometric objects.
 :
 : Kinds of functions supported:
 : constants (core/complex)
 : real polynomials over x, sin(x), and cos(x) (types/polynomial)
 : complex polynomials over x, sin(x), and cos(x) (types/cpolynomial)
 : adhoc functions with certain operators determined by function keys (limited)
 :
 : Note: limitations on some quotient operators right now
 :
 : Copyright© Mary Holstege 2022-2025
 : CC-BY (https://creativecommons.org/licenses/by/4.0/)
 : @since April 2023
 : @custom:Status Bleeding edge
 :)
module namespace this="http://mathling.com/core/functions";

import module namespace config="http://mathling.com/core/config"
       at "../core/config.xqy";
import module namespace errors="http://mathling.com/core/errors"
       at "../core/errors.xqy";
import module namespace util="http://mathling.com/core/utilities"
       at "../core/utilities.xqy";
import module namespace z="http://mathling.com/core/complex"
       at "../core/complex.xqy";
import module namespace zv="http://mathling.com/core/complex/vector"
       at "../core/vcomplex.xqy";
import module namespace poly="http://mathling.com/type/polynomial"
       at "../types/polynomial.xqy";
import module namespace zpoly="http://mathling.com/type/polynomial/complex"
       at "../types/cpolynomial.xqy";
import module namespace quotient="http://mathling.com/type/polynomial/quotient"
       at "../types/quotient.xqy";
import module namespace adhoc="http://mathling.com/type/adhoc-function"
       at "../types/adhoc-function.xqy";

declare namespace map="http://www.w3.org/2005/xpath-functions/map";
declare namespace math="http://www.w3.org/2005/xpath-functions/math";

(:~
 : value()
 : The value of f(x) for a real-valued x. Returns a complex value:
 : use z:as-real() if you need to.
 :
 : @param $f: an object representing a certain kind of function
 : @param $x: double value
 : @return: complex value f(x)
 :)
declare function this:value(
  $f as map(xs:string,item()*),
  $x as xs:double
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then $f
    else if ($kind="polynomial") then poly:value($f, $x)=>z:as-complex()
    else if ($kind="complex-polynomial") then zpoly:value($f, $x)
    else if ($kind="polynomial-quotient") then (
      this:value(quotient:u($f), $x)=>z:divide( this:value(quotient:v($f), $x) )
    ) else if ($f("value") instance of function(*)) then (
      $f("value")($f, $x)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:~
 : zvalue()
 : The value of f(z) for a complex value z
 :
 : @param $f: an object representing a certain kind of function
 : @param $z: complex value
 : @return: a complex value f(z)
 :)
declare function this:zvalue(
  $f as map(xs:string,item()*),
  $z as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then $f
    else if ($kind="polynomial") then poly:zvalue($f, $z)
    else if ($kind="complex-polynomial") then zpoly:zvalue($f, $z)
    else if ($kind="polynomial-quotient") then (
      this:zvalue(quotient:u($f), $z)=>z:divide( this:zvalue(quotient:v($f), $z) )
    ) else if ($f("zvalue") instance of function(*)) then (
      $f("zvalue")($f, $z)
    ) else if ($f("zvaluev") instance of function(*)) then (
      $f("zvaluev")($f, z:as-vector($z))=>z:vector()
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:~
 : zvaluev()
 : The value of f(z) for a complex value z vector
 :
 : @param $f: an object representing a certain kind of function
 : @param $z: complex value vector
 : @return: a complex value f(z) vector
 :)
declare function this:zvaluev(
  $f as map(xs:string,item()*),
  $z as xs:double*
) as xs:double*
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then $f=>z:as-vector()
    else if ($kind="polynomial") then poly:zvaluev($f, $z)
    else if ($kind="complex-polynomial") then zpoly:zvaluev($f, $z)
    else if ($kind="polynomial-quotient") then (
      this:zvaluev(quotient:u($f), $z)=>zv:divide( this:zvaluev(quotient:v($f), $z) )
    ) else if ($f("zvaluev") instance of function(*)) then (
      $f("zvaluev")($f, $z)
    ) else if ($f("zvalue") instance of function(*)) then (
      $f("zvalue")($f, z:vector($z))=>z:as-vector()
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:~
 : derivative()
 : Compute the derivative of the function.
 :
 : @param $f: an object representing a certain kind of function
 : @return: an object representing the function that is the derivative of f
 :)
declare function this:derivative(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then (
      z:as-complex(0)
    ) else if ($kind="polynomial") then (
      poly:derivative($f)
    ) else if ($kind="complex-polynomial") then (
      zpoly:derivative($f)
    ) else if ($kind="polynomial-quotient") then (
      (: d(u/v) = (v du - u dv) / v² :)
      let $u := quotient:u($f)
      let $v := quotient:v($f)
      let $du := this:derivative($u)
      let $dv := this:derivative($v)
      return (
        quotient:quotient(
          (this:multiply($v, $du)=>this:sub( this:multiply($u, $dv) )),
          this:multiply($v, $v)
        )
      )
    ) else if ($f("derivative") instance of function(*)) then (
      $f("derivative")($f)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:~
 : antiderivative()
 : Compute the anti-derivative of the function.
 :
 : @param $f: an object representing a certain kind of function
 : @return: an object representing the function that is the anti-derivative of f
 :)
declare function this:antiderivative(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then (
      if (z:eq($f, 0)) then $f else zpoly:polynomial( ($f, z:as-complex(0)) )
    ) else if ($kind="polynomial") then (
      poly:antiderivative($f)
    ) else if ($kind="complex-polynomial") then (
      zpoly:antiderivative($f)
    ) else if ($kind="polynomial-quotient") then (
      (: Not easy:
       : ∫u'/v = u/v + ∫(uv'/v²) so
       : ∫u/v = (∫u)/v + ∫((∫u)v'/v²)
       : Which in theory we could keep going on until the right hand bit gets
       : down to a v' that is 0, but skip this for now
       :)
      errors:error("UTIL-NOTIMPLEMENTED", ("anti-derivative", $f))
    ) else if ($f("antiderivative") instance of function(*)) then (
      $f("antiderivative")($f)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:~
 : definite-integral()
 : Definite integral of a function (exact)
 : Note: if the result includes an imaginary part, this will raise
 : an error.
 :
 : @param $f: the function
 : @param $a: start of range of integration
 : @param $b: end of range of integration
 :)
declare function this:definite-integral(
  $f as map(xs:string,item()*),
  $a as xs:double,
  $b as xs:double
) as xs:double
{
  let $anti := this:antiderivative($f)
  return ($anti=>this:value($b)=>z:sub( $anti=>this:value($a) ) )=>z:as-real()
};

(:~
 : describe()
 : Describe the function
 :
 : @param $f: the function
 :)
declare function this:describe(
  $f as map(xs:string,item()*)
) as xs:string
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then z:quote($f)
    else if ($kind="polynomial") then poly:describe($f)
    else if ($kind="complex-polynomial") then zpoly:describe($f)
    else if ($kind="polynomial-quotient") then (
      "("||this:describe(quotient:u($f))||")/("||this:describe(quotient:v($f))||")"
    ) else if ($f("describe") instance of function(*)) then (
      $f("describe")($f)
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:~
 : function()
 : Return a function that takes values of the function, e.g. for curve plotting
 :
 : @param $f: the function
 :)
declare function this:function(
  $f as map(xs:string,item()*)
) as function(xs:double) as xs:double
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then
      function($v as xs:double) as xs:double {
        z:as-real($f)
      }
    else if ($kind="polynomial") then poly:function($f)
    else if ($kind="complex-polynomial") then zpoly:function($f)
    else if ($kind="polynomial-quotient") then (
      let $uf := this:function(quotient:u($f))
      let $vf := this:function(quotient:v($f))
      return (
        function($v as xs:double) as xs:double {
          $uf($v) div $vf($v)
        }
      )
    ) else if ($f("function") instance of function(*)) then (
      $f("function")($f)
    ) else if ($f("value") instance of function(*)) then (
      function ($v as xs:double) as xs:double {
        $f("value")($f, $v)
      }
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:~
 : zfunction()
 : Return a function over complex values that takes values of the function, e.g. for curve plotting
 :
 : @param $f: the function
 :)
declare function this:zfunction(
  $f as map(xs:string,item()*)
) as function(map(xs:string,item()*)) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then
      function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
        $f
      }
    else if ($kind="polynomial") then poly:zfunction($f)
    else if ($kind="complex-polynomial") then zpoly:zfunction($f)
    else if ($kind="polynomial-quotient") then (
      let $uf := this:zfunction(quotient:u($f))
      let $vf := this:zfunction(quotient:v($f))
      return (
        function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
          $uf($v)=>z:divide( $vf($v) )
        }
      )
    ) else if ($f("zfunction") instance of function(*)) then (
      $f("zfunction")($f)
    ) else if ($f("zvalue") instance of function(*)) then (
      function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
        $f("zvalue")($f, $v)
      }
    ) else if ($f("zvaluev") instance of function(*)) then (
      function($v as map(xs:string,item()*)) as map(xs:string,item()*) {
        $f("zvaluev")($f, z:as-vector($v))=>z:vector()
      }
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};


(:~
 : zfunctionv()
 : Return a function over complex values (vector form) that takes values of the function, e.g. for curve plotting
 :
 : @param $f: the function
 :)
declare function this:zfunctionv(
  $f as map(xs:string,item()*)
) as function(xs:double*) as xs:double*
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then
      function($v as xs:double*) as xs:double* {
        z:as-vector($f)
      }
    else if ($kind="polynomial") then poly:zfunctionv($f)
    else if ($kind="complex-polynomial") then zpoly:zfunctionv($f)
    else if ($kind="polynomial-quotient") then (
      let $uf := this:zfunctionv(quotient:u($f))
      let $vf := this:zfunctionv(quotient:v($f))
      return (
        function($v as xs:double*) as xs:double* {
          $uf($v)=>zv:divide( $vf($v) )
        }
      )
    ) else if ($f("zfunctionv") instance of function(*)) then (
      $f("zfunctionv")($f)
    ) else if ($f("zfunction") instance of function(*)) then (
      let $zf := $f("zfunction")($f)
      return (
        function ($v as xs:double*) as xs:double* {
          $zf(z:vector($v))=>z:as-vector()
        }
      )
    ) else if ($f("zvaluev") instance of function(*)) then (
      function($v as xs:double*) as xs:double* {
        $f("zvaluev")($f, $v)
      }
    ) else if ($f("zvalue") instance of function(*)) then (
      function($v as xs:double*) as xs:double* {
        $f("zvalue")($f, z:vector($v))=>z:as-vector()
      }
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

(:======================================================================
 : Operations
 :======================================================================:)

(:~
 : add()
 : Add two functions
 :
 : @param $f1: one function
 : @param $f2: another function
 : @return object representing f1+f2
 :)
declare function this:add(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex") then $f1=>z:add($f2) else this:add($f2, $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        zpoly:as-polynomial($f1)=>zpoly:add( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (poly:op($f1)=poly:op($f2))
        then $f1=>poly:add($f2)
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then zpoly:as-polynomial($f1)=>zpoly:add( $f2 )
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add($f2, $f1)
      ) else (
        this:adhoc-add($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        $f1=>zpoly:add( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (zpoly:op($f1)=poly:op($f2))
        then $f1=>zpoly:add( zpoly:as-polynomial($f2) )
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then $f1=>zpoly:add($f2)
        else this:adhoc-add($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add($f2, $f1)
      ) else (
        this:adhoc-add($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: b/c + a = (b + ac) / c :)
        quotient:quotient(
          this:add( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient(
          this:add( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b + c/d = (ad + cb) / (bd) :)
        return (
          quotient:quotient(
            this:add( this:multiply($u1, $v2), this:multiply($u2, $v1) ),
            this:multiply( $v1, $v2 )
          )
        )
      ) else (
        this:adhoc-add($f1, $f2)
      )
    ) else (
      this:adhoc-add($f1, $f2)
    )
  )
};

(:~
 : sub()
 : Subtract two functions
 :
 : @param $f1: one function
 : @param $f2: another function
 : @return object representing f1-f2
 :)
declare function this:sub(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex") then $f1=>z:sub($f2) else this:add(this:minus($f2), $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        zpoly:as-polynomial($f1)=>zpoly:sub( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (poly:op($f1)=poly:op($f2))
        then $f1=>poly:sub($f2)
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then zpoly:as-polynomial($f1)=>zpoly:sub( $f2 )
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add(this:minus($f2), $f1)
      ) else (
        this:adhoc-sub($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        $f1=>zpoly:sub( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (zpoly:op($f1)=poly:op($f2))
        then $f1=>zpoly:sub( zpoly:as-polynomial($f2) )
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then $f1=>zpoly:sub($f2)
        else this:adhoc-sub($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:add(this:minus($f2), $f1)
      ) else (
        this:adhoc-sub($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: b/c - a = (b - ac) / c :)
        quotient:quotient(
          this:sub( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="complex-polynomial") then (
        (: b/c - a = (b + ac) / c :)
        quotient:quotient(
          this:sub( quotient:u($f1), this:multiply($f2, quotient:v($f1)) ),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b - c/d = (ad - cb) / (bd) :)
        return (
          quotient:quotient(
            this:sub( this:multiply($u1, $v2), this:multiply($u2, $v1) ),
            this:multiply( $v1, $v2 )
          )
        )
      ) else (
        this:adhoc-sub($f1, $f2)
      )
    ) else (
      this:adhoc-sub($f1, $f2)
    )
  )
};

(:~
 : multiply()
 : Multiply two functions
 :
 : @param $f1: one function
 : @param $f2: another function
 : @return object representing f1 * f2
 :)
declare function this:multiply(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex") then $f1=>z:multiply($f2) else this:multiply($f2, $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(z:as-real($f2))
        else zpoly:as-polynomial($f1)=>zpoly:multiply( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (poly:op($f1)=poly:op($f2))
        then $f1=>poly:multiply($f2)
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (poly:op($f1)=zpoly:op($f2))
        then zpoly:as-polynomial($f1)=>zpoly:multiply( $f2 )
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:multiply($f2, $f1)
      ) else (
        this:adhoc-multiply($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(z:as-real($f2))
        else $f1=>zpoly:multiply( zpoly:polynomial(($f2)) )
      ) else if ($kind2="polynomial") then (
        if (zpoly:op($f1)=poly:op($f2))
        then $f1=>zpoly:multiply( zpoly:as-polynomial($f2) )
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        if (zpoly:op($f1)=zpoly:op($f2))
        then $f1=>zpoly:multiply($f2)
        else this:adhoc-multiply($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        this:multiply($f2, $f1)
      ) else (
        this:adhoc-multiply($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: b/c * a = ba / c :)
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), $f2),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b * c/d = (ac) / (bd) :)
        return (
          quotient:quotient(
            this:multiply( $u1, $u1 ),
            this:multiply( $v1, $v2 )
          )
        )
      ) else (
        this:adhoc-multiply($f1, $f2)
      )
    ) else (
      this:adhoc-multiply($f1, $f2)
    )
  )
};

(:~
 : divide()
 : Divide two functions
 :
 : @param $f1: one function
 : @param $f2: another function
 : @return object representing f1/f2
 :)
declare function this:divide(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind1 := util:kind($f1)
  let $kind2 := util:kind($f2)
  return (
    if ($kind1="complex") then (
      if ($kind2="complex")
      then $f1=>z:divide($f2)
      else this:multiply(z:reciprocal($f2), $f1)
    )
    else if ($kind1="polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(1 div z:as-real($f2))
        else zpoly:as-polynomial($f1)=>zpoly:ztimes( z:reciprocal($f2) )
      ) else if ($kind2="polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        (: a / (b/c) =  ac / b :)
        quotient:quotient(
          this:multiply($f1, quotient:v($f2)),
          quotient:u($f2)
        )
      ) else (
        this:adhoc-divide($f1, $f2)
      )
    )
    else if ($kind1="complex-polynomial") then (
      if ($kind2="complex") then (
        if (z:is-real($f2))
        then $f1=>this:times(1 div z:as-real($f2))
        else $f1=>zpoly:ztimes( z:reciprocal($f2) )
      ) else if ($kind2="polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="complex-polynomial") then (
        quotient:quotient($f1, $f2)
      ) else if ($kind2="polynomial-quotient") then (
        (: a / (b/c) =  ac / b :)
        quotient:quotient(
          this:multiply($f1, quotient:v($f2)),
          quotient:u($f2)
        )
      ) else (
        this:adhoc-divide($f1, $f2)
      )
    )
    else if ($kind1="polynomial-quotient") then (
      if ($kind2="complex") then (
        quotient:quotient(
          this:multiply(quotient:u($f1), z:reciprocal($f2)),
          quotient:v($f1)
        )
      ) else if ($kind2="polynomial") then (
        (: (b/c) / a = b / ca :)
        quotient:quotient(
          quotient:u($f1),
          this:multiply(quotient:v($f1), $f2)
        )
      ) else if ($kind2="complex-polynomial") then (
        (: (b/c) / a = b / ca :)
        quotient:quotient(
          quotient:u($f1),
          this:multiply(quotient:v($f1), $f2)
        )
      ) else if ($kind2="polynomial-quotient") then (
        let $u1 := quotient:u($f1)
        let $v1 := quotient:v($f1)
        let $u2 := quotient:u($f2)
        let $v2 := quotient:v($f2)
        (: a/b / c/d = a/b * d/c = ad/bc :)
        return (
          quotient:quotient(
            this:multiply( $u1, $v2),
            this:multiply( $v1, $u2 )
          )
        )
      ) else (
        this:adhoc-divide($f1, $f2)
      )
    ) else (
      this:adhoc-divide($f1, $f2)
    )
  )
};

(:~
 : pow()
 : Raise one function to another
 :
 : @param $f1: one function
 : @param $f2: another function
 : @return object representing f1^f2
 :)
declare function this:pow(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:adhoc-pow($f1, $f2)
};

(:~
 : log()
 : Log of a function
 :
 : @param $f1: one function
 : @return object representing log(f1)
 :)
declare function this:log(
  $f1 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  this:adhoc-log($f1)
};

(:~
 : times()
 : Multiply function by constant
 :
 : @param $f: the function
 : @param $k: constant
 : @return object representing k*f
 :)
declare function this:times(
  $f as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then z:times($f, $k)
    else if ($kind="polynomial") then poly:times($f, $k)
    else if ($kind="complex-polynomial") then zpoly:times($f, $k)
    else if ($kind="polynomial-quotient") then (
      quotient:quotient(
        this:times(quotient:u($f), $k),
        quotient:v($f)
      )
    ) else (
      this:adhoc-times($f, $k)
    )
  )
};

(:~
 : minus()
 : Negate the function
 :
 : @param $f: the function
 : @return object representing -f
 :)
declare function this:minus(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then z:minus($f)
    else if ($kind="polynomial") then poly:times($f, -1)
    else if ($kind="complex-polynomial") then zpoly:times($f, -1)
    else if ($kind="polynomial-quotient") then (
      quotient:quotient(
        this:minus(quotient:u($f)),
        quotient:v($f)
      )
    ) else (
      this:adhoc-minus($f)
    )
  )
};

(:~
 : order()
 : Order of the function. Raises an error for ad hoc functions with no
 : defined order.
 :
 : @param $f: the function
 : @return polynomial order of the function (if there is a defined order)
 :)
declare function this:order(
  $f as map(xs:string,item()*)
) as xs:integer
{
  let $kind := util:kind($f) return (
    if ($kind="complex") then 0
    else if ($kind="polynomial") then poly:order($f)
    else if ($kind="complex-polynomial") then zpoly:order($f)
    else if ($kind="polynomial-quotient") then (
      abs(this:order(quotient:u($f)) - this:order(quotient:v($f)))
    ) else (
      errors:error("UTIL-BADARGS", ("f", $f))
    )
  )
};

declare %private function this:adhoc-add(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      "("||this:describe($f1)||")+("||this:describe($f2)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      ($f1=>this:value($x))=>z:add( $f2=>this:value($x) )
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      $f1=>this:zvalue($z)=>z:add( $f2=>this:zvalue($z) )
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      $f1=>this:zvaluev($z)=>zv:add( $f2=>this:zvaluev($z) )
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        this:function($f1)($x) + this:function($f2)($x)
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        (this:zfunction($f1)($z))=>z:add( this:zfunction($f2)($z) )
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative"))) and
      (not(util:kind($f2)="adhoc-function") or exists($f2("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-add($f1, $f2)
      }
    ) else ()
  )=>
  adhoc:set-antiderivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("antiderivative"))) and
      (not(util:kind($f2)="adhoc-function") or exists($f2("antiderivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:antiderivative-add($f1, $f2)
      }
    ) else ()
  )
};

declare %private function this:adhoc-sub(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      "("||this:describe($f1)||")-("||this:describe($f2)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      ($f1=>this:value($x))=>z:sub( $f2=>this:value($x) )
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      $f1=>this:zvalue($z)=>z:sub( $f2=>this:zvalue($z) )
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      $f1=>this:zvaluev($z)=>zv:sub( $f2=>this:zvaluev($z) )
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        this:function($f1)($x) - this:function($f2)($x)
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        (this:zfunction($f1)($z))=>z:sub( this:zfunction($f2)($z) )
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative"))) and
      (not(util:kind($f2)="adhoc-function") or exists($f2("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-sub($f1, $f2)
      }
    ) else ()
  )=>
  adhoc:set-antiderivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("antiderivative"))) and
      (not(util:kind($f2)="adhoc-function") or exists($f2("antiderivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:antiderivative-sub($f1, $f2)
      }
    ) else ()
  )
};

declare %private function this:adhoc-multiply(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      "("||this:describe($f1)||")*("||this:describe($f2)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      ($f1=>this:value($x))=>z:multiply( $f2=>this:value($x) )
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      ($f1=>this:zvalue($z))=>z:multiply( $f2=>this:zvalue($z) )
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      ($f1=>this:zvaluev($z))=>zv:multiply( $f2=>this:zvaluev($z) )
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        this:function($f1)($x) * this:function($f2)($x)
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        (this:zfunction($f1)($z))=>z:multiply( this:zfunction($f2)($z) )
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative"))) and
      (not(util:kind($f2)="adhoc-function") or exists($f2("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-multiply($f1, $f2)
      }
    ) else ()
  )
};

declare %private function this:adhoc-divide(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      "("||this:describe($f1)||")/("||this:describe($f2)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      ($f1=>this:value($x))=>z:divide( $f2=>this:value($x) )
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      ($f1=>this:zvalue($z))=>z:divide( $f2=>this:zvalue($z) )
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      ($f1=>this:zvaluev($z))=>zv:divide( $f2=>this:zvaluev($z) )
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        this:function($f1)($x) div this:function($f2)($x)
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        (this:zfunction($f1)($z))=>z:divide( this:zfunction($f2)($z) )
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative"))) and
      (not(util:kind($f2)="adhoc-function") or exists($f2("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-divide($f1, $f2)
      }
    ) else ()
  )
};

declare %private function this:adhoc-pow(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      "("||this:describe($f1)||")^("||this:describe($f2)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      ($f1=>this:value($x))=>z:ppow( $f2=>this:value($x) )
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      ($f1=>this:zvalue($z))=>z:ppow( $f2=>this:zvalue($z) )
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      ($f1=>this:zvaluev($z))=>zv:ppow( $f2=>this:zvaluev($z) )
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        math:pow(this:function($f1)($x), this:function($f2)($x))
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        (this:zfunction($f1)($z))=>z:ppow( this:zfunction($f2)($z) )
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative"))) and
      (not(util:kind($f2)="adhoc-function") or exists($f2("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-pow($f1, $f2)
      }
    ) else ()
  )
};

declare %private function this:adhoc-times(
  $f1 as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      $k||"("||this:describe($f1)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      $f1=>this:value($x)=>z:times($k)
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      $f1=>this:zvalue($z)=>z:times($k)
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      $f1=>this:zvaluev($z)=>zv:times($k)
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        $k * this:function($f1)($x)
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        (this:zfunction($f1)($z))=>z:times($k)
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-times($f1, $k)
      }
    ) else ()
  )=>
  adhoc:set-antiderivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("antiderivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:antiderivative-times($f1, $k)
      }
    ) else ()
  )
};

declare %private function this:adhoc-minus(
  $f1 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      "-("||this:describe($f1)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      $f1=>this:value($x)=>z:minus()
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      $f1=>this:zvalue($z)=>z:minus()
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      $f1=>this:zvaluev($z)=>zv:minus()
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        -this:function($f1)($x)
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        (this:zfunction($f1)($z))=>z:minus()
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-minus($f1)
      }
    ) else ()
  )=>
  adhoc:set-antiderivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("antiderivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:antiderivative-minus($f1)
      }
    ) else ()
  )
};

declare %private function this:adhoc-log(
  $f1 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  adhoc:adhoc-function(
    function ($f as map(xs:string,item()*)) as xs:string {
      "log("||this:describe($f1)||")"
    }
  )=>
  adhoc:set-value(
    function($f as map(xs:string,item()*), $x as xs:double) as map(xs:string,item()*) {
      z:log($f1=>this:value($x))
    }
  )=>
  adhoc:set-zvalue(
    function($f as map(xs:string,item()*), $z as map(xs:string,item()*)) as map(xs:string,item()*) {
      z:log($f1=>this:zvalue($z))
    }
  )=>
  adhoc:set-zvaluev(
    function($f as map(xs:string,item()*), $z as xs:double*) as xs:double* {
      zv:log($f1=>this:zvaluev($z))
    }
  )=>
  adhoc:set-function(
    function($f as map(xs:string,item()*)) as function(xs:double) as xs:double {
      function($x as xs:double) as xs:double {
        math:log(this:function($f1)($x))
      }
    }
  )=>
  adhoc:set-zfunction(
    function($f as map(xs:string,item()*)) as function(map(xs:string,item()*)) as map(xs:string,item()*) {
      function($z as map(xs:string,item()*)) as map(xs:string,item()*) {
        z:log(this:zfunction($f1)($z))
      }
    }
  )=>
  adhoc:set-derivative(
    if (
      (not(util:kind($f1)="adhoc-function") or exists($f1("derivative")))
    ) then (
      function($f as map(xs:string,item()*)) as map(xs:string,item()*) {
        this:derivative-log($f1)
      }
    ) else ()
  )
};

(:~
 : derivative-add()
 : Return a function object representing the derivative of the sum of two
 : functions, suitable for using with adhoc:set-derivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing d(f1+f2)
 :)
declare function this:derivative-add(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f + g) = df + dg :)
  this:adhoc-add(this:derivative($f1), this:derivative($f2))
};

(:~
 : derivative-sub()
 : Return a function object representing the derivative of the difference of two
 : functions, suitable for using with adhoc:set-derivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing d(f1-f2)
 :)
declare function this:derivative-sub(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f - g) = df - dg :)
  this:adhoc-sub(this:derivative($f1), this:derivative($f2))
};

(:~
 : derivative-multiply()
 : Return a function object representing the derivative of the product of two
 : functions, suitable for using with adhoc:set-derivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing d(f1*f2)
 :)
declare function this:derivative-multiply(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f * g) = f*dg + g*df :)
  this:adhoc-add(
    this:adhoc-multiply($f1, this:derivative($f2)),
    this:adhoc-multiply($f2, this:derivative($f1))
  )
};

(:~
 : derivative-divide()
 : Return a function object representing the derivative of the quotient of two
 : functions, suitable for using with adhoc:set-derivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing d(f1/f2)
 :)
declare function this:derivative-divide(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f / g) = (df*g + dg*f)/g*g :)
  this:adhoc-sub(
    this:adhoc-multiply(this:derivative($f1), $f2),
    this:adhoc-multiply(this:derivative($f2), $f1)
  )=>this:adhoc-divide(
    this:adhoc-multiply($f2, $f2)
  )
};

(:~
 : derivative-pow()
 : Return a function object representing the derivative of the raising of
 : one function to the other, suitable for using with adhoc:set-derivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing d(f1^f2)
 :)
declare function this:derivative-pow(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(f^g) = (f^g)(df (g/f) + dg ln(f) ) :)
  this:adhoc-multiply(
    this:adhoc-pow($f1, $f2),
    this:adhoc-add(
      this:adhoc-multiply(
        this:derivative($f1),
        this:adhoc-divide($f2, $f1)
      ),
      this:adhoc-multiply(
        this:derivative($f2),
        this:adhoc-log($f1)
      )
    )
  )
};

(:~
 : derivative-log()
 : Return a function object representing the derivative of taking the
 : log of a function.
 :
 : @param $f1: function object for first function
 : @return object representing d(log(f1))
 :)
declare function this:derivative-log(
  $f1 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(log(f)) = d(f)/f :)
  this:adhoc-divide( this:derivative($f1), $f1 )
};

(:~
 : derivative-times()
 : Return a function object representing the derivative of the scaling of
 : a function by a constant, suitable for using with adhoc:set-derivative().
 :
 : @param $f: function object for function
 : @param $k: constant
 : @return object representing d(k*f)
 :)
declare function this:derivative-times(
  $f as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  (: d(k*f) = k*df :)
  this:adhoc-times(this:derivative($f), $k)
};

(:~
 : derivative-minus()
 : Return a function object representing the derivative of the negation of
 : a function, suitable for using with adhoc:set-derivative().
 :
 : @param $f: function object for function
 : @return object representing d(-f)
 :)
declare function this:derivative-minus(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: d(-f) = -df :)
  this:adhoc-minus(this:derivative($f))
};

(:~
 : antiderivative-add()
 : Return a function object representing the antiderivative of the sum of two
 : functions, suitable for using with adhoc:set-antiderivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing ∫(f1+f2)
 :)
declare function this:antiderivative-add(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f + g) = ∫f + ∫g :)
  this:adhoc-add(this:antiderivative($f1), this:antiderivative($f2))
};

(:~
 : antiderivative-sub()
 : Return a function object representing the antiderivative of the difference
 : of two functions, suitable for using with adhoc:set-antiderivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing ∫(f1-f2)
 :)
declare function this:antiderivative-sub(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f - g) = ∫f - ∫g :)
  this:adhoc-sub(this:antiderivative($f1), this:antiderivative($f2))
};

(:~
 : antiderivative-multiply()
 : Return a function object representing the antiderivative of the product of two
 : functions, suitable for using with adhoc:set-antiderivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing ∫(f1*f2)
 :)
declare function this:antiderivative-multiply(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f * g) = f*∫g - ∫(df*∫g) :)
  this:adhoc-sub(
    this:adhoc-multiply($f1, this:antiderivative($f2)),
    this:antiderivative(
      this:adhoc-multiply(
        this:derivative($f1),
        this:antiderivative($f2)
      )
    )
  )
};

(:~
 : antiderivative-divide()
 : Return a function object representing the antiderivative of the quotient
 : of two functions, suitable for using with adhoc:set-antiderivative().
 :
 : @param $f1: function object for first function
 : @param $f2: function object for second function
 : @return object representing ∫(f1/f2)
 :)
declare function this:antiderivative-divide(
  $f1 as map(xs:string,item()*),
  $f2 as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(f / g) = f / g + ∫[(f*dg) / (g*g)] :)
  this:adhoc-add(
    this:adhoc-divide($f1, $f2),
    this:antiderivative(
      this:adhoc-divide(
        this:adhoc-multiply($f1, this:derivative($f2)),
        this:adhoc-multiply($f2, $f2)
      )
    )
  )
};

(:~
 : antiderivative-times()
 : Return a function object representing the antiderivative of the scaling of
 : a function by a constant, suitable for using with adhoc:set-antiderivative().
 :
 : @param $f: function object
 : @param $k: constant
 : @return object representing ∫(k*f)
 :)
declare function this:antiderivative-times(
  $f as map(xs:string,item()*),
  $k as xs:double
) as map(xs:string,item()*)
{
  (: ∫(k*f) = k*∫f :)
  this:adhoc-times(this:antiderivative($f), $k)
};

(:~
 : antiderivative-minus()
 : Return a function object representing the antiderivative of the negation of
 : a function, suitable for using with adhoc:set-antiderivative().
 :
 : @param $f: function object
 : @return object representing ∫(-f)
 :)
declare function this:antiderivative-minus(
  $f as map(xs:string,item()*)
) as map(xs:string,item()*)
{
  (: ∫(-f) = -∫f :)
  this:adhoc-minus(this:antiderivative($f))
};