
#ifndef __fvec_ppc_h__
#define __fvec_ppc_h__


#define FVEC_WIDTH 4
struct fvec {
	float f0;
	float f1;
	float f2;
	float f3;
};


inline fvec fv_neg( const fvec &a )
{
	fvec out = { -a.f0, -a.f1, -a.f2, -a.f3 };
	return out;
}


inline fvec fv_abs( const fvec &a )
{
	fvec out = { __fabsf(a.f0), __fabsf(a.f1), __fabsf(a.f2), __fabsf(a.f3) };
	return out;
}


inline fvec fv_add( const fvec &a, const fvec &b )
{
	fvec out = { a.f0 + b.f0, a.f1 + b.f1, a.f2 + b.f2, a.f3 + b.f3 };
	return out;
}


inline fvec fv_sub( const fvec &a, const fvec &b )
{
	fvec out = { a.f0 - b.f0, a.f1 - b.f1, a.f2 - b.f2, a.f3 - b.f3 };
	return out;
}


inline fvec fv_mul( const fvec &a, const fvec &b )
{
	fvec out = { a.f0 * b.f0, a.f1 * b.f1, a.f2 * b.f2, a.f3 * b.f3 };
	return out;
}


inline fvec fv_muladd( const fvec &a, const fvec &b, const fvec &c )
{
	fvec out;
	out.f0 = (a.f0 + (b.f0 * c.f0));
	out.f1 = (a.f1 + (b.f1 * c.f1));
	out.f2 = (a.f2 + (b.f2 * c.f2));
	out.f3 = (a.f3 + (b.f3 * c.f3));
	return out;
}


inline fvec fv_mulsub( const fvec &a, const fvec &b, const fvec &c )
{
	fvec out;
	out.f0 = (a.f0 - (b.f0 * c.f0));
	out.f1 = (a.f1 - (b.f1 * c.f1));
	out.f2 = (a.f2 - (b.f2 * c.f2));
	out.f3 = (a.f3 - (b.f3 * c.f3));
	return out;
}


inline fvec fv_rsqrt_fast( const fvec &a )
{
	fvec out;
	
	float t0 = __frsqrte(a.f0);
	float t1 = __frsqrte(a.f1);
	float t2 = __frsqrte(a.f2);
	float t3 = __frsqrte(a.f3);
	
	out.f0 = t0 + (0.5f * t0) * (1.0f - a.f0 * t0 * t0);
	out.f1 = t1 + (0.5f * t1) * (1.0f - a.f1 * t1 * t1);
	out.f2 = t2 + (0.5f * t2) * (1.0f - a.f2 * t2 * t2);
	out.f3 = t3 + (0.5f * t3) * (1.0f - a.f3 * t3 * t3);
	
	return out;
}


inline fvec fv_rsqrt_precise( const fvec &a )
{
	fvec out;
	
	float t0 = __frsqrte(a.f0);
	float t1 = __frsqrte(a.f1);
	float t2 = __frsqrte(a.f2);
	float t3 = __frsqrte(a.f3);
	
	t0 = t0 + (0.5f * t0) * (1.0f - a.f0 * t0 * t0);
	t1 = t1 + (0.5f * t1) * (1.0f - a.f1 * t1 * t1);
	t2 = t2 + (0.5f * t2) * (1.0f - a.f2 * t2 * t2);
	t3 = t3 + (0.5f * t3) * (1.0f - a.f3 * t3 * t3);
	
	out.f0 = t0 + (0.5f * t0) * (1.0f - a.f0 * t0 * t0);
	out.f1 = t1 + (0.5f * t1) * (1.0f - a.f1 * t1 * t1);
	out.f2 = t2 + (0.5f * t2) * (1.0f - a.f2 * t2 * t2);
	out.f3 = t3 + (0.5f * t3) * (1.0f - a.f3 * t3 * t3);
	
	return out;
}


inline fvec fv_recip_fast( const fvec &a )
{
	fvec out;
	out.f0 = __fres(a.f0);
	out.f1 = __fres(a.f1);
	out.f2 = __fres(a.f2);
	out.f3 = __fres(a.f3);
	return out;
}


inline fvec fv_recip_precise( const fvec &a )
{
	fvec out;
	
	float f0 = __fres(a.f0);
	float f1 = __fres(a.f1);
	float f2 = __fres(a.f2);
	float f3 = __fres(a.f3);
	
	out.f0 = f0 * (2.0f - a.f0 * f0);
	out.f1 = f1 * (2.0f - a.f1 * f1);
	out.f2 = f2 * (2.0f - a.f2 * f2);
	out.f3 = f3 * (2.0f - a.f3 * f3);
	
	return out;
}


inline fvec fv_clamp_pos( const fvec &a )
{
	fvec out;
	out.f0 = 0.5f * (a.f0 + __fabsf(a.f0));
	out.f1 = 0.5f * (a.f1 + __fabsf(a.f1));
	out.f2 = 0.5f * (a.f2 + __fabsf(a.f2));
	out.f3 = 0.5f * (a.f3 + __fabsf(a.f3));
	return out;
}


