/*
   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 "long_int.h"
#include "long_int-s.h"

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

#ifndef have_sn_add
void xn(add)(naturel a, longueur la, naturel b, longueur lb, naturel c) {
  ndouble ret;
  longueur i;

  /* classe les arguments de sorte que la >= lb */
  if (la < lb) {
    naturel  x  = a;
    longueur lx = la;
    a = b; b = x; la = lb; lb = lx;
  }

  /* addition des chiffres communs */
  for (ret=0, i=0; i<lb; i++) {
    ret += (ndouble)a[i] + (ndouble)b[i];
    c[i] = ret;
    ret >>= HW;
  }

  /* copie la fin de a et propage la retenue */
  xn(move)(c+i,a+i,la-i);
  c[la] = 0;
  if (ret) while (++c[i] == 0) i++;

}
#endif

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

#ifndef have_sn_inc_1
ndouble xn(inc_1)(naturel a, longueur la, chiffre b) {
  ndouble ret = b;
  longueur i;

  for (i=0; (ret) && (i<la); i++) {
    ret += (ndouble)a[i];
    a[i] = ret;
    ret >>= HW;
  }

  return(ret);
}
#endif

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

#ifndef have_sn_inc
ndouble xn(inc)(naturel a, longueur la, naturel b, longueur lb) {
  ndouble ret;
  longueur i;

  /* addition des chiffres communs */
  for (ret=0, i=0; i<lb; i++) {
    ret += (ndouble)a[i] + (ndouble)b[i];
    a[i] = ret;
    ret >>= HW;
  }
  
  /* propage la retenue */
  while ((ret) && (i < la)) {ret = (++a[i] == 0); i++;}
  return(ret);
}
#endif

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

#ifndef have_sn_sub
void xn(sub)(naturel a, longueur la, naturel b, longueur lb, naturel c) {
  zdouble ret;
  longueur i;

  /* soustraction des chiffres communs */
  for (ret=0, i=0; i<lb; i++) {
    ret += (ndouble)a[i] - (ndouble)b[i];
    c[i] = ret;
    ret >>= HW;
  }

  /* copie la fin de a et propage la retenue */
  xn(move)(c+i,a+i,la-i);
  if (ret) while (c[i]-- == 0) i++;

}
#endif

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

#ifndef have_sn_dec_1
zdouble xn(dec_1)(naturel a, longueur la, chiffre b) {
  zdouble ret = -(ndouble)b;
  longueur i;

  for (i=0; (ret) && (i<la); i++) {
    ret += (ndouble)a[i];
    a[i] = ret;
    ret >>= HW;
  }

  return(ret);
}
#endif

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

#ifndef have_sn_dec
zdouble xn(dec)(naturel a, longueur la, naturel b, longueur lb) {
  zdouble ret;
  longueur i;

  /* soustraction des chiffres communs */
  for (ret=0, i=0; i<lb; i++) {
    ret += (ndouble)a[i] - (ndouble)b[i];
    a[i] = ret;
    ret >>= HW;
  }
  
  /* propage la retenue */
  while ((ret) && (i < la)) {ret = -(a[i]-- == 0); i++;}
  return(ret);
}
#endif

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

#ifndef have_sz_addsub
void xz(addsub)(entier *a, entier *b, entier *c, longueur s) {
  longueur la = Lg(a),    lb = Lg(b);
  longueur sa = Signe(a), sb = Signe(b)^s;
  naturel  va, vb;

  /* aiguillage selon les signes */
  if (sa^sb) { /* soustraction */
    zdouble ret;
    longueur i;

    /* classe les arguments de sorte que la >= lb */
    if (la > lb) {
      va = a->val; vb = b->val;
    } else if (la < lb) {
      va = b->val; vb = a->val;
      i  = la; la = lb; lb = i;
      sa = sb;
    } else { /* même longueur, compare les chiffres de tête */
      for (i=la-1; (i>=0) && (a->val[i] == b->val[i]) ; i--);
      if (i < 0) {c->hd = 0; return;}
      if (a->val[i] > b->val[i]) {va = a->val; vb = b->val;}
      else              {sa = sb; va = b->val; vb = a->val;}
      la = i+1; lb = la;
    }

    /* soustraction des chiffres communs */
    for (ret=0, i=0; i<lb; i++) {
      ret += (ndouble)va[i] - (ndouble)vb[i];
      c->val[i] = ret;
      ret >>= HW;
    }

    /* copie la fin de a et propage la retenue */
    xn(move)(c->val+i,va+i,la-i);
    if (ret) while (c->val[i]-- == 0) i++;
    make_head(c,la,sa);
  }

  else { /* addition */
    ndouble ret;
    longueur i;

    /* classe les arguments de sorte que la >= lb */
    if (la >= lb) {
      va = a->val; vb = b->val;
    } else {
      va = b->val; vb = a->val;
      i  = la; la = lb; lb = i;
    }

    /* addition des chiffres communs */
    for (ret=0, i=0; i<lb; i++) {
      ret += (ndouble)va[i] + (ndouble)vb[i];
      c->val[i] = ret;
      ret >>= HW;
    }

    /* copie la fin de a et propage la retenue */
    xn(move)(c->val+i,va+i,la-i);
    c->val[la] = 0;
    if (ret) while (++c->val[i] == 0) i++;
    make_head(c,la+1,sa);
  }
}

#ifdef use_slong
void xz(add)(entier *a, entier *b, entier *c) {xz(addsub)(a,b,c,0);}
void xz(sub)(entier *a, entier *b, entier *c) {xz(addsub)(a,b,c,SIGN_m);}
#endif

#endif
