/*
* Copyright 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/


#ifndef F16_H
#define F16_H

#include <stdint.h>
#include <ostream>



/** Round a float to the appropriate F16 value. */
inline int32_t _f16_round(float f)
{
	if (f>0.0F) return (int32_t)(f+0.5F);
	if (f<0.0F) return (int32_t)(f-0.5F);
	return 0;
}



/** A class for F15.16 fixed point arithmetic with saturation.  */
class F16 {


	private:

	int32_t mV;


	public:

	F16() {}

	F16(int i) { mV = i<<16; }
	F16(float f) { mV = _f16_round(f*65536.0F); }
	F16(double f) { mV = _f16_round((float)f*65536.0F); }

	int32_t& raw() { return mV; }
	const int32_t& raw() const { return mV; }

	float f() const { return mV/65536.0F; }

	//operator float() const { return mV/65536.0F; }
	//operator int() const { return mV>>16; }

	F16 operator=(float f)
	{
		mV = _f16_round(f*65536.0F);
		return *this;
	}

	F16 operator=(int i)
	{
		mV = i<<16;
		return *this;
	}

	F16 operator=(const F16& other)
	{
		mV = other.mV;
		return mV;
	}

	F16 operator+(const F16& other) const
	{
		F16 retVal;
		retVal.mV = mV + other.mV;
		return retVal;
	}

	F16& operator+=(const F16& other)
	{
		mV += other.mV;
		return *this;
	}

	F16 operator-(const F16& other) const
	{
		F16 retVal;
		retVal.mV = mV - other.mV;
		return retVal;
	}

	F16& operator-=(const F16& other)
	{
		mV -= other.mV;
		return *this;
	}

	F16 operator*(const F16& other) const
	{
		F16 retVal;
		int64_t p = (int64_t)mV * (int64_t)other.mV;
		retVal.mV = p>>16;
		return retVal;
	}

	F16& operator*=(const F16& other)
	{
		int64_t p = (int64_t)mV * (int64_t)other.mV;
		mV = p>>16;
		return *this;
	}

	F16 operator*(float f) const
	{
		F16 retVal;
		retVal.mV = mV * f;
		return retVal;
	}

	F16& operator*=(float f)
	{
		mV *= f;
		return *this;
	}

	F16 operator/(const F16& other) const
	{
		F16 retVal;
		int64_t pV = (int64_t)mV << 16;
		retVal.mV = pV / other.mV;
		return retVal;
	}

	F16& operator/=(const F16& other)
	{
		int64_t pV = (int64_t)mV << 16;
		mV = pV / other.mV;
		return *this;
	}

	F16 operator/(float f) const
	{
		F16 retVal;
		retVal.mV = mV / f;
		return retVal;
	}

	F16& operator/=(float f)
	{
		mV /= f;
		return *this;
	}

	bool operator>(const F16& other) const
	{
		return mV>other.mV;
	}

	bool operator<(const F16& other) const
	{
		return mV<other.mV;
	}

	bool operator==(const F16& other) const
	{
		return mV==other.mV;
	}

	bool operator>(float f) const
	{
		return (mV/65536.0F) > f;
	}

	bool operator<(float f) const
	{
		return (mV/65536.0F) < f;
	}

	bool operator==(float f) const
	{
		return (mV/65536.0F) == f;
	}

};



inline std::ostream& operator<<(std::ostream& os, const F16& v)
{
	os << v.f();
	return os;
}

#endif

