# Addition/Subtraktion von Integers

# Macro: In der DS MSDptr/len/LSDptr wird eine 1 unterhalb des Pointers ptr
# addiert. Unterhalb von MSDptr muss 1 Digit Platz sein.
# Dabei ist  ptr - MSDptr = count  und  0 < count <= len .
# Eventuell wird MSDptr erniedrigt und len erhöht.
  #define DS_1_plus(ptr,count)  \
    {                                                                      \
      var uintD* ptr_from_DS_1_plus = (ptr);                               \
      var uintC count_from_DS_1_plus = (count);                            \
      loop {                                                               \
        if (--count_from_DS_1_plus==0) { # Zähler erniedrigen              \
          # Beim Most Significant Digit angelangt                          \
          *(--ptr_from_DS_1_plus) += 1;                                    \
          # jetzt ist ptr_from_DS_1_plus = MSDptr                          \
          if (*ptr_from_DS_1_plus == bit(intDsize-1)) {                    \
            # 7FFF + 1 muss zu 00008000 werden:                            \
            *--MSDptr = 0;                                                 \
            len++;                                                         \
            if (uintWCoverflow(len)) {                                     \
              RESTORE_NUM_STACK; BN_ueberlauf();                           \
            }                                                              \
          }                                                                \
          break;                                                           \
        }                                                                  \
        if (!((*(--ptr_from_DS_1_plus) += 1) == 0)) # weiterincrementieren \
          break; # kein weiterer Übertrag -> Schleife abbrechen            \
      }                                                                    \
    }

# Macro: In der DS MSDptr/len/LSDptr wird eine 1 unterhalb des Pointers ptr
# subtrahiert. Unterhalb von MSDptr muss 1 Digit Platz sein.
# Dabei ist  ptr - MSDptr = count  und  0 < count <= len .
# Eventuell wird MSDptr erniedrigt und len erhöht.
  #define DS_minus1_plus(ptr,count)  \
    {                                                                 \
      var uintD* ptr_from_DS_minus1_plus = (ptr);                     \
      var uintC count_from_DS_minus1_plus = (count);                  \
      loop {                                                          \
        if (--count_from_DS_minus1_plus==0) { # Zähler erniedrigen    \
          # Beim Most Significant Digit angelangt                     \
          *(--ptr_from_DS_minus1_plus) -= 1;                          \
          # jetzt ist ptr_from_DS_minus1_plus = MSDptr                \
          if (*ptr_from_DS_minus1_plus == (uintD)bit(intDsize-1)-1) { \
            # 8000 - 1 muss zu FFFF7FFF werden:                       \
            *--MSDptr = (uintD)(-1);                                  \
            len++;                                                    \
            if (uintWCoverflow(len)) {                                \
              RESTORE_NUM_STACK; BN_ueberlauf();                      \
            }                                                         \
          }                                                           \
          break;                                                      \
        }                                                             \
        if (!((sintD)(*(--ptr_from_DS_minus1_plus) -= 1) == -1)) # weiterdecrementieren \
          break; # kein weiterer Übertrag -> Schleife abbrechen       \
      }                                                               \
    }

# (1+ x), wo x ein Integer ist. Ergebnis Integer.
# can trigger GC
  global object I_1_plus_I (object x);
  global object I_1_plus_I(x)
    var object x;
    {
      if (I_fixnump(x)) {
        # x ist Fixnum
        if (eq(x,Fixnum_minus1))
          return Fixnum_0; # (1+ -1) = 0
        if (!eq(x,Fixnum_mpos))
          return fixnum_inc(x,1); # bleibt Fixnum: direkt 1 addieren
      }
      # die sichere Methode
      {
        SAVE_NUM_STACK # num_stack retten
        var uintD* MSDptr;
        var uintC len;
        var uintD* LSDptr;
        I_to_NDS_1(x, MSDptr=,len=,LSDptr=); # NDS zu x bilden.
        DS_1_plus(LSDptr,len); # zur NDS 1 addieren
        var object result = DS_to_I(MSDptr,len); # wieder zum Integer machen
        RESTORE_NUM_STACK # num_stack zurück
        return result;
      }
    }

