File r36/cslbase/arith01.c artifact 64c9c1d5a2 part of check-in 3c4d7b69af



/*  arith01.c                         Copyright (C) 1990 Codemist Ltd */

/*
 * Arithmetic functions.
 *        Addition of generic numbers
 *        and in particular a lot of bignum support.
 *
 * Version 1.4   November 1990.
 *
 */

/* Signature: 07d5049f 23-Jan-1999 */

#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "machine.h"
#include "tags.h"
#include "cslerror.h"
#include "externs.h"
#include "arith.h"
#ifdef TIMEOUT
#include "timeout.h"
#endif

/*
 * I start off with a collection of utility functions that create
 * Lisp structures to represent various sorts of numbers
 * and which extract values from same.
 * The typedefs that explain the layout of these structures are in "tags.h"
 */

Lisp_Object make_one_word_bignum(int32 n)
/*
 * n is an integer - create a bignum representation of it.  This is
 * done when n is outside the range 0xf8000000 to 0x07ffffff.
 */
{   Lisp_Object w = getvector(TAG_NUMBERS, TYPE_BIGNUM, 8);
    Lisp_Object nil;
    errexit();
    bignum_digits(w)[0] = n;
    return w;
}

Lisp_Object make_two_word_bignum(int32 a1, unsigned32 a0)
/*
 * This make a 2-word bignum from the 2-word value (a1,a0), where it
 * must have been arranged already that a1 and a0 are correctly
 * normalized to put in the two words as indicated.
 */
{
    Lisp_Object w = getvector(TAG_NUMBERS, TYPE_BIGNUM, 12);
    Lisp_Object nil;
    errexit();
    bignum_digits(w)[0] = a0;
    bignum_digits(w)[1] = a1;
    bignum_digits(w)[2] = 0;
    return w;
}

#ifdef COMMON
Lisp_Object make_sfloat(double d)
/*
 * Turn a regular floating point value into a Lisp "short float", which
 * is an immediate object obtained by using the bottom 4 bits of a 32-bit
 * word as tag, and the rest as just whatever would stand for a regular
 * single precision value.  In doing the conversion here I ignore
 * rounding etc - short floats are to save heap turn-over, but will
 * not give robust numeric results.
 */
{
    Float_union w;
    w.f = (float)d;
    return (w.i & ~(int32)0xf) + TAG_SFLOAT;
}
#endif

Lisp_Object make_boxfloat(double a, int32 type)
/*
 * Make a boxed float (single, double or long according to the type specifier)
 * if type==0 this makes a short float
 */
{
    Lisp_Object r, nil;
#ifndef COMMON
    CSL_IGNORE(type);
#endif
#ifdef COMMON
    switch (type)
    {
case 0:
        {   Float_union aa;
            aa.f = (float)a;
            return (aa.i & ~(int32)0xf) + TAG_SFLOAT;
        }
case TYPE_SINGLE_FLOAT:
        r = getvector(TAG_BOXFLOAT, TYPE_SINGLE_FLOAT, sizeof(Single_Float));
        errexit();
        single_float_val(r) = (float)a;
        return r;
default: /* TYPE_DOUBLE_FLOAT I hope */
#endif
        r = getvector(TAG_BOXFLOAT, TYPE_DOUBLE_FLOAT, sizeof(Double_Float));
        errexit();
        double_float_val(r) = a;
        return r;
#ifdef COMMON
case TYPE_LONG_FLOAT:
        r = getvector(TAG_BOXFLOAT, TYPE_LONG_FLOAT, sizeof(Long_Float));
        errexit();
        long_float_val(r) = a;
        return r;
    }
#endif
}

static double bignum_to_float(Lisp_Object v, int32 h, int *xp)
/*
 * Convert a Lisp bignum to get a floating point value.  This uses at most the
 * top 3 digits of the bignum's representation since that is enough to achieve
 * full double precision accuracy.
 * This can not overflow, because it leaves an exponent-adjustment value
 * in *xp. You need ldexp(r, *xp) afterwards.
 */
{
    int32 n = (h>>2) - 2;  /* Last index into the data */
    int x = 31*(int)n;
    int32 msd = (int32)bignum_digits(v)[n];
/* NB signed conversion on next line */
    double r = (double)msd;
    switch (n)
    {
default:        /* for very big numbers combine in 3 digits */
        r = TWO_31*r + (double)bignum_digits(v)[--n];
        x -= 31;
        /* drop through */
case 1: r = TWO_31*r + (double)bignum_digits(v)[--n];
        x -= 31;
        /* drop through */
case 0: break;  /* do no more */
    }
    *xp = x;
    return r;
}

