PsyReal.java

package coneforest.psylla.core;

import coneforest.psylla.runtime.*;
import java.math.BigDecimal;
import java.math.BigInteger;

/**
*	The representation of {@code real}.
*/
@Type("real")
public final class PsyReal
	implements PsyRealNumeric
{
	/**
	*	The number zero.
	*/
	public static final PsyReal ZERO=new PsyReal(0.D);

	/**
	*	The number one.
	*/
	public static final PsyReal	ONE=new PsyReal(1.D);

	/**
	*	The number two.
	*/
	public static final PsyReal TWO=new PsyReal(2.D);

	/**
	*	The number minus one.
	*/
	public static final PsyReal MINUS_ONE=new PsyReal(-1.D);

	/**
	*	The number π.
	*/
	public static final PsyReal PI=new PsyReal(Math.PI);

	/**
	*	The number e.
	*/
	public static final PsyReal E=new PsyReal(Math.E);

	/**
	*	The largest positive finite value, (2−<sup>−52</sup>)·2<sup>1023</sup>.
	*/
	public static final PsyReal MAX_VALUE=new PsyReal(Double.MAX_VALUE);

	/**
	*	The smallest positive nonzero value, 2<sup>−1074</sup>.
	*/
	public static final PsyReal MIN_VALUE=new PsyReal(Double.MIN_VALUE);

	/**
	*	The Not-a-Number (NaN) value.
	*/
	public static final PsyReal NAN=new PsyReal(Double.NaN);

	private final double value;

	/**
	*	Creates a new {@code real} representing the specified {@code double} value.
	*
	*	@param value a {@code double} value.
	*/
	public PsyReal(final double value)
	{
		this.value=value;
	}

	@Override
	public PsyIntegral psyToIntegral()
		throws PsyRangeCheckException
	{
		if(Double.isInfinite(value))
			throw new PsyRangeCheckException();
		return PsyIntegral.of(BigDecimal.valueOf(value).toBigInteger());
	}

	@Override
	public boolean isZero()
	{
		return value==0.D;
	}

	@Override
	public int intValue()
	{
		return (int)value;
	}

	@Override
	public long longValue()
	{
		return (long)value;
	}

	@Override
	public double doubleValue()
	{
		return value;
	}

	@Override
	public PsyRational rationalValue()
		throws PsyUndefinedResultException
	{
		if(value==0.D||value==-0.D)
			return PsyInteger.ZERO;
		if(Double.isNaN(value)||Double.isInfinite(value))
			throw new PsyUndefinedResultException();

		final long bits=Double.doubleToLongBits(value);
		final long mantissa=bits&0x000FFFFFFFFFFFFFL;
		final int exponent=(int)((bits&0x7FF0000000000000L)>>52);

		if(exponent==0)
			return (PsyRational)PsyInteger
					.of((bits&0x8000000000000000L)==0L? mantissa: -mantissa)
					.psyDiv(new PsyBigInteger(BigInteger.ZERO.flipBit(-51-Double.MAX_EXPONENT)));

		final int pow=exponent-Double.MAX_EXPONENT;

		PsyRational retval=(PsyRational)PsyInteger.of(mantissa)
				.psyDiv(PsyInteger.of(0x10000000000000L))
				.psyAdd(PsyInteger.ONE);
		if((bits&0x8000000000000000L)!=0L)
			retval=retval.psyNeg();
		return (PsyRational)(pow>=0?
				retval.psyMul((1+pow<Double.SIZE)?
						PsyInteger.of(1L<<pow):
						new PsyBigInteger(BigInteger.ZERO.flipBit(pow))):
				retval.psyDiv((1-pow<Double.SIZE)?
						PsyInteger.of(1L<<-pow):
						new PsyBigInteger(BigInteger.ZERO.flipBit(-pow))));
	}

	@Override
	public PsyReal psyNeg()
	{
		return new PsyReal(-value);
	}

	@Override
	public PsyReal psyAbs()
	{
		return new PsyReal(Math.abs(value));
	}

	@Override
	public PsyReal psySignum()
	{
		return new PsyReal(Math.signum(value));
	}

	@Override
	public PsyReal psyAdd(final PsyRealNumeric oNumeric)
	{
		return new PsyReal(value+oNumeric.doubleValue());
	}

	@Override
	public PsyReal psySub(final PsyRealNumeric oNumeric)
	{
		return new PsyReal(value-oNumeric.doubleValue());
	}

	@Override
	public PsyReal psyReciprocal()
	{
		return new PsyReal(1.D/value);
	}

	@Override
	public PsyReal psyMul(final PsyRealNumeric oRealNumeric)
	{
		return new PsyReal(value*oRealNumeric.doubleValue());
	}

	@Override
	public PsyReal psyDiv(final PsyRealNumeric oRealNumeric)
	{
		return new PsyReal(value/oRealNumeric.doubleValue());
	}

	@Override
	public PsyInteger psyRound()
	{
		return PsyInteger.of(Math.round(value));
	}

	@Override
	public PsyReal psyFloor()
	{
		return new PsyReal(Math.floor(value));
	}

	@Override
	public PsyReal psyCeiling()
	{
		return new PsyReal(Math.ceil(value));
	}

	@Override
	public int compareTo(final PsyRealNumeric oNumeric)
	{
		return Double.compare(value, oNumeric.doubleValue());
	}

	@Override
	public String toSyntaxString()
	{
		if(value==Double.NEGATIVE_INFINITY)
			return "-∞";
		if(value==Double.POSITIVE_INFINITY)
			return "∞";
		return String.valueOf(value);
	}

	@Override
	public int hashCode()
	{
		return Double.hashCode(value);
	}

	@Override
	public boolean equals(final Object obj)
	{
		return obj instanceof PsyReal oReal
				&& psyEq(oReal).booleanValue();
	}

	/**
	*	{@return the {@code real} initialized to the value represented by the specified {@link
	*	String} image}
	*
	*	@param image the string to be parsed.
	*/
	public static PsyReal parseLiteral(final String image)
	{
		return new PsyReal(switch(image)
			{
				case "∞"->Double.POSITIVE_INFINITY;
				case "-∞"->Double.NEGATIVE_INFINITY;
				default->Double.parseDouble(image);
			});
	}
}