/* -*- mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
// vim:sts=8:sw=8:ts=8:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s

/*
 * Copyright (C) 2015 the FFLAS-FFPACK group
 * Written by Brice Boyer (briceboyer) <boyer.brice@gmail.com>
 *
 * This file is Free Software and part of FFLAS-FFPACK.
 *
 * ========LICENCE========
 * This file is part of the library FFLAS-FFPACK.
 *
 * FFLAS-FFPACK is free software: you can redistribute it and/or modify
 * it under the terms of the  GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * ========LICENCE========
 *
 */


#include "fflas-ffpack/utils/timer.h"
#include "Matio.h"
#include "fflas-ffpack/fflas/fflas.h"
#include "fflas-ffpack/fflas-ffpack-config.h"
#include "test-utils.h"
#include "assert.h"
#include "fflas-ffpack/utils/args-parser.h"
#include "fflas-ffpack/utils/flimits.h"

#include <givaro/udl.h>

// using namespace FFPACK;
#define NEWWINO
// #define NOTRANDOM
//
#define DIVIDE_INTO(x,y) (((x) + (y) - 1)/(y))

const int algos = 6 ;
const int algos_k = 2 ;

using Givaro::Modular;
using Givaro::ModularBalanced;
using Givaro::Timer;
using FFLAS::FieldTraits;
typedef std::vector<Timer> time_v ;
typedef std::vector<int> int_v ;

const int selec[] = {
	0
	,1
	,2
	,3
	,4
	,5
};

const int selec_k[] = {
	0
	,1
};

const char * descr[] = {
	"322 low mem"
	, "322 first 1"
	, "322 4 tmp  "
	, "223 low mem"
	, "232 first 1"
	, "232 all tmp"
	, "comp left  "
	, "comp right "
	// , "322 sqrt   "
};

const char * descr_k[] = {
	"comp left  "
	, "comp right "
};

namespace FFLAS { /*  compression */

	template<class Elem, int Num>
	struct Packer ;

	template<>
	struct Packer<double,2> {
		uint64_t bits = (limits<double>::digits()/2) ;
		double   base = (double) (1_ui64 << bits) ;
		uint64_t mask = (1_ui64 << bits) - 1_ui64 ;

		template<class T>
		void accu(double * p, T * w) {
			*p *= base ;
			*p += (double)*w ;
		}
	} ;


	/* ****** */
	/*  pack  */
	/* ****** */

	/*  pack nb words (a,b,c) -> [a|b|c] */
	template<class wide_T, class pack_T, int Nb>
	void pack_word( pack_T * packed,
			const wide_T * words, int32_t stride,
			Packer<pack_T,Nb> & packer) ;


	template<class wide_T>
	void pack_word/*<wide_T,double,2>*/( double * packed,
					     const wide_T * words, int32_t stride,
					     Packer<double,2> & packer)
	{
		// std::cout << "pack " << *words << '+' << *(words+stride) << " * " << (uint64_t) packer.base << " = ";
		// words += stride ;
		*packed = (double) *words ;
		words += stride ;
		packer.accu(packed,words);
		// std::cout << (uint64_t) *packed << std::endl;
	}

	/*  pack nb words (a,b) -> [a|b|0]  filling with zeros */
	template<class wide_T, class pack_T, int Nb>
	void pack_word_part( pack_T * packed, int32_t nb,
			     const wide_T * words, int32_t stride,
			     Packer<pack_T,Nb> & packer) ;

	template<class wide_T>
	void pack_word_part/* <wide_T,double,2> */( double * packed, int32_t nb,
						    const wide_T * words, int32_t stride,
						    Packer<double,2> & packer)
	{
		assert(nb == 1);
		*packed = (double) *words ;
		// words += stride ;
		// packer.accu(packed,words);
		*packed *= packer.base ;
	}

	/* ****** */
	/* unpack */
	/* ****** */

	template<class wide_T, class pack_T, int Nb>
	void unpack_word( wide_T * words, int32_t stride,
			  const pack_T * packed,
			  Packer<pack_T,Nb> & packer);

	template<class wide_T>
	void unpack_word/* <wide_T,double,2> */( wide_T * words, int32_t stride,
						 const double * packed,
						 Packer<double ,2> & packer)
	{
		uint64_t pck = (uint64_t) *packed ;
		words += stride ;
		*words = (double) (pck & packer.mask) ;
		words -= stride ;
		pck >>= packer.bits ;
		*words = (double) pck /*  & packer.mask  */ ;
	}


	template<class wide_T, class pack_T, int Nb>
	void unpack_word_part( wide_T * words, int32_t stride,
			       const pack_T * packed, int32_t nb,
			       Packer<pack_T,Nb> & packer);

	template<class wide_T>
	void unpack_word_part/* <wide_T,double,2> */( wide_T * words, int32_t stride,
						      const double * packed, int32_t nb,
						      Packer<double,2> & packer)
	{
		assert(nb == 1);
		words += stride ;
		*words = 0 ;
		words -= stride ;
		uint64_t pck = (uint64_t) *packed ;
		pck >>= packer.bits ;
		*words =  (double)pck /*  & packer.mask  */ ;
	}

	/* ****** */
	/*  pack  */
	/* ****** */

	template<class wide_T, class pack_T, int Nb, bool row_packed>
	void pack_matrix( pack_T * packed, int32_t row_p, int32_t col_p, int32_t ldm_p,
			  const wide_T * elemts, int32_t row_e, int32_t col_e, int32_t ldm_e,
			  Packer<pack_T,Nb> & packer)
	{
		if (row_packed == true) {
			for (int32_t i = 0 ; i < row_e ;  i++ ) {
				const wide_T * e_p = elemts + i * ldm_e ;
				pack_T * p_p = packed + i * ldm_p ;
				int32_t j = 0 ;
				for ( ; j < col_e/Nb*Nb ;  j+=Nb, e_p+=Nb, p_p++) {
					pack_word<wide_T>(p_p,e_p,1,packer);

				}
				if (j < col_e)
					pack_word_part<wide_T>(p_p,col_e-j,e_p,1,packer);
			}
		}
		else { /*  col_packed */
			int32_t i = 0 ;
			int32_t ii = 0 ;
			for ( ; i < row_e/Nb*Nb ;  i += Nb , ii++) {
				const wide_T * e_p = elemts + i * ldm_e ;
				pack_T * p_p = packed + ii * ldm_p ;
				for (int32_t j = 0 ; j < col_e ;  j++, e_p++, p_p++) {
					pack_word<wide_T>(p_p,e_p,ldm_e,packer);

				}
			}
			if (i < row_e)
				pack_word_part<wide_T>(packed+i*ldm_p,row_e-i,elemts+ii*ldm_e,ldm_e,packer);

		}
	}

	/* ****** */
	/* unpack */
	/* ****** */

	template<class wide_T, class pack_T, int Nb, bool row_packed>
	void unpack_matrix( wide_T * elemts, int32_t row_e, int32_t col_e, int32_t ldm_e,
			    const pack_T * packed, int32_t row_p, int32_t col_p, int32_t ldm_p,
			    Packer<pack_T,Nb> & packer)
	{
		if (row_packed == true) {
			for (int32_t i = 0 ; i < row_e ;  i++ ) {
				wide_T * e_p = elemts + i * ldm_e ;
				const pack_T * p_p = packed + i * ldm_p ;
				int32_t j = 0 ;
				for ( ; j < col_e/Nb*Nb ;  j+=Nb, e_p+=Nb, p_p++) {
					unpack_word<wide_T>(e_p,1,p_p,packer);

				}
				if (j < col_e)
					unpack_word_part<wide_T>(e_p,1,p_p,col_e-j,packer);
			}
		}
		else { /*  col_packed */
			int32_t i = 0 ;
			int32_t ii = 0 ;
			for ( ; i < row_e/Nb*Nb ;  i += Nb , ii++) {
				wide_T * e_p = elemts + i * ldm_e ;
				const pack_T * p_p = packed + ii * ldm_p ;
				for (int32_t j = 0 ; j < col_e ;  j++, e_p++, p_p++) {
					unpack_word<wide_T>(e_p,ldm_e,p_p,packer);

				}
			}
			if (i < row_e)
				unpack_word_part<wide_T>(elemts+i*ldm_e,ldm_e,packed+ii*ldm_p,row_e-i,packer);

		}
	}

	/*  compress A */
	template<class Field, bool left_compress >
	void
	fgemm_compressed(const Field & F,
			 int m, int n, int k,
			 const typename Field::Element * A, int lda,
			 const typename Field::Element * B, int ldb,
			 typename Field::Element * C, int ldc
			)
	{
		Givaro::ZRing<double>   NoField;
		double * A_k, * B_k, * C_k ;

		typedef typename Field::Element elem_t ;
		Packer<elem_t,2> packer ;

		int m_k = m , n_k = n , lda_k = lda, ldb_k = ldb, ldc_k = ldc ;
		if (left_compress) {
			m_k = DIVIDE_INTO(m,2)*2 ;
			lda_k = m_k ;
			ldc_k = n ;

			A_k =  FFLAS::fflas_new<double>(m_k*k) ;
			//!@bug don't zero all, just the "border"
			FFLAS::fzero(NoField,m_k,k,A_k,k);

			B_k = const_cast<typename Field::Element *>(B) ;

			pack_matrix<elem_t,elem_t,2,false>(A_k,m_k,k,lda_k,
							   A,m,k,lda,
							   packer);
		}
		else {
			n_k = DIVIDE_INTO(n,2)*2 ;
			ldb_k = n_k ;
			ldc_k = n_k ;

			A_k = const_cast<typename Field::Element *>(A) ;
			B_k = FFLAS::fflas_new<double>(k*n_k)  ;
			//!@bug don't zero all, just the "border"
			FFLAS::fzero(NoField,k,n_k,B_k,n_k);

			pack_matrix<elem_t,elem_t,2,true>(B_k,k,n_k,ldb_k,
							  B,k,n,ldb,
							  packer);
		}

		C_k = FFLAS::fflas_new<double>(m_k*n_k) ;
			//!@bug don't zero all, just the "border"
		FFLAS::fzero(NoField,m_k,n_k,C_k,n_k);

		pack_matrix<elem_t,elem_t,2,!left_compress>(C_k,m_k,n_k,ldc_k,
							    C,m,n,ldc,
							    packer);

#if 0
		double * C_e = FFLAS::fflas_new<double>(m*ldc);
		unpack_matrix<elem_t,elem_t,2,!left_compress>(C_e,m,n,ldc,
							      C_k,m_k,n_k,ldc_k,
							      packer);

		int	faux = 0 ;
		for (int i = 0 ; i < m ; ++i) {
			for (int j = 0 ; j < n ; ++j) {
				if (! (C[i*ldc+j] == C_e[i*ldc+j]) ) {
					++faux ;
				}
			}
		}
		if (faux) {
			std::cout << "bad pack/unpack ; bad/all = " << faux << '/' << m*n << " ~~  " << (double)faux/(double)(m*n) << std::endl;
		}

		if (faux && (n<20)) {
			std::cout << "IN " << std::endl;
			for (int i = 0 ; i < m ; ++i) {
				for (int j = 0 ; j < n ; ++j)
					std::cout << C[i*ldc+j] << ' ';
				std::cout << std::endl;
			}
			std::cout << "OUT" << std::endl;
			for (int i = 0 ; i < m ; ++i) {
				for (int j = 0 ; j < n ; ++j)
					std::cout << C_e[i*ldc+j] << ' ';
				std::cout << std::endl;
			}


		}

		if (faux)
			exit(-1);
#endif




		Givaro::DoubleDomain G ;

		fgemm(G,FFLAS::FflasNoTrans,FFLAS::FflasNoTrans,
		      m_k,n_k,k, 1, A_k,lda_k, B_k,ldb_k, 0, C_k, ldc_k);

		// cblas_dgemm(CblasRowMajor, CblasNoTrans,CblasNoTrans,
		      // m_k,n_k,k, 1, A_k,lda_k, B_k,ldb_k, 0, C_k, ldc_k);


		unpack_matrix<elem_t,elem_t,2,!left_compress>(C,m,n,ldc,
							      C_k,m_k,n_k,ldc_k,
							      packer);

		if (left_compress)
			FFLAS::fflas_delete(A_k);
		else
			FFLAS::fflas_delete(B_k);
		FFLAS::fflas_delete(C_k);
	}

}

