local N = {}

local D = "(math.pi/180)"

N.FUNCS = {

  sin = "math.sin", cos = "math.cos", tan = "math.tan",
  cot = "(function(t) return math.cos(t)/math.sin(t) end)",
  sec = "(function(t) return 1/math.cos(t) end)",
  csc = "(function(t) return 1/math.sin(t) end)",

  sind = "(function(t) return math.sin(t*" .. D .. ") end)",
  cosd = "(function(t) return math.cos(t*" .. D .. ") end)",
  tand = "(function(t) return math.tan(t*" .. D .. ") end)",

  arcsin = "math.asin", arccos = "math.acos", arctan = "math.atan",
  asin = "math.asin", acos = "math.acos", atan = "math.atan",
  arcsind = "(function(t) return math.asin(t)/" .. D .. " end)",
  arccosd = "(function(t) return math.acos(t)/" .. D .. " end)",
  arctand = "(function(t) return math.atan(t)/" .. D .. " end)",

  sinh = "math.sinh", cosh = "math.cosh", tanh = "math.tanh",
  coth = "(function(t) return math.cosh(t)/math.sinh(t) end)",
  sech = "(function(t) return 1/math.cosh(t) end)",
  csch = "(function(t) return 1/math.sinh(t) end)",

  arcsinh = "(function(t) return math.log(t+math.sqrt(t*t+1)) end)",
  arccosh = "(function(t) return math.log(t+math.sqrt(t*t-1)) end)",
  arctanh = "(function(t) return 0.5*math.log((1+t)/(1-t)) end)",
  asinh = "(function(t) return math.log(t+math.sqrt(t*t+1)) end)",
  acosh = "(function(t) return math.log(t+math.sqrt(t*t-1)) end)",
  atanh = "(function(t) return 0.5*math.log((1+t)/(1-t)) end)",

  exp = "math.exp",
  ln  = "math.log",
  log = "(function(v) return math.log(v)/math.log(10) end)",
  log2 = "(function(v) return math.log(v)/math.log(2) end)",
  logb = "(function(v, b) return math.log(v)/math.log(b) end)",

  sqrt = "math.sqrt", cbrt = "(function(v) return (v<0 and -1 or 1)*math.abs(v)^(1/3) end)",
  abs = "math.abs", sign = "(function(v) return v>0 and 1 or v<0 and -1 or 0 end)",
  floor = "math.floor", ceil = "math.ceil",
  round = "__round",
  min = "math.min", max = "math.max",
}

N.PREAMBLE = table.concat({
  "local __mabs, __mexp, __mlog = math.abs, math.exp, math.log",
  "if not math.sinh then",
  "  math.sinh = function(x) return (__mexp(x)-__mexp(-x))/2 end",
  "  math.cosh = function(x) return (__mexp(x)+__mexp(-x))/2 end",
  "  math.tanh = function(x) local a,b=__mexp(x),__mexp(-x) return (a-b)/(a+b) end",
  "end",

  "local __roundmt",
  "local function __round(x, d)",
  "  d = d or 0",
  "  local m = 10 ^ d",
  "  local r = (x >= 0) and math.floor(x*m + 0.5)/m or -math.floor(-x*m + 0.5)/m",
  "  return setmetatable({v = r, d = d}, __roundmt)",
  "end",

  "local function __u(a) return type(a)=='table' and a.v or a end",
  "__roundmt = {",
  "  __add=function(a,b) return __u(a)+__u(b) end,",
  "  __sub=function(a,b) return __u(a)-__u(b) end,",
  "  __mul=function(a,b) return __u(a)*__u(b) end,",
  "  __div=function(a,b) return __u(a)/__u(b) end,",
  "  __pow=function(a,b) return __u(a)^__u(b) end,",
  "  __unm=function(a) return -__u(a) end,",
  "  __eq=function(a,b) return __u(a)==__u(b) end,",
  "  __lt=function(a,b) return __u(a)<__u(b) end,",
  "  __le=function(a,b) return __u(a)<=__u(b) end,",
  "  __tostring=function(a) return tostring(a.v) end,",
  "}",
  "local pi = math.pi; local e = math.exp(1)",
}, "\n") .. "\n"

N.CONSTS = { pi = "math.pi", e = "math.exp(1)" }

function N.inject_locals()
  local out = {}

  local names = {}
  for k in pairs(N.FUNCS) do names[#names+1] = k end
  table.sort(names)
  for _, k in ipairs(names) do
    out[#out+1] = "local " .. k .. " = " .. N.FUNCS[k]
  end
  return N.PREAMBLE .. table.concat(out, "\n") .. "\n"
end

N.doc_precision = -1

function N.set_precision(p)
  N.doc_precision = (type(p) == "number") and p or -1
end

function N.display(v, sep, fallback_dec)
  sep = sep or "."
  if type(v) ~= "number" then
    if v == nil then return "" end
    return tostring(v)
  end
  local p = N.doc_precision
  if not p or p < 0 then p = fallback_dec end
  local s
  if p and p >= 0 then
    s = string.format("%." .. p .. "f", v)
    if s:find("%.") then s = s:gsub("0+$", ""):gsub("%.$", "") end
  else
    s = tostring(v)
  end
  return (s:gsub("%.", sep, 1))
end

return N
