/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1988 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/* Special version adapted from libm for use in libc. */

static int	n0 = 0, n1 = 1;

static double   two52 = 4.503599627370496000E+15;
static double   twom52 = 2.220446049250313081E-16;

static double 
setexception(int n, double x)
{
	return (0.0);
}

double 
copysign(double x, double y)
{
	long           *px = (long *) &x;
	long           *py = (long *) &y;
	px[n0] = (px[n0] & 0x7fffffff) | (py[n0] & 0x80000000);
	return (x);
}

static double 
fabs(double x)
{
	long           *px = (long *) &x;
	px[0] &= 0x7fffffff;

	return (x);
}

static int 
finite(double x)
{
	long           *px = (long *) &x;
	return ((px[n0] & 0x7ff00000) != 0x7ff00000);
}

static int 
ilogb(double x)
{
	long           *px = (long *) &x, k;
	k = px[n0] & 0x7ff00000;
	if (k == 0) {
		if ((px[n1] | (px[n0] & 0x7fffffff)) == 0)
			return (0x80000001);
		else {
			x *= two52;
			return ((px[n0] & 0x7ff00000) >> 20) - 1075;
		}
	} else if (k != 0x7ff00000)
		return (k >> 20) - 1023;
	else
		return (0x7fffffff);
}

static double 
scalbn(double x, int n)
{
	long           *px = (long *) &x, k;
	double          twom54 = twom52 * 0.25;
	k = (px[n0] & 0x7ff00000) >> 20;
	if (k == 0x7ff)
		return (x + x);
	if ((px[n1] | (px[n0] & 0x7fffffff)) == 0)
		return (x);
	if (k == 0) {
		x *= two52;
		k = ((px[n0] & 0x7ff00000) >> 20) - 52;
	}
	k = k + n;
	if (n > 5000)
		return (setexception(2, x));
	if (n < -5000)
		return (setexception(1, x));
	if (k > 0x7fe)
		return (setexception(2, x));
	if (k <= -54)
		return (setexception(1, x));
	if (k > 0) {
		px[n0] = (px[n0] & 0x800fffff) | (k << 20);
		return (x);
	}
	k += 54;
	px[n0] = (px[n0] & 0x800fffff) | (k << 20);
	return (x * twom54);
}

double 
fmod(double x, double y)
{
	int             ny, nr;
	double          r, z, w;

	int finite(), ilogb(); 
	double fabs(), scalbn(), copysign();

	/* purge off exception values */
	if (!finite(x) || y != y || y == 0.0) {
		return ((x * y) / (x * y));
	}
	/* scale and subtract to get the remainder */
	r = fabs(x);
	y = fabs(y);
	ny = ilogb(y);
	while (r >= y) {
		nr = ilogb(r);
		if (nr == ny)
			w = y;
		else {
			z = scalbn(y, nr - ny - 1);
			w = z + z;
		}
		if (r >= w)
			r -= w;
		else
			r -= z;
	}

	/* restore sign */
	return (copysign(r, x));
}