namespace FFLAS { /*  tools */


	template<class Field>
	void finit_fuzzy(Field & F, size_t m, size_t n, double * C, size_t ldc)
	{


		if (n == ldc)
			// FFLAS::vectorised::modp<true,true>(C,C,m*n,p,invp,0,p-1);
			FFLAS::vectorised::modp<Field,true>(F,C,m*n,C);
		else
			for (size_t i = 0 ; i < m ; ++i)
				// FFLAS::vectorised::modp<true,true>(C+i*ldc,C+i*ldc,n,p,invp,0,p-1);
				FFLAS::vectorised::modp<Field,true>(F,C+i*ldc,n,C+i*ldc);
	}


	// C = a*A + B
	void add(const size_t m, const size_t n,
		 double a,
		 const double *A, const size_t lda,
		 const double *B, const size_t ldb,
		 double *C, const size_t ldc)
	{
		const double *Ai = A,*Bi = B;
		double *Ci       = C;
		for (;Ai < A+m*lda ; Ai+=lda,Bi+=ldb,Ci+=ldc)
			for (size_t j = 0 ; j < n ; ++j)
				Ci[j] = a * Ai[j] + Bi[j];
	}

	// C = C-(A+B)
	void subadd(const size_t m, const size_t n,
		    const double *A, const size_t lda,
		    const double *B, const size_t ldb,
		    double *C, const size_t ldc)
	{
		const double *Ai = A,*Bi = B;
		double *Ci       = C;
		for (;Ai < A+m*lda ; Ai+=lda,Bi+=ldb,Ci+=ldc)
			for (size_t j = 0 ; j < n ; ++j) {
				Ci[j] = Ci[j] - Ai[j] - Bi[j] ;
			}

	}

	// C = -(A+B)
	void negadd(const size_t m, const size_t n,
		    const double *A, const size_t lda,
		    const double *B, const size_t ldb,
		    double *C, const size_t ldc)
	{
		const double *Ai = A,*Bi = B;
		double *Ci       = C;
		for (;Ai < A+m*lda ; Ai+=lda,Bi+=ldb,Ci+=ldc)
			for (size_t j = 0 ; j < n ; ++j) {
				Ci[j] =  - Ai[j] - Bi[j] ;
			}

	}


	// C = C+A-B
	void addsub(const size_t m, const size_t n,
		    const double *A, const size_t lda,
		    const double *B, const size_t ldb,
		    double *C, const size_t ldc)
	{
		const double *Ai = A,*Bi = B;
		double *Ci       = C;
		for (;Ai < A+m*lda ; Ai+=lda,Bi+=ldb,Ci+=ldc)
			for (size_t j = 0 ; j < n ; ++j) {
				Ci[j] = Ci[j] + Ai[j] - Bi[j] ;
			}

	}


	// C = (C+B)/e
	template<class Field>
	void addscalinf(const Field & F, const size_t m, const size_t n,
			const double *B, const size_t ldb,
			double e,
			double *C, const size_t ldc)
	{
		const double * Bi = B;
		double * Ci = C;
		for (;Bi < B+m*ldb ; Ci+=ldc, Bi += ldb)
			for (size_t j = 0 ; j < n ; ++j)
				Ci[j]= (Ci[j]+Bi[j])*e ;
		// F.init( Ci[j], (Ci[j]+Bi[j])/e );

	}

	// C = (C-B)/e
	template<class Field>
	void subscalinf(const Field & F, const size_t m, const size_t n,
			const double *B, const size_t ldb,
			double e,
			double *C, const size_t ldc)
	{
		const double * Bi = B;
		double * Ci = C;
		for (;Bi < B+m*ldb ; Ci+=ldc, Bi += ldb)
			for (size_t j = 0 ; j < n ; ++j)
				Ci[j]= (Ci[j]-Bi[j])*e ;
		// F.init( Ci[j], (Ci[j]-Bi[j])/e );

	}

	// C = (D-B)/e
	template<class Field>
	void subscal(const Field & F, const size_t m, const size_t n,
		     const double *D, const size_t ldd,
		     const double *B, const size_t ldb,
		     double e,
		     double *C, const size_t ldc)
	{
		const double * Bi = B;
		const double * Di = D;
		double * Ci = C;
		for (;Bi < B+m*ldb ; Ci+=ldc, Bi += ldb, Di += ldd)
			for (size_t j = 0 ; j < n ; ++j)
				Ci[j] = (Di[j]-Bi[j])*e ;

	}

	// C = (D+B)/e
	template<class Field>
	void addscal(const Field & F, const size_t m, const size_t n,
		     const double *D, const size_t ldd,
		     const double *B, const size_t ldb,
		     double e,
		     double *C, const size_t ldc)
	{
		const double * Bi = B;
		const double * Di = D;
		double * Ci = C;
		for (;Bi < B+m*ldb ; Ci+=ldc, Bi += ldb, Di += ldd)
			for (size_t j = 0 ; j < n ; ++j)
				Ci[j] = (Di[j]+Bi[j])*e ;

	}

	// C = C + (D-B)/e
	template<class Field>
	void subscalacc(const Field & F, const size_t m, const size_t n,
			const double *D, const size_t ldd,
			const double *B, const size_t ldb,
			double e,
			double *C, const size_t ldc)
	{
		const double * Bi = B;
		const double * Di = D;
		double * Ci = C;
		for (;Bi < B+m*ldb ; Ci+=ldc, Bi += ldb, Di += ldd)
			for (size_t j = 0 ; j < n ; ++j)
				Ci[j] += (Di[j]-Bi[j])*e ;

	}

#ifndef TRE
	// #ifndef NDEBUG
// #define TRE 1
	// #else
	#define TRE (size_t)(__FFLASFFPACK_WINOTHRESHOLD)
	// #define TRE (size_t)(__FFLASFFPACK_WINOTHRESHOLD*0.9)
	// #endif
#endif
	template<class Field>
	double * gemm_fflas(const Field & F,
			    const size_t m, const size_t n, const size_t k,
			    const double *A, size_t lda,
			    const double *B, size_t ldb,
			    double * C, size_t ldc,
			    int rec =  0)
	{
		Givaro::DoubleDomain R;
		FFLAS::fgemm(R,
			     FFLAS::FflasNoTrans,FFLAS::FflasNoTrans,
			     m,n,k,
			     1,
			     A,lda, B,ldb,
			     0,
			     C, ldc);

		// cblas_dgemm(CblasRowMajor, CblasNoTrans,CblasNoTrans,
			    // m,n,k,1,A,lda,B,ldb,0,C,ldc);

		return C;
	}
} // FFLAS

namespace FFLAS { namespace Protected { namespace Rec {

