/// <summary>
        /// This gets the bounding box of all the input values, then creates random vectors within that box
        /// </summary>
        private static VectorND[] GetRandomNodeWeights(int count, ISOMInput[] inputs)
        {
            var aabb = MathND.GetAABB(inputs.Select(o => o.Weights));

            aabb = MathND.ResizeAABB(aabb, 1.1);     // allow the return vectors to be slightly outside the input box

            return(Enumerable.Range(0, count).
                   Select(o => MathND.GetRandomVector(aabb.Item1, aabb.Item2)).
                   ToArray());
        }
        /// <summary>
        /// This does a bounding box of inputs, and also makes sure all positions are closer to the desired node than
        /// other nodes
        /// </summary>
        private static VectorND[] GetRandomWeights_InsideCell(int count, ISOMInput[] inputs, SOMNode[] nodes, int nodeIndex)
        {
            var inputAABB = MathND.GetAABB(inputs.Select(o => o.Weights));

            // This actually could happen.  Detecting an infinite loop instead
            //if (!MathND.IsInside(inputAABB, nodes[nodeIndex].Weights))
            //{
            //    throw new ArgumentException("The node sits outside the input's aabb");
            //}

            List <VectorND> retVal = new List <VectorND>();

            for (int cntr = 0; cntr < count; cntr++)
            {
                int infiniteLoopDetector = 0;

                while (true)
                {
                    VectorND attempt = MathND.GetRandomVector(inputAABB.Item1, inputAABB.Item2);

                    var closest = nodes.
                                  Select((o, i) => new { Index = i, DistSquared = (attempt - o.Weights).LengthSquared }).
                                  OrderBy(o => o.DistSquared).
                                  First();

                    if (closest.Index == nodeIndex)
                    {
                        retVal.Add(attempt);
                        break;
                    }

                    infiniteLoopDetector++;
                    if (infiniteLoopDetector > 1000)
                    {
                        // Instead of giving up, increase the range that the weights can exist in.  When testing this other method, there is almost never an improved
                        // node.  It's just an infinite loop in the caller (keeps trying for an improvement, but it never happens)
                        return(GetRandomWeights_InsideCell_LARGERBUTFAIL(count, inputs, nodes, nodeIndex));
                    }
                }
            }

            return(retVal.ToArray());
        }
        /// <summary>
        /// Only call this from the other overload.  This uses a much larger bounding box (it still makes sure all returned points
        /// are closer to the desired node than other nodes)
        /// </summary>
        private static VectorND[] GetRandomWeights_InsideCell_LARGERBUTFAIL(int count, ISOMInput[] inputs, SOMNode[] nodes, int nodeIndex)
        {
            //TODO: May want to get a voronoi, then choose random points within the convex hull (more elegant, but may not be any faster)

            // This works, but the caller generally never finds a solution that's better then what it already has.  So if you need
            // to use this overload, you're just going to spin the processor

            #region calculate rectangle to choose points from

            // Get some nearby nodes
            double?largestDistance = Enumerable.Range(0, nodes.Length).
                                     Where(o => o != nodeIndex).
                                     Select(o => (nodes[o].Weights - nodes[nodeIndex].Weights).LengthSquared).
                                     OrderByDescending(o => o).
                                     FirstOrDefault();

            var inputAABB = MathND.GetAABB(inputs.Select(o => o.Weights));

            if (largestDistance == null)
            {
                largestDistance = Math.Max(
                    (inputAABB.Item1 - nodes[nodeIndex].Weights).LengthSquared,
                    (inputAABB.Item2 - nodes[nodeIndex].Weights).LengthSquared);
            }

            largestDistance = Math.Sqrt(largestDistance.Value);

            var bounds = MathND.GetAABB(new[]
            {
                nodes[nodeIndex].Weights.Select(o => o - largestDistance.Value).ToArray().ToVectorND(),
                nodes[nodeIndex].Weights.Select(o => o + largestDistance.Value).ToArray().ToVectorND(),
                inputAABB.Item1,
                inputAABB.Item2,
            });

            #endregion



            List <VectorND> retVal = new List <VectorND>();
            //int largestIteration = 0;

            for (int cntr = 0; cntr < count; cntr++)
            {
                int infiniteLoopDetector = 0;

                while (true)
                {
                    //if(infiniteLoopDetector > largestIteration)
                    //{
                    //    largestIteration = infiniteLoopDetector;      // I tested several times, and averaged about 5-15 iterations
                    //}

                    VectorND attempt = MathND.GetRandomVector(bounds.Item1, bounds.Item2);

                    var closest = nodes.
                                  Select((o, i) => new { Index = i, DistSquared = (attempt - o.Weights).LengthSquared }).
                                  OrderBy(o => o.DistSquared).
                                  First();

                    if (closest.Index == nodeIndex)
                    {
                        retVal.Add(attempt);
                        break;
                    }

                    infiniteLoopDetector++;
                    if (infiniteLoopDetector > 1000)
                    {
                        throw new ApplicationException("Infinite loop detected");
                    }
                }
            }

            return(retVal.ToArray());
        }