/*
	mathlib.c

	math primitives

	Copyright (C) 1996-1997  Id Software, Inc.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#include <math.h>

#define IMPLEMENT_R_Cull
#define IMPLEMENT_VectorNormalize

#include "QF/mathlib.h"
#include "QF/qtypes.h"
#include "QF/sys.h"

VISIBLE int nanmask = 255 << 23;
static plane_t _frustum[4];
VISIBLE plane_t   *const frustum = _frustum;
static vec3_t _vec3_origin = { 0, 0, 0 };
VISIBLE const vec_t * const vec3_origin = _vec3_origin;
static quat_t _quat_origin = { 0, 0, 0, 0 };
VISIBLE const vec_t * const quat_origin = _quat_origin;

#define DEG2RAD(a) (a * (M_PI / 180.0))

#define FMANTBITS 23
#define FMANTMASK ((1 << FMANTBITS) - 1)
#define FEXPBITS 8
#define FEXPMASK ((1 << FEXPBITS) - 1)
#define FBIAS (1 << (FEXPBITS - 1))
#define FEXPMAX ((1 << FEXPBITS) - 1)

#define HMANTBITS 10
#define HMANTMASK ((1 << HMANTBITS) - 1)
#define HEXPBITS 5
#define HEXPMASK ((1 << HEXPBITS) - 1)
#define HBIAS (1 << (HEXPBITS - 1))
#define HEXPMAX ((1 << HEXPBITS) - 1)

int16_t
FloatToHalf (float x)
{
	union {
		float       f;
		uint32_t    u;
	} uf;
	unsigned    sign;
	int         exp;
	unsigned    mant;
	int16_t     half;

	uf.f = x;
	sign = (uf.u >> (FEXPBITS + FMANTBITS)) & 1;
	exp = ((uf.u >> FMANTBITS) & FEXPMASK) - FBIAS + HBIAS;
	mant = (uf.u & FMANTMASK) >> (FMANTBITS - HMANTBITS);
	if (exp <= 0) {
		mant |= 1 << HMANTBITS;
		mant >>= min (1 - exp, HMANTBITS + 1);
		exp = 0;
	} else if (exp >= HEXPMAX) {
		mant = 0;
		exp = HEXPMAX;
	}
	half = (sign << (HEXPBITS + HMANTBITS)) | (exp << HMANTBITS) | mant;
	return half;
}

float
HalfToFloat (int16_t x)
{
	union {
		float       f;
		uint32_t    u;
	} uf;
	unsigned    sign;
	int         exp;
	unsigned    mant;

	sign = (x >> (HEXPBITS + HMANTBITS)) & 1;
	exp = ((x >> HMANTBITS) & HEXPMASK);
	mant = (x & HMANTMASK) << (FMANTBITS - HMANTBITS);

	if (exp == 0) {
		if (mant) {
			while (mant < (1 << FMANTBITS)) {
				mant <<= 1;
				exp--;
			}
			mant &= (1 << FMANTBITS) - 1;
			exp += FBIAS - HBIAS + 1;
		}
	} else if (exp == HEXPMAX) {
		exp = FEXPMAX;
	} else {
		exp += FBIAS - HBIAS;
	}
	uf.u = (sign << (FEXPBITS + FMANTBITS)) | (exp << FMANTBITS) | mant;
	return uf.f;
}

static void
ProjectPointOnPlane (vec3_t dst, const vec3_t p, const vec3_t normal)
{
	float		inv_denom, d;
	vec3_t		n;

	inv_denom = 1.0F / DotProduct (normal, normal);

	d = DotProduct (normal, p) * inv_denom;

	VectorScale (normal, inv_denom * d, n);

	VectorSubtract (p, n, dst);
}

// assumes "src" is normalized
static void
PerpendicularVector (vec3_t dst, const vec3_t src)
{
	int			pos, i;
	float		minelem = 1.0F;
	vec3_t		tempvec;

	/* find the smallest magnitude axially aligned vector */
	for (pos = 0, i = 0; i < 3; i++) {
		if (fabs (src[i]) < minelem) {
			pos = i;
			minelem = fabs (src[i]);
		}
	}
	VectorZero (tempvec);
	tempvec[pos] = 1.0F;

	/* project the point onto the plane defined by src */
	ProjectPointOnPlane (dst, tempvec, src);

	/* normalize the result */
	VectorNormalize (dst);
}

#if defined(_WIN32) && !defined(__GNUC__)
# pragma optimize( "", off )
#endif

VISIBLE void
VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
{
	float d;

	right[0] = forward[2];
	right[1] = -forward[0];
	right[2] = forward[1];

	d = DotProduct(forward, right);
	VectorMultSub (right, d, forward, right);
	VectorNormalize (right);
	CrossProduct(right, forward, up);
}

