content: package tim.prune.data;
  
  public class FractionalSeconds
  {
  	private final long _numerator;
  	private final byte _divisorDigits;
  
  
  	public FractionalSeconds(long inDegrees, long inDegFrac, int inDigits)
  	{
  		// Limit digits to maximum 13 to stop longs from overflowing
  		int digits = inDigits;
  		long degFrac = inDegFrac;
  		while (digits > 13)
  		{
  			digits--;
  			degFrac = degFrac / 10L;
  		}
  		long degDenom = getMultiplier(digits);
  		_numerator = (inDegrees * degDenom + degFrac) * 60L * 60L;
  		_divisorDigits = (byte) digits;
  	}
  
  	public FractionalSeconds(long inDegrees, long inMinutes, long inMinFrac, int inDigits)
  	{
  		long minDenom = getMultiplier(inDigits);
  		_numerator = ((inDegrees * 60L + inMinutes) * minDenom + inMinFrac)  * 60L;
  		_divisorDigits = (byte) inDigits;
  	}
  
  	public FractionalSeconds(long inDegrees, long inMinutes, long inSeconds, long inSecFrac, int inDigits)
  	{
  		long secDenom = getMultiplier(inDigits);
  		_numerator = ((inDegrees * 60L + inMinutes) * 60L + inSeconds) * secDenom + inSecFrac;
  		_divisorDigits = (byte) inDigits;
  	}
  
  	public static FractionalSeconds fromDouble(double inDegrees, int inNumDigits)
  	{
  		double value = Math.abs(inDegrees) * getMultiplier(inNumDigits);
  		long numerator = (long) (value * 60.0 * 60.0);
  		return new FractionalSeconds(numerator, (byte) inNumDigits);
  	}
  
  	private FractionalSeconds(long inNumerator, byte inNumDigits)
  	{
  		_numerator = inNumerator;
  		_divisorDigits = inNumDigits;
  	}
  
  	public int getTotalSeconds()
  	{
  		long seconds = _numerator / getMultiplier(_divisorDigits);
  		return (int) seconds;
  	}
  
  	public int getWholeDegrees() {
  		return getTotalSeconds() / 60 / 60;
  	}
  
  	public int getWholeMinutes() {
  		return (getTotalSeconds() / 60) % 60;
  	}
  
  	public int getWholeSeconds() {
  		return getTotalSeconds() % 60;
  	}
  
  	public String getFractionDegrees() {
  		return getFractionString(_numerator / 60L / 60L, _divisorDigits);
  	}
  
  	public String getFractionDegrees(int inNumDigits)
  	{
  		if (inNumDigits != _divisorDigits) {
  			return roundToSeconds(inNumDigits).divideBy(3600L).getFractionSeconds();
  		}
  		return getFractionDegrees();
  	}
  
  	public String getFractionMinutes() {
  		return getFractionString(_numerator / 60L, _divisorDigits);
  	}
  
  	public String getFractionMinutes(int inNumDigits)
  	{
  		if (inNumDigits != _divisorDigits) {
  			return roundToSeconds(inNumDigits).divideBy(60L).getFractionSeconds();
  		}
  		return getFractionMinutes();
  	}
  
  	public String getFractionSeconds() {
  		return getFractionString(_numerator, _divisorDigits);
  	}
  
  	public String getFractionSeconds(int inNumDigits) {
  		return roundToSeconds(inNumDigits).getFractionSeconds();
  	}
  
  	public double getDouble() {
  		return _numerator * 1.0 / getMultiplier(_divisorDigits) / 3600.0;
  	}
  
  	public FractionalSeconds roundToDegrees(int inNumDigits) {
  		return roundTo(60L * 60L, inNumDigits);
  	}
  
  	public FractionalSeconds roundToMinutes(int inNumDigits) {
  		return roundTo(60L, inNumDigits);
  	}
  
  	public FractionalSeconds roundToSeconds(int inNumDigits) {
  		return roundTo(1L, inNumDigits);
  	}
  
  	private FractionalSeconds roundTo(long inMultFactor, int inNumDigits)
  	{
  		if (inNumDigits > _divisorDigits)
  		{
  			long num = _numerator * getMultiplier(inNumDigits - _divisorDigits);
  			return new FractionalSeconds(num, (byte) inNumDigits);
  		}
  		else if (inNumDigits < _divisorDigits)
  		{
  			long factor = getMultiplier(_divisorDigits - inNumDigits);
  			long num = _numerator + factor / 2L * inMultFactor;
  			return new FractionalSeconds(num / factor, (byte) inNumDigits);
  		}
  		return this;
  	}
  
  	private FractionalSeconds divideBy(long divisor) {
  		return new FractionalSeconds(_numerator / divisor, _divisorDigits);
  	}
  
  	private static String getFractionString(long inNumerator, byte inDivisorDigits)
  	{
  		if (inDivisorDigits == 0) {
  			return "";
  		}
  		long factor = getMultiplier(inDivisorDigits);
  		long remainder = inNumerator % factor;
  		return String.format("%0" + inDivisorDigits + "d", remainder);
  	}
  
  	/** Return the multiplier given the number of digits */
  	private static long getMultiplier(int inNumDigits)
  	{
  		int numDigits = inNumDigits;
  		long value = 1L;
  		while (numDigits > 0) {
  			numDigits--;
  			value = value * 10L;
  		}
  		return value;
  	}
  
  	public boolean isWithinOneEightyDegrees()
  	{
  		final long oneEighty = 180L * 60L * 60L * getMultiplier(_divisorDigits);
  		return _numerator <= oneEighty;
  	}
  
  	public FractionalSeconds wrapToThreeSixtyDegrees() {
  		return new FractionalSeconds(_numerator % getThreeSixtyDegrees(), _divisorDigits);
  	}
  
  	private long getThreeSixtyDegrees() {
  		return 360L * 60L * 60L * getMultiplier(_divisorDigits);
  	}
  
  	public FractionalSeconds invert() {
  		return new FractionalSeconds(getThreeSixtyDegrees() - _numerator, _divisorDigits);
  	}
  }
  