/*
   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                    |
   |                                                                        |
   |                              Addition/Soustraction                     |
   |                                                                        |
   +------------------------------------------------------------------------+ */

/* M. Quercia, 31/01/2001 */

#include "macros-s.h"


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

/* void xn(add)(naturel a, longueur la, naturel b, longueur lb, naturel c) */
#ifdef have_sn_add
ENTER(sn_add)

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

        /* additionne les chiffres communs */
        subl   %ecx,%edx
        jecxz  .Lsn_add_copy
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        leal   (%ebx,%ecx,4),%ebx
        negl   %ecx
        clc
        .align 4
.Lsn_add_add_digits:
        movl   (%esi,%ecx,4),%eax
        adcl   (%edi,%ecx,4),%eax
        movl   %eax,(%ebx,%ecx,4)
        incl   %ecx
        jne    .Lsn_add_add_digits
        jnc    .Lsn_add_copy

        /* propage la retenue */
        testl  %edx,%edx
        jz     .Lsn_add_last_ret
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsn_add_ret_loop:
        movl   (%esi,%edx,4),%eax
        incl   %eax
        jne    .Lsn_add_copy1
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jne    .Lsn_add_ret_loop
.Lsn_add_last_ret:
        movl   $1,(%ebx)
        RETURN

        /* copie la fin de a */
.Lsn_add_copy:
        testl  %edx,%edx
        jz     .Lsn_add_finish
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsn_add_copy_loop:
        movl   (%esi,%edx,4),%eax
.Lsn_add_copy1:
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jne    .Lsn_add_copy_loop
.Lsn_add_finish:
        movl   %ecx,(%ebx)
        
EXIT(sn_add)
#endif

                     /* +--------------------+
                        |  c <- a-b, a >= b  |
                        +--------------------+ */

/* void xn(sub)(naturel a, longueur la, naturel b, longueur lb, naturel c) */
#ifdef have_sn_sub
ENTER(sn_sub)

        movl   a,%esi
        movl   la,%edx
        movl   b,%edi
        movl   lb,%ecx
        movl   c,%ebx
        subl   %ecx,%edx

        /* soustraction des chiffres communs */
        jecxz  .Lsn_sub_copy
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        leal   (%ebx,%ecx,4),%ebx
        negl   %ecx
        clc
        .align 4
.Lsn_sub_sub_digits:
        movl   (%esi,%ecx,4),%eax
        sbbl   (%edi,%ecx,4),%eax
        movl   %eax,(%ebx,%ecx,4)
        incl   %ecx
        jne    .Lsn_sub_sub_digits
        jnb    .Lsn_sub_copy

        /* propage la retenue */
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsn_sub_ret_loop:
        movl   (%esi,%edx,4),%eax
        subl   $1,%eax
        jnb    .Lsn_sub_copy1
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jne    .Lsn_sub_ret_loop
        RETURN

        /* copie la fin de a */
.Lsn_sub_copy:
        testl  %edx,%edx
        jz     .Lsn_sub_exit
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsn_sub_copy_loop:
        movl   (%esi,%edx,4),%eax
.Lsn_sub_copy1:
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jne    .Lsn_sub_copy_loop
        
EXIT(sn_sub)
#endif

          /* +------------------------------------------+
             |  c <- a - b,longueur(c) >= max(la,lb)+1  |
             +------------------------------------------+ */

/* void sz_sub(entier * a, entier * b, entier * c) */
#ifdef have_sz_addsub
ENTER(sz_sub)
        movl   zb,%edi
        movl   (%edi),%ecx
        xorl   $SIGN_m,%ecx
        jmp    .Lsz_addsub


          /* +-------------------------------------------+
             |  c <- a + b, longueur(c) >= max(la,lb)+1  |
             +-------------------------------------------+ */

/* void sz_add(entier *a, entier *b, entier *c) */
ENTER(sz_add)
        movl   zb,%edi
        movl   (%edi),%ecx

        /* aiguillage selon les signes */
.Lsz_addsub:
        movl   za,%esi
        movl   (%esi),%edx
        movl   %ecx,%eax
        movl   %edx,%ebx
        xorl   %edx,%eax
        andl   $SIGN_m,%ebx
        andl   $LONG_m,%ecx
        andl   $LONG_m,%edx
        testl  %eax,%eax
        js     .Lsz_soustraction

        /* classe les arguments de sorte que la >= lb */
.Lsz_addition:
        cmpl   %ecx,%edx
        jae    .Lsz_add_a_bigger
        xchgl  %esi,%edi
        xchgl  %ecx,%edx