VISIBLE void
RotatePointAroundVector (vec3_t dst, const vec3_t axis, const vec3_t point,
						 float degrees)
{
	float       m[3][3];
	float       im[3][3];
	float       zrot[3][3];
	float       tmpmat[3][3];
	float       rot[3][3];
	int         i;
	vec3_t      vr, vup, vf;

	VectorCopy (axis, vf);

	PerpendicularVector (vr, axis);
	CrossProduct (vr, vf, vup);

	m[0][0] = vr[0];
	m[1][0] = vr[1];
	m[2][0] = vr[2];

	m[0][1] = vup[0];
	m[1][1] = vup[1];
	m[2][1] = vup[2];

	m[0][2] = vf[0];
	m[1][2] = vf[1];
	m[2][2] = vf[2];

	memcpy (im, m, sizeof (im));

	im[0][1] = m[1][0];
	im[0][2] = m[2][0];
	im[1][0] = m[0][1];
	im[1][2] = m[2][1];
	im[2][0] = m[0][2];
	im[2][1] = m[1][2];

	memset (zrot, 0, sizeof (zrot));
	zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;

	zrot[0][0] = cos (DEG2RAD (degrees));
	zrot[0][1] = sin (DEG2RAD (degrees));
	zrot[1][0] = -sin (DEG2RAD (degrees));
	zrot[1][1] = cos (DEG2RAD (degrees));

	R_ConcatRotations (m, zrot, tmpmat);
	R_ConcatRotations (tmpmat, im, rot);

	for (i = 0; i < 3; i++) {
		dst[i] = DotProduct (rot[i], point);
	}
}

VISIBLE void
QuatMult (const quat_t q1, const quat_t q2, quat_t out)
{
	vec_t      s;
	vec3_t     v;

	s = q1[0] * q2[0] - DotProduct (q1 + 1, q2 + 1);
	CrossProduct (q1 + 1, q2 + 1, v);
	VectorMultAdd (v, q1[0], q2 + 1, v);
	VectorMultAdd (v, q2[0], q1 + 1, out + 1);
	out[0] = s;
}

VISIBLE void
QuatMultVec (const quat_t q, const vec3_t v, vec3_t out)
{
	vec_t      s;
	vec3_t     tv;

	s = -DotProduct (q + 1, v);
	CrossProduct (q + 1, v, tv);
	VectorMultAdd (tv, q[0], v, tv);
	CrossProduct (q + 1, tv, out);
	VectorMultSub (out, s, q + 1, out);
	VectorMultAdd (out, q[0], tv, out);
}

VISIBLE void
QuatInverse (const quat_t in, quat_t out)
{
	quat_t      q;
	vec_t       m;

	m = QDotProduct (in, in);	// in * in*
	QuatConj (in, q);
	QuatScale (q, 1 / m, out);
}

VISIBLE void
QuatExp (const quat_t a, quat_t b)
{
	vec3_t      n;
	vec_t       th;
	vec_t       r;
	vec_t       c, s;

	VectorCopy (a + 1, n);
	th = VectorNormalize (n);
	r = expf (a[0]);
	c = cosf (th);
	s = sinf (th);
	VectorScale (n, r * s, b + 1);
	b[0] = r * c;
}

VISIBLE void
QuatToMatrix (const quat_t q, vec_t *m, int homogenous, int vertical)
{
	vec_t       aa, ab, ac, ad, bb, bc, bd, cc, cd, dd;
	vec_t       *_m[4] = {
		m + (homogenous ? 0 : 0),
		m + (homogenous ? 4 : 3),
		m + (homogenous ? 8 : 6),
		m + (homogenous ? 12 : 9),
	};

	aa = q[0] * q[0];
	ab = q[0] * q[1];
	ac = q[0] * q[2];
	ad = q[0] * q[3];

	bb = q[1] * q[1];
	bc = q[1] * q[2];
	bd = q[1] * q[3];

	cc = q[2] * q[2];
	cd = q[2] * q[3];

	dd = q[3] * q[3];

	if (vertical) {
		VectorSet (aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac), _m[0]);
		VectorSet (2 * (bc - ad), aa - bb + cc - dd, 2 * (cd + ab), _m[1]);
		VectorSet (2 * (bd + ac), 2 * (cd - ab), aa - bb - cc + dd, _m[2]);
	} else {
		VectorSet (aa + bb - cc - dd, 2 * (bc - ad), 2 * (bd + ac), _m[0]);
		VectorSet (2 * (bc + ad), aa - bb + cc - dd, 2 * (cd - ab), _m[1]);
		VectorSet (2 * (bd - ac), 2 * (cd + ab), aa - bb - cc + dd, _m[2]);
	}
	if (homogenous) {
		_m[0][3] = 0;
		_m[1][3] = 0;
		_m[2][3] = 0;
		VectorZero (_m[3]);
		_m[3][3] = 1;
	}
}

#if defined(_WIN32) && !defined(__GNUC__)
# pragma optimize( "", on )
#endif

VISIBLE float
anglemod (float a)
{
	a = (360.0 / 65536) * ((int) (a * (65536 / 360.0)) & 65535);
	return a;
}

/*
	BOPS_Error

	Split out like this for ASM to call.
*/
void __attribute__ ((noreturn)) BOPS_Error (void);
VISIBLE void __attribute__ ((noreturn))
BOPS_Error (void)
{
	Sys_Error ("BoxOnPlaneSide:  Bad signbits");
}

