
#ifndef __fvec_altivec_h__
#define __fvec_altivec_h__


#define FVEC_WIDTH 4
#define FVEC_WIDTH_SHIFT 2
typedef vector float fvec;


inline fvec fv_neg( fvec a )
{
	return (vector float)vec_vxor( (vector unsigned int)a, (vector unsigned int)(0x8000000) );
}


inline fvec fv_abs( fvec a )
{
	return (vector float)vec_vandc( (vector unsigned int)a, (vector unsigned int)(0x8000000) );
}


inline fvec fv_add( fvec a, fvec b )
{
	return vec_vaddfp(a,b);
}


inline fvec fv_sub( fvec a, fvec b )
{
	return vec_vsubfp(a,b);
}


inline fvec fv_mul( fvec a, fvec b )
{
	return vec_vmaddfp( a, b, (vector float)(0.0f) );
}


inline fvec fv_muladd( fvec a, fvec b, fvec c )
{
	return vec_vmaddfp( b, c, a );
}


inline fvec fv_mulsub( fvec a, fvec b, fvec c )
{
	return vec_vnmsubfp( b, c, a );
}


inline fvec fv_rsqrt_fast( fvec a )
{
	return vec_vrsqrtefp( a );
}


inline fvec fv_rsqrt_precise( fvec a )
{
	// uses x1 = x0 + 0.5 * x0 * (1 - a * x0 * x0)
	vector float est = vec_vrsqrtefp( a );
	vector float t0 = vec_vmaddfp( est, est, (vector float)(0.0f) );
	vector float t1 = vec_vmaddfp( est, (vector float)(0.5f), (vector float)(0.0f) );
	t0 = vec_vnmsubfp( a, t0, (vector float)(1.0f) );
	return vec_vmaddfp( t0, t1, est );
}


inline fvec fv_recip_fast( fvec a )
{
	return vec_vrefp( a );
}


inline fvec fv_recip_precise( fvec a )
{
	// uses x1 = x0 + x0 * (1 - a * x0)
	vector float est = vec_vrefp( a );
	return vec_vmaddfp( est, vec_vnmsubfp( est, a, (vector float)(1.0f) ), est );
}


inline fvec fv_clamp_pos( fvec a )
{
	return vec_vmaxfp( a, (vector float)(0.0f) );
}


inline fvec fv_clamp_neg( fvec a )
{
	return vec_vminfp( a, (vector float)(0.0f) );
}


inline fvec fv_clamp_max( fvec a, fvec max )
{
	return vec_vminfp( a, max );
}


inline fvec fv_clamp_min( fvec a, fvec min )
{
	return vec_vmaxfp( a, min );
}


inline fvec fv_clamp_pos_max( fvec a, fvec max )
{
	return vec_vmaxfp( vec_vminfp( a, max ), (vector float)(0.0f) );
}


inline fvec fv_clamp_min_neg( fvec a, fvec min )
{
	return vec_vminfp( vec_vmaxfp( a, min ), (vector float)(0.0f) );
}


inline fvec fv_clamp_min_max( fvec a, fvec min, fvec max )
{
	return vec_vmaxfp( vec_vminfp( a, max ), min );
}


inline void fv_merge4_frommem( fvec &a, fvec &b, fvec &c, fvec &d, const float *in )
{
	const vector float *inv = (const vector float *)in;
	
	vector float v0 = inv[0];
	vector float v2 = inv[2];
	vector float v1 = inv[1];
	vector float v3 = inv[3];
	
	vector float t0 = vec_mergeh ( v0, v2 );
	vector float t1 = vec_mergeh ( v1, v3 );
	vector float t2 = vec_mergel ( v0, v2 );
	vector float t3 = vec_mergel ( v1, v3 );
	
	a = vec_mergeh ( t0, t1 );
	b = vec_mergel ( t0, t1 );
	c = vec_mergeh ( t2, t3 );
	d = vec_mergel ( t2, t3 );
}


inline void fv_merge4_tomem( float *out, fvec a, fvec b, fvec c, fvec d )
{
	vector float *outv = (vector float *)out;
	
	vector float t0 = vec_mergeh ( a, c );
	vector float t1 = vec_mergeh ( b, d );
	vector float t2 = vec_mergel ( a, c );
	vector float t3 = vec_mergel ( b, d );
	
	outv[0] = vec_mergeh ( t0, t1 );
	outv[1] = vec_mergel ( t0, t1 );
	outv[2] = vec_mergeh ( t2, t3 );
	outv[3] = vec_mergel ( t2, t3 );
}


inline void fv_merge3pad_frommem( fvec &a, fvec &b, fvec &c, const float *in )
{
	const vector float *inv = (const vector float *)in;
	
	vector float v0 = inv[0];
	vector float v2 = inv[2];
	vector float v1 = inv[1];
	vector float v3 = inv[3];
	
	vector float t0 = vec_mergeh ( v0, v2 );
	vector float t1 = vec_mergeh ( v1, v3 );
	vector float t2 = vec_mergel ( v0, v2 );
	vector float t3 = vec_mergel ( v1, v3 );
	
	a = vec_mergeh ( t0, t1 );
	b = vec_mergel ( t0, t1 );
	c = vec_mergeh ( t2, t3 );
}


inline void fv_merge3pad_tomem( float *out, fvec a, fvec b, fvec c )
{
	vector float *outv = (vector float *)out;
	
	vector float t0 = vec_mergeh ( a, c );
	vector float t1 = vec_mergeh ( b, b );
	vector float t2 = vec_mergel ( a, c );
	vector float t3 = vec_mergel ( b, b );
	
	outv[0] = vec_mergeh ( t0, t1 );
	outv[1] = vec_mergel ( t0, t1 );
	outv[2] = vec_mergeh ( t2, t3 );
	outv[3] = vec_mergel ( t2, t3 );
}


inline void fv_merge2_frommem( fvec &a, fvec &b, const float *in )
{
	const vector float *inv = (const vector float *)in;
	
	vector float v0 = inv[0];
	vector float v1 = inv[1];
	
	vector float t0 = vec_mergeh( v0, v1 );
	vector float t1 = vec_mergel( v0, v1 );
	
	a = vec_mergeh ( t0, t1 );
	b = vec_mergel ( t0, t1 );
}


inline void fv_merge2_tomem( float *out, fvec a, fvec b )
{
	vector float *outv = (vector float *)out;
	outv[0] = vec_mergeh( a, b );
	outv[1] = vec_mergel( a, b );
}


#define fv_zero ((vector float)(0.0f))
#define fv_half ((vector float)(0.5f))
#define fv_one  ((vector float)(1.0f))
#define fv_two  ((vector float)(2.0f))
#define fv_constf(f) ((vector float)(f))
inline fvec fv_loadf( float f )
{
	union {
		fvec v;
		float f[4];
	} temp;
	temp.f[0] = f;
	temp.f[1] = f;
	temp.f[2] = f;
	temp.f[3] = f;
	return temp.v;
}

#endif