# (1- x), wo x ein Integer ist. Ergebnis Integer.
# can trigger GC
  global object I_minus1_plus_I (object x);
  global object I_minus1_plus_I(x)
    var object x;
    {
      if (I_fixnump(x)) {
        # x ist Fixnum
        if (eq(x,Fixnum_0))
          return Fixnum_minus1; # (1- 0) = -1
        if (!eq(x,Fixnum_mneg))
          return fixnum_inc(x,-1); # bleibt Fixnum: direkt 1 subtrahieren
      }
      # die sichere Methode
      {
        SAVE_NUM_STACK # num_stack retten
        var uintD* MSDptr;
        var uintC len;
        var uintD* LSDptr;
        I_to_NDS_1(x, MSDptr=,len=,LSDptr=); # NDS zu x bilden.
        DS_minus1_plus(LSDptr,len); # von der NDS 1 subtrahieren
        var object result = DS_to_I(MSDptr,len); # wieder zum Integer machen
        RESTORE_NUM_STACK # num_stack zurück
        return result;
      }
    }

# (+ x y), wo x und y Integers sind. Ergebnis Integer.
# can trigger GC
  global object I_I_plus_I (object x, object y);
  global object I_I_plus_I(x,y)
    var object x;
    var object y;
    # Methode:
    # x Fixnum ->
    #   y Fixnum -> beide direkt addieren, mit L_to_I beenden
    #   y Bignum -> falls x=0, y; sonst beide zu DS machen, addieren.
    # x Bignum ->
    #   y Fixnum -> falls y=0, x; sonst beide zu DS machen, addieren.
    #   y Bignum -> beide zu DS machen, addieren.
    {
      var uintD* MSDptr;
      var uintC len;
      var uintD* LSDptr;
      # MSDptr/len/LSDptr bilden die DS des Ergebnisses.
      if (I_fixnump(x)) {
        # x ist Fixnum
        if (I_fixnump(y)) {
          # x,y sind Fixnums
          #if (oint_data_len+1 < intLsize)
          return L_to_I( FN_to_L(x) + FN_to_L(y) ); # als 32-Bit-Zahlen addieren
          #elif defined(intQsize)
          return Q_to_I( FN_to_Q(x) + FN_to_Q(y) ); # als 64-Bit-Zahlen addieren
          #else
          var sint32 xhi = FN_sign(x);
          var uint32 xlo = FN_to_L(x);
          var sint32 yhi = FN_sign(y);
          var uint32 ylo = FN_to_L(y);
          xhi += yhi;
          xlo += ylo;
          if (xlo < ylo)
            xhi += 1;
          return L2_to_I(xhi,xlo);
          #endif
        } else {
          # x ist Fixnum, y ist Bignum
          var object h; h = x; x = y; y = h; # x und y vertauschen
          goto xBN_yFN;
        }
      } else {
        # x ist Bignum
        if (I_fixnump(y))
        xBN_yFN: {
          # x ist Bignum, y ist Fixnum, also x länger
          var sint32 y_ = FN_to_L(y); # Wert von y
          if (FN_L_zerop(y,y_))
            return x; # bei y=0 Ergebnis x
          {
            SAVE_NUM_STACK # num_stack retten
            BN_to_NDS_1(x, MSDptr=,len=,LSDptr=); # NDS zu x bilden.
            # len>=bn_minlength. len>pFN_maxlength erzwingen:
            if ((bn_minlength==pFN_maxlength) && (len==pFN_maxlength)) {
              var sintD sign = sign_of_sintD(MSDptr[0]);
              *--MSDptr = sign; len++;
            }
            # y_ zu den oberen pFN_maxlength Digits von x addieren:
            {
              var uintD* midptr = &LSDptr[-(uintP)pFN_maxlength];
              var uint32 x_ = pFN_maxlength_digits_at(midptr);
              var uint32 x_new = x_+(uint32)y_;
              set_pFN_maxlength_digits_at(midptr,x_new);
              if (x_new < x_) {
                # Carry.
                if (!FN_L_minusp(y,y_)) { # kürzerer Summand war positiv
                  # Dann ist ein positiver Übertrag weiterzutragen
                  # (Beispiel: 0002FFFC + 0007 = 00030003)
                  DS_1_plus(midptr,len-pFN_maxlength);
                }
              } else {
                # Kein Carry.
                if (FN_L_minusp(y,y_)) { # kürzerer Summand war negativ
                  # Dann ist ein negativer Übertrag weiterzutragen
                  # (Beispiel: 00020003 + FFF5 = 0001FFF8)
                  DS_minus1_plus(midptr,len-pFN_maxlength);
                }
              }
            }
            var object result = DS_to_I(MSDptr,len); # DS wieder zum Integer machen
            RESTORE_NUM_STACK # num_stack zurück
            return result;
          }
        } else {
          # x und y sind Bignums
          SAVE_NUM_STACK # num_stack retten
          if (Bignum_length(x) < Bignum_length(y)) {
            var object h; h = x; x = y; y = h; # x und y vertauschen
          }
          # Nun ist x das längere von beiden.
          BN_to_NDS_1(x, MSDptr=,len=,LSDptr=); # NDS zu x bilden.
          var uintD* yMSDptr;
          var uintC ylen;
          var uintD* yLSDptr;
          BN_to_NDS_nocopy(y, yMSDptr=,ylen=,yLSDptr=); # NDS zu y bilden.
          # yMSDptr/ylen/yLSDptr bilden die DS des kürzeren Arguments y.
          # len>ylen erzwingen:
          if (len==ylen) {
            var sintD sign = sign_of_sintD(MSDptr[0]);
            *--MSDptr = sign;
            len++;
            if (uintWCoverflow(len)) {
              RESTORE_NUM_STACK; BN_ueberlauf();
            }
          }
          # addieren:
          {
            var uintD* midptr = LSDptr-(uintP)ylen;
            var uintD carry = addto_loop_down(yLSDptr,LSDptr,ylen);
            if (carry) {
              # Carry.
              if ((sintD)yMSDptr[0] >=0) { # kürzerer Summand war positiv
                # Dann ist ein positiver Übertrag weiterzutragen
                # (Beispiel: 0002FFFC + 0007 = 00030003)
                DS_1_plus(midptr,len-ylen);
              }
            } else {
              # Kein Carry.
              if ((sintD)yMSDptr[0] <0) { # kürzerer Summand war negativ
                # Dann ist ein negativer Übertrag weiterzutragen
                # (Beispiel: 00020003 + FFF5 = 0001FFF8)
                DS_minus1_plus(midptr,len-ylen);
              }
            }
          }
          var object result = DS_to_I(MSDptr,len); # DS wieder zum Integer machen
          RESTORE_NUM_STACK # num_stack zurück
          return result;
        }
      }
    }

