/*
   This file is part of Numerix.  Numerix 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.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA 
*/

/* +------------------------------------------------------------------------+
   |                                                                        |
   |                      Entiers de longueur arbitraire                    |
   |                                                                        |
   |                           Multiplication                               |
   |                                                                        |
   +------------------------------------------------------------------------+ */

/* M. Quercia, 06/02/2001 */

#include "macros-s.h"

                         /* +------------+
                            |  c <- a*b  |
                            +------------+ */

/* void xn(mul_2)(naturel a, longueur la, ndouble b, naturel c) */
#ifdef have_sn_mul_2
ENTER(sn_mul_2)
#undef  b0
#undef  b1
#undef  ret0
#undef  ret1
#define b0     arg3
#define b1     arg4
#define ret0   (%esp)
#define ret1   4(%esp)

        movl   a,%esi
        movl   la,%ecx
        movl   c,%edi
        movl   b1,%ebx
        testl  %ebx,%ebx
        jz     .Lsn_mul_2_one_digit

        /* multiplication par deux chiffres */
        pushl  $0        /* ret0 = ret1 = 0 */
        pushl  $0
        jecxz  .Lsn_mul_2_two_last
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        negl   %ecx
        .align 4
.Lsn_mul_2_two_loop:
        movl   (%esi,%ecx,4),%eax
        movl   %eax,%ebx
        mull   b0
        addl   ret0,%eax
        adcl   ret1,%edx
        movl   %eax,(%edi,%ecx,4)
        movl   $0,%eax
        adcl   %eax,%eax
        movl   %edx,ret0
        movl   %eax,ret1
        movl   %ebx,%eax
        mull   b1
        addl   %eax,ret0
        adcl   %edx,ret1
        incl   %ecx
        jne    .Lsn_mul_2_two_loop

.Lsn_mul_2_two_last:
        popl   %eax  /* récupère les deux retenues */
        movl   %eax,(%edi)
        popl   %eax
        movl   %eax,4(%edi)
        RETURN

        /* multiplication par un seul chiffre */
.Lsn_mul_2_one_digit:
        xorl   %ebx,%ebx
        jecxz  .Lsn_mul_2_one_last
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        negl   %ecx
        .align 4        
.Lsn_mul_2_one_loop:
        movl   b0,%eax
        mull   (%esi,%ecx,4)
        addl   %ebx,%eax
        adcl   $0,%edx
        movl   %eax,(%edi,%ecx,4)
        movl   %edx,%ebx
        incl   %ecx
        jne    .Lsn_mul_2_one_loop
.Lsn_mul_2_one_last:
        movl   %ebx,(%edi)
        movl   $0,4(%edi)

EXIT(sn_mul_2)
#endif

                        /* +---------------+
                           |  idem dans Z  |
                           +---------------+ */

/* void xz(mul_2)(entier *a, zdouble b, entier *c) */
#ifdef have_sz_mul_2
ENTER(sz_mul_2)
#undef  b0
#undef  b1
#undef  ret0
#undef  ret1
#define ret0   (%esp)
#define ret1   4(%esp)
#define b0     8(%esp)
#define b1     12(%esp)

        /* prévoit la longueur et le signe du résultat */
        movl   za,%esi
        movl   (%esi),%ecx
        movl   arg4,%edi
        movl   arg2,%eax
        movl   arg3,%ebx
        testl  %ebx,%ebx
        jns    .Lsz_mul_2_bpos
        xorl   $SIGN_m,%ecx
        negl   %eax
        adcl   $0,%ebx
        negl   %ebx
.Lsz_mul_2_bpos:
        jz     .Lsz_mul_2_one_digit

        /* multiplication par deux chiffres */
        pushl  %ebx      /* sauve le multiplicande */
        pushl  %eax
        pushl  $0        /* ret0 = ret1 = 0 */
        pushl  $0
        leal   (,%ecx,1),%eax
        movl   %eax,(%edi)
        andl   $LONG_m,%ecx
        jecxz  .Lsz_mul_2_two_last
        leal   4(%esi,%ecx,4),%esi
        leal   4(%edi,%ecx,4),%edi
        negl   %ecx
        .align 4