	// Field must be Givaro::Modular<double>
	template<class Field>
	double *
	gemm_bini_322_0(const Field & F
			, const size_t m
			, const size_t n
			, const size_t k
			, const double *A , const size_t lda
			, const double *B , const size_t ldb
			, double *C , const size_t ldc
			, int rec
			, const double & epsilon
		       )
	{
		Givaro::ZRing<double>   NoField;
		// const double p = (double)F.characteristic();
		size_t M = (n>m)?std::min(k,m):std::min(k,n);
		// std::cout << rec << ',' <<  M  << std::endl;
		// Field G(p*p);

		if ( M < TRE  || rec <= 0) {
			return gemm_fflas(F, m,n,k, A,lda, B,ldb, C, ldc);
		}

		assert(k/2*2==k); // k divisible par 2
		assert(n/2*2==n); // n divisible par 2
		assert(m/3*3==m); // m divisible par 3


		size_t n2 = n/2;
		size_t k2 = k/2;
		size_t m3 = m/3;

		// std::cout << "€ = " << epsilon << std::endl;

		// sub matrices in A
		const double * A11 = A;
		const double * A12 = A   +k2;
		const double * A21 = A   +lda*m3;
		const double * A22 = A21 +k2;
		const double * A31 = A21 +lda*m3;
		const double * A32 = A31 +k2;

		// sub matrices in C
		double * C11 = C;
		double * C12 = C   +n2;
		double * C21 = C   +ldc*m3;
		double * C22 = C21 +n2;
		double * C31 = C21 +ldc*m3;
		double * C32 = C31 +n2;

		// sub matrices in B
		const double * B11 = B;
		const double * B12 = B   +n2;
		const double * B21 = B   +ldb*k2;
		const double * B22 = B21 +n2;

		FFLAS::fzero(NoField,m,n,C,ldc);

		/*
		 * Algo :
		 * S1  := A11  +A22;
		 * S4  := e*A12+A22;
		 * S5  := A11  +e*A12;
		 * S6  := A21  +A32;
		 * S9  := A21  +e*A31;
		 * S10 := e*A31+A32;
		 *
		 * T1  := e*B11 +B22;
		 * T2  := B21   +B22;
		 * T4  := -e*B11+B21;
		 * T5  := e*B12 +B22;
		 * T6  := B11   +e*B22;
		 * T7  := B11   +B12;
		 * T9  := B12   -e*B22;
		 * T10 := B11   +e*B21;
		 *
		 * P1 := S1 *T1;
		 * P2 := A22*T2;
		 * P3 := A11*B22;
		 * P4 := S4 *T4;
		 * P5 := S5 *T5;
		 * P6 := S6 *T6;
		 * P7 := A21*T7;
		 * P8 := A32*B11;
		 * P9 := S9 *T9;
		 * P10:= S10*T10;
		 *
		 * C11 := (P1-P2-P3+P4)/e;
		 * C12 := (P3-P5)/(-e) ;
		 * C21 := P4+P6-P10 ;
		 * C22 := P1-P5+P9;
		 * C31 := (-P8+P10)/e;
		 * C32 := (P6-P7-P8+P9)/e;
		 *
		 */

		double * S1 = FFLAS::fflas_new<double>(m3*k2) ;
		// double * C11t = FFLAS::fflas_new<double>(n2*m3) ;
		// S1  := A11  +A22;
		FFLAS::fadd(NoField,m3,k2,A11,lda,A22,lda,S1,k2);
		// T1  := e*B11 +B22;
		double * T1 = FFLAS::fflas_new<double>(n2*k2) ; // ou aire
		add(k2,n2,epsilon,B11,ldb,B22,ldb,T1,n2);
		// P1 := S1 *T1; (dans C22)
		gemm_bini_322_0(F,m3,n2,k2,S1,k2,T1,n2,C22,ldc,rec-1,epsilon);
		// S4  := e*A12+A22;
		double * eA12 = FFLAS::fflas_new<double >(m3*k2);
		FFLAS::fscal(NoField,m3,k2,epsilon,A12,lda,eA12,k2) ;
		FFLAS::fadd(NoField,m3,k2,eA12,k2,A22,lda,S1,k2);
		// T4  := -e*B11+B21;
		add(k2,n2,-epsilon,B11,ldb,B21,ldb,T1,n2);
		// P4 := S4 *T4; (dans C21)
		gemm_bini_322_0(F,m3,n2,k2,S1,k2,T1,n2,C21,ldc,rec-1,epsilon);
		// C11 = P1+P4
		FFLAS::fadd(NoField,m3,n2,C21,ldc,C22,ldc,C11,ldc);
		// T2  := B21  +B22;
		FFLAS::fadd(NoField,k2,n2,B21,ldb,B22,ldb,T1,n2);
		// P2 := A22*T2;
		double * P1 = FFLAS::fflas_new<double>(n2*m3) ; // ou aire
		gemm_bini_322_0(F,m3,n2,k2,A22,lda,T1,n2,P1,n2,rec-1,epsilon);
		// P3 := A11*B22; (dans C12)
		gemm_bini_322_0(F,m3,n2,k2,A11,lda,B22,ldb,C12,ldc,rec-1,epsilon);
		// C11 -= (P2+P3)
		subadd(m3,n2,P1,n2,C12,ldc,C11,ldc);
		// S5  := A11  +e*A12;
		FFLAS::fadd(NoField,m3,k2,eA12,k2,A11,lda,S1,k2);
		// T5  := e*B12 +B22;
		add(k2,n2,epsilon,B12,ldb,B22,ldb,T1,n2);
		// P5 := S5 *T5;
		double * P2 = FFLAS::fflas_new<double>(n2*m3) ; // ou aire
		gemm_bini_322_0(F,m3,n2,k2,S1,k2,T1,n2,P2,n2,rec-1,epsilon);
		// C12 -= P5
		subscalinf(NoField,m3,n2,P2,n2,-(double)1/epsilon,C12,ldc);
		// S6  := A21  +A32;
		FFLAS::fadd(NoField,m3,k2,A21,lda,A32,lda,S1,k2);
		// T6  := B11   +e*B22;
		add(k2,n2,epsilon,B22,ldb,B11,ldb,T1,n2);
		// P6 := S6 *T6; dans C32
		gemm_bini_322_0(F,m3,n2,k2,S1,k2,T1,n2,C32,ldc,rec-1,epsilon);
		// C21+= P6
		FFLAS::faddin(NoField,m3,n2,C32,ldc,C21,ldc);
		// T7  := B11  +B12;
		FFLAS::fadd(NoField,k2,n2,B11,ldb,B12,ldb,T1,n2);
		// P7 := A21*T7; !signe
		gemm_bini_322_0(F,m3,n2,k2,A21,lda,T1,n2,P1,n2,rec-1,epsilon);
		// P8 := A32*B11; dans C31 !signe
		gemm_bini_322_0(F,m3,n2,k2,A32,lda,B11,ldb,C31,ldc,rec-1,epsilon);
		// C32 -= P8+P7
		subadd(m3,n2,P1,n2,C31,ldc,C32,ldc);
		// S9  := A21  +e*A31;
		double * eA31 = eA12 ;
		FFLAS::fscal(NoField,m3,k2,epsilon,A31,lda,eA31,k2);
		FFLAS::fadd(NoField,m3,k2,eA31,k2,A21,lda,S1,k2);
		// T9  := B12   -e*B22;
		add(k2,n2,-epsilon,B22,ldb,B12,ldb,T1,n2);
		// P9 := S9 *T9;
		gemm_bini_322_0(F,m3,n2,k2,S1,k2,T1,n2,P1,n2,rec-1,epsilon);
		// C32= (C32+P9)/p
		addscalinf(NoField,m3,n2,P1,n2,(double)1/epsilon,C32,ldc);
		// C22+= P9-P5
		addsub(m3,n2,P1,n2,P2,n2,C22,ldc);
		FFLAS::fflas_delete( P2);
		// S10 := e*A31+A32;
		FFLAS::fadd(NoField,m3,k2,eA31,k2,A32,lda,S1,k2);
		FFLAS::fflas_delete( eA12 );
		// T10 := B11   +e*B21;
		add(k2,n2,epsilon,B21,ldb,B11,ldb,T1,n2);
		// P10:= S10*T10;
		gemm_bini_322_0(F,m3,n2,k2,S1,k2,T1,n2,P1,n2,rec-1,epsilon);
		FFLAS::fflas_delete( S1);
		FFLAS::fflas_delete( T1);
		// C21-= P10
		FFLAS::fsubin(NoField,m3,n2,P1,n2,C21,ldc);
		// C31= (C31-P10)/(-epsilon)
		subscalinf(NoField,m3,n2,P1,n2,-(double)1/epsilon,C31,ldc);
		FFLAS::fflas_delete( P1);
		// C11 := (P1+P-P3+P4)/e;
		FFLAS::fscalin(NoField,m3,n2,(double)1/epsilon,C11,ldc);

		return C;

	}

	// Field must be Givaro::Modular<double>
	template<class Field>
	double *
	gemm_bini_322_mem(const Field & F
			  , const size_t m
			  , const size_t n
			  , const size_t k
			  , const double *A , const size_t lda
			  , const double *B , const size_t ldb
			  , double *C , const size_t ldc
			  , int rec
			  , const double & epsilon
			 )
	{
		Givaro::ZRing<double>   NoField;
		// const double p = (double)F.characteristic();
		size_t M = (n>m)?std::min(k,m):std::min(k,n);
		// std::cout << rec << ',' <<  M  << std::endl;
		// Field G(p*p);

		if ( M < TRE  || rec <= 0) {
			// std::cout << "ffw" << std::endl;
			return gemm_fflas(F, m,n,k, A,lda, B,ldb, C, ldc);
			// return gemm_fflas(NoField, m,n,k, A,lda, B,ldb, C, ldc);
		}

		assert(k/2*2==k); // k divisible par 2
		assert(n/2*2==n); // n divisible par 2
		assert(m/3*3==m); // m divisible par 3

		// std::cout << "tested" << std::endl;

		size_t n2 = n/2;
		size_t k2 = k/2;
		size_t m3 = m/3;

		// std::cout << "€ = " << epsilon << std::endl;

		// sub matrices in A
		const double * A11 = A;
		const double * A12 = A   +k2;
		const double * A21 = A   +lda*m3;
		const double * A22 = A21 +k2;
		const double * A31 = A21 +lda*m3;
		const double * A32 = A31 +k2;

		// sub matrices in C
		double * C11 = C;
		double * C12 = C   +n2;
		double * C21 = C   +ldc*m3;
		double * C22 = C21 +n2;
		double * C31 = C21 +ldc*m3;
		double * C32 = C31 +n2;

		// sub matrices in B
		const double * B11 = B;
		const double * B12 = B   +n2;
		const double * B21 = B   +ldb*k2;
		const double * B22 = B21 +n2;

		FFLAS::fzero(F,m,n,C,ldc);

		/*
		 * Algo :
		 * S1  := A11  +A22;
		 * S4  := e*A12+A22;
		 * S5  := A11  +e*A12;
		 * S6  := A21  +A32;
		 * S9  := A21  +e*A31;
		 * S3 := e*A31+A32;
		 *
		 * T1  := e*B11 +B22;
		 * T2  := B21   +B22;
		 * T4  := -e*B11+B21;
		 * T5  := e*B12 +B22;
		 * T6  := B11   +e*B22;
		 * T7  := B11   +B12;
		 * T9  := B12   -e*B22;
		 * T3 := B11   +e*B21;
		 *
		 * P1 := S1 *T1;
		 * P2 := A22*T2;
		 * P10 := A11*B22;
		 * P4 := S4 *T4;
		 * P5 := S5 *T5;
		 * P6 := S6 *T6;
		 * P7 := A21*T7;
		 * P8 := A32*B11;
		 * P9 := S9 *T9;
		 * P3:= S3*T3;
		 *
		 * C11 := (P1-P2-P10+P4)/e;
		 * C12 := (P10-P5)/(-e) ;
		 * C21 := P4+P6-P3 ;
		 * C22 := P1-P5+P9;
		 * C31 := (-P8+P3)/e;
		 * C32 := (P6-P7-P8+P9)/e;
		 *
		 */


		// P10
		gemm_bini_322_mem(F,m3,n2,k2,A11,lda,B22,ldb,C11,ldc,rec-1,epsilon);
		// S5
		double * X = FFLAS::fflas_new<double>(m3*k2);
		add(m3,k2,epsilon,A12,lda,A11,lda,X,k2);
		// T5
		// double * Y = FFLAS::fflas_new<double>(std::max(k2,m3)*n2);
		double * Y = FFLAS::fflas_new<double>(k2*n2);
		add(k2,n2,epsilon,B12,ldb,B22,ldb,Y,n2);
		// P5
		gemm_bini_322_mem(F,m3,n2,k2,X,k2,Y,n2,C22,ldc,rec-1,epsilon);
		// C12
		subscal(NoField,m3,n2,C22,ldc,C11,ldc,(double)1/epsilon,C12,ldc);
		// T2
		FFLAS::fadd(NoField,k2,n2,B21,ldb,B22,ldb,Y,n2);
		// P2
		gemm_bini_322_mem(F,m3,n2,k2,A22,lda,Y,n2,C31,ldc,rec-1,epsilon);
		// C11
		FFLAS::faddin(NoField,m3,n2,C31,ldc,C11,ldc);
		// S1
		FFLAS::fadd(NoField,m3,k2,A11,lda,A22,lda,X,k2);
		// T1
		add(k2,n2,epsilon,B11,ldb,B22,ldb,Y,n2);
		// P1
		gemm_bini_322_mem(F,m3,n2,k2,X,k2,Y,n2,C21,ldc,rec-1,epsilon);
		// C22
		FFLAS::fsub(NoField,m3,n2,C21,ldc,C22,ldc,C22,ldc);
		// C11
		FFLAS::fsub(NoField,m3,n2,C21,ldc,C11,ldc,C11,ldc);
		// S4
		add(m3,k2,epsilon,A12,lda,A22,lda,X,k2);
		// T4
		add(k2,n2,-epsilon,B11,ldb,B21,ldb,Y,n2);
		// P4
		gemm_bini_322_mem(F,m3,n2,k2,X,k2,Y,n2,C21,ldc,rec-1,epsilon);
		// C11
		addscalinf(NoField,m3,n2,C21,ldc,(double)1/epsilon,C11,ldc);
		// S9
		add(m3,k2,epsilon,A31,lda,A21,lda,X,k2);
		// T9
		add(k2,n2,-epsilon,B22,ldb,B12,ldb,Y,n2);
		// P9
		gemm_bini_322_mem(F,m3,n2,k2,X,k2,Y,n2,C32,ldc,rec-1,epsilon);
		//  C22
		FFLAS::faddin(NoField,m3,n2,C32,ldc,C22,ldc);
		// S6
		FFLAS::fadd(NoField,m3,k2,A21,lda,A32,lda,X,k2);
		// T6
		add(k2,n2,epsilon,B22,ldb,B11,ldb,Y,n2);
		// P6
		gemm_bini_322_mem(F,m3,n2,k2,X,k2,Y,n2,C31,ldc,rec-1,epsilon);
		// C21
		FFLAS::faddin(NoField,m3,n2,C31,ldc,C21,ldc);
		// C32
		FFLAS::faddin(NoField,m3,n2,C31,ldc,C32,ldc);
		// T7
		FFLAS::fadd(NoField,k2,n2,B11,ldb,B12,ldb,Y,n2);
		// P7
		gemm_bini_322_mem(F,m3,n2,k2,A21,lda,Y,n2,C31,ldc,rec-1,epsilon);
		// if (epsilon > 1 && rec == 2) { FFLAS::finit(G,m3,n2,C31,ldc);}
		// C32
		FFLAS::fsubin(NoField,m3,n2,C31,ldc,C32,ldc);
		// S3
		add(m3,k2,epsilon,A31,lda,A32,lda,X,k2);
		// T3
		add(k2,n2,epsilon,B21,ldb,B11,ldb,Y,n2);
		// P3
		gemm_bini_322_mem(F,m3,n2,k2,X,k2,Y,n2,C31,ldc,rec-1,epsilon);
		FFLAS::fflas_delete( X);
		FFLAS::fflas_delete( Y );
		// C21
		FFLAS::fsubin(NoField,m3,n2,C31,ldc,C21,ldc);
		// P8
		Y = FFLAS::fflas_new<double>(m3*n2);
		gemm_bini_322_mem(F,m3,n2,k2,A32,lda,B11,ldb,Y,n2,rec-1,epsilon);
		// C31
		subscalinf(NoField,m3,n2,Y,n2,(double)1/epsilon,C31,ldc);
		// FFLAS::fsubin(NoField,m3,n2,Y,n2,C31,ldc);
		// C32
		subscalinf(NoField,m3,n2,Y,n2,(double)1/epsilon,C32,ldc);
		// FFLAS::fsubin(NoField,m3,n2,Y,n2,C32,ldc);
		// FFLAS::fscalin(NoField,m3,n,(double)1/epsilon,C31,ldc);
		FFLAS::fflas_delete( Y );


		return C;

	}