#ifndef USE_INTEL_ASM

/*
	BoxOnPlaneSide

	Returns 1, 2, or 1 + 2
*/
VISIBLE int
BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, plane_t *p)
{
	float       dist1, dist2;
	int         sides;

#if 0
	// this is done by the BOX_ON_PLANE_SIDE macro before
	// calling this function

	// fast axial cases
	if (p->type < 3) {
		if (p->dist <= emins[p->type])
			return 1;
		if (p->dist >= emaxs[p->type])
			return 2;
		return 3;
	}
#endif

	// general case
	switch (p->signbits) {
		case 0:
			dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emaxs[2];
			dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
				p->normal[2] * emins[2];
			break;
		case 1:
			dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emaxs[2];
			dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
				p->normal[2] * emins[2];
			break;
		case 2:
			dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
				p->normal[2] * emaxs[2];
			dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emins[2];
			break;
		case 3:
			dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
				p->normal[2] * emaxs[2];
			dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emins[2];
			break;
		case 4:
			dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emins[2];
			dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
				p->normal[2] * emaxs[2];
			break;
		case 5:
			dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emins[2];
			dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
				p->normal[2] * emaxs[2];
			break;
		case 6:
			dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] +
				p->normal[2] * emins[2];
			dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emaxs[2];
			break;
		case 7:
			dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] +
				p->normal[2] * emins[2];
			dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] +
				p->normal[2] * emaxs[2];
			break;
		default:
			BOPS_Error ();
	}

#if 0
	int         i;
	vec3_t      corners[2];

	for (i = 0; i < 3; i++) {
		if (plane->normal[i] < 0) {
			corners[0][i] = emins[i];
			corners[1][i] = emaxs[i];
		} else {
			corners[1][i] = emins[i];
			corners[0][i] = emaxs[i];
		}
	}
	dist = DotProduct (plane->normal, corners[0]) - plane->dist;
	dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
	sides = 0;
	if (dist1 >= 0)
		sides = 1;
	if (dist2 < 0)
		sides |= 2;
#endif

	sides = 0;
	if (dist1 >= p->dist)
		sides = 1;
	if (dist2 < p->dist)
		sides |= 2;

#ifdef PARANOID
	if (sides == 0)
		Sys_Error ("BoxOnPlaneSide: sides==0");
#endif

	return sides;
}
#endif

/*
	angles is a left(?) handed system: 'pitch yaw roll' with x (pitch) axis to
	the right, y (yaw) axis up and z (roll) axis forward.

	the math in AngleVectors has the entity frame as left handed with x
	(forward) axis forward, y (right) axis to the right and z (up) up. However,
	the world is a right handed system with x to the right, y forward and
	z up.

	pitch =
		cp 0 -sp
		0  1  0
		sp 0  cp

	yaw =
		 cy sy 0
		-sy cy 0
		 0  0  1

	roll =
		1  0  0
		0  cr sr
		0 -sr cr

	final = roll * (pitch * yaw)
	final =
		[forward]
		[-right]	-ve due to left handed to right handed conversion
		[up]
*/
VISIBLE void
AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
	float       angle, sr, sp, sy, cr, cp, cy;

	angle = angles[YAW] * (M_PI * 2 / 360);
	sy = sin (angle);
	cy = cos (angle);
	angle = angles[PITCH] * (M_PI * 2 / 360);
	sp = sin (angle);
	cp = cos (angle);
	angle = angles[ROLL] * (M_PI * 2 / 360);
	sr = sin (angle);
	cr = cos (angle);

	forward[0] = cp * cy;
	forward[1] = cp * sy;
	forward[2] = -sp;
	// need to flip right because it's a left handed system in a right handed
	// world
	right[0] = -1 * (sr * sp * cy + cr * -sy);
	right[1] = -1 * (sr * sp * sy + cr * cy);
	right[2] = -1 * (sr * cp);
	up[0] = (cr * sp * cy + -sr * -sy);
	up[1] = (cr * sp * sy + -sr * cy);
	up[2] = cr * cp;
}

VISIBLE void
AngleQuat (const vec3_t angles, quat_t q)
{
	float       alpha, sr, sp, sy, cr, cp, cy;

	// alpha is half the angle
	alpha = angles[YAW] * (M_PI / 360);
	sy = sin (alpha);
	cy = cos (alpha);
	alpha = angles[PITCH] * (M_PI / 360);
	sp = sin (alpha);
	cp = cos (alpha);
	alpha = angles[ROLL] * (M_PI / 360);
	sr = sin (alpha);
	cr = cos (alpha);

	QuatSet (cy * cp * cr + sy * sp * sr,
			 cy * cp * sr - sy * sp * cr,
			 cy * sp * cr + sy * cp * sr,
			 sy * cp * cr - cy * sp * sr,
			 q);
}

VISIBLE int
_VectorCompare (const vec3_t v1, const vec3_t v2)
{
	int         i;

	for (i = 0; i < 3; i++)
		if (fabs (v1[i] - v2[i]) > EQUAL_EPSILON)
			return 0;

	return 1;
}