.Lsz_add_a_bigger:
        orl    %edx,%ebx
        pushl  %ebx
        movl   zc,%ebx

        /* additionne les chiffres communs */
        subl   %ecx,%edx
        leal   4(%esi,%ecx,4),%esi
        leal   4(%edi,%ecx,4),%edi
        leal   4(%ebx,%ecx,4),%ebx
        jecxz  .Lsz_add_copy
        negl   %ecx
        clc
        .align 4
.Lsz_add_add_digits:
        movl   (%esi,%ecx,4),%eax
        adcl   (%edi,%ecx,4),%eax
        movl   %eax,(%ebx,%ecx,4)
        incl   %ecx
        jne    .Lsz_add_add_digits
        jnc    .Lsz_add_copy

        /* propage la retenue */
        testl  %edx,%edx
        jz     .Lsz_add_last_ret
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsz_add_ret_loop:
        movl   (%esi,%edx,4),%eax
        incl   %eax
        jne    .Lsz_add_copy1
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jne    .Lsz_add_ret_loop
.Lsz_add_last_ret:
        movl   $1,(%ebx)
        popl   %eax
        incl   %eax
        movl   zc,%ebx
        movl   %eax,(%ebx)
        RETURN

        /* copie la fin de a */
.Lsz_add_copy:
        testl  %edx,%edx
        jz     .Lsz_add_finish
        cmpl   %esi,%ebx
        jz     .Lsz_add_finish
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsz_add_copy_loop:
        movl   (%esi,%edx,4),%eax
.Lsz_add_copy1:
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jne    .Lsz_add_copy_loop
.Lsz_add_finish:
        popl   %eax
        movl   zc,%ebx
        movl   %eax,(%ebx)
        RETURN

        /* classe les arguments de sorte que a >= b */
.Lsz_soustraction:
        cmpl   %ecx,%edx
        ja     .Lsz_sub_a_bigger
        jb     .Lsz_sub_a_smaller

        /* mêmes longueurs, compare les chiffres de tête */
        jecxz  .Lsz_sub_equal
        .align 4
.Lsz_sub_cmp_ab:
        movl   (%esi,%edx,4),%eax
        cmpl   (%edi,%ecx,4),%eax
        ja     .Lsz_sub_a_bigger
        jb     .Lsz_sub_a_smaller
        decl   %edx
        loop   .Lsz_sub_cmp_ab
.Lsz_sub_equal:
        movl   zc,%ebx     /* si a = b, retultat = 0 */
        movl   %ecx,(%ebx)
        RETURN

.Lsz_sub_a_smaller:
        xchgl  %esi,%edi
        xchgl  %ecx,%edx
        xorl   $SIGN_m,%ebx
.Lsz_sub_a_bigger:
        orl    %edx,%ebx
        pushl  %ebx
        movl   zc,%ebx
        subl   %ecx,%edx

        /* soustraction des chiffres communs */
        leal   4(%esi,%ecx,4),%esi
        leal   4(%edi,%ecx,4),%edi
        leal   4(%ebx,%ecx,4),%ebx
        jecxz  .Lsz_sub_copy
        negl   %ecx
        clc
        .align 4
.Lsz_sub_sub_digits:
        movl   (%esi,%ecx,4),%eax
        sbbl   (%edi,%ecx,4),%eax
        movl   %eax,(%ebx,%ecx,4)
        incl   %ecx
        jne    .Lsz_sub_sub_digits
        jnb    .Lsz_sub_copy

        /* propage la retenue */
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsz_sub_ret_loop:
        movl   (%esi,%edx,4),%eax
        subl   $1,%eax
        jnb    .Lsz_sub_copy1
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jmp    .Lsz_sub_ret_loop

        /* copie la fin de a */
.Lsz_sub_copy:
        testl  %edx,%edx
        jz     .Lsz_sub_finish
        cmpl   %esi,%ebx
        jz     .Lsz_sub_finish
        leal   (%esi,%edx,4),%esi
        leal   (%ebx,%edx,4),%ebx
        negl   %edx
        .align 4
.Lsz_sub_copy_loop:
        movl   (%esi,%edx,4),%eax
.Lsz_sub_copy1:
        movl   %eax,(%ebx,%edx,4)
        incl   %edx
        jne    .Lsz_sub_copy_loop

        /* contrôle la longueur du résultat */
.Lsz_sub_finish:
        popl   %ecx
        .align 4
.Lsz_sub_test:
        leal   -4(%ebx),%ebx
        movl   (%ebx),%eax
        testl  %eax,%eax
        jnz    .Lsz_sub_lg_ok
        loop   .Lsz_sub_test
.Lsz_sub_lg_ok:
        movl   zc,%ebx
        movl   %ecx,(%ebx)
        