	// Field must be Givaro::Modular<double>
	template<class Field>
	double *
	gemm_bini_223_mem(const Field & F
			  , const size_t m
			  , const size_t n
			  , const size_t k
			  , const double *A , const size_t lda
			  , const double *B , const size_t ldb
			  , double *C , const size_t ldc
			  , int rec
			  , const double & epsilon
			 )
	{
		Givaro::ZRing<double>   NoField;
		// const double p = (double)F.characteristic();
		size_t M = (n>m)?std::min(k,m):std::min(k,n);
		// std::cout << rec << ',' <<  M  << std::endl;
		// Field G(p*p);

		if ( M < TRE  || rec <= 0) {
			// std::cout << "ffw" << std::endl;
			return gemm_fflas(F, m,n,k, A,lda, B,ldb, C, ldc);
			// return gemm_fflas(NoField, m,n,k, A,lda, B,ldb, C, ldc);
		}

		assert(k/2*2==k); // k divisible par 2
		assert(n/3*3==n); // n divisible par 2
		assert(m/2*2==m); // m divisible par 3

		// std::cout << "tested" << std::endl;

		size_t m2 = m/2;
		size_t k2 = k/2;
		size_t n3 = n/3;

		// std::cout << "€ = " << epsilon << std::endl;

		// sub matrices in A
		const double * A11 = A;
		const double * A12 = A   +k2;
		const double * A21 = A   +lda*m2;
		const double * A22 = A21 +k2;

		// sub matrices in C
		double * C11 = C;
		double * C12 = C   +n3;
		double * C13 = C   +2*n3;
		double * C21 = C   +ldc*m2;
		double * C22 = C21 +n3;
		double * C23 = C21 +2*n3;



		// sub matrices in B
		const double * B11 = B;
		const double * B12 = B   +n3;
		const double * B13 = B   +2*n3;
		const double * B21 = B   +ldb*k2;
		const double * B22 = B21 +n3;
		const double * B23 = B21 +2*n3;


		FFLAS::fzero(F,m,n,C,ldc);

		/*
		 * Algo :
		 * S1  := B11  +B22;
		 * S4  := e*B21+B22;
		 * S5  := B11  +e*B21;
		 * S6  := B12  +B23;
		 * S9  := B12  +e*B13;
		 * S3 := e*B13+B23;
		 *
		 * T1  := e*A11 +A22;
		 * T2  := A12   +A22;
		 * T4  := -e*A11+A12;
		 * T5  := e*A21 +A22;
		 * T6  := A11   +e*A22;
		 * T7  := A11   +A21;
		 * T9  := A21   -e*A22;
		 * T3  := A11   +e*A12;
		 *
		 * P1 := S1 *T1;
		 * P2 := T2 * B22;
		 * P10 := A22 * B11;
		 * P4 := S4 *T4;
		 * P5 := S5 *T5;
		 * P6 := S6 *T6;
		 * P7 := T7*B12;
		 * P8 := A11*B23;
		 * P9 := S9 *T9;
		 * P3 := S3*T3;
		 *
		 * C11 := (P1-P2-P10+P4)/e;
		 * C21 := (P10-P5)/(-e) ;
		 * C12 := P4+P6-P3 ;
		 * C22 := P1-P5+P9;
		 * C13 := (-P8+P3)/e;
		 * C23 := (P6-P7-P8+P9)/e;
		 *
		 */


		// P10
		gemm_bini_223_mem(F,m2,n3,k2,A22,lda,B11,ldb,C11,ldc,rec-1,epsilon);
		// S5
		double * Y = FFLAS::fflas_new<double>(k2*n3);
		add(k2,n3,epsilon,B21,ldb,B11,ldb,Y,n3);
		// T5
		double * X = FFLAS::fflas_new<double>(m2*k2);
		add(m2,k2,epsilon,A21,lda,A22,lda,X,k2);
		// P5
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C22,ldc,rec-1,epsilon);
		// C12
		subscal(NoField,m2,n3,C22,ldc,C11,ldc,(double)1/epsilon,C21,ldc);
		// T2
		FFLAS::fadd(NoField,m2,k2,A12,lda,A22,lda,X,k2);
		// P2
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,B22,ldb,C13,ldc,rec-1,epsilon);
		// C11
		FFLAS::faddin(NoField,m2,n3,C13,ldc,C11,ldc);
		// S1
		FFLAS::fadd(NoField,k2,n3,B11,ldb,B22,ldb,Y,n3);
		// T1
		add(m2,k2,epsilon,A11,lda,A22,lda,X,k2);
		// P1
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C12,ldc,rec-1,epsilon);
		// C22
		FFLAS::fsub(NoField,m2,n3,C12,ldc,C22,ldc,C22,ldc);
		// C11
		FFLAS::fsub(NoField,m2,n3,C12,ldc,C11,ldc,C11,ldc);
		// S4
		add(k2,n3,epsilon,B21,ldb,B22,ldb,Y,n3);
		// T4
		add(m2,k2,-epsilon,A11,lda,A12,lda,X,k2);
		// P4
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C12,ldc,rec-1,epsilon);
		// C11
		addscalinf(NoField,m2,n3,C12,ldc,(double)1/epsilon,C11,ldc);
		// S9
		add(k2,n3,epsilon,B13,ldb,B12,ldb,Y,n3);
		// T9
		add(m2,k2,-epsilon,A22,lda,A21,lda,X,k2);
		// P9
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C23,ldc,rec-1,epsilon);
		//  C22
		FFLAS::faddin(NoField,m2,n3,C23,ldc,C22,ldc);
		// S6
		FFLAS::fadd(NoField,k2,n3,B12,ldb,B23,ldb,Y,n3);
		// T6
		add(m2,k2,epsilon,A22,lda,A11,lda,X,k2);
		// P6
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C13,ldc,rec-1,epsilon);
		// C21
		FFLAS::faddin(NoField,m2,n3,C13,ldc,C12,ldc);
		// C32
		FFLAS::faddin(NoField,m2,n3,C13,ldc,C23,ldc);
		// T7
		FFLAS::fadd(NoField,m2,k2,A11,lda,A21,lda,X,k2);
		// P7
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,B12,ldb,C13,ldc,rec-1,epsilon);
		// if (epsilon > 1 && rec == 2) { FFLAS::finit(G,m2,n3,C31,ldc);}
		// C32
		FFLAS::fsubin(NoField,m2,n3,C13,ldc,C23,ldc);
		// S3
		add(k2,n3,epsilon,B13,ldb,B23,ldb,Y,n3);
		// T3
		add(m2,k2,epsilon,A12,lda,A11,lda,X,k2);
		// P3
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C13,ldc,rec-1,epsilon);
		FFLAS::fflas_delete( Y );
		FFLAS::fflas_delete( X );
		// C21
		FFLAS::fsubin(NoField,m2,n3,C13,ldc,C12,ldc);
		// P8
		Y = FFLAS::fflas_new<double>(m2*n3);
		gemm_bini_223_mem(F,m2,n3,k2,A11,lda,B23,ldb,Y,n3,rec-1,epsilon);
		// C31
		subscalinf(NoField,m2,n3,Y,n3,(double)1/epsilon,C13,ldc);
		// C32
		subscalinf(NoField,m2,n3,Y,n3,(double)1/epsilon,C23,ldc);
		FFLAS::fflas_delete( Y );


		return C;

	}

	// Field must be Givaro::Modular<double>
	template<class Field>
	double *
	gemm_bini_322_2(const Field & F
			, const size_t m
			, const size_t n
			, const size_t k
			, const double *A , const size_t lda
			, const double *B , const size_t ldb
			, double *C , const size_t ldc
			, int rec
			, const double & epsilon
		       )
	{
		Givaro::ZRing<double>   NoField;
		// const double p = (double)F.characteristic();
		size_t M = (n>m)?std::min(k,m):std::min(k,n);
		// std::cout << rec << ',' <<  M  << std::endl;
		// Field G(p*p);

		if ( M < TRE  || rec <= 0) {
			// std::cout << "ffw" << std::endl;
			return gemm_fflas(F, m,n,k, A,lda, B,ldb, C, ldc);
			// return gemm_fflas(NoField, m,n,k, A,lda, B,ldb, C, ldc);
		}

		assert(k/2*2==k); // k divisible par 2
		assert(n/2*2==n); // n divisible par 2
		assert(m/3*3==m); // m divisible par 3

		// std::cout << "tested" << std::endl;

		size_t n2 = n/2;
		size_t k2 = k/2;
		size_t m3 = m/3;

		// std::cout << "€ = " << epsilon << std::endl;

		// sub matrices in A
		const double * A11 = A;
		const double * A12 = A   +k2;
		const double * A21 = A   +lda*m3;
		const double * A22 = A21 +k2;
		const double * A31 = A21 +lda*m3;
		const double * A32 = A31 +k2;

		// sub matrices in C
		double * C11 = C;
		double * C12 = C   +n2;
		double * C21 = C   +ldc*m3;
		double * C22 = C21 +n2;
		double * C31 = C21 +ldc*m3;
		double * C32 = C31 +n2;

		// sub matrices in B
		const double * B11 = B;
		const double * B12 = B   +n2;
		const double * B21 = B   +ldb*k2;
		const double * B22 = B21 +n2;

		FFLAS::fzero(F,m,n,C,ldc);

		/*
		 * Algo :
		 * S1 := A11  +A22;
		 * S4 := e*A12+A22;
		 * S5 := A11  +e*A12;
		 * S3 := e*A31+A32;
		 * S6 := A21  +A32;
		 * S9 := A21  +e*A31;
		 *
		 * T1 := e*B11 +B22;
		 * T2 := B21   +B22;
		 * T3 := B11   +e*B21;
		 * T4 := -e*B11+B21;
		 * T5 := e*B12 +B22;
		 * T6 := B11   +e*B22;
		 * T7 := B11   +B12;
		 * T9 := B12   -e*B22;
		 *
		 * P1 := S1 *T1;
		 * P2 := A22*T2;
		 * P10 := A11*B22;
		 * P4 := S4 *T4;
		 * P5 := S5 *T5;
		 * P6 := S6 *T6;
		 * P7 := A21*T7;
		 * P8 := A32*B11;
		 * P9 := S9 *T9;
		 * P3:= S3*T3;
		 *
		 * C11 := (P1-P2-P10+P4)/e;
		 * C12 := (P10-P5)/(-e) ;
		 * C21 := P4+P6-P3 ;
		 * C22 := P1-P5+P9;
		 * C31 := (-P8+P3)/e;
		 * C32 := (P6-P7-P8+P9)/e;
		 *
		 */

		double * U = FFLAS::fflas_new<double>(m3*n2);
		double * V = FFLAS::fflas_new<double>(m3*n2);
		double * X = FFLAS::fflas_new<double>(m3*std::max(k2,n2));
		double * Y = FFLAS::fflas_new<double>(std::max(k2,m3)*n2);

		// S4
		add(m3,k2,epsilon,A12,lda,A22,lda,X,k2);
		// T4
		add(k2,n2,-epsilon,B11,ldb,B21,ldb,Y,n2);
		// P4
		gemm_bini_322_2(F,m3,n2,k2,X,k2,Y,n2,U,n2,rec-1,epsilon);
		// S9
		add(m3,k2,epsilon,A31,lda,A21,lda,X,k2);
		// T9
		add(k2,n2,-epsilon,B22,ldb,B12,ldb,Y,n2);
		// P9
		gemm_bini_322_2(F,m3,n2,k2,X,k2,Y,n2,V,n2,rec-1,epsilon);
		// S5
		add(m3,k2,epsilon,A12,lda,A11,lda,X,k2);
		// T5
		add(k2,n2,epsilon,B12,ldb,B22,ldb,Y,n2);
		// P5
		gemm_bini_322_2(F,m3,n2,k2,X,k2,Y,n2,C12,ldc,rec-1,epsilon);
		// S3
		add(m3,k2,epsilon,A31,lda,A32,lda,X,k2);
		// T3
		add(k2,n2,epsilon,B21,ldb,B11,ldb,Y,n2);
		// P3
		gemm_bini_322_2(F,m3,n2,k2,X,k2,Y,n2,C31,ldc,rec-1,epsilon);
		// C22 = P9-P5
		FFLAS::fsub(NoField,m3,n2,V,n2,C12,ldc,C22,ldc);
		// C21 = P4-P3
		FFLAS::fsub(NoField,m3,n2,U,n2,C31,ldc,C21,ldc);
		// T2
		FFLAS::fadd(NoField,k2,n2,B21,ldb,B22,ldb,Y,n2);
		// P2
		gemm_bini_322_2(F,m3,n2,k2,A22,lda,Y,n2,X,n2,rec-1,epsilon);
		// XXX approximate
		// C11 = (P4 - P2) / e
		subscal(NoField,m3,n2,U,n2,X,n2,1./epsilon,C11,ldc);
		// T7
		FFLAS::fadd(NoField,k2,n2,B11,ldb,B12,ldb,Y,n2);
		// P7
		gemm_bini_322_2(F,m3,n2,k2,A21,lda,Y,n2,X,n2,rec-1,epsilon);
		// XXX approximate
		// C32 = (P9-P7) / e
		subscal(NoField,m3,n2,V,n2,X,n2,1./epsilon,C32,ldc);
		// S1
		FFLAS::fadd(NoField,m3,k2,A11,lda,A22,lda,X,k2);
		// T1
		add(k2,n2,epsilon,B11,ldb,B22,ldb,Y,n2);
		// P1
		gemm_bini_322_2(F,m3,n2,k2,X,k2,Y,n2,U,n2,rec-1,epsilon);
		// C22 += P1
		FFLAS::faddin(NoField,m3,n2,U,n2,C22,ldc);
		// P10
		gemm_bini_322_2(F,m3,n2,k2,A11,lda,B22,ldb,V,n2,rec-1,epsilon);
		// C12 = (P5-P10)/e
		subscalinf(NoField,m3,n2,V,n2,1./epsilon,C12,ldc);
		// XXX approximate
		// C11 = C11 + (P1-P10)/e
		subscalacc(NoField,m3,n2,U,n2,V,n2,1./epsilon,C11,ldc);
		// S6
		FFLAS::fadd(NoField,m3,k2,A21,lda,A32,lda,X,k2);
		// T6
		add(k2,n2,epsilon,B22,ldb,B11,ldb,Y,n2);
		// P6
		gemm_bini_322_2(F,m3,n2,k2,X,k2,Y,n2,U,n2,rec-1,epsilon);
		// C21 += P6
		FFLAS::faddin(NoField,m3,n2,U,n2,C21,ldc);
		// P8
		gemm_bini_322_2(F,m3,n2,k2,A32,lda,B11,ldb,V,n2,rec-1,epsilon);
		// C31 = (P3-P8)/2
		subscalinf(NoField,m3,n2,V,n2,1./epsilon,C31,ldc);
		// XXX approximate
		// C32 = C32 + (P6-P8)/e
		subscalacc(NoField,m3,n2,U,n2,V,n2,1./epsilon,C32,ldc);


		FFLAS::fflas_delete( X);
		FFLAS::fflas_delete( Y );
		FFLAS::fflas_delete( U);
		FFLAS::fflas_delete( V);


		return C;

	}


	// Field must be Givaro::Modular<double>
	template<class Field>
	double *
	gemm_bini_232_2(const Field & F
			, const size_t m
			, const size_t n
			, const size_t k
			, const double *A , const size_t lda
			, const double *B , const size_t ldb
			, double *C , const size_t ldc
			, int rec
			, const double & epsilon
		       )
	{
		Givaro::ZRing<double>   NoField;
		// const double p = (double)F.characteristic();
		size_t M = (n>m)?std::min(k,m):std::min(k,n);
		// Field G(p*p);

		if ( M < TRE  || rec <= 0) {
			// std::cout << "ffw" << std::endl;
			return gemm_fflas(F, m,n,k, A,lda, B,ldb, C, ldc);
			// return gemm_fflas(NoField, m,n,k, A,lda, B,ldb, C, ldc);
		}

		assert(k/3*3==k); // k divisible par 3
		assert(n/2*2==n); // n divisible par 2
		assert(m/2*2==m); // m divisible par 2

		// std::cout << "tested" << std::endl;

		size_t n2 = n/2;
		size_t k3 = k/3;
		size_t m2 = m/2;

		// std::cout << "€ = " << epsilon << std::endl;

		// sub matrices in B
		const double * B11 = B;
		const double * B12 = B   +n2;
		const double * B21 = B   +ldb*k3;
		const double * B22 = B21 +n2;
		const double * B31 = B21 +ldb*k3;
		const double * B32 = B31 +n2;

		// sub matrices in C
		double * C11 = C;
		double * C12 = C   +n2;
		double * C21 = C   +ldc*m2;
		double * C22 = C21 +n2;

		// sub matrices in A

		const double * A11 = A;
		const double * A12 = A   +k3;
		const double * A13 = A   +2*k3;
		const double * A21 = A   +lda*m2;
		const double * A22 = A21 +k3;
		const double * A23 = A21 +2*k3;


		FFLAS::fzero(F,m,n,C,ldc);

		/*
		 * Algo :
		 *
		 * S1  := A11  +A22*e;
		 * S3  := -(A11+A21);
		 * S4  := A11+A12*e;
		 * S5  := A21 - A22*e;
		 * S6  := A12*e  + A23;
		 * S8 := -(A13+A23):
		 * S9  := A22*e  + A23;
		 * S10 := -A12*e+A13;
		 *
		 * T1  := B11 +B22;
		 * T4  := e*B12+B22;
		 * T5  := B11 +e*B12;
		 * T6  := B21   +B32;
		 * T9  := B21   + e*B31;
		 * T10 := e*B31   +B32;
		 *
		 * P1 := Bini232(S1,T1 ,e);
		 * P2 := Bini232(A11,B22 ,e);
		 * P3 := Bini232(S3,B11,e);
		 * P4 := Bini232(S4,T4 ,e);
		 * P5 := Bini232(S5,T5 ,e);
		 * P6 := Bini232(S6,T6 ,e);
		 * P7 := Bini232(A23,B21 ,e);
		 * P8 := Bini232(S8,B32,e);
		 * P9 := Bini232(S9,T9 ,e);
		 * P10:= Bini232(S10,T10,e);
		 *
		 *
		 * C11 := evalm(P1-P4+(P6-P7+P8+P10)/e);
		 * C12 := evalm((-P2+P4)/e+P10) ;
		 * C21 := evalm(P5+(-P7+P9)/e) ;
		 * C22 := evalm((P1-P2+P3+P5)/e+P6-P9);
		 *
		 */

		double * U = FFLAS::fflas_new<double>(m2*n2);
		double * V = FFLAS::fflas_new<double>(m2*n2);
		double * X = FFLAS::fflas_new<double>(m2*k3);
		double * Y = FFLAS::fflas_new<double>(k3*n2);

		// S1
		add(m2,k3,epsilon,A22,lda,A11,lda,X,k3);
		// T1
		FFLAS::fadd(NoField,k3,n2,B11,ldb,B22,ldb,Y,n2);
		// P1 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// S3
		negadd(m2,k3,A11,lda,A21,lda,X,k3);
		// P3 (in V)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,B11,ldb,V,n2,rec-1,epsilon);
		// C22 = (P1+P3)/e
		// FFLAS::fadd(NoField,m2,n2,U,n2,V,n2,C22,ldc); // XXX acc
		addscal(NoField,m2,n2,U,n2,V,n2,(double)1/epsilon,C22,ldc);
		// S6
		add(m2,k3,epsilon,A12,lda,A23,lda,X,k3);
		// T6
		FFLAS::fadd(NoField,k3,n2,B21,ldb,B32,ldb,Y,n2);
		// P6 (in V)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,V,n2,rec-1,epsilon);
		// C22 += P6
		FFLAS::faddin(NoField,m2,n2,V,n2,C22,ldc);
		// S8
		negadd(m2,k3,A13,lda,A23,lda,X,k3);
		// P8 (in C11)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,B32,ldb,C11,ldc,rec-1,epsilon);
		// C11 = (P8+P6)/e
		addscalinf(NoField,m2,n2,V,n2,(double)1/epsilon,C11,ldc);
		// C11 += P1
		FFLAS::faddin(NoField,m2,n2,U,n2,C11,ldc);
		// S4
		add(m2,k3,epsilon,A12,lda,A11,lda,X,k3);
		// T4
		add(k3,n2,epsilon,B12,ldb,B22,ldb,Y,n2);
		// P4 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// C11 -= P4
		FFLAS::fsubin(NoField,m2,n2,U,n2,C11,ldc);
		// P2 (in C12)
		gemm_bini_232_2(F,m2,n2,k3,A11,lda,B22,ldb,C12,ldc,rec-1,epsilon);
		// S5
		add(m2,k3,-epsilon,A22,lda,A21,lda,X,k3);
		// T5
		add(k3,n2,epsilon,B12,ldb,B11,ldb,Y,n2);
		// P5 (in V)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,V,n2,rec-1,epsilon);
		// C22 += (P5-P2)/e
		subscalacc(NoField,m2,n2,V,n2,C12,ldc,(double)1/epsilon,C22,ldc);
		// C12 = (P4-P2)/e
		subscalinf(NoField,m2,n2,U,n2,-(double)1/epsilon,C12,ldc);
		// S9
		add(m2,k3,epsilon,A22,lda,A23,lda,X,k3);
		// T9
		add(k3,n2,epsilon,B31,ldb,B21,ldb,Y,n2);
		// P9 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// C22 -= P9
		FFLAS::fsubin(NoField,m2,n2,U,n2,C22,ldc);
		// P7 (in C21)
		gemm_bini_232_2(F,m2,n2,k3,A23,lda,B21,ldb,C21,ldc,rec-1,epsilon);
		// C11 = C11  - P7/e
		add(m2,n2,-(double)1/epsilon,C21,ldc,C11,ldc,C11,ldc);
		// C21 =  (P9-P7)/e
		subscalinf(NoField,m2,n2,U,n2,-(double)1/epsilon,C21,ldc);
		// C21 += P5
		FFLAS::faddin(NoField,m2,n2,V,n2,C21,ldc);
		// S10
		add(m2,k3,-epsilon,A12,lda,A13,lda,X,k3);
		// T10
		add(k3,n2,epsilon,B31,ldb,B32,ldb,Y,n2);
		// P10 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// C12 += P10
		FFLAS::faddin(NoField,m2,n2,U,n2,C12,ldc);
		// C11 += P10/e
		add(m2,n2,(double)1/epsilon,U,n2,C11,ldc,C11,ldc);


		FFLAS::fflas_delete( X );
		FFLAS::fflas_delete( Y );
		FFLAS::fflas_delete( U );
		FFLAS::fflas_delete( V );


		return C;

	}

	template<class Field>
	double *
	gemm_bini_232_3_acc(const Field & F
			    , const size_t m
			    , const size_t n
			    , const size_t k
			    , const double *A , const size_t lda
			    , const double *B , const size_t ldb
			    , double *C , const size_t ldc
			    , int rec
			    , const double & epsilon
			   )
	{
		if (rec != 0)
			exit(-1);
		Givaro::DoubleDomain R;
		FFLAS::fgemm(R,
			     FFLAS::FflasNoTrans,FFLAS::FflasNoTrans,
			     m,n,k,
			     1,
			     A,lda, B,ldb,
			     1,
			     C, ldc);


	}

	template<class Field>
	double *
	gemm_bini_232_3(const Field & F
			, const size_t m
			, const size_t n
			, const size_t k
			, const double *A , const size_t lda
			, const double *B , const size_t ldb
			, double *C , const size_t ldc
			, int rec
			, const double & epsilon
		       )
	{
		Givaro::ZRing<double>   NoField;
		// const double p = (double)F.characteristic();
		size_t M = (n>m)?std::min(k,m):std::min(k,n);
		// Field G(p*p);

		if ( M < TRE  || rec <= 0) {
			// std::cout << "ffw" << std::endl;
			return gemm_fflas(F, m,n,k, A,lda, B,ldb, C, ldc);
			// return gemm_fflas(NoField, m,n,k, A,lda, B,ldb, C, ldc);
		}

		assert(k/3*3==k); // k divisible par 3
		assert(n/2*2==n); // n divisible par 2
		assert(m/2*2==m); // m divisible par 2

		// std::cout << "tested" << std::endl;

		size_t n2 = n/2;
		size_t k3 = k/3;
		size_t m2 = m/2;

		// std::cout << "€ = " << epsilon << std::endl;

		// sub matrices in B
		const double * B11 = B;
		const double * B12 = B   +n2;
		const double * B21 = B   +ldb*k3;
		const double * B22 = B21 +n2;
		const double * B31 = B21 +ldb*k3;
		const double * B32 = B31 +n2;

		// sub matrices in C
		double * C11 = C;
		double * C12 = C   +n2;
		double * C21 = C   +ldc*m2;
		double * C22 = C21 +n2;

		// sub matrices in A

		const double * A11 = A;
		const double * A12 = A   +k3;
		const double * A13 = A   +2*k3;
		const double * A21 = A   +lda*m2;
		const double * A22 = A21 +k3;
		const double * A23 = A21 +2*k3;


		FFLAS::fzero(F,m,n,C,ldc);

		/*
		 * Algo :
		 *
		 * S1  := A11  +A22*e;
		 * S3  := -(A11+A21);
		 * S4  := A11+A12*e;
		 * S5  := A21 - A22*e;
		 * S6  := A12*e  + A23;
		 * S8 := -(A13+A23):
		 * S9  := A22*e  + A23;
		 * S10 := -A12*e+A13;
		 *
		 * T1  := B11 +B22;
		 * T4  := e*B12+B22;
		 * T5  := B11 +e*B12;
		 * T6  := B21   +B32;
		 * T9  := B21   + e*B31;
		 * T10 := e*B31   +B32;
		 *
		 * P1 := Bini232(S1,T1 ,e);
		 * P2 := Bini232(A11,B22 ,e);
		 * P3 := Bini232(S3,B11,e);
		 * P4 := Bini232(S4,T4 ,e);
		 * P5 := Bini232(S5,T5 ,e);
		 * P6 := Bini232(S6,T6 ,e);
		 * P7 := Bini232(A23,B21 ,e);
		 * P8 := Bini232(S8,B32,e);
		 * P9 := Bini232(S9,T9 ,e);
		 * P10:= Bini232(S10,T10,e);
		 *
		 *
		 * C11 := evalm(P1-P4+(P6-P7+P8+P10)/e);
		 * C12 := evalm((-P2+P4)/e+P10) ;
		 * C21 := evalm(P5+(-P7+P9)/e) ;
		 * C22 := evalm((P1-P2+P3+P5)/e+P6-P9);
		 *
		 */

		// could be just one band for the scalings



		double * U = FFLAS::fflas_new<double>(m2*n2);
		double * V = FFLAS::fflas_new<double>(std::max(k3,m2)*n2);
		double * X = FFLAS::fflas_new<double>(m2*k3);
		double * Y = FFLAS::fflas_new<double>(k3*n2);

		// S1
		double * eA22 = FFLAS::fflas_new<double>(std::max(m2,n2)*k3);
		FFLAS::fscal(NoField,m2,k3,epsilon,A22,lda,eA22,k3);
		FFLAS::fadd(NoField,m2,k3,eA22,k3,A11,lda,X,k3);
		// T1
		FFLAS::fadd(NoField,k3,n2,B11,ldb,B22,ldb,Y,n2);
		// P1 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// S3
		negadd(m2,k3,A11,lda,A21,lda,X,k3);
		// P3 (in V)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,B11,ldb,V,n2,rec-1,epsilon);
		// C22 = (P1+P3)/e
		addscal(NoField,m2,n2,U,n2,V,n2,(double)1/epsilon,C22,ldc);
		// S6
		double * eA12 = FFLAS::fflas_new<double>(m2*k3);
		FFLAS::fscal(NoField,m2,k3,epsilon,A12,lda,eA12,k3);
		FFLAS::fadd(NoField,m2,k3,eA12,k3,A23,lda,X,k3);
		// T6
		FFLAS::fadd(NoField,k3,n2,B21,ldb,B32,ldb,Y,n2);
		// P6 (in V)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,V,n2,rec-1,epsilon);
		// C22 += P6
		FFLAS::faddin(NoField,m2,n2,V,n2,C22,ldc);
		// S8
		negadd(m2,k3,A13,lda,A23,lda,X,k3);
		// P8 (in C11)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,B32,ldb,C11,ldc,rec-1,epsilon);
		// C11 = (P8+P6)/e
		addscalinf(NoField,m2,n2,V,n2,(double)1/epsilon,C11,ldc);
		// C11 += P1
		FFLAS::faddin(NoField,m2,n2,U,n2,C11,ldc);
		// S4
		FFLAS::fadd(NoField,m2,k3,eA12,k3,A11,lda,X,k3);
		// T4
		double * eB12 = V ; // FFLAS::fflas_new<double>(n2*k3);
		FFLAS::fscal(NoField,k3,n2,epsilon,B12,ldb,eB12,n2);
		FFLAS::fadd(NoField,k3,n2,eB12,n2,B22,ldb,Y,n2);
		// P4 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// C11 -= P4
		FFLAS::fsubin(NoField,m2,n2,U,n2,C11,ldc);
		// P2 (in C12)
		gemm_bini_232_2(F,m2,n2,k3,A11,lda,B22,ldb,C12,ldc,rec-1,epsilon);
		// S5
		FFLAS::fsub(NoField,m2,k3,A21,lda,eA22,k3,X,k3);
		// T5
		FFLAS::fadd(NoField,k3,n2,eB12,n2,B11,ldb,Y,n2);
		// FFLAS::fflas_delete( eB12);
		// P5 (in V)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,V,n2,rec-1,epsilon);
		// C22 += (P5-P2)/e
		subscalacc(NoField,m2,n2,V,n2,C12,ldc,(double)1/epsilon,C22,ldc);
		// C12 = (P4-P2)/e
		subscalinf(NoField,m2,n2,U,n2,-(double)1/epsilon,C12,ldc);
		// S9
		FFLAS::fadd(NoField,m2,k3,eA22,k3,A23,lda,X,k3);
		double * eB31 = eA22 ;
		FFLAS::fscal(NoField,k3,n2,epsilon,B31,ldb,eB31,n2);
		// T9
		FFLAS::fadd(NoField,k3,n2,eB31,n2,B21,ldb,Y,n2);
		// P9 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// C22 -= P9
		FFLAS::fsubin(NoField,m2,n2,U,n2,C22,ldc);
		// P7 (in C21)
		gemm_bini_232_2(F,m2,n2,k3,A23,lda,B21,ldb,C21,ldc,rec-1,epsilon);
		// C11 = C11  - P7/e
		add(m2,n2,-(double)1/epsilon,C21,ldc,C11,ldc,C11,ldc);
		// C21 =  (P9-P7)/e
		subscalinf(NoField,m2,n2,U,n2,-(double)1/epsilon,C21,ldc);
		// C21 += P5
		FFLAS::faddin(NoField,m2,n2,V,n2,C21,ldc);
		// S10
		FFLAS::fsub(NoField,m2,k3,A13,lda,eA12,k3,X,k3);
		FFLAS::fflas_delete( eA12);
		// T10
		FFLAS::fadd(NoField,k3,n2,eB31,n2,B32,ldb,Y,n2);
		FFLAS::fflas_delete( eA22);
		// P10 (in U)
		gemm_bini_232_2(F,m2,n2,k3,X,k3,Y,n2,U,n2,rec-1,epsilon);
		// C12 += P10
		FFLAS::faddin(NoField,m2,n2,U,n2,C12,ldc);
		// C11 += P10/e
		add(m2,n2,(double)1/epsilon,U,n2,C11,ldc,C11,ldc);


		FFLAS::fflas_delete( X );
		FFLAS::fflas_delete( Y );
		FFLAS::fflas_delete( U );
		FFLAS::fflas_delete( V );


		return C;

	}

