%mathpiper,def="RoundToPrecision"

//Retract("RoundToPrecision",*);

10 # RoundToPrecision( N_IsDecimal, newPrec_IsPositiveInteger ) <--
[
    //If(InVerboseMode(),Tell("RoundToPrecision_D",{N,newPrec}));
    Local(rep,sgn,oldInt,oldPrec,oldScale,strOInt,LS,BIP0);
    Local(newInt,newScale,newRep,ans);
    BIP0     := BuiltinPrecisionGet();
    sgn      := Sign(N);
    rep      := NumberToRep( If(sgn<0,-N,N) );
    oldInt   := rep[1];
    oldPrec  := rep[2];
    oldScale := rep[3];
    If( newPrec > oldPrec, BuiltinPrecisionSet(newPrec) );
    strOInt  := ExpressionToString(oldInt);
    LS       := Length(strOInt);
    //If(InVerboseMode(),
    //   [
    //      Tell("   ",rep);
    //      Tell("         ",oldInt);
    //      Tell("         ",strOInt);
    //      Tell("         ",LS);
    //      Tell("         ",{newPrec,oldPrec});
    //   ]
    //);
    
    Local(first,secnd,rem,ad);
    if ( newPrec = oldPrec )
        [ ans := N; ]
    else if ( newPrec < oldPrec )
        [
            first    := StringMidGet(1, newPrec, strOInt); 
            secnd    := StringMidGet(newPrec+1, LS-newPrec, strOInt);
            rem      := ToAtom(ConcatStrings(".",secnd));
            ad       := If(rem>=0.5, 1, 0 );
            newInt   := sgn * ( ToAtom(first) + ad );
            newScale := oldScale - ( oldPrec - newPrec ); 
            newRep   := {newInt,newPrec,newScale};
            ans      := RepToNumber(newRep);
            //If(InVerboseMode(),
            //   [
            //      Tell("         ",{first,secnd});
            //      Tell("         ",{rem,ad});
            //      Tell("         ",newRep);
            //      Tell("         ",ans);
            //   ]
            //);
        ]
    else
        [
            //If(InVerboseMode(),Tell("   newPrec > oldPrec "));
            Local(diffPrec);
            diffPrec := oldPrec - newPrec;
            newInt   := sgn * ToAtom(strOInt) * 10^(-diffPrec) ;
            newScale := oldScale - diffPrec;
            newRep   := {newInt,newPrec,newScale};
            //If(InVerboseMode(),[Tell("         ",diffPrec);Tell("         ",newRep);]);
            ans      := RepToNumber(newRep);
        ];
    BuiltinPrecisionSet(BIP0);
    ans;
];


15 # RoundToPrecision( N_IsInteger, newPrec_IsPositiveInteger ) <--
[
    //If(InVerboseMode(),Tell("RoundToPrecision_I",{N,newPrec}));
    Local(oldRep,oldPrec,decN,newDecN,ans);
    oldRep   := NumberToRep(N);
    oldPrec  := oldRep[2];
    decN     := N*1.0;
    newDecN  := RoundToPrecision( decN, newPrec );
    //If(InVerboseMode(),Tell("    ",oldRep));
    //If(InVerboseMode(),Tell("   ",oldPrec));
    //If(InVerboseMode(),Tell("   ",newPrec));
    //If(InVerboseMode(),Tell("   ",newDecN));
    If( newPrec <= oldPrec, 
        ans := Round(newDecN),
        ans := Round(newDecN*10^(newPrec-oldPrec))
    );
    ans;
];


20 # RoundToPrecision( N_IsComplex, newPrec_IsPositiveInteger ) <--
[
    //If(InVerboseMode(),Tell("RoundToPrecision_C",{N,newPrec}));
    Local(rr,ii);
    rr := Re(N);
    ii := Im(N);
    Complex(RoundToPrecision(rr,newPrec),RoundToPrecision(ii,newPrec));
];

%/mathpiper

  
   

    



%mathpiper_docs,name="RoundToPrecision",categories="Programmer Functions;Numerical (Arbitrary Precision)"
*CMD RoundToPrecision --- Rounds or Sets a number to the specified precision
*STD
*CALL
	RoundToPrecision(number,precision)
