-- tkz_elements_point.lua
-- date 2023/11/10
-- version 1.20b
-- Copyright 2023  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.

-- point.lua
require 'tkz_elements_class'

point = class(function(p,re,im)
       if type(re) == 'number' then 
         p.re = re
         p.im = im
       else
         p.re = re.re
         p.im = re.im
       end
       p.type = 'point'
       p.argument = math.atan(p.im, p.re)
       p.modulus = math.sqrt(p.re*p.re  + p.im*p.im)
end)

local sqrt = math.sqrt
local cos  = math.cos
local sin  = math.sin
local exp  = math.exp
local atan = math.atan
local min  = math.min
local max = math.max
local abs = math.abs

local function topoint (z1)
   if (type(z1) == "number") then return point(z1,0) else return z1 end
end

local function check(z1,z2)
  if     type(z1) == 'number' then return point(z1,0),z2
  elseif type(z2) == 'number' then return z1,point(z2,0) 
  else return z1,z2
  end
end
-- -------------------------------------------------------------------
-- metamethods
-- -------------------------------------------------------------------
-- redefine arithmetic operators!
function point.__add(z1,z2)
  local c1,c2 = check(z1,z2)
  return point(c1.re + c2.re, c1.im + c2.im)
end

function point.__sub(z1,z2)
  local c1,c2 = check(z1,z2)
  return point(c1.re - c2.re, c1.im - c2.im)
end

function point.__unm(z)
    local z = topoint(z)
  return point(-z.re, -z.im)
end

function point.__mul(z1,z2)
  local c1,c2 = check(z1,z2)
  return point(c1.re*c2.re - c1.im*c2.im, c1.im*c2.re + c1.re*c2.im)
end

-- dot product is '..'  (a+ib) (c-id) = ac+bd + i(bc-ad) 
function point.__concat(z1,z2)
    local z
    z = z1 * point.conj(z2)
  return z.re
end

-- determinant  is '^' (a+ib)   (a-ib) (c+id) = ac+bd + i(ad - bc)
function point.__pow(z1,z2)
    local z
    z = point.conj(z1) * z2
   return z.im
end

function point.__div(x,y)
   local xx = topoint(x); local yy = topoint(y)
   return point(
      (xx.re * yy.re + xx.im * yy.im) / (yy.re * yy.re + yy.im * yy.im),
      (xx.im * yy.re - xx.re * yy.im) / (yy.re * yy.re + yy.im * yy.im)
   )
end

function point.__tostring(z)
   if (z.re == 0) then
      if (z.im == 0) then
         return "0"
      else
         if  (z.im ==1) then
            return ""  .. "i"
         else  
            if  (z.im ==-1) then
               return ""  .. "-i" 
            else          
         return "" .. z.im .. "i"
      end
      end
      end
   else
      if (z.im > 0) then 
        if  (z.im ==1) then
           return "" .. z.re .. "+"  .. "i" 
        else     
         return "" .. z.re .. "+" .. z.im .. "i" 
     end
      elseif (z.im < 0) then
         if  (z.im ==-1) then
            return "" .. z.re .. "-"  .. "i" 
         else     
         return "" .. z.re .. z.im .. "i" 
      end 
      else
         return "" .. z.re
      
      end
   end
end

function point.__tonumber(z)
   if (z.im == 0) then
      return z.re
   else
      return nil
   end
end

function point.__eq(z1,z2)
  return z1.re==z2.re and z1.im==z2.im
end
-- -------------------------------------------------------------------   
local function pyth(a, b)
	if a == 0 and b == 0 then return 0 end
	a, b = abs(a), abs(b)
	a, b = max(a,b), min(a,b)
	return a * sqrt(1 + (b / a)^2)
end

function point.conj(z)
  return point(z.re,-z.im)
end

function point.mod(z)
    local function sqr(x) return x*x end
  return pyth (z.re,z.im)
end

function point.abs (z) 
  local function sqr(x) return x*x end
  return sqrt(sqr(z.re)  + sqr(z.im))
end

function point.norm (z)
    local function sqr(x) return x*x end
   return (sqr(z.re)  + sqr(z.im))
end

function point.arg (z)
   return math.atan(z.im, z.re)
end

function point.get(z)
   return z.re, z.im
end

function point.sqrt(z)
  local y = sqrt((point.mod(z)-z.re)/2)
  local x = z.im/(2*y)
  return point(x,y)
end