.Lsz_mul_2_two_loop:
        movl   (%esi,%ecx,4),%eax
        movl   %eax,%ebx
        mull   b0
        addl   ret0,%eax
        adcl   ret1,%edx
        movl   %eax,(%edi,%ecx,4)
        movl   $0,%eax
        adcl   %eax,%eax
        movl   %edx,ret0
        movl   %eax,ret1
        movl   %ebx,%eax
        mull   b1
        addl   %eax,ret0
        adcl   %edx,ret1
        incl   %ecx
        jne    .Lsz_mul_2_two_loop

.Lsz_mul_2_two_last:
        popl   %eax  /* récupère les deux retenues */
        movl   %eax,(%edi)
        movl   arg4,%esi
        popl   %eax
        testl  %eax,%eax
        jz     .Lsz_mul_2_two_done
        movl   %eax,4(%edi)
        incl   (%esi)
.Lsz_mul_2_two_done:
        RETURN

        /* multiplication par un seul chiffre */
.Lsz_mul_2_one_digit:
        testl  %eax,%eax
        jz     .Lsz_mul_2_null
        pushl  %eax
        movl   %ecx,(%edi)
        andl   $LONG_m,%ecx
        jecxz  .Lsz_mul_2_one_last
        leal   4(%esi,%ecx,4),%esi
        leal   4(%edi,%ecx,4),%edi
        negl   %ecx
        .align 4        
.Lsz_mul_2_one_loop:
        movl   (%esp),%eax
        mull   (%esi,%ecx,4)
        addl   %ebx,%eax
        adcl   $0,%edx
        movl   %eax,(%edi,%ecx,4)
        movl   %edx,%ebx
        incl   %ecx
        jne    .Lsz_mul_2_one_loop

.Lsz_mul_2_one_last:
        movl   arg4,%esi
        testl  %ebx,%ebx
        jz     .Lsz_mul_2_one_done
        movl   %ebx,(%edi)
        incl   (%esi)
.Lsz_mul_2_one_done:
        RETURN

.Lsz_mul_2_null:
        movl   %eax,(%edi)

EXIT(sz_mul_2)
#endif

                /* +-------------------------------+
                   |  c <- a*b, algorithme en n^2  |
                   +-------------------------------+ */

#ifdef have_sn_mul_n2
/* c <- a*b, algorithme en n^2 */
/* void xn(mul_n2)(naturel a, longueur la, naturel b, longueur lb, naturel c) */
ENTER(sn_mul_n2)

/* Variables locales */
#undef  mul0
#undef  loc_lb
#undef  loc_la
#undef  loc_c 
#undef  loc_b 
#undef  loc_a 
#define mul0    -16(%ebp)
#define loc_lb  -20(%ebp)
#define loc_la  -24(%ebp)
#define loc_c   -28(%ebp)
#define loc_b   -32(%ebp)
#define loc_a   -36(%ebp)

        /* classe les arguments de sorte que la >= lb */
        movl   a,%esi
        movl   la,%ecx
        movl   b,%edi
        movl   lb,%edx
        movl   c,%ebx
        cmpl   %edx,%ecx
        jae    .Lsn_mul_n2_a_bigger
        xchgl  %esi,%edi
        xchgl  %ecx,%edx
.Lsn_mul_n2_a_bigger:
.Lsn_mul_n2_aux_entry:
        testl  %edx,%edx
        jz     .Lsn_mul_n2_zero

        /* init variables locales */
        leal   (%esi,%ecx,4),%esi
        leal   (%ebx,%ecx,4),%ebx
        negl   %ecx
        pushl  (%edi)
        pushl  %edx
        pushl  %ecx
        pushl  %ebx
        pushl  %edi
        pushl  %esi

        /* traite le premier chiffre de b à part pour ne pas initialiser c */
        xorl   %edi,%edi
        .align 4        
.Lsn_mul_n2_one_loop:
        movl   mul0,%eax
        mull   (%esi,%ecx,4)
        addl   %edi,%eax
        adcl   $0,%edx
        movl   %eax,(%ebx,%ecx,4)
        movl   %edx,%edi
        incl   %ecx
        jne    .Lsn_mul_n2_one_loop

        /* chiffres suivants */
        .align 4
