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

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

        private IntVector() { }

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

        public IntVector(int[] values)
        {
            this.values = values.ToArray();
        }

        public IntVector(IEnumerable<int> values)
        {
            this.values = values.ToArray();
        }

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

        public string ToArrayString()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < values.Length; i++)
            {
                sb.Append(values[i]);
                sb.Append(',');
            }
            return sb.ToString();
        }

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

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

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

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

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

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

        public int 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 int Sum()
        {
            int result = 0;
            for (int i = 0; i < values.Length; i++)
                result += values[i];
            return result;
        }

        public int Prod()
        {
            int 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 IntVector SubVector(int len, int index = 0)
        {
            int[] array = new int[len];
            for (int i = 0; i < len; i++)
                array[i] = values[index + i];
            return new IntVector() { values = array };
        }
        
        public static bool operator ==(IntVector v1, IntVector 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 !=(IntVector v1, IntVector v2)
        {
            return !(v1 == v2);
        }

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

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

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

        public static IntVector operator *(int a, IntVector v1)
        {
            return v1 * a;
        }

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

            return v1 * a;
        }

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

        public static double Dot(IntVector v1, IntVector 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 IntVector(int[] array)
        {
            return new IntVector() { values = array };
        }

        public static explicit operator IntVector(Vector vec)
        {
            int[] array = new int[vec.Length];
            for (int i = 0; i < vec.Length; i++)
                array[i] = (int)vec[i];
            return new IntVector() { values = array };
        }

        public static explicit operator int[] (IntVector v)
        {
            return v.values;
        }

        public static explicit operator Vector(IntVector vec)
        {
            double[] array = new double[vec.Length];
            for (int i = 0; i < vec.Length; i++)
                array[i] = vec[i];
            return new Vector(array);
        }

        /// <summary>
        /// Create a IntVector whose elements are the same default value
        /// </summary>
        public static IntVector Create(int len, int value = 0)
        {
            IntVector v = new IntVector();
            v.values = new int[len];
            if (value != 0)
                for (int i = 0; i < len; i++)
                    v.values[i] = value;
            return v;
        }

        /// <summary>
        /// Create a IntVector whose elements range from a beginning value to an end value
        /// </summary>
        /// <param name="from">beginning value</param>
        /// <param name="to">end value (the last element equals to this value minus one)</param>
        /// <returns></returns>
        public static IntVector CreateSeq(int from, int to)
        {
            IntVector v = new IntVector();
            v.values = new int[to - from];
            for (int i = 0; i < v.values.Length; i++)
                v.values[i] = from + i;
            return v;
        }

        /// <summary>
        /// Create a IntVector whose elements are random values between lower and upper
        /// </summary>
        public static IntVector CreateRandom(Random rand, int len, int lower = 0, int upper = 100)
        {
            IntVector v = new IntVector();
            v.values = new int[len];
            for (int i = 0; i < len; i++)
                v.values[i] = rand.Next(lower, upper);
            return v;
        }
    }
}