# (- x), wenn x ein Integer ist. Ergebnis Integer.
# can trigger GC
  local object I_minus_I (object x);
  local object I_minus_I(x)
    var object x;
    {
      if (I_fixnump(x)) {
        # Fixnum -> Long, negieren, -> Integer
        #if (oint_data_len+1 < intLsize)
        return L_to_I(- FN_to_L(x));
        #elif defined(intQsize)
        return Q_to_I(- FN_to_Q(x));
        #else
        var sint32 xhi = FN_sign(x);
        var uint32 xlo = FN_to_L(x);
        if (xlo==0) {
          xhi = -xhi;
        } else {
          xlo = -xlo; xhi = ~xhi;
        }
        return L2_to_I(xhi,xlo);
        #endif
      } else {
        # x Bignum
        SAVE_NUM_STACK # num_stack retten
        var uintD* MSDptr;
        var uintC len;
        var uintD* LSDptr;
        BN_to_NDS_1(x, MSDptr=,len=,LSDptr=); # NDS zu x bilden, len>0
        # vorsorglich 1 Digit mehr belegen:
        {
          var sintD sign = sign_of_sintD(MSDptr[0]);
          *--MSDptr = sign;
          len++;
          if (uintWCoverflow(len)) {
            RESTORE_NUM_STACK; BN_ueberlauf();
          }
        }
        # Negierschleife:
        neg_loop_down(LSDptr,len);
        # MSDigit ist nun = 0x0000 oder = 0xFFFF
        var object result = DS_to_I(MSDptr,len); # DS wieder zum Integer machen
        RESTORE_NUM_STACK # num_stack zurück
        return result;
      }
    }

