/// <summary>
        /// Sets the Rneuron
        /// </summary>
        /// <param name="rneuron"></param>
        public void SetRneuronWithInputs(Rneuron rneuron)
        {
            var rneuronId = rneuron.ID;

            // add rneuron to cache
            Rneurons.TryAdd(rneuronId, rneuron);

            bool          isFirstInput   = true;
            RlmInputValue lastInputValue = null;
            int           cnt            = 0;

            IComparer <RlmInputKey> distinctComparer = new RlmInputKeyDistinctComparer();
            IComparer <RlmInputKey> linearComparer   = new RlmInputKeyLinearComparer();

            // build dynamic inputs
            foreach (var i in rneuron.Input_Values_Reneurons)
            {
                RlmInputKey inputKey = new RlmInputKey()
                {
                    Value = i.Value, InputNum = cnt, Type = i.InputType
                };
                inputKey.DoubleValue = (i.InputType == Enums.RlmInputType.Linear) ? Convert.ToDouble(i.Value) : 0D;
                RlmInputValue inputVal = null;

                if (!isFirstInput)
                {
                    if (lastInputValue.RelatedInputs == null)
                    {
                        lastInputValue.RelatedInputs = new SortedList <RlmInputKey, RlmInputValue>(i.InputType == Enums.RlmInputType.Linear ? linearComparer : distinctComparer);
                    }

                    if (!lastInputValue.RelatedInputs.TryGetValue(inputKey, out inputVal))
                    {
                        inputVal = new RlmInputValue();
                        lastInputValue.RelatedInputs.Add(inputKey, inputVal);
                    }

                    lastInputValue = inputVal;
                }
                else
                {
                    if (DynamicInputs == null)
                    {
                        DynamicInputs = new SortedList <RlmInputKey, RlmInputValue>(i.InputType == Enums.RlmInputType.Linear ? linearComparer : distinctComparer);
                    }

                    isFirstInput = false;
                    if (!DynamicInputs.TryGetValue(inputKey, out inputVal))
                    {
                        inputVal = new RlmInputValue();
                        DynamicInputs.Add(inputKey, inputVal);
                    }

                    lastInputValue = inputVal;
                }
                cnt++;
            }

            lastInputValue.RneuronId = rneuronId;
        }
        /// <summary>
        /// Gets best Solution
        /// </summary>
        /// <param name="inputs"></param>
        /// <param name="linearTolerance"></param>
        /// <param name="predict"></param>
        /// <returns></returns>
        public Solution GetBestSolution(IEnumerable <RlmIOWithValue> inputs, double linearTolerance = 0, bool predict = false)
        {
            Solution retVal = null;

            var         comparer      = new DynamicInputComparer();//Util.DynamicInputComparer;
            List <long> rneuronsFound = new List <long>();

            var rangeInfos = new Dictionary <int, InputRangeInfo>();
            int cnt        = 0;

            foreach (var item in inputs)
            {
                if (item.Type == Enums.RlmInputType.Linear)
                {
                    double val = Convert.ToDouble(item.Value);
                    double off = (item.Max - item.Min) * ((linearTolerance == 0) ? 0 : (linearTolerance / 100D));

                    rangeInfos.Add(cnt, new InputRangeInfo()
                    {
                        InputId = item.ID, InputType = item.Type, FromValue = val - off, ToValue = val + off
                    });
                }
                else
                {
                    rangeInfos.Add(cnt, new InputRangeInfo()
                    {
                        InputId = item.ID, InputType = item.Type, Value = item.Value
                    });
                }
                cnt++;
            }

            //swGetRneuron.Start();

            //RnnInputValue.RecurseInputForMatchingRneurons(DynamicInputs, rangeInfos, rneuronsFound);

            //swGetRneuron.Stop();
            //GetRneuronTimes.Add(swGetRneuron.Elapsed);
            //swGetRneuron.Reset();

            //TODO Cache Box, current implementation is slow don't know why
            if (!predict)
            {
                if (CacheBox.IsWithinRange(rangeInfos, linearTolerance))
                {
                    swGetRneuron.Start();

                    RlmInputValue.RecurseInputForMatchingRneurons(CacheBox.CachedInputs, rangeInfos, rneuronsFound);

                    swGetRneuron.Stop();
                    GetRneuronTimes.Add(swGetRneuron.Elapsed);
                    swGetRneuron.Reset();
                }
                else
                {
                    swRebuildCache.Start();

                    CacheBoxCount++;
                    CacheBox.Clear();

                    var cacheBoxRangeInfos = new Dictionary <int, InputRangeInfo>();
                    int cacheRangeCnt      = 0;
                    foreach (var item in inputs)
                    {
                        if (item.Type == Enums.RlmInputType.Linear)
                        {
                            double val     = Convert.ToDouble(item.Value);
                            double dataOff = (item.Max - item.Min) * ((linearTolerance == 0) ? 0 : (linearTolerance / 100D));
                            //double cacheMargin = (CacheBoxMargin == 0) ? 0 : ((item.Max - item.Min) * (CacheBoxMargin / 100));
                            double momentum = item.InputMomentum.MomentumDirection;
                            double toOff    = 0;
                            double fromOff  = 0;
                            double cacheOff = 0;

                            if (UseMomentumAvgValue)
                            {
                                cacheOff = item.InputMomentum.MomentumValue * MomentumAdjustment;
                            }
                            else
                            {
                                cacheOff = (item.Max - item.Min) * ((linearTolerance == 0) ? 0 : (MomentumAdjustment / 100D));
                            }


                            if (momentum > 0)
                            {
                                var offset = momentum * cacheOff;
                                toOff   = val + dataOff + (cacheOff + offset);
                                fromOff = val - dataOff - (cacheOff - offset);
                            }
                            else if (momentum < 0)
                            {
                                var offset = Math.Abs(momentum) * cacheOff;
                                toOff   = val + dataOff + (cacheOff - offset);
                                fromOff = val - dataOff - (cacheOff + offset);
                            }
                            else
                            {
                                toOff   = val + dataOff + cacheOff;
                                fromOff = val - dataOff - cacheOff;
                            }

                            double cacheMargin = (CacheBoxMargin == 0) ? 0 : (cacheOff) * (CacheBoxMargin / 100D);

                            toOff   += cacheMargin;
                            fromOff -= cacheMargin;

                            cacheBoxRangeInfos.Add(cacheRangeCnt, new InputRangeInfo()
                            {
                                InputId = item.ID, FromValue = Math.Ceiling(fromOff), ToValue = Math.Ceiling(toOff)
                            });
                        }
                        else
                        {
                            cacheBoxRangeInfos.Add(cacheRangeCnt, new InputRangeInfo()
                            {
                                InputId = item.ID, Value = item.Value
                            });
                        }
                        cacheRangeCnt++;
                    }

                    CacheBox.SetRanges(cacheBoxRangeInfos.Values);

                    CacheBox.CachedInputs = RlmInputValue.RecurseInputForMatchingRneuronsForCaching(DynamicInputs, cacheBoxRangeInfos, rangeInfos, rneuronsFound);
                    //RnnInputValue.RecurseInputForMatchingRneurons(CacheBox.CachedInputs, rangeInfos, rneuronsFound);

                    swRebuildCache.Stop();
                    RebuildCacheboxTimes.Add(swRebuildCache.Elapsed);
                    swRebuildCache.Reset();
                }
            }
            else
            {
                RlmInputValue.RecurseInputForMatchingRneurons(DynamicInputs, rangeInfos, rneuronsFound);
            }

            BestSolution currBS = null;

            foreach (var rneuronId in rneuronsFound)
            {
                Dictionary <long, BestSolution> bsDict;
                if (BestSolutions.TryGetValue(rneuronId, out bsDict))
                {
                    foreach (var bs in bsDict.Values)
                    {
                        if (!predict)
                        {
                            if (currBS != null)
                            {
                                if (bs.CycleScore > currBS.CycleScore)
                                {
                                    currBS = bs;
                                }
                                else if (bs.CycleScore == currBS.CycleScore && bs.SessionScore >= currBS.SessionScore && bs.CycleOrder >= currBS.CycleOrder)
                                {
                                    currBS = bs;
                                }
                            }
                            else
                            {
                                currBS = bs;
                            }
                        }
                        else
                        {
                            if (currBS != null)
                            {
                                if (bs.SessionScore > currBS.SessionScore)
                                {
                                    currBS = bs;
                                }
                                else if (bs.SessionScore == currBS.SessionScore && bs.CycleScore >= currBS.CycleScore && bs.CycleOrder >= currBS.CycleOrder)
                                {
                                    currBS = bs;
                                }
                            }
                            else
                            {
                                currBS = bs;
                            }
                        }
                    }
                }
            }

            if (currBS != null)
            {
                retVal = Solutions[currBS.SolutionId];
            }

            return(retVal);
        }
        /// <summary>
        /// Gets existing Rneuron and creates a new one if not existing
        /// </summary>
        /// <param name="inputs">Inputs with value</param>
        /// <param name="rnetworkID">Current NetworkId</param>
        /// <returns></returns>
        public GetRneuronResult GetRneuronFromInputs(IEnumerable <RlmIOWithValue> inputs, long rnetworkID)
        {
            GetRneuronResult retVal  = new GetRneuronResult();
            Rneuron          rneuron = null;

            // generate key based on input values
            long rneuronId = Util.GenerateHashKey(inputs.Select(a => a.Value).ToArray());

            // create new rneuron if not exists
            if (!Rneurons.TryGetValue(rneuronId, out rneuron))
            {
                rneuron = new Rneuron()
                {
                    ID = rneuronId, Rnetwork_ID = rnetworkID
                };

                bool          isFirstInput   = true;
                RlmInputValue lastInputValue = null;
                int           cnt            = 0;

                IComparer <RlmInputKey> distinctComparer = new RlmInputKeyDistinctComparer();
                IComparer <RlmInputKey> linearComparer   = new RlmInputKeyLinearComparer();

                foreach (var i in inputs)
                {
                    // create IVR instance
                    var ivr = new Input_Values_Rneuron()
                    {
                        ID         = Util.GenerateHashKey(rneuronId, i.ID),
                        Value      = i.Value,
                        Input_ID   = i.ID,
                        Rneuron_ID = rneuronId,
                        DotNetType = i.DotNetType,
                        InputType  = i.Type
                    };
                    rneuron.Input_Values_Reneurons.Add(ivr);

                    RlmInputKey inputKey = new RlmInputKey()
                    {
                        Value = ivr.Value, InputNum = cnt, Type = i.Type
                    };
                    inputKey.DoubleValue = (i.Type == Enums.RlmInputType.Linear) ? Convert.ToDouble(ivr.Value) : 0D;
                    RlmInputValue inputVal = null;

                    if (!isFirstInput)
                    {
                        if (lastInputValue.RelatedInputs == null)
                        {
                            lastInputValue.RelatedInputs = new SortedList <RlmInputKey, RlmInputValue>(i.Type == Enums.RlmInputType.Linear ? linearComparer : distinctComparer);
                        }

                        if (!lastInputValue.RelatedInputs.TryGetValue(inputKey, out inputVal))
                        {
                            inputVal = new RlmInputValue();
                            lastInputValue.RelatedInputs.Add(inputKey, inputVal);
                        }

                        lastInputValue = inputVal;
                    }
                    else
                    {
                        if (DynamicInputs == null)
                        {
                            DynamicInputs = new SortedList <RlmInputKey, RlmInputValue>(i.Type == Enums.RlmInputType.Linear ? linearComparer : distinctComparer);
                        }

                        isFirstInput = false;
                        if (!DynamicInputs.TryGetValue(inputKey, out inputVal))
                        {
                            inputVal = new RlmInputValue();
                            DynamicInputs.Add(inputKey, inputVal);
                        }

                        lastInputValue = inputVal;
                    }
                    cnt++;
                }

                lastInputValue.RneuronId = rneuronId;

                Rneurons.TryAdd(rneuronId, rneuron);
                //rneuron_queue.Add(retVal);
                //if (Rneurons.TryAdd(rneuronId, retVal))
                //{
                //}
                //Rneurons2.Enqueue(retVal);
                //rneuron_queue.Add(retVal);

                retVal.Rneuron       = rneuron;
                retVal.ExistsInCache = false;
            }
            else
            {
                retVal.Rneuron       = rneuron;
                retVal.ExistsInCache = true;
            }

            return(retVal);
        }