private double Interpolate1D(PValue _pv)
        {
            if (_pv == null)
            {
                return(double.NaN);
            }

            PValue pv1, pv2;

            this.FindNearestNeighbors(_pv, out pv1, out pv2);

            // check the neighbors
            bool neighb_not_same = PValue.InGeneralPosition1D(pv1, pv2);

            if (neighb_not_same)
            {
                // interpolate
                double f1, f2;
                PValue.GetLinearCoords(pv1, pv2, _pv, out f1, out f2);
                if (this.CanInterpolate)
                {
                    return(f1 * this.table[pv1] + f2 * this.table[pv2]);
                }
                else
                {
                    return((f1 > f2) ? this.table[pv1] : this.table[pv2]);
                }
            }
            else
            {
                // the neighbors are the same -> we are out of bounds
                return(this.table[pv1]);
            }
        }
        public double this[PValue index]
        {
            get
            {
                if (!this.IsValid)
                {
                    return(double.NaN);
                }

                if (this.table.ContainsKey(index))
                {
                    return(this.table[index]);
                }
                else
                {
                    return(this.RetrieveValue(index));
                }
            }
            set
            {
                if (this.IsValid && this.table.ContainsKey(index))
                {
                    this.table[index] = value;
                }
            }
        }
        private void FindNearestNeighbors(PValue _pv, out PValue _pvSmaller, out PValue _pvBigger, out PValue _pv3)
        {
            int n       = this.table.Count;
            int counter = -1;

            foreach (var entry in this.table)
            {
                counter++;
                if (entry.Key >= _pv)
                {
                    break;
                }
            }

            _pvSmaller = this.table.ElementAt(counter).Key;
            if (counter == 0 || counter == n - 1)
            {
                // A. out of range
                _pvBigger = this.table.ElementAt(counter).Key;
                _pv3      = null;
                return;
            }
            else
            {
                _pvBigger = this.table.ElementAt(counter + 1).Key;
            }

            // B. find a third point for an interpolation triangle

            // order the table values according to distance from the query value _pv
            SortedList <PValue, double> sorted_acc_to_dist = new SortedList <PValue, double>(new PValueDistComparer(_pv));

            foreach (var entry in this.table)
            {
                if (entry.Key == _pvSmaller || entry.Key == _pvBigger)
                {
                    continue;
                }

                sorted_acc_to_dist.Add(entry.Key, entry.Value);
            }

            // find closest value that builds a triangle around the query value _pv
            foreach (var entry in sorted_acc_to_dist)
            {
                if (PValue.IsInsideTriangle(_pvSmaller, _pvBigger, entry.Key, _pv, true))
                {
                    _pv3 = entry.Key;
                    return;
                }
            }

            // C. no well-defined triangle could be found
            // if none could be found, leave the third point empty
            _pv3 = null;
        }
        private double RetrieveValue(PValue _pv)
        {
            if (_pv == null)
            {
                return(double.NaN);
            }

            if (_pv.Dim == ParameterValueDim.DIM_1)
            {
                return(this.Interpolate1D(_pv));
            }
            else if (_pv.Dim == ParameterValueDim.DIM_2)
            {
                return(this.Interpolate2D(_pv));
            }
            else
            {
                return(this.table.ElementAt(0).Value);
            }
        }
        private double Interpolate2D(PValue _pv)
        {
            if (_pv == null)
            {
                return(double.NaN);
            }

            PValue pv1, pv2, pv3;

            this.FindNearestNeighbors(_pv, out pv1, out pv2, out pv3);

            // check the neighbors
            if (pv3 == null)
            {
                // 1D interpolation
                return(Interpolate1D(_pv));
            }
            else
            {
                // 2D interpolation
                if (this.CanInterpolate)
                {
                    double f1, f2, f3;
                    PValue.GetBarycentricCoords(pv1, pv2, pv3, _pv, out f1, out f2, out f3);
                    return(f1 * this.table[pv1] + f2 * this.table[pv2] + f3 * this.table[pv3]);
                }
                else
                {
                    SortedList <PValue, double> sorted_neighb = new SortedList <PValue, double>(new PValueDistComparer(_pv));
                    sorted_neighb.Add(pv1, this.table[pv1]);
                    sorted_neighb.Add(pv2, this.table[pv2]);
                    sorted_neighb.Add(pv3, this.table[pv3]);

                    return(sorted_neighb.ElementAt(0).Value);
                }
            }
        }
        private void FindNearestNeighbors(PValue _pv, out PValue _pvSmaller, out PValue _pvBigger)
        {
            int n       = this.table.Count;
            int counter = -1;

            foreach (var entry in this.table)
            {
                counter++;
                if (entry.Key >= _pv)
                {
                    break;
                }
            }

            _pvSmaller = this.table.ElementAt(counter).Key;
            if (counter == 0 || counter == n - 1)
            {
                _pvBigger = this.table.ElementAt(counter).Key;
            }
            else
            {
                _pvBigger = this.table.ElementAt(counter + 1).Key;
            }
        }