VISIBLE void
_VectorMA (const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc)
{
	vecc[0] = veca[0] + scale * vecb[0];
	vecc[1] = veca[1] + scale * vecb[1];
	vecc[2] = veca[2] + scale * vecb[2];
}

VISIBLE vec_t
_DotProduct (const vec3_t v1, const vec3_t v2)
{
	return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}

VISIBLE void
_VectorSubtract (const vec3_t veca, const vec3_t vecb, vec3_t out)
{
	out[0] = veca[0] - vecb[0];
	out[1] = veca[1] - vecb[1];
	out[2] = veca[2] - vecb[2];
}

VISIBLE void
_VectorAdd (const vec3_t veca, const vec3_t vecb, vec3_t out)
{
	out[0] = veca[0] + vecb[0];
	out[1] = veca[1] + vecb[1];
	out[2] = veca[2] + vecb[2];
}

VISIBLE void
_VectorCopy (const vec3_t in, vec3_t out)
{
	out[0] = in[0];
	out[1] = in[1];
	out[2] = in[2];
}

VISIBLE void
CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross)
{
	float v10 = v1[0];
	float v11 = v1[1];
	float v12 = v1[2];
	float v20 = v2[0];
	float v21 = v2[1];
	float v22 = v2[2];

	cross[0] = v11 * v22 - v12 * v21;
	cross[1] = v12 * v20 - v10 * v22;
	cross[2] = v10 * v21 - v11 * v20;
}

VISIBLE vec_t
_VectorLength (const vec3_t v)
{
	float		length;

	length = sqrt (DotProduct (v, v));
	return length;
}

VISIBLE vec_t
_VectorNormalize (vec3_t v)
{
	int         i;
	double      length;

	length = 0;
	for (i = 0; i < 3; i++)
		length += v[i] * v[i];
	length = sqrt (length);
	if (length == 0)
		return 0;

	for (i = 0; i < 3; i++)
		v[i] /= length;

	return length;
}

VISIBLE void
_VectorScale (const vec3_t in, vec_t scale, vec3_t out)
{
	out[0] = in[0] * scale;
	out[1] = in[1] * scale;
	out[2] = in[2] * scale;
}

VISIBLE int
Q_log2 (int val)
{
	int         answer = 0;

	while ((val >>= 1) != 0)
		answer++;
	return answer;
}

VISIBLE void
R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
{
	out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
		in1[0][2] * in2[2][0];
	out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
		in1[0][2] * in2[2][1];
	out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
		in1[0][2] * in2[2][2];
	out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
		in1[1][2] * in2[2][0];
	out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
		in1[1][2] * in2[2][1];
	out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
		in1[1][2] * in2[2][2];
	out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
		in1[2][2] * in2[2][0];
	out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
		in1[2][2] * in2[2][1];
	out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
		in1[2][2] * in2[2][2];
}

VISIBLE void
R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
{
	out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
		in1[0][2] * in2[2][0];
	out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
		in1[0][2] * in2[2][1];
	out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
		in1[0][2] * in2[2][2];
	out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
		in1[0][2] * in2[2][3] + in1[0][3];
	out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
		in1[1][2] * in2[2][0];
	out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
		in1[1][2] * in2[2][1];
	out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
		in1[1][2] * in2[2][2];
	out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
		in1[1][2] * in2[2][3] + in1[1][3];
	out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
		in1[2][2] * in2[2][0];
	out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
		in1[2][2] * in2[2][1];
	out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
		in1[2][2] * in2[2][2];
	out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
		in1[2][2] * in2[2][3] + in1[2][3];
}

/*
	FloorDivMod

	Returns mathematically correct (floor-based) quotient and remainder for
	numer and denom, both of which should contain no fractional part. The
	quotient must fit in 32 bits.
*/
VISIBLE void
FloorDivMod (double numer, double denom, int *quotient, int *rem)
{
	double		x;
	int			q, r;

#ifndef PARANOID
	if (denom <= 0.0)
		Sys_Error ("FloorDivMod: bad denominator %f", denom);

//	if ((floor(numer) != numer) || (floor(denom) != denom))
//		Sys_Error ("FloorDivMod: non-integer numer or denom %f %f",
//				   numer, denom);
#endif

	if (numer >= 0.0) {
		x = floor (numer / denom);
		q = (int) x;
		r = (int) floor (numer - (x * denom));
	} else {
		// perform operations with positive values, and fix mod to make
		// floor-based
		x = floor (-numer / denom);
		q = -(int) x;
		r = (int) floor (-numer - (x * denom));
		if (r != 0) {
			q--;
			r = (int) denom - r;
		}
	}

	*quotient = q;
	*rem = r;
}

VISIBLE int
GreatestCommonDivisor (int i1, int i2)
{
	if (i1 > i2) {
		if (i2 == 0)
			return (i1);
		return GreatestCommonDivisor (i2, i1 % i2);
	} else {
		if (i1 == 0)
			return (i2);
		return GreatestCommonDivisor (i1, i2 % i1);
	}
}

