-- tkz_elements_functions_maths.lua
-- date 2025/06/10
-- version 4.10c
-- Copyright 2025  Alain Matthes
-- This work may be distributed and/or modified under the
-- conditions of the LaTeX Project Public License, either version 1.3
-- of this license or (at your option) any later version.
-- The latest version of this license is in
-- http://www.latex-project.org/lppl.txt
-- and version 1.3 or later is part of all distributions of LaTeX
-- version 2005/12/01 or later.
-- This work has the LPPL maintenance status “maintained”.
-- The Current Maintainer of this work is Alain Matthes.

---------------------------------------------------------------------------
---- numbers --------------------------------------------------------------
---------------------------------------------------------------------------

function is_integer_(x)
  return type(x) == "number"
     and x == x               -- exclude NaN
     and x < math.huge        -- exclude +inf
     and -x < math.huge       -- exclude -inf
     and is_zero_(x - tkz_round_(x))
end

function near_integer_(x)
	return is_zero_(x % 1)
end

function residue_(x)
  return x % 1
end

function is_zero_(x)
	return math.abs(x) < tkz.epsilon
end

function set_zero_(x)
  return is_zero_(x) and 0 or x
end

function checknumber_(x)
  if type(x) == "number" then
    return string.format("%.12f", x)
  elseif type(x) == "string" and tonumber(x) then
    return string.format("%.12f", tonumber(x))
  else
    return x -- table ou invalide
  end
end

function tkz_round_(num, idp)
  idp = idp or 0
  local mult = 10 ^ idp
	return math.floor(num * mult + 0.5) / mult
end


---------------------------------------------------------------------------
---------------------------------------------------------------------------
---------------------------------------------------------------------------

function dot_product_(a, b, c)
	return (b - a) .. (c - a)
end


function orient2d_(x1, y1, x2, y2, x3, y3)
    return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)
end

function det3pt_(a, b, c)
  local x1, y1, x2, y2, x3, y3 = a.re, a.im, b.re, b.im, c.re, c.im
    return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)
end
--orient2d or det3pt determinant de 3 pts

function islinear_(z1, z2, z3)
	return math.abs((z2 - z1) ^ (z3 - z1)) < tkz.epsilon
end


function isortho_(z1, z2, z3)
	return math.abs((z2 - z1) .. (z3 - z1)) < tkz.epsilon
end

function parabola_(xa, ya, xb, yb, xc, yc) -- added
	local D = (xa - xb) * (xa - xc) * (xb - xc)
	local A = (xc * (yb - ya) + xb * (ya - yc) + xa * (yc - yb)) / D
	local B = (xc * xc * (ya - yb) + xb * xb * (yc - ya) + xa * xa * (yb - yc)) / D
	local C = (xb * xc * (xb - xc) * ya + xc * xa * (xc - xa) * yb + xa * xb * (xa - xb) * yc) / D
	return A, B, C
end


---------------------------------------------------------------------------
---- angles -------------------------------------
---------------------------------------------------------------------------

function get_angle_(a, b, c)
	if (b == a) or (c == a) then
		tex.error("Points confused in get_angle_")
	end
	return point.arg((c - a) / (b - a))
end

function get_angle_normalize_(a, b, c)
 return angle_normalize_(get_angle_(a, b, c))
end

function angle_normalize_(a)
	while a < 0 do
		a = a + 2 * math.pi
	end
	while a >= 2 * math.pi do
		a = a - 2 * math.pi
	end
	return a
end

function tkz_angle_between_vectors_(a, b, c, d)
	-- Vector calculation
	local zab = b - a
	local zcd = d - c

	-- Angle between vectors using ratio argument
	local theta = math.atan(-zab.im * zcd.re + zab.re * zcd.im, zab.re * zcd.re + zab.im * zcd.im)

	return theta -- Angle in radians
end
---------------------------------------------------------------------------
-------------------------- end angles --------------------------
---------------------------------------------------------------------------




---------------------------------------------------------------------------
----------------- display  ---------------------------------------
-----------------------------------------------------
-- Optionnel : si utils.sign n’est pas fiable, utilise ceci
local function sign(x)
  if x > 0 then return "+" end
  if x < 0 then return "-" end
  return ""
end

function tkz_display_(z)
  local function display_real(r)
    if r == nil then return "" end
    local fmt
    if near_integer_(r) then
      r = math.round(r)
      fmt = "%.0f"
    else
      fmt = string.format("%%.%df", tkz_dc)
    end
    return string.format(fmt, r)
  end

  local function display_imag(r, force_sign)
    local sgn = sign(r)
    if not force_sign and sgn == "+" then
      sgn = "" -- supprime le + quand il n’est pas nécessaire
    end
    r = math.abs(r)
    local part
    if math.abs(r - 1) < tkz.epsilon then
      part = "" -- afficher juste "i" ou "-i"
    elseif near_integer_(r) then
      part = tostring(math.round(r))
    else
      part = display_real(r)
    end
    return sgn, part
  end

  if type(z) == "number" then
    return display_real(z)
  end

  local re, im = z.re, z.im
  local is_re_zero = is_zero_(re)
  local is_im_zero = is_zero_(im)

  if is_re_zero and is_im_zero then
    return "0"
  end

  if is_im_zero then
    return display_real(re)
  end

  if is_re_zero then
    local sgn, part = display_imag(im, false)
    return sgn .. part .. "i"
  end

  -- Cas général : partie réelle et partie imaginaire
  local str_re = display_real(re)
  local sgn_im, part_im = display_imag(im, true)
  return str_re .. sgn_im .. part_im .. "i"
end

---   end display  ---------------------------------------
---------------------------------------------------