.Lsn_mul_n2_loop0:
        movl   %edx,(%ebx)
        decl   loc_lb
        jz     .Lsn_mul_n2_exit

        movl   loc_b,%edi
        leal   4(%edi),%edi
        movl   %edi,loc_b
        movl   (%edi),%edi
        movl   %edi,mul0
        movl   loc_a,%esi
        movl   loc_c,%ebx
        leal   4(%ebx),%ebx
        movl   %ebx,loc_c
        movl   loc_la,%ecx
        xorl   %edi,%edi

        .align 4
.Lsn_mul_n2_loop1:
        movl   mul0,%eax
        mull   (%esi,%ecx,4)
        addl   %edi,%eax
        adcl   $0,%edx
        addl   %eax,(%ebx,%ecx,4)
        adcl   $0,%edx
        movl   %edx,%edi
        incl   %ecx
        jne    .Lsn_mul_n2_loop1
        jmp    .Lsn_mul_n2_loop0

        /* multiplication par 0 chiffre */
        .align 4
.Lsn_mul_n2_zero:
        movl   %edx,-4(%ebx,%ecx,4)
        loop   .Lsn_mul_n2_zero

EXIT(sn_mul_n2)
#endif

	           /* +-------------------------+
                      |  Multiplication dans Z  |
                      +-------------------------+ */

#ifdef have_sz_mul_n2
/* c <- a*b, longueur(c) >= la+lb */
/* void xz(mul_n2)(entier *a, entier *b, entier *c) */
ENTER(sz_mul_n2)

        /* sépare les signes et les longueurs */
        movl   za,%esi
        movl   zb,%edi
        movl   (%esi),%edx
        movl   %edx,%eax
        movl   (%edi),%ecx
        xorl   %ecx,%eax
        movl   zc,%ebx
        andl   $LONG_m,%ecx
        andl   $LONG_m,%edx

        /* classe les arguments de sorte que la >= lb */
        cmpl   %edx,%ecx
        jbe    .Lsz_mul_n2_a_bigger
        xchgl  %esi,%edi
        xchgl  %ecx,%edx
.Lsz_mul_n2_a_bigger:

        /* traite à part les petites multiplications */
        cmpl   $2,%ecx
        ja     .Lsz_mul_n2_big_b
        je     .Lsz_mul_n2_b2
        jecxz  .Lsz_mul_n2_b0

        /* multiplication par un chiffre */
        pushl  4(%edi)              /* sauve multiplicateur */
        xorl   $1,%eax              /* longueur et signe résultat */
        movl   %eax,(%ebx)
        movl   %edx,%ecx
        leal   4(%esi,%edx,4),%esi  /* pointe en fin de nombre */
        leal   4(%ebx,%edx,4),%ebx
        negl   %ecx
        xorl   %edi,%edi            /* ret = 0 */

        .align 4                    /* multiplie chaque chiffre de a */
.Lsz_mul_n22_one_loop:
        movl   (%esp),%eax
        mull   (%esi,%ecx,4)
        addl   %edi,%eax
        adcl   $0,%edx
        movl   %eax,(%ebx,%ecx,4)
        movl   %edx,%edi
        incl   %ecx
        jne    .Lsz_mul_n22_one_loop

        testl  %edx,%edx            /* dernière retenue = 0 ? */
        jz     .Lsz_mul_n2_one_ok
        movl   %edx,(%ebx)          /* sinon rallonge c d'un chiffre */
        movl   zc,%ebx
        incl   (%ebx)
.Lsz_mul_n2_one_ok:
        RETURN

        /* multiplication par zéro chiffre */
.Lsz_mul_n2_b0:
        movl   %ecx,(%ebx)
        RETURN

        /* multiplication par deux chiffres */
.Lsz_mul_n2_b2:
        xorl   $2,%eax              /* longueur et signe du résultat */
        incl   %eax
        movl   %eax,(%ebx)
        pushl  8(%edi)              /* sauve multiplicateur */
        pushl  4(%edi)
        pushl  $0                   /* ret0 = ret1 = 0 */
        pushl  $0
        movl   %edx,%ecx
        leal   4(%esi,%edx,4),%esi  /* pointe en fin de nombre */
        leal   4(%ebx,%edx,4),%ebx
        negl   %ecx
        .align 4
