/*=============================================================================

    This file is part of FLINT.

    FLINT is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    FLINT is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FLINT; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

=============================================================================*/
/******************************************************************************

    Copyright (C) 2011 Andy Novocin
    Copyright (C) 2011 William Hart
    Copyright (C) 2011 Sebastian Pancratz
   
******************************************************************************/

#include <stdlib.h>
#include "fmpz_poly.h"

#define TRACE 0

void fmpz_poly_factor_zassenhaus_recombination(fmpz_poly_factor_t final_fac, 
	const fmpz_poly_factor_t lifted_fac, 
    const fmpz_poly_t F, const fmpz_t P, slong exp)
{
    const slong r = lifted_fac->num;

    slong k, *used_arr, *sub_arr;
    fmpz_poly_t f, Q, R, tryme;
    fmpz *leadF;

    used_arr = flint_calloc(2 * r, sizeof(slong));
    sub_arr  = used_arr + r;

    fmpz_poly_init(f);
    fmpz_poly_init(Q);
    fmpz_poly_init(R);
    fmpz_poly_init(tryme);
    fmpz_poly_set(f, F);

#if TRACE == 1
    fmpz_poly_factor_print(lifted_fac); flint_printf(" lifted_fac\n");
#endif

    leadF = fmpz_poly_lead(F);

    for (k = 1; k < r; k++)
    {
        slong count = 0, indx = k - 1, l;

        for(l = 0; l < k; l++)
            sub_arr[l] = l;

        sub_arr[indx]--;
        while ((indx >= 0))
        {
            sub_arr[indx] = sub_arr[indx] + 1;

            for (l = indx + 1; l < k; l++)
                sub_arr[l] = sub_arr[l - 1] + 1;

            if (sub_arr[k - 1] > r - 1)
                indx--;
            else
            {
                for(l = 0; l < k; l++)
                {
                    if (used_arr[sub_arr[l]] == 1)
                        break;
                }

             /* Need to involve leadF, perhaps set coeff 0 to leadF and do 
                leadF * rest and check if under M_bits... here I'm using a 
                trial division... */
                fmpz_poly_set_fmpz(tryme, leadF);

                for(l = 0; l < k; l++)
                    fmpz_poly_mul(tryme, tryme, lifted_fac->p + (sub_arr[l]));

                fmpz_poly_scalar_smod_fmpz(tryme, tryme, P);
                fmpz_poly_primitive_part(tryme, tryme);
                fmpz_poly_divrem(Q, R, f, tryme);

#if TRACE == 1
                fmpz_poly_print(tryme); flint_printf(" is tryme\n");
                fmpz_poly_print(R); flint_printf(" is R\n");
#endif

                if (fmpz_poly_is_zero(R))
                {
                    fmpz_poly_factor_insert(final_fac, tryme, exp);

                    for(l = 0; l < k; l++)
                    {
                        used_arr[sub_arr[l]] = 1;
                        count++;
                    }

                    fmpz_poly_set(f, Q);
                    leadF = fmpz_poly_lead(f);
                 /* If r - count = k then the rest are irreducible.  
                    TODO: Add a test for that case */
                }

                indx = k - 1;
            }
        }

     /* This is where we switch to the next loop for k.  So we will have 
        found all factors using <= k local factors.  We should/could update 
        f to be the rest divided away (or multiply the remaining), could 
        also adjust r.  It is the number of remaining factors so if you 
        update then test if r = k or k+1 in which case the remaining f is 
        irreducible. */
    }

    {
        slong test = 0;

        for (k = 0; k < r; k++)
            test = test + used_arr[k];

        if (test == 0)
            fmpz_poly_factor_insert(final_fac, f, exp);
    }

    fmpz_poly_clear(f);
    fmpz_poly_clear(tryme);
    fmpz_poly_clear(Q);
    fmpz_poly_clear(R);
    flint_free(used_arr);
}

#undef TRACE

