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

namespace EvoBoat.EvoAlg
{
    /// <summary>
    /// Basic numeric optimization problem
    /// </summary>
    public class NumOptProblem<S> : IProblem<S, Vector>
        where S : VecSolution<Vector, double>, new()
    {
        public string Name { get; set; }
        public Func<Vector, double> Func { get; set; }

        protected double? optimum;
        public double? Optimum
        {
            get { return optimum; }
        }

        protected double? recAccu;
        public double? RecAccu
        {
            get { return recAccu; }
        }

        protected int dimension;
        public int Dimension
        {
            get { return dimension; }
        }

        protected Vector lowers, uppers, ranges;
        public Vector Lowers
        {
            get { return lowers; }
        }
        public Vector Uppers
        {
            get { return uppers; }
        }
        public Vector Ranges
        {
            get { return ranges; }
        }

        /// <summary>
        /// 指定目标函数和维数创建优化问题（每维范围默认为0~1）
        /// </summary>
        public NumOptProblem(Func<Vector, double> func, int dimension, string name = null, double? optimum = null, double? requiredAccuracy = null)
        {
            this.Func = func;
            this.dimension = dimension;
            this.lowers = new double[dimension];
            this.uppers = new double[dimension];
            this.ranges = new double[dimension];
            for (int d = 0; d < dimension; d++)
                this.ranges[d] = this.uppers[d] = 1.0;
            this.Name = (name != null) ? name : func.ToString();
            this.optimum = optimum;
            this.recAccu = requiredAccuracy;
        }

        /// <summary>
        /// 指定目标函数和上下限向量创建优化问题
        /// </summary>
        /// <param name="func"></param>
        /// <param name="lowers">下限向量</param>
        /// <param name="uppers">上限向量</param>
        public NumOptProblem(Func<Vector, double> func, Vector lowers, Vector uppers, string name = null, double? optimum = null, double? requiredAccuracy = null)
        {
            if (lowers.Length != uppers.Length)
                throw new ArgumentException("Inequal lengthes");
            this.Func = func;
            this.dimension = lowers.Length;
            this.lowers = lowers;
            this.uppers = uppers;
            this.ranges = uppers - lowers;
            this.Name = (name != null) ? name : func.ToString();
            this.optimum = optimum;
            this.recAccu = requiredAccuracy;
        }

        public override string ToString()
        {
            return Name;
        }

        public double Evaluate(Vector x)
        {
            return Func(x);
        }
        public double Evaluate(S sol)
        {
            sol.Fit = Func(sol.Content);
            return sol.Fit;
        }

        public bool IsFeasible(Vector x)
        {
            for (int d = 0; d < dimension; d++)
                if (x[d] < lowers[d] || x[d] > uppers[d])
                    return false;
            return true;
        }
    }
}