*PARMS
{number} -- a number (Decimal, Integer, or Complex) whose precision is to be changed
{precision} -- the new precision to be used


*DESC
This function changes the precision of an {arbitrary-precision number} (A.P.N.).
If the new precision is less than the original precision, the {significand}
will be appropriately rounded.  If the new precision is greater than the 
original precision, terminal zeros will be appended to the {significand}
and the indicated precision will be reset accordingly.

Internally, MathPiper represents an A.P.N. as Java a BigIntegers or
BigDecimal.  Java code handles calculations using such numbers.

All the information needed to correctly understand the precision attached to a number,
and the rounding and comparison thereof, is contained in the Java structure.

For a Decimal number (essentially anything with a decimal point), the representation
consists of an arbitrary-precision integer (called the {significand}) containing {all}
the significant digits of the number, and a {scale factor} telling where the implied 
decimal point is supposed to be placed with respect to the end of this integer.  
The {precision} of the number is usually just the number of digits in the 
{significand}.

The three components of the List representing a decimal number are, respectively,
{{BigInteger (unscaled), Precision, ScaleFactor}}.
Note that the second of these is redundent: only the BigInteger and the ScaleFactor
are needed to completely define the number.  The name 'BigInteger' is Java's
terminology for the {significand}.

For an Integer number, the integer is its own significand, and again, the number of
its digits gives its precision.  The representation is still a list, with the number
as first component and its precision as second.

For a Complex number, the representation is a List of Lists,
containing the representations of the Real and Imaginary parts of the number.

The best way to {consistently} deal with precision and rounding issues is by making use
of the information given by the representation (or "rep" for short)..

NOTE:  It is important to recognize the distinction (often misused or misunderstood)
between rounding "to a specified precision" (which this function does) and rounding
"to a specified number of decimal places", which in MathPiper is accomplished by
the function {RoundToPlace} (q.v.).  

For Decimal numbers and Decimal Complex numbers, the concept of Rounding DOWN to a 
given precision is well understood, and the concept of Rounding UP is pretty
clear also.  

However, for Integers and Complex Integers (Gaussian Integers), the
concept of Rounding down is somewhat obscure, and the concept of Rounding up 
makes very little sense at all.  An 8-digit integer, when rounded DOWN to 5
digits of precision, remains an 8-digit integer still, but the last 3 digits
have become zeros; the original number is still approximated by the new one.
But an 8-digit integer, when rounded UP to 10 digits of precision, has
two trailing zeros appended to it.  In the latter case, the new integer is
arguably not any kind of approximation of the old one.  

On the whole, it is probably best NOT to round integers UP.
*E.G.
In> dec:=123.45678
Result: 123.45678
In> NumberToRep(dec)
Result: {12345678,8,5}

In> dec2:=RoundToPrecision(dec,5)
Result: 123.46
In> NumberToRep(dec2)
Result: {12346,5,2}

In> dec3:=RoundToPrecision(dec,10)
Result: 123.4567800
In> NumberToRep(dec3)
Result: {1234567800,10,7}

In> cmplx:=12.345-I*567.891
Result: Complex(12.345,-567.891)
In> NumberToRep(cmplx)
Result: {{12345,5,3},{-567891,6,3}}

In> cmplx2:=RoundToPrecision(cmplx,4)
Result: Complex(12.34,-567.9)
In> NumberToRep(cmplx2)
Result: {{1234,4,2},{-5679,4,1}}

In> cmplx3:=RoundToPrecision(cmplx,8)
Result: Complex(12.345000,-567.89100)
In> NumberToRep(cmplx3)
Result: {{12345000,8,6},{-56789100,8,5}}

In> int:=12345678
Result: 12345678
In> NumberToRep(int)
Result: {12345678,8}

In> int2:=RoundToPrecision(int,5)
Result: 12346000
In> NumberToRep(int2)
Result: {12346000,8}

In> int3:=RoundToPrecision(int,10)
Result: 1234567800
In> NumberToRep(int3)
Result: {1234567800,10}
*SEE RoundToPlace, RoundToN, NumberToRep, DumpNumber
%/mathpiper_docs