.Lsz_mul_n22_two_loop:
        movl   (%esi,%ecx,4),%eax   /* multiplie chaque chiffre de a */
        movl   %eax,%edi
        mull   8(%esp)
        addl   (%esp),%eax
        adcl   4(%esp),%edx
        movl   %eax,(%ebx,%ecx,4)
        movl   $0,%eax
        adcl   %eax,%eax
        movl   %edx,(%esp)
        movl   %eax,4(%esp)
        movl   %edi,%eax
        mull   12(%esp)
        addl   %eax,(%esp)
        adcl   %edx,4(%esp)
        incl   %ecx
        jne    .Lsz_mul_n22_two_loop

        movl   (%esp),%eax          /* récupère les deux retenues */
        movl   %eax,(%ebx)
        movl   4(%esp),%eax
        testl  %eax,%eax
        jz     .Lsz_mul_n2_two_ok
        movl   %eax,4(%ebx)        /* allonge c si ret1 <> 0 */
        movl   zc,%ebx
        incl   (%ebx)
.Lsz_mul_n2_two_ok:
        RETURN

        /* multiplication par au moins trois chiffres */
.Lsz_mul_n2_big_b:
	/* pointe sur les naturels */
        leal   4(%esi),%esi
        leal   4(%edi),%edi
        leal   4(%ebx),%ebx
        
        /* longueur et signe du résultat */
        andl   $SIGN_m,%eax
        addl   %ecx,%eax
        addl   %edx,%eax
        movl   %eax,-4(%ebx)

	/* copie a dans la pile s'il va être écrasé */
	cmpl   %esi,%ebx
	jnz    .Lsz_mul_n2_a_free
	movl   %edx,%eax
	.align 4
.Lsz_mul_n2_copy_a:
	pushl  -4(%esi,%eax,4)
	decl   %eax
	jne    .Lsz_mul_n2_copy_a
	movl   %esp,%esi
	cmpl   %edi,%ebx
	jnz    .Lsz_mul_n2_b_free
	movl   %esp,%edi
	jmp    .Lsz_mul_n2_b_free
.Lsz_mul_n2_a_free:	

	/* copie b dans la pile s'il va être écrasé */
	cmpl   %edi,%ebx
	jnz    .Lsz_mul_n2_b_free
	movl   %ecx,%eax
	.align 4
.Lsz_mul_n2_copy_b:
	pushl  -4(%edi,%eax,4)
	decl   %eax
	jne    .Lsz_mul_n2_copy_b
	movl   %esp,%edi
.Lsz_mul_n2_b_free:	

	/* multiplication en n2 ou par Karatsuba */
        pushl  %ebx
        pushl  %ecx
        pushl  %edi
        pushl  %edx
        pushl  %esi
	call   sn_karamul

        /* vérifie la longueur et recopie l'entête du résultat */
.Lsz_mul_n2_lc:
        movl   -4(%ebx),%ecx
        andl   $LONG_m,%ecx
        testl  $-1,-4(%ebx,%ecx,4)
        jnz    .Lsz_mul_n2_exit
        decl   -4(%ebx)

EXIT(sz_mul_n2)
#endif

                  /* +-------------------------------------+
                     |  c <- a*b, algorithme de Karatsuba  |
                     +-------------------------------------+ */

/* void xn(karamul)(naturel a, longueur la, naturel b, longueur lb, naturel c) */
#ifdef have_sn_karamul
ENTER(sn_karamul)

/* variables locales */
#undef  l0
#undef  l1
#undef  l2
#undef  lu
#undef  lv
#undef  aa
#undef  bb
#undef  s
#undef stack_size
#define l0 -16(%ebp)
#define l1 -20(%ebp)
#define l2 -24(%ebp)
#define lu -28(%ebp)
#define lv -32(%ebp)
#define aa -36(%ebp)
#define bb -40(%ebp)
#define s  -44(%ebp)
#define stack_size 32
					
        /* classe les arguments de sorte que la >= lb */
        movl   a,%esi
        movl   la,%ecx
        movl   b,%edi
        movl   lb,%edx
        movl   c,%ebx
        cmpl   %edx,%ecx
        jae    .Lsn_karamul_a_bigger
        xchgl  %esi,%edi
        xchgl  %ecx,%edx
.Lsn_karamul_a_bigger:

	/* élimine les petits produits */
        cmpl   $klim,%edx
        jb     .Lsn_mul_n2_aux_entry

	/* si lb <= la/2, distribue par tranches de lb chiffres */
