/*
 * dms - calculate in degrees, minutes, and seconds (based on deg)
 *
 * Copyright (C) 1999,2010  David I. Bell and Landon Curt Noll
 *
 * Calc is open software; you can redistribute it and/or modify it under
 * the terms of the version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * Calc is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU Lesser General
 * Public License for more details.
 *
 * A copy of version 2.1 of the GNU Lesser General Public License is
 * distributed with calc under the filename COPYING-LGPL.  You should have
 * received a copy with calc; if not, write to Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Under source code control:	1990/02/15 01:50:33
 * File existed as early as:	before 1990
 *
 * Share and enjoy!  :-)	http://www.isthe.com/chongo/tech/comp/calc/
 */


obj dms {deg, min, sec};

define dms(deg, min, sec)
{
    local obj dms ans;		/* return value */

    /* default missing args to 0 */
    if (isnull(sec)) {
	sec = 0;
    }
    if (isnull(min)) {
	min = 0;
    }

    /* load object */
    ans.deg = deg;
    ans.min = min;
    ans.sec = sec;

    /* return properly formed object */
    ans = fixdms(ans);
    return ans;
}


define dms_add(a, b)
{
    local obj dms ans;		/* return value */

    /* initalize value to 1st arg */
    if (istype(a, ans)) {
	/* 1st arg is dms object, load it */
	ans.deg = a.deg;
	ans.min = a.min;
	ans.sec = a.sec;
    } else {
	/* 1st arg is not dms, assume scalar degrees */
	ans.deg = a;
	ans.min = 0;
	ans.sec = 0;
    }

    /* add value of 2nd arg */
    if (istype(b, ans)) {
	/* 2nd arg is dms object, add it */
	ans.deg += b.deg;
	ans.min += b.min;
	ans.sec += b.sec;
    } else {
	/* 2nd arg is not dms, add scalar degrees */
	ans.deg += b;
    }

    /* return normalized result */
    ans = fixdms(ans);
    return ans;
}


define dms_neg(a)
{
    local obj dms ans;		/* return value */

    /* negate argument */
    if (istype(a, ans)) {
	/* 1st arg is dms object, load it */
	ans.deg = -a.deg;
	ans.min = -a.min;
	ans.sec = -a.sec;
    } else {
	/* 2nd arg is not dms, negate scalar degrees */
	ans.deg = -a;
	ans.min = 0;
	ans.sec = 0;
    }

    /* return normalized result */
    ans = fixdms(ans);
    return ans;
}


define dms_sub(a, b)
{
    local obj dms ans;		/* return value */

    /* initalize value to 1st arg */
    if (istype(a, ans)) {
	/* 1st arg is dms object, load it */
	ans.deg = a.deg;
	ans.min = a.min;
	ans.sec = a.sec;
    } else {
	/* 1st arg is not dms, assume scalar degrees */
	ans.deg = a;
	ans.min = 0;
	ans.sec = 0;
    }

    /* subtract value of 2nd arg */
    if (istype(b, ans)) {
	/* 2nd arg is dms object, subtract it */
	ans.deg -= b.deg;
	ans.min -= b.min;
	ans.sec -= b.sec;
    } else {
	/* 2nd arg is not dms, subtract scalar degrees */
	ans.deg -= b;
    }

    /* return normalized result */
    ans = fixdms(ans);
    return ans;
}


define dms_mul(a, b)
{
    local obj dms ans;		/* return value */

    /* dms object multiplication */
    if (istype(a, ans) && istype(b, ans)) {
	ans.deg = dms_abs(a) * dms_abs(b);
	ans.min = 0;
	ans.sec = 0;

    /* scalar multiplication */
    } else if (istype(a, ans)) {
	ans.deg = a.deg * b;
	ans.min = a.min * b;
	ans.sec = a.sec * b;
    } else {
	ans.deg = b.deg * a;
	ans.min = b.min * a;
	ans.sec = b.sec * a;
    }

    /* return normalized result */
    ans = fixdms(ans);
    return ans;
}


