private bool TryGetContaining(Conditions conditions, out Conditions containingConditions, out EnvelopePoint[,] points)
        {
            containingConditions = new Conditions();
            points = new EnvelopePoint[0, 0];
            Dictionary <Conditions, EnvelopePoint[, ]> .Enumerator enumerator = cache.GetEnumerator();
            int  bestNumPoints = 0;
            bool result        = false;

            while (enumerator.MoveNext())
            {
                Conditions enumConditions = cachedConditions[enumerator.Current.Key];
                if (enumConditions.Contains(conditions))
                {
                    result = true;
                    int numPoints = Mathf.FloorToInt((conditions.upperBoundSpeed - conditions.lowerBoundSpeed) / enumConditions.stepSpeed + 1) *
                                    Mathf.FloorToInt((conditions.upperBoundAltitude - conditions.lowerBoundAltitude) / enumConditions.stepAltitude + 1);
                    if (numPoints > bestNumPoints)
                    {
                        bestNumPoints        = numPoints;
                        containingConditions = enumConditions;
                        points = cache[enumConditions];
                    }
                }
            }
            return(result);
        }
        public void Calculate(CelestialBody body, float lowerBoundSpeed = 0, float upperBoundSpeed = 2000, float lowerBoundAltitude = 0, float upperBoundAltitude = 60000, float stepSpeed = 50f, float stepAltitude = 500)
        {
            // Set up calculation conditions and bounds
            Conditions newConditions;

            newConditions = new Conditions(body, lowerBoundSpeed, upperBoundSpeed, stepSpeed, lowerBoundAltitude, upperBoundAltitude, stepAltitude);

            if (currentConditions.Equals(newConditions) && Status != TaskStatus.WaitingToRun)
            {
                return;
            }

            Cancel();

            // Generate 'coarse' cache
            float firstStepSpeed    = (newConditions.upperBoundSpeed - newConditions.lowerBoundSpeed) / resolution[0, 0];
            float firstStepAltitude = (newConditions.upperBoundAltitude - newConditions.lowerBoundAltitude) / resolution[0, 1];

            EnvelopePoint[] preliminaryData = new EnvelopePoint[(resolution[0, 0] + 1) * (resolution[0, 1] + 1)];
            // Probably won't run in parallel because it's very short.
            // But the UI will hang waiting for this to complete, so a self-triggering CancellationToken is provided with a life span of 5 seconds.
            try
            {
                Parallel.For(0, preliminaryData.Length, new ParallelOptions()
                {
                    CancellationToken = new CancellationTokenSource(5000).Token
                },
                             WindTunnelWindow.Instance.GetAeroPredictor, (index, state, predictor) =>
                {
                    int x = index % (resolution[0, 0] + 1), y = index / (resolution[0, 0] + 1);
                    EnvelopePoint result   = new EnvelopePoint(predictor, newConditions.body, y * firstStepAltitude + newConditions.lowerBoundAltitude, x * firstStepSpeed + newConditions.lowerBoundSpeed);
                    preliminaryData[index] = result;
                    cache[new SurfCoords(result.speed, result.altitude)] = result;
                    return(predictor);
                }, (predictor) => (predictor as VesselCache.IReleasable)?.Release());
            }
            catch (OperationCanceledException)
            {
                Debug.LogError("Wind Tunnel: Initial pass timed out.");
            }
            catch (AggregateException ex)
            {
                Debug.LogError("Wind Tunnel: Initial pass threw an inner exception.");
                Debug.LogException(ex.InnerException);
            }

            cancellationTokenSource = new CancellationTokenSource();

            WindTunnel.Instance.StartCoroutine(Processing(newConditions, preliminaryData.To2Dimension(resolution[0, 0] + 1)));
        }
        private static void GenerateSurfPoint(object obj)
        {
            GenData data = (GenData)obj;

            if (data.storeState.manager.Cancelled)
            {
                return;
            }
            //Debug.Log("Starting point: " + data.altitude + "/" + data.speed);
            EnvelopePoint result = new EnvelopePoint(data.vessel, data.conditions.body, data.altitude, data.speed, data.AoA_guess, data.maxA_guess, data.pitchI_guess);

            //Debug.Log("Point solved: " + data.altitude + "/" + data.speed);

            data.storeState.StoreResult(result);
        }
        private IEnumerator Processing(CalculationManager manager, Conditions conditions, AeroPredictor vessel)
        {
            int numPtsX = (int)Math.Ceiling((conditions.upperBoundSpeed - conditions.lowerBoundSpeed) / conditions.stepSpeed);
            int numPtsY = (int)Math.Ceiling((conditions.upperBoundAltitude - conditions.lowerBoundAltitude) / conditions.stepAltitude);

            EnvelopePoint[,] newEnvelopePoints = new EnvelopePoint[numPtsX + 1, numPtsY + 1];

            GenData rootData = new GenData(vessel, conditions, 0, 0, manager);

            //System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            //timer.Start();
            ThreadPool.QueueUserWorkItem(SetupInBackground, rootData, true);

            while (!manager.Completed)
            {
                //Debug.Log(manager.PercentComplete + "% done calculating...");
                if (manager.Status == CalculationManager.RunStatus.Cancelled)
                {
                    yield break;
                }
                yield return(0);
            }
            //timer.Stop();
            //Debug.Log("Time taken: " + timer.ElapsedMilliseconds / 1000f);

            newEnvelopePoints = ((CalculationManager.State[, ])rootData.storeState.Result)
                                .SelectToArray(pt => (EnvelopePoint)pt.Result);

            AddToCache(conditions, newEnvelopePoints);
            if (!manager.Cancelled)
            {
                envelopePoints    = newEnvelopePoints;
                currentConditions = conditions;
                UpdateGraphs();
                CalculateOptimalLines(vessel, conditions, WindTunnelWindow.Instance.TargetSpeed, WindTunnelWindow.Instance.TargetAltitude, 0, 0);

                valuesSet = true;
            }
            yield return(0);

            if (!manager.Cancelled)
            {
                Conditions nextConditions = conditions.Modify(stepSpeed: conditions.stepSpeed / 2, stepAltitude: conditions.stepAltitude / 2);
                WindTunnel.Instance.StartCoroutine(RefinementProcessing(calculationManager, nextConditions, vessel, newEnvelopePoints, conditions));
            }
        }
        private IEnumerator RefinementProcessing(CalculationManager manager, Conditions conditions, AeroPredictor vessel, EnvelopePoint[,] basisData, Conditions basisConditions = new Conditions(), Queue <Conditions> followOnConditions = null, bool forcePushToGraph = false)
        {
            int numPtsX = (int)Math.Ceiling((conditions.upperBoundSpeed - conditions.lowerBoundSpeed) / conditions.stepSpeed);
            int numPtsY = (int)Math.Ceiling((conditions.upperBoundAltitude - conditions.lowerBoundAltitude) / conditions.stepAltitude);

            EnvelopePoint[,] newEnvelopePoints = new EnvelopePoint[numPtsX + 1, numPtsY + 1];

            CalculationManager backgroundManager = new CalculationManager();

            manager.OnCancelCallback           += backgroundManager.Cancel;
            CalculationManager.State[,] results = new CalculationManager.State[numPtsX + 1, numPtsY + 1];
            GenData rootData = new GenData(vessel, conditions, 0, 0, backgroundManager);

            ThreadPool.QueueUserWorkItem(ContinueInBackground, new object[] { rootData, results, basisData, basisConditions });
            while (!backgroundManager.Completed)
            {
                if (manager.Status == CalculationManager.RunStatus.Cancelled)
                {
                    backgroundManager.Cancel();
                    yield break;
                }
                yield return(0);
            }
            manager.OnCancelCallback -= backgroundManager.Cancel;

            newEnvelopePoints = ((CalculationManager.State[, ])rootData.storeState.Result)
                                .SelectToArray(pt => (EnvelopePoint)pt.Result);

            AddToCache(conditions, newEnvelopePoints);
            if (currentConditions.Equals(conditions) || (forcePushToGraph && !backgroundManager.Cancelled))
            {
                envelopePoints    = newEnvelopePoints;
                currentConditions = conditions;
                UpdateGraphs();
                valuesSet = true;
            }
            backgroundManager.Dispose();
            if (!manager.Cancelled && followOnConditions != null && followOnConditions.Count > 0)
            {
                yield return(0);

                Conditions nextConditions = followOnConditions.Dequeue();
                WindTunnel.Instance.StartCoroutine(RefinementProcessing(manager, nextConditions, vessel, newEnvelopePoints, conditions, followOnConditions, forcePushToGraph));
            }
        }
        private IEnumerator Processing(CalculationManager manager, Conditions conditions, AeroPredictor vessel)
        {
            int numPtsX = (int)Math.Ceiling((conditions.upperBoundSpeed - conditions.lowerBoundSpeed) / conditions.stepSpeed);
            int numPtsY = (int)Math.Ceiling((conditions.upperBoundAltitude - conditions.lowerBoundAltitude) / conditions.stepAltitude);

            EnvelopePoint[,] newEnvelopePoints = new EnvelopePoint[numPtsX + 1, numPtsY + 1];

            GenData rootData = new GenData(vessel, conditions, 0, 0, manager);

            ThreadPool.QueueUserWorkItem(SetupInBackground, rootData);

            while (!manager.Completed)
            {
                //Debug.Log(manager.PercentComplete + "% done calculating...");
                if (manager.Status == CalculationManager.RunStatus.Cancelled)
                {
                    yield break;
                }
                yield return(0);
            }

            newEnvelopePoints = ((CalculationManager.State[, ])rootData.storeState.Result)
                                .SelectToArray(pt => (EnvelopePoint)pt.Result);

            if (!manager.Cancelled)
            {
                //cache.Add(conditions, newEnvelopePoints);
                AddToCache(conditions, newEnvelopePoints);
                envelopePoints    = newEnvelopePoints;
                currentConditions = conditions;
                GenerateGraphs();
                valuesSet = true;
            }

            float stepSpeed = conditions.stepSpeed, stepAltitude = conditions.stepAltitude;

            for (int i = 2; i <= 2; i++)
            {
                yield return(0);

                CalculationManager backgroundManager = new CalculationManager();
                manager.OnCancelCallback += backgroundManager.Cancel;
                conditions = new Conditions(conditions.body, conditions.lowerBoundSpeed, conditions.upperBoundSpeed,
                                            stepSpeed / i, conditions.lowerBoundAltitude, conditions.upperBoundAltitude, stepAltitude / i);
                CalculationManager.State[,] prevResults = ((CalculationManager.State[, ])rootData.storeState.Result).SelectToArray(p => p);
                rootData = new GenData(vessel, conditions, 0, 0, backgroundManager);
                ThreadPool.QueueUserWorkItem(ContinueInBackground, new object[] { rootData, prevResults });
                while (!backgroundManager.Completed)
                {
                    if (manager.Status == CalculationManager.RunStatus.Cancelled)
                    {
                        backgroundManager.Cancel();
                        yield break;
                    }
                    yield return(0);
                }

                newEnvelopePoints = ((CalculationManager.State[, ])rootData.storeState.Result)
                                    .SelectToArray(pt => (EnvelopePoint)pt.Result);

                if (!manager.Cancelled)
                {
                    //cache.Add(conditions, newEnvelopePoints);
                    AddToCache(conditions, newEnvelopePoints);
                    envelopePoints    = newEnvelopePoints;
                    currentConditions = conditions;
                    UpdateGraphs();
                    valuesSet = true;
                }
            }
        }
 public SurfCoords(EnvelopePoint point) : this(point.speed, point.altitude)
 {
 }
        private IEnumerator Processing(Conditions conditions, EnvelopePoint[,] prelimData)
        {
            CancellationTokenSource closureCancellationTokenSource = this.cancellationTokenSource;

            primaryProgress = new EnvelopePoint[conditions.Resolution];
            int cachedCount = 0;

            stopwatch.Reset();
            stopwatch.Start();

            task = Task.Factory.StartNew <EnvelopePoint[, ]>(
                () =>
            {
                float[,] AoAs_guess = null, maxAs_guess = null, pitchIs_guess = null;
                AoAs_guess          = prelimData.SelectToArray(pt => pt.AoA_level);
                maxAs_guess         = prelimData.SelectToArray(pt => pt.AoA_max);
                pitchIs_guess       = prelimData.SelectToArray(pt => pt.pitchInput);

                try
                {
                    //OrderablePartitioner<EnvelopePoint> partitioner = Partitioner.Create(primaryProgress, true);
                    Parallel.For <AeroPredictor>(0, primaryProgress.Length, new ParallelOptions()
                    {
                        CancellationToken = closureCancellationTokenSource.Token
                    },
                                                 WindTunnelWindow.Instance.GetAeroPredictor,
                                                 (index, state, predictor) =>
                    {
                        int x             = index % conditions.XResolution, y = index / conditions.XResolution;
                        SurfCoords coords = new SurfCoords(x * conditions.stepSpeed + conditions.lowerBoundSpeed,
                                                           y * conditions.stepAltitude + conditions.lowerBoundAltitude);

                        EnvelopePoint result;
                        if (!cache.TryGetValue(coords, out result))
                        {
                            result        = new EnvelopePoint(predictor, conditions.body, y * conditions.stepAltitude + conditions.lowerBoundAltitude, x * conditions.stepSpeed + conditions.lowerBoundSpeed);
                            cache[coords] = result;
                        }
                        else
                        {
                            Interlocked.Increment(ref cachedCount);
                        }
                        primaryProgress[index] = result;
                        return(predictor);
                    }, (predictor) => (predictor as VesselCache.IReleasable)?.Release());

                    closureCancellationTokenSource.Token.ThrowIfCancellationRequested();
                    Debug.Log("KWT Data run finished. " + cachedCount + " of " + primaryProgress.Length + " retreived from cache. (" + (float)cachedCount / primaryProgress.Length * 100 + "%)");

                    return(primaryProgress.To2Dimension(conditions.XResolution));
                }
                catch (AggregateException aggregateException)
                {
                    foreach (var ex in aggregateException.Flatten().InnerExceptions)
                    {
                        Debug.LogException(ex);
                    }
                    throw aggregateException;
                }
            },
                closureCancellationTokenSource.Token);

            //if (task.Wait(25))
            //Debug.Log("KWT: Waiting actually did something!");

            while (task.Status < TaskStatus.RanToCompletion)
            {
                //Debug.Log(manager.PercentComplete + "% done calculating...");
                yield return(0);
            }
            //timer.Stop();
            //Debug.Log("Time taken: " + timer.ElapsedMilliseconds / 1000f);

            if (task.Status > TaskStatus.RanToCompletion)
            {
                if (task.Status == TaskStatus.Faulted)
                {
                    Debug.LogError("Wind tunnel task faulted (Envelope)");
                    Debug.LogException(task.Exception);
                }
                else if (task.Status == TaskStatus.Canceled)
                {
                    Debug.Log("Wind tunnel task was canceled. (Envelope)");
                }
                yield break;
            }

            if (!closureCancellationTokenSource.IsCancellationRequested)
            {
                envelopePoints    = ((Task <EnvelopePoint[, ]>)task).Result;
                currentConditions = conditions;
                UpdateGraphs();
                EnvelopeLine.CalculateOptimalLines(conditions, WindTunnelWindow.Instance.TargetSpeed, WindTunnelWindow.Instance.TargetAltitude, 0, 0, envelopePoints, closureCancellationTokenSource, graphables);
                valuesSet = true;
            }

            if (cachedCount < primaryProgress.Length)
            {
                yield return(0);
            }

            if (!closureCancellationTokenSource.IsCancellationRequested)
            {
                Conditions newConditions;
                for (int i = 1; i <= resolution.GetUpperBound(0); i++)
                {
                    if (resolution[i, 0] + 1 > conditions.XResolution || resolution[i, 1] + 1 > conditions.YResolution)
                    {
                        newConditions = conditions.Modify(
                            stepSpeed: Math.Min((conditions.upperBoundSpeed - conditions.lowerBoundSpeed) / resolution[i, 0], conditions.stepSpeed),
                            stepAltitude: Math.Min((conditions.upperBoundAltitude - conditions.lowerBoundAltitude) / resolution[i, 1], conditions.stepAltitude));
                        Debug.Log("Wind Tunnel graphing higher res at:" + newConditions.XResolution + " by " + newConditions.YResolution);
                        WindTunnel.Instance.StartCoroutine(Processing(newConditions, envelopePoints));
                        yield break;
                    }
                }

                Debug.Log("Wind Tunnel Graph reached maximum resolution");
            }
        }