-- tkz_elements_matrices.lua
-- date 2025/02/26
-- version 3.32
-- 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.

-- ----------------------------------------------------------------------------
matrix={}
function matrix: new (value)
    local type          = 'matrix'
    local rows          = #value
    local cols          = #value[1]
    local set           = value
    local det           = determinant(value)
    local o =  {set    = set,
                rows   = rows,
                cols   = cols,
                det    = det,
                type   = type }
    setmetatable(o, self)
    self.__index = self
    return o
end

function matrix.__mul(m1,m2)
  if getmetatable(m1) ~= matrix then return  k_mul_matrix(m1, m2) end  
  if getmetatable(m2) ~= matrix then return  k_mul_matrix(m2, m1) end
  return mul_matrix(m1,m2)
end

function matrix.__add(m1,m2)
  return add_matrix(m1,m2)
end

function matrix.__sub(m1,m2)
  return add_matrix(m1,k_mul_matrix(-1, m2))
end

function matrix.__pow(m, num)
   -- Handle transpose (when num is 'T')
   if num == 'T' then
      return transposeMatrix(m)
   end

   -- Handle exponentiation by 0 (returns the identity matrix)
   if num == 0 then
      return matrix:new(#m, "I")  -- Identity matrix
   end

   -- Handle negative exponents (invert the matrix)
   if num < 0 then
      local inv_matrix, err = inv_matrix(m)
      if not inv_matrix then 
         return nil, err  -- Return nil and the error if matrix is non-invertible
      end
      num = -num  -- Make exponent positive for easier handling
      m = inv_matrix  -- Now use the inverted matrix
   end

   -- Now handle the positive exponentiation
   local result = m
   for i = 2, num do
      result = mul_matrix(result, m)  -- Repeated multiplication
   end

   return result
end


function matrix.__tostring( A )
  local mt = (A.type=='matrix' and A.set or A)
	local k = {}
	for i = 1,#mt do
		local n = {}
		for j = 1,#mt[1] do
			n[j] = display(mt[i][j])
		end
		k[i] = table.concat(n, " ")
	end
	return table.concat(k)
end

function matrix.__eq( A , B )
  local mt1 = (A.type=='matrix' and A.set or A)
  local mt2 = (B.type=='matrix' and B.set or B)
	if A.type ~= B.type then
		return false
	end

	if #mt1 ~= #mt2 or #mt1[1] ~= #mt2[1] then
		return false
	end

	for i = 1,#mt1 do
		for j = 1,#mt1[1] do
			if mt1[i][j] ~= mt2[i][j] then
				return false
			end
		end
	end
	return true
end

function matrix : square(n,...)
   local m = {}
   local t = table.pack(...)
   if n*n == #t then
      for i = 1, n do
           m[i] = {}
          for j = 1, n do
                  m[i][j] = t[n*(i-1)+j]
          end
      end
      return matrix : new (m)
   else
      return nil
   end
end

function matrix: vector (...)
  local m = {}
  local t = table.pack(...)
  for i = 1, #t do
       m[i] = {}
      m[i][1] = t[i]
  end
  return matrix : new (m)
end

function matrix : homogenization ()
  return homogenization_ (self)
end

function matrix : htm_apply (...)
  local obj,nb,t
  local tp = table.pack(...)
  obj = tp[1]
  nb = tp.n
  if nb == 1 then 
     if obj.type == "point" then
        return htm_apply_ ( self,obj )
     elseif    obj.type == "line" then 
           return  htm_apply_L_ (self,obj)
     elseif    obj.type == "triangle" then
          return   htm_apply_T_ (self,obj)
     elseif    obj.type == "circle" then
        return     htm_apply_C (self,obj)
      elseif   obj.type == "square"
            or obj.type == "rectangle"
            or obj.type == "quadrilateral"
            or obj.type == "parallelogram" then
        return     htm_apply_Q (self,obj)
      end
  else
    t = {}
     for i=1,tp.n do
    table.insert(t , htm_apply_ ( self , tp[i])) 
       end
    return table.unpack ( t )      
  end
end

function matrix: k_mul (n)
  return k_mul_matrix(n, self)
end

function matrix : get (i,j)
  return get_element_( self,i,j )
end

function matrix: inverse ()
  return inv_matrix (self)
end

function matrix: adjugate ()
  return adjugate_ (self)
end

function matrix: transpose ()
  return transposeMatrix (self)
end

function matrix: is_diagonal ()
  return isDiagonal_ (self)
end

function matrix: is_orthogonal ()
  return isOrthogonal_ (self)
end

function matrix: diagonalize () -- return two matrices D and P
  return diagonalize_ (self)
end

function matrix: print (style,fmt)
  local style = (style or 'bmatrix')
  local fmt  = (fmt or 0)
  return print_matrix (self,style,fmt)
end

function matrix: identity (n)
  return id_matrix (n)
end

-------------------------
-- homogeneous transformation matrix
function matrix : htm (phi,a,b,sx,sy)
  local tx = (a or 0)
  local ty = (b or 0)
  local sx = (sx or 1)
  local sy = (sy or 1)
  local phi = (phi or 0)
  return matrix : square (3,sx*math.cos(phi),-math.sin(phi),tx,math.sin(phi),sy*math.cos(phi),ty,0,0,1)
end
-------------------------

function matrix: is_orthogonal ()
  return isOrthogonal_ (self)
end

return matrix