.Lsn_karamul_aux_entry:	
	movl   %ecx,%eax
	shrl   $1,%ecx
	cmpl   %ecx,%edx
	ja     .Lsn_karamul_big_b
	pushl  %edx   /* l0 <- lb */

	/* hi(c) <- hi(a)*b */
	leal   (%ebx,%edx,4),%ebx
	leal   (%esi,%edx,4),%esi
	subl   %edx,%eax
	pushl  %ebx
	pushl  %edx
	pushl  %edi
	pushl  %eax
	pushl  %esi
	call   sn_karamul
	addl   $20,%esp

	/* calcule low(a)*b */
	movl   l0,%ecx
	movl   %ecx,%eax
	negl   %ecx
	leal   (%ebx,%ecx,4),%ebx
	leal   (%esi,%ecx,4),%esi
	leal   (%esp,%ecx,8),%esp
	pushl  %esp
	pushl  %eax
	pushl  %edi
	pushl  %eax
	pushl  %esi
	call   sn_karamul
	leal   20(%esp),%esi

	/* copie la partie basse dans c */
	movl   l0,%ecx
	movl   %ecx,%edx
	.align 4
.Lsn_karamul_cpy_low_c:
	movl   -4(%esi,%ecx,4),%eax
	movl   %eax,-4(%ebx,%ecx,4)
	loop   .Lsn_karamul_cpy_low_c

	/* ajoute la partie haute */
	movl   %edx,%ecx
	leal   (%esi,%edx,8),%esi
	leal   (%ebx,%edx,8),%ebx
	negl   %ecx
	clc
	.align 4
.Lsn_karamul_add_high_c:
	movl   (%esi,%ecx,4),%eax
	adcl   %eax,(%ebx,%ecx,4)
	incl   %ecx
	jne    .Lsn_karamul_add_high_c
	jnc    .Lsn_karamul_small_b_done
.Lsn_karamul_add_ret_c:
	incl   %ecx
	incl   -4(%ebx,%ecx,4)
	jz     .Lsn_karamul_add_ret_c
.Lsn_karamul_small_b_done:
	RETURN

	/* ici lb > la/2, prépare la dichotomie */
.Lsn_karamul_big_b:
	subl   $stack_size,%esp
        movl   %ecx,l0
        subl   %ecx,%eax
        movl   %eax,l1
	subl   %ecx,%edx
	movl   %edx,l2
	movl   %esi,aa
	movl   %edi,bb

        /* c <- |a0-a1| */
	leal   (%esi,%ecx,4),%edi /* edi -> a1 */
	movl   %eax,%edx
	subl   %ecx,%edx
	je     .Lsn_karamul_cmp_a01
	cmpl   $0,(%edi,%ecx,4)     /* si l1 > l0 chiffre de tête = 0 ? */
	jne    .Lsn_karamul_a1_bigger  /* sinon a1 > a0 */
	xorl   %edx,%edx

.Lsn_karamul_cmp_a01:                  /* si l1 = l0 compare les chiffres de tête */
	movl   -4(%esi,%ecx,4),%eax
	cmpl   -4(%edi,%ecx,4),%eax
	jb     .Lsn_karamul_a1_bigger
	ja     .Lsn_karamul_a1_smaller
	loop   .Lsn_karamul_cmp_a01
	movl   $0,s
	jmp    .Lsn_karamul_mul_a0_b0 /* saute si a0 = a1 */

	/* ici a0 <> a1 */
.Lsn_karamul_a1_smaller:               /* a0 > a1, les échange */
	movl   $-1,s
	xchgl  %esi,%edi
	jmp    .Lsn_karamul_sub_a0_a1
.Lsn_karamul_a1_bigger:	             /* a0 < a1, calcule a1-a0 */
	movl   $1,s

.Lsn_karamul_sub_a0_a1:
	movl   %ecx,lu
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        leal   (%ebx,%ecx,4),%ebx
	negl   %ecx
	clc
	.align 4

.Lsn_karamul_sub_a01:                  /* soustrait les chiffres communs */
	movl   (%edi,%ecx,4),%eax
	sbbl   (%esi,%ecx,4),%eax
	movl   %eax,(%ebx,%ecx,4)
	incl   %ecx
	jne    .Lsn_karamul_sub_a01
	decl   %edx
	jnz    .Lsn_karamul_sub_a01_done
	movl   (%edi),%eax           /* si a1 est plus long, dernier chiffre */
	sbbl   $0,%eax
	movl   %eax,(%ebx)
	incl   lu
	leal   4(%ebx),%ebx