double float_of_number(Lisp_Object a)
/*
 * Return a (double precision) floating point value for the given Lisp
 * number, or 0.0 in case of trouble.  This is often called in circumstances
 * where I already know the type of its argument and so its type-dispatch
 * is unnecessary - in doing so I am trading off performance against
 * code repetition.
 */
{
    if (is_fixnum(a)) return (double)int_of_fixnum(a);
#ifdef COMMON
    else if (is_sfloat(a))
    {   Float_union w;
        w.i = a - TAG_SFLOAT;
        return (double)w.f;
    }
#endif
    else if (is_bfloat(a))
    {   int32 h = type_of_header(flthdr(a));
        switch (h)
        {
#ifdef COMMON
    case TYPE_SINGLE_FLOAT:
            return (double)single_float_val(a);
#endif
    case TYPE_DOUBLE_FLOAT:
            return double_float_val(a);
#ifdef COMMON
    case TYPE_LONG_FLOAT:
            return (double)long_float_val(a);
#endif
    default:
            return 0.0;
        }
    }
    else
    {   Header h = numhdr(a);
        int x1;
        double r1;
        switch (type_of_header(h))
        {
    case TYPE_BIGNUM:
            r1 = bignum_to_float(a, length_of_header(h), &x1);
            return ldexp(r1, x1);
#ifdef COMMON
    case TYPE_RATNUM:
            {   int x2;
                Lisp_Object na = numerator(a);
                a = denominator(a);
                if (is_fixnum(na)) r1 = float_of_number(na), x1 = 0;
                else r1 = bignum_to_float(na, 
                              length_of_header(numhdr(na)), &x1);
                if (is_fixnum(a)) r1 = r1 / float_of_number(a), x2 = 0;
                else r1 = r1 / bignum_to_float(a,
                                   length_of_header(numhdr(a)), &x2);
/* Floating point overflow can only arise in this ldexp() */
                return ldexp(r1, x1 - x2);
            }
#endif
    default:
/*
 * If the value was non-numeric or a complex number I hand back 0.0,
 * and since I am supposed to have checked the object type already
 * this OUGHT not to arrive - bit raising an exception seems over-keen.
 */
            return 0.0;
        }
    }
}

int32 thirty_two_bits(Lisp_Object a)
/*
 * return a 32 bit integer value for the Lisp integer (fixnum or bignum)
 * passed down - ignore any higher order bits and return 0 if the arg was
 * floating, rational etc or not a number at all.  Only really wanted where
 * links between C-specific code (that might really want 32-bit values)
 * and Lisp are being coded.
 */
{
    switch ((int)a & TAG_BITS)
    {
case TAG_FIXNUM:
        return int_of_fixnum(a);
case TAG_NUMBERS:
        if (is_bignum(a))
        {   int len = bignum_length(a);
/*
 * Note that I keep 31 bits per word and use a 2s complement representation.
 * thus if I have a one-word bignum I just want its contents but in all
 * other cases I need just one bit from the next word up.
 */
            if (len == 8) return bignum_digits(a)[0]; /* One word bignum */
            return bignum_digits(a)[0] | (bignum_digits(a)[1] << 31);
        }
        /* else drop through */
case TAG_BOXFLOAT:
default:
/*
 * return 0 for all non-fixnums
 */
        return 0;
    }
}

#ifdef COMMON
Lisp_Object make_complex(Lisp_Object r, Lisp_Object i)
{
    Lisp_Object v, nil = C_nil;
/*
 * Here r and i are expected to be either both rational (which in this
 * context includes the case of integer values) or both of the same
 * floating point type.  It is assumed that this has already been
 * arranged by here.
 */
    if (i == fixnum_of_int(0)) return r;
    stackcheck2(0, r, i);
    push2(r, i);
    v = getvector(TAG_NUMBERS, TYPE_COMPLEX_NUM, sizeof(Complex_Number));
/*
 * The vector r has uninitialized contents here - dodgy.  If the call
 * to getvector succeeded then I fill it in, otherwise I will not
 * refer to it again, and I think that unreferenced vectors containing junk
 * are OK.
 */
    pop2(i, r);
    errexit();
    real_part(v) = r;
    imag_part(v) = i;
    return v;
}

Lisp_Object make_ratio(Lisp_Object p, Lisp_Object q)
/*
 * By the time this is called (p/q) must be in its lowest terms, q>0
 */
{
    Lisp_Object v, nil = C_nil;
    if (q == fixnum_of_int(1)) return p;
    stackcheck2(0, p, q);
    push2(p, q);
    v = getvector(TAG_NUMBERS, TYPE_RATNUM, sizeof(Rational_Number));
    pop2(q, p);
    errexit();
    numerator(v) = p;
    denominator(v) = q;
    return v;
}

#endif

/*
 * The next bit of code seems pretty dreadful, but I think that is just
 * what generic arithmetic is all about.  The code for plus2 is written
 * as a dispatch function into over 30 separate possible type-specific
 * versions of the code.  In a very few simple (and performance-critical)
 * cases the code is written in-line in plus2 - in particular arithmetic
 * on fixnums is done that way.  Similarly for other cases.
 * I Use one-character suffices to remind me about types:
 *          i       fixnum
 *          b       bignum
 *          r       ratio
 *          s       short float
 *          f       boxed float (single/double/long)
 *          c       complex
 *
 * Throughout this code I am going to IGNORE floating point exceptions,
 * at least for a first attempt.  Decent detection of and recovery after
 * floating point overflow seems an extra unpleasant distraction!  Note
 * that C allows me to trap the SIGFPE exception, but returning from
 * the exception handler gives undefined behaviour - one is expected
 * to longjmp out, which means accepting the cost of using setjmp.
 *
 * It would perhaps be reasonable to write the dispatch code as a big
 * macro so that the versions for plus, times etc could all be kept
 * in step - I have not done that (a) because the macro would have been
 * bigger than I like macros to be (b) it would have involved token-
 * splicing (or VERY many parameters) to generate the names of the
 * separate type-specific procedures and (c) doing it by hand allows me
 * total flexibility about coding various cases in-line.
 */

