/*
** BASE.C   Handles float to ASCII and ASCII to float conversion
**          in any number base.
** Will Menninger, 11-14-92, ANSI C
**
** Eval is a floating point expression evaluator.
** This file last updated in version 1.10
** For the version number, see eval.h
** Copyright (C) 1993  Will Menninger
**
** This program 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 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.,
** 675 Mass Ave, Cambridge, MA 02139, USA.
**
** The author until 9/93 can be contacted at:
** e-mail:     willus@ilm.pfc.mit.edu
** U.S. mail:  Will Menninger, 45 River St., #2, Boston, MA 02108-1124
**
**
*/

#include "eval.h"
#include <float.h>

#define SCI     1
#define FREE    3

static int  sigfig;
static int  maxexp;
static int  fixed;
static int  sci;
static int  dplace;
static int  maxdigits;
static int  ibase;
static int  obase;

static void sci_out(double val,char *buf);
static void intadd(int x);
static void free_out(double val,char *buf);
static int  digval(char c);
static int  ascii_int(int base,char *s);
static void addtobuf(char *s);
static void initbuf(char *s);
static void incbuf(char c);


void baseconv(double val,char *buf)

    {
    if (sci)
        sci_out(val,buf);
    else
        free_out(val,buf);
    }


void set_scinote(int val)

    {
    sci=val;
    }


void set_sigfig(int val)

    {
    sigfig=val;
    }


void set_dplace(int val)

    {
    dplace=val;
    }


void set_maxexp(int val)

    {
    maxexp=val;
    }


void set_fix(int val)

    {
    fixed=val;
    }


void setobase(int base)

    {
    obase=base;
    maxdigits=precision(base);
    }


void setibase(int base)

    {
    ibase=base;
    }


int getobase(void)

    {
    return(obase);
    }


int getibase(void)

    {
    return(ibase);
    }


int get_scinote(void)

    {
    return(sci);
    }


int get_fix(void)

    {
    return(fixed);
    }


int precision(int base)

    {
    return((int)(DBL_MANT_DIG*log(2.)/log((double)base)));
    }


void print_outtype(void)

    {
    printf("\nScientific notation:       %s\n",sci ? "ON" : "OFF");
    printf("Significant digits:        ");
    if (sigfig>0)
        printf("%d",sigfig);
    else
        printf("max");
    if (!sci && fixed)
        printf(" (ignored)");
    printf("\n");
    printf("Fixed precision:           %s",fixed ? "ON" : "OFF");
    if (sci)
        printf(" (trailing zeroes printed.)");
    printf("\n");
    printf("Digits after the decimal:  %d",dplace);
    if (sci || !fixed)
        printf(" (ignored)");
    printf("\n");
    printf("Exponent limit:            ");
    if (maxexp<0)
        printf("none");
    else
        printf("%d",maxexp);
    if (sci)
        printf(" (ignored)");
    printf("\n\n");
    }


double asciiconv(int base,char *s)

    {
    double  val;
    int     i,div,expc;

    for (i=0;s[i]!=EOS;i++)
        s[i]=tolower((int)s[i]);
    expc= base>14 ? '\\' : 'e';
    div=-1;
    val=0;
    for (i=0;s[i]!=expc && s[i]!='\\' && s[i]!=EOS;i++)
        {
        if (s[i]=='.')
            {
            div++;
            continue;
            }
        if (div>=0)
            div++;
        val=val*base+digval(s[i]);
        }
    if (div<0)
        div=0;
    if (s[i]!=EOS)
        div-=ascii_int(base,&s[i+1]);
    if (div!=0)
        val=val/pow((double)base,(double)div);
    return(val);
    }


static int ascii_int(int base,char *s)

    {
    int     i,val,sign;

    val=0;
    if (s[0]=='-' || s[0]=='+')
        {
        sign=(s[0]=='+');
        i=1;
        }
    else
        {
        sign=1;
        i=0;
        }
    for (;s[i]!=EOS;i++)
        val=base*val+digval(s[i]);
    return(sign ? val : -val);
    }


static int digval(char c)

    {
    c=tolower((int)c);
    return((int)(c<='9' ? c-'0' : c-'a'+10));
    }


