All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jclarion.clarion.ClarionDecimal Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2010, by Andrew Barnham
 *
 * The contents of this file are subject to
 * GNU Lesser General Public License (LGPL), v.3
 * http://www.gnu.org/licenses/lgpl.txt
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 */
package org.jclarion.clarion;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;

import org.jclarion.clarion.memory.CMem;
import org.jclarion.clarion.primative.AbstractStateFactory;
import org.jclarion.clarion.primative.AbstractStateGetter;
import org.jclarion.clarion.primative.GlobalStateGetter;
import org.jclarion.clarion.primative.ThreadStateGetter;

/**
 * Model decimal. Internall stored as java.math.BigDecimal
 * 
 * @author barney
 *
 */
public class ClarionDecimal extends ClarionObject 
{
    public static final int DECIMAL = 1;
    public static final int PDECIMAL = 2;
    
    
    /**
     * Max size of the decimal. -1 = no limit.
     */
    private int size;
    
    /**
     *  Number of bits reserved for precision. -1 = no limit
     */
    private int precision;

    private int encoding=DECIMAL;

    
    public static class State
    {
        private BigDecimal val;

        public State(ClarionDecimal.State base) {
            val=base.val;
        }
        public State() {
        }
    }
    
    private static class StateFactory extends AbstractStateFactory
    {
        @Override
        public State cloneState(State base) {
            return new State(base);
        }

        @Override
        public State createState() {
            return new State();
        }
    }
    private static StateFactory factory=new StateFactory();
    
    private AbstractStateGetter state=new GlobalStateGetter(factory);
    
    public void initThread()
    {
        if (!state.isThreaded()) state=new ThreadStateGetter(factory,state); 
        super.initThread();
    }
    
    @Override
    public Object getLockedObject(Thread t) 
    {
        if (!state.isThreaded()) return this;
        return new ClarionDecimal(this,t);
    }

    public ClarionDecimal(ClarionDecimal base,Thread lock)
    {
        super(base,lock);
        this.encoding=base.encoding;
        this.precision=base.precision;
        this.size=base.size;
        if (lock!=null) this.state=base.state.getLockedGetter(lock);
    }

    
    /**
     * Create new clarion decimal. Unbounded size and precision. Defaults to 0
     */
    public ClarionDecimal()
    {
        state.get().val = BigDecimal.ZERO;
        size=-1;
        precision=-1;
    }
    
    public int getSize()
    {
        return size;
    }
    
    public int getPrecision()
    {
        return precision;
    }
    
    @Override
    public boolean isConstrained() 
    {
        return size!=-1 || precision!=-1;
    }
    
    
    /**
     * Create new clarion decimal with given value. Size and precision are
     * unbounded
     */
    public ClarionDecimal(String value)
    {
        try {
            state.get().val = new BigDecimal(value);
        } catch (NumberFormatException ex) {
            state.get().val =BigDecimal.ZERO;
        }
        size=-1;
        precision=-1;
    }
    
    public BigDecimal toBigDecimal()
    {
        return state.get().val;
    }

    public ClarionDecimal(BigDecimal value)
    {
        state.get().val = value;
        size=-1;
        precision=-1;
    }

    /**
     * Create new clarion decimal with given size and zero precision.
     * Default value is 0
     */
    public ClarionDecimal(int value)
    {
        this.size=-1;
        precision=-1;
        
        state.get().val = BigDecimal.valueOf(value);
    }

    /**
     * Create new clarion decimal with given size and precision.
     * Default value is 0
     */
    public ClarionDecimal(int size,int precision)
    {
        this.size=size;
        this.precision=precision;
        state.get().val = cleanValue(BigDecimal.ZERO);
    }

    /**
     * Create new clarion decimal with given size and precision and value
     */
    public ClarionDecimal(int size,int precision,Object value)
    {
        this.size=size;
        this.precision=precision;
        try {
            state.get().val = cleanValue(new BigDecimal(value.toString()));
        } catch (NumberFormatException ex) {
            state.get().val = cleanValue(BigDecimal.ZERO);
        }
    }
    
    public ClarionDecimal setThread()
    {
        initThread();
        return this;
    }
    
    public String toString()
    {
        return state.get().val.toPlainString();
    }