#ifdef COMMON

static Lisp_Object plusis(Lisp_Object a, Lisp_Object b)
{
    Float_union bb;
    bb.i = b - TAG_SFLOAT;
    bb.f = (float)((float)int_of_fixnum(a) + bb.f);
    return (bb.i & ~(int32)0xf) + TAG_SFLOAT;
}
#endif

/*
 * Bignums are represented as vectors where the most significant 32-bit
 * digit is treated as signed, and the remaining ones are unsigned.
 */

static Lisp_Object plusib(Lisp_Object a, Lisp_Object b)
/*
 * Add a fixnum to a bignum, returning a result as a fixnum or bignum
 * depending on its size.  This seems much nastier than one would have
 * hoped.
 */
{
    int32 len = bignum_length(b), i, sign = int_of_fixnum(a), s;
    Lisp_Object c, nil;
    len = (len >> 2) - 1;
    if (len == 1)
    {   int32 t;
/*
 * Partly because it will be a common case and partly because it has
 * various special cases I have special purpose code to cope with
 * adding a fixnum to a one-word bignum.
 */
        s = (int32)bignum_digits(b)[0] + sign;
        t = s + s;
        if (top_bit_set(s ^ t))  /* needs to turn into two-word bignum */
        {   c = getvector(TAG_NUMBERS, TYPE_BIGNUM, 12);
            errexit();
            if (top_bit_set(s))
            {   bignum_digits(c)[0] = clear_top_bit(s);
                bignum_digits(c)[1] = (unsigned32)(-1);
            }
            else
            {   bignum_digits(c)[0] = s;
                bignum_digits(c)[1] = 0;
            }
            bignum_digits(c)[2] = 0;  /* set padding word to zero to be tidy */
            return c;
        }
        t = s & fix_mask;    /* Will it fit as a fixnum? */
        if (t == 0 || t == fix_mask) return fixnum_of_int(s);
        /* here the result is a one-word bignum */
        c = getvector(TAG_NUMBERS, TYPE_BIGNUM, 8);
        errexit();
        bignum_digits(c)[0] = s;
        return c;
    }
/*
 * Now, after all the silly cases have been handled, I have a calculation
 * which seems set to give a multi-word result.  The result here can at
 * least never shrink to a fixnum since subtracting a fixnum can at
 * most shrink the length of a number by one word.  I omit the stack-
 * check here in the hope that code here never nests enough for trouble.
 */
    push(b);
    c = getvector(TAG_NUMBERS, TYPE_BIGNUM, 4+(len<<2));
    pop(b);
    errexit();
    s = bignum_digits(b)[0] + clear_top_bit(sign);
    bignum_digits(c)[0] = clear_top_bit(s);
    if (sign >= 0) sign = 0; else sign = 0x7fffffff; /* extend the sign */

    len--;
    for (i=1; i<len; i++)
    {   s = bignum_digits(b)[i] + sign + top_bit(s);
        bignum_digits(c)[i] = clear_top_bit(s);
    }
    /* Now just the most significant digit remains to be processed  */
    if (sign != 0) sign = -1;
    {   s = bignum_digits(b)[i] + sign + top_bit(s);
        if (!signed_overflow(s))         /* did it overflow? */
        {
/*
 * Here the most significant digit did not produce an overflow, but maybe
 * what we actually had was some cancellation and the MSD is now zero
 * or -1, so that the number should shrink...
 */
            if ((s == 0 && (bignum_digits(c)[i-1] & 0x40000000) == 0) ||
                (s == -1 && (bignum_digits(c)[i-1] & 0x40000000) != 0))
            {   /* shrink the number */
                numhdr(c) -= pack_hdrlength(1L);
                if (s == -1) bignum_digits(c)[i-1] |= ~0x7fffffff;
/*
 * Now sometimes the shrinkage will leave a padding word, sometimes it
 * will really allow me to save space.
 */
                if ((i & 1) == 0)
                {   bignum_digits(c)[i] = 0;   /* leave the unused word tidy */
                    return c;
                }
/*
 * Having shrunk the number I am leaving a doubleword of unallocated space
 * in the heap.  Dump a header word into it to make it look like an
 * 8-byte bignum since that will allow the garbage collector to handle it.
 * It I left it containing arbitrary junk I could wreck myself.
 */
                bignum_digits(c)[i] = make_bighdr(2L);
                return c;
            }
            bignum_digits(c)[i] = s;  /* length unchanged */
            return c;
        }
/*
 * Here the result is one word longer than the input-bignum.
 * Once again SOMTIMES this will not involve allocating more store,
 * but just encroaching into the previously unused word that was padding
 * things out to a multiple of 8 bytes.
 */
        if ((i & 1) == 1)
        {   bignum_digits(c)[i++] = clear_top_bit(s);
            bignum_digits(c)[i] = top_bit_set(s) ? -1 : 0;
            numhdr(c) += pack_hdrlength(1L);
            return c;
        }
        push(c);
        b = getvector(TAG_NUMBERS, TYPE_BIGNUM, 12+(len<<2));
        pop(c);
        errexit();
        for (i=0; i<=len; i++)
            bignum_digits(b)[i] = bignum_digits(c)[i];
        bignum_digits(b)[i++] = clear_top_bit(s);
        bignum_digits(b)[i] = top_bit_set(s) ? -1 : 0;
        return b;
    }
}