define dms_print(a)
{
    local obj dms ans;		/* temp object for dms type testing */

    /* firewall - arg must be a dms object */
    if (! istype(a, ans)) {
	quit "dms_print called with non dms object";
    }

    /* print in dms form */
    print a.deg : 'd' : a.min : 'm' : a.sec : 's' :;
}


define dms_abs(a)
{
    local obj dms ans;		/* temp object for dms type testing */
    local deg;			/* return scalar value */

    /* firewall - just absolute value non dms objects */
    if (! istype(a, ans)) {
    	return abs(a);
    }

    /* compute degrees */
    deg = a.deg + a.min / 60 + a.sec / 3600;

    /* return degrees */
    return deg;
}


define dms_norm(a)
{
    local obj dms ans;		/* temp object for dms type testing */
    local deg;			/* degrees */

    /* firewall - arg must be a dms object */
    if (! istype(a, ans)) {
	quit "dms_norm called with non dms object";
    }

    /* square degrees (norm is the square of absolute value */
    deg = dms_abs(a);

    /* return degrees */
    return deg*deg;
}


define dms_test(a)
{
    local obj dms ans;		/* temp value */

    /* firewall - arg must be a dms object */
    if (! istype(a, ans)) {
	quit "dms_test called with non dms object";
    }

    /* return false of non-zero */
    ans = fixdms(a);
    if (ans.deg == 0 && ans.min == 0 && ans.sec == 0) {
	/* false */
	return 0;
    }
    /* true */
    return 1;
}


define dms_int(a)
{
    local obj dms ans;		/* return value */

    /* firewall - arg must be a dms object */
    if (! istype(a, ans)) {
	quit "dms_int called with non dms object";
    }

    /* normalize the argument */
    ans = fixdms(a);

    /* truncate to the nearest second */
    ans.sec = int(ans.sec);

    /* return value to the nearest second */
    return ans;
}


define dms_frac(a)
{
    local obj dms ans;		/* return value */

    /* firewall - arg must be a dms object */
    if (! istype(a, ans)) {
	quit "dms_frac called with non dms object";
    }

    /* normalize the argument */
    ans = fixdms(a);

    /* remove all but fractional seconds */
    ans.deg = 0;
    ans.min = 0;
    ans.sec = frac(ans.sec);

    /* return value to the second fraction */
    return ans;
}


define dms_rel(a,b)
{
    local abs_a, abs_b;		/* scalars of the arguments */

    /* compute scalars of the arguments */
    abs_a = dms_abs(a);
    abs_b = dms_abs(b);

    /* return the comparison */
    return cmp(abs_a, abs_b);
}


define dms_cmp(a,b)
{
    local abs_a, abs_b;		/* scalars of the arguments */

    /* compute scalars of the arguments */
    abs_a = dms_abs(a);
    abs_b = dms_abs(b);

    /* return the equality comparison */
    return (abs_a == abs_b);
}


define dms_inc(a)
{
    local obj dms ans;		/* return value */

    /* increment a dms object */
    if (istype(a, ans)) {
	ans = a;
	++ans.sec;

	/* return normalized result */
	ans = fixdms(ans);
	return ans;
    }

    /* increment a scalar */
    return a+1;
}


define dms_dec(a)
{
    local obj dms ans;		/* return value */

    /* decrement a dms object */
    if (istype(a, ans)) {
	ans = a;
	--ans.sec;

	/* return normalized result */
	ans = fixdms(ans);
	return ans;
    }

    /* decrement a scalar */
    return a-1;
}


define fixdms(a)
{
    local obj dms ans;		/* temp value */

    /* firewall */
    if (! istype(a, ans)) {
	quit "attempt to fix a non dms object";
    }

    /* force minutes to be intergral */
    a.min += frac(a.deg) * 60;
    a.deg = int(a.deg);

    /* force degrees to be intergral */
    a.sec += frac(a.min) * 60;
    a.min = int(a.min);

    /* carry excess seconds into minutes */
    a.min += a.sec // 60;
    a.sec %= 60;

    /* carry excess minutes into degrees */
    a.deg += a.min // 60;
    a.min %= 60;

    /* round degrees :-) */
    a.deg %= 360;

    /* return normalized result */
    return a;
}

if (config("resource_debug") & 3) {
    print "obj dms {deg, min, sec} defined";
}
