from sympy import ZZ, QQ, ZZ_I, EX, Matrix, eye, zeros, symbols
from sympy.polys.matrices import DM, DomainMatrix
from sympy.polys.matrices.dense import ddm_irref_den, ddm_irref
from sympy.polys.matrices.ddm import DDM
from sympy.polys.matrices.sdm import SDM, sdm_irref, sdm_rref_den

import pytest


#
# The dense and sparse implementations of rref_den are ddm_irref_den and
# sdm_irref_den. These can give results that differ by some factor and also
# give different results if the order of the rows is changed. The tests below
# show all results on lowest terms as should be returned by cancel_denom.
#
# The EX domain is also a case where the dense and sparse implementations
# can give results in different forms: the results should be equivalent but
# are not canonical because EX does not have a canonical form.
#


a, b, c, d = symbols('a, b, c, d')


qq_large_1 = DM([
[  (1,2),  (1,3),  (1,5),  (1,7), (1,11), (1,13), (1,17), (1,19), (1,23), (1,29), (1,31)],
[ (1,37), (1,41), (1,43), (1,47), (1,53), (1,59), (1,61), (1,67), (1,71), (1,73), (1,79)],
[ (1,83), (1,89), (1,97),(1,101),(1,103),(1,107),(1,109),(1,113),(1,127),(1,131),(1,137)],
[(1,139),(1,149),(1,151),(1,157),(1,163),(1,167),(1,173),(1,179),(1,181),(1,191),(1,193)],
[(1,197),(1,199),(1,211),(1,223),(1,227),(1,229),(1,233),(1,239),(1,241),(1,251),(1,257)],
[(1,263),(1,269),(1,271),(1,277),(1,281),(1,283),(1,293),(1,307),(1,311),(1,313),(1,317)],
[(1,331),(1,337),(1,347),(1,349),(1,353),(1,359),(1,367),(1,373),(1,379),(1,383),(1,389)],
[(1,397),(1,401),(1,409),(1,419),(1,421),(1,431),(1,433),(1,439),(1,443),(1,449),(1,457)],
[(1,461),(1,463),(1,467),(1,479),(1,487),(1,491),(1,499),(1,503),(1,509),(1,521),(1,523)],
[(1,541),(1,547),(1,557),(1,563),(1,569),(1,571),(1,577),(1,587),(1,593),(1,599),(1,601)],
[(1,607),(1,613),(1,617),(1,619),(1,631),(1,641),(1,643),(1,647),(1,653),(1,659),(1,661)]],
        QQ)

qq_large_2 = qq_large_1 + 10**100 * DomainMatrix.eye(11, QQ)