#if 0
	template<class Field>
	double *
	gemm_bini_322_sqrt(const Field & F
			   , const size_t m
			   , const size_t n
			   , const size_t k
			   , const double *A , const size_t lda
			   , const double *B , const size_t ldb
			   , double *C , const size_t ldc
			   , int rec
			   , const double & epsilon
			  )
	{
		Givaro::ZRing<double>   NoField;
		// const double p = (double)F.characteristic();
		size_t M = (n>m)?std::min(k,m):std::min(k,n);
		// std::cout << rec << ',' <<  M  << std::endl;
		// Field G(p*p);

		if ( M < TRE  || rec <= 0) {
			// std::cout << "ffw" << std::endl;
			return gemm_fflas(F, m,n,k, A,lda, B,ldb, C, ldc);
			// return gemm_fflas(NoField, m,n,k, A,lda, B,ldb, C, ldc);
		}

		assert(k/2*2==k); // k divisible par 2
		assert(n/3*3==n); // n divisible par 2
		assert(m/2*2==m); // m divisible par 3

		// std::cout << "tested" << std::endl;

		size_t m2 = m/2;
		size_t k2 = k/2;
		size_t n3 = n/3;

		// std::cout << "€ = " << epsilon << std::endl;

		// sub matrices in A
		const double * A11 = A;
		const double * A12 = A   +k2;
		const double * A21 = A   +lda*m2;
		const double * A22 = A21 +k2;

		// sub matrices in C
		double * C11 = C;
		double * C12 = C   +n3;
		double * C13 = C   +2*n3;
		double * C21 = C   +ldc*m2;
		double * C22 = C21 +n3;
		double * C23 = C21 +2*n3;



		// sub matrices in B
		const double * B11 = B;
		const double * B12 = B   +n3;
		const double * B13 = B   +2*n3;
		const double * B21 = B   +ldb*k2;
		const double * B22 = B21 +n3;
		const double * B23 = B21 +2*n3;


		FFLAS::fzero(F,m,n,C,ldc);

		/*
		 * Algo :
		 * S1  := B11  +B22;
		 * S4  := e*B21+B22;
		 * S5  := B11  +e*B21;
		 * S6  := B12  +B23;
		 * S9  := B12  +e*B13;
		 * S3 := e*B13+B23;
		 *
		 * T1  := e*A11 +A22;
		 * T2  := A12   +A22;
		 * T4  := -e*A11+A12;
		 * T5  := e*A21 +A22;
		 * T6  := A11   +e*A22;
		 * T7  := A11   +A21;
		 * T9  := A21   -e*A22;
		 * T3  := A11   +e*A12;
		 *
		 * P1 := S1 *T1;
		 * P2 := T2 * B22;
		 * P10 := A22 * B11;
		 * P4 := S4 *T4;
		 * P5 := S5 *T5;
		 * P6 := S6 *T6;
		 * P7 := T7*B12;
		 * P8 := A11*B23;
		 * P9 := S9 *T9;
		 * P3 := S3*T3;
		 *
		 * C11 := (P1-P2-P10+P4)/e;
		 * C21 := (P10-P5)/(-e) ;
		 * C12 := P4+P6-P3 ;
		 * C22 := P1-P5+P9;
		 * C13 := (-P8+P3)/e;
		 * C23 := (P6-P7-P8+P9)/e;
		 *
		 */


		// P10
		gemm_bini_223_mem(F,m2,n3,k2,A22,lda,B11,ldb,C11,ldc,rec-1,epsilon);
		// S5
		double * Y = FFLAS::fflas_new<double>(k2*n3);
		add(k2,n3,epsilon,B21,ldb,B11,ldb,Y,n3);
		// T5
		double * X = FFLAS::fflas_new<double>(m2*k2);
		add(m2,k2,epsilon,A21,lda,A22,lda,X,k2);
		// P5
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C22,ldc,rec-1,epsilon);
		// C12
		subscal(NoField,m2,n3,C22,ldc,C11,ldc,(double)1/epsilon,C21,ldc);
		// T2
		FFLAS::fadd(NoField,m2,k2,A12,lda,A22,lda,X,k2);
		// P2
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,B22,ldb,C13,ldc,rec-1,epsilon);
		// C11
		FFLAS::faddin(NoField,m2,n3,C13,ldc,C11,ldc);
		// S1
		FFLAS::fadd(NoField,k2,n3,B11,ldb,B22,ldb,Y,n3);
		// T1
		add(m2,k2,epsilon,A11,lda,A22,lda,X,k2);
		// P1
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C12,ldc,rec-1,epsilon);
		// C22
		FFLAS::fsub(NoField,m2,n3,C12,ldc,C22,ldc,C22,ldc);
		// C11
		FFLAS::fsub(NoField,m2,n3,C12,ldc,C11,ldc,C11,ldc);
		// S4
		add(k2,n3,epsilon,B21,ldb,B22,ldb,Y,n3);
		// T4
		add(m2,k2,-epsilon,A11,lda,A12,lda,X,k2);
		// P4
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C12,ldc,rec-1,epsilon);
		// C11
		addscalinf(NoField,m2,n3,C12,ldc,(double)1/epsilon,C11,ldc);
		// S9
		add(k2,n3,epsilon,B13,ldb,B12,ldb,Y,n3);
		// T9
		add(m2,k2,-epsilon,A22,lda,A21,lda,X,k2);
		// P9
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C23,ldc,rec-1,epsilon);
		//  C22
		FFLAS::faddin(NoField,m2,n3,C23,ldc,C22,ldc);
		// S6
		FFLAS::fadd(NoField,k2,n3,B12,ldb,B23,ldb,Y,n3);
		// T6
		add(m2,k2,epsilon,A22,lda,A11,lda,X,k2);
		// P6
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C13,ldc,rec-1,epsilon);
		// C21
		FFLAS::faddin(NoField,m2,n3,C13,ldc,C12,ldc);
		// C32
		FFLAS::faddin(NoField,m2,n3,C13,ldc,C23,ldc);
		// T7
		FFLAS::fadd(NoField,m2,k2,A11,lda,A21,lda,X,k2);
		// P7
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,B12,ldb,C13,ldc,rec-1,epsilon);
		// if (epsilon > 1 && rec == 2) { FFLAS::finit(G,m2,n3,C31,ldc);}
		// C32
		FFLAS::fsubin(NoField,m2,n3,C13,ldc,C23,ldc);
		// S3
		add(k2,n3,epsilon,B13,ldb,B23,ldb,Y,n3);
		// T3
		add(m2,k2,epsilon,A12,lda,A11,lda,X,k2);
		// P3
		gemm_bini_223_mem(F,m2,n3,k2,X,k2,Y,n3,C13,ldc,rec-1,epsilon);
		FFLAS::fflas_delete( Y );
		FFLAS::fflas_delete( X );
		// C21
		FFLAS::fsubin(NoField,m2,n3,C13,ldc,C12,ldc);
		// P8
		Y = FFLAS::fflas_new<double>(m2*n3);
		gemm_bini_223_mem(F,m2,n3,k2,A11,lda,B23,ldb,Y,n3,rec-1,epsilon);
		// C31
		subscalinf(NoField,m2,n3,Y,n3,(double)1/epsilon,C13,ldc);
		// C32
		subscalinf(NoField,m2,n3,Y,n3,(double)1/epsilon,C23,ldc);
		FFLAS::fflas_delete( Y );


		return C;

	}
