using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LiczbyZespolone
{
    struct Complex
    {
        public double Real, Imag; //pola

        public Complex(double real, double imag) //konstrukror
        {
            this.Real = real;
            this.Imag = imag;
        }

        #region Metody
        /*public void Conj()
        {
            Imag = -Imag;
        }*/

        public override string ToString()
        {
            return "(" + Real.ToString() + "," + Imag.ToString() + ")";
        }

        public string ToStringI()
        {
            string s = Real.ToString();
            if (Imag >= 0) s += "+i"; else s += "-i";
            s += Math.Abs(Imag);
            return s;
        }

        public override bool Equals(object obj)
        {
            if (obj is Complex)
            {
                Complex z = (Complex)obj;
                return (this.Real == z.Real && this.Imag == z.Imag);
            }
            else return false;
        }

        public override int GetHashCode()
        {
            return Real.GetHashCode() ^ Imag.GetHashCode();
        }
        #endregion

        #region Wlasnosci
        public double Norm //wlasnosc tylko do odczytu
        {
            get //odczyt
            {
                return Real * Real + Imag * Imag;
            }
        }

        public double Radius
        {
            get //odczyt
            {
                return Math.Sqrt(Norm);
            }
            set //zapis
            {
                double phi = Phase;
                Real = value * System.Math.Cos(phi);
                Imag = value * System.Math.Sin(phi);
            }
        }

        public double Phase
        {
            get //odczyt
            {
                //zmieniam zakres z -Pi:Pi na 0:2Pi
                double phi = Math.Atan2(Imag, Real);
                if (phi < 0) phi += 2 * Math.PI;
                return phi;
            }
            set //zapis
            {
                double r = Radius;
                Real = r * System.Math.Cos(value);
                Imag = r * System.Math.Sin(value);
            }
        }

        public string ToStringPolar()
        {
            return "R=" + Radius + ", Phi=" + Phase + " (" + 180 * Phase / Math.PI + " stopni)";
        }

        public Complex Conj
        {
            get
            {
                return new Complex(Real, -Imag);
            }
        }

        public static Complex I //statyczna wlasnosc tylko do odczytu
        {
            get
            {
                return new Complex(0, 1);
            }
        }
        #endregion

        #region Operatory
        //operatory arytmetyczne
        public static Complex operator -(Complex z)
        {
            return new Complex(-z.Real, -z.Imag);
        }

        public static Complex operator +(Complex z1, Complex z2)
        {
            return new Complex(z1.Real + z2.Real, z1.Imag + z2.Imag);
        }

        public static Complex operator -(Complex z1, Complex z2)
        {
            return new Complex(z1.Real - z2.Real, z1.Imag - z2.Imag);
        }

        public static Complex operator *(Complex z1, Complex z2)
        {
            return new Complex(z1.Real * z2.Real - z1.Imag * z2.Imag, z1.Real * z2.Imag + z1.Imag * z2.Real);
        }

        public static Complex operator /(Complex z1, Complex z2)
        {
            if (z2.Norm == 0) throw new Exception("Dzielenie liczby zespolonej przez zero");
            //z1 *= Conj(z2);
            z1 *= z2.Conj;
            z1.Real /= z2.Norm;
            z1.Imag /= z2.Norm;
            return z1;
        }

        //operatory logiczne
        public static bool operator ==(Complex z1, Complex z2)
        {
            return ((z1.Real == z2.Real) && (z1.Imag == z2.Imag));
        }

        public static bool operator !=(Complex z1, Complex z2)
        {
            return !(z1 == z2);
        }

        //konwersja z Complex na double
        public static explicit operator double(Complex z)
        {
            if (z.Imag == 0) return z.Real;
            else throw new Exception("Konwersja liczby zespolonej na rzeczywistą jest niemożliwa (część urojona różna od zera)");
        }

        //konwersja z double na Complex
        public static implicit operator Complex(double real)
        {
            return new Complex(real, 0);
        }
        #endregion

        public static Complex CreateFromPolar(double radius, double phase)
        {
            return new Complex(radius * Math.Cos(phase), radius * Math.Sin(phase));
        }

        public static Complex Cis(double phase)
        {
            return CreateFromPolar(1, phase);
        }

        #region Funkcje matematyczne (metody statyczne)
        public static double Abs(Complex z)
        {
            return z.Radius;
        }

        public static double Arg(Complex z)
        {
            return z.Phase;
        }

        public static Complex Exp(Complex z)
        {
            return Math.Exp(z.Real) * CreateFromPolar(1, z.Imag);
        }

        public static Complex Cosh(Complex z)
        {
            return 0.5 * (Exp(z) + Exp(-z));
        }

        public static Complex Sinh(Complex z)
        {
            return 0.5 * (Exp(z) - Exp(-z));
        }

        public static Complex Tanh(Complex z)
        {
            return Sinh(z) / Cosh(z);
        }

        public static Complex Cos(Complex z)
        {
            Complex i = Complex.I;
            return 0.5 * (Exp(i * z) + Exp(-i * z));
        }

        public static Complex Sin(Complex z)
        {
            Complex i = Complex.I;
            return 0.5 * (-i) * (Exp(i * z) - Exp(-i * z));
        }

        public static Complex Tan(Complex z)
        {
            return Sin(z) / Cos(z);
        }

        public static Complex Sqrt(Complex z)
        {
            return Math.Sqrt(z.Radius) * (new Complex(Math.Cos(0.5 * z.Phase), Math.Sin(0.5 * z.Phase)));
        }

        public static Complex Log(Complex z)
        {
            return new Complex(Math.Log(z.Radius), z.Phase);
        }

        public static Complex Log10(Complex z)
        {
            return Log(z) / Math.Log(10);
        }

        public static Complex Acos(Complex z)
        {
            Complex i = Complex.I;
            return -i * Log(z + i * Sqrt(1 - z * z));
        }

        public static Complex Asin(Complex z)
        {
            Complex i = Complex.I;
            return -i * Log(i * z + Sqrt(1 - z * z));
        }

        public static Complex Atan(Complex z)
        {
            Complex i = Complex.I;
            return -0.5 * i * Log((1 + i * z) / (1 - i * z));
        }

        static public Complex Pow(Complex z, double exponent)
        {
            return Exp(exponent * Log(z));
        }
        #endregion
    }
}