.Lsn_karamul_sub_a01_done:

        /* c+lu <- |b0-b1| */
	movl   bb,%esi
	movl   l0,%ecx
	movl   l2,%edx
	leal   (%esi,%ecx,4),%edi /* edi -> b1 */
.Lsn_karamul_tst_b0:
	cmpl   %ecx,%edx
	je     .Lsn_karamul_cmp_b01
	ja     .Lsn_karamul_big_b1
	cmpl   $0,-4(%esi,%ecx,4)   /* si l0 > l2, chiffre de tête de b0 = 0 ? */
	jne    .Lsn_karamul_b1_smaller
	decl   %ecx
	jmp    .Lsn_karamul_tst_b0

.Lsn_karamul_big_b1:
	cmpl   $0,(%edi,%ecx,4)     /* si l2 > l0, chiffre de tête de b1 = 0 ? */
	jne    .Lsn_karamul_b1_bigger  /* sinon b1 > b0 */
	decl   %edx

.Lsn_karamul_cmp_b01:                  /* si l1 = l0 compare les chiffres de tête */
	movl   -4(%esi,%ecx,4),%eax
	cmpl   -4(%edi,%ecx,4),%eax
	jb     .Lsn_karamul_b1_bigger
	ja     .Lsn_karamul_b1_smaller
	decl   %edx
	loop   .Lsn_karamul_cmp_b01
	movl   $0,s
	jmp    .Lsn_karamul_mul_a0_b0 /* saute si b0 = b1 */

	/* ici b0 <> b1 */
.Lsn_karamul_b1_smaller:               /* b0 > b1, les échange */
	negl   s
	xchgl  %esi,%edi
	xchgl  %edx,%ecx
.Lsn_karamul_b1_bigger:	             /* b0 < b1, calcule b1-b0 */

	movl   %edx,lv
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        leal   (%ebx,%ecx,4),%ebx
	subl   %ecx,%edx
	negl   %ecx
	clc
	.align 4

.Lsn_karamul_sub_b01:                  /* soustrait les chiffres communs */
	movl   (%edi,%ecx,4),%eax
	sbbl   (%esi,%ecx,4),%eax
	movl   %eax,(%ebx,%ecx,4)
	incl   %ecx
	jne    .Lsn_karamul_sub_b01
	decl   %edx
	js     .Lsn_karamul_sub_b01_done
	.align 4
.Lsn_karamul_ret_b01:
	movl   (%edi,%ecx,4),%eax  /* propage la retenue */
	sbbl   $0,%eax
	movl   %eax,(%ebx,%ecx,4)
	incl   %ecx
	decl   %edx
	jns    .Lsn_karamul_ret_b01
.Lsn_karamul_sub_b01_done:

	/* calcule (a0-a1)(b0-b1) */
	movl   lu,%eax
	movl   lv,%ecx
	movl   %eax,%edx
	addl   %ecx,%edx
	addl   %edx,%edx
	addl   %edx,%edx
	subl   %edx,%esp

	pushl  %esp
	movl   c,%ebx
	pushl  %eax
	pushl  %ebx
	leal   (%ebx,%eax,4),%ebx
	pushl  %ecx
	pushl  %ebx
	call   sn_karamul
	addl   $20,%esp

	/* calcule a0b0 */
.Lsn_karamul_mul_a0_b0:
	movl   l0,%ebx
	pushl  c
	pushl  %ebx
	pushl  bb
	pushl  %ebx
	pushl  aa
	call   sn_karamul

	/* calcule a1b1 */
	addl   %ebx,%ebx
	addl   %ebx,%ebx
	addl   %ebx,(%esp)
	addl   %ebx,8(%esp)
	addl   %ebx,%ebx
	addl   %ebx,16(%esp)
	movl   l1,%eax
	movl   %eax,4(%esp)
	movl   l2,%eax
	movl   %eax,12(%esp)
	call   sn_karamul
	