static void sci_out(double val,char *buf)

    {
    double   ex,f;
    int      sf,sn,i,j,iex,dec,nz;

    initbuf(buf);
    if (sigfig<=0)
        sf=maxdigits;
    else
        sf=sigfig;
    if (val==0.)
        {
        incbuf((char)'0');
        if (fixed && sf>1)
            {
            incbuf((char)'.');
            for (i=1;i<sf;i++)
                incbuf((char)'0');
            }
        addtobuf(obase>14 ? " x 10^0":"e0");
        return;
        }
    sn=val>0;
    val=fabs(val);
    ex=log(val)/log((double)obase);
    ex=floor(ex)+1.;
    iex=ex;
    val=val+(obase/2.)*pow((double)obase,ex-1.-sf);
    f=val/pow((double)obase,ex);
    if (f>=1.)
        {
        f=f/obase;
        iex++;
        }
    initbuf(buf);
    dec=0;
    nz=0;
    if (!sn)
        incbuf((char)'-');
    for (i=0;i<sf;i++)
        {
        if (i==1)
            dec=1;
        if (i>=maxdigits)
            nz++;
        else
            {
            f=f*obase;
            if (f>=1.)
                {
                if (dec)
                    {
                    incbuf((char)'.');
                    dec=0;
                    }
                if (nz)
                    {
                    for (j=0;j<nz;j++)
                        incbuf((char)'0');
                    nz=0;
                    }
                j=f;
                incbuf((char)(j<10 ? '0'+j : 'A'+j-10));
                f=f-j;
                }
            else
                nz++;
            }
        }
    if (fixed)
        {
        if (dec)
            incbuf((char)'.');
        if (nz)
            for (j=0;j<nz;j++)
                incbuf((char)'0');
        }
    addtobuf(obase>14 ? " x 10^" : "e");
    intadd(iex-1);
    }


static void intadd(int x)

    {
    char    temp[100];
    int     i,j,sn;

    if (!x)
        {
        incbuf((char)'0');
        return;
        }
    sn=(x>=0);
    if (x<0)
        x=-x;
    for (i=0;x;i++)
        {
        j=x%obase;
        temp[i]=j<10 ? j+'0' : 'A'+j-10;
        x/=obase;
        }
    if (!sn)
        incbuf((char)'-');
    for (j=i-1;j>=0;j--)
        incbuf((char)temp[j]);
    }


static void free_out(double val,char *buf)

    {
    double  ex,f;
    int     sf,sn,i,j,k,iex,iex2,nz,dec;

    initbuf(buf);
    if (fixed || sigfig<=0)
        sf=maxdigits;
    else
        sf=sigfig;
    if (val==0.)
        {
        incbuf((char)'0');
        if (fixed)
            {
            if (dplace>0)
                incbuf((char)'.');
            for (i=0;i<dplace;i++)
                incbuf((char)'0');
            }
        return;
        }
    sn=val>0;
    val=fabs(val);
    ex=log(val)/log((double)obase);
    ex=floor(ex)+1.;
    if (maxexp>=0 && fabs(ex)>=maxexp+1)
        {
        sci_out(sn ? val : -val,buf);
        return;
        }
    iex=ex;
    if (fixed)
        iex2=-1-dplace;
    else
        iex2=ex-1-sf;
    val=val+(obase/2.)*pow((double)obase,(double)iex2);
    if (iex-maxdigits-1<iex2)
        val=val+pow((double)obase,(double)iex-maxdigits);
    f=val/pow((double)obase,ex);
    if (f>=1.)
        {
        f=f/obase;
        iex++;
        }
    k=iex;
    if (!sn)
        incbuf((char)'-');
    dec=0;
    nz=0;
    if (fixed && iex<=-dplace)
        {
        incbuf((char)'0');
        if (dplace>0)
            {
            incbuf((char)'.');
            for (j=0;j<dplace;j++)
                incbuf((char)'0');
            }
        return;
        }
    if (iex<=0)
        {
        incbuf((char)'0');
        dec=1;
        nz=-iex;
        }
    for (i=0;fixed || i<sf;i++)
        {
        f=f*obase;
        if (i<maxdigits && f>=1.)
            {
            if (dec)
                {
                incbuf((char)'.');
                dec=0;
                }
            if (nz)
                {
                for (j=0;j<nz;j++)
                    incbuf((char)'0');
                nz=0;
                }
            j=f;
            incbuf((char)(j<10 ? '0'+j : 'A'+j-10));
            f=f-j;
            }
        else
            if (k<=0)
                nz++;
            else
                incbuf((char)'0');
        k--;
        if (fixed && k<=-dplace)
            break;
        if (k==0)
            dec=1;
        }
    if (k>0)
        for (;k>0;k--)
            incbuf((char)'0');
    if (fixed)
        {
        if (dec)
            incbuf((char)'.');
        if (nz)
            for (j=0;j<nz;j++)
                incbuf((char)'0');
        }
    }


static void addtobuf(char *s)

    {
    int     i;

    for (i=0;s[i]!=EOS;i++)
        incbuf((char)s[i]);
    }


static int   bufindex;
static char *bufptr;

static void initbuf(char *s)

    {
    bufindex=0;
    bufptr=s;
    }


static void incbuf(char c)

    {
    if (bufindex>MAXOUTLEN-50)
        return;
    if (bufindex==MAXOUTLEN-50)
        {
        bufindex++;
        strcat(bufptr,"*** BUFFER OVERFLOW!");
        return;
        }
    bufptr[bufindex++]=c;
    bufptr[bufindex]=EOS;
    }


syntax highlighted by Code2HTML, v. 0.9.1