    /**
     * clone object
     * @return
     */
    public ClarionDecimal like()
    {
        ClarionDecimal n = new ClarionDecimal(size,precision,state.get().val);
        n.setEncoding(encoding);
        return n;
    }
    
    /**
     * return array clone of the object based on size. Note java object
     * array returned is one larger than specified as clarion starts arrays at
     * offset '1' whilst java as offset '0' 
     * 
     * @param size
     * @return
     */
    public ClarionArray dim(int size)
    {
    	return new ClarionArray(this,size);
    }

    @Override
    public ClarionObject add(ClarionObject object) {
        return new ClarionDecimal(state.get().val.add(object.getDecimal().state.get().val));
    }

    @Override
    public int compareTo(ClarionObject object) {
        if (object instanceof ClarionString) {
            return -object.compareTo(this);
        }
        if (object instanceof ClarionReal) {
            return -object.compareTo(this);
        }
        return state.get().val.compareTo(object.getDecimal().state.get().val);
    }

    @Override
    public int intValue() {
        BigInteger i = state.get().val.toBigInteger();
        if (i.bitLength()>31) return 0; // number too big
        return i.intValue();
    }

    @Override
    public ClarionObject multiply(ClarionObject object) {
        return new ClarionDecimal(state.get().val.multiply(object.getDecimal().state.get().val));
    }

    @Override
    public void setValue(ClarionObject object) {
        BigDecimal new_value;
        if (object==null) {
            new_value=BigDecimal.ZERO;
        } else {
            new_value=cleanValue(object.getDecimal().state.get().val);
        }
        
        if (new_value.compareTo(state.get().val)!=0) {
            state.get().val=new_value;
            notifyChange();
        }
    }
    
    private BigDecimal cleanValue(BigDecimal i)
    {
        if (size==-1) return i;
        
        if (i.scale()!=precision) {
            i=i.setScale(precision,BigDecimal.ROUND_HALF_UP);
        }
        
        if (i.precision()>size) {
            
            char bits[] = new char[size];
            for (int scan=0;scan>4)&0x0f;
                int lo = (val)&0x0f;
                int mul;
                int nval;
                if (encoding==PDECIMAL && read==0) {
                    mul=10;
                    nval=hi;
                    if (lo==0x0d) negate=true;
                } else {
                    mul=100;
                    nval=hi*10+lo;
                }
                result=result.multiply(BigInteger.valueOf(mul)).add(BigInteger.valueOf(nval));
            }
            
            if (read==0) break;
            
            val=is.readByte();
            read--;
        }
        
        if (negate) result=result.negate();
        
        setValue( new BigDecimal(result,precision) );
    }

    /**
     * Decimal encoding is based on nybbles.
     * Nybble 0 = sign
     * Nybble 1 = MSD (most significant decimal)
     * Nybble 2 = LSD (least significant decimal)
     * 
     * Decimal always encodes odd # of places.
     * 
     */
    @Override
    public void serialize(CMem is) 
    {
        BigDecimal value = state.get().val;
        
        if (size==-1) {
            getString().serialize(is);
            return;
        }
        
        byte result[] = new byte[size/2+1];
        BigInteger bi = value.unscaledValue().abs();
            
        int scan=0;
        if (encoding==PDECIMAL) {
            BigInteger results[] =bi.divideAndRemainder(BigInteger.TEN);
            bi=results[0];
            int val = results[1].intValue();
            result[result.length-1-scan]=(byte)((val)<<4);
            scan++;
        }
        
        while (bi.signum()>0) {
            BigInteger results[] =bi.divideAndRemainder(BigInteger.valueOf(100));
            bi=results[0];
                
            int val = results[1].intValue();
            try {
                result[result.length-1-scan]=(byte)(((val/10)<<4)+(val%10));
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw(ex);
            }
            scan++;
        }
        if (encoding==DECIMAL) {
            if (value.signum()<0) {
                result[0]=(byte)(result[0]|0x80);
            }
        } else {
            if (value.signum()>0) {
                result[result.length-1]=(byte)(result[result.length-1]|0x0c);
            } else {
                result[result.length-1]=(byte)(result[result.length-1]|0x0d);
            }
        }
        is.writeBytes(result,0,result.length);
    }
    
    public boolean isDecimal()
    {
        return true;
    }

    @Override
    public ClarionObject genericLike() {
        return like();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy