summaryrefslogtreecommitdiff
path: root/graphics/pgf/base/tex/generic/libraries/luamath
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2023-01-16 03:03:27 +0000
committerNorbert Preining <norbert@preining.info>2023-01-16 03:03:27 +0000
commit6f9e1680085e7bb4d258f6f8116369d122e196e1 (patch)
tree9ac0ecb239240d1d672b188f29c1479de215074b /graphics/pgf/base/tex/generic/libraries/luamath
parentb8345f39630408bb198e7636381ce4240154ca9b (diff)
CTAN sync 202301160303
Diffstat (limited to 'graphics/pgf/base/tex/generic/libraries/luamath')
-rw-r--r--graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/functions.lua626
-rw-r--r--graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/parser.lua481
-rw-r--r--graphics/pgf/base/tex/generic/libraries/luamath/pgflibraryluamath.code.tex558
3 files changed, 1665 insertions, 0 deletions
diff --git a/graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/functions.lua b/graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/functions.lua
new file mode 100644
index 0000000000..367d6ae298
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/functions.lua
@@ -0,0 +1,626 @@
+-- Copyright 2011 by Christophe Jorssen
+--
+-- This file may be distributed and/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License.
+--
+-- See the file doc/generic/pgf/licenses/LICENSE for more details.
+--
+-- $Id$
+--
+
+local pgfluamathfunctions = pgfluamathfunctions or {}
+
+-- Maps function names to their function.
+--
+-- Note that this allows to register functions which are not in pgfluamathfunctions.
+--
+-- Note that the string keys are not necessarily the same as the function
+-- names. In particular, the math expression "not(1,1)" will execute notPGF(1,1)
+--
+-- Note that each function which is added to pgfluamathfunctions will _automatically_ be inserted into this map, see __newindex.
+-- (I fear it will not be erased directly...)
+pgfluamathfunctions.stringToFunctionMap = {}
+
+local newFunctionAllocatedCallback = function(table,key,value)
+ local keyName = tostring(key):gsub("PGF","")
+ if not value then
+ stringToFunctionMap[keyName] = nil
+ elseif type(value) == 'function' then
+ -- remember it, and strip PGF suffix (i.e. remember 'not' instead of 'notPGF')
+ pgfluamathfunctions.stringToFunctionMap[keyName] = value
+ end
+ rawset(table,key,value)
+end
+
+setmetatable(pgfluamathfunctions, { __newindex = newFunctionAllocatedCallback })
+
+local mathabs, mathacos, mathasin = math.abs, math.acos, math.asin
+local mathatan, mathceil = math.atan, math.ceil
+local mathcos, mathdeg = math.cos, math.deg
+local mathexp, mathfloor, mathfmod = math.exp, math.floor, math.fmod
+local mathlog, mathmax = math.log, math.max
+local mathmin, mathpi = math.min, math.pi
+local mathrad, mathrandom = math.rad, math.random
+local mathrandomseed, mathsin = math.randomseed, math.sin
+local mathsqrt = math.sqrt
+local mathtan = math.tan
+
+local trigFormatToRadians = mathrad
+
+local radiansToTrigFormat = mathdeg
+
+pgfluamathfunctions.TrigFormat = { 'deg', 'rad' }
+pgfluamathfunctions.stringToFunctionMap["TrigFormat"] = nil
+
+-- choice is one of the valid choices in TrigFormat.
+function pgfluamathfunctions.setTrigFormat(choice)
+ if choice == 'deg' then
+ trigFormatToRadians = mathrad
+ radiansToTrigFormat = mathdeg
+ elseif choice == 'rad' then
+ local identity = function(x) return x end
+ trigFormatToRadians = identity
+ radiansToTrigFormat = identity
+ else
+ error("The argument '" .. tostring(choice) .. "' is no valid choice for setTrigFormat.")
+ end
+end
+pgfluamathfunctions.stringToFunctionMap["setTrigFormat"] = nil
+
+pgfluamathfunctions.setRandomSeed = mathrandomseed
+pgfluamathfunctions.stringToFunctionMap["setRandomSeed"] = nil
+
+-------------------------------------------
+
+function pgfluamathfunctions.add(x,y)
+ return x+y
+end
+
+function pgfluamathfunctions.subtract(x,y)
+ return x-y
+end
+
+function pgfluamathfunctions.neg(x)
+ return -x
+end
+
+function pgfluamathfunctions.multiply(x,y)
+ return x*y
+end
+
+function pgfluamathfunctions.veclen(x,y)
+ return mathsqrt(x*x+y*y)
+end
+
+function pgfluamathfunctions.divide(x,y)
+ return x/y
+end
+
+function pgfluamathfunctions.div(x,y)
+ return mathfloor(x/y)
+end
+
+function pgfluamathfunctions.pow(x,y)
+ -- do not use math.pow -- it is deprecated as of LUA 5.3
+ return x^y
+end
+
+function pgfluamathfunctions.factorial(x)
+-- TODO: x must be an integer
+ if x == 0 then
+ return 1
+ else
+ return x * pgfluamathfunctions.factorial(x-1)
+ end
+end
+
+function pgfluamathfunctions.ifthenelse(x,y,z)
+ if x~= 0 then
+ return y
+ else
+ return z
+ end
+end
+
+function pgfluamathfunctions.equal(x,y)
+ if x == y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.greater(x,y)
+ if x > y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.less(x,y)
+ if x < y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.min(x,y)
+ return mathmin(x,y)
+end
+
+function pgfluamathfunctions.max(x,y)
+ return mathmax(x,y)
+end
+
+function pgfluamathfunctions.notequal(x,y)
+ if x ~= y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.notless(x,y)
+ if x >= y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.notgreater(x,y)
+ if x <= y then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.andPGF(x,y)
+ if (x ~= 0) and (y ~= 0) then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.orPGF(x,y)
+ if (x ~= 0) or (y ~= 0) then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.notPGF(x)
+ if x == 0 then
+ return 1
+ else
+ return 0
+ end
+end
+
+function pgfluamathfunctions.pi()
+ return mathpi
+end
+
+function pgfluamathfunctions.e()
+ return mathexp(1)
+end
+
+function pgfluamathfunctions.abs(x)
+ return mathabs(x)
+end
+
+function pgfluamathfunctions.floor(x)
+ return mathfloor(x)
+end
+
+function pgfluamathfunctions.ceil(x)
+ return mathceil(x)
+end
+
+function pgfluamathfunctions.exp(x)
+ return mathexp(x)
+end
+
+function pgfluamathfunctions.ln(x)
+ return mathlog(x)
+end
+
+local logOf10 = mathlog(10)
+function pgfluamathfunctions.log10(x)
+ return mathlog(x) / logOf10
+end
+
+local logOf2 = mathlog(2)
+function pgfluamathfunctions.log2(x)
+ return mathlog(x) / logOf2
+end
+
+function pgfluamathfunctions.sqrt(x)
+ return mathsqrt(x)
+end
+
+function pgfluamathfunctions.sign(x)
+ if x < 0 then
+ return -1.0
+ elseif x == 0 then
+ return 0.0
+ else
+ return 1.0
+ end
+end
+function pgfluamathfunctions.real(x)
+ -- "ensure that x contains a decimal point" is kind of a no-op here, isn't it!?
+ return x
+end
+
+function pgfluamathfunctions.rnd()
+ return mathrandom()
+end
+
+function pgfluamathfunctions.rand()
+ return -1 + mathrandom() *2
+end
+
+function pgfluamathfunctions.random(x,y)
+ if x == nil and y == nil then
+ return mathrandom()
+ elseif y == nil then
+ return mathrandom(x)
+ else
+ return mathrandom(x,y)
+ end
+end
+
+function pgfluamathfunctions.deg(x)
+ return mathdeg(x)
+end
+
+function pgfluamathfunctions.rad(x)
+ return mathrad(x)
+end
+
+function pgfluamathfunctions.round(x)
+ if x<0 then
+ return -mathfloor(mathabs(x)+0.5)
+ else
+ return mathfloor(x + 0.5)
+ end
+end
+
+function pgfluamathfunctions.gcd(a, b)
+ if b == 0 then
+ return a
+ else
+ return pgfluamathfunctions.gcd(b, a%b)
+ end
+end
+
+function pgfluamathfunctions.isprime(a)
+ local ifisprime = true
+ if a == 1 then
+ ifisprime = false
+ elseif a == 2 then
+ ifisprime = true
+-- if a > 2 then
+ else
+ local i, imax = 2, mathceil(mathsqrt(a)) + 1
+ while ifisprime and (i < imax) do
+ if pgfluamathfunctions.gcd(a,i) ~= 1 then
+ ifisprime = false
+ end
+ i = i + 1
+ end
+ end
+ if ifisprime then
+ return 1
+ else
+ return 0
+ end
+end
+
+
+function pgfluamathfunctions.split_braces_to_explist(s)
+ -- (Thanks to mpg and zappathustra from fctt)
+ -- Make unpack available whatever lua version is used
+ -- (unpack in lua 5.1 table.unpack in lua 5.2)
+ local unpack = table.unpack or unpack
+ local t = {}
+ for i in s:gmatch('%b{}') do
+ table.insert(t, tonumber(i:sub(2, -2)))
+ end
+ return unpack(t)
+end
+
+function pgfluamathfunctions.split_braces_to_table(s)
+ local t = {}
+ for i in s:gmatch('%b{}') do
+ table.insert(t, tonumber(i:sub(2, -2)))
+ end
+ return t
+end
+
+function pgfluamathfunctions.mathtrue()
+ return 1.0
+end
+pgfluamathfunctions.stringToFunctionMap["true"] = pgfluamathfunctions.mathtrue
+
+function pgfluamathfunctions.mathfalse()
+ return 0.0
+end
+pgfluamathfunctions.stringToFunctionMap["false"] = pgfluamathfunctions.mathfalse
+
+function pgfluamathfunctions.frac(a)
+ -- should be positive, apparently
+ return mathabs(a - pgfluamathfunctions.int(a))
+end
+
+function pgfluamathfunctions.int(a)
+ if a < 0 then
+ return -mathfloor(mathabs(a))
+ else
+ return mathfloor(a)
+ end
+end
+
+function pgfluamathfunctions.iseven(a)
+ if (a % 2) == 0 then
+ return 1.0
+ else
+ return 0.0
+ end
+end
+
+function pgfluamathfunctions.isodd(a)
+ if (a % 2) == 0 then
+ return 0.0
+ else
+ return 1.0
+ end
+end
+
+function pgfluamathfunctions.mod(x,y)
+ if x/y < 0 then
+ return -(mathabs(x)%mathabs(y))
+ else
+ return mathabs(x)%mathabs(y)
+ end
+end
+
+function pgfluamathfunctions.Mod(x,y)
+ local tmp = pgfluamathfunctions.mod(x,y)
+ if tmp < 0 then
+ tmp = tmp + y
+ end
+ return tmp
+end
+
+function pgfluamathfunctions.Sin(x)
+ return mathsin(trigFormatToRadians(x))
+end
+pgfluamathfunctions.sin=pgfluamathfunctions.Sin
+
+
+function pgfluamathfunctions.cosh(x)
+ -- math.cosh is deprecated as of LUA 5.3 . reimplement it:
+ return 0.5* (mathexp(x) + mathexp(-x))
+end
+function pgfluamathfunctions.sinh(x)
+ -- math.sinh is deprecated as of LUA 5.3 . reimplement it:
+ return 0.5* (mathexp(x) - mathexp(-x))
+end
+
+local sinh = pgfluamathfunctions.sinh
+local cosh = pgfluamathfunctions.cosh
+function pgfluamathfunctions.tanh(x)
+ -- math.tanh is deprecated as of LUA 5.3 . reimplement it:
+ return sinh(x)/cosh(x)
+end
+
+function pgfluamathfunctions.Cos(x)
+ return mathcos(trigFormatToRadians(x))
+end
+pgfluamathfunctions.cos=pgfluamathfunctions.Cos
+
+function pgfluamathfunctions.Tan(x)
+ return mathtan(trigFormatToRadians(x))
+end
+pgfluamathfunctions.tan=pgfluamathfunctions.Tan
+
+function pgfluamathfunctions.aSin(x)
+ return radiansToTrigFormat(mathasin(x))
+end
+pgfluamathfunctions.asin=pgfluamathfunctions.aSin
+
+function pgfluamathfunctions.aCos(x)
+ return radiansToTrigFormat(mathacos(x))
+end
+pgfluamathfunctions.acos=pgfluamathfunctions.aCos
+
+function pgfluamathfunctions.aTan(x)
+ return radiansToTrigFormat(mathatan(x))
+end
+pgfluamathfunctions.atan=pgfluamathfunctions.aTan
+
+local mathatan2
+if math.atan2 == nil then
+ -- math.atan2 has been deprecated since LUA 5.3
+ mathatan2 = function (y,x) return mathatan(y,x) end
+else
+ mathatan2 = math.atan2
+end
+
+function pgfluamathfunctions.aTan2(y,x)
+ return radiansToTrigFormat(mathatan2(y,x))
+end
+pgfluamathfunctions.atan2=pgfluamathfunctions.aTan2
+pgfluamathfunctions.atantwo=pgfluamathfunctions.aTan2
+
+function pgfluamathfunctions.cot(x)
+ return pgfluamathfunctions.cos(x) / pgfluamathfunctions.sin(x)
+end
+function pgfluamathfunctions.sec(x)
+ return 1 / pgfluamathfunctions.cos(x)
+end
+function pgfluamathfunctions.cosec(x)
+ return 1 / pgfluamathfunctions.sin(x)
+end
+
+function pgfluamathfunctions.pointnormalised (pgfx, pgfy)
+ local pgfx_normalised, pgfy_normalised
+ if pgfx == 0. and pgfy == 0. then
+ -- Original pgf macro gives this result
+ tex.dimen['pgf@x'] = "0pt"
+ tex.dimen['pgf@y'] = "1pt"
+ else
+ pgfx_normalised = pgfx/math.sqrt(pgfx^2 + pgfy^2)
+ pgfx_normalised = pgfx_normalised - pgfx_normalised%0.00001
+ pgfy_normalised = pgfy/math.sqrt(pgfx^2 + pgfy^2)
+ pgfy_normalised = pgfy_normalised - pgfy_normalised%0.00001
+ tex.dimen['pgf@x'] = tostring(pgfx_normalised) .. "pt"
+ tex.dimen['pgf@y'] = tostring(pgfy_normalised) .. "pt"
+ end
+ return nil
+end
+
+local isnan = function(x)
+ return x ~= x
+end
+
+pgfluamathfunctions.isnan = isnan
+
+local infty = 1/0
+pgfluamathfunctions.infty = infty
+
+local nan = math.sqrt(-1)
+pgfluamathfunctions.nan = nan
+
+local stringlen = string.len
+local globaltonumber = tonumber
+local stringsub=string.sub
+local stringformat = string.format
+local stringsub = string.sub
+
+-- like tonumber(x), but it also accepts nan, inf, infty, and the TeX FPU format
+function pgfluamathfunctions.tonumber(x)
+ if type(x) == 'number' then return x end
+ if not x then return x end
+
+ local len = stringlen(x)
+ local result = globaltonumber(x)
+ if not result then
+ if len >2 and stringsub(x,2,2) == 'Y' and stringsub(x,len,len) == ']' then
+ -- Ah - some TeX FPU input of the form 1Y1.0e3] . OK. transform it
+ local flag = stringsub(x,1,1)
+ if flag == '0' then
+ -- ah, 0.0
+ result = 0.0
+ elseif flag == '1' then
+ result = globaltonumber(stringsub(x,3, len-1))
+ elseif flag == '2' then
+ result = globaltonumber("-" .. stringsub(x,3, len-1))
+ elseif flag == '3' then
+ result = nan
+ elseif flag == '4' then
+ result = infty
+ elseif flag == '5' then
+ result = -infty
+ end
+ else
+ local lower = x:lower()
+ if lower == 'nan' then
+ result = nan
+ elseif lower == "-nan" then
+ result = nan
+ elseif lower == 'inf' or lower == 'infty' then
+ result = infty
+ elseif lower == '-inf' or lower == '-infty' then
+ result = -infty
+ end
+ end
+ end
+
+ return result
+end
+
+local stringlen = string.len
+local globaltonumber = tonumber
+local stringformat = string.format
+local stringsub = string.sub
+local stringfind = string.find
+local stringbyte = string.byte
+local NULL_CHAR = string.byte("0",1)
+
+local function discardTrailingZeros(x)
+ local result = x
+ -- printf is too stupid: I would like to have
+ -- 1. a fast method
+ -- 2. a reliable method
+ -- 3. full precision of x
+ -- 4. a fixed point representation
+ -- the 'f' modifier has trailing zeros (stupid!)
+ -- the 'g' modified can switch to scientific notation (no-go!)
+ local periodOff = stringfind(result, '.',1,true)
+ if periodOff ~= nil then
+ -- strip trailing zeros
+ local chars = { stringbyte(result,1,#result) };
+ local lastNonZero = #chars
+ for i = #chars, periodOff, -1 do
+ if chars[i] ~= NULL_CHAR then lastNonZero=i; break; end
+ end
+ if lastNonZero ~= #chars then
+ -- Ah: we had at least one trailing zero.
+ -- discard all but the last.
+ lastNonZero = mathmax(periodOff+1,lastNonZero)
+ end
+ result = stringsub(result, 1, lastNonZero)
+ end
+ return result;
+end
+
+local function discardTrailingZerosFromMantissa(x)
+ local mantissaStart = stringfind(x, "e")
+
+ local mantissa = stringsub(x,1,mantissaStart-1)
+ local exponent = stringsub(x,mantissaStart)
+
+ return discardTrailingZeros(mantissa) .. exponent
+end
+
+
+-- a helper function which has no catcode issues when communicating with TeX:
+function pgfluamathfunctions.tostringfixed(x)
+ if x == nil then
+ return ""
+ end
+
+ return discardTrailingZeros(stringformat("%f", x))
+end
+
+-- converts an input number to a string which is accepted by the TeX FPU
+function pgfluamathfunctions.toTeXstring(x)
+ local result = ""
+ if x ~= nil then
+ if x == infty then result = "4Y0.0e0]"
+ elseif x == -infty then result = "5Y0.0e0]"
+ elseif isnan(x) then result = "3Y0.0e0]"
+ elseif x == 0 then result = "0Y0.0e0]"
+ else
+ result = discardTrailingZerosFromMantissa(stringformat("%.10e", x))
+ if x > 0 then
+ result = "1Y" .. result .. "]"
+ else
+ result = "2Y" .. stringsub(result,2) .. "]"
+ end
+ end
+ end
+ return result
+end
+
+return pgfluamathfunctions
diff --git a/graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/parser.lua b/graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/parser.lua
new file mode 100644
index 0000000000..42b690e6cf
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/libraries/luamath/pgf/luamath/parser.lua
@@ -0,0 +1,481 @@
+-- Copyright 2011 by Christophe Jorssen and Mark Wibrow
+-- Copyright 2014 by Christian Feuersaenger
+--
+-- This file may be distributed and/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License.
+--
+-- See the file doc/generic/pgf/licenses/LICENSE for more details.
+--
+-- $Id$
+--
+-- usage:
+--
+-- pgfluamathparser = require("pgf.luamath.parser")
+--
+-- local result = pgfluamathparser.pgfmathparse("1+ 2*4^2")
+--
+-- This LUA class has a direct backend in \pgfuselibrary{luamath}, see the documentation of that TeX package.
+
+local pgfluamathparser = pgfluamathparser or {}
+
+pgfluamathfunctions = require("pgf.luamath.functions")
+
+-- lpeg is always present in luatex
+local lpeg = require("lpeg")
+
+local S, P, R = lpeg.S, lpeg.P, lpeg.R
+local C, Cc, Ct = lpeg.C, lpeg.Cc, lpeg.Ct
+local Cf, Cg, Cs = lpeg.Cf, lpeg.Cg, lpeg.Cs
+local V = lpeg.V
+local match = lpeg.match
+
+local space_pattern = S(" \n\r\t")^0
+local tex_unit =
+ P('pt') + P('mm') + P('cm') + P('in') +
+ -- while valid units, the font-depending ones need special attention... move them to the TeX side. For now.
+ -- P('ex') + P('em') +
+ P('bp') + P('pc') +
+ P('dd') + P('cc') + P('sp');
+
+local one_digit_pattern = R("09")
+local positive_integer_pattern = one_digit_pattern^1
+-- FIXME : it might be a better idea to remove '-' from all number_patterns! Instead, rely on the prefix operator 'neg' to implement negative numbers.
+-- Is that wise? It is certainly less efficient...
+local integer_pattern = S("+-")^-1 * positive_integer_pattern
+-- Valid positive decimals are |xxx.xxx|, |.xxx| and |xxx.|
+local positive_integer_or_decimal_pattern = positive_integer_pattern * ( P(".") * one_digit_pattern^0)^-1 +
+ (P(".") * one_digit_pattern^1)
+local integer_or_decimal_pattern = S("+-")^-1 * positive_integer_or_decimal_pattern
+local fpu_pattern = R"05" * P"Y" * positive_integer_or_decimal_pattern * P"e" * S("+-")^-1 * R("09")^1 * P"]"
+local unbounded_pattern = P"inf" + P"INF" + P"nan" + P"NaN" + P"Inf"
+local number_pattern = C(unbounded_pattern + fpu_pattern + integer_or_decimal_pattern * (S"eE" * integer_pattern + C(tex_unit))^-1)
+
+local underscore_pattern = P("_")
+
+local letter_pattern = R("az","AZ")
+local alphanum__pattern = letter_pattern + one_digit_pattern + underscore_pattern
+
+local identifier_pattern = letter_pattern^1 * alphanum__pattern^0
+
+local openparen_pattern = P("(") * space_pattern
+local closeparen_pattern = P(")")
+local opencurlybrace_pattern = P("{")
+local closecurlybrace_pattern = P("}")
+local openbrace_pattern = P("[")
+local closebrace_pattern = P("]")
+
+-- hm. what about '\\' or '\%' ?
+-- accept \pgf@x, \count0, \dimen42, \c@pgf@counta, \wd0, \ht0, \dp 0
+local controlsequence_pattern = P"\\" * C( (R("az","AZ") + P"@")^1) * space_pattern* C( R"09"^0 )
+
+-- local string = P('"') * C((1 - P('"'))^0) * P('"')
+
+local comma_pattern = P(",") * space_pattern
+
+
+----------------
+local TermOp = C(S("+-")) * space_pattern
+local EqualityOp = C( P"==" + P"!=" ) * space_pattern
+local RelationalOp = C( P"<=" + P">=" + P"<" + P">" ) * space_pattern
+local FactorOp = C(S("*/")) * space_pattern
+
+-- Grammar
+local Exp, Term, Factor = V"Exp", V"Term", V"Factor"
+local Prefix = V"Prefix"
+local Postfix = V"Postfix"
+
+
+
+local function eval (v1, op, v2)
+ if (op == "+") then return v1 + v2
+ elseif (op == "-") then return v1 - v2
+ elseif (op == "*") then return v1 * v2
+ elseif (op == "/") then return v1 / v2
+ else
+ error("This function must not be invoked for operator "..op)
+ end
+end
+
+local pgfStringToFunctionMap = pgfluamathfunctions.stringToFunctionMap
+local function function_eval(name, ... )
+ local f = pgfStringToFunctionMap[name]
+ if not f then
+ error("Function '" .. name .. "' is undefined (did not find pgfluamathfunctions."..name .." (looked into pgfluamathfunctions.stringToFunctionMap))")
+ end
+ -- FIXME: validate signature
+ return f(...)
+end
+
+
+local func =
+ (C(identifier_pattern) * space_pattern * openparen_pattern * Exp * (comma_pattern * Exp)^0 * closeparen_pattern) / function_eval;
+
+local functionWithoutArg = identifier_pattern / function_eval
+
+-- this is what can occur as exponent after '^'.
+-- I have the impression that the priorities could be implemented in a better way than this... but it seems to work.
+local pow_exponent =
+ -- allows 2^-4, 2^1e4, 2^2
+ -- FIXME : why not 2^1e2 ?
+ Cg(C(integer_or_decimal_pattern)
+ -- 2^pi, 2^multiply(2,2)
+ + Cg(func+functionWithoutArg)
+ -- 2^(2+2)
+ + openparen_pattern * Exp * closeparen_pattern )
+
+local function prefix_eval(op, x)
+ if op == "-" then
+ return pgfluamathfunctions.neg(x)
+ elseif op == "!" then
+ return pgfluamathfunctions.notPGF(x)
+ else
+ error("This function must not be invoked for operator "..op)
+ end
+end
+
+
+local prefix_operator = C( S"-!" )
+local prefix_operator_pattern = (prefix_operator * space_pattern * Cg(Prefix) ) / prefix_eval
+
+-- apparently, we need to distinguish between <expr> ! and <expr> != <expr2>:
+local postfix_operator = C( S"r!" - P"!=" ) + C(P"^") * space_pattern * pow_exponent
+
+pgfluamathfunctions.functionMustBeEvaluatedInTeX = function()
+ error("The function in this context cannot be evaluated by LUA because it depends on TeX macros.")
+end
+
+local ternary_eval = pgfluamathfunctions.ifthenelse
+
+local factorial_eval = pgfluamathfunctions.factorial
+local deg = pgfluamathfunctions.deg
+local pow_eval = pgfluamathfunctions.pow
+
+-- @param prefix the argument before the postfix operator.
+-- @param op either nil or the postfix operator
+-- @param arg either nil or the (mandatory) argument for 'op'
+local function postfix_eval(prefix, op, arg)
+ local result
+ if op == nil then
+ result = prefix
+ elseif op == "r" then
+ if arg then error("parser setup error: expected nil argument") end
+ result = deg(prefix)
+ elseif op == "!" then
+ if arg then error("parser setup error: expected nil argument") end
+ result = factorial_eval(prefix)
+ elseif op == "^" then
+ if not arg then error("parser setup error: ^ with its argument") end
+ result = pow_eval(prefix, arg)
+ else
+ error("Parser setup error: " .. tostring(op) .. " unexpected in this context")
+ end
+ return result
+end
+
+local function equality_eval(v1, op, v2)
+ local fct
+ if (op == "==") then fct = pgfluamathfunctions.equal
+ elseif (op == "!=") then fct = pgfluamathfunctions.notequal
+ else
+ error("This function must not be invoked for operator "..op)
+ end
+ return fct(v1,v2)
+end
+local function relational_eval(v1, op, v2)
+ local fct
+ if (op == "<") then fct = pgfluamathfunctions.less
+ elseif (op == ">") then fct = pgfluamathfunctions.greater
+ elseif (op == ">=") then fct = pgfluamathfunctions.notless
+ elseif (op == "<=") then fct = pgfluamathfunctions.notgreater
+ else
+ error("This function must not be invoked for operator "..op)
+ end
+ return fct(v1,v2)
+end
+
+-- @return either the box property or nil
+-- @param cs "wd", "ht", or "dp"
+-- @param intSuffix some integer
+local function get_tex_box(cs, intSuffix)
+ -- assume get_tex_box is only called when a dimension is required.
+ local result
+ pgfluamathparser.units_declared = true
+ local box =tex.box[tonumber(intSuffix)]
+ if not box then error("There is no box " .. intSuffix) end
+ if cs == "wd" then
+ result = box.width / 65536
+ elseif cs == "ht" then
+ result = box.height / 65536
+ elseif cs == "dp" then
+ result = box.depth / 65536
+ else
+ result = nil
+ end
+ return result
+end
+
+
+local function controlsequence_eval(cs, intSuffix)
+ local result
+ if intSuffix and #intSuffix >0 then
+ if cs == "count" then
+ result= pgfluamathparser.get_tex_count(intSuffix)
+ elseif cs == "dimen" then
+ result= pgfluamathparser.get_tex_dimen(intSuffix)
+ else
+ result = get_tex_box(cs,intSuffix)
+ if not result then
+ -- this can happen - we cannot expand \chardef'ed boxes here.
+ -- this will be done by the TeX part
+ error('I do not know/support the TeX register "\\' .. cs .. '"')
+ end
+ end
+ else
+ result = pgfluamathparser.get_tex_register(cs)
+ end
+ return result
+end
+
+pgfluamathparser.units_declared = false
+function pgfluamathparser.get_tex_register(register)
+ -- register is a string which could be a count or a dimen.
+ if pcall(tex.getcount, register) then
+ return tex.count[register]
+ elseif pcall(tex.getdimen, register) then
+ pgfluamathparser.units_declared = true
+ return tex.dimen[register] / 65536 -- return in points.
+ else
+ error('I do not know the TeX register "' .. register .. '"')
+ return nil
+ end
+end
+
+function pgfluamathparser.get_tex_count(count)
+ -- count is expected to be a number
+ return tex.count[tonumber(count)]
+end
+
+function pgfluamathparser.get_tex_dimen(dimen)
+ -- dimen is expected to be a number
+ pgfluamathparser.units_declared = true
+ return tex.dimen[tonumber(dimen)] / 65536
+end
+
+function pgfluamathparser.get_tex_sp(dimension)
+ -- dimension should be a string
+ pgfluamathparser.units_declared = true
+ return tex.sp(dimension) / 65536
+end
+
+
+local initialRule = V"initial"
+
+local Summand = V"Summand"
+local Relational = V"Relational"
+local Equality = V"Equality"
+local LogicalOr = V"LogicalOr"
+local LogicalAnd = V"LogicalAnd"
+
+local pgftonumber = pgfluamathfunctions.tonumber
+local tonumber_withunit = pgfluamathparser.get_tex_sp
+local function number_optional_units_eval(x, unit)
+ if not unit then
+ return pgftonumber(x)
+ else
+ return tonumber_withunit(x)
+ end
+end
+
+-- @param scale the number.
+-- @param controlsequence either nil in which case just the number must be returned or a control sequence
+-- @see controlsequence_eval
+local function scaled_controlsequence_eval(scale, controlsequence, intSuffix)
+ if controlsequence==nil then
+ return scale
+ else
+ return scale * controlsequence_eval(controlsequence, intSuffix)
+ end
+end
+
+-- Grammar
+--
+-- for me:
+-- - use '/' to evaluate all expressions which contain a _constant_ number of captures.
+-- - use Cf to evaluate expressions which contain a _dynamic_ number of captures
+--
+-- see unittest_luamathparser.tex for tons of examples
+local G = P{ "initialRule",
+ initialRule = space_pattern* Exp * -1;
+ -- ternary operator (or chained ternary operators):
+ -- FIXME : is this chaining a good idea!?
+ Exp = Cf( LogicalOr * Cg(P"?" * space_pattern * LogicalOr * P":" *space_pattern * LogicalOr )^0, ternary_eval) ;
+ LogicalOr = Cf(LogicalAnd * (P"||" * space_pattern * LogicalAnd)^0, pgfluamathfunctions.orPGF);
+ LogicalAnd = Cf(Equality * (P"&&" * space_pattern * Equality)^0, pgfluamathfunctions.andPGF);
+ Equality = Cf(Relational * Cg(EqualityOp * Relational)^0, equality_eval);
+ Relational = Cf(Summand * Cg(RelationalOp * Summand)^0, relational_eval);
+ Summand = Cf(Term * Cg(TermOp * Term)^0, eval) ;
+ Term = Cf(Prefix * Cg(FactorOp * Prefix)^0, eval);
+ Prefix = prefix_operator_pattern + Postfix;
+ -- this calls 'postfix_eval' with nil arguments if it is no postfix operation.. but that does not hurt (right?)
+ Postfix = Factor * (postfix_operator * space_pattern)^-1 / postfix_eval;
+ Factor =
+ (
+ number_pattern / number_optional_units_eval *
+ -- this construction will evaluate number_pattern with 'number_optional_units_eval' FIRST.
+ -- also accept '0.5 \pgf@x' here:
+ space_pattern *controlsequence_pattern^-1 / scaled_controlsequence_eval
+ + func
+ + functionWithoutArg
+ + openparen_pattern * Exp * closeparen_pattern
+ + controlsequence_pattern / controlsequence_eval
+ ) *space_pattern
+ ;
+}
+
+-- does not reset units_declared.
+local function pgfmathparseinternal(str)
+ local result = match(G,str)
+ if result == nil then
+ error("The string '" .. str .. "' is no valid PGF math expression. Please check for syntax errors.")
+ end
+ return result
+end
+
+
+-- This is the math parser function in this module.
+--
+-- @param str a string like "1+1" which is accepted by the PGF math language
+-- @return the result of the expression.
+--
+-- Throws an error if the string is no valid expression.
+function pgfluamathparser.pgfmathparse(str)
+ pgfluamathparser.units_declared = false
+
+ return pgfmathparseinternal(str)
+end
+
+local pgfmathparse = pgfluamathparser.pgfmathparse
+local tostringfixed = pgfluamathfunctions.tostringfixed
+local tostringfpu = pgfluamathfunctions.toTeXstring
+
+local tmpFunctionArgumentPrefix = "tmpVar"
+local stackOfLocalFunctions = {}
+
+-- This is a backend for PGF's 'declare function'.
+-- \tikzset{declare function={mu(\x,\i)=\x^\i;}}
+-- will boil down to
+-- pgfluamathparser.declareExpressionFunction("mu", 2, "#1^#2")
+--
+-- The local function will be pushed on a stack of known local functions and is
+-- available until popLocalExpressionFunction() is called. TeX will call this using
+-- \aftergroup.
+--
+-- @param name the name of the new function
+-- @param numArgs the number of arguments
+-- @param expression an expression containing #1, ... #n where n is numArgs
+--
+-- ATTENTION: local functions behave DIFFERENTLY in LUA!
+-- In LUA, local variables are not expanded whereas TeX expands them.
+-- The difference is
+--
+-- declare function={mu1(\x,\i)=\x^\i;}
+-- \pgfmathparse{mu1(-5,2)} --> -25
+-- \pgfluamathparse{mu1(-5,2)} --> 25
+--
+-- x = -5
+-- \pgfmathparse{mu1(x,2)} --> 25
+-- \pgfluamathparse{mu1(x,2)} --> 25
+--
+-- In an early prototype, I simulated TeX's expansion to fix the first case (successfully).
+-- BUT: that "simulated expansion" broke the second case because LUA will evaluate "x" and hand -5 to the local function.
+-- I decided to keep it as is. Perhaps we should fix PGF's expansion approach in TeX (which is ugly anyway)
+function pgfluamathparser.pushLocalExpressionFunction(name, numArgs, expression)
+ -- now we have "tmpVar1^tmpVar2" instead of "#1^#2"
+ local normalizedExpr = expression:gsub("#", tmpFunctionArgumentPrefix)
+ local restores = {}
+ local tmpVars = {}
+ for i=1,numArgs do
+ local tmpVar = tmpFunctionArgumentPrefix .. tostring(i)
+ tmpVars[i] = tmpVar
+ end
+
+ local newFunction = function(...)
+ local args = table.pack(...)
+
+ -- define "tmpVar1" ... "tmpVarN" to return args[i].
+ -- Of course, we need to restore "tmpVar<i>" after we return!
+ for i=1,numArgs do
+ local tmpVar = tmpVars[i]
+ local value = args[i]
+ restores[i] = pgfStringToFunctionMap[tmpVar]
+ pgfStringToFunctionMap[tmpVar] = function () return value end
+ end
+
+ -- parse our expression.
+
+ -- FIXME : this here is an attempt to mess around with "units_declared".
+ -- It would be better to call pgfmathparse and introduce some
+ -- semaphore to check if pgfmathparse is a nested call-- in this case, it should
+ -- not reset units_declared. But there is no "finally" block and pcall is crap (looses stack trace).
+ local success,result = pcall(pgfmathparseinternal, normalizedExpr)
+
+ -- remove 'tmpVar1', ... from the function table:
+ for i=1,numArgs do
+ local tmpVar = tmpVars[i]
+ pgfStringToFunctionMap[tmpVar] = restores[i]
+ end
+
+ if success==false then error(result) end
+ return result
+ end
+ table.insert(stackOfLocalFunctions, name)
+ pgfStringToFunctionMap[name] = newFunction
+end
+
+function pgfluamathparser.popLocalExpressionFunction()
+ local name = stackOfLocalFunctions[#stackOfLocalFunctions]
+ pgfStringToFunctionMap[name] = nil
+ -- this removes the last element:
+ table.remove(stackOfLocalFunctions)
+end
+
+
+-- A Utility function which simplifies the interaction with the TeX code
+-- @param expression the input expression (string)
+-- @param outputFormatChoice 0 if the result should be a fixed point number, 1 if it should be in FPU format
+-- @param showErrorMessage (boolean) true if any error should be displayed, false if errors should simply result in an invocation of TeX's parser (the default)
+--
+-- it defines \pgfmathresult and \ifpgfmathunitsdeclared
+function pgfluamathparser.texCallParser(expression, outputFormatChoice, showErrorMessage)
+ local success, result
+ if showErrorMessage then
+ result = pgfmathparse(expression)
+ success = true
+ else
+ success, result = pcall(pgfmathparse, expression)
+ end
+
+ if success and result then
+ local result_str
+ if outputFormatChoice == 0 then
+ -- luamath/output format=fixed
+ result_str = tostringfixed(result)
+ else
+ -- luamath/output format=fixed
+ result_str = tostringfpu(result)
+ end
+ tex.sprint("\\def\\pgfmathresult{" .. result_str .. "}")
+ if pgfluamathparser.units_declared then
+ tex.sprint("\\pgfmathunitsdeclaredtrue")
+ else
+ tex.sprint("\\pgfmathunitsdeclaredfalse")
+ end
+ else
+ tex.sprint("\\def\\pgfmathresult{}")
+ tex.sprint("\\pgfmathunitsdeclaredfalse")
+ end
+end
+
+return pgfluamathparser
diff --git a/graphics/pgf/base/tex/generic/libraries/luamath/pgflibraryluamath.code.tex b/graphics/pgf/base/tex/generic/libraries/luamath/pgflibraryluamath.code.tex
new file mode 100644
index 0000000000..cd991dab4c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/libraries/luamath/pgflibraryluamath.code.tex
@@ -0,0 +1,558 @@
+% Copyright 2019 by Christophe Jorssen and Mark Wibrow
+% Copyright 2019 by Christian Feuersänger
+%
+% This file may be distributed and/or modified
+%
+% 1. under the LaTeX Project Public License and/or
+% 2. under the GNU Public License.
+%
+% See the file doc/generic/pgf/licenses/LICENSE for more details.
+%
+% $Id$
+%
+%
+% This is a library for a LUA math parser and LUA math operations.
+% Advantage compared to its TeX pendant: it is FASTER and has HIGHER
+% ACCURACY.
+% Disadvantage: any function declared by means of
+% \pgfmathdeclarefunction is NOT automatically transported to the LUA
+% side (at the time of this writing).
+% LUA functions need to be defined by means of LUA code
+%
+% function pgfluamathfunctions.myoperation(a,b)
+% return a*2*b
+% end
+%
+% this will automatically set up 'myoperation' for use in
+% \pgfluamathparse{myoperation(4,2)}
+%
+% The library has TWO use-cases which are more or less distinct: one
+% is to use LUA for all math function, but not for math expression
+% parsing -- this was still under control of the TeX math parser.
+% This approach works like the 'fpu' library.
+%
+% The second approach is to use LUA to parse all math expressions -
+% but if someone calls \pgfmathadd@{1}{2}, it would use the TeX
+% command. This approach is relatively lightweight because it does not
+% need to substitute all \pgfmath* macros.
+%
+% One can mix both modes.
+
+% *******************************************************************
+% Some luatex stuff. Should be put elsewhere (e.g. in
+% pgfutil-luatex-engine).
+
+\edef\pgfliblua@oldcatcodedoublequote{\the\catcode`\"}%
+\catcode`\"=12
+
+% We assume luatex version > 0.39:
+% - \directlua <general text> will work
+% - \directlua is the only luatex primitive that we can assume
+% accessible without being prefixed by the format via
+% tex.enableprimitives.
+% Ideas taken from the ifluatex package (Heiko Oberdiek)
+\let\pgfutil@ifluatex\iffalse
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname directlua\endcsname\relax
+\else
+ \expandafter\let\csname pgfutil@ifluatex\expandafter\endcsname
+ \csname iftrue\endcsname
+\fi
+
+\pgfutil@ifluatex
+ \let\pgfutil@directlua\directlua
+ \pgfutil@directlua{%
+ tex.enableprimitives('pgfutil@',{'luaescapestring'})}%
+\else
+ \def\pgfutil@directlua#1{}%
+ \def\pgfutil@luaescapestring#1{}%
+\fi
+
+\pgfutil@directlua{%
+ pgfluamathfunctions = require("pgf.luamath.functions")
+ pgfluamathparser = require("pgf.luamath.parser")}%
+
+
+% Patch some configuration macros such that the modifications are
+% available in LUA as well:
+\pgfkeys{
+ /pgf/trig format/deg/.add code={}{\directlua{pgfluamathfunctions.setTrigFormat("deg")}\aftergroup\pgfmath@settrigformat},
+ /pgf/trig format/rad/.add code={}{\directlua{pgfluamathfunctions.setTrigFormat("rad")}\aftergroup\pgfmath@settrigformat},
+}%
+% ... and reactivate the key:
+\pgfmathiftrigonometricusesdeg{%
+ \pgfkeys{/pgf/trig format/deg}%
+}{%
+ \pgfkeys{/pgf/trig format/rad}%
+}%
+
+% re-activates the current trig format. This is important after a TeX
+% group has been closed.
+\def\pgfmath@settrigformat{%
+ \pgfmathiftrigonometricusesdeg{%
+ \directlua{pgfluamathfunctions.setTrigFormat("deg")}%
+ }{%
+ \directlua{pgfluamathfunctions.setTrigFormat("rad")}%
+ }%
+}%
+
+\let\pgfmathsetseed@pgfbasic = \pgfmathsetseed
+\def\pgfmathsetseed#1{%
+ \pgfmathsetseed@pgfbasic{#1}%
+ \directlua{pgfluamathfunctions.setRandomSeed(pgfluamathfunctions.tonumber("\pgfmathresult"))}%
+}%
+
+% Patch 'declare function' such that it communicates the function
+% directly to LUA.
+\let\pgfmathnotifynewdeclarefunction@orig=\pgfmathnotifynewdeclarefunction
+\def\pgfmathnotifynewdeclarefunction#1#2#3{%
+ \pgfmathnotifynewdeclarefunction@orig{#1}{#2}{#3}%
+ %
+ % we have to check if '#3' contains control sequences.
+ % this is highly tricky as it may contain '#1'...
+ \begingroup
+ \toks0={#3}%
+ \xdef\pgf@marshal@glob{\the\toks0 }%
+ \endgroup
+ \pgfutil@command@to@string\pgf@marshal@glob\pgf@marshal
+ \expandafter\pgfutilifcontainsmacro\expandafter{\pgf@marshal}{%
+ \def\pgf@temp{1}%
+ }{%
+ \def\pgf@temp{0}%
+ }%
+ \if1\pgf@temp
+ % let lua produce an error when evaluating this function -- we cannot possibly expand the macro to its current state:
+ \directlua{pgfluamathparser.pushLocalExpressionFunction(%
+ "\pgfutil@luaescapestring{#1}",%
+ #2,%
+ "functionMustBeEvaluatedInTeX")}%
+ \else
+ \directlua{pgfluamathparser.pushLocalExpressionFunction(%
+ "\pgfutil@luaescapestring{#1}",%
+ #2,%
+ "\pgfutil@luaescapestring{#3}")}%
+ \fi
+ %
+ % ensure that the local function is removed at the end of the
+ % scope. To this end, we maintain a stack on the LUA side.
+ \aftergroup\pgfluamathparse@pop@local@function
+}%
+
+\def\pgfluamathparse@pop@local@function{%
+ \directlua{pgfluamathparser.popLocalExpressionFunction()}%
+}%
+
+% End of luatex stuff
+% *******************************************************************
+
+% Loading part: based on fpu library
+
+
+% if LUA failed to evaluate the expression, it will be evaluated in
+% TeX as fallback. This boolean defines if the error message shall be
+% shown or suppressed. The default is to suppress it and show any
+% resulting TeX errors.
+%
+% If LUA fails \pgfmathresult will be empty.
+%
+% This is actually only for debugging; it will be set implicitly when
+% activating/deactivating TeX fallback.
+\newif\ifpgfluamathshowerrormessage
+
+% Defines what happens if LUA failed to evaluate the expression: if
+% this is true, the TeX parser will be invoked as fallback.
+%
+% This happens
+% - for \chardef'ed boxes for which LUA lacks evaluation capabilities
+% - if the expression includes some function which is unavailable in
+% LUA (defined only in TeX)
+% - some special cases which simply haven't been added to the LUA
+% parser (yet).
+% At the time of this writing, this includes
+% -- arrays created via '{}' and indexed with '[]'
+% -- strings with "<str>"
+% -- 'scalar' function
+% -- hex/octal/binary input
+\newif\ifpgfluamathenableTeXfallback
+
+\newif\ifpgfluamathcomputationactive
+\newif\ifpgfluamathparseractive
+
+\def\pgfluamath@makecomputationactive{%
+ \ifpgfluamathcomputationactive
+ \else
+ \pgfluamath@checkuninstallcmd%
+ \pgfluamath@install%
+ \pgfluamathcomputationactivetrue
+ \fi}%
+
+\def\pgfluamath@makecomputationinactive{%
+ \ifpgfluamathcomputationactive
+ \pgfluamath@uninstall%
+ \pgfluamathcomputationactivefalse
+ \fi}%
+
+\let\pgfluamath@pgfmathparse\pgfmathparse
+\def\pgfluamath@makeparseractive{%
+ \ifpgfluamathparseractive
+ \else
+ \let\pgfluamath@pgfmathparse\pgfmathparse
+ \let\pgfmathparse\pgfluamathparse
+ \let\pgfmath@iftrue=\pgfmathluamath@iftrue
+ \pgfluamathparseractivetrue
+ \fi}%
+
+\let\pgfmath@iftrue@basic=\pgfmath@iftrue
+
+\def\pgfluamath@makeparserinactive{%
+ \ifpgfluamathparseractive
+ \let\pgfmathparse\pgfluamath@pgfmathparse
+ \let\pgfmath@iftrue=\pgfmath@iftrue@basic
+ \pgfluamathparseractivefalse
+ \fi}%
+
+\pgfqkeys{/pgf}{%
+ % Enable lua-side computation of \pgfmathresult
+ % (will be deactivated after the current TeX group)
+ luamath/.is choice,
+ luamath/only computation/.code={%
+ \pgfutil@ifluatex
+ \pgfluamath@makecomputationactive
+ \pgfluamath@makeparserinactive
+ \else
+ \pgfmath@error{Sorry, you need the luaTeX engine to use the
+ luamath library}{}%
+ \fi},
+ luamath/parser and computation/.code={%
+ \pgfutil@ifluatex
+ \pgfluamath@makecomputationactive
+ \pgfluamath@makeparseractive
+ \else
+ \pgfmath@error{Sorry, you need the luaTeX engine to use the
+ luamath library}{}%
+ \fi},
+ luamath/off/.code={%
+ \pgfluamath@makecomputationinactive
+ \pgfluamath@makeparserinactive},
+ luamath/.default=only computation,
+ %
+ % activates ONLY the parser. This is fast and does not replace the
+ % \pgfmath* functions.
+ % @see |parser and computation| which also allows to write
+ % \pgfmathadd{1}{2} to call LUA
+ luamath/parser/.code={%
+ \pgfutil@ifluatex
+ \pgfluamath@makeparseractive
+ \else
+ \pgfmath@error{Sorry, you need the luaTeX engine to use the
+ luamath library}{}%
+ \fi
+ },
+ luamath/output format/.is choice,
+ luamath/output format/fixed/.code= {\def\pgfluamath@outputformat@choice{0}},
+ % returns results for use in the FPU
+ luamath/output format/float/.code= {\def\pgfluamath@outputformat@choice{1}},
+ luamath/output format/fixed,
+ % this is merely useful for debugging purposes, I guess.
+ luamath/show error message/.is if=pgfluamathshowerrormessage,
+ luamath/enable TeX fallback/.is choice,
+ luamath/enable TeX fallback/true/.code={\pgfluamathenableTeXfallbacktrue\pgfluamathshowerrormessagefalse},
+ luamath/enable TeX fallback/false/.code={\pgfluamathenableTeXfallbackfalse\pgfluamathshowerrormessagetrue},
+ luamath/enable TeX fallback/.default=true,
+ luamath/enable TeX fallback=true,
+}%
+
+\def\pgfluamath@uninstall@appendcmd#1{%
+ \expandafter\gdef\expandafter\pgfluamath@uninstall\expandafter{%
+ \pgfluamath@uninstall #1}}%
+
+% If the uninstall command is already assembled, it will skip the
+% uninstall assemblation.
+\def\pgfluamath@checkuninstallcmd{%
+ \pgfutil@ifundefined{pgfluamath@uninstall}{%
+ \global\let\pgfluamath@uninstall=\pgfutil@empty
+ }{%
+ % We already HAVE an uninstall command (prepared globally).
+ % So: don't waste time assembling one!
+ \def\pgfluamath@uninstall@appendcmd##1{}%
+ \def\pgfluamath@prepareuninstallcmd##1{}%
+ }%
+}%
+
+% This assembles an uninstall command globally ON FIRST USAGE.
+% See \pgfmathfloat@plots@checkuninstallcmd
+\def\pgfluamath@prepareuninstallcmd#1{%
+ \expandafter\global\expandafter\let
+ \csname pgfluamath@backup@\string#1\endcsname=#1%
+ \expandafter\gdef\expandafter\pgfluamath@uninstall\expandafter{%
+ \pgfluamath@uninstall
+ \expandafter\let\expandafter#1\csname pgfluamath@backup@\string#1\endcsname}%
+}%
+
+\def\pgfluamath@install@function#1=#2{%
+ \pgfluamath@prepareuninstallcmd{#1}%
+ \let#1=#2%
+}%
+
+\def\pgfluamath@install{%
+ \pgfluamath@install@function\pgfmathadd@=\pgfluamathadd@%
+ \pgfluamath@install@function\pgfmathsubtract@=\pgfluamathsubtract@%
+ \pgfluamath@install@function\pgfmathneg@=\pgfluamathneg@%
+ \pgfluamath@install@function\pgfmathmultiply@=\pgfluamathmultiply@%
+ \pgfluamath@install@function\pgfmath@iftrue=\pgfmathluamath@iftrue%
+ \pgfluamath@install@function\pgfmathdivide@=\pgfluamathdivide@%
+ % \pgfluamath@install@function\pgfmathdiv@=\pgfluamathdiv@%
+ \pgfluamath@install@function\pgfmathfactorial@=\pgfluamathfactorial@%
+ \pgfluamath@install@function\pgfmathsqrt@=\pgfluamathsqrt@%
+ % \pgfluamath@install@function\pgfmathpow@=\pgfluamathpow@%
+ \pgfluamath@install@function\pgfmathe@=\pgfluamathe@%
+ \pgfluamath@install@function\pgfmathexp@=\pgfluamathexp@%
+ \pgfluamath@install@function\pgfmathln@=\pgfluamathln@%
+ \pgfluamath@install@function\pgfmathlogten@=\pgfluamathlogten@%
+ % \pgfluamath@install@function\pgfmathlogtwo@=\pgfluamathlogtwo@%
+ \pgfluamath@install@function\pgfmathabs@=\pgfluamathabs@%
+ \pgfluamath@install@function\pgfmathmod@=\pgfluamathmod@%
+ \pgfluamath@install@function\pgfmathMod@=\pgfluamathMod@%
+ \pgfluamath@install@function\pgfmathround@=\pgfluamathround@%
+ \pgfluamath@install@function\pgfmathfloor@=\pgfluamathfloor@%
+ \pgfluamath@install@function\pgfmathceil@=\pgfluamathceil@%
+ % \pgfluamath@install@function\pgfmathint@=\pgfluamathint@%
+ % \pgfluamath@install@function\pgfmathfrac@=\pgfluamathfrac@%
+ % \pgfluamath@install@function\pgfmathreal@=\pgfluamathreal@%
+ \pgfluamath@install@function\pgfmathgcd@=\pgfluamathgcd@%
+ \pgfluamath@install@function\pgfmathisprime@=\pgfluamathisprime@%
+ \pgfluamath@install@function\pgfmathpi@=\pgfluamathpi@%
+ \pgfluamath@install@function\pgfmathrad@=\pgfluamathrad@%
+ \pgfluamath@install@function\pgfmathdeg@=\pgfluamathdeg@%
+ \pgfluamath@install@function\pgfmathsin@=\pgfluamathsin@%
+ \pgfluamath@install@function\pgfmathcos@=\pgfluamathcos@%
+ \pgfluamath@install@function\pgfmathtan@=\pgfluamathtan@%
+ \pgfluamath@install@function\pgfmathsec@=\pgfluamathsec@%
+ \pgfluamath@install@function\pgfmathcosec@=\pgfluamathcosec@%
+ \pgfluamath@install@function\pgfmathcot@=\pgfluamathcot@%
+ \pgfluamath@install@function\pgfmathasin@=\pgfluamathasin@%
+ \pgfluamath@install@function\pgfmathacos@=\pgfluamathacos@%
+ \pgfluamath@install@function\pgfmathatan@=\pgfluamathatan@%
+ \pgfluamath@install@function\pgfmathatantwo@=\pgfluamathatantwo@%
+ \pgfluamath@install@function\pgfmathmax@=\pgfluamathmax@%
+ \pgfluamath@install@function\pgfmathmin@=\pgfluamathmin@%
+ % \pgfluamath@install@function\pgfmath@pi=\pgfluamathpi@%
+ % \pgfluamath@install@function\pgfmathpi=\pgfluamathpi@%
+ % \pgfluamath@install@function\pgfmathe@=\pgfluamathe@%
+ % \pgfluamath@install@function\pgfmathe=\pgfluamathe@%
+ % \pgfluamath@install@function\pgfmathlessthan@=\pgfluamathlessthan@%
+ % \pgfluamath@install@function\pgfmathless@=\pgfluamathlessthan@%
+ % \pgfluamath@install@function\pgfmathgreaterthan@=\pgfluamathgreaterthan@%
+ % \pgfluamath@install@function\pgfmathgreater@=\pgfluamathgreaterthan@%
+ % \pgfluamath@install@function\pgfmathpow@=\pgfluamathpow@
+ \pgfluamath@install@function\pgfmathrand@=\pgfluamathrand@
+ \pgfluamath@install@function\pgfmathrand=\pgfluamathrand@
+ \pgfluamath@install@function\pgfmathrnd@=\pgfluamathrnd@
+ \pgfluamath@install@function\pgfmathrnd=\pgfluamathrnd@
+ % \pgfluamath@install@function\pgfmathtrue@=\pgfluamathtrue@
+ % \pgfluamath@install@function\pgfmathfalse@=\pgfluamathfalse@
+ % \pgfluamath@install@function\pgfmathnot@=\pgfluamathnot@
+ % \pgfluamath@install@function\pgfmathhex@=\pgfluamathhex@
+ % \pgfluamath@install@function\pgfmathHex@=\pgfluamathHex@
+ % \pgfluamath@install@function\pgfmathoct@=\pgfluamathoct@
+ % \pgfluamath@install@function\pgfmathbin@=\pgfluamathbin@
+ % \pgfluamath@install@function\pgfmathand@=\pgfluamathand@
+ % \pgfluamath@install@function\pgfmathor@=\pgfluamathor@
+ % \pgfluamath@install@function\pgfmathfactorial@=\pgfluamathfactorial@
+ % \pgfluamath@install@function\pgfmathveclen@=\pgfluamathveclen@
+ % \pgfluamath@install@function\pgfmathcosh@=\pgfluamathcosh@
+ % \pgfluamath@install@function\pgfmathsinh@=\pgfluamathsinh@
+ % \pgfluamath@install@function\pgfmathtanh@=\pgfluamathtanh@
+ % \pgfluamath@install@function@unimplemented{ceil}%
+ % \pgfluamath@install@function@unimplemented{frac}%
+ % \pgfluamath@install@function@unimplemented{log2}%
+ % \pgfluamath@install@function@unimplemented{log10}%
+ % \pgfluamath@install@function@unimplemented{equalto}%
+ % \pgfluamath@install@function@unimplemented{random}%
+ % \pgfluamath@install@function@unimplemented{setseed}%
+ % \pgfluamath@install@function@unimplemented{Mod}%
+ % \pgfluamath@install@function@unimplemented{real}%
+ % \pgfluamath@install@function@unimplemented{notequal}%
+ \pgfluamath@install@function\pgfmathreciprocal=\pgfluamathreciprocal%
+ \pgfluamath@install@function\pgfpointnormalised=\pgfluamathpointnormalised
+}%
+
+\def\pgfluamathgetresult#1{%
+ \edef\pgfmathresult{\pgfutil@directlua{tex.print(-1,#1)}}}%
+
+\def\pgfmathluamath@iftrue{%
+ \if 0\pgfluamath@outputformat@choice
+ \let\pgfmathluamath@@iftrue@v=\pgfluamathone
+ \else
+ \let\pgfmathluamath@@iftrue@v=\pgfluamathfloatone
+ \fi
+ \pgfmathluamath@iftrue@
+}%
+\def\pgfluamathone{1.0}%
+\def\pgfluamathfloatone{1Y1.0e+00]}%
+\def\pgfmathluamath@iftrue@#1#2{%
+ \ifx\pgfmathresult\pgfmathluamath@@iftrue@v
+ \def\pgfmath@next{#1}%
+ \else
+ \def\pgfmath@next{#2}%
+ \fi
+ \pgfmath@next
+}%
+
+\def\pgfluamathpi@{%
+ \pgfluamathgetresult{pgfluamathfunctions.pi()}}%
+\def\pgfluamathe@{%
+ \pgfluamathgetresult{pgfluamathfunctions.e()}}%
+\def\pgfluamathadd@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.add(#1,#2)}}%
+\def\pgfluamathsubtract@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.subtract(#1,#2)}}%
+\def\pgfluamathneg@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.neg(#1)}}%
+\def\pgfluamathmultiply@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.multiply(#1,#2)}}%
+\def\pgfluamathdivide@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.divide(#1,#2)}}%
+\def\pgfluamathabs@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.abs(#1)}}%
+\def\pgfluamathround@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.round(#1)}}%
+\def\pgfluamathfloor@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.floor(#1)}}%
+\def\pgfluamathceil@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.ceil(#1)}}%
+\def\pgfluamathgcd@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.gcd(#1,#2)}}%
+\def\pgfluamathisprime@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.isprime(#1)}}%
+\def\pgfluamathmax@#1{%
+ \pgfluamathgetresult{%
+ math.max(pgfluamathfunctions.split_braces_to_explist("#1"))}}%
+\def\pgfluamathmin@#1{%
+ \pgfluamathgetresult{%
+ math.min(pgfluamathfunctions.split_braces_to_explist("#1"))}}%
+\def\pgfluamathsin@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.Sin(#1)}}%
+\def\pgfluamathcos@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.Cos(#1)}}%
+\def\pgfluamathtan@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.Tan(#1)}}%
+\def\pgfluamathmod@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.mod(#1,#2)}}%
+\def\pgfluamathMod@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.Mod(#1,#2)}}%
+\def\pgfluamathrad@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.rad(#1)}}%
+\def\pgfluamathdeg@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.deg(#1)}}%
+\def\pgfluamathatan@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.aTan(#1)}}%
+\def\pgfluamathatantwo@#1#2{%
+ \pgfluamathgetresult{pgfluamathfunctions.aTan2(#1,#2)}}%
+\def\pgfluamathasin@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.aSin(#1)}}%
+\def\pgfluamathacos@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.aCos(#1)}}%
+\def\pgfluamathcot@#1{%
+ \pgfluamathgetresult{1/pgfluamathfunctions.Tan(#1)}}%
+\def\pgfluamathsec@#1{%
+ \pgfluamathgetresult{1/pgfluamathfunctions.Cos(#1)}}%
+\def\pgfluamathcosec@#1{%
+ \pgfluamathgetresult{1/pgfluamathfunctions.Sin(#1)}}%
+\def\pgfluamathexp@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.exp(#1)}}%
+\def\pgfluamathln@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.log(#1)}}%
+\def\pgfluamathlogten@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.log10(#1)}}%
+\def\pgfluamathsqrt@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.sqrt(#1)}}%
+\def\pgfluamathrnd@{%
+ \pgfluamathgetresult{pgfluamathfunctions.rnd()}}%
+\def\pgfluamathrand@{%
+ \pgfluamathgetresult{pgfluamathfunctions.rand(-1,1)}}%
+\def\pgfluamathfactorial@#1{%
+ \pgfluamathgetresult{pgfluamathfunctions.factorial(#1)}}%
+\def\pgfluamathreciprocal#1{%
+ \pgfluamathgetresult{1/#1}}%
+% \pgfluamath@install@function\pgfmath@pi=\pgfluamathpi@%
+% \pgfluamath@install@function\pgfmathpi=\pgfluamathpi@%
+% \pgfluamath@install@function\pgfmathe@=\pgfluamathe@%
+% \pgfluamath@install@function\pgfmathe=\pgfluamathe@%
+% \pgfluamath@install@function\pgfmathlessthan@=\pgfluamathlessthan@%
+% \pgfluamath@install@function\pgfmathless@=\pgfluamathlessthan@%
+% \pgfluamath@install@function\pgfmathgreaterthan@=\pgfluamathgreaterthan@%
+% \pgfluamath@install@function\pgfmathgreater@=\pgfluamathgreaterthan@%
+% \pgfluamath@install@function\pgfmathpow@=\pgfluamathpow@
+% \pgfluamath@install@function\pgfmathrand@=\pgfluamathrand@
+% \pgfluamath@install@function\pgfmathrand=\pgfluamathrand@
+% \pgfluamath@install@function\pgfmathrnd@=\pgfluamathrnd@
+% \pgfluamath@install@function\pgfmathrnd=\pgfluamathrnd@
+% \pgfluamath@install@function\pgfmathtrue@=\pgfluamathtrue@
+% \pgfluamath@install@function\pgfmathfalse@=\pgfluamathfalse@
+% \pgfluamath@install@function\pgfmathnot@=\pgfluamathnot@
+% \pgfluamath@install@function\pgfmathhex@=\pgfluamathhex@
+% \pgfluamath@install@function\pgfmathHex@=\pgfluamathHex@
+% \pgfluamath@install@function\pgfmathoct@=\pgfluamathoct@
+% \pgfluamath@install@function\pgfmathbin@=\pgfluamathbin@
+% \pgfluamath@install@function\pgfmathand@=\pgfluamathand@
+% \pgfluamath@install@function\pgfmathor@=\pgfluamathor@
+% \pgfluamath@install@function\pgfmathfactorial@=\pgfluamathfactorial@
+% \pgfluamath@install@function\pgfmathveclen@=\pgfluamathveclen@
+% \pgfluamath@install@function\pgfmathcosh@=\pgfluamathcosh@
+% \pgfluamath@install@function\pgfmathsinh@=\pgfluamathsinh@
+% \pgfluamath@install@function\pgfmathtanh@=\pgfluamathtanh@
+
+\def\pgfluamathpointnormalised#1{%
+ \pgf@process{#1}%
+ \pgfutil@directlua{%
+ pgfluamathfunctions.pointnormalised(%
+ \pgf@sys@tonumber{\pgf@x},\pgf@sys@tonumber{\pgf@y})}%
+ \ignorespaces}%
+
+% Parser
+
+\newif\ifpgfluamathunitsdeclared
+
+% true if and only if LUA failed to evaluate the expression and the
+% expression was evaluated by means of the TeX parser as fallback.
+%
+% This happens
+% - for \chardef'ed boxes for which LUA lacks evaluation capabilities
+% - if the expression includes some function which is unavailable in
+% LUA (defined only in TeX)
+% - some special cases which simply haven't been added to the LUA
+% parser (yet).
+% At the time of this writing, this includes
+% -- arrays created via '{}' and indexed with '[]'
+% -- strings with "<str>"
+% -- 'scalar' function
+% -- hex/octal/binary input
+\newif\ifpgfluamathusedTeXfallback
+
+% Parses the math expression and defines \pgfmathresult and
+% \ifpgfmathunitsdeclared to contain the result.
+%
+% If \ifpgfluamathparseractive then the result is also assigned to
+% \pgfmathresult. Furthermore, if the expression cannot be evaluated (because LUA does not support
+% it), it will be evaluated by mean of TeX as fallback.
+%
+% @see \ifpgfluamathusedTeXfallback
+% @see \ifpgfluamathshowerrormessage
+\def\pgfluamathparse#1{%
+ \pgfluamathusedTeXfallbackfalse
+ \pgfutil@directlua{pgfluamathparser.texCallParser(
+ "\pgfutil@luaescapestring{#1}",
+ \pgfluamath@outputformat@choice,
+ \ifpgfluamathshowerrormessage true\else false\fi)%
+ }%
+ \ifx\pgfmathresult\pgfutil@empty
+ \ifpgfluamathenableTeXfallback
+ \pgfluamathusedTeXfallbacktrue
+ \pgfluamath@pgfmathparse{#1}%
+ \fi
+ \fi
+}%
+
+\catcode`\"=\pgfliblua@oldcatcodedoublequote
+\endinput