using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;

namespace AutoDiff
{
    /// <summary>
    /// Represents a parametric term after it has been compiled for efficient evaluation/differentiation. A parametric
    /// term has some variables that function as "constant parameters" and others that function as actual variables.
    /// </summary>
    [ContractClass(typeof(ParametricCompiledTermContract))]
    public interface IParametricCompiledTerm
    {
        /// <summary>
        /// Evaluates the compiled term at the given point.
        /// </summary>
        /// <param name="arg">The point at which to evaluate..</param>
        /// <param name="parameters">The parameter values</param>
        /// <returns>The value of the function represented by the term at the given point.</returns>
        /// <remarks>The number at <c>arg[i]</c> is the value assigned to the variable <c>Variables[i]</c>.</remarks>
        double Evaluate(double[] arg, double[] parameters);

        /// <summary>
        /// Computes the gradient of the compiled term at the given point.
        /// </summary>
        /// <param name="arg">The point at which to differentiate.</param>
        /// <param name="parameters">The parameter values</param>
        /// <returns>A tuple, where the first item is the gradient at <paramref name="arg"/> and the second item is 
        /// the value at <paramref name="arg"/>. That is, the second value is the same as running <see cref="Evaluate"/> on 
        /// <paramref name="arg"/> and <paramref name="parameters"/>.</returns>
        /// <remarks>The number at <c>arg[i]</c> is the value assigned to the variable <c>Variables[i]</c>.</remarks>
        Tuple<double[], double> Differentiate(double[] arg, double[] parameters);

        /// <summary>
        /// The collection of variables contained in this compiled term.
        /// </summary>
        /// <remarks>
        /// The order of variables in this collection specifies the meaning of each argument in <see cref="Differentiate"/> or
        /// <see cref="Evaluate"/>. That is, the variable at <c>Variables[i]</c> corresponds to the i-th element in the <c>arg</c> parameter of <see cref="Differentiate"/>
        /// and <see cref="Evaluate"/>.
        /// </remarks>
        ReadOnlyCollection<Variable> Variables { get; }

        /// <summary>
        /// The collection of parameter variables contained in this compiled term.
        /// </summary>
        /// <remarks>
        /// The order of variables in this collection specifies the meaning of each argument in <see cref="Differentiate"/> or
        /// <see cref="Evaluate"/>. That is, the variable at <c>Variables[i]</c> corresponds to the i-th element in the <c>parameters</c> parameter of <see cref="Differentiate"/>
        /// and <see cref="Evaluate"/>.
        /// </remarks>
        ReadOnlyCollection<Variable> Parameters { get; }
    }

    [ContractClassFor(typeof(IParametricCompiledTerm))]
    abstract class ParametricCompiledTermContract : IParametricCompiledTerm
    {

        public double Evaluate(double[] arg, double[] parameters)
        {
            Contract.Requires(arg != null);
            Contract.Requires(arg.Length == Variables.Count);
            Contract.Requires(parameters != null);
            Contract.Requires(parameters.Length == Parameters.Count);

            return default(double);
        }

        public Tuple<double[], double> Differentiate(double[] arg, double[] parameters)
        {
            Contract.Requires(arg != null);
            Contract.Requires(arg.Length == Variables.Count);
            Contract.Requires(parameters != null);
            Contract.Requires(parameters.Length == Parameters.Count);

            Contract.Ensures(Contract.Result<Tuple<double[], double>>() != null);
            Contract.Ensures(Contract.Result<Tuple<double[], double>>().Item1.Length == arg.Length);

            return null;
        }

        public ReadOnlyCollection<Variable> Variables
        {
            get
            {
                Contract.Ensures(Contract.Result<ReadOnlyCollection<Variable>>() != null);
                return null;
            }
        }

        public ReadOnlyCollection<Variable> Parameters
        {
            get
            {
                Contract.Ensures(Contract.Result<ReadOnlyCollection<Variable>>() != null);
                return null;
            }
        }
    }
}