inline fvec fv_clamp_neg( const fvec &a )
{
	fvec out;
	out.f0 = 0.5f * (a.f0 - __fabsf(a.f0));
	out.f1 = 0.5f * (a.f1 - __fabsf(a.f1));
	out.f2 = 0.5f * (a.f2 - __fabsf(a.f2));
	out.f3 = 0.5f * (a.f3 - __fabsf(a.f3));
	return out;
}


inline fvec fv_clamp_max( const fvec &a, const fvec &max )
{
	float f0 = max.f0 - a.f0;
	float f1 = max.f1 - a.f1;
	float f2 = max.f2 - a.f2;
	float f3 = max.f3 - a.f3;
	
	fvec out;
	out.f0 = max.f0 - 0.5f * (f0 + __fabsf(f0));
	out.f1 = max.f1 - 0.5f * (f1 + __fabsf(f1));
	out.f2 = max.f2 - 0.5f * (f2 + __fabsf(f2));
	out.f3 = max.f3 - 0.5f * (f3 + __fabsf(f3));
	return out;
}


inline fvec fv_clamp_min( const fvec &a, const fvec &min )
{
	float f0 = a.f0 - min.f0;
	float f1 = a.f1 - min.f1;
	float f2 = a.f2 - min.f2;
	float f3 = a.f3 - min.f3;
	
	fvec out;
	out.f0 = min.f0 + 0.5f * (f0 + __fabsf(f0));
	out.f1 = min.f1 + 0.5f * (f1 + __fabsf(f1));
	out.f2 = min.f2 + 0.5f * (f2 + __fabsf(f2));
	out.f3 = min.f3 + 0.5f * (f3 + __fabsf(f3));
	return out;
}


inline fvec fv_clamp_pos_max( const fvec &a, const fvec &max )
{
	return fv_clamp_pos( fv_clamp_max( a, max ) );
}


inline fvec fv_clamp_min_neg( const fvec &a, const fvec &min )
{
	return fv_clamp_neg( fv_clamp_min( a, min ) );
}


inline fvec fv_clamp_min_max( const fvec &a, const fvec &min, const fvec &max )
{
	return fv_clamp_min( fv_clamp_max( a, max ), min );
}


inline void fv_merge4_frommem( fvec &a, fvec &b, fvec &c, fvec &d, const float *in )
{
	a.f0 = in[0];
	b.f0 = in[1];
	c.f0 = in[2];
	d.f0 = in[3];
	
	a.f1 = in[4];
	b.f1 = in[5];
	c.f1 = in[6];
	d.f1 = in[7];
	
	a.f2 = in[8];
	b.f2 = in[9];
	c.f2 = in[10];
	d.f2 = in[11];
	
	a.f3 = in[12];
	b.f3 = in[13];
	c.f3 = in[14];
	d.f3 = in[15];
}


inline void fv_merge4_tomem( float *out, const fvec &a, const fvec &b, const fvec &c, const fvec &d )
{
	out[0] = a.f0;
	out[1] = b.f0;
	out[2] = c.f0;
	out[3] = d.f0;
	
	out[4] = a.f1;
	out[5] = b.f1;
	out[6] = c.f1;
	out[7] = d.f1;
	
	out[8] = a.f2;
	out[9] = b.f2;
	out[10] = c.f2;
	out[11] = d.f2;
	
	out[12] = a.f3;
	out[13] = b.f3;
	out[14] = c.f3;
	out[15] = d.f3;
}


inline void fv_merge3pad_frommem( fvec &a, fvec &b, fvec &c, const float *in )
{
	a.f0 = in[0];
	b.f0 = in[1];
	c.f0 = in[2];
	
	a.f1 = in[4];
	b.f1 = in[5];
	c.f1 = in[6];
	
	a.f2 = in[8];
	b.f2 = in[9];
	c.f2 = in[10];
	
	a.f3 = in[12];
	b.f3 = in[13];
	c.f3 = in[14];
}


inline void fv_merge3pad_tomem( float *out, const fvec &a, const fvec &b, const fvec &c )
{
	out[0] = a.f0;
	out[1] = b.f0;
	out[2] = c.f0;
	
	out[4] = a.f1;
	out[5] = b.f1;
	out[6] = c.f1;
	
	out[8] = a.f2;
	out[9] = b.f2;
	out[10] = c.f2;
	
	out[12] = a.f3;
	out[13] = b.f3;
	out[14] = c.f3;
}


inline void fv_merge2_frommem( fvec &a, fvec &b, const float *in )
{
	a.f0 = in[0];
	b.f0 = in[1];
	
	a.f1 = in[2];
	b.f1 = in[3];
	
	a.f2 = in[4];
	b.f2 = in[5];
	
	a.f3 = in[6];
	b.f3 = in[7];
}


inline void fv_merge2_tomem( float *out, const fvec &a, const fvec &b )
{
	out[0] = a.f0;
	out[1] = b.f0;
	
	out[2] = a.f1;
	out[3] = b.f1;
	
	out[4] = a.f2;
	out[5] = b.f2;
	
	out[6] = a.f3;
	out[7] = b.f3;
}


inline fvec fv_float( float v )
	{ fvec out = { v, v, v, v }; return out; }

#define fv_zero (fv_float(0.0f))
#define fv_half (fv_float(0.5f))
#define fv_one  (fv_float(1.0f))
#define fv_two  (fv_float(2.0f))


#endif
