/*
 * Created on 01.jan.2004
 * Enables use of decimal numbers for MIDLets
 *
 */
/**
 * The DecimalNumber class enables use of decimal numbers for MIDlets. 
 * Contains methods for adding, subtracting, multiplying, dividing numbers and for "pretty output" with decimals.
 * @author Hallvord R. M. Steen
 */
public class DecimalNumber {
	/**
	 * the base number value of this object 
	 */
	public int number;
	/**
	 * the number of decimals after the point
	 */
	public int decPos;
	/**
	 * the DecimalNumber(int, int) constructor accepts two integers describing a 
	 * base number and the "decimal position" (number of digits after the point).
	 * @param int number the full number without decimal point
	 * @param int decPos the number of digits after the decimal point
	 * @exception IllegalArgumentException if second argument is smaller than 0
	 */
	public DecimalNumber(int number, int decPos) throws IllegalArgumentException{
		if(decPos<0)throw new IllegalArgumentException("Decimal position can not be < 0");
		
		this.number=number;
		this.decPos=decPos;
		
	}
	/**
	 * the DecimalNumber(int) constructor accepts one integer
	 * to create a DecimalNumber object with 0 digits after the decimal 
	 * @param int number the full number without decimal point
	 */
	public DecimalNumber(int number){
		this.number=number;
		this.decPos=0;
		
	}
	/**
	 * the DecimalNumber(String) constructor accepts a string containing digits and 
	 * possibly a decimal point - for instance "3.14"
	 * @param String number the full number including any decimal point formatted as a string
	 */

	public DecimalNumber(String number){
		StringBuffer n = new StringBuffer(number);
		if(number.indexOf('.')>-1) {
			this.decPos=number.length()- (number.lastIndexOf('.')+1);
			n=n.delete( number.indexOf('.'), number.indexOf('.')+1 );
		}else{
			this.decPos=0; 
		}
		
		this.number=Integer.valueOf( n.toString() ).intValue();
	}
	
/**
 * Addition method for DecimalNumber. Ensures both DecimalNumber objects 
 * have the same number of digits behind the decimal point before adding them. 
 * @param n DecimalNumber to add
 */
	public void add(DecimalNumber n){
		if(this.decPos>n.decPos){ 
			n.number*=expOfTen(this.decPos-n.decPos);
		}else if(this.decPos<n.decPos){
			this.number*=expOfTen(n.decPos-this.decPos);
			this.decPos=n.decPos;
		}
		
		this.number+=n.number;
		
	}
	/**
	 * addition method for DecimalNumber. Ensures both DecimalNumber objects 
	 * have the same number of digits behind the decimal point before adding them.
	 * @param String n String containing digits and possibly decimal point to add
	 */
	public void add(String n){
		DecimalNumber nn=new DecimalNumber(n);
		this.add(nn);
	}
	/**
	 * addition method for DecimalNumber
	 * @param int n integer to add
	 */

	public void add(int n){
		DecimalNumber nn=new DecimalNumber(n);
		this.add(nn);
	}
	/**
	 * subtraction method for DecimalNumber.  Ensures both DecimalNumber objects 
	 * have the same number of digits behind the decimal point before subtracting them.
	 * @param DecimalNumber n number to subtract
	 */

	public void subtract(DecimalNumber n){
		if(this.decPos>n.decPos){ // this.dP= 2, n. =1
			n.number*=expOfTen(this.decPos-n.decPos);
		}else if(this.decPos<n.decPos){
			this.number*=expOfTen(n.decPos-this.decPos);
			this.decPos=n.decPos;
		}
		
		this.number-=n.number;
		
	}

	/**
	 * subtraction method for DecimalNumber.  Ensures both DecimalNumber objects 
	 * have the same number of digits behind the decimal point before subtracting them.
	 * @param String n number to subtract formatted as string of digits, with or without 
	 * decimal point
	 */
	public void subtract(String n){
		DecimalNumber nn=new DecimalNumber(n);
		this.subtract(nn);
	}
	/**
	 * subtraction method for DecimalNumber.  Ensures both DecimalNumber objects 
	 * have the same number of digits behind the decimal point before subtracting them.
	 * @param int n number to subtract
	 */

	public void subtract(int n){
		DecimalNumber nn=new DecimalNumber(n);
		this.subtract(nn);
	}
	/**
	 * multiplication method for DecimalNumber. Multiplies the factors and 
	 * calculates the new decimal position.
	 * @param DecimalNumber n DecimalNumber to multiply by
	 */

	public void multiply(DecimalNumber n){
		this.number*=n.number;
		this.decPos+=n.decPos;
		
	}
	/**
	 * multiplication method for DecimalNumber. Multiplies the factors and 
	 * calculates the new decimal position.
	 * @param String s a string of digits with or without a decimal point to multiply by
	 */
	public void multiply(String s){
		DecimalNumber n = new DecimalNumber(s);
		this.multiply(n);
	}
	/**
	 * multiplication method for DecimalNumber. Multiplies the factors and 
	 * calculates the new decimal position.
	 * @param int i integer to multiply by
	 */

	public void multiply(int i){
		DecimalNumber n = new DecimalNumber(i);
		this.multiply(n);
	}
	/**
	 * division method for DecimalNumber. Divides the factors and 
	 * calculates the new decimal position.
	 * @param DecimalNumber n to divide by
	 */