#ifdef COMMON
static Lisp_Object plusir(Lisp_Object a, Lisp_Object b)
/*
 * fixnum and ratio, but also valid for bignum and ratio.
 * Note that if the inputs were in lowest terms there is no need for
 * and GCD calculations here.
 */
{
    Lisp_Object nil;
    push(b);
    a = times2(a, denominator(b));
    nil = C_nil;
    if (!exception_pending()) a = plus2(a, numerator(stack[0]));
    pop(b);
    errexit();
    return make_ratio(a, denominator(b));
}

static Lisp_Object plusic(Lisp_Object a, Lisp_Object b)
/*
 * real of any sort plus complex.
 */
{
    Lisp_Object nil;
    push(b);
    a = plus2(a, real_part(b));
    pop(b);
    errexit();
/*
 * make_complex() takes responsibility for mapping #C(n 0) onto n
 */
    return make_complex(a, imag_part(b));
}
#endif

static Lisp_Object plusif(Lisp_Object a, Lisp_Object b)
/*
 * Fixnum plus boxed-float.
 */
{
    double d = (double)int_of_fixnum(a) + float_of_number(b);
    return make_boxfloat(d, type_of_header(flthdr(b)));
}

#ifdef COMMON
#define plussi(a, b) plusis(b, a)

#define plussb(a, b) plusbs(b, a)

#define plussr(a, b) plusrs(b, a)

#define plussc(a, b) plusic(a, b)

#endif

static Lisp_Object plussf(Lisp_Object a, Lisp_Object b)
/*
 * This can be used for any rational value plus a boxed-float. plusif()
 * is separated just for (minor) efficiency reasons.
 */
{
    double d = float_of_number(a) + float_of_number(b);
    return make_boxfloat(d, type_of_header(flthdr(b)));
}

#define plusbi(a, b) plusib(b, a)

#ifdef COMMON

static Lisp_Object plusbs(Lisp_Object a, Lisp_Object b)
{
    double d = float_of_number(a) + float_of_number(b);
    return make_sfloat(d);
}

#endif

Lisp_Object lengthen_by_one_bit(Lisp_Object a, int32 msd)
/*
 * (a) is a bignum, and arithmetic on it has (just) caused overflow
 * in its top word - I just need to stick on another word. (msd) is the
 * current top word, and its sign will be used to decide on the value
 * that must be appended.
 */
{
    int32 len = bignum_length(a);
/*
 * Sometimes I need to allocate a new vector and copy data across into it
 */
    if ((len & 4) == 0)
    {   Lisp_Object b, nil;
        int32 i;
        push(a);
        b = getvector(TAG_NUMBERS, TYPE_BIGNUM, len+4);
        pop(a);
        errexit();
        len = (len >> 2) - 1;
        for (i=0; i<len; i++)
            bignum_digits(b)[i] = clear_top_bit(bignum_digits(a)[i]);
        bignum_digits(b)[i] = top_bit_set(msd) ? -1 : 0;
        bignum_digits(b)[i+1] = 0;
        return b;
    }
    else
/*
 * .. whereas sometimes I have a spare word already available.
 */
    {   numhdr(a) += pack_hdrlength(1L);
        len = (len >> 2) - 1;
        bignum_digits(a)[len-1] = clear_top_bit(bignum_digits(a)[len-1]);
        bignum_digits(a)[len] = top_bit_set(msd) ? -1 : 0;
        return a;
    }
}