# (- x y), wo x und y Integers sind. Ergebnis Integer.
# can trigger GC
  global object I_I_minus_I (object x, object y);
  global object I_I_minus_I(x,y)
    var object x;
    var object y;
    # Methode:
    # x Fixnum ->
    #   y Fixnum -> beide direkt subtrahieren, mit L_to_I beenden
    #   y Bignum -> falls x=0, (- y); sonst beide zu DS machen, subtrahieren.
    # x Bignum ->
    #   y Fixnum -> falls y=0, x; sonst beide zu DS machen, subtrahieren.
    #   y Bignum -> beide zu DS machen, subtrahieren.
    {
      var uintD* MSDptr;
      var uintC len;
      var uintD* LSDptr;
      # MSDptr/len/LSDptr bilden die DS des Ergebnisses.
      if (I_fixnump(x)) {
        # x ist Fixnum
        if (I_fixnump(y)) {
          # x,y sind Fixnums
          #if (oint_data_len+1 < intLsize)
          return L_to_I( FN_to_L(x) - FN_to_L(y) ); # als 32-Bit-Zahlen subtrahieren
          #elif defined(intQsize)
          return Q_to_I( FN_to_Q(x) - FN_to_Q(y) ); # als 64-Bit-Zahlen subtrahieren
          #else
          var sint32 xhi = FN_sign(x);
          var uint32 xlo = FN_to_L(x);
          var sint32 yhi = FN_sign(y);
          var uint32 ylo = FN_to_L(y);
          xhi -= yhi;
          if (xlo < ylo)
            xhi -= 1;
          xlo -= ylo;
          return L2_to_I(xhi,xlo);
          #endif
        } else {
          # x ist Fixnum, y ist Bignum, also y länger
          var sint32 x_ = FN_to_L(x); # Wert von x
          if (FN_L_zerop(x,x_))
            return I_minus_I(y); # bei x=0 Ergebnis (- y)
          {
            SAVE_NUM_STACK # num_stack retten
            BN_to_NDS_1(y, MSDptr=,len=,LSDptr=); # NDS zu y bilden.
            # vorsorglich 1 Digit mehr belegen:
            {
              var sintD sign = sign_of_sintD(MSDptr[0]);
              *--MSDptr = sign;
              len++;
              if (uintWCoverflow(len)) {
                RESTORE_NUM_STACK; BN_ueberlauf();
              }
            }
            # Negierschleife:
            neg_loop_down(LSDptr,len);
            # MSDigit ist nun = 0x0000 oder = 0xFFFF
            # x_ zu den oberen pFN_maxlength Digits von -y addieren:
            {
              var uintD* midptr = &LSDptr[-(uintP)pFN_maxlength];
              var uint32 y_ = pFN_maxlength_digits_at(midptr);
              var uint32 y_new = y_+(uint32)x_;
              set_pFN_maxlength_digits_at(midptr,y_new);
              if (y_new < y_) {
                # Carry.
                if (!FN_L_minusp(x,x_)) { # kürzerer Summand war positiv
                  # Dann ist ein positiver Übertrag weiterzutragen
                  # (Beispiel: 0002FFFC + 0007 = 00030003)
                  DS_1_plus(midptr,len-pFN_maxlength);
                }
              } else {
                # Kein Carry.
                if (FN_L_minusp(x,x_)) { # kürzerer Summand war negativ
                  # Dann ist ein negativer Übertrag weiterzutragen
                  # (Beispiel: 00020003 + FFF5 = 0001FFF8)
                  DS_minus1_plus(midptr,len-pFN_maxlength);
                }
              }
            }
            var object result = DS_to_I(MSDptr,len); # DS wieder zum Integer machen
            RESTORE_NUM_STACK # num_stack zurück
            return result;
          }
        }
      } else {
        # x ist Bignum
        if (I_fixnump(y)) {
          # x ist Bignum, y ist Fixnum, also x länger
          var sint32 y_ = FN_to_L(y); # Wert von y
          if (FN_L_zerop(y,y_))
            return x; # bei y=0 Ergebnis x
          {
            SAVE_NUM_STACK # num_stack retten
            BN_to_NDS_1(x, MSDptr=,len=,LSDptr=); # NDS zu x bilden.
            # len>=bn_minlength. len>pFN_maxlength erzwingen:
            if ((bn_minlength==pFN_maxlength) && (len==pFN_maxlength)) {
              var sintD sign = sign_of_sintD(MSDptr[0]);
              *--MSDptr = sign; len++;
            }
            # y_ von den oberen pFN_maxlength Digits von x subtrahieren:
            {
              var uintD* midptr = &LSDptr[-(uintP)pFN_maxlength];
              var uint32 x_ = pFN_maxlength_digits_at(midptr);
              var uint32 x_new = x_-(uint32)y_;
              set_pFN_maxlength_digits_at(midptr,x_new);
              if (x_new > x_) { # bzw. (x_ < (uint32)y_), da y_>0
                # Carry.
                if (!FN_L_minusp(y,y_)) { # kürzerer Summand war positiv
                  # Dann ist ein negativer Übertrag weiterzutragen
                  # (Beispiel: 00030003 - 0007 = 0002FFFC)
                  DS_minus1_plus(midptr,len-pFN_maxlength);
                }
              } else {
                # Kein Carry.
                if (FN_L_minusp(y,y_)) { # kürzerer Summand war negativ
                  # Dann ist ein positiver Übertrag weiterzutragen
                  # (Beispiel: 0002FFF8 - FFF5 = 00030003)
                  DS_1_plus(midptr,len-pFN_maxlength);
                }
              }
            }
            var object result = DS_to_I(MSDptr,len); # DS wieder zum Integer machen
            RESTORE_NUM_STACK # num_stack zurück
            return result;
          }
        } else {
          # x und y sind Bignums
          if (Bignum_length(x) > Bignum_length(y)) {
            # x das längere von beiden.
            SAVE_NUM_STACK # num_stack retten
            BN_to_NDS_1(x, MSDptr=,len=,LSDptr=); # NDS zu x bilden.
            var uintD* yMSDptr;
            var uintC ylen;
            var uintD* yLSDptr;
            BN_to_NDS_nocopy(y, yMSDptr=,ylen=,yLSDptr=); # NDS zu y bilden.
            # yMSDptr/ylen/yLSDptr bilden die DS des kürzeren Arguments y.
            # Es ist len>ylen.
            # subtrahieren:
            {
              var uintD* midptr = LSDptr-(uintP)ylen;
              var uintD carry = subfrom_loop_down(yLSDptr,LSDptr,ylen);
              if (carry) {
                # Carry.
                if ((sintD)yMSDptr[0] >=0) { # kürzerer Summand war positiv
                  # Dann ist ein negativer Übertrag weiterzutragen
                  # (Beispiel: 00030003 - 0007 = 0002FFFC)
                  DS_minus1_plus(midptr,len-ylen);
                }
              } else {
                # Kein Carry.
                if ((sintD)yMSDptr[0] <0) { # kürzerer Summand war negativ
                  # Dann ist ein positiver Übertrag weiterzutragen
                  # (Beispiel: 0002FFF8 - FFF5 = 00030003)
                  DS_1_plus(midptr,len-ylen);
                }
              }
            }
            var object result = DS_to_I(MSDptr,len); # DS wieder zum Integer machen
            RESTORE_NUM_STACK # num_stack zurück
            return result;
          } else {
            # y das längere von beiden.
            SAVE_NUM_STACK # num_stack retten
            BN_to_NDS_1(y, MSDptr=,len=,LSDptr=); # NDS zu y bilden.
            # vorsorglich 1 Digit mehr belegen:
            {
              var sintD sign = sign_of_sintD(MSDptr[0]);
              *--MSDptr = sign;
              len++;
              if (uintWCoverflow(len)) {
                RESTORE_NUM_STACK; BN_ueberlauf();
              }
            }
            # Negierschleife:
            neg_loop_down(LSDptr,len);
            # MSDigit ist nun = 0x0000 oder = 0xFFFF
            var uintD* xMSDptr;
            var uintC xlen;
            var uintD* xLSDptr;
            BN_to_NDS_nocopy(x, xMSDptr=,xlen=,xLSDptr=); # NDS zu x bilden.
            # xMSDptr/xlen/xLSDptr bilden die DS des kürzeren Arguments x.
            # Es ist jetzt len>xlen.
            # addieren:
            {
              var uintD* midptr = LSDptr-(uintP)xlen;
              var uintD carry = addto_loop_down(xLSDptr,LSDptr,xlen);
              if (carry) {
                # Carry.
                if ((sintD)xMSDptr[0] >=0) { # kürzerer Summand war positiv
                  # Dann ist ein positiver Übertrag weiterzutragen
                  # (Beispiel: 0002FFFC + 0007 = 00030003)
                  DS_1_plus(midptr,len-xlen);
                }
              } else {
                # Kein Carry.
                if ((sintD)xMSDptr[0] <0) { # kürzerer Summand war negativ
                  # Dann ist ein negativer Übertrag weiterzutragen
                  # (Beispiel: 00020003 + FFF5 = 0001FFF8)
                  DS_minus1_plus(midptr,len-xlen);
                }
              }
            }
            var object result = DS_to_I(MSDptr,len); # DS wieder zum Integer machen
            RESTORE_NUM_STACK # num_stack zurück
            return result;
          }
        }
      }
    }

# (abs x), wenn x ein Integer ist. Ergebnis Integer.
# can trigger GC
  local object I_abs_I (object x);
  local object I_abs_I(x)
    var object x;
    {
      # Methode:
      # Bei x<0: (- x), sonst x.
      if (R_minusp(x))
        return I_minus_I(x);
      else
        return x;
    }