#ifndef USE_INTEL_ASM
/*
  Invert24To16

  Inverts an 8.24 value to a 16.16 value
*/
VISIBLE fixed16_t
Invert24To16 (fixed16_t val)
{
	if (val < 256)
		return (0xFFFFFFFF);

	return (fixed16_t)
		(((double) 0x10000 * (double) 0x1000000 / (double) val) + 0.5);
}
#endif

void
Mat3Init (const quat_t rot, const vec3_t scale, mat3_t mat)
{
	QuatToMatrix (rot, mat, 0, 1);
	VectorScale (mat + 0, scale[0], mat + 0);
	VectorScale (mat + 3, scale[1], mat + 3);
	VectorScale (mat + 6, scale[2], mat + 6);
}

void
Mat3Transpose (const mat3_t a, mat3_t b)
{
	vec_t       t;
	int         i, j;

	for (i = 0; i < 2; i++) {
		b[i * 3 + i] = a[i * 3 + i];		// in case b != a
		for (j = i + 1; j < 3; j++) {
			t = a[i * 3 + j];				// in case b == a
			b[i * 3 + j] = a[j * 3 + i];
			b[j * 3 + i] = t;
		}
	}
	b[i * 3 + i] = a[i * 3 + i];		// in case b != a
}

vec_t
Mat3Determinant (const mat3_t m)
{
	vec3_t      t;
	CrossProduct (m + 3, m + 6, t);
	return DotProduct (m + 0, t);
}

typedef vec_t mat2_t[2 * 2];

static void
Mat3Sub2 (const mat3_t m3, mat2_t m2, int i, int j)
{
	int         si, sj, di, dj;

	for (di = 0; di < 2; di++) {
		for (dj = 0; dj < 2; dj++) {
			si = di + ((di >= i) ? 1 : 0);
			sj = dj + ((dj >= j) ? 1 : 0);
			m2[di * 2 + dj] = m3[si * 3 + sj];
		}
	}
}

static vec_t
Mat2Det (const mat2_t m)
{
	return m[0] * m[3] - m[1] * m[2];
}

int
Mat3Inverse (const mat3_t a, mat3_t b)
{
	mat3_t      tb;
	mat2_t      m2;
	vec_t      *m = b;
	int         i, j;
	vec_t       det;
	vec_t       sign[2] = { 1, -1};

	det = Mat3Determinant (a);
	if (det * det < 1e-6) {
		Mat3Identity (b);
		return 0;
	}
	if (b == a)
		m = tb;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			Mat3Sub2 (a, m2, i, j);
			m[j * 3 + i] = sign[(i + j) & 1] * Mat2Det (m2) / det;
		}
	}
	if (m != b)
		Mat3Copy (m, b);
	return 1;
}

void Mat3Mult (const mat3_t a, const mat3_t b, mat3_t c)
{
	mat3_t      ta, tb;					// in case c == b or c == a
	int         i, j, k;

	Mat3Transpose (a, ta);				// transpose so we can use dot
	Mat3Copy (b, tb);

	k = 0;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			c[k++] = DotProduct (ta + 3 * j, tb + 3 * i);
		}
	}
}

void Mat3MultVec (const mat3_t a, const vec3_t b, vec3_t c)
{
	int         i;
	vec3_t      tb;

	VectorCopy (b, tb);
	for (i = 0; i < 3; i++)
		c[i] = a[i + 0] * tb[0] + a[i + 3] * b[1] + a[i + 6] * b[2];
}

#define sqr(x) ((x) * (x))
void Mat3SymEigen (const mat3_t m, vec3_t e)
{
	vec_t       p, q, r;
	vec_t       phi;
	mat3_t      B;

	p = sqr (m[1]) + sqr (m[2]) + sqr (m[5]);
	if (p < 1e-6) {
		e[0] = m[0];
		e[1] = m[4];
		e[2] = m[8];
		return;
	}
	q = Mat3Trace (m) / 3;
	p = sqr (m[0] - q) + sqr (m[4] - q) + sqr (m[8] - q) + 2 * p;
	p = sqrt (p);
	Mat3Zero (B);
	B[0] = B[4] = B[8] = q;
	Mat3Subtract (m, B, B);
	Mat3Scale (B, 1.0 / p, B);
	r = Mat3Determinant (B) / 2;
	if (r >= 1)
		phi = 0;
	else if (r <= -1)
		phi = M_PI / 3;
	else
		phi = acos (r) / 3;

	e[0] = q + 2 * p * cos (phi);
	e[2] = q + 2 * p * cos (phi + M_PI * 2 / 3);
	e[1] = 3 * q - e[0] - e[2];
}

void
Mat4Init (const quat_t rot, const vec3_t scale, const vec3_t trans, mat4_t mat)
{
	QuatToMatrix (rot, mat, 1, 1);
	VectorScale (mat + 0, scale[0], mat + 0);
	VectorScale (mat + 4, scale[1], mat + 4);
	VectorScale (mat + 8, scale[2], mat + 8);
	VectorCopy (trans, mat + 12);
}

