// Vector.cs
// version 1.0
// fecha: 7-Enero-2003
// Juan Manuel Cueva Lovelle
// Ejemplo de sobrecarga de operadores
// También ilustra el uso del preprocesador

#define DEPURACION

using System;

namespace SobrecargaOperadores
{
    /// <summary>
    /// Ilustra la sobrecarga de operadores con vectores.
    /// No se puede sobrecargar el operador asignación.
    /// También ilustra el uso del preprocesador.
    /// </summary>
    class Vector
    {
        /// <summary>
        /// es el nombre del vector
        /// </summary>
        protected string nombre="NULO";


        /// <summary>
        /// Propiedad Nombre: es el nombre del vector
        /// </summary>
        public string Nombre
        {
            set {nombre=value;}
            get {return nombre;}
        }

        /// <summary>
        /// Coordenadas del vector
        /// </summary>
        /// <remarks>
        /// x,y, z se inicializan a 0 por defecto
        ///</remarks>
        protected float x,y,z;
        #region XYZ
        //Propiedades X,Y,Z
        /// <summary>
        /// Propiedad X: Coordenada x del vector
        /// </summary>
        public float X
        {
            set {X=value;}
            get {return X;}
        }
        /// <summary>
        /// Propiedad Y: Coordenada y del vector
        /// </summary>
        public float Y
        {
            set {Y=value;}
            get {return Y;}
        }
        /// <summary>
        /// Propiedad Z: Coordenada z del vector
        /// </summary>
        public float Z
        {
            set {Z=value;}
            get {return Z;}
        }
        #endregion

        /// <summary>
        /// Constructor sin parámetros
        /// </summary>
        /// <remarks> Crea por defecto el vector NULO(0,0,0)</remarks>
        public Vector()
        {
         #if DEPURACION
            Console.WriteLine("Se ha creado el vector "+this);
         #endif
        }

        /// <summary>
        /// Constructor con parámetros
        /// </summary>
        /// <param name="nombre">Es el nombre del vector</param>
        /// <param name="x">Coordenada x</param>
        /// <param name="y">Coordenada y</param>
        /// <param name="z">Coordenada z</param>
        public Vector(string nombre, float x, float y, float z)
        {
            this.nombre=nombre;
            this.x=x;
            this.y=y;
            this.z=z;
            #if DEPURACION
            Console.WriteLine("Se ha creado el vector "+this);
            #endif
        }

        /// <summary>
        /// Redefinición del método ToString()
        /// </summary>
        /// <returns>nombre(x,y,z)</returns>
        public override string ToString()
        {
            return(nombre+"("+x+", "+y+", "+z+")");
        }

        /// <summary>
        /// Sobrecarga del operador +
        /// </summary>
        /// <remarks>Es obligatorio declararlo público y estático</remarks>
        /// <param name="v1">Operando izquierdo</param>
        /// <param name="v2">Operando derecho</param>
        /// <returns>
        /// Suma de los dos vectores
        /// </returns>
        public static Vector operator +(Vector v1, Vector v2)
        {
            return new Vector(  "Suma",
                                v1.x+v2.x,
                                v1.y+v2.y,
                                v1.z+v2.z);
        }

        /// <summary>
        /// Sobrecarga del operador -
        /// </summary>
        /// <remarks>Es obligatorio declarararlo público y estático</remarks>
        /// <param name="v1">Operando izquierdo</param>
        /// <param name="v2">Operando derecho</param>
        /// <returns>Vector</returns>
        public static Vector operator -(Vector v1, Vector v2)
        {
            return new Vector(  "Resta",
                                v1.x-v2.x,
                                v1.y-v2.y,
                                v1.z-v2.z);
        }
        /// <summary>
        /// Sobrecarga del operador unario -
        /// </summary>
        /// <param name="v">Operando</param>
        /// <returns>Vector cambiado de signo</returns>
        public static Vector operator - (Vector v)
        {
            return new Vector ("CambioSigno", -v.x, -v.y, -v.z);
        }

        /// <summary>
        /// Producto escalar
        /// Sobrecarga del operador *
        /// </summary>
        /// <param name="v1">Operando izquierdo</param>
        /// <param name="v2">Operando derecho</param>
        /// <returns>Producto escalar</returns>
        public static float operator*(Vector v1, Vector v2)
        {
            return (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z);
        }
        /// <summary>
        /// Producto de un escalar por un vector.
        /// Sobrecarga del operador *
        /// </summary>
        /// <param name="k">el escalar es el operando izquierdo</param>
        /// <param name="v">el vector ees el operando derecho</param>
        /// <returns>Escalar por vector</returns>
        public static Vector operator*(float k, Vector v)
        {
            return new Vector("EscalarPorVector", k*v.x,k*v.y,k*v.z);
        }

