diff options
author | Norbert Preining <norbert@preining.info> | 2023-01-16 03:03:27 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2023-01-16 03:03:27 +0000 |
commit | 6f9e1680085e7bb4d258f6f8116369d122e196e1 (patch) | |
tree | 9ac0ecb239240d1d672b188f29c1479de215074b /graphics/pgf/base/tex/generic/libraries/luamath | |
parent | b8345f39630408bb198e7636381ce4240154ca9b (diff) |
CTAN sync 202301160303
Diffstat (limited to 'graphics/pgf/base/tex/generic/libraries/luamath')
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 |