void
Mat4Transpose (const mat4_t a, mat4_t b)
{
	vec_t       t;
	int         i, j;

	for (i = 0; i < 3; i++) {
		b[i * 4 + i] = a[i * 4 + i];		// in case b != a
		for (j = i + 1; j < 4; j++) {
			t = a[i * 4 + j];				// in case b == a
			b[i * 4 + j] = a[j * 4 + i];
			b[j * 4 + i] = t;
		}
	}
	b[i * 4 + i] = a[i * 4 + i];		// in case b != a
}

static void
Mat4Sub3 (const mat4_t m4, mat3_t m3, int i, int j)
{
	int         si, sj, di, dj;

	for (di = 0; di < 3; di++) {
		for (dj = 0; dj < 3; dj++) {
			si = di + ((di >= i) ? 1 : 0);
			sj = dj + ((dj >= j) ? 1 : 0);
			m3[di * 3 + dj] = m4[si * 4 + sj];
		}
	}
}

static vec_t
Mat4Det (const mat4_t m)
{
	mat3_t      t;
	int         i;
	vec_t       res = 0, det, s = 1;

	for (i = 0; i < 4; i++, s = -s) {
		Mat4Sub3 (m, t, 0, i);
		det = Mat3Determinant (t);
		res += m[i] * det * s;
	}
	return res;
}

int
Mat4Inverse (const mat4_t a, mat4_t b)
{
	mat4_t      tb;
	mat3_t      m3;
	vec_t      *m = b;
	int         i, j;
	vec_t       det;
	vec_t       sign[2] = { 1, -1};

	det = Mat4Det (a);
	if (det * det < 1e-6) {
		Mat4Identity (b);
		return 0;
	}
	if (b == a)
		m = tb;
	for (i = 0; i < 4; i++) {
		for (j = 0; j < 4; j++) {
			Mat4Sub3 (a, m3, i, j);
			m[j * 4 + i] = sign[(i + j) & 1] * Mat3Determinant (m3) / det;
		}
	}
	if (m != b)
		Mat4Copy (m, b);
	return 1;
}

void
Mat4Mult (const mat4_t a, const mat4_t b, mat4_t c)
{
	mat4_t      ta, tb;					// in case c == b or c == a
	int         i, j, k;

	Mat4Transpose (a, ta);				// transpose so we can use dot
	Mat4Copy (b, tb);

	k = 0;
	for (i = 0; i < 4; i++) {
		for (j = 0; j < 4; j++) {
			c[k++] = QDotProduct (ta + 4 * j, tb + 4 * i);
		}
	}
}

void
Mat4MultVec (const mat4_t a, const vec3_t b, vec3_t c)
{
	int         i;
	vec3_t      tb;

	VectorCopy (b, tb);
	for (i = 0; i < 3; i++)
		c[i] = a[i + 0] * tb[0] + a[i + 4] * b[1] + a[i + 8] * b[2] + a[i +12];
}

void
Mat4as3MultVec (const mat4_t a, const vec3_t b, vec3_t c)
{
	int         i;
	vec3_t      tb;

	VectorCopy (b, tb);
	for (i = 0; i < 3; i++)
		c[i] = a[i + 0] * tb[0] + a[i + 4] * b[1] + a[i + 8] * b[2];
}

int
Mat3Decompose (const mat3_t mat, quat_t rot, vec3_t shear, vec3_t scale)
{
	vec3_t      row[3], shr, scl;
	vec_t       l, t;
	int         i, j;

	for (i = 0; i < 3; i++)
		for (j = 0; j < 3; j++)
			row[j][i] = mat[i * 3 + j];

	l = DotProduct (row[0], row[0]);
	if (l < 1e-5)
		return 0;
	scl[0] = sqrt (l);
	VectorScale (row[0], 1/scl[0], row[0]);
	shr[0] = DotProduct (row[0], row[1]);

	VectorMultSub (row[1], shr[0], row[0], row[1]);
	l = DotProduct (row[1], row[1]);
	if (l < 1e-5)
		return 0;
	scl[1] = sqrt (l);
	shr[0] /= scl[1];
	VectorScale (row[1], 1/scl[1], row[1]);
	shr[1] = DotProduct (row[0], row[2]);

	VectorMultSub (row[2], shr[1], row[0], row[2]);
	shr[2] = DotProduct (row[1], row[2]);
	VectorMultSub (row[2], shr[2], row[1], row[2]);
	l = DotProduct (row[2], row[2]);
	if (l < 1e-5)
		return 0;
	scl[2] = sqrt (l);
	shr[1] /= scl[2];
	shr[2] /= scl[2];
	VectorScale (row[2], 1/scl[2], row[2]);
	if (scale)
		VectorCopy (scl, scale);
	if (shear)
		VectorCopy (shr, shear);
	if (!rot)
		return 1;

	t = 1 + row[0][0] + row[1][1] + row[2][2];
	if (t >= 1e-5) {
		vec_t       s = sqrt (t) * 2;
		rot[0] = s / 4;
		rot[1] = (row[2][1] - row[1][2]) / s;
		rot[2] = (row[0][2] - row[2][0]) / s;
		rot[3] = (row[1][0] - row[0][1]) / s;
	} else {
		if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
			vec_t       s = sqrt (1 + row[0][0] - row[1][1] - row[2][2]) * 2;
			rot[0] = (row[2][1] - row[1][2]) / s;
			rot[1] = s / 4;
			rot[2] = (row[1][0] + row[0][1]) / s;
			rot[3] = (row[0][2] + row[2][0]) / s;
		} else if (row[1][1] > row[2][2]) {
			vec_t       s = sqrt (1 + row[1][1] - row[0][0] - row[2][2]) * 2;
			rot[0] = (row[0][2] - row[2][0]) / s;
			rot[1] = (row[1][0] + row[0][1]) / s;
			rot[2] = s / 4;
			rot[3] = (row[2][1] + row[1][2]) / s;
		} else {
			vec_t       s = sqrt (1 + row[2][2] - row[0][0] - row[1][1]) * 2;
			rot[0] = (row[1][0] - row[0][1]) / s;
			rot[1] = (row[0][2] + row[2][0]) / s;
			rot[2] = (row[2][1] + row[1][2]) / s;
			rot[3] = s / 4;
		}
	}
	return 1;
}

