Click here to Skip to main content
15,897,371 members
Articles / Programming Languages / C#

The List Trifecta, Part 1

Rate me:
Please Sign up or sign in to vote.
4.97/5 (21 votes)
20 May 2016LGPL321 min read 37.1K   161   40  
The A-list is an all-purpose list, a data structure that can support most standard list operation in O(log n) time and does lots of other stuff, too
//
// Fixed-point math structures produced with the help of T4 (FixedPoint.tt)
// NOTE: THIS CODE HAS NOT BEEN WELL-TESTED AND DOES NOT YET HAVE A TEST SUITE.
// 

namespace Loyc.Math
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using System.Diagnostics;

	/// <summary>Fixed-point type based on Int32 with 8 fractional bits</summary>
	public partial struct FPI8 : IComparable<FPI8>, IEquatable<FPI8>, IConvertible
	{
		public const int Frac = 8;
		public const Int32 Unit = 1 << Frac;
		public static FPI8 Prescaled(Int32 n) { FPI8 r = new FPI8(); r.N = n; return r; }
		public static readonly FPI8 Zero = new FPI8();
		public static readonly FPI8 One = new FPI8(1);
		public static readonly FPI8 Epsilon = Prescaled(1);
		public static readonly FPI8 MaxValue = Prescaled(Int32.MaxValue);
		public static readonly FPI8 MinValue = Prescaled(Int32.MinValue);
		public const Int32 MaxInt = Int32.MaxValue >> Frac;
		public const Int32 MinInt = Int32.MinValue >> Frac;
		public const double MaxDouble = Int32.MaxValue / (double)(1 << Frac);
		public const double MinDouble = Int32.MinValue / (double)(1 << Frac);
		public const Int32 Mask = (1 << Frac) - 1;

		public static explicit operator FPI8(int value) { return new FPI8(value); }
		public static implicit operator FPI8(short value) { return new FPI8(value); }
		
		public static explicit operator FPI8(uint value) { return new FPI8(value); }
		// C# complains about ambiguity if we have two implicit conversion operators
		//public static implicit operator FPI8(ushort value) { return new FPI8(value); }

		public static explicit operator FPI8(long value) { return new FPI8(value); }
		public static explicit operator FPI8(ulong value) { return new FPI8(value); }
		public static explicit operator FPI8(float value) { return new FPI8(value); }
		public static explicit operator FPI8(double value) { return new FPI8(value); }
		public static explicit operator int(FPI8 value) { return (int)(value.N >> Frac); }
		public static explicit operator long(FPI8 value) { return (long)(value.N >> Frac); }
		public static explicit operator uint(FPI8 value) { return (uint)(value.N >> Frac); }
		public static explicit operator ulong(FPI8 value) { return (ulong)(value.N >> Frac); }
		public static explicit operator float(FPI8 value) { return (float)value.N * (1.0f / (1 << Frac)); }
		public static explicit operator double(FPI8 value) { return (double)value.N * (1.0 / (1 << Frac)); }
		
		public static explicit operator FPI16(FPI8 value)
		{
			if (value.N > Int32.MaxValue >> 8)
				return FPI16.MaxValue;
			if (value.N < Int32.MinValue >> 8)
				return FPI16.MinValue;
			return FPI16.Prescaled((Int32)(value.N << 8));
		}
		public static explicit operator FPI23(FPI8 value)
		{
			if (value.N > Int32.MaxValue >> 15)
				return FPI23.MaxValue;
			if (value.N < Int32.MinValue >> 15)
				return FPI23.MinValue;
			return FPI23.Prescaled((Int32)(value.N << 15));
		}
		public static explicit operator FPL16(FPI8 value)
		{
			return FPL16.Prescaled(((Int64)value.N << 8));
		}
		public static explicit operator FPL32(FPI8 value)
		{
			return FPL32.Prescaled(((Int64)value.N << 24));
		}

		public Int32 N;

		private static void Overflow()
		{
 			throw new OverflowException();
		}
		public static FPI8 CheckedCast(int num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI8 CheckedCast(uint num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI8 CheckedCast(long num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI8 CheckedCast(ulong num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI8 CheckedCast(double num)
		{
			if (!(num >= MinDouble && num <= MaxDouble))
				Overflow();
			return FastCast(num);
		}
		public static FPI8 FastCast(int num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI8 FastCast(uint num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI8 FastCast(long num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI8 FastCast(double num)
		{
			return Prescaled((int)Math.Round(MathEx.ShiftLeft(num, Frac)));
		}

		public FPI8(int num)
		{
			N = (Int32)num << Frac;
			if (num < MinInt)
				N = Int32.MinValue;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI8(uint num)
		{
			N = (Int32)num << Frac;
			if (num > (uint)MaxInt)
				N = Int32.MaxValue;
		}
		public FPI8(long num)
		{
			N = (Int32)num << Frac;
			if (num < MinInt)
				N = Int32.MinValue;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI8(ulong num)
		{
			N = (Int32)num << Frac;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI8(double num)
		{
			N = (Int32)Math.Round(MathEx.ShiftLeft(num, Frac));
			if (num <= MinDouble)
				N = Int32.MinValue;
			if (num >= MaxDouble)
				N = Int32.MaxValue;
		}

		public static FPI8 operator +(FPI8 a, Int32 b) { a.N += b << Frac; return a; }
		public static FPI8 operator -(FPI8 a, Int32 b) { a.N -= b << Frac; return a; }
		public static FPI8 operator *(FPI8 a, Int32 b) { a.N *= b; return a; }
		public static FPI8 operator /(FPI8 a, Int32 b) { a.N /= b; return a; }
		public static FPI8 operator %(FPI8 a, Int32 b) { a.N %= b << Frac; return a; }
		public static FPI8 operator +(FPI8 a, FPI8 b) { a.N += b.N; return a; }
		public static FPI8 operator -(FPI8 a, FPI8 b) { a.N -= b.N; return a; }
		public static FPI8 operator -(FPI8 a) { a.N = -a.N; return a; }

		public static FPI8 operator *(FPI8 a, FPI8 b) { return Prescaled((int)((long)a.N * (long)b.N >> Frac)); }
		public static FPI8 operator /(FPI8 a, FPI8 b) { return Prescaled((int)((long)(a.N << Frac) / b.N)); }
		public static FPI8 operator %(FPI8 a, FPI8 b) { a.N %= b.N; return a; }
		public static FPI8 operator <<(FPI8 a, int b) { a.N <<= b; return a; }
		public static FPI8 operator >>(FPI8 a, int b) { a.N >>= b; return a; }
		public static bool operator ==(FPI8 a, FPI8 b) { return a.N == b.N; }
		public static bool operator !=(FPI8 a, FPI8 b) { return a.N != b.N; }
		public static bool operator >=(FPI8 a, FPI8 b) { return a.N >= b.N; }
		public static bool operator <=(FPI8 a, FPI8 b) { return a.N <= b.N; }
		public static bool operator >(FPI8 a, FPI8 b) { return a.N > b.N; }
		public static bool operator <(FPI8 a, FPI8 b) { return a.N < b.N; }
		
		public static bool operator ==(FPI8 a, Int32 b) { return a.N == b << Frac; }
		public static bool operator !=(FPI8 a, Int32 b) { return a.N != b << Frac; }
		public static bool operator >=(FPI8 a, Int32 b) { return a.N >= b << Frac; }
		public static bool operator <=(FPI8 a, Int32 b) { return a.N <= b << Frac; }
		public static bool operator >(FPI8 a, Int32 b) { return a.N > b << Frac; }
		public static bool operator <(FPI8 a, Int32 b) { return a.N < b << Frac; }

		public static FPI8 operator &(FPI8 a, FPI8 b) { a.N &= b.N; return a; }
		public static FPI8 operator |(FPI8 a, FPI8 b) { a.N |= b.N; return a; }
		public static FPI8 operator ^(FPI8 a, FPI8 b) { a.N ^= b.N; return a; }
		public static FPI8 operator ~(FPI8 a) { a.N = ~a.N; return a; }
		
		public static FPI8 operator ++(FPI8 a) { a.N += Unit; return a; }
		public static FPI8 operator --(FPI8 a) { a.N -= Unit; return a; }
		
		public FPI8 Abs() { return Prescaled(N >= 0 ? N : -N); }
		public int CountOnes() { return MathEx.CountOnes(N); }
		public int Log2Floor()
		{
			int r = MathEx.Log2Floor(N);
			if (r >= 0) r -= Frac;
			return r;
		}
		public FPI8 Sqrt()
		{
			if ((uint)N <= (uint)MaxInt)
				return Prescaled((Int32)MathEx.Sqrt((uint)N << Frac));
			else
				// Compute lower-precision answer (this path is also taken if N is negative)
				return Prescaled(MathEx.Sqrt(N) << Frac/2);
		}
		public FPI8 MulDiv(FPI8 mul, FPI8 div)
		{
			return Prescaled(MathEx.MulDiv(N, mul.N, div.N));
		}
		public FPI8 MulShift(FPI8 mul, int shift)
		{
			return Prescaled(MathEx.MulShift(N, mul.N, shift + Frac));
		}

		public override bool Equals(object obj)
		{
			return obj is FPI8 && ((FPI8)obj).N == N;
		}
		public override int GetHashCode()
		{
			return N.GetHashCode();
		}
		public override string ToString()
		{
			return ((double)this).ToString("0.###");
		}

		public int CompareTo(FPI8 other)
		{
			return N.CompareTo(other.N);
		}
		public bool Equals(FPI8 other)
		{
			return N == other.N;
		}

		#region IConvertible

		TypeCode IConvertible.GetTypeCode()
		{
			return TypeCode.Object;
		}
		public bool ToBoolean(IFormatProvider provider)
		{
			return N != 0;
		}
		public sbyte ToSByte(IFormatProvider provider)
		{
			return checked((sbyte)(N >> Frac));
		}
		public short ToInt16(IFormatProvider provider)
		{
			return checked((short)(N >> Frac));
		}
		public int ToInt32(IFormatProvider provider)
		{
			return checked((int)(N >> Frac));
		}
		public long ToInt64(IFormatProvider provider)
		{
			return checked((long)(N >> Frac));
		}
		public byte ToByte(IFormatProvider provider)
		{
			return checked((byte)(N >> Frac));
		}
		public ushort ToUInt16(IFormatProvider provider)
		{
			return checked((ushort)(N >> Frac));
		}
		public uint ToUInt32(IFormatProvider provider)
		{
			return checked((uint)(N >> Frac));
		}
		public ulong ToUInt64(IFormatProvider provider)
		{
			return checked((ulong)(N >> Frac));
		}
		public char ToChar(IFormatProvider provider)
		{
			return checked((char)(N >> Frac));
		}
		public double ToDouble(IFormatProvider provider)
		{
			return (double)this;
		}
		DateTime IConvertible.ToDateTime(IFormatProvider provider)
		{
			throw new InvalidCastException();
		}
		public decimal ToDecimal(IFormatProvider provider)
		{
			return (decimal)(double)this;
		}
		public float ToSingle(IFormatProvider provider)
		{
			return (float)this;
		}
		string IConvertible.ToString(IFormatProvider provider)
		{
			return ToString();
		}
		object IConvertible.ToType(Type conversionType, IFormatProvider provider)
		{
			return Convert.ChangeType((double)this, conversionType, provider);
		}

		#endregion
	}

	/// <summary>Fixed-point type based on Int32 with 16 fractional bits</summary>
	public partial struct FPI16 : IComparable<FPI16>, IEquatable<FPI16>, IConvertible
	{
		public const int Frac = 16;
		public const Int32 Unit = 1 << Frac;
		public static FPI16 Prescaled(Int32 n) { FPI16 r = new FPI16(); r.N = n; return r; }
		public static readonly FPI16 Zero = new FPI16();
		public static readonly FPI16 One = new FPI16(1);
		public static readonly FPI16 Epsilon = Prescaled(1);
		public static readonly FPI16 MaxValue = Prescaled(Int32.MaxValue);
		public static readonly FPI16 MinValue = Prescaled(Int32.MinValue);
		public const Int32 MaxInt = Int32.MaxValue >> Frac;
		public const Int32 MinInt = Int32.MinValue >> Frac;
		public const double MaxDouble = Int32.MaxValue / (double)(1 << Frac);
		public const double MinDouble = Int32.MinValue / (double)(1 << Frac);
		public const Int32 Mask = (1 << Frac) - 1;

		public static explicit operator FPI16(int value) { return new FPI16(value); }
		public static implicit operator FPI16(short value) { return new FPI16(value); }
		
		public static explicit operator FPI16(uint value) { return new FPI16(value); }
		// C# complains about ambiguity if we have two implicit conversion operators
		//public static implicit operator FPI16(ushort value) { return new FPI16(value); }

		public static explicit operator FPI16(long value) { return new FPI16(value); }
		public static explicit operator FPI16(ulong value) { return new FPI16(value); }
		public static explicit operator FPI16(float value) { return new FPI16(value); }
		public static explicit operator FPI16(double value) { return new FPI16(value); }
		public static explicit operator int(FPI16 value) { return (int)(value.N >> Frac); }
		public static explicit operator long(FPI16 value) { return (long)(value.N >> Frac); }
		public static explicit operator uint(FPI16 value) { return (uint)(value.N >> Frac); }
		public static explicit operator ulong(FPI16 value) { return (ulong)(value.N >> Frac); }
		public static explicit operator float(FPI16 value) { return (float)value.N * (1.0f / (1 << Frac)); }
		public static explicit operator double(FPI16 value) { return (double)value.N * (1.0 / (1 << Frac)); }
		
		public static explicit operator FPI8(FPI16 value)
		{
			return FPI8.Prescaled((Int32)(value.N >> 8));
		}
		public static explicit operator FPI23(FPI16 value)
		{
			if (value.N > Int32.MaxValue >> 7)
				return FPI23.MaxValue;
			if (value.N < Int32.MinValue >> 7)
				return FPI23.MinValue;
			return FPI23.Prescaled((Int32)(value.N << 7));
		}
		public static explicit operator FPL16(FPI16 value)
		{
			return FPL16.Prescaled((Int64)(value.N));
		}
		public static explicit operator FPL32(FPI16 value)
		{
			return FPL32.Prescaled(((Int64)value.N << 16));
		}

		public Int32 N;

		private static void Overflow()
		{
 			throw new OverflowException();
		}
		public static FPI16 CheckedCast(int num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI16 CheckedCast(uint num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI16 CheckedCast(long num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI16 CheckedCast(ulong num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI16 CheckedCast(double num)
		{
			if (!(num >= MinDouble && num <= MaxDouble))
				Overflow();
			return FastCast(num);
		}
		public static FPI16 FastCast(int num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI16 FastCast(uint num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI16 FastCast(long num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI16 FastCast(double num)
		{
			return Prescaled((int)Math.Round(MathEx.ShiftLeft(num, Frac)));
		}

		public FPI16(int num)
		{
			N = (Int32)num << Frac;
			if (num < MinInt)
				N = Int32.MinValue;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI16(uint num)
		{
			N = (Int32)num << Frac;
			if (num > (uint)MaxInt)
				N = Int32.MaxValue;
		}
		public FPI16(long num)
		{
			N = (Int32)num << Frac;
			if (num < MinInt)
				N = Int32.MinValue;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI16(ulong num)
		{
			N = (Int32)num << Frac;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI16(double num)
		{
			N = (Int32)Math.Round(MathEx.ShiftLeft(num, Frac));
			if (num <= MinDouble)
				N = Int32.MinValue;
			if (num >= MaxDouble)
				N = Int32.MaxValue;
		}

		public static FPI16 operator +(FPI16 a, Int32 b) { a.N += b << Frac; return a; }
		public static FPI16 operator -(FPI16 a, Int32 b) { a.N -= b << Frac; return a; }
		public static FPI16 operator *(FPI16 a, Int32 b) { a.N *= b; return a; }
		public static FPI16 operator /(FPI16 a, Int32 b) { a.N /= b; return a; }
		public static FPI16 operator %(FPI16 a, Int32 b) { a.N %= b << Frac; return a; }
		public static FPI16 operator +(FPI16 a, FPI16 b) { a.N += b.N; return a; }
		public static FPI16 operator -(FPI16 a, FPI16 b) { a.N -= b.N; return a; }
		public static FPI16 operator -(FPI16 a) { a.N = -a.N; return a; }

		public static FPI16 operator *(FPI16 a, FPI16 b) { return Prescaled((int)((long)a.N * (long)b.N >> Frac)); }
		public static FPI16 operator /(FPI16 a, FPI16 b) { return Prescaled((int)((long)(a.N << Frac) / b.N)); }
		public static FPI16 operator %(FPI16 a, FPI16 b) { a.N %= b.N; return a; }
		public static FPI16 operator <<(FPI16 a, int b) { a.N <<= b; return a; }
		public static FPI16 operator >>(FPI16 a, int b) { a.N >>= b; return a; }
		public static bool operator ==(FPI16 a, FPI16 b) { return a.N == b.N; }
		public static bool operator !=(FPI16 a, FPI16 b) { return a.N != b.N; }
		public static bool operator >=(FPI16 a, FPI16 b) { return a.N >= b.N; }
		public static bool operator <=(FPI16 a, FPI16 b) { return a.N <= b.N; }
		public static bool operator >(FPI16 a, FPI16 b) { return a.N > b.N; }
		public static bool operator <(FPI16 a, FPI16 b) { return a.N < b.N; }
		
		public static bool operator ==(FPI16 a, Int32 b) { return a.N == b << Frac; }
		public static bool operator !=(FPI16 a, Int32 b) { return a.N != b << Frac; }
		public static bool operator >=(FPI16 a, Int32 b) { return a.N >= b << Frac; }
		public static bool operator <=(FPI16 a, Int32 b) { return a.N <= b << Frac; }
		public static bool operator >(FPI16 a, Int32 b) { return a.N > b << Frac; }
		public static bool operator <(FPI16 a, Int32 b) { return a.N < b << Frac; }

		public static FPI16 operator &(FPI16 a, FPI16 b) { a.N &= b.N; return a; }
		public static FPI16 operator |(FPI16 a, FPI16 b) { a.N |= b.N; return a; }
		public static FPI16 operator ^(FPI16 a, FPI16 b) { a.N ^= b.N; return a; }
		public static FPI16 operator ~(FPI16 a) { a.N = ~a.N; return a; }
		
		public static FPI16 operator ++(FPI16 a) { a.N += Unit; return a; }
		public static FPI16 operator --(FPI16 a) { a.N -= Unit; return a; }
		
		public FPI16 Abs() { return Prescaled(N >= 0 ? N : -N); }
		public int CountOnes() { return MathEx.CountOnes(N); }
		public int Log2Floor()
		{
			int r = MathEx.Log2Floor(N);
			if (r >= 0) r -= Frac;
			return r;
		}
		public FPI16 Sqrt()
		{
			if ((uint)N <= (uint)MaxInt)
				return Prescaled((Int32)MathEx.Sqrt((uint)N << Frac));
			else
				// Compute lower-precision answer (this path is also taken if N is negative)
				return Prescaled(MathEx.Sqrt(N) << Frac/2);
		}
		public FPI16 MulDiv(FPI16 mul, FPI16 div)
		{
			return Prescaled(MathEx.MulDiv(N, mul.N, div.N));
		}
		public FPI16 MulShift(FPI16 mul, int shift)
		{
			return Prescaled(MathEx.MulShift(N, mul.N, shift + Frac));
		}

		public override bool Equals(object obj)
		{
			return obj is FPI16 && ((FPI16)obj).N == N;
		}
		public override int GetHashCode()
		{
			return N.GetHashCode();
		}
		public override string ToString()
		{
			return ((double)this).ToString("0.#####");
		}

		public int CompareTo(FPI16 other)
		{
			return N.CompareTo(other.N);
		}
		public bool Equals(FPI16 other)
		{
			return N == other.N;
		}

		#region IConvertible

		TypeCode IConvertible.GetTypeCode()
		{
			return TypeCode.Object;
		}
		public bool ToBoolean(IFormatProvider provider)
		{
			return N != 0;
		}
		public sbyte ToSByte(IFormatProvider provider)
		{
			return checked((sbyte)(N >> Frac));
		}
		public short ToInt16(IFormatProvider provider)
		{
			return checked((short)(N >> Frac));
		}
		public int ToInt32(IFormatProvider provider)
		{
			return checked((int)(N >> Frac));
		}
		public long ToInt64(IFormatProvider provider)
		{
			return checked((long)(N >> Frac));
		}
		public byte ToByte(IFormatProvider provider)
		{
			return checked((byte)(N >> Frac));
		}
		public ushort ToUInt16(IFormatProvider provider)
		{
			return checked((ushort)(N >> Frac));
		}
		public uint ToUInt32(IFormatProvider provider)
		{
			return checked((uint)(N >> Frac));
		}
		public ulong ToUInt64(IFormatProvider provider)
		{
			return checked((ulong)(N >> Frac));
		}
		public char ToChar(IFormatProvider provider)
		{
			return checked((char)(N >> Frac));
		}
		public double ToDouble(IFormatProvider provider)
		{
			return (double)this;
		}
		DateTime IConvertible.ToDateTime(IFormatProvider provider)
		{
			throw new InvalidCastException();
		}
		public decimal ToDecimal(IFormatProvider provider)
		{
			return (decimal)(double)this;
		}
		public float ToSingle(IFormatProvider provider)
		{
			return (float)this;
		}
		string IConvertible.ToString(IFormatProvider provider)
		{
			return ToString();
		}
		object IConvertible.ToType(Type conversionType, IFormatProvider provider)
		{
			return Convert.ChangeType((double)this, conversionType, provider);
		}

		#endregion
	}

	/// <summary>Fixed-point type based on Int32 with 23 fractional bits</summary>
	public partial struct FPI23 : IComparable<FPI23>, IEquatable<FPI23>, IConvertible
	{
		public const int Frac = 23;
		public const Int32 Unit = 1 << Frac;
		public static FPI23 Prescaled(Int32 n) { FPI23 r = new FPI23(); r.N = n; return r; }
		public static readonly FPI23 Zero = new FPI23();
		public static readonly FPI23 One = new FPI23(1);
		public static readonly FPI23 Epsilon = Prescaled(1);
		public static readonly FPI23 MaxValue = Prescaled(Int32.MaxValue);
		public static readonly FPI23 MinValue = Prescaled(Int32.MinValue);
		public const Int32 MaxInt = Int32.MaxValue >> Frac;
		public const Int32 MinInt = Int32.MinValue >> Frac;
		public const double MaxDouble = Int32.MaxValue / (double)(1 << Frac);
		public const double MinDouble = Int32.MinValue / (double)(1 << Frac);
		public const Int32 Mask = (1 << Frac) - 1;

		public static explicit operator FPI23(int value) { return new FPI23(value); }
		public static implicit operator FPI23(sbyte value) { return new FPI23(value); }
		
		public static explicit operator FPI23(uint value) { return new FPI23(value); }
		//public static implicit operator FPI23(byte value) { return new FPI23(value); }

		public static explicit operator FPI23(long value) { return new FPI23(value); }
		public static explicit operator FPI23(ulong value) { return new FPI23(value); }
		public static explicit operator FPI23(float value) { return new FPI23(value); }
		public static explicit operator FPI23(double value) { return new FPI23(value); }
		public static explicit operator int(FPI23 value) { return (int)(value.N >> Frac); }
		public static explicit operator long(FPI23 value) { return (long)(value.N >> Frac); }
		public static explicit operator uint(FPI23 value) { return (uint)(value.N >> Frac); }
		public static explicit operator ulong(FPI23 value) { return (ulong)(value.N >> Frac); }
		public static explicit operator float(FPI23 value) { return (float)value.N * (1.0f / (1 << Frac)); }
		public static explicit operator double(FPI23 value) { return (double)value.N * (1.0 / (1 << Frac)); }
		
		public static explicit operator FPI8(FPI23 value)
		{
			return FPI8.Prescaled((Int32)(value.N >> 15));
		}
		public static explicit operator FPI16(FPI23 value)
		{
			return FPI16.Prescaled((Int32)(value.N >> 7));
		}
		public static explicit operator FPL16(FPI23 value)
		{
			return FPL16.Prescaled((Int64)(value.N >> 7));
		}
		public static explicit operator FPL32(FPI23 value)
		{
			return FPL32.Prescaled(((Int64)value.N << 9));
		}

		public Int32 N;

		private static void Overflow()
		{
 			throw new OverflowException();
		}
		public static FPI23 CheckedCast(int num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI23 CheckedCast(uint num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI23 CheckedCast(long num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI23 CheckedCast(ulong num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int32)num << Frac);
		}
		public static FPI23 CheckedCast(double num)
		{
			if (!(num >= MinDouble && num <= MaxDouble))
				Overflow();
			return FastCast(num);
		}
		public static FPI23 FastCast(int num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI23 FastCast(uint num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI23 FastCast(long num)
		{
			return Prescaled((Int32)num << Frac);
		}
		public static FPI23 FastCast(double num)
		{
			return Prescaled((int)Math.Round(MathEx.ShiftLeft(num, Frac)));
		}

		public FPI23(int num)
		{
			N = (Int32)num << Frac;
			if (num < MinInt)
				N = Int32.MinValue;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI23(uint num)
		{
			N = (Int32)num << Frac;
			if (num > (uint)MaxInt)
				N = Int32.MaxValue;
		}
		public FPI23(long num)
		{
			N = (Int32)num << Frac;
			if (num < MinInt)
				N = Int32.MinValue;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI23(ulong num)
		{
			N = (Int32)num << Frac;
			if (num > MaxInt)
				N = Int32.MaxValue;
		}
		public FPI23(double num)
		{
			N = (Int32)Math.Round(MathEx.ShiftLeft(num, Frac));
			if (num <= MinDouble)
				N = Int32.MinValue;
			if (num >= MaxDouble)
				N = Int32.MaxValue;
		}

		public static FPI23 operator +(FPI23 a, Int32 b) { a.N += b << Frac; return a; }
		public static FPI23 operator -(FPI23 a, Int32 b) { a.N -= b << Frac; return a; }
		public static FPI23 operator *(FPI23 a, Int32 b) { a.N *= b; return a; }
		public static FPI23 operator /(FPI23 a, Int32 b) { a.N /= b; return a; }
		public static FPI23 operator %(FPI23 a, Int32 b) { a.N %= b << Frac; return a; }
		public static FPI23 operator +(FPI23 a, FPI23 b) { a.N += b.N; return a; }
		public static FPI23 operator -(FPI23 a, FPI23 b) { a.N -= b.N; return a; }
		public static FPI23 operator -(FPI23 a) { a.N = -a.N; return a; }

		public static FPI23 operator *(FPI23 a, FPI23 b) { return Prescaled((int)((long)a.N * (long)b.N >> Frac)); }
		public static FPI23 operator /(FPI23 a, FPI23 b) { return Prescaled((int)((long)(a.N << Frac) / b.N)); }
		public static FPI23 operator %(FPI23 a, FPI23 b) { a.N %= b.N; return a; }
		public static FPI23 operator <<(FPI23 a, int b) { a.N <<= b; return a; }
		public static FPI23 operator >>(FPI23 a, int b) { a.N >>= b; return a; }
		public static bool operator ==(FPI23 a, FPI23 b) { return a.N == b.N; }
		public static bool operator !=(FPI23 a, FPI23 b) { return a.N != b.N; }
		public static bool operator >=(FPI23 a, FPI23 b) { return a.N >= b.N; }
		public static bool operator <=(FPI23 a, FPI23 b) { return a.N <= b.N; }
		public static bool operator >(FPI23 a, FPI23 b) { return a.N > b.N; }
		public static bool operator <(FPI23 a, FPI23 b) { return a.N < b.N; }
		
		public static bool operator ==(FPI23 a, Int32 b) { return a.N == b << Frac; }
		public static bool operator !=(FPI23 a, Int32 b) { return a.N != b << Frac; }
		public static bool operator >=(FPI23 a, Int32 b) { return a.N >= b << Frac; }
		public static bool operator <=(FPI23 a, Int32 b) { return a.N <= b << Frac; }
		public static bool operator >(FPI23 a, Int32 b) { return a.N > b << Frac; }
		public static bool operator <(FPI23 a, Int32 b) { return a.N < b << Frac; }

		public static FPI23 operator &(FPI23 a, FPI23 b) { a.N &= b.N; return a; }
		public static FPI23 operator |(FPI23 a, FPI23 b) { a.N |= b.N; return a; }
		public static FPI23 operator ^(FPI23 a, FPI23 b) { a.N ^= b.N; return a; }
		public static FPI23 operator ~(FPI23 a) { a.N = ~a.N; return a; }
		
		public static FPI23 operator ++(FPI23 a) { a.N += Unit; return a; }
		public static FPI23 operator --(FPI23 a) { a.N -= Unit; return a; }
		
		public FPI23 Abs() { return Prescaled(N >= 0 ? N : -N); }
		public int CountOnes() { return MathEx.CountOnes(N); }
		public int Log2Floor()
		{
			int r = MathEx.Log2Floor(N);
			if (r >= 0) r -= Frac;
			return r;
		}
		public FPI23 Sqrt()
		{
			if ((uint)N <= (uint)MaxInt)
				return Prescaled((Int32)MathEx.Sqrt((uint)N << Frac));
			else
				// Compute lower-precision answer (this path is also taken if N is negative)
				return Prescaled(MathEx.Sqrt(N << 1) << Frac/2);
		}
		public FPI23 MulDiv(FPI23 mul, FPI23 div)
		{
			return Prescaled(MathEx.MulDiv(N, mul.N, div.N));
		}
		public FPI23 MulShift(FPI23 mul, int shift)
		{
			return Prescaled(MathEx.MulShift(N, mul.N, shift + Frac));
		}

		public override bool Equals(object obj)
		{
			return obj is FPI23 && ((FPI23)obj).N == N;
		}
		public override int GetHashCode()
		{
			return N.GetHashCode();
		}
		public override string ToString()
		{
			return ((double)this).ToString("0.#######");
		}

		public int CompareTo(FPI23 other)
		{
			return N.CompareTo(other.N);
		}
		public bool Equals(FPI23 other)
		{
			return N == other.N;
		}

		#region IConvertible

		TypeCode IConvertible.GetTypeCode()
		{
			return TypeCode.Object;
		}
		public bool ToBoolean(IFormatProvider provider)
		{
			return N != 0;
		}
		public sbyte ToSByte(IFormatProvider provider)
		{
			return checked((sbyte)(N >> Frac));
		}
		public short ToInt16(IFormatProvider provider)
		{
			return checked((short)(N >> Frac));
		}
		public int ToInt32(IFormatProvider provider)
		{
			return checked((int)(N >> Frac));
		}
		public long ToInt64(IFormatProvider provider)
		{
			return checked((long)(N >> Frac));
		}
		public byte ToByte(IFormatProvider provider)
		{
			return checked((byte)(N >> Frac));
		}
		public ushort ToUInt16(IFormatProvider provider)
		{
			return checked((ushort)(N >> Frac));
		}
		public uint ToUInt32(IFormatProvider provider)
		{
			return checked((uint)(N >> Frac));
		}
		public ulong ToUInt64(IFormatProvider provider)
		{
			return checked((ulong)(N >> Frac));
		}
		public char ToChar(IFormatProvider provider)
		{
			return checked((char)(N >> Frac));
		}
		public double ToDouble(IFormatProvider provider)
		{
			return (double)this;
		}
		DateTime IConvertible.ToDateTime(IFormatProvider provider)
		{
			throw new InvalidCastException();
		}
		public decimal ToDecimal(IFormatProvider provider)
		{
			return (decimal)(double)this;
		}
		public float ToSingle(IFormatProvider provider)
		{
			return (float)this;
		}
		string IConvertible.ToString(IFormatProvider provider)
		{
			return ToString();
		}
		object IConvertible.ToType(Type conversionType, IFormatProvider provider)
		{
			return Convert.ChangeType((double)this, conversionType, provider);
		}

		#endregion
	}

	/// <summary>Fixed-point type based on Int64 with 16 fractional bits</summary>
	public partial struct FPL16 : IComparable<FPL16>, IEquatable<FPL16>, IConvertible
	{
		public const int Frac = 16;
		public const Int64 Unit = 1 << Frac;
		public static FPL16 Prescaled(Int64 n) { FPL16 r = new FPL16(); r.N = n; return r; }
		public static readonly FPL16 Zero = new FPL16();
		public static readonly FPL16 One = new FPL16(1);
		public static readonly FPL16 Epsilon = Prescaled(1);
		public static readonly FPL16 MaxValue = Prescaled(Int64.MaxValue);
		public static readonly FPL16 MinValue = Prescaled(Int64.MinValue);
		public const Int64 MaxInt = Int64.MaxValue >> Frac;
		public const Int64 MinInt = Int64.MinValue >> Frac;
		public const double MaxDouble = Int64.MaxValue / (double)(1 << Frac);
		public const double MinDouble = Int64.MinValue / (double)(1 << Frac);
		public const Int64 Mask = (1 << Frac) - 1;

		public static implicit operator FPL16(int value) { return new FPL16(value); }
		
		public static implicit operator FPL16(uint value) { return new FPL16(value); }

		public static explicit operator FPL16(long value) { return new FPL16(value); }
		public static explicit operator FPL16(ulong value) { return new FPL16(value); }
		public static explicit operator FPL16(float value) { return new FPL16(value); }
		public static explicit operator FPL16(double value) { return new FPL16(value); }
		public static explicit operator int(FPL16 value) { return (int)(value.N >> Frac); }
		public static explicit operator long(FPL16 value) { return (long)(value.N >> Frac); }
		public static explicit operator uint(FPL16 value) { return (uint)(value.N >> Frac); }
		public static explicit operator ulong(FPL16 value) { return (ulong)(value.N >> Frac); }
		public static explicit operator float(FPL16 value) { return (float)value.N * (1.0f / (1 << Frac)); }
		public static explicit operator double(FPL16 value) { return (double)value.N * (1.0 / (1 << Frac)); }
		
		public static explicit operator FPI8(FPL16 value)
		{
			if (value.N > Int64.MaxValue >> 24)
				return FPI8.MaxValue;
			if (value.N < Int64.MinValue >> 24)
				return FPI8.MinValue;
			return FPI8.Prescaled((Int32)(value.N >> 8));
		}
		public static explicit operator FPI16(FPL16 value)
		{
			if (value.N > Int64.MaxValue >> 32)
				return FPI16.MaxValue;
			if (value.N < Int64.MinValue >> 32)
				return FPI16.MinValue;
			return FPI16.Prescaled((Int32)(value.N));
		}
		public static explicit operator FPI23(FPL16 value)
		{
			if (value.N > Int64.MaxValue >> 39)
				return FPI23.MaxValue;
			if (value.N < Int64.MinValue >> 39)
				return FPI23.MinValue;
			return FPI23.Prescaled((Int32)(value.N << 7));
		}
		public static explicit operator FPL32(FPL16 value)
		{
			if (value.N > Int64.MaxValue >> 16)
				return FPL32.MaxValue;
			if (value.N < Int64.MinValue >> 16)
				return FPL32.MinValue;
			return FPL32.Prescaled(((Int64)value.N << 16));
		}

		public Int64 N;

		private static void Overflow()
		{
 			throw new OverflowException();
		}
		public static FPL16 CheckedCast(long num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int64)num << Frac);
		}
		public static FPL16 CheckedCast(ulong num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int64)num << Frac);
		}
		public static FPL16 CheckedCast(double num)
		{
			if (!(num >= MinDouble && num <= MaxDouble))
				Overflow();
			return FastCast(num);
		}
		public static FPL16 FastCast(int num)
		{
			return Prescaled((Int64)num << Frac);
		}
		public static FPL16 FastCast(uint num)
		{
			return Prescaled((Int64)num << Frac);
		}
		public static FPL16 FastCast(long num)
		{
			return Prescaled((Int64)num << Frac);
		}
		public static FPL16 FastCast(double num)
		{
			return Prescaled((int)Math.Round(MathEx.ShiftLeft(num, Frac)));
		}

		public FPL16(int num)
		{
			N = (Int64)num << Frac;
		}
		public FPL16(uint num)
		{
			N = (Int64)num << Frac;
		}
		public FPL16(long num)
		{
			N = (Int64)num << Frac;
			if (num < MinInt)
				N = Int64.MinValue;
			if (num > MaxInt)
				N = Int64.MaxValue;
		}
		public FPL16(ulong num)
		{
			N = (Int64)num << Frac;
			if (num > MaxInt)
				N = Int64.MaxValue;
		}
		public FPL16(double num)
		{
			N = (Int64)Math.Round(MathEx.ShiftLeft(num, Frac));
			if (num <= MinDouble)
				N = Int64.MinValue;
			if (num >= MaxDouble)
				N = Int64.MaxValue;
		}

		public static FPL16 operator +(FPL16 a, Int64 b) { a.N += b << Frac; return a; }
		public static FPL16 operator -(FPL16 a, Int64 b) { a.N -= b << Frac; return a; }
		public static FPL16 operator *(FPL16 a, Int64 b) { a.N *= b; return a; }
		public static FPL16 operator /(FPL16 a, Int64 b) { a.N /= b; return a; }
		public static FPL16 operator %(FPL16 a, Int64 b) { a.N %= b << Frac; return a; }
		public static FPL16 operator +(FPL16 a, FPL16 b) { a.N += b.N; return a; }
		public static FPL16 operator -(FPL16 a, FPL16 b) { a.N -= b.N; return a; }
		public static FPL16 operator -(FPL16 a) { a.N = -a.N; return a; }

		public static FPL16 operator *(FPL16 a, FPL16 b)
		{
			return Prescaled(MathEx.MulShift(a.N, b.N, Frac));
			// Flaw: unreliable if Frac < 32
			//var afrac = a.N & Mask;
			//var bfrac = b.N & Mask;
			//var whole = (FPL16)((Int64)a * (Int64)b);
			//whole.N += afrac * bfrac >> Frac;
			//return whole;
		}
		public static FPL16 operator /(FPL16 a, FPL16 b)
		{
			long whole = a.N / b.N;
			long remainder = a.N % b.N;
			remainder = (remainder << Frac) / b.N;
			Debug.Assert(remainder < (1 << Frac));
			a.N = (whole << Frac) + remainder;
			return a;
			// TODO: test negative numbers: 7 / -2.5, -7 / 2.5, -7 / -2.5
		}
		public static FPL16 operator %(FPL16 a, FPL16 b) { a.N %= b.N; return a; }
		public static FPL16 operator <<(FPL16 a, int b) { a.N <<= b; return a; }
		public static FPL16 operator >>(FPL16 a, int b) { a.N >>= b; return a; }
		public static bool operator ==(FPL16 a, FPL16 b) { return a.N == b.N; }
		public static bool operator !=(FPL16 a, FPL16 b) { return a.N != b.N; }
		public static bool operator >=(FPL16 a, FPL16 b) { return a.N >= b.N; }
		public static bool operator <=(FPL16 a, FPL16 b) { return a.N <= b.N; }
		public static bool operator >(FPL16 a, FPL16 b) { return a.N > b.N; }
		public static bool operator <(FPL16 a, FPL16 b) { return a.N < b.N; }
		
		public static bool operator ==(FPL16 a, Int64 b) { return a.N == b << Frac; }
		public static bool operator !=(FPL16 a, Int64 b) { return a.N != b << Frac; }
		public static bool operator >=(FPL16 a, Int64 b) { return a.N >= b << Frac; }
		public static bool operator <=(FPL16 a, Int64 b) { return a.N <= b << Frac; }
		public static bool operator >(FPL16 a, Int64 b) { return a.N > b << Frac; }
		public static bool operator <(FPL16 a, Int64 b) { return a.N < b << Frac; }

		public static FPL16 operator &(FPL16 a, FPL16 b) { a.N &= b.N; return a; }
		public static FPL16 operator |(FPL16 a, FPL16 b) { a.N |= b.N; return a; }
		public static FPL16 operator ^(FPL16 a, FPL16 b) { a.N ^= b.N; return a; }
		public static FPL16 operator ~(FPL16 a) { a.N = ~a.N; return a; }
		
		public static FPL16 operator ++(FPL16 a) { a.N += Unit; return a; }
		public static FPL16 operator --(FPL16 a) { a.N -= Unit; return a; }
		
		public FPL16 Abs() { return Prescaled(N >= 0 ? N : -N); }
		public int CountOnes() { return MathEx.CountOnes(N); }
		public int Log2Floor()
		{
			int r = MathEx.Log2Floor(N);
			if (r >= 0) r -= Frac;
			return r;
		}
		public FPL16 Sqrt()
		{
			if ((ulong)N <= (ulong)MaxInt)
				return Prescaled((Int64)MathEx.Sqrt((ulong)N << Frac));
			else
				// Compute lower-precision answer (this path is also taken if N is negative)
				return Prescaled(MathEx.Sqrt(N) << Frac/2);
		}
		public FPL16 MulDiv(FPL16 mul, FPL16 div)
		{
			return Prescaled(MathEx.MulDiv(N, mul.N, div.N));
		}
		public FPL16 MulShift(FPL16 mul, int shift)
		{
			return Prescaled(MathEx.MulShift(N, mul.N, shift + Frac));
		}

		public override bool Equals(object obj)
		{
			return obj is FPL16 && ((FPL16)obj).N == N;
		}
		public override int GetHashCode()
		{
			return N.GetHashCode();
		}
		public override string ToString()
		{
			return ((double)this).ToString("0.#####");
		}

		public int CompareTo(FPL16 other)
		{
			return N.CompareTo(other.N);
		}
		public bool Equals(FPL16 other)
		{
			return N == other.N;
		}

		#region IConvertible

		TypeCode IConvertible.GetTypeCode()
		{
			return TypeCode.Object;
		}
		public bool ToBoolean(IFormatProvider provider)
		{
			return N != 0;
		}
		public sbyte ToSByte(IFormatProvider provider)
		{
			return checked((sbyte)(N >> Frac));
		}
		public short ToInt16(IFormatProvider provider)
		{
			return checked((short)(N >> Frac));
		}
		public int ToInt32(IFormatProvider provider)
		{
			return checked((int)(N >> Frac));
		}
		public long ToInt64(IFormatProvider provider)
		{
			return checked((long)(N >> Frac));
		}
		public byte ToByte(IFormatProvider provider)
		{
			return checked((byte)(N >> Frac));
		}
		public ushort ToUInt16(IFormatProvider provider)
		{
			return checked((ushort)(N >> Frac));
		}
		public uint ToUInt32(IFormatProvider provider)
		{
			return checked((uint)(N >> Frac));
		}
		public ulong ToUInt64(IFormatProvider provider)
		{
			return checked((ulong)(N >> Frac));
		}
		public char ToChar(IFormatProvider provider)
		{
			return checked((char)(N >> Frac));
		}
		public double ToDouble(IFormatProvider provider)
		{
			return (double)this;
		}
		DateTime IConvertible.ToDateTime(IFormatProvider provider)
		{
			throw new InvalidCastException();
		}
		public decimal ToDecimal(IFormatProvider provider)
		{
			return (decimal)(double)this;
		}
		public float ToSingle(IFormatProvider provider)
		{
			return (float)this;
		}
		string IConvertible.ToString(IFormatProvider provider)
		{
			return ToString();
		}
		object IConvertible.ToType(Type conversionType, IFormatProvider provider)
		{
			return Convert.ChangeType((double)this, conversionType, provider);
		}

		#endregion
	}

	/// <summary>Fixed-point type based on Int64 with 32 fractional bits</summary>
	public partial struct FPL32 : IComparable<FPL32>, IEquatable<FPL32>, IConvertible
	{
		public const int Frac = 32;
		public const Int64 Unit = 1 << Frac;
		public static FPL32 Prescaled(Int64 n) { FPL32 r = new FPL32(); r.N = n; return r; }
		public static readonly FPL32 Zero = new FPL32();
		public static readonly FPL32 One = new FPL32(1);
		public static readonly FPL32 Epsilon = Prescaled(1);
		public static readonly FPL32 MaxValue = Prescaled(Int64.MaxValue);
		public static readonly FPL32 MinValue = Prescaled(Int64.MinValue);
		public const Int64 MaxInt = Int64.MaxValue >> Frac;
		public const Int64 MinInt = Int64.MinValue >> Frac;
		public const double MaxDouble = Int64.MaxValue / (double)(1 << Frac);
		public const double MinDouble = Int64.MinValue / (double)(1 << Frac);
		public const Int64 Mask = (1 << Frac) - 1;

		public static implicit operator FPL32(int value) { return new FPL32(value); }
		
		public static explicit operator FPL32(uint value) { return new FPL32(value); }
		// C# complains about ambiguity if we have two implicit conversion operators
		//public static implicit operator FPL32(ushort value) { return new FPL32(value); }

		public static explicit operator FPL32(long value) { return new FPL32(value); }
		public static explicit operator FPL32(ulong value) { return new FPL32(value); }
		public static explicit operator FPL32(float value) { return new FPL32(value); }
		public static explicit operator FPL32(double value) { return new FPL32(value); }
		public static explicit operator int(FPL32 value) { return (int)(value.N >> Frac); }
		public static explicit operator long(FPL32 value) { return (long)(value.N >> Frac); }
		public static explicit operator uint(FPL32 value) { return (uint)(value.N >> Frac); }
		public static explicit operator ulong(FPL32 value) { return (ulong)(value.N >> Frac); }
		public static explicit operator float(FPL32 value) { return (float)value.N * (1.0f / (1 << Frac)); }
		public static explicit operator double(FPL32 value) { return (double)value.N * (1.0 / (1 << Frac)); }
		
		public static explicit operator FPI8(FPL32 value)
		{
			if (value.N > Int64.MaxValue >> 8)
				return FPI8.MaxValue;
			if (value.N < Int64.MinValue >> 8)
				return FPI8.MinValue;
			return FPI8.Prescaled((Int32)(value.N >> 24));
		}
		public static explicit operator FPI16(FPL32 value)
		{
			if (value.N > Int64.MaxValue >> 16)
				return FPI16.MaxValue;
			if (value.N < Int64.MinValue >> 16)
				return FPI16.MinValue;
			return FPI16.Prescaled((Int32)(value.N >> 16));
		}
		public static explicit operator FPI23(FPL32 value)
		{
			if (value.N > Int64.MaxValue >> 23)
				return FPI23.MaxValue;
			if (value.N < Int64.MinValue >> 23)
				return FPI23.MinValue;
			return FPI23.Prescaled((Int32)(value.N >> 9));
		}
		public static explicit operator FPL16(FPL32 value)
		{
			return FPL16.Prescaled((Int64)(value.N >> 16));
		}

		public Int64 N;

		private static void Overflow()
		{
 			throw new OverflowException();
		}
		public static FPL32 CheckedCast(long num)
		{
			if (num < MinInt || num > MaxInt)
				Overflow();
			return Prescaled((Int64)num << Frac);
		}
		public static FPL32 CheckedCast(ulong num)
		{
			if (num > MaxInt)
				Overflow();
			return Prescaled((Int64)num << Frac);
		}
		public static FPL32 CheckedCast(double num)
		{
			if (!(num >= MinDouble && num <= MaxDouble))
				Overflow();
			return FastCast(num);
		}
		public static FPL32 FastCast(int num)
		{
			return Prescaled((Int64)num << Frac);
		}
		public static FPL32 FastCast(uint num)
		{
			return Prescaled((Int64)num << Frac);
		}
		public static FPL32 FastCast(long num)
		{
			return Prescaled((Int64)num << Frac);
		}
		public static FPL32 FastCast(double num)
		{
			return Prescaled((int)Math.Round(MathEx.ShiftLeft(num, Frac)));
		}

		public FPL32(int num)
		{
			N = (Int64)num << Frac;
		}
		public FPL32(uint num)
		{
			N = (Int64)num << Frac;
			if (num > (uint)MaxInt)
				N = Int64.MaxValue;
		}
		public FPL32(long num)
		{
			N = (Int64)num << Frac;
			if (num < MinInt)
				N = Int64.MinValue;
			if (num > MaxInt)
				N = Int64.MaxValue;
		}
		public FPL32(ulong num)
		{
			N = (Int64)num << Frac;
			if (num > MaxInt)
				N = Int64.MaxValue;
		}
		public FPL32(double num)
		{
			N = (Int64)Math.Round(MathEx.ShiftLeft(num, Frac));
			if (num <= MinDouble)
				N = Int64.MinValue;
			if (num >= MaxDouble)
				N = Int64.MaxValue;
		}

		public static FPL32 operator +(FPL32 a, Int64 b) { a.N += b << Frac; return a; }
		public static FPL32 operator -(FPL32 a, Int64 b) { a.N -= b << Frac; return a; }
		public static FPL32 operator *(FPL32 a, Int64 b) { a.N *= b; return a; }
		public static FPL32 operator /(FPL32 a, Int64 b) { a.N /= b; return a; }
		public static FPL32 operator %(FPL32 a, Int64 b) { a.N %= b << Frac; return a; }
		public static FPL32 operator +(FPL32 a, FPL32 b) { a.N += b.N; return a; }
		public static FPL32 operator -(FPL32 a, FPL32 b) { a.N -= b.N; return a; }
		public static FPL32 operator -(FPL32 a) { a.N = -a.N; return a; }

		public static FPL32 operator *(FPL32 a, FPL32 b)
		{
			return Prescaled(MathEx.MulShift(a.N, b.N, Frac));
			// Flaw: unreliable if Frac < 32
			//var afrac = a.N & Mask;
			//var bfrac = b.N & Mask;
			//var whole = (FPL32)((Int64)a * (Int64)b);
			//whole.N += afrac * bfrac >> Frac;
			//return whole;
		}
		public static FPL32 operator /(FPL32 a, FPL32 b)
		{
			long whole = a.N / b.N;
			long remainder = a.N % b.N;
			remainder = (remainder << Frac) / b.N;
			Debug.Assert(remainder < (1 << Frac));
			a.N = (whole << Frac) + remainder;
			return a;
			// TODO: test negative numbers: 7 / -2.5, -7 / 2.5, -7 / -2.5
		}
		public static FPL32 operator %(FPL32 a, FPL32 b) { a.N %= b.N; return a; }
		public static FPL32 operator <<(FPL32 a, int b) { a.N <<= b; return a; }
		public static FPL32 operator >>(FPL32 a, int b) { a.N >>= b; return a; }
		public static bool operator ==(FPL32 a, FPL32 b) { return a.N == b.N; }
		public static bool operator !=(FPL32 a, FPL32 b) { return a.N != b.N; }
		public static bool operator >=(FPL32 a, FPL32 b) { return a.N >= b.N; }
		public static bool operator <=(FPL32 a, FPL32 b) { return a.N <= b.N; }
		public static bool operator >(FPL32 a, FPL32 b) { return a.N > b.N; }
		public static bool operator <(FPL32 a, FPL32 b) { return a.N < b.N; }
		
		public static bool operator ==(FPL32 a, Int64 b) { return a.N == b << Frac; }
		public static bool operator !=(FPL32 a, Int64 b) { return a.N != b << Frac; }
		public static bool operator >=(FPL32 a, Int64 b) { return a.N >= b << Frac; }
		public static bool operator <=(FPL32 a, Int64 b) { return a.N <= b << Frac; }
		public static bool operator >(FPL32 a, Int64 b) { return a.N > b << Frac; }
		public static bool operator <(FPL32 a, Int64 b) { return a.N < b << Frac; }

		public static FPL32 operator &(FPL32 a, FPL32 b) { a.N &= b.N; return a; }
		public static FPL32 operator |(FPL32 a, FPL32 b) { a.N |= b.N; return a; }
		public static FPL32 operator ^(FPL32 a, FPL32 b) { a.N ^= b.N; return a; }
		public static FPL32 operator ~(FPL32 a) { a.N = ~a.N; return a; }
		
		public static FPL32 operator ++(FPL32 a) { a.N += Unit; return a; }
		public static FPL32 operator --(FPL32 a) { a.N -= Unit; return a; }
		
		public FPL32 Abs() { return Prescaled(N >= 0 ? N : -N); }
		public int CountOnes() { return MathEx.CountOnes(N); }
		public int Log2Floor()
		{
			int r = MathEx.Log2Floor(N);
			if (r >= 0) r -= Frac;
			return r;
		}
		public FPL32 Sqrt()
		{
			if ((ulong)N <= (ulong)MaxInt)
				return Prescaled((Int64)MathEx.Sqrt((ulong)N << Frac));
			else
				// Compute lower-precision answer (this path is also taken if N is negative)
				return Prescaled(MathEx.Sqrt(N) << Frac/2);
		}
		public FPL32 MulDiv(FPL32 mul, FPL32 div)
		{
			return Prescaled(MathEx.MulDiv(N, mul.N, div.N));
		}
		public FPL32 MulShift(FPL32 mul, int shift)
		{
			return Prescaled(MathEx.MulShift(N, mul.N, shift + Frac));
		}

		public override bool Equals(object obj)
		{
			return obj is FPL32 && ((FPL32)obj).N == N;
		}
		public override int GetHashCode()
		{
			return N.GetHashCode();
		}
		public override string ToString()
		{
			return ((double)this).ToString("0.##########");
		}

		public int CompareTo(FPL32 other)
		{
			return N.CompareTo(other.N);
		}
		public bool Equals(FPL32 other)
		{
			return N == other.N;
		}

		#region IConvertible

		TypeCode IConvertible.GetTypeCode()
		{
			return TypeCode.Object;
		}
		public bool ToBoolean(IFormatProvider provider)
		{
			return N != 0;
		}
		public sbyte ToSByte(IFormatProvider provider)
		{
			return checked((sbyte)(N >> Frac));
		}
		public short ToInt16(IFormatProvider provider)
		{
			return checked((short)(N >> Frac));
		}
		public int ToInt32(IFormatProvider provider)
		{
			return checked((int)(N >> Frac));
		}
		public long ToInt64(IFormatProvider provider)
		{
			return checked((long)(N >> Frac));
		}
		public byte ToByte(IFormatProvider provider)
		{
			return checked((byte)(N >> Frac));
		}
		public ushort ToUInt16(IFormatProvider provider)
		{
			return checked((ushort)(N >> Frac));
		}
		public uint ToUInt32(IFormatProvider provider)
		{
			return checked((uint)(N >> Frac));
		}
		public ulong ToUInt64(IFormatProvider provider)
		{
			return checked((ulong)(N >> Frac));
		}
		public char ToChar(IFormatProvider provider)
		{
			return checked((char)(N >> Frac));
		}
		public double ToDouble(IFormatProvider provider)
		{
			return (double)this;
		}
		DateTime IConvertible.ToDateTime(IFormatProvider provider)
		{
			throw new InvalidCastException();
		}
		public decimal ToDecimal(IFormatProvider provider)
		{
			return (decimal)(double)this;
		}
		public float ToSingle(IFormatProvider provider)
		{
			return (float)this;
		}
		string IConvertible.ToString(IFormatProvider provider)
		{
			return ToString();
		}
		object IConvertible.ToType(Type conversionType, IFormatProvider provider)
		{
			return Convert.ChangeType((double)this, conversionType, provider);
		}

		#endregion
	}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions