using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Xml.Serialization;

namespace VisiMath
{
    /// <summary>
    /// Vector4 with float components
    /// </summary>
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct Vector4 : IEquatable<Vector4>, IXmlSerializable
    {
        #region Fields
        /// <summary>
        /// X value of the vector
        /// </summary>
        public float X;
        /// <summary>
        /// Y value of the vector
        /// </summary>
        public float Y;
        /// <summary>
        /// Z value of the vector
        /// </summary>
        public float Z;
        /// <summary>
        /// W value of the vector
        /// </summary>
        public float W;
        #endregion

        #region Constants

        /// <summary>
        /// Defines a unit-length Vector4 that points towards the X-axis.
        /// </summary>
        public static readonly Vector4 UnitX = new Vector4(1, 0, 0, 0);

        /// <summary>
        /// Defines a unit-length Vector4 that points towards the Y-axis.
        /// </summary>
        public static readonly Vector4 UnitY = new Vector4(0, 1, 0, 0);

        /// <summary>
        /// /// Defines a unit-length Vector4 that points towards the Z-axis.
        /// </summary>
        public static readonly Vector4 UnitZ = new Vector4(0, 0, 1, 0);

        /// <summary>
        /// /// Defines a unit-length Vector4 that points towards the W-axis.
        /// </summary>
        public static readonly Vector4 UnitW = new Vector4(0, 0, 0, 1);

        /// <summary>
        /// Defines a zero-length Vector4.
        /// </summary>
        public static readonly Vector4 Zero = new Vector4(0, 0, 0, 0);

        /// <summary>
        /// Defines an instance with all components set to 1.
        /// </summary>
        public static readonly Vector4 One = new Vector4(1, 1, 1, 1);

        /// <summary>
        /// Invalid vector
        /// </summary>
        public static readonly Vector4 Invalid = new Vector4(float.NaN);

        /// <summary>
        /// Defines the size of the Vector4 struct in bytes.
        /// </summary>
        public static readonly int SizeInBytes = Marshal.SizeOf(new Vector4());

        #endregion

        #region Constructors

        /// <summary>
        /// Basic constructor, constructs Vector4 with specified value for all fields
        /// </summary>
        public Vector4(float val)
        {
            X = val; Y = val; Z = val; W = val;
        }

        /// <summary>
        /// Constructs Vector4 from specified coordinates
        /// </summary>
        /// <param name="x">X coordinate</param>
        /// <param name="y">Y coordinate</param>
        /// <param name="z">Z coordinate</param>
        public Vector4(float x, float y, float z, float w)
        {
            X = x; Y = y; Z = z; W = w;
        }

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="vec">Source value</param>
        public Vector4(Vector4 vec)
        {
            X = vec.X; Y = vec.Y; Z = vec.Z; W = vec.W;
        }

        /// <summary>
        /// Fast copy constructor
        /// </summary>
        /// <param name="vec">Source value</param>
        public Vector4(ref Vector4 vec)
        {
            X = vec.X; Y = vec.Y; Z = vec.Z; W = vec.W;

        }

        /// <summary>
        /// Creates Vector4 from Vector3 (W = 1)
        /// </summary>
        /// <param name="vec">Source value</param>
        public Vector4(ref Vector3 vec)
        {
            X = vec.X; Y = vec.Y; Z = vec.Z; W = 1;
        }

        /// <summary>
        /// Creates Vector4 from Vector3
        /// </summary>
        /// <param name="vec">Source value</param>
        public Vector4(ref Vector3 vec, float w)
        {
            X = vec.X; Y = vec.Y; Z = vec.Z; W = w;
        }

        /// <summary>
        /// Constructs Vector4 from float array
        /// </summary>
        /// <exception cref="System.ArgumentException">
        /// Thrown if argument doesn't have at least 4 components
        /// </exception>
        /// <param name="field">float array with values, must have at least 4 elements</param>
        public Vector4(float[] field)
        {
            if (field.Length < 4)
                throw new System.ArgumentException("Attempt to initialize Vector4 with float field with less then 4 elements");
            X = field[0];
            Y = field[1];
            Z = field[2];
            W = field[3];
        }

        /// <summary>
        /// XML reader constructor
        /// </summary>
        /// <param name="reader">XML reader</param>
        public Vector4(System.Xml.XmlReader reader)
        {
            X = Y = Z = W = 0;
            ReadXml(reader);
        }


        #endregion


        #region Assigning and modifications

        /// <summary>
        /// constructs Vector4 with specified value for all fields
        /// </summary>
        /// <param name="val">value for all fields</param>
        public void Assing(float val)
        {
            X = val;
            Y = val;
            Z = val;
            W = val;
        }


        /// <summary>
        /// Sets vector to specified values
        /// </summary>
        /// <param name="x">X value</param>
        /// <param name="y">Y value</param>
        /// <param name="z">Z value</param>
        public void Assign(float x, float y, float z, float w)
        {
            X = x; Y = y; Z = z; W = w;
        }


        /// <summary>
        /// Sets vector as copy of source vector
        /// </summary>
        /// <param name="vec">source vector</param>
        public void Assign(ref Vector4 vec)
        {
            X = vec.X; Y = vec.Y; Z = vec.Z; W = vec.W;
        }

        /// <summary>
        /// Sets vector to values from float field
        /// </summary>
        /// <exception cref="System.ArgumentException">
        /// Thrown if argument doesn't have at least 4 components
        /// </exception>
        /// <param name="field">Source values, must have at least 4 elements</param>
        public void Assign(float[] field)
        {
            if (field.Length < 4)
                throw new System.ArgumentException("Attempt to initialize Vector4 with float field with less then 3 elements");
            X = field[0];
            Y = field[1];
            Z = field[2];
            W = field[3];
        }

        /// <summary>
        /// Makes Vector invalid by setting X component to float.NotANumber
        /// </summary>
        public void MakeInvalid()
        {
            X = float.NaN;
        }

        /// <summary>
        /// Inverts vector
        /// </summary>
        public void Invert()
        {
            X = -X;
            Y = -Y;
            Z = -Z;
            W = -W;
        }

        /// <summary>
        /// Adds specified vector to this vector - using ref for faster computing
        /// </summary>
        /// <param name="vec">operand</param>
        public void Add(ref Vector4 vec)
        {
            X += vec.X;
            Y += vec.Y;
            Z += vec.Z;
            W += vec.W;
        }

        /// <summary>
        /// Adds specified vector to this vector - using ref for faster computing
        /// </summary>
        /// <param name="vec">operand</param>
        /// <remarks>Use ref overloaded version where possible, it's faster</remarks>
        public void Add(Vector4 vec)
        {
            Add(ref vec);
        }

        /// <summary>
        /// Adds values to this vector
        /// </summary>
        public void Add(float x, float y, float z, float w)
        {
            X += x;
            Y += y;
            Z += z;
            W += w;
        }

        /// <summary>
        /// Substracts values from the vector
        /// </summary>
        public void Substract(float x, float y, float z, float w)
        {
            X -= x;
            Y -= y;
            Z -= z;
            W -= w;
        }

        /// <summary>
        /// Substracts specified vector from this vector - using ref for faster computing
        /// </summary>
        /// <param name="vec">operand</param>
        public void Substract(ref Vector4 vec)
        {
            X -= vec.X;
            Y -= vec.Y;
            Z -= vec.Z;
            W -= vec.W;
        }

        /// <summary>
        /// Substracts specified vector from this vector - using ref for faster computing
        /// </summary>
        /// <param name="vec">operand</param>
        /// <remarks>Use ref overloaded version where possible, it's faster</remarks>
        public void Substract(Vector4 vec)
        {
            Substract(ref vec);
        }

        /// <summary>
        /// Multiplies vector by scalar value
        /// </summary>
        /// <param name="newscale">Scalar value</param>
        public void Mul(float newscale)
        {
            X *= newscale;
            Y *= newscale;
            Z *= newscale;
            W *= newscale;
        }

        /// <summary>
        /// Divides vector by the scalar value
        /// </summary>
        /// <param name="div">scalar value</param>
        public void Divide(float div)
        {
            X /= div;
            Y /= div;
            Z /= div;
            W /= div;
        }

        /// <summary>
        /// Normalizes vector. If length of the vector is zero, doesn't do anything.
        /// </summary>
        public void Normalize()
        {
            float l = Length;
            if (l != 0.0f)
            {
                Mul(1.0f / Length);
            }
        }

        /// <summary>
        /// Fast normalization (less precission, usable for some operations
        /// </summary>
        public void NormalizeFast()
        {
            float newscale = MathTools.InverseSqrFast(X * X + Y * Y + Z * Z + W*W);
            Mul(newscale);
        }


        /// <summary>
        /// Transforms vector by rotational matrix
        /// </summary>
        /// <param name="mat">Rotational matrix</param>
        public void Transform(ref Matrix3 mat)
        {
            mat.Transform(ref this);
        }

        /// <summary>
        /// Transforms vector by homogenous matrix 4x4
        /// </summary>
        /// <param name="mat">Matrix</param>
        public void Transform(ref Matrix4 mat)
        {
            mat.Transform(ref this);
        }


        /// <summary>
        /// Clamps values of the vector 
        /// </summary>
        /// <param name="min">Minimal possible value</param>
        /// <param name="max">Maximal possible value</param>
        public void Clamp(ref Vector4 min, ref Vector4 max)
        {
            X = X < min.X ? min.X : X > max.X ? max.X : X;
            Y = Y < min.Y ? min.Y : Y > max.Y ? max.Y : Y;
            Z = Z < min.Z ? min.Z : Z > max.Z ? max.Z : Z;
            W = W < min.W ? min.W : W > max.W ? max.W : W;
        }

        /// <summary>
        /// Clamps values of the vector to minimal possible value
        /// </summary>
        /// <param name="min"></param>
        public void ClampMin(ref Vector4 min)
        {
            if (X < min.X) X = min.X;
            if (Y < min.Y) Y = min.Y;
            if (Z < min.Z) Z = min.Z;
            if (W < min.W) W = min.W;
        }

        /// <summary>
        /// Clamps values of the vector to maximal possible value
        /// </summary>
        /// <param name="max"></param>
        public void ClampMax(ref Vector4 max)
        {
            if (X > max.X) X = max.X;
            if (Y > max.Y) Y = max.Y;
            if (Z > max.Z) Z = max.Z;
            if (W > max.W) W = max.W;
        }


        /// <summary>
        /// Lineary interpolates vector to target vector
        /// </summary>
        /// <param name="target">Target vector</param>
        /// <param name="inter">Interpolation value, assumes (0-1)</param>
        public void Interpolate(ref Vector4 target, float inter)
        {
            X += inter * (target.X - X);
            Y += inter * (target.Y - Y);
            Z += inter * (target.Z - Z);
            W += inter * (target.W - W);
        }


        #endregion

        #region Methods

        /// <summary>
        /// Property for the vector as an float array
        /// </summary>
        /// <exception cref="System.ArgumentException">
        /// Thrown if argument doesnt have at least 4 components</exception>
        [XmlIgnore]
        public float[] Array
        {
            get { return new float[] { X, Y, Z, W }; }
            set
            {
                if (value.Length >= 4)
                {
                    Assign(value);
                }
                else
                {
                    throw new ArgumentException("Vector4: Float array must contain at least 4 elements");
                }
            }
        }

        /// <summary>
        /// Vector3 component of vector4
        /// </summary>

        [XmlIgnore]
        public Vector3 XYZ
        {
            get
            {
                return new Vector3(X, Y, Z);
            }
            set
            {
                X = value.X;
                Y = value.Y;
                Z = value.Z;
            }
        }

        /// <summary>
        /// An index accessor 
        /// Maps index [0] -> X, [1] -> Y and [2] -> Z.
        /// </summary>
        /// <param name="index">The array index referring to a component within the vector (i.e. x, y, z)</param>
        /// <exception cref="System.ArgumentException">
        /// Thrown if the array argument isn't between 0 and 3 
        /// </exception>
        public float this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: { return X; }
                    case 1: { return Y; }
                    case 2: { return Z; }
                    case 3: { return W; }
                    default: throw new ArgumentException("Indexing out of the vector", "index");
                }
            }
            set
            {
                switch (index)
                {
                    case 0: { X = value; break; }
                    case 1: { Y = value; break; }
                    case 2: { Z = value; break; }
                    case 3: { W = value; break; }
                    default: throw new ArgumentException("Indexing out of the vector", "index");
                }
            }
        }

        /// <summary>
        /// Checks validity of the vector
        /// </summary>
        /// <returns>True if vector.X is not notanumber</returns>
        public bool IsValid()
        {
            if (float.IsNaN(X))
                return false;
            return true;
        }

        /// <summary>
        /// Returns true if vector is normalized
        /// </summary>
        /// <returns></returns>
        public bool IsNormalized()
        {
            if (LengthSquared == 1)
                return true;
            return false;
        }

        /// <summary>
        /// Vector length
        /// </summary>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// Thrown if new desired length is below zero
        /// </exception>
        /// <exception cref="System.ArgumentException">
        /// Thrown if vector is zero vector.
        /// </exception>
        [XmlIgnore]
        public float Length
        {
            get
            {
                return (float)System.Math.Sqrt(LengthSquared);
            }

            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("value", value, "Cannot set negative lenght to Vector4");
                if (this.Equals(Vector4.Zero))
                {
                    throw new ArgumentException("Cannot set length to zero vector");
                }
                Mul(value / Length);
            }
        }

        /// <summary>
        /// Fast but imprecisse vector length
        /// </summary>
        [XmlIgnore]
        public float LengthFast
        {
            get
            {
                return 1.0f / MathTools.InverseSqrFast(X * X + Y * Y + Z * Z + W*W);
            }
        }

        /// <summary>
        /// Squared lenght for fast distance comparison
        /// </summary>
        [XmlIgnore]
        public float LengthSquared
        {
            get
            {
                return (X * X + Y * Y + Z * Z + W * W);
            }
        }


        /// <summary>
        /// Returns distance to other vector
        /// </summary>
        /// <param name="vec2">Vector to measure the distance</param>
        /// <returns>Distance between this and vec2</returns>
        public float Distance(ref Vector4 vec2)
        {
            float retval = (X - vec2.X) * (X - vec2.X)
                         + (Y - vec2.Y) * (Y - vec2.Y)
                         + (Z - vec2.Z) * (Z - vec2.Z)
                         + (W - vec2.W) * (W - vec2.W);
            return (float)Math.Sqrt(retval);
        }

        /// <summary>
        /// Returns distance to other vector, fast but unacurrate version (good enough for distance checking)
        /// </summary>
        /// <param name="vec2">Vector to measure the distance</param>
        /// <returns>Distance between this and vec2</returns>
        public float DistanceFast(ref Vector4 vec2)
        {
            float retval = (X - vec2.X) * (X - vec2.X)
                         + (Y - vec2.Y) * (Y - vec2.Y)
                         + (Z - vec2.Z) * (Z - vec2.Z)
                         + (W - vec2.W) * (W - vec2.W);
            return 1.0f / MathTools.InverseSqrFast(retval);
        }

        /// <summary>
        /// Returns squared distance to other vector, fast, for distance comparison
        /// </summary>
        /// <param name="vec2">Vector to measure the distance</param>
        /// <returns>Distance between this and vec2</returns>
        public float DistanceSquared(ref Vector4 vec2)
        {
            return (X - vec2.X) * (X - vec2.X)
                         + (Y - vec2.Y) * (Y - vec2.Y)
                         + (Z - vec2.Z) * (Z - vec2.Z)
                         + (W - vec2.W) * (W - vec2.W);
        }


        /// <summary>
        /// Dot product with another vector
        /// </summary>
        /// <param name="vec2">Second vector</param>
        /// <returns>Dot product</returns>
        public float Dot(ref Vector4 vec2)
        {
            return this.X * vec2.X + this.Y * vec2.Y + this.Z * vec2.Z + this.W * vec2.X;
        }

        /// <summary>
        /// Returns normalized copy of this vector
        /// </summary>
        /// <returns>Normalized copy</returns>
        public Vector4 NormalizedCopy()
        {
            Vector4 rval = new Vector4(this);
            rval.Normalize();
            return rval;
        }

        /// <summary>
        /// Computes angle between vectors
        /// </summary>
        /// <param name="v2">second operand</param>
        /// <returns>Angle between this and v2 in radians</returns>
        public float Angle(Vector4 v2)
        {
            Vector4 v1 = NormalizedCopy();
            v2.Normalize();
            return (float)Math.Acos(v1.Dot(ref v2));
        }

        /// <summary>
        /// Computes angle between vectors, expects both vectors to be normalized!
        /// </summary>
        /// <param name="v2">second operand</param>
        /// <returns>Angle between this and v2 (both must be normalized vectors) in radians</returns>
        public float AngleNorm(ref Vector4 v2)
        {
            return (float)Math.Acos(this.Dot(ref v2));
        }


        #endregion

        #region operators

        public static Vector4 operator +(Vector4 left, Vector4 right)
        {
            left.Add(ref right);
            return left;
        }

        public static Vector4 operator -(Vector4 left, Vector4 right)
        {
            left.Substract(ref right);
            return left;
        }

        public static Vector4 operator -(Vector4 vec)
        {
            vec.Invert();
            return vec;
        }

        public static Vector4 operator *(Vector4 vec, float scale)
        {
            vec.Mul(scale);
            return vec;
        }

        public static Vector4 operator *(float scale, Vector4 vec)
        {
            vec.Mul(scale);
            return vec;
        }

        public static Vector4 operator /(Vector4 vec, float scale)
        {
            vec.Divide(scale);
            return vec;
        }

        public static bool operator ==(Vector4 left, Vector4 right)
        {
            return left.Equals(right);
        }

        public static bool operator !=(Vector4 left, Vector4 right)
        {
            return !left.Equals(right);
        }

        /// <summary>
        /// Compares magnitude of the vectors
        /// </summary>
        /// <param name="left">left operand</param>
        /// <param name="right">right operand</param>
        /// <returns>True if left operand's lenght is smaller than right</returns>
        public static bool operator <(Vector4 left, Vector4 right)
        {
            return left.LengthSquared < right.LengthSquared;
        }

        /// <summary>
        /// Compares magnitude of the vectors
        /// </summary>
        /// <param name="left">left operand</param>
        /// <param name="right">right operand</param>
        /// <returns>True if left operand's lenght is bigger than right</returns>
        public static bool operator >(Vector4 left, Vector4 right)
        {
            return left.LengthSquared > right.LengthSquared;
        }

        /// <summary>
        /// Compares magnitude of the vectors
        /// </summary>
        /// <param name="left">left operand</param>
        /// <param name="right">right operand</param>
        /// <returns>True if left operand's lenght is equal or smaller than right</returns>
        public static bool operator <=(Vector4 left, Vector4 right)
        {
            return left.LengthSquared <= right.LengthSquared;
        }

        /// <summary>
        /// Compares magnitude of the vectors
        /// </summary>
        /// <param name="left">left operand</param>
        /// <param name="right">right operand</param>
        /// <returns>True if left operand's lenght is equal or bigger than right</returns>
        public static bool operator >=(Vector4 left, Vector4 right)
        {
            return left.LengthSquared >= right.LengthSquared;
        }


        #endregion

        #region Static functions


        #region Products

        /// <summary>
        /// Dot product of two vectors
        /// </summary>
        /// <param name="v1">first operand</param>
        /// <param name="v2">second operand</param>
        /// <returns>Dot product of the vectors</returns>
        public static float Dot(ref Vector4 v1, ref Vector4 v2)
        {
            return v1.Dot(ref v2);
        }




        #endregion

        /// <summary>
        /// Returns normalized copy of the vector
        /// </summary>
        /// <param name="v">operand</param>
        /// <returns>Normalized copy of v</returns>
        public static Vector4 Normalize(ref Vector4 v)
        {
            return v.NormalizedCopy();
        }


        #region math operations

        /// <summary>
        /// Adds two vectors and sets res with result
        /// </summary>
        /// <param name="v1">first operand</param>
        /// <param name="v2">second operand</param>
        /// <param name="res">vector to save the result</param>
        public static void Add(ref Vector4 v1, ref Vector4 v2, out Vector4 res)
        {
            res.X = v1.X + v2.X;
            res.Y = v1.Y + v2.Y;
            res.Z = v1.Z + v2.Z;
            res.W = v1.W + v2.W;
        }

        /// <summary>
        /// Adds two vectors and returns result
        /// </summary>
        /// <param name="v1">first operand</param>
        /// <param name="v2">second operand</param>
        /// <returns>v1+v2</returns>
        public static Vector4 Add(ref Vector4 v1, ref Vector4 v2)
        {
            return new Vector4(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z, v1.W + v2.W);
        }

        /// <summary>
        /// Substracts two vectors and sets res with result
        /// </summary>
        /// <param name="v1">first operand</param>
        /// <param name="v2">second operand</param>
        /// <param name="res">vector to save the result</param>
        public static void Substract(ref Vector4 v1, ref Vector4 v2, out Vector4 res)
        {
            res.X = v1.X - v2.X;
            res.Y = v1.Y - v2.Y;
            res.Z = v1.Z - v2.Z;
            res.W = v1.W - v2.W;
        }

        /// <summary>
        /// Substracts two vectors and returns result
        /// </summary>
        /// <param name="v1">first operand</param>
        /// <param name="v2">second operand</param>
        /// <param name="res">v1-v2</param>
        /// <returns></returns>
        public static Vector4 Substract(ref Vector4 v1, ref Vector4 v2)
        {
            return new Vector4(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z, v1.W - v2.W);
        }

        /// <summary>
        /// Multiplies vector by scalar value and sets result to res
        /// </summary>
        /// <param name="v1">vector</param>
        /// <param name="newscale">scalar</param>
        /// <param name="res">Vector to save result to</param>
        public static void Mul(ref Vector4 v1, float newscale, out Vector4 res)
        {
            res.X = v1.X * newscale;
            res.Y = v1.Y * newscale;
            res.Z = v1.Z * newscale;
            res.W = v1.W * newscale;
        }

        /// <summary>
        /// Multiplies vector by scalar value and returns result
        /// </summary>
        /// <param name="v1">vector</param>
        /// <param name="newscale">scalar</param>
        /// <returns>V1*newscale</returns>
        public static Vector4 Mul(ref Vector4 v1, float newscale)
        {
            return new Vector4(v1.X * newscale, v1.Y * newscale, v1.Z * newscale, v1.W * newscale);
        }

        /// <summary>
        /// Divides vector by scalar value and returns result
        /// </summary>
        /// <param name="v1">Vector</param>
        /// <param name="div">scalar</param>
        /// <returns>v1/div</returns>
        public static Vector4 Divide(ref Vector4 v1, float div)
        {
            Vector4 rval = new Vector4(v1);
            rval.Divide(div);
            return rval;
        }

        /// <summary>
        /// Divides vector by scalar value and returns result
        /// </summary>
        /// <param name="v1">Vector</param>
        /// <param name="div">scalar</param>
        /// <param name="res">Vector to save the result to</param>
        public static void Divide(ref Vector4 v1, float div, out Vector4 res)
        {
            res = new Vector4(v1);
            res.Divide(div);
        }

        #endregion


        #region Clamping

        /// <summary>
        /// Clamps value of the vector to minimum and maximum
        /// </summary>
        /// <param name="v1">Vector to be clamped</param>
        /// <param name="min">Minimal allowed values</param>
        /// <param name="max">Maximal allowed values</param>
        /// <param name="res">Ouput vector</param>
        public static void Clamp(ref Vector4 v1, ref Vector4 min, ref Vector4 max, out Vector4 res)
        {
            res = new Vector4(v1);
            res.Clamp(ref min, ref max);
        }

        /// <summary>
        /// Clamps value of the vector to minimum
        /// </summary>
        /// <param name="v1">Vector to be clamped</param>
        /// <param name="min">Minimal allowed values</param>
        /// <param name="res">Ouput vector</param>
        public static void ClampMin(ref Vector4 v1, ref Vector4 min, out Vector4 res)
        {
            res = new Vector4(v1);
            res.ClampMin(ref min);
        }

        /// <summary>
        /// Clamps value of the vector to maximum
        /// </summary>
        /// <param name="v1">Vector to be clamped</param>
        /// <param name="max">Maximal allowed values</param>
        /// <param name="res">Ouput vector</param>
        public static void ClampMax(ref Vector4 v1, ref Vector4 max, out Vector4 res)
        {
            res = new Vector4(v1);
            res.ClampMax(ref max);
        }

        /// <summary>
        /// Clamps value of the vector to minimum and maximum
        /// </summary>
        /// <param name="v1">Vector to be clamped</param>
        /// <param name="min">Minimal allowed values</param>
        /// <param name="max">Maximal allowed values</param>
        /// <returns>Clamped vector</returns>
        public static Vector4 Clamp(ref Vector4 v1, ref Vector4 min, ref Vector4 max)
        {
            Vector4 res = new Vector4(v1);
            res.Clamp(ref min, ref max);
            return res;
        }

        /// <summary>
        /// Clamps value of the vector to minimum
        /// </summary>
        /// <param name="v1">Vector to be clamped</param>
        /// <param name="min">Minimal allowed values</param>
        /// <returns>Clamped vector</returns>
        public static Vector4 ClampMin(Vector4 v1, ref Vector4 min)
        {
            v1.ClampMin(ref min);
            return v1;
        }

        /// <summary>
        /// Clamps value of the vector to maximum
        /// </summary>
        /// <param name="v1">Vector to be clamped</param>
        /// <param name="max">Maximal allowed values</param>
        /// <returns>Clamped vector</returns>
        public static Vector4 ClampMax(Vector4 v1, ref Vector4 max)
        {
            v1.ClampMax(ref max);
            return v1;
        }

        #endregion



        #region Interpolation

        /// <summary>
        /// Lineary interpolates vector to target vector
        /// </summary>
        /// <param name="source">Source vector</param>
        /// <param name="target">Target vector</param>
        /// <param name="inter">Interpolation value, assumes (0-1)</param>
        /// <param name="result">Vector to save result to</param>
        public static void Interpolate(ref Vector4 source, ref Vector4 target, float inter, out Vector4 result)
        {
            result.X = inter * (target.X - source.X) + source.X;
            result.Y = inter * (target.Y - source.Y) + source.Y;
            result.Z = inter * (target.Z - source.Z) + source.Z;
            result.W = inter * (target.W - source.W) + source.W;
        }


        /// <summary>
        /// Lineary interpolates vector to target vector
        /// </summary>
        /// <param name="source">Source vector</param>
        /// <param name="target">Target vector</param>
        /// <param name="inter">Interpolation value, assumes (0-1)</param>
        /// <returns></returns>
        public static Vector4 Interpolate(ref Vector4 source, ref Vector4 target, float inter)
        {
            Vector4 result = new Vector4(source);
            result.Interpolate(ref target, inter);
            return result;
        }

        #endregion







        #endregion


        #region overrides

        public override string ToString()
        {
            return String.Format("({0}, {1}, {2}, {3})", X, Y, Z, W);
        }

        public override int GetHashCode()
        {
            return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (!(obj is Vector4))
                return false;

            return this.Equals((Vector4)obj);
        }

        #endregion


        #region IEquatable<Vector4> Members

        public bool Equals(Vector4 other)
        {
            return
                X == other.X &&
                Y == other.Y &&
                Z == other.Z &&
                W == other.W;
        }

        #endregion

        #region IXmlSerializable Members

        /// <summary>
        /// Reqired by IXMLserializable
        /// </summary>
        /// <returns>Null</returns>
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Deserializes object from XML
        /// </summary>
        /// <param name="reader">XML reader</param>
        public void ReadXml(System.Xml.XmlReader reader)
        {
            XMLHelper.FloatFromString(reader["X"], out X);
            XMLHelper.FloatFromString(reader["Y"], out Y);
            XMLHelper.FloatFromString(reader["Z"], out Z);
            XMLHelper.FloatFromString(reader["W"], out W);            
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            XMLHelper.WriteFloatAttribute(writer, "X", X);
            XMLHelper.WriteFloatAttribute(writer, "Y", Y);
            XMLHelper.WriteFloatAttribute(writer, "Z", Z);
            XMLHelper.WriteFloatAttribute(writer, "W", W);
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        /// <param name="rootname">Node to save value to</param>
        public void WriteXml(System.Xml.XmlWriter writer, string rootname)
        {
            writer.WriteStartElement(rootname);
            WriteXml(writer);
            writer.WriteEndElement();
        }

        #endregion
    }
}