int
Mat4Decompose (const mat4_t mat, quat_t rot, vec3_t shear, vec3_t scale,
			   vec3_t trans)
{
	mat3_t      m3;

	if (trans)
		VectorCopy (mat + 12, trans);
	Mat4toMat3 (mat, m3);
	return Mat3Decompose (m3, rot, shear, scale);
}

void
BarycentricCoords (const vec_t **points, int num_points, const vec3_t p,
				   vec_t *lambda)
{
	vec3_t      a, b, c, x, ab, bc, ca, n;
	vec_t       div;
	if (num_points > 4)
		Sys_Error ("Don't know how to compute the barycentric coordinates "
				   "for %d points", num_points);
	switch (num_points) {
		case 1:
			lambda[0] = 1;
			return;
		case 2:
			VectorSubtract (p, points[0], x);
			VectorSubtract (points[1], points[0], a);
			lambda[1] = DotProduct (x, a) / DotProduct (a, a);
			lambda[0] = 1 - lambda[1];
			return;
		case 3:
			VectorSubtract (p, points[0], x);
			VectorSubtract (points[1], points[0], a);
			VectorSubtract (points[2], points[0], b);
			CrossProduct (a, b, ab);
			div = DotProduct (ab, ab);
			CrossProduct (x, b, n);
			lambda[1] = DotProduct (n, ab) / div;
			CrossProduct (a, x, n);
			lambda[2] = DotProduct (n, ab) / div;
			lambda[0] = 1 - lambda[1] - lambda[2];
			return;
		case 4:
			VectorSubtract (p, points[0], x);
			VectorSubtract (points[1], points[0], a);
			VectorSubtract (points[2], points[0], b);
			VectorSubtract (points[3], points[0], c);
			CrossProduct (a, b, ab);
			CrossProduct (b, c, bc);
			CrossProduct (c, a, ca);
			div = DotProduct (a, bc);
			lambda[1] = DotProduct (x, bc) / div;
			lambda[2] = DotProduct (x, ca) / div;
			lambda[3] = DotProduct (x, ab) / div;
			lambda[0] = 1 - lambda[1] - lambda[2] - lambda[3];
			return;
	}
	Sys_Error ("Not enough points to project or enclose the point");
}

static int
circum_circle (const vec_t **points, int num_points, sphere_t *sphere)
{
	vec3_t      a, c, b;
	vec3_t      bc, ca, ab;
	vec_t       aa, bb, cc;
	vec_t       div;
	vec_t       alpha, beta, gamma;

	switch (num_points) {
		case 1:
			VectorCopy (points[0], sphere->center);
			return 1;
		case 2:
			VectorBlend (points[0], points[1], 0.5, sphere->center);
			return 1;
		case 3:
			VectorSubtract (points[0], points[1], a);
			VectorSubtract (points[0], points[2], b);
			VectorSubtract (points[1], points[2], c);
			aa = DotProduct (a, a);
			bb = DotProduct (b, b);
			cc = DotProduct (c, c);
			div = DotProduct (a, c);
			div = 2 * (aa * cc - div * div);
			if (fabs (div) < EQUAL_EPSILON) {
				// degenerate
				return 0;
			}
			alpha = cc * DotProduct (a, b) / div;
			beta = -bb * DotProduct (a, c) / div;
			gamma = aa * DotProduct (b, c) / div;
			VectorScale (points[0], alpha, sphere->center);
			VectorMultAdd (sphere->center, beta, points[1], sphere->center);
			VectorMultAdd (sphere->center, gamma, points[2], sphere->center);
			return 1;
		case 4:
			VectorSubtract (points[1], points[0], a);
			VectorSubtract (points[2], points[0], b);
			VectorSubtract (points[3], points[0], c);
			CrossProduct (b, c, bc);
			CrossProduct (c, a, ca);
			CrossProduct (a, b, ab);
			div = 2 * DotProduct (a, bc);
			if (fabs (div) < EQUAL_EPSILON) {
				// degenerate
				return 0;
			}
			aa = DotProduct (a, a) / div;
			bb = DotProduct (b, b) / div;
			cc = DotProduct (c, c) / div;
			VectorScale (bc, aa, sphere->center);
			VectorMultAdd (sphere->center, bb, ca, sphere->center);
			VectorMultAdd (sphere->center, cc, ab, sphere->center);
			VectorAdd (sphere->center, points[0], sphere->center);
			sphere->radius = VectorDistance (sphere->center, points[0]);
			return 1;
	}
	return 0;
}

