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

namespace EvoBoat.EvoAlg
{
    public class Vector : IVector<double>, ICloneable<Vector>
    {
        internal double[] values;
        public int Length
        {
            get { return values.Length; }
        }
        public double this[int index]
        {
            get { return values[index]; }
            set { values[index] = value; }
        }

        Vector() { }

        public Vector(int length)
        {
            this.values = new double[length];
        }

        public Vector(double[] values)
        {
            this.values = values.ToArray();
        }

        public Vector(IEnumerable<double> values)
        {
            this.values = values.ToArray();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder("[");
            for (int i = 0; i < values.Length; i++)
            {
                sb.Append(string.Format("{0:F2}", values[i]));
                sb.Append(',');
            }
            if (sb.Length > 1)
                sb[sb.Length - 1] = ']';
            else
                sb.Append(']');
            return sb.ToString();
        }

        public override bool Equals(object obj)
        {
            Vector v = obj as Vector;
            return (!object.ReferenceEquals(v, null) && this == v);
        }

        public override int GetHashCode()
        {
            return values.GetHashCode();
        }

        public IEnumerator<double> GetEnumerator()
        {
            for (int i = 0; i < values.Length; i++)
                yield return values[i];
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public int IndexOf(double v)
        {
            for (int i = 0; i < values.Length; i++)
                if (values[i] == v)
                    return i;
            return -1;
        }

        public double Max()
        {
            return values.Max();
        }

        public double Min()
        {
            return values.Min();
        }

        public int IndexofMin()
        {
            int mini = 0;
            for (int i = 1; i < values.Length; i++)
                if (values[i] < values[mini])
                    mini = i;
            return mini;
        }

        public int IndexofMax()
        {
            int maxi = 0;
            for (int i = 1; i < values.Length; i++)
                if (values[i] > values[maxi])
                    maxi = i;
            return maxi;
        }

        public double Sum()
        {
            double result = 0;
            for (int i = 0; i < values.Length; i++)
                result += values[i];
            return result;
        }

        public double Prod()
        {
            double result = 1;
            for (int i = 0; i < values.Length; i++)
                result *= values[i];
            return result;
        }

        public double Norm()
        {
            double result = 0;
            for (int i = 0; i < values.Length; i++)
                result += (values[i] * values[i]);
            return Math.Sqrt(result);
        }

        public void Sort()
        {
            Array.Sort(values);
        }

        public Vector Clone()
        {
            return new Vector(values);
        }

        public Vector SubVector(int len)
        {
            double[] array = new double[len];
            for (int i = 0; i < len; i++)
                array[i] = values[i];
            return new Vector() { values = array };
        }

        public Vector SubVector(int index, int len)
        {
            double[] array = new double[len];
            for (int i = 0; i < len; i++)
                array[i] = values[index + i];
            return new Vector() { values = array };
        }

        public Vector SubVectorFrom(int index)
        {
            int len = values.Length - index;
            return this.SubVector(index, len);
        }

        public static bool operator ==(Vector v1, Vector v2)
        {
            if (v1.values.Length != v2.values.Length)
                return false;
            for (int i = 0; i < v1.values.Length; i++)
                if (v1.values[i] != v2.values[i])
                    return false;
            return true;
        }

        public static bool operator !=(Vector v1, Vector v2)
        {
            return !(v1 == v2);
        }

        public static Vector operator +(Vector v1, Vector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            Vector v = new Vector(v1.values);
            for (int i = 0; i < v1.values.Length; i++)
                v.values[i] += v2.values[i];
            return v;
        }

        public static Vector operator -(Vector v1, Vector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            Vector v = new Vector(v1.values);
            for (int i = 0; i < v1.values.Length; i++)
                v.values[i] -= v2.values[i];
            return v;
        }

        public static Vector operator *(Vector v1, double a)
        {
            Vector v = new Vector(v1.values);
            for (int i = 0; i < v1.values.Length; i++)
                v.values[i] *= a;
            return v;
        }

        public static Vector operator *(double a, Vector v1)
        {
            return v1 * a;
        }

        public static Vector operator *(Vector v1, Vector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            Vector v = new Vector(v1.values);
            for (int i = 0; i < v1.values.Length; i++)
                v.values[i] *= v2.values[i];
            return v;
        }

        public static double Dot(Vector v1, Vector v2)
        {
            if (v1.values.Length != v2.values.Length)
                throw new ArgumentException("Inequal lengthes");
            double result = 0;
            for (int i = 0; i < v1.values.Length; i++)
                result += (v1.values[i] * v2.values[i]);
            return result;
        }

        public static implicit operator Vector(double[] array)
        {
            Vector v = new Vector();
            v.values = array;
            return v;
        }

        public static explicit operator double[] (Vector v)
        {
            return v.values;
        }

        /// <summary>
        /// 生成长度为len的0向量
        /// </summary>
        /// <param name="len">向量长度</param>
        /// <param name="d">默认元素值</param>
        public static Vector Create(int len, double d = 0.0)
        {
            Vector v = new Vector();
            v.values = new double[len];
            if (d != 0)
                for (int i = 0; i < len; i++)
                    v.values[i] = d;
            return v;
        }

        /// <summary>
        /// 生成随机向量
        /// </summary>
        /// <param name="len">向量长度</param>
        /// <param name="lower">每一维的最小值</param>
        /// <param name="upper">每一维的最大值</param>
        public static Vector CreateRandom(int len, double lower = 0.0, double upper = 1.0)
        {
            Random rand = new Random((int)DateTime.Now.Ticks);
            Vector v = new Vector();
            v.values = new double[len];
            for (int i = 0; i < len; i++)
                v.values[i] = lower + rand.NextDouble() * (upper - lower);
            return v;
        }
    }

    public static class VectorExtension
    {
        public static double MaxByIndex(this Vector v, Func<int, double> selector)
        {
            double result = double.NegativeInfinity, d;
            for (int i = 0; i < v.Length; i++)
            {
                d = selector(i);
                if (d > result)
                    result = d;
            }
            return result;
        }

        public static double MinByIndex(this Vector v, Func<int, double> selector)
        {
            double result = double.PositiveInfinity, d;
            for (int i = 0; i < v.Length; i++)
            {
                d = selector(i);
                if (d < result)
                    result = d;
            }
            return result;
        }

        public static double SumByIndex(this Vector v, Func<int, double> selector)
        {
            double result = 0;
            for (int i = 0; i < v.Length; i++)
                result += selector(i);
            return result;
        }

        public static double ProdByIndex(this Vector v, Func<int, double> selector)
        {
            double result = 1;
            for (int i = 0; i < v.Length; i++)
                result *= selector(i);
            return result;
        }
    }
}
