module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));

fn float _cosf(float x) @cname("cosf") @weak @nostrip
{
	uint ix = x.word();
	uint sign = ix >> 31;
	ix &= 0x7fffffff;

	switch
	{
		case (ix <= 0x3f490fda):
			// |x| < 2**-12
			if (ix < 0x39800000)
			{
				/* raise inexact if x != 0 */
				force_eval_add(x, 0x1p120f);
				return 1.0f;
			}
			return __cosdf(x);
		// |x| ~<= 5*pi/4
		case (ix <= 0x407b53d1):
			// |x|  ~> 3*pi/4
			if (ix > 0x4016cbe3) return -__cosdf(sign ? x + S2PI2 : x - S2PI2);
			return sign ? __sindf(x + S1PI2) : __sindf(S1PI2 - x);
		// |x| ~<= 9*pi/4
		case (ix <= 0x40e231d5):
			if (ix > 0x40afeddf) return __cosdf(sign ? x + S4PI2 : x - S4PI2);
			return sign ? __sindf((double)(-x) - S3PI2) : __sindf(x - S3PI2);
	}

	// general argument reduction needed
	double y @noinit;
	switch (__rem_pio2f(x, &y) & 3)
	{
		case 0: return  __cosdf(y);
		case 1: return  __sindf(-y);
		case 2: return  -__cosdf(y);
		default: return __sindf(y);
	}
}

/* origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

fn double _cos(double x) @cname("cos")  @weak @nostrip
{
	// High word of x.
	uint ix = x.high_word() & 0x7fffffff;

	switch
	{
		// |x| ~< pi/4
		case ix <= 0x3fe921fb:
			if (ix < 0x3e46a09e)
			{
				// |x| < 2**-27 * sqrt(2)
				// raise inexact if x!=0
				force_eval_add(x, 0x1p120f);
				return 1.0;
			}
			return __cos(x, 0);
		case ix >= 0x7ff00000:
			// cos(Inf or NaN) is NaN
			return x - x;
		default:
			// argument reduction
			double[2] y @noinit;
			switch (__rem_pio2(x, &y) & 3)
			{
				case 0: return __cos(y[0], y[1]);
				case 1: return -__sin(y[0], y[1], 1);
				case 2: return -__cos(y[0], y[1]);
				default: return __sin(y[0], y[1], 1);
			}
	}
}