int
CircumSphere (const vec3_t points[], int num_points, sphere_t *sphere)
{
	const vec_t *p[] = {points[0], points[1], points[2], points[3]};

	if (num_points > 4)
		return 0;
	sphere->radius = 0;
	if (num_points) {
		if (circum_circle (p, num_points, sphere)) {
			if (num_points > 1)
				sphere->radius = VectorDistance (sphere->center, points[0]);
			return 1;
		}
		return 0;
	}
	VectorZero (sphere->center);
	return 1;
}

static void
closest_point (const vec_t **points, int num_points, const vec3_t x,
			   vec3_t closest)
{
	vec3_t      a, b, n, d;
	vec_t       l;

	switch (num_points) {
		default:
		case 1:
			VectorCopy (points[0], closest);
			break;
		case 2:
			VectorSubtract (points[1], points[0], n);
			VectorSubtract (x, points[0], d);
			l = DotProduct (d, n) / DotProduct (n, n);
			VectorMultAdd (points[0], l, n, closest);
			break;
		case 3:
			VectorSubtract (points[1], points[0], a);
			VectorSubtract (points[2], points[0], b);
			CrossProduct (a, b, n);
			VectorSubtract (points[0], x, d);
			l = DotProduct (d, n) / DotProduct (n, n);
			VectorMultAdd (x, l, n, closest);
			break;
	}
}

sphere_t
SmallestEnclosingBall (const vec3_t points[], int num_points)
{
	sphere_t    sphere;
	const vec_t *best;
	const vec_t *support[4];
	int         num_support;
	vec_t       dist, best_dist;
	int         i;
	int         itters = 0;

	if (num_points < 3) {
		CircumSphere (points, num_points, &sphere);
		return sphere;
	}

	for (i = 0; i < 4; i++)
		support[i] = 0;

	VectorCopy (points[0], sphere.center);
	best_dist = dist = 0;
	best = 0;
	for (i = 1; i < num_points; i++) {
		dist = VectorDistance_fast (points[i], sphere.center);
		if (dist > best_dist) {
			best_dist = dist;
			best = points[i];
		}
	}
	num_support = 1;
	support[0] = best;
	sphere.radius = best_dist;	// note: radius squared until the end

	while (1) {
		vec3_t      affine;
		vec3_t      center_to_affine, center_to_point;
		vec_t       affine_dist, point_proj, point_dist, bound;
		vec_t       scale = 1;
		int         i;

		if (itters++ > 10)
			Sys_Error ("stuck SEB");
		best = 0;

		if (num_support == 4) {
			vec_t       lambda[4];
			int         dropped = 0;

			BarycentricCoords (support, 4, sphere.center, lambda);
			for (i = 0; i < 4; i++) {
				support[i - dropped] = support[i];
				if (lambda[i] < 0) {
					dropped++;
					num_support--;
				}
			}
			if (!dropped)
				break;
			for (i = 4 - dropped; i < 4; i++)
				support[i] = 0;
		}
		closest_point (support, num_support, sphere.center, affine);
		VectorSubtract (affine, sphere.center, center_to_affine);
		affine_dist = DotProduct (center_to_affine, center_to_affine);
		if (affine_dist < 1e-2 * sphere.radius)
			break;
		for (i = 0; i < num_points; i++) {
			if (points[i] == support [0] || points[i] == support[1]
				|| points[i] == support[2])
				continue;
			VectorSubtract (points[i], sphere.center, center_to_point);
			point_proj = DotProduct (center_to_affine, center_to_point);
			if (affine_dist - point_proj <= 0
				|| ((affine_dist - point_proj) * (affine_dist - point_proj)
					< 1e-8 * sphere.radius * affine_dist))
				continue;
			point_dist = DotProduct (center_to_point, center_to_point);
			bound = sphere.radius - point_dist;
			bound /= 2 * (affine_dist - point_proj);
			if (bound < scale) {
				best = points[i];
				scale = bound;
			}
		}
		VectorMultAdd (sphere.center, scale, center_to_affine, sphere.center);
		if (!best)
			break;
		sphere.radius = VectorDistance_fast (sphere.center, best);
		support[num_support++] = best;
	}
	best_dist = 0;
	for (i = 0; i < num_points; i++) {
		dist = VectorDistance_fast (sphere.center, points[i]);
		if (dist > best_dist)
			best_dist = dist;
	}
	sphere.radius = sqrt (best_dist);
	return sphere;
}