	public void divide(DecimalNumber n){
		// enable slightly greater precision when dividing by bigger numbers.
		// should perhaps have a more exact routine for determining precision.
		if(this.isSmallerThan(n)){
			// result will be < 1
			this.decPos+=3;
			this.number=this.number*1000; 
		}
		this.number/=n.number;
		this.decPos-=n.decPos;
	}
	/**
	 * division method for DecimalNumber. Divides the factors and 
	 * calculates the new decimal position.
	 * @param String s containing a string of digits with or without decimal point to divide by
	 */
	public void divide(String s){
		DecimalNumber n = new DecimalNumber(s);
		this.divide(n);		
	}
	/**
	 * division method for DecimalNumber. Divides the factors and 
	 * calculates the new decimal position.
	 * @param int i to divide by
	 */

	public void divide(int i){
		DecimalNumber n = new DecimalNumber(i);
		this.divide(n);		
	}
	/**
	 * Checks if the value of this is smaller than the value of the argument 
	 * @param DecimalNumber n to compare with the value of this number 
	 * @return boolean true if the value of the argument is greather
	 */
	public boolean isSmallerThan(DecimalNumber n) {
		return (this.number/expOfTen(this.decPos) < n.number/expOfTen(n.decPos) );
	}
	/**
	 * Checks if the value of this is larger than the value of the argument  
	 * @param DecimalNumber n to compare with the value of this number
	 * @return boolean true if the value of the argument is smaller
	 */
	public boolean isLargerThan(DecimalNumber n) {
		return (this.number/expOfTen(this.decPos) > n.number/expOfTen(n.decPos) );
	}
	/**
	 * Checks if the value of this is smaller than the value of the argument 
	 * @param int i to compare with the value of this number 
	 * @return boolean true if the value of the argument is greather
	 */

	public boolean isSmallerThan(int i) {
		DecimalNumber dn=new DecimalNumber(i);
		return this.isSmallerThan(dn);
	}
	/**
	 * Checks if the value of this is larger than the value of the argument  
	 * @param int i to compare with the value of this number
	 * @return boolean true if the value of the argument is smaller
	 */
	public boolean isLargerThan(int i) {
		DecimalNumber dn=new DecimalNumber(i);
		return this.isLargerThan(dn);
	}
	
/**
 * 10 to the power of i 
 * @param int i the number of times we want ten multiplied with itself
 * @return 10 to the power of i
 */
private int expOfTen(int i) {
	int result=1;
	for( int j = 0; j<i ; j++ )
		result *=10;
	return result;
}

/**
 * Returns a string representation with decimal point in the correct position and superfluous
 * trailing 0s stripped. If you wish to control the number of decimals and avoid stripping 
 * trailing 0s see the {@link #round(int)} method. 
 * @return String representing the value of the object as a string of digits with decimal point correctly placed.
 * 
 */

public String toString(){
		StringBuffer result=new StringBuffer( String.valueOf(this.number) );
		if(result.length()<=this.decPos){
			// pad with 0s
			for( int i = this.decPos; i>=result.length()-1; i-- ) // one extra 0 to go before the decimal point
				result=result.insert(0, '0');
		}
		// 01234 
		if( this.decPos>0 ) result.insert(result.length()-(this.decPos), '.'); 
	/*
	 * Removing superfluous 0s at end
	 * If this is not desirable use round() rather than toString()
	 */
		while(result.charAt(result.length()-1)=='0'){ 
			result.deleteCharAt(result.length()-1);
		}
		if(result.charAt(result.length()-1)=='.') result.deleteCharAt(result.length()-1);
		return result.toString();	
		
	}
	
/**
 * DecimalNumber serialized to byte array. This is just a wrapper for 
 * the String.getBytes() function.
 * @return byte array
 */
public byte[] getBytes() {
	String s=this.toString();
	return s.getBytes();
}

/**
 * Function that returns a "prettified" string with the correct number of decimals. 
 * Rounds up if removing a digit that is larger than 4. Using '0' as argument will 
 * return the integer value of this object.
 * @param int numDec the number of decimals we want in the returned string
 * @return string with the correct number of decimals
 */
public String round( int numDec ) {
	StringBuffer nr = new StringBuffer(String.valueOf(this.number));
	int dPos=this.decPos;
	if(numDec<this.decPos){
		int mente = 0;
		int strlength= nr.length();
		for (int i=strlength-1 ; i >= strlength-(dPos-numDec); i--) {
			if(nr.charAt(i)>'4') mente=1; else mente=0;
			//System.out.println("\n\n hei> "+i+' '+nr.charAt(i)+' '+mente+' '+ nr+"\n\n");
			nr.deleteCharAt(i);
//			dPos--;
			// horrible line to "round up": convert StringBuffer to string, then to int, then add, 
			// then to string, then to StringBuffer. whoa. Does Java have to be this ugly??
			if(mente==1) nr=new StringBuffer( String.valueOf(Integer.valueOf(nr.toString()).intValue()+mente));
		}
		dPos=numDec;
	}else{
		// need to add 0s
		for(int i =0; i<(numDec-this.decPos); i++){
			nr.insert(nr.length()-1, '0');
			dPos++;
		}
	}
	if( dPos>0 ) nr.insert(nr.length()-(dPos), '.');
	return nr.toString();
}

}