static Lisp_Object plusbb(Lisp_Object a, Lisp_Object b)
/*
 * add two bignums.
 */
{
    int32 la = bignum_length(a),
          lb = bignum_length(b),
          i, s, carry;
    Lisp_Object c, nil;
    if (la < lb)    /* maybe swap order of args */
    {   Lisp_Object t = a;
        int32 t1;
        a = b; b = t;
        t1 = la; la = lb; lb = t1;
    }
/*
 * now (a) is AT LEAST as long as b.  I have special case code for
 * when both args are single-word bignums, since I expect that to be
 * an especially common case.
 */
    if (la == 8)    /* and hence b also has only 1 digit */
    {   int32 va = bignum_digits(a)[0],
              vb = bignum_digits(b)[0],
              vc = va + vb;
        if (signed_overflow(vc)) /* we have a 2-word bignum result */
        {   Lisp_Object w = getvector(TAG_NUMBERS, TYPE_BIGNUM, 12);
            errexit();
            bignum_digits(w)[0] = clear_top_bit(vc);
            bignum_digits(w)[1] = top_bit_set(vc) ? -1 : 0;
            bignum_digits(w)[2] = 0;
            return w;
        }
/*
 * here the result fits into one word - maybe it will squash down into
 * a fixnum?
 */
        else
        {   vb = vc & fix_mask;
            if (vb == 0 || vb == fix_mask) return fixnum_of_int(vc);
            else return make_one_word_bignum(vc);
        }
    }
    push2(a, b);
    c = getvector(TAG_NUMBERS, TYPE_BIGNUM, la);
    pop2(b, a);
    errexit();
    la = (la >> 2) - 2;
    lb = (lb >> 2) - 2;
    carry = 0;
/*
 * Add all but the top digit of b
 */
    for (i=0; i<lb; i++)
    {   carry = bignum_digits(a)[i] + bignum_digits(b)[i] + top_bit(carry);
        bignum_digits(c)[i] = clear_top_bit(carry);
    }
    if (la == lb) s = bignum_digits(b)[i];
    else
/*
 * If a is strictly longer than b I sign extend b here and add in as many
 * copies of 0 or -1 as needbe to get up to the length of a.
 */
    {   s = bignum_digits(b)[i];
        carry =  bignum_digits(a)[i] + clear_top_bit(s) + top_bit(carry);
        bignum_digits(c)[i] = clear_top_bit(carry);
        if (s < 0) s = -1; else s = 0;
        for (i++; i<la; i++)
        {   carry = bignum_digits(a)[i] + clear_top_bit(s) + top_bit(carry);
            bignum_digits(c)[i] = clear_top_bit(carry);
        }
    }
/*
 * the most significant digit is added using signed arithmetic so that I
 * can tell if it overflowed.
 */
    carry = bignum_digits(a)[i] + s + top_bit(carry);
    if (!signed_overflow(carry))
    {
/*
 * Here the number has not expanded - but it may be shrinking, and it can
 * shrink by any number of words, all the way down to a fixnum maybe.  Note
 * that I started with at least a 2-word bignum here.
 */
        int32 msd;
        bignum_digits(c)[i] = carry;
        if (carry == 0)
        {   int32 j = i-1;
            while ((msd = bignum_digits(c)[j]) == 0 && j > 0) j--;
/*
 * ... but I may need a zero word on the front if the next word down
 * has its top bit set... (top of 31 bits, that is)
 */
            if ((msd & 0x40000000) != 0)
            {   j++;
                if (i == j) return c;
            }
            if (j == 0)
            {   int32 s = bignum_digits(c)[0];
                if ((s & fix_mask) == 0) return fixnum_of_int(s);
            }
/*
 * If I am shrinking by one word and had an even length to start with
 * I do not have to mess about so much.
 */
            if (j == i-1 && (i & 1) == 0)
            {   numhdr(c) -= pack_hdrlength(1L);
                return c;
            }
            numhdr(c) -= pack_hdrlength(i - j);
            i = (i+1) | 1;
            j = (j+1) | 1;     /* Round up to odd index */
/*
 * I forge a header word to allow the garbage collector to skip over
 * (and in due course reclaim) the space that turned out not to be needed.
 */
            bignum_digits(c)[j] = make_bighdr(i - j);
            return c;
        }
/*
 * Now do all the same sorts of things but this time for negative numbers.
 */
        else if (carry == -1)
        {   int32 j = i-1;
            msd = carry;    /* in case j = 0 */
            while ((msd = bignum_digits(c)[j]) == 0x7fffffff && j > 0) j--;
            if ((msd & 0x40000000) == 0)
            {   j++;
                if (i == j) return c;
            }
            if (j == 0)
            {   int32 s = bignum_digits(c)[0] | ~0x7fffffff;
                if ((s & fix_mask) == fix_mask) return fixnum_of_int(s);
            }
            if (j == i-1 && (i & 1) == 0)
            {   bignum_digits(c)[i] = 0;
                bignum_digits(c)[i-1] |= ~0x7fffffff;
                numhdr(c) -= pack_hdrlength(1);
                return c;
            }
            numhdr(c) -= pack_hdrlength(i - j);
            bignum_digits(c)[j+1] = 0;
            bignum_digits(c)[j] |= ~0x7fffffff;
            i = (i+1) | 1;
            j = (j+1) | 1;     /* Round up to odd index */
            bignum_digits(c)[j] = make_bighdr(i - j);
            return c;
        }
        return c;
    }
    else
    {   bignum_digits(c)[i] = carry;
        return lengthen_by_one_bit(c, carry);
    }
}

#ifdef COMMON
#define plusbr(a, b) plusir(a, b)

#define plusbc(a, b) plusic(a, b)
#endif

#define plusbf(a, b) plussf(a, b)

#ifdef COMMON
#define plusri(a, b) plusir(b, a)

#define plusrs(a, b) plusbs(a, b)

#define plusrb(a, b) plusri(a, b)

