-- tkz_elements_matrices.lua -- date 2025/05/25 -- version 4.00c -- 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 = {} matrix.__index = 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) return o end setmetatable(matrix, { __call = function(cls, ...) return cls:new(...) 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 matrix.column = matrix.vector function matrix:row_vector(...) local m = {} local t = table.pack(...) m[1] = {} for j = 1, #t do m[1][j] = t[j] end return matrix:new(m) end function matrix:create(rows, cols) local mat = {} for i = 1, rows do mat[i] = {} for j = 1, cols do mat[i][j] = 0 end end return matrix:new(mat) 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) if i == nil and j == nil then return self.set -- retourne toute la matrice else return get_element_(self, i, j) end 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_square() return self.rows == self.cols 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 function matrix:swap_rows(row1, row2) self.set[row1], self.set[row2] = self.set[row2], self.set[row1] return self.set end function matrix:k_mul_row(row, k) for j = 1, #self.set[row] do self.set[row][j] = self.set[row][j] * k end return self.set end -- Ajoute à la ligne target_row la ligne source_row multipliée par k function matrix:add_k_mul_row(target_row, source_row, k) for j = 1, #self.set[target_row] do self.set[target_row][j] = self.set[target_row][j] + self.set[source_row][j] * k end return self.set end function matrix:gauss_jordan() local rows = self.rows local cols = self.cols if rows == cols then for i = 1, rows do local maxRow = i for k = i + 1, rows do if math.abs(self.set[k][i]) > math.abs(self.set[maxRow][i]) then maxRow = k end end if maxRow ~= i then self:swap_rows(i, maxRow) end local pivot = self.set[i][i] if math.abs(pivot) < tkz_epsilon then return nil, "Matrix is singular or nearly singular" end self:k_mul_row(i, 1 / pivot) for j = 1, rows do if i ~= j then local scalar = -self.set[j][i] self:add_k_mul_row(j, i, scalar) end end end return matrix:new(self.set) else return nil, "Matrix must be square" end end function matrix:gauss_jordan_rect() local rows = self.rows local cols = self.cols for i = 1, rows do local maxRow = i for k = i + 1, rows do if math.abs(self.set[k][i]) > math.abs(self.set[maxRow][i]) then maxRow = k end end if maxRow ~= i then local ok = self:swap_rows(i, maxRow) -- if not ok then return nil, err end end local pivot = self.set[i][i] if math.abs(pivot) < tkz_epsilon then return nil --, "Matrix is singular or nearly singular" end local ok = self:k_mul_row(i, 1 / pivot) -- if not ok then return nil, err end for j = 1, rows do if i ~= j then local scalar = -self.set[j][i] local ok = self:add_k_mul_row(j, i, scalar) -- if not ok then return nil, err end end end end return matrix:new(self.set) end function matrix:rank() local A = (self.type == "matrix" and self.set or self) local m, n = self.rows, self.cols local rank = 0 for col = 1, n do local pivot_row = rank + 1 -- Finding the pivot while pivot_row <= m and A[pivot_row][col] == 0 do pivot_row = pivot_row + 1 end if pivot_row <= m then -- Swap lines if necessary A[rank + 1], A[pivot_row] = A[pivot_row], A[rank + 1] -- Normaliser le pivot à 1 local pivot = A[rank + 1][col] for j = 1, n do A[rank + 1][j] = A[rank + 1][j] / pivot end -- Eliminate the column for i = 1, m do if i ~= rank + 1 then local factor = A[i][col] for j = 1, n do A[i][j] = A[i][j] - factor * A[rank + 1][j] end end end rank = rank + 1 end end return rank end function solve_linear_system(M, N) if not M or not N then return nil, "Error: Input matrices are nil." end local m, n = M.rows, M.cols if N.rows ~= m or N.cols ~= 1 then return nil, "Error: Incompatible dimensions between M and N." end local Aug = {} -- Construction of the augmented matrix for i = 1, m do Aug[i] = {} for j = 1, n do Aug[i][j] = M.set[i][j] end Aug[i][n + 1] = N.set[i][1] -- Add column vector end local MA = matrix:new(Aug) local rankM = M:rank() local rankMA = MA:rank() -- System compatibility check if rankM < rankMA then return nil, "Incompatible system (no solution)" elseif rankM < n then return nil, "Indeterminate system (infinite solutions)" end -- Résolution par Gauss-Jordan local sol = MA:gauss_jordan_rect() -- local S = {} for i = 1, m do S[i] = { sol.set[i][n + 1] } -- n + 1 car n=3, on veut la colonne 4 end return matrix:new(S) end return matrix