RREF_EXAMPLES = [
    (
        'zz_1',
         DM([[1, 2, 3]], ZZ),
         DM([[1, 2, 3]], ZZ),
         ZZ(1),
    ),

    (
        'zz_2',
         DomainMatrix([], (0, 0), ZZ),
         DomainMatrix([], (0, 0), ZZ),
         ZZ(1),
    ),

    (
        'zz_3',
        DM([[1, 2],
            [3, 4]], ZZ),
        DM([[1, 0],
            [0, 1]], ZZ),
        ZZ(1),
    ),

    (
        'zz_4',
        DM([[1, 0],
            [3, 4]], ZZ),
        DM([[1, 0],
            [0, 1]], ZZ),
        ZZ(1),
    ),

    (
        'zz_5',
        DM([[0, 2],
            [3, 4]], ZZ),
        DM([[1, 0],
            [0, 1]], ZZ),
        ZZ(1),
    ),

    (
        'zz_6',
        DM([[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]], ZZ),
        DM([[1, 0, -1],
            [0, 1,  2],
            [0, 0,  0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_7',
        DM([[0, 0, 0],
            [0, 0, 0],
            [1, 0, 0]], ZZ),
        DM([[1, 0, 0],
            [0, 0, 0],
            [0, 0, 0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_8',
        DM([[0, 0, 0],
            [0, 0, 0],
            [0, 0, 0]], ZZ),
        DM([[0, 0, 0],
            [0, 0, 0],
            [0, 0, 0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_9',
        DM([[1, 1, 0],
            [0, 0, 2],
            [0, 0, 0]], ZZ),
        DM([[1, 1, 0],
            [0, 0, 1],
            [0, 0, 0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_10',
        DM([[2, 2, 0],
            [0, 0, 2],
            [0, 0, 0]], ZZ),
        DM([[1, 1, 0],
            [0, 0, 1],
            [0, 0, 0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_11',
        DM([[2, 2, 0],
            [0, 2, 2],
            [0, 0, 2]], ZZ),
        DM([[1, 0, 0],
            [0, 1, 0],
            [0, 0, 1]], ZZ),
        ZZ(1),
    ),

    (
        'zz_12',
        DM([[ 1,  2,  3],
            [ 4,  5,  6],
            [ 7,  8,  9],
            [10, 11, 12]], ZZ),
        DM([[1,  0, -1],
            [0,  1,  2],
            [0,  0,  0],
            [0,  0,  0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_13',
        DM([[ 1,  2,  3],
            [ 4,  5,  6],
            [ 7,  8,  9],
            [10, 11, 13]], ZZ),
        DM([[ 1,  0,  0],
            [ 0,  1,  0],
            [ 0,  0,  1],
            [ 0,  0,  0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_14',
        DM([[1, 2,  4, 3],
            [4, 5, 10, 6],
            [7, 8, 16, 9]], ZZ),
        DM([[1, 0, 0, -1],
            [0, 1, 2,  2],
            [0, 0, 0,  0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_15',
        DM([[1, 2,  4, 3],
            [4, 5, 10, 6],
            [7, 8, 17, 9]], ZZ),
        DM([[1, 0, 0, -1],
            [0, 1, 0,  2],
            [0, 0, 1,  0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_16',
        DM([[1, 2, 0, 1],
            [1, 1, 9, 0]], ZZ),
        DM([[1, 0, 18, -1],
            [0, 1, -9,  1]], ZZ),
        ZZ(1),
    ),

    (
        'zz_17',
        DM([[1, 1, 1],
            [1, 2, 2]], ZZ),
        DM([[1, 0, 0],
            [0, 1, 1]], ZZ),
        ZZ(1),
    ),

    (
        # Here the sparse implementation and dense implementation give very
        # different denominators: 4061232 and -1765176.
        'zz_18',
        DM([[94, 24,  0, 27, 0],
            [79,  0,  0,  0, 0],
            [85, 16, 71, 81, 0],
            [ 0,  0, 72, 77, 0],
            [21,  0, 34,  0, 0]], ZZ),
        DM([[ 1,  0,  0,  0, 0],
            [ 0,  1,  0,  0, 0],
            [ 0,  0,  1,  0, 0],
            [ 0,  0,  0,  1, 0],
            [ 0,  0,  0,  0, 0]], ZZ),
        ZZ(1),
    ),

    (
        # Let's have a denominator that cannot be cancelled.
        'zz_19',
        DM([[1, 2, 4],
            [4, 5, 6]], ZZ),
        DM([[3, 0, -8],
            [0, 3, 10]], ZZ),
        ZZ(3),
    ),

    (
        'zz_20',
        DM([[0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 4]], ZZ),
        DM([[0, 0, 0, 0, 1],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_21',
        DM([[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
            [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
            [0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 1]], ZZ),
        DM([[1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
            [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
            [0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
            [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_22',
        DM([[1, 1, 1, 0, 1],
            [1, 1, 0, 1, 0],
            [1, 0, 1, 0, 1],
            [1, 1, 0, 1, 0],
            [1, 0, 0, 0, 0]], ZZ),
        DM([[1, 0, 0, 0, 0],
            [0, 1, 0, 0, 0],
            [0, 0, 1, 0, 1],
            [0, 0, 0, 1, 0],
            [0, 0, 0, 0, 0]], ZZ),
        ZZ(1),
    ),

    (
        'zz_large_1',
        DM([
[ 0,  0,  0, 81,  0,  0, 75,  0,  0,  0,  0,  0,  0, 27,  0,  0,  0,  0,  0,  0],
[ 0,  0,  0,  0,  0, 86,  0, 92, 79, 54,  0,  7,  0,  0,  0,  0, 79,  0,  0,  0],
[89, 54, 81,  0,  0, 20,  0,  0,  0,  0,  0,  0, 51,  0, 94,  0,  0, 77,  0,  0],
[ 0,  0,  0, 96,  0,  0,  0,  0,  0,  0,  0,  0, 48, 29,  0,  0,  5,  0, 32,  0],
[ 0, 70,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,  0,  0,  0, 11],
[ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 37,  0, 43,  0,  0],
[ 0,  0,  0,  0,  0, 38, 91,  0,  0,  0,  0, 38,  0,  0,  0,  0,  0, 26,  0,  0],
[69,  0,  0,  0,  0,  0, 94,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 55],
[ 0, 13, 18, 49, 49, 88,  0,  0, 35, 54,  0,  0, 51,  0,  0,  0,  0,  0,  0, 87],
[ 0,  0,  0,  0, 31,  0, 40,  0,  0,  0,  0,  0,  0, 50,  0,  0,  0,  0, 88,  0],
[ 0,  0,  0,  0,  0,  0,  0,  0, 98,  0,  0,  0, 15, 53,  0, 92,  0,  0,  0,  0],
[ 0,  0,  0, 95,  0,  0,  0, 36,  0,  0,  0,  0,  0, 72,  0,  0,  0,  0, 73, 19],
[ 0, 65, 14, 96,  0,  0,  0,  0,  0,  0,  0,  0,  0, 90,  0,  0,  0, 34,  0,  0],
[ 0,  0,  0, 16, 39, 44,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 51,  0,  0],
[ 0, 17,  0,  0,  0, 99, 84, 13, 50, 84,  0,  0,  0,  0, 95,  0, 43, 33, 20,  0],
[79,  0, 17, 52, 99, 12, 69,  0, 98,  0, 68,  0,  0,  0,  0,  0,  0,  0,  0,  0],
[ 0,  0,  0, 82,  0, 44,  0,  0,  0, 97,  0,  0,  0,  0,  0, 10,  0,  0, 31,  0],
[ 0,  0, 21,  0, 67,  0,  0,  0,  0,  0,  4,  0, 50,  0,  0,  0, 33,  0,  0,  0],
[ 0,  0,  0,  0,  9, 42,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  8],
[ 0, 77,  0,  0,  0,  0,  0,  0,  0,  0, 34, 93,  0,  0,  0,  0, 47,  0,  0,  0]],
           ZZ),
        DM([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]], ZZ),
        ZZ(1),
    ),

    (
        'zz_large_2',
        DM([
[ 0,  0,  0,  0, 50,  0,  6, 81,  0,  1, 86,  0,  0, 98, 82, 94,  4,  0,  0, 29],
[ 0, 44, 43,  0, 62,  0,  0,  0, 60,  0,  0,  0,  0, 71,  9,  0, 57, 41,  0, 93],
[ 0,  0, 28,  0, 74, 89, 42,  0, 28,  0,  6,  0,  0,  0, 44,  0,  0,  0, 77, 19],
[ 0, 21, 82,  0, 30, 88,  0, 89, 68,  0,  0,  0, 79, 41,  0,  0, 99,  0,  0,  0],
[31,  0,  0,  0, 19, 64,  0,  0, 79,  0,  5,  0, 72, 10, 60, 32, 64, 59,  0, 24],
[ 0,  0,  0,  0,  0, 57,  0, 94,  0, 83, 20,  0,  0,  9, 31,  0, 49, 26, 58,  0],
[ 0, 65, 56, 31, 64,  0,  0,  0,  0,  0,  0, 52, 85,  0,  0,  0,  0, 51,  0,  0],
[ 0, 35,  0,  0,  0, 69,  0,  0, 64,  0,  0,  0,  0, 70,  0,  0, 90,  0, 75, 76],
[69,  7,  0, 90,  0,  0, 84,  0, 47, 69, 19, 20, 42,  0,  0, 32, 71, 35,  0,  0],
[39,  0, 90,  0,  0,  4, 85,  0,  0, 55,  0,  0,  0, 35, 67, 40,  0, 40,  0, 77],
[98, 63,  0, 71,  0, 50,  0,  2, 61,  0, 38,  0,  0,  0,  0, 75,  0, 40, 33, 56],
[ 0, 73,  0, 64,  0, 38,  0, 35, 61,  0,  0, 52,  0,  7,  0, 51,  0,  0,  0, 34],
[ 0,  0, 28,  0, 34,  5, 63, 45, 14, 42, 60, 16, 76, 54, 99,  0, 28, 30,  0,  0],
[58, 37, 14,  0,  0,  0, 94,  0,  0, 90,  0,  0,  0,  0,  0,  0,  0,  8, 90, 53],
[86, 74, 94,  0, 49, 10, 60,  0, 40, 18,  0,  0,  0, 31, 60, 24,  0,  1,  0, 29],
[53,  0,  0, 97,  0,  0, 58,  0,  0, 39, 44, 47,  0,  0,  0, 12, 50,  0,  0, 11],
[ 4,  0, 92, 10, 28,  0,  0, 89,  0,  0, 18, 54, 23, 39,  0,  2,  0, 48,  0, 92],
[ 0,  0, 90, 77, 95, 33,  0,  0, 49, 22, 39,  0,  0,  0,  0,  0,  0, 40,  0,  0],
[96,  0,  0,  0,  0, 38, 86,  0, 22, 76,  0,  0,  0,  0, 83, 88, 95, 65, 72,  0],
[81, 65,  0,  4, 60,  0, 19,  0,  0, 68,  0,  0, 89,  0, 67, 22,  0,  0, 55, 33]],
           ZZ),
        DM([
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]],
           ZZ),
        ZZ(1),
    ),

    (
        'zz_large_3',
        DM([
[62,35,89,58,22,47,30,28,52,72,17,56,80,26,64,21,10,35,24,42,96,32,23,50,92,37,76,94,63,66],
[20,47,96,34,10,98,19,6,29,2,19,92,61,94,38,41,32,9,5,94,31,58,27,41,72,85,61,62,40,46],
[69,26,35,68,25,52,94,13,38,65,81,10,29,15,5,4,13,99,85,0,80,51,60,60,26,77,85,2,87,25],
[99,58,69,15,52,12,18,7,27,56,12,54,21,92,38,95,33,83,28,1,44,8,29,84,92,12,2,25,46,46],
[93,13,55,48,35,87,24,40,23,35,25,32,0,19,0,85,4,79,26,11,46,75,7,96,76,11,7,57,99,75],
[128,85,26,51,161,173,77,78,85,103,123,58,91,147,38,91,161,36,123,81,102,25,75,59,17,150,112,65,77,143],
[15,59,61,82,12,83,34,8,94,71,66,7,91,21,48,69,26,12,64,38,97,87,38,15,51,33,93,43,66,89],
[74,74,53,39,69,90,41,80,32,66,40,83,87,87,61,38,12,80,24,49,37,90,19,33,56,0,46,57,56,60],
[82,11,0,25,56,58,39,49,92,93,80,38,19,62,33,85,19,61,14,30,45,91,97,34,97,53,92,28,33,43],
[83,79,41,16,95,35,53,45,26,4,71,76,61,69,69,72,87,92,59,72,54,11,22,83,8,57,77,55,19,22],
[49,34,13,31,72,77,52,70,46,41,37,6,42,66,35,6,75,33,62,57,30,14,26,31,9,95,89,13,12,90],
[29,3,49,30,51,32,77,41,38,50,16,1,87,81,93,88,58,91,83,0,38,67,29,64,60,84,5,60,23,28],
[79,51,13,20,89,96,25,8,39,62,86,52,49,81,3,85,86,3,61,24,72,11,49,28,8,55,23,52,65,53],
[96,86,73,20,41,20,37,18,10,61,85,24,40,83,69,41,4,92,23,99,64,33,18,36,32,56,60,98,39,24],
[32,62,47,80,51,66,17,1,9,30,65,75,75,88,99,92,64,53,53,86,38,51,41,14,35,18,39,25,26,32],
[39,21,8,16,33,6,35,85,75,62,43,34,18,68,71,28,32,18,12,0,81,53,1,99,3,5,45,99,35,33],
[19,95,89,45,75,94,92,5,84,93,34,17,50,56,79,98,68,82,65,81,51,90,5,95,33,71,46,61,14,7],
[53,92,8,49,67,84,21,79,49,95,66,48,36,14,62,97,26,45,58,31,83,48,11,89,67,72,91,34,56,89],
[56,76,99,92,40,8,0,16,15,48,35,72,91,46,81,14,86,60,51,7,33,12,53,78,48,21,3,89,15,79],
[81,43,33,49,6,49,36,32,57,74,87,91,17,37,31,17,67,1,40,38,69,8,3,48,59,37,64,97,11,3],
[98,48,77,16,2,48,57,38,63,59,79,35,16,71,60,86,71,41,14,76,80,97,77,69,4,58,22,55,26,73],
[80,47,78,44,31,48,47,29,29,62,19,21,17,24,19,3,53,93,97,57,13,54,12,10,77,66,60,75,32,21],
[86,63,2,13,71,38,86,23,18,15,91,65,77,65,9,92,50,0,17,42,99,80,99,27,10,99,92,9,87,84],
[66,27,72,13,13,15,72,75,39,3,14,71,15,68,10,19,49,54,11,29,47,20,63,13,97,47,24,62,16,96],
[42,63,83,60,49,68,9,53,75,87,40,25,12,63,0,12,0,95,46,46,55,25,89,1,51,1,1,96,80,52],
[35,9,97,13,86,39,66,48,41,57,23,38,11,9,35,72,88,13,41,60,10,64,71,23,1,5,23,57,6,19],
[70,61,5,50,72,60,77,13,41,94,1,45,52,22,99,47,27,18,99,42,16,48,26,9,88,77,10,94,11,92],
[55,68,58,2,72,56,81,52,79,37,1,40,21,46,27,60,37,13,97,42,85,98,69,60,76,44,42,46,29,73],
[73,0,43,17,89,97,45,2,68,14,55,60,95,2,74,85,88,68,93,76,38,76,2,51,45,76,50,79,56,18],
[72,58,41,39,24,80,23,79,44,7,98,75,30,6,85,60,20,58,77,71,90,51,38,80,30,15,33,10,82,8]],
            ZZ),
        Matrix([
    [eye(29) * 2028539767964472550625641331179545072876560857886207583101,
     Matrix([ 4260575808093245475167216057435155595594339172099000182569,
              169148395880755256182802335904188369274227936894862744452,
              4915975976683942569102447281579134986891620721539038348914,
              6113916866367364958834844982578214901958429746875633283248,
              5585689617819894460378537031623265659753379011388162534838,
              359776822829880747716695359574308645968094838905181892423,
              -2800926112141776386671436511182421432449325232461665113305,
              941642292388230001722444876624818265766384442910688463158,
              3648811843256146649321864698600908938933015862008642023935,
              -4104526163246702252932955226754097174212129127510547462419,
              -704814955438106792441896903238080197619233342348191408078,
              1640882266829725529929398131287244562048075707575030019335,
              -4068330845192910563212155694231438198040299927120544468520,
              136589038308366497790495711534532612862715724187671166593,
              2544937011460702462290799932536905731142196510605191645593,
              755591839174293940486133926192300657264122907519174116472,
              -3683838489869297144348089243628436188645897133242795965021,
              -522207137101161299969706310062775465103537953077871128403,
              -2260451796032703984456606059649402832441331339246756656334,
              -6476809325293587953616004856993300606040336446656916663680,
              3521944238996782387785653800944972787867472610035040989081,
              2270762115788407950241944504104975551914297395787473242379,
              -3259947194628712441902262570532921252128444706733549251156,
              -5624569821491886970999097239695637132075823246850431083557,
              -3262698255682055804320585332902837076064075936601504555698,
              5786719943788937667411185880136324396357603606944869545501,
              -955257841973865996077323863289453200904051299086000660036,
              -1294235552446355326174641248209752679127075717918392702116,
              -3718353510747301598130831152458342785269166356215331448279,
             ]),],
        [zeros(1, 29), zeros(1, 1)],
        ]).to_DM().to_dense(),
        ZZ(2028539767964472550625641331179545072876560857886207583101),
    ),


    (
        'qq_1',
        DM([[(1,2), 0], [0, 2]], QQ),
        DM([[1, 0], [0, 1]], QQ),
        QQ(1),
    ),

    (
        # Standard square case
        'qq_2',
        DM([[0, 1],
            [1, 1]], QQ),
        DM([[1, 0],
            [0, 1]], QQ),
        QQ(1),
    ),

    (
        # m < n  case
        'qq_3',
        DM([[1, 2, 1],
            [3, 4, 1]], QQ),
        DM([[1, 0, -1],
            [0, 1,  1]], QQ),
        QQ(1),
    ),

    (
        # same m < n  but reversed
        'qq_4',
        DM([[3, 4, 1],
            [1, 2, 1]], QQ),
        DM([[1, 0, -1],
            [0, 1,  1]], QQ),
        QQ(1),
    ),

    (
        # m > n case
        'qq_5',
        DM([[1, 0],
            [1, 3],
            [0, 1]], QQ),
        DM([[1, 0],
            [0, 1],
            [0, 0]], QQ),
        QQ(1),
    ),

    (
        # Example with missing pivot
        'qq_6',
        DM([[1, 0, 1],
            [3, 0, 1]], QQ),
        DM([[1, 0, 0],
            [0, 0, 1]], QQ),
        QQ(1),
    ),

    (
        # This is intended to trigger the threshold where we give up on
        # clearing denominators.
        'qq_large_1',
        qq_large_1,
        DomainMatrix.eye(11, QQ).to_dense(),
        QQ(1),
    ),

    (
        # This is intended to trigger the threshold where we use rref_den over
        # QQ.
        'qq_large_2',
        qq_large_2,
        DomainMatrix.eye(11, QQ).to_dense(),
        QQ(1),
    ),

    (
        # Example with missing pivot and no replacement

        # This example is just enough to show a different result from the dense
        # and sparse versions of the algorithm:
        #
        #   >>> A = Matrix([[0, 1], [0, 2], [1, 0]])
        #   >>> A.to_DM().to_sparse().rref_den()[0].to_Matrix()
        #   Matrix([
        #   [1, 0],
        #   [0, 1],
        #   [0, 0]])
        #   >>> A.to_DM().to_dense().rref_den()[0].to_Matrix()
        #   Matrix([
        #   [2, 0],
        #   [0, 2],
        #   [0, 0]])
        #
        'qq_7',
        DM([[0, 1],
            [0, 2],
            [1, 0]], QQ),
        DM([[1, 0],
            [0, 1],
            [0, 0]], QQ),
        QQ(1),
    ),

    (
        # Gaussian integers
        'zz_i_1',
        DM([[(0,1), 1, 1],
            [    1, 1, 1]], ZZ_I),
        DM([[1, 0, 0],
            [0, 1, 1]], ZZ_I),
        ZZ_I(1),
    ),

    (
        # EX: test_issue_23718
        'EX_1',
        DM([
        [a, b, 1],
        [c, d, 1]], EX),
        DM([[a*d - b*c,         0, -b + d],
            [        0, a*d - b*c,  a - c]], EX),
        EX(a*d - b*c),
    ),

]


def _to_DM(A, ans):
    """Convert the answer to DomainMatrix."""
    if isinstance(A, DomainMatrix):
        return A.to_dense()
    elif isinstance(A, Matrix):
        return A.to_DM(ans.domain).to_dense()

    if not (hasattr(A, 'shape') and hasattr(A, 'domain')):
        shape, domain = ans.shape, ans.domain
    else:
        shape, domain = A.shape, A.domain

    if isinstance(A, (DDM, list)):
        return DomainMatrix(list(A), shape, domain).to_dense()
    elif isinstance(A, (SDM, dict)):
        return DomainMatrix(dict(A), shape, domain).to_dense()
    else:
        assert False # pragma: no cover


def _pivots(A_rref):
    """Return the pivots from the rref of A."""
    return tuple(sorted(map(min, A_rref.to_sdm().values())))


def _check_cancel(result, rref_ans, den_ans):
    """Check the cancelled result."""
    rref, den, pivots = result
    if isinstance(rref, (DDM, SDM, list, dict)):
        assert type(pivots) is list
        pivots = tuple(pivots)
    rref = _to_DM(rref, rref_ans)
    rref2, den2 = rref.cancel_denom(den)
    assert rref2 == rref_ans
    assert den2 == den_ans
    assert pivots == _pivots(rref)


def _check_divide(result, rref_ans, den_ans):
    """Check the divided result."""
    rref, pivots = result
    if isinstance(rref, (DDM, SDM, list, dict)):
        assert type(pivots) is list
        pivots = tuple(pivots)
    rref_ans = rref_ans.to_field() / den_ans
    rref = _to_DM(rref, rref_ans)
    assert rref == rref_ans
    assert _pivots(rref) == pivots


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_Matrix_rref(name, A, A_rref, den):
    K = A.domain
    A = A.to_Matrix()
    A_rref_found, pivots = A.rref()
    if K.is_EX:
        A_rref_found = A_rref_found.expand()
    _check_divide((A_rref_found, pivots), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_dense_rref(name, A, A_rref, den):
    A = A.to_field()
    _check_divide(A.rref(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_dense_rref_den(name, A, A_rref, den):
    _check_cancel(A.rref_den(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref(name, A, A_rref, den):
    A = A.to_field().to_sparse()
    _check_divide(A.rref(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den(name, A, A_rref, den):
    A = A.to_sparse()
    _check_cancel(A.rref_den(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den_keep_domain(name, A, A_rref, den):
    A = A.to_sparse()
    A_rref_f, den_f, pivots_f = A.rref_den(keep_domain=False)
    A_rref_f = A_rref_f.to_field() / den_f
    _check_divide((A_rref_f, pivots_f), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den_keep_domain_CD(name, A, A_rref, den):
    A = A.to_sparse()
    A_rref_f, den_f, pivots_f = A.rref_den(keep_domain=False, method='CD')
    A_rref_f = A_rref_f.to_field() / den_f
    _check_divide((A_rref_f, pivots_f), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_dm_sparse_rref_den_keep_domain_GJ(name, A, A_rref, den):
    A = A.to_sparse()
    A_rref_f, den_f, pivots_f = A.rref_den(keep_domain=False, method='GJ')
    A_rref_f = A_rref_f.to_field() / den_f
    _check_divide((A_rref_f, pivots_f), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_rref_den(name, A, A_rref, den):
    A = A.to_ddm()
    _check_cancel(A.rref_den(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sdm_rref_den(name, A, A_rref, den):
    A = A.to_sdm()
    _check_cancel(A.rref_den(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_rref(name, A, A_rref, den):
    A = A.to_field().to_ddm()
    _check_divide(A.rref(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sdm_rref(name, A, A_rref, den):
    A = A.to_field().to_sdm()
    _check_divide(A.rref(), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_irref(name, A, A_rref, den):
    A = A.to_field().to_ddm().copy()
    pivots_found = ddm_irref(A)
    _check_divide((A, pivots_found), A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_ddm_irref_den(name, A, A_rref, den):
    A = A.to_ddm().copy()
    (den_found, pivots_found) = ddm_irref_den(A, A.domain)
    result = (A, den_found, pivots_found)
    _check_cancel(result, A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sparse_sdm_rref(name, A, A_rref, den):
    A = A.to_field().to_sdm()
    _check_divide(sdm_irref(A)[:2], A_rref, den)


@pytest.mark.parametrize('name, A, A_rref, den', RREF_EXAMPLES)
def test_sparse_sdm_rref_den(name, A, A_rref, den):
    A = A.to_sdm().copy()
    K = A.domain
    _check_cancel(sdm_rref_den(A, K), A_rref, den)