static Lisp_Object plusrr(Lisp_Object a, Lisp_Object b)
/*
 * Adding two ratios involves some effort to keep the result in
 * lowest terms.
 */
{
    Lisp_Object nil = C_nil;
    Lisp_Object na = numerator(a), nb = numerator(b);
    Lisp_Object da = denominator(a), db = denominator(b);
    Lisp_Object w = nil;
    push5(na, nb, da, db, nil);
#define g   stack[0]
#define db  stack[-1]
#define da  stack[-2]
#define nb  stack[-3]
#define na  stack[-4]
    g = gcd(da, db);
    nil = C_nil;
    if (exception_pending()) goto fail;
/*
 * all the calls to quot2() in this procedure are expected - nay required -
 * to give exact integer quotients.
 */
    db = quot2(db, g);
    nil = C_nil;
    if (exception_pending()) goto fail;
    g = quot2(da, g);
    nil = C_nil;
    if (exception_pending()) goto fail;
    na = times2(na, db);
    nil = C_nil;
    if (exception_pending()) goto fail;
    nb = times2(nb, g);
    nil = C_nil;
    if (exception_pending()) goto fail;
    na = plus2(na, nb);
    nil = C_nil;
    if (exception_pending()) goto fail;
    da = times2(da, db);
    nil = C_nil;
    if (exception_pending()) goto fail;
    g = gcd(na, da);
    nil = C_nil;
    if (exception_pending()) goto fail;
    na = quot2(na, g);
    nil = C_nil;
    if (exception_pending()) goto fail;
    da = quot2(da, g);
    nil = C_nil;
    if (exception_pending()) goto fail;
    w = make_ratio(na, da);
/*
 * All the goto statements and the label seem a fair way of expressing
 * the common action that has to be taken if an error or interrupt is
 * detected during any of the intermediate steps here.  Anyone who
 * objects can change it if they really want...
 */
fail:
    popv(5);
    return w;
#undef na
#undef nb
#undef da
#undef db
#undef g
}

#define plusrc(a, b) plusic(a, b)

#define plusrf(a, b) plussf(a, b)

#define plusci(a, b) plusic(b, a)

#define pluscs(a, b) plussc(b, a)

#define pluscb(a, b) plusbc(b, a)

#define pluscr(a, b) plusrc(b, a)

static Lisp_Object pluscc(Lisp_Object a, Lisp_Object b)
/*
 * Add complex values.
 */
{
    Lisp_Object c, nil;
    push2(a, b);
    c = plus2(imag_part(a), imag_part(b));
    pop2(b, a);
    errexit();
    a = plus2(real_part(a), real_part(b));
    errexit();
    return make_complex(a, c);
}

#define pluscf(a, b) plusfc(b, a)
#endif

#define plusfi(a, b) plusif(b, a)

#ifdef COMMON
#define plusfs(a, b) plussf(b, a)
#endif

#define plusfb(a, b) plusbf(b, a)

#ifdef COMMON
#define plusfr(a, b) plusrf(b, a)

#define plusfc(a, b) plusic(a, b)
#endif

static Lisp_Object plusff(Lisp_Object a, Lisp_Object b)
/*
 * Add two boxed floats - the type of the result must match the
 * longer of the types of the arguments, hence the extra
 * messing about.
 */
{
#ifdef COMMON
    int32 ha = type_of_header(flthdr(a)), hb = type_of_header(flthdr(b));
#endif
    double d;
/*
 * This is written as a declaration followed by a separate assignment to
 * d because I hit a compiler bug on a VAX once otherwise.
 */
    d = float_of_number(a) + float_of_number(b);
#ifdef COMMON
    if (ha == TYPE_LONG_FLOAT || hb == TYPE_LONG_FLOAT)
        ha = TYPE_LONG_FLOAT;
    else if (ha == TYPE_DOUBLE_FLOAT || hb == TYPE_DOUBLE_FLOAT)
        ha = TYPE_DOUBLE_FLOAT;
    else ha = TYPE_SINGLE_FLOAT;
    return make_boxfloat(d, ha);
#else
    return make_boxfloat(d, TYPE_DOUBLE_FLOAT);
#endif
}

/*
 * and now for the dispatch code...
 */

/*
 * The following verifies that a number is properly formatted - a
 * fixnum if small enough or a decently normalised bignum.  For use when
 * there is suspicion of a bug wrt such matters. Call is
 *   validate_number("msg", numberToCheck, nX, nY)
 * where nX and nY must be numbers and are shown in any
 * diagnostic.
 */

