@@ -7,8 +7,9 @@ from flint.types.fmpz cimport any_as_fmpz
77from flint.pyflint cimport global_random_state
88from flint.types.fmpq cimport any_as_fmpq
99cimport cython
10+ cimport libc.stdlib
1011
11- from flint.flintlib.functions.fmpz cimport fmpz_set, fmpz_init, fmpz_clear
12+ from flint.flintlib.functions.fmpz cimport fmpz_set, fmpz_init, fmpz_clear, fmpz_set_si, fmpz_mul
1213from flint.flintlib.functions.fmpz cimport fmpz_is_zero, fmpz_is_pm1
1314from flint.flintlib.types.fmpz cimport (
1415 fmpz_mat_struct,
@@ -563,6 +564,130 @@ cdef class fmpz_mat(flint_mat):
563564 raise ZeroDivisionError (" singular matrix in solve()" )
564565 return u
565566
567+ def _fflu (self ):
568+ """
569+ Fraction-free LU decomposition of *self*.
570+
571+ >>> A = fmpz_mat([[1, 2], [3, 4]])
572+ >>> LU, d, perm, rank = A._fflu()
573+ >>> LU
574+ [1, 2]
575+ [3, -2]
576+ >>> d
577+ -2
578+ >>> perm
579+ [0, 1]
580+ >>> rank
581+ 2
582+
583+ The matrix *LU* is the LU contains both the lower and upper parts of
584+ the decomposition. The integer *d* is the divisor and is up to a sign
585+ the determinant when *self* is square. The list *perm* is the
586+ permutation of the rows of *self*.
587+
588+ This is the raw output from the underlying FLINT function fmpz_mat_fflu.
589+ The method :meth:`fflu` provides a more understandable representation
590+ of the decomposition.
591+
592+ """
593+ cdef fmpz d
594+ cdef slong* perm
595+ cdef slong r, c, rank
596+ cdef fmpz_mat LU
597+ r = fmpz_mat_nrows(self .val)
598+ c = fmpz_mat_ncols(self .val)
599+ perm = < slong* > libc.stdlib.malloc(r * sizeof(slong))
600+ if perm is NULL :
601+ raise MemoryError (" malloc failed" )
602+ try :
603+ for i in range (r):
604+ perm[i] = i
605+ LU = fmpz_mat.__new__ (fmpz_mat)
606+ fmpz_mat_init((< fmpz_mat> LU).val, r, c)
607+ print (fmpz_mat_nrows(LU.val), fmpz_mat_ncols(LU.val))
608+ d = fmpz.__new__ (fmpz)
609+ rank = fmpz_mat_fflu(LU.val, d.val, perm, self .val, 0 )
610+ print (fmpz_mat_nrows(LU.val), fmpz_mat_ncols(LU.val))
611+ perm_int = []
612+ for i in range (r):
613+ perm_int.append(perm[i])
614+ finally :
615+ libc.stdlib.free(perm)
616+
617+ return LU, d, perm_int, rank
618+
619+ def fflu (self ):
620+ """
621+ Fraction-free LU decomposition of *self*.
622+
623+ Returns a tuple (*P*, *L*, *D*, *U*) representing the the fraction-free
624+ LU decomposition of a matrix *A* as
625+
626+ P*A = L*inv(D)*U
627+
628+ where *P* is a permutation matrix, *L* is lower triangular, *D* is
629+ diagonal and *U* is upper triangular.
630+
631+ >>> A = fmpz_mat([[1, 2], [3, 4]])
632+ >>> P, L, D, U = A.fflu()
633+ >>> P
634+ [1, 0]
635+ [0, 1]
636+ >>> L
637+ [1, 0]
638+ [3, -2]
639+ >>> D
640+ [1, 0]
641+ [0, -2]
642+ >>> U
643+ [1, 2]
644+ [0, -2]
645+ >>> P*A == L*D.inv()*U
646+ True
647+
648+ This method works for matrices of any shape and rank.
649+
650+ """
651+ cdef slong r, c
652+ cdef slong i, j, k, l
653+ cdef fmpz di
654+ cdef fmpz_mat P, L, U, LU, D
655+ r = fmpz_mat_nrows(self .val)
656+ c = fmpz_mat_ncols(self .val)
657+
658+ U, d, perm, rank = self ._fflu()
659+
660+ P = fmpz_mat(r, r)
661+ for i, pi in enumerate (perm):
662+ fmpz_set_si(fmpz_mat_entry(P.val, i, pi), 1 )
663+
664+ L = fmpz_mat(r, r)
665+
666+ i = j = k = 0
667+ while i < r and j < c:
668+ if not fmpz_is_zero(fmpz_mat_entry(U.val, i, j)):
669+ fmpz_set(fmpz_mat_entry(L.val, i, k), fmpz_mat_entry(U.val, i, j))
670+ for l in range (i + 1 , r):
671+ fmpz_set(fmpz_mat_entry(L.val, l, k), fmpz_mat_entry(U.val, l, j))
672+ fmpz_set_si(fmpz_mat_entry(U.val, l, j), 0 )
673+ i += 1
674+ k += 1
675+ j += 1
676+
677+ for k in range (k, r):
678+ fmpz_set_si(fmpz_mat_entry(L.val, k, k), 1 )
679+
680+ D = fmpz_mat(r, r)
681+
682+ if r >= 1 :
683+ fmpz_set(fmpz_mat_entry(D.val, 0 , 0 ), fmpz_mat_entry(L.val, 0 , 0 ))
684+ di = fmpz(1 )
685+ for i in range (1 , r):
686+ fmpz_mul(di.val, fmpz_mat_entry(L.val, i- 1 , i- 1 ), fmpz_mat_entry(L.val, i, i))
687+ fmpz_set(fmpz_mat_entry(D.val, i, i), di.val)
688+
689+ return P, L, D, U
690+
566691 def rref (self , inplace = False ):
567692 """
568693 Computes the reduced row echelon form (rref) of *self*,
0 commit comments