        /// <summary>
        /// Producto de un vector por un escalar
        /// </summary>
        /// <param name="v">Operando izquierdo</param>
        /// <param name="k">Operando derecho</param>
        /// <returns></returns>
        public static Vector operator*(Vector v, float k)
        {
            return new Vector("VectorPorEscalar", k*v.x,k*v.y,k*v.z);
        }
        /// <summary>
        /// Producto vectorial.
        /// Sobrecarga del operador ^
        /// </summary>
        /// <remarks>Es obligatorio declarararlo publico y estático</remarks>
        /// <param name="v1">Operando izquierdo</param>
        /// <param name="v2">Operando derecho</param>
        /// <returns>Producto vectorial</returns>
        public static Vector operator ^ (Vector v1, Vector v2)
        {
            return new Vector("Producto vectorial",
                               v1.y*v2.z-v1.z*v2.y,
                               v1.z*v2.x-v1.x*v2.z,
                               v1.x*v2.y-v1.y*v2.x
                              );
        }
        /// <summary>
        /// Sobrecarga del operador de comparación ==
        /// </summary>
        /// <remarks>Es obligatorio:
        /// a) sobrecargarlo en compañia de !=.
        /// b) redefinir los métodos Equals() y GetHashCode() de Object.
        /// c) declarararlo publico y estático.
        /// Dos vectores son iguales si tienen las mismas coordenadas
        /// </remarks>
        /// <param name="v1">Operando izquierdo</param>
        /// <param name="v2">Operando derecho</param>
        /// <returns>true: Si son iguales</returns>
        public static bool operator == (Vector v1, Vector v2)
        {
            if ( (v1.x==v2.x)&&
                 (v1.y==v2.y)&&
                 (v1.z==v2.z)
                ) return true;
            else return false;
        }
        /// <summary>
        /// Si se sobrecarga == es obligatorio redefinir Equals()
        /// </summary>
        /// <param name="o">Objeto Vector</param>
        /// <returns>true: si es igual</returns>
        public override bool Equals(object o)
        {
            try
            {
                return (bool) (this == (Vector) o);
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// Si se sobrecarga == es obligatorio redefinir GetHashCode()
        /// </summary>
        /// <returns>Lo mismo que su antepasado Object</returns>
        /// <see cref="Equals(object)">
        /// Este método de Object también se ha redefinido </see>
        public override int GetHashCode()
            {
                return base.GetHashCode();
             }

        /// <summary>
        /// Sobrecarga del operador de comparación !=
        /// </summary>
        /// <remarks>Es obligatorio:
        /// - sobrecargarlo en compañia de ==
        /// - redefinir los métodos Equals() y GetHashCode() de Object
        /// - declarararlo publico y estático
        /// </remarks>
        /// <param name="v1">Operando izquierdo</param>
        /// <param name="v2">Operando derecho</param>
        /// <returns>bool</returns>
        public static bool operator != (Vector v1, Vector v2)
        {
            if ( (v1.x!=v2.x)&&
                (v1.y!=v2.y)&&
                (v1.z!=v2.z)
                ) return true;
            else return false;
        }

    /// <summary>
    /// Prueba unitaria de la clase Vector
    /// </summary>
    [STAThread]
    static void Main(string[] args)
        {
            Vector n =new Vector();
            Console.WriteLine(n);//NULO(0, 0, 0)

            Vector a = new Vector("A",1,2,3);
            Console.WriteLine(a); //A(1, 2, 3)

            // Las constantes por defecto son double, se convierten a float
            Vector b = new Vector("B",(float)1.1,(float)2.2,(float)3.3);
            Console.WriteLine(b); //B(1.1, 2.2, 3.3)

            n = a + b;
            Console.WriteLine("a + b = "+n); //a + b = Suma(2.1, 4.2, 6.3)

            float pe = a * b;
            Console.WriteLine("Producto escalar a * b = {0}",pe);
            //Producto escalar a * b = 15.4

            n = a + b + n;
            Console.WriteLine("a + b + n = "+n);//a + b + n = Suma(4.2, 8.4, 12.6)

            n = a + b - n;
            Console.WriteLine("a + b - n = "+n);//a + b - n = Resta(-2.1, -4.2, -6.3)

            n= a ^ b;
            Console.WriteLine("a ^ b= "+n);
            //a ^ b= Producto vectorial(-2.384186E-07, 1.192093E-07, 0)

            n = a = b;
            Console.WriteLine(b); //B(1.1, 2.2, 3.3)
            Console.WriteLine(a); //B(1.1, 2.2, 3.3)
            Console.WriteLine(n); //B(1.1, 2.2, 3.3)

            Console.WriteLine(a==b); //True
            Console.WriteLine(a.Equals(b)); //True

            Console.WriteLine(a.GetHashCode());
            Console.WriteLine(b.GetHashCode());
            Console.WriteLine(n.GetHashCode());

            Console.WriteLine(a!=b); //False

            a=-b;
            a.Nombre="A";
            Console.WriteLine(a);

            a=5*a;
            Console.WriteLine("5*a="+a);

            a=a * (float) 0.2;
            Console.WriteLine("a*0.2="+a);
          
        


        //Espera una tecla
        Console.ReadLine();
        }
    }
}