void validate_number(char *s, Lisp_Object a, Lisp_Object b, Lisp_Object c)
{
    int32 la, w, msd;
    if (!is_numbers(a)) return;
    la = (length_of_header(numhdr(a))>>2)-2;
    if (la < 0)
        {   trace_printf("%s: number with no digits (%.8x)\n", s, numhdr(a));
            if (is_number(b)) prin_to_trace(b), trace_printf("\n");
            if (is_number(c)) prin_to_trace(c), trace_printf("\n");
            my_exit(EXIT_FAILURE);
        }
    if (la == 0)
    {   msd = bignum_digits(a)[0];
        w = msd & fix_mask;
        if (w == 0 || w == fix_mask)
        {   trace_printf("%s: %.8x should be fixnum\n", s, msd);
            if (is_number(b)) prin_to_trace(b), trace_printf("\n");
            if (is_number(c)) prin_to_trace(c), trace_printf("\n");
            my_exit(EXIT_FAILURE);
        }
        if (signed_overflow(msd))
        {   trace_printf("%s: %.8x should be two-word\n", s, msd);
            if (is_number(b)) prin_to_trace(b), trace_printf("\n");
            if (is_number(c)) prin_to_trace(c), trace_printf("\n");
            my_exit(EXIT_FAILURE);
        }
        return;
    }
    msd = bignum_digits(a)[la];
    if (signed_overflow(msd))
    {   trace_printf("%s: %.8x should be longer\n", s, msd);
        if (is_number(b)) prin_to_trace(b), trace_printf("\n");
        if (is_number(c)) prin_to_trace(c), trace_printf("\n");
        my_exit(EXIT_FAILURE);
    }
    if (msd == 0 && ((msd = bignum_digits(a)[la-1]) & 0x40000000) == 0)
    {   trace_printf("%s: 0: %.8x should be shorter\n", s, msd);
        if (is_number(b)) prin_to_trace(b), trace_printf("\n");
        if (is_number(c)) prin_to_trace(c), trace_printf("\n");
        my_exit(EXIT_FAILURE);
    }
    if (msd == -1 && ((msd = bignum_digits(a)[la-1]) & 0x40000000) != 0)
    {   trace_printf("%s: -1: %.8x should be shorter\n", s, msd);
        if (is_number(b)) prin_to_trace(b), trace_printf("\n");
        if (is_number(c)) prin_to_trace(c), trace_printf("\n");
        my_exit(EXIT_FAILURE);
    }
}


Lisp_Object plus2(Lisp_Object a, Lisp_Object b)
/*
 * I probably want to change the specification of plus2 so that the fixnum +
 * fixnum case is always expected to be done before the main body of the code
 * is entered.  Well maybe even if I do that it then costs very little to
 * include the fixnum code here as well, so I will not delete it.
 */
{
    switch ((int)a & TAG_BITS)
    {
case TAG_FIXNUM:
        switch ((int)b & TAG_BITS)
        {
    case TAG_FIXNUM:
/*
 * This is where fixnum + fixnum arithmetic happens - the case I most want to
 * make efficient.
 */
            {   int32 r = int_of_fixnum(a) + int_of_fixnum(b);
                int32 t = r & fix_mask;
                if (t == 0 || t == fix_mask) return fixnum_of_int(r);
                else return make_one_word_bignum(r);
            }
#ifdef COMMON
    case TAG_SFLOAT:
            return plusis(a, b);
#endif
    case TAG_NUMBERS:
            {   int32 hb = type_of_header(numhdr(b));
                switch (hb)
                {
        case TYPE_BIGNUM:
                return plusib(a, b);
#ifdef COMMON
        case TYPE_RATNUM:
                return plusir(a, b);
        case TYPE_COMPLEX_NUM:
                return plusic(a, b);
#endif
        default:
                return aerror1("bad arg for plus", b);
                }
            }
    case TAG_BOXFLOAT:
            return plusif(a, b);
    default:
            return aerror1("bad arg for plus",  b);
        }
#ifdef COMMON
case TAG_SFLOAT:
        switch (b & TAG_BITS)
        {
    case TAG_FIXNUM:
            return plussi(a, b);
    case TAG_SFLOAT:
            {   Float_union aa, bb;
                aa.i = a - TAG_SFLOAT;
                bb.i = b - TAG_SFLOAT;
                aa.f = (float)(aa.f + bb.f);
                return (aa.i & ~(int32)0xf) + TAG_SFLOAT;
            }
    case TAG_NUMBERS:
            {   int32 hb = type_of_header(numhdr(b));
                switch (hb)
                {
        case TYPE_BIGNUM:
                return plussb(a, b);
        case TYPE_RATNUM:
                return plussr(a, b);
        case TYPE_COMPLEX_NUM:
                return plussc(a, b);
        default:
                return aerror1("bad arg for plus",  b);
                }
            }
    case TAG_BOXFLOAT:
            return plussf(a, b);
    default:
            return aerror1("bad arg for plus",  b);
        }
#endif
case TAG_NUMBERS:
        {   int32 ha = type_of_header(numhdr(a));
            switch (ha)
            {
    case TYPE_BIGNUM:
                switch ((int)b & TAG_BITS)
                {
            case TAG_FIXNUM:
                    return plusbi(a, b);
#ifdef COMMON
            case TAG_SFLOAT:
                    return plusbs(a, b);
#endif
            case TAG_NUMBERS:
                    {   int32 hb = type_of_header(numhdr(b));
                        switch (hb)
                        {
                case TYPE_BIGNUM:
                        return plusbb(a, b);
#ifdef COMMON
                case TYPE_RATNUM:
                        return plusbr(a, b);
                case TYPE_COMPLEX_NUM:
                        return plusbc(a, b);
#endif
                default:
                        return aerror1("bad arg for plus",  b);
                        }
                    }
            case TAG_BOXFLOAT:
                    return plusbf(a, b);
            default:
                    return aerror1("bad arg for plus",  b);
                }
#ifdef COMMON
    case TYPE_RATNUM:
                switch (b & TAG_BITS)
                {
            case TAG_FIXNUM:
                    return plusri(a, b);
            case TAG_SFLOAT:
                    return plusrs(a, b);
            case TAG_NUMBERS:
                    {   int32 hb = type_of_header(numhdr(b));
                        switch (hb)
                        {
                case TYPE_BIGNUM:
                        return plusrb(a, b);
                case TYPE_RATNUM:
                        return plusrr(a, b);
                case TYPE_COMPLEX_NUM:
                        return plusrc(a, b);
                default:
                        return aerror1("bad arg for plus",  b);
                        }
                    }
            case TAG_BOXFLOAT:
                    return plusrf(a, b);
            default:
                    return aerror1("bad arg for plus",  b);
                }
    case TYPE_COMPLEX_NUM:
                switch (b & TAG_BITS)
                {
            case TAG_FIXNUM:
                    return plusci(a, b);
            case TAG_SFLOAT:
                    return pluscs(a, b);
            case TAG_NUMBERS:
                    {   int32 hb = type_of_header(numhdr(b));
                        switch (hb)
                        {
                case TYPE_BIGNUM:
                        return pluscb(a, b);
                case TYPE_RATNUM:
                        return pluscr(a, b);
                case TYPE_COMPLEX_NUM:
                        return pluscc(a, b);
                default:
                        return aerror1("bad arg for plus",  b);
                        }
                    }
            case TAG_BOXFLOAT:
                    return pluscf(a, b);
            default:
                    return aerror1("bad arg for plus",  b);
                }
#endif
    default:    return aerror1("bad arg for plus",  a);
            }
        }
case TAG_BOXFLOAT:
        switch ((int)b & TAG_BITS)
        {
    case TAG_FIXNUM:
            return plusfi(a, b);
#ifdef COMMON
    case TAG_SFLOAT:
            return plusfs(a, b);
#endif
    case TAG_NUMBERS:
            {   int32 hb = type_of_header(numhdr(b));
                switch (hb)
                {
        case TYPE_BIGNUM:
                return plusfb(a, b);
#ifdef COMMON
        case TYPE_RATNUM:
                return plusfr(a, b);
        case TYPE_COMPLEX_NUM:
                return plusfc(a, b);
#endif
        default:
                return aerror1("bad arg for plus",  b);
                }
            }
    case TAG_BOXFLOAT:
            return plusff(a, b);
    default:
            return aerror1("bad arg for plus",  b);
        }
default:
        return aerror1("bad arg for plus",  a);
    }
}