EXIT(sz_add)
#endif

               /* +---------------------------------+
                  |  a <- a+b, retourne la retenue  |
                  +---------------------------------+ */

/* ndouble xn(inc_1)(naturel a, longueur la, chiffre b) */
#ifdef have_sn_inc_1
ENTER(sn_inc_1)

        movl   a,%esi
        movl   la,%ecx
        movl   b,%eax
        xorl   %edx,%edx
        jecxz  .Lsn_inc_1_exit
 
        addl   %eax,(%esi)
        movl   %edx,%eax
        jnc    .Lsn_inc_1_exit
        decl   %ecx
        jecxz  .Lsn_inc_1_last_ret

        .align 4
.Lsn_inc_1_loop:
        leal   4(%esi),%esi
        incl   (%esi)
        jnz    .Lsn_inc_1_exit
        loop   .Lsn_inc_1_loop
.Lsn_inc_1_last_ret:
        incl   %eax

EXIT(sn_inc_1)
#endif

              /* +-----------------------------------+
                 |  a <- a + b, retourne la retenue  |
                 +-----------------------------------+ */

/* ndouble xn(inc)(naturel a, longueur la, naturel b, longueur lb) */
#ifdef have_sn_inc
ENTER(sn_inc)

        movl   a,%esi
        movl   la,%edx
        movl   b,%edi
        movl   lb,%ecx
        jecxz  .Lsn_inc_exit0

        /* additionne les chiffres communs */
        subl   %ecx,%edx
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        negl   %ecx
        clc
        .align 4
.Lsn_inc_add_digits:
        movl   (%edi,%ecx,4),%eax
        adcl   %eax,(%esi,%ecx,4)
        incl   %ecx
        jne    .Lsn_inc_add_digits
        jc     .Lsn_inc_propagate_ret
.Lsn_inc_exit0:
        xorl   %eax,%eax
        xorl   %edx,%edx
        RETURN

        /* propage la retenue */
        .align 4
.Lsn_inc_ret_loop:
        incl   (%esi)
        jnz    .Lsn_inc_exit0
        leal   4(%esi),%esi
.Lsn_inc_propagate_ret:
        decl   %edx
        jns    .Lsn_inc_ret_loop
.Lsn_inc_last_ret:
        xorl   %edx,%edx
        movl   $1,%eax
        
EXIT(sn_inc)
#endif

#ifdef have_sn_dec_1
/* a <- a-b, retourne la retenue */
/* zdouble xn(dec_1)(naturel a, longueur la, chiffre b) */
ENTER(sn_dec_1)

        movl   a,%esi
        movl   la,%ecx
        xorl   %edx,%edx
        movl   b,%eax
        jecxz  .Lsn_dec_1_last_ret
 
        subl   %eax,(%esi)
        movl   %edx,%eax
        jmp    .Lsn_dec_1_loop1
        .align 4
.Lsn_dec_1_loop:
        leal   4(%esi),%esi
        subl   $1,(%esi)
.Lsn_dec_1_loop1:
        jnb    .Lsn_dec_1_exit
        loop   .Lsn_dec_1_loop
.Lsn_dec_1_last_ret:
        negl   %eax
        sbbl   $0,%edx

EXIT(sn_dec_1)
#endif

               /* +---------------------------------+
                  |  a <- a-b, retourne la retenue  |
                  +---------------------------------+ */

/* zdouble xn(dec)(naturel a, longueur la, naturel b, longueur lb) */
#ifdef have_sn_dec
ENTER(sn_dec)

        movl   a,%esi
        movl   la,%edx
        movl   b,%edi
        movl   lb,%ecx
        jecxz  .Lsn_dec_exit0

        /* soustraction des chiffres communs */
        subl   %ecx,%edx
        leal   (%esi,%ecx,4),%esi
        leal   (%edi,%ecx,4),%edi
        negl   %ecx
        clc
        .align 4
.Lsn_dec_sub_digits:
        movl   (%edi,%ecx,4),%eax
        sbbl   %eax,(%esi,%ecx,4)
        incl   %ecx
        jne    .Lsn_dec_sub_digits
        jb     .Lsn_dec_propagate_ret
.Lsn_dec_exit0:
        xorl   %eax,%eax
        xorl   %edx,%edx
        RETURN

        /* propage la retenue */
        .align 4
.Lsn_dec_ret_loop:
        subl   $1,(%esi)
        jnb    .Lsn_dec_exit0
        leal   4(%esi),%esi
.Lsn_dec_propagate_ret:
        decl   %edx
        jns    .Lsn_dec_ret_loop
.Lsn_dec_last_ret:
        movl   %edx,%eax

EXIT(sn_dec)
#endif