/*  	addition croisée :
  
  		l0      l0      l0      l0     l1+l2-2l0
  	     <------> <-----> <-----> <-----> <--->
  	     +-------+-------+-------+-------+-----+
  	     | alpha | beta  | gamma | delta | eps |
  	     +-------+-------+-------+-------+-----+
  		     | alpha | beta  |
  		     +-------+-------+-----+
  		     | gamma | delta | eps |
  		     +-------+-------+-----+
*/
	
	/* gamma <- gamma + beta          */
	/* edi -> fin de beta             */
	/* esi -> fin de gamma            */
	movl   l0,%ecx
	movl   16(%esp),%edi
	leal   (%edi,%ecx,4),%esi
	negl   %ecx
	clc
	.align 4

.Lsn_karamul_gb:
	movl   (%edi,%ecx,4),%eax
	adcl   %eax,(%esi,%ecx,4)
	incl   %ecx
	jne    .Lsn_karamul_gb
	jnc    .Lsn_karamul_gb_done
.Lsn_karamul_gb_ret:
	adcl   $0,(%esi,%ecx,4)
	incl   %ecx
	jc     .Lsn_karamul_gb_ret
.Lsn_karamul_gb_done:

	/* beta <- alpha + gamma           */
	/* edi -> fin de beta (déjà fait)  */
	/* esi -> fin de gamma (déjà fait) */
	/* ebx -> fin de alpha             */
	movl   l0,%ecx
	negl   %ecx
	leal   (%edi,%ecx,4),%ebx
	clc
	.align 4

.Lsn_karamul_ab:
	movl   (%esi,%ecx,4),%eax
	adcl   (%ebx,%ecx,4),%eax
	movl   %eax,(%edi,%ecx,4)
	incl   %ecx
	jne    .Lsn_karamul_ab
	pushf			/* sauve retenue */

	/* gamma:delta <- gamma:delta + delta:eps */
	/* esi -> fin de gamma:eps                */
	/* ebx -> fin de delta:eps                */
	movl   l1,%ecx
	addl   l2,%ecx
	subl   l0,%ecx
	leal   (%esi,%ecx,4),%ebx
	leal   (%edi,%ecx,4),%esi
	negl   %ecx
	popf			/* récupère retenue précédente */
	.align 4

.Lsn_karamul_gd:
	movl   (%ebx,%ecx,4),%eax
	adcl   %eax,(%esi,%ecx,4)
	incl   %ecx
	jne    .Lsn_karamul_gd
	jnc    .Lsn_karamul_gd_done
.Lsn_karamul_gd_ret:
	adcl   $0,(%esi,%ecx,4)
	incl   %ecx
	jc     .Lsn_karamul_gd_ret
.Lsn_karamul_gd_done:

	leal   20(%esp),%esp
	movl   s,%eax
	testl  %eax,%eax
	jz     .Lsn_karamul_exit
	js     .Lsn_karamul_x2_neg

	/* retranche (a0-a1)(b0-b1) */
	/* edi -> beta              */
	/* esp -> (a0-a1)(b0-b1)    */
	movl   l0,%ecx
	negl   %ecx
	leal   (%edi,%ecx,4),%edi
	movl   lu,%ecx
	addl   lv,%ecx
	clc
	.align 4

.Lsn_karamul_sub_x2:
	popl   %eax
	sbbl   %eax,(%edi)
	leal   4(%edi),%edi
	loop   .Lsn_karamul_sub_x2
	jnb    .Lsn_karamul_exit
.Lsn_karamul_sub_x2_ret:
	sbbl   $0,(%edi)	
	leal   4(%edi),%edi
	jb     .Lsn_karamul_sub_x2_ret
	jmp    .Lsn_karamul_exit

.Lsn_karamul_x2_neg:
	/* ajoute (a0-a1)(b0-b1) */
	/* edi -> beta           */
	/* esp -> (a0-a1)(b0-b1) */
	movl   l0,%ecx
	negl   %ecx
	leal   (%edi,%ecx,4),%edi
	movl   lu,%ecx
	addl   lv,%ecx
	clc
	.align 4

.Lsn_karamul_add_x2:
	popl   %eax
	adcl   %eax,(%edi)
	leal   4(%edi),%edi
	loop   .Lsn_karamul_add_x2
	jnb    .Lsn_karamul_exit
.Lsn_karamul_add_x2_ret:
	adcl   $0,(%edi)	
	leal   4(%edi),%edi
	jb     .Lsn_karamul_add_x2_ret

	/* terminé */
EXIT(sn_karamul)
#endif