Lisp_Object difference2(Lisp_Object a, Lisp_Object b)
{
    Lisp_Object nil;
    switch ((int)b & TAG_BITS)
    {
case TAG_FIXNUM:
        if (is_fixnum(a))
        {
            int32 r = int_of_fixnum(a) - int_of_fixnum(b);
            int32 t = r & fix_mask;
            if (t == 0 || t == fix_mask) return fixnum_of_int(r);
            else return make_one_word_bignum(r);
        }
        else if (b != ~0x7ffffffe) return plus2(a, 2*TAG_FIXNUM-b);
        else
        {   push(a);
            b = make_one_word_bignum(-int_of_fixnum(b));
            break;
        }
case TAG_NUMBERS:
        push(a);
        if (type_of_header(numhdr(b)) == TYPE_BIGNUM) b = negateb(b);
        else b = negate(b);
        break;
case TAG_BOXFLOAT:
default:
        push(a);
        b = negate(b);
        break;
    }
    pop(a);
    errexit();
    return plus2(a, b);
}

Lisp_Object add1(Lisp_Object p)
/*
 * Increment a number.  Short cut when the number is a fixnum, otherwise
 * just hand over to the general addition code.
 */
{
    if (is_fixnum(p))
    {   unsigned32 r = (int32)p + 0x10;
                                  /* fixnums have data shifted left 4 bits */
        if (r == ~0x7ffffffe)     /* The ONLY possible overflow case here  */
            return make_one_word_bignum(1 + int_of_fixnum(p));
        else return (Lisp_Object)r;
    }
    else return plus2(p, fixnum_of_int(1));
}

Lisp_Object sub1(Lisp_Object p)
/*
 * Decrement a number.  Short cut when the number is a fixnum, otherwise
 * just hand over to the general addition code.
 */
{
    if (is_fixnum(p))
    {   if (p == ~0x7ffffffe)     /* The ONLY possible overflow case here  */
            return make_one_word_bignum(int_of_fixnum(p) - 1);
        else return (Lisp_Object)(p - 0x10);
    }
    else return plus2(p, fixnum_of_int(-1));
}

/* end of arith01.c */




REDUCE Historical
REDUCE Sourceforge Project | Historical SVN Repository | GitHub Mirror | SourceHut Mirror | NotABug Mirror | Chisel Mirror | Chisel RSS ]