#endif


} // Rec
} // Protected
} // FFLAS

namespace FFLAS { namespace Protected {

	template<class Field>
	typename Field::Element *
	gemm_bini_p(const Field &F
		    , const size_t m
		    , const size_t n
		    , const size_t k
		    , const typename Field::Element *A
		    , const size_t lda
		    , const typename Field::Element *B
		    , const size_t ldb
		    , typename Field::Element *C
		    , const size_t ldc
		    , int rec
		    , size_t algo
		   )
	{

		assert(k/6*6==k); // k divisible par 6
		assert(n/6*6==n); // n divisible par 6
		assert(m/6*6==m); // m divisible par 6

		// e-formule
		double epsilon = (double) F.characteristic() ;
		switch(algo) {
		case 0 :
			Rec::gemm_bini_322_mem(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			FFLAS::finit_fuzzy(F,m,n,C,ldc);
			// FFLAS::finit(F,m,n,C,ldc);
			break;
		case 1 :
			Rec::gemm_bini_322_0(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			FFLAS::finit_fuzzy(F,m,n,C,ldc);
			// FFLAS::finit(F,m,n,C,ldc);
			break;
		case 2 :
			Rec::gemm_bini_322_2(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			FFLAS::finit_fuzzy(F,m,n,C,ldc);
			break;
		case 3 :
			Rec::gemm_bini_223_mem(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			FFLAS::finit_fuzzy(F,m,n,C,ldc);
			// FFLAS::finit(F,m,n,C,ldc);
			break;
		case 4 :
			Rec::gemm_bini_232_2(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			FFLAS::finit_fuzzy(F,m,n,C,ldc);
			break;
		case 5 :
			Rec::gemm_bini_232_3(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			FFLAS::finit_fuzzy(F,m,n,C,ldc);
			break;
#if 0
		case 8 : {
				 double epsilon2 = sqrt((double)epsilon);
				 std::cout << epsilon2 << std::endl;
				 Rec::gemm_bini_322_sqrt(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon2);
				 // FFLAS::finit_fuzzy(F,m,n,C,ldc);
				 for(size_t i = 0  ; i < m ; ++i) {
					 for(size_t j = 0  ; j < n ; ++j)
						 C[i*ldc+j] = rint(fmod(C[i*ldc+j],epsilon2));
				 }
				 break;
			 }
#endif
		default :
			 std::cout << " not an algo :" << algo << std::endl;;
			 exit(-1);
		}



		return C;

	}

	template<class Field>
	typename Field::Element *
	gemm_bini_e(const Field &F
		    , const size_t m
		    , const size_t n
		    , const size_t k
		    , const typename Field::Element *A
		    , const size_t lda
		    , const typename Field::Element *B
		    , const size_t ldb
		    , typename Field::Element *C
		    , const size_t ldc
		    , int rec
		    , size_t algo
		   )
	{

		assert(k/2*2==k); // k divisible par 2
		assert(n/2*2==n); // n divisible par 2
		assert(m/3*3==m); // m divisible par 3

		// e-formule
		double epsilon = 1./(1<<27);
		switch(algo) {
		case 0 :
			Rec::gemm_bini_322_mem(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			break;
		case 1 :
			Rec::gemm_bini_322_0(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			break;
		case 2 :
			Rec::gemm_bini_322_2(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			break;
		case 3 :
			Rec::gemm_bini_223_mem(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			break;
		case 4 :
			Rec::gemm_bini_232_2(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			break;
		case 5 :
			Rec::gemm_bini_232_3(F,m,n,k,A,lda,B,ldb,C,ldc,rec,epsilon);
			break;
		default :
			std::cout << " not an algo :" << algo << std::endl;;
			exit(-1);
		}


		// vire les e.
		// FFLAS::finit_fuzzy(F,m,n,C,ldc);
		FFLAS::finit_fuzzy(F,m,n,C,ldc);

		return C;

	}

	template<class Field>
	typename Field::Element *
	gemm_compress(const Field &F
		      , const size_t m
		      , const size_t n
		      , const size_t k
		      , const typename Field::Element *A
		      , const size_t lda
		      , const typename Field::Element *B
		      , const size_t ldb
		      , typename Field::Element *C
		      , const size_t ldc
		      , int rec
		      , size_t algo
		     )
	{

		assert(k/6*6==k); // k divisible par 6
		assert(n/6*6==n); // n divisible par 6
		assert(m/6*6==m); // m divisible par 6

		switch(algo) {
		case 0 :
			fgemm_compressed<Field,true>(F,(int)m,(int)n,(int)k,A,(int)lda,B,(int)ldb,C,(int)ldc);
			FFLAS::freduce(F,m,n,C,ldc);
			break;
		case 1 :
			fgemm_compressed<Field,false>(F,(int)m,(int)n,(int)k,A,(int)lda,B,(int)ldb,C,(int)ldc);
			FFLAS::freduce(F,m,n,C,ldc);
			break;
		default :
			std::cout << " not an algo :" << algo << std::endl;;
			exit(-1);
		}



		return C;

	}

} // Protected
} // FFLAS

template<class Field>
void check_equal(const Field & F,int m,int n,
		 typename Field::Element * D,int ldd,
		 typename Field::Element * E,int lde,
		 const char * nomalgo, const char * madescr, int & ok_p)
{
	int faux = 0 ;
	for (int i = 0 ; i < m ; ++i) {
		for (int j = 0 ; j < n ; ++j) {
			if (!F.areEqual(D[i*ldd+j],E[i*lde+j])) {
				++faux ;
			}
		}
	}
	if (faux) {
		std::cout << nomalgo << " " << madescr << " : bad/all = " << faux << '/' << m*n << " ~~  " << (double)faux/(double)(m*n) << std::endl;
	}
	else ok_p ++ ;


#if 1
	if (faux && (n<20)) {
		std::cout << "OK" << std::endl;
		for (int i = 0 ; i < m ; ++i) {
			for (int j = 0 ; j < n ; ++j)
				std::cout << D[i*ldd+j] << ' ';
			std::cout << std::endl;
		}
		std::cout << "KO" << std::endl;
		for (int i = 0 ; i < m ; ++i) {
			for (int j = 0 ; j < n ; ++j)
				std::cout << E[i*lde+j] << ' ';
			std::cout << std::endl;
		}


		std::cout << "Diff" << std::endl;
		for (int i = 0 ; i < m ; ++i) {
			for (int j = 0 ; j < n ; ++j)
				std::cout << D[i*ldd+j]-E[i*lde+j] << ' ';
			std::cout << std::endl;
		}
	}
#endif
}


template<class Field>
void test_algos(const Field &F, int m, int n, int k
	       , const typename Field::Element * A, int lda
	       , const typename Field::Element * B, int ldb
	       , int r
	       , time_v & tim_k, time_v & tim_e , time_v & tim_p
	       , int_v & ok_k, int_v & ok_e, int_v & ok_p
	       , FFLAS::Timer & tim_wd, int & nb_wd
	       , bool with_e
	       , bool with_k
	      )
{
	FFLAS::Timer tmp ;
	typedef typename Field::Element Element;

	Element * D = FFLAS::fflas_new<Element>(m*n);
	Element * C = FFLAS::fflas_new<Element>(m*n);

	tmp.clear();tmp.start();
	fgemm(F,FFLAS::FflasNoTrans,FFLAS::FflasNoTrans,
	      m,n,k, 1, A,k, B,n, 0, D, n);
	tmp.stop(); tim_wd += tmp ; nb_wd ++;

	/*  bini_p */
	if (with_e) {
		for (int algo = 0 ; algo < algos ; ++algo) {
			tmp.clear();tmp.start();
			FFLAS::Protected::gemm_bini_e(F,m,n,k,A,k,B,n,C,n,r,selec[algo]);
			tmp.stop(); tim_e[algo] += tmp ;

			/*  checking  */
			check_equal(F,m,n,D,n,C,n,"bini_e",descr[algo],ok_e[algo]) ;
		}
	}

	/*  compress */
	if (with_k && std::is_same<typename FieldTraits<Field>::category,FFLAS::FieldCategories::ModularTag>::value && (! FieldTraits<Field>::balanced)) {
		for (int algo = 0 ; algo < algos_k ; ++algo) {
			tmp.clear();tmp.start();
			FFLAS::Protected::gemm_compress(F,m,n,k,A,k,B,n,C,n,r,selec_k[algo]);
			tmp.stop(); tim_k[algo] += tmp ;

			/*  checking  */
			check_equal(F,m,n,D,n,C,n,"compress",descr_k[algo],ok_k[algo]) ;


		}
	}

	/*  bini_p */
	for (int algo = 0 ; algo < algos ; ++algo) {
		tmp.clear();tmp.start();
		FFLAS::Protected::gemm_bini_p(F,m,n,k,A,k,B,n,C,n,r,selec[algo]);
		tmp.stop(); tim_p[algo] += tmp ;

		/*  checking  */
		check_equal(F,m,n,D,n,C,n,"bini_p",descr[algo],ok_p[algo]) ;


	}

	FFLAS::fflas_delete(C);
	FFLAS::fflas_delete(D);
}

template<class T>
struct changeField {
	typedef T other ;
};

template<>
struct changeField<Modular<double> > {
	typedef Givaro::Modular<float> other;
};

template<>
struct changeField<ModularBalanced<double> > {
	typedef ModularBalanced<float> other;
};

double descrip(int algo, int_v & ok_e, time_v & tim_e, int iters, const char ** madescr, const char * nom)
{
	int min_e = -1 ;
	double bini_e = -1 ;
	for (int i = 0 ; i < algo ; ++i){
		if (ok_e[i] == (int)iters) {
			double bini1 = tim_e[i].usertime()/(double)ok_e[i] ;
			if (bini_e <  0) {
				bini_e = bini1;
				min_e = (int) i ;
			}
			else if (bini1 < bini_e) {
				min_e  = (int)i ;
				bini_e = bini1 ;
			}
		}
	}
	for (int i = 0 ; i < algo ; ++i){
		if (ok_e[i] == (int)iters) {
			double bini1 = tim_e[i].usertime()/(double)ok_e[i] ;
			std::cout << nom << " ( " << madescr[i] << " ) : " ;
			if ((int)i == min_e) std::cout << " * " ;
			else std::cout << "   ";
			std::cout << bini1  << 's'<<  std::endl;
		}
	}

	return bini_e ;
}


template<class Field>
void test(int m, int k, int n, int p, int r, bool with_e, bool with_k, 	int iters = 4)
{

	typedef typename Field::Element Element;

	Element * A = FFLAS::fflas_new<Element>(m*k);
	Element * B = FFLAS::fflas_new<Element>(n*k);


	Field F(p);
	F.write(std::cout<< " * Field " ) << std::endl;

	typedef typename changeField<Field>::other Field_f  ;
	typedef typename Field_f::Element Element_f ;
	Field_f F_f(p);
	Element_f * A_f = FFLAS::fflas_new<Element_f>(m*k);
	Element_f * B_f = FFLAS::fflas_new<Element_f>(n*k);
	Element_f * C_f = FFLAS::fflas_new<Element_f>(m*n);

#if defined(NOTRANDOM)
	int i0 ;
	int j0 ;
	Element p2 ; F.init(p2,(int)F.mOne/2);
	std::cout << p2 << std::endl;
#warning "not random"
	for (int i = 0 ; i < m ; ++i)
		for (int j = 0 ; j < k ; ++j) {
			i0 = i/(m/3);
			j0 = j/(k/2);
			if      (i0 == 0 and j0 == 0) A[i*k+j] = F.mOne ;
			else if (i0 == 0 and j0 == 1) A[i*k+j] = F.zero ;
			else if (i0 == 1 and j0 == 0) A[i*k+j] = F.mOne ;
			else if (i0 == 1 and j0 == 1) A[i*k+j] = F.mOne ;
			else if (i0 == 2 and j0 == 0) A[i*k+j] = F.mOne ;
			else if (i0 == 2 and j0 == 1) A[i*k+j] = F.mOne ;
			else A[i*k+j] = F.mOne ;
		}
	for (int i = 0 ; i < k ; ++i)
		for (int j = 0 ; j < n ; ++j) {
			i0 = i/(k/2);
			j0 = j/(n/2);
			if      (i0 == 0 and j0 == 0) B[i*n+j] = F.mOne ;
			else if (i0 == 0 and j0 == 1) B[i*n+j] = F.mOne ;
			else if (i0 == 1 and j0 == 0) B[i*n+j] = F.mOne ;
			else if (i0 == 1 and j0 == 1) B[i*n+j] = F.zero ;
			else B[i*n+j] = F.mOne ;

		}
#endif

	time_v tim_e(algos), tim_p(algos), tim_k(algos_k);
	FFLAS::Timer tim_wd; tim_wd.clear();
	FFLAS::Timer tim_wf; tim_wf.clear();
	FFLAS::Timer tmp;
	for (int i = 0 ; i < algos ; ++i) {
		tim_e[i].clear();
		tim_p[i].clear();
	}
	for (int i = 0 ; i < algos_k ; ++i) {
		tim_k[i].clear();
	}

	int_v ok_p(algos,0),  ok_e(algos,0), ok_k(algos_k,0);
	int nb_wd = 0 , nb_wf = 0 ;

	for (int b = 0 ; b < iters ; ++b) {
		std::cout << "iter " << b+1 << " of " << iters << std::endl;
#if not defined(NOTRANDOM)
		FFPACK::RandomMatrix(F,A,m,k,k);
		FFPACK::RandomMatrix(F,B,k,n,n);
#endif
		FFLAS::finit(F_f,m,k,A,k,A_f,k);
		FFLAS::finit(F_f,k,n,B,n,B_f,n);

		tmp.clear();tmp.start();
		fgemm(F_f,FFLAS::FflasNoTrans,FFLAS::FflasNoTrans,
		      m,n,k, 1, A_f,k, B_f,n, 0, C_f, n);
		tmp.stop(); tim_wf += tmp ; nb_wf ++ ;

		test_algos(F,m,n,k,A,k,B,n,r,
			   tim_k,tim_e,tim_p,
			   ok_k,ok_e,ok_p,
			   tim_wd,nb_wd,
			   with_e,with_k);
	}
	std::cout  << std::endl << "results" << std::endl;

	double bini_e = descrip(algos,ok_e,tim_e,iters,descr,"Bini_e");
	double bini_p = descrip(algos,ok_p,tim_p,iters,descr,"Bini_p");
	double bini_k = descrip(algos_k,ok_k,tim_k,iters,descr_k,"Bini_k");


	double t_wd = tim_wd.usertime()/(double)(nb_wd);
	double t_wf = tim_wf.usertime()/(double)(nb_wf);

	std::cout << "Wino d : " << t_wd << 's'<<  std::endl;
	std::cout << "Wino f : " << t_wf << 's'<<  std::endl;
	double wino =  std::min(t_wd,t_wf) ;
	if (bini_e>=0)
		std::cout << "Gain e: " << ((bini_e-wino)/wino)*100 << '%' << std::endl;
	if (bini_p>=0)
		std::cout << "Gain p: " << ((bini_p-wino)/wino)*100 << '%' << std::endl;
	if (bini_k>=0)
		std::cout << "Gain k: " << ((bini_k-wino)/wino)*100 << '%' << std::endl;




	FFLAS::fflas_delete( A );
	FFLAS::fflas_delete( B);

	FFLAS::fflas_delete( A_f );
	FFLAS::fflas_delete( B_f);
	FFLAS::fflas_delete( C_f);
}



int main(int ac, char **av) {
	static int m = 36 ;
	static int n = 12 ;
	static int k = 18 ;
	static int p = 101;
	bool  eps = false ;
	bool  kom = false ;
	int r = 1 ;
	int seed = (int) time(NULL);
    int iters = 4;

	static Argument as[] = {
		{ 'p', "-p P", "Set the field characteristic.",  TYPE_INT , &p },
		{ 'n', "-n N", "Set the number of cols in C.",   TYPE_INT , &n },
		{ 'm', "-m N", "Set the number of rows in C.",   TYPE_INT , &m },
		{ 'k', "-k N", "Set the number of rows in B.",   TYPE_INT , &k },
		{ 'r', "-k N", "Set the recursive number Bini.", TYPE_INT , &r },
		{ 'i', "-i N", "Set the numebr of iterations.", TYPE_INT , &iters },
		{ 's', "-s N", "Set the seed                 .", TYPE_INT , &seed },
		{ 'e', "-e " , "epsilon                 .", TYPE_NONE , &eps },
		{ 'c', "-c " , "compress                .", TYPE_NONE , &kom},
		END_OF_ARGUMENTS
	};
	FFLAS::parseArguments(ac,av,as);

	srand(seed);
	srand48(seed);

	std::cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << std::endl;
	std::cout << "size: " << m << ',' << k << ',' << n << std::endl;
	std::cout << "mod : " << p << std::endl;
	std::cout << "rec : " << r << std::endl;
	std::cout << "seed: " << seed << std::endl;
	std::cout << "thre: " << TRE << std::endl;
	std::cout << "=====================================================" << std::endl;
	test<Modular<double> > (m,k,n,p,r,eps,kom,iters);
	std::cout << "=====================================================" << std::endl;
	test<ModularBalanced<double> > (m,k,n,p,r,eps,kom,iters);
	std::cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" << std::endl;

	return 0;
}
/* -*- mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
// vim:sts=8:sw=8:ts=8:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s

