internal void Solve(Z3BaseParams parameters, IEnumerable <IGoal> modelGoals,
                            Action <int> addRow, Func <int, ArithExpr> mkGoalRow, Action <Z3Result> setResult)
        {
            _variables.Clear();
            _goals.Clear();

            try
            {
                // Mark that we are in solving phase
                _isSolving = true;

                // Construct Z3
                ConstructSolver(parameters);

                // Add all the variables
                foreach (int vid in _model.VariableIndices)
                {
                    AddVariable(vid);
                }

                // Add all the rows
                foreach (int rid in _model.RowIndices)
                {
                    addRow(rid);
                }

                // Add enabled goals to optimization problem
                foreach (IGoal g in modelGoals)
                {
                    if (!g.Enabled)
                    {
                        continue;
                    }

                    ArithExpr gr = mkGoalRow(g.Index);
                    if (g.Minimize)
                    {
                        _goals.Add(g, _optSolver.MkMinimize(gr));
                    }
                    else
                    {
                        _goals.Add(g, _optSolver.MkMaximize(gr));
                    }
                }

                if (_goals.Any() && parameters.SMT2LogFile != null)
                {
                    Debug.WriteLine("Dumping SMT2 benchmark to log file...");
                    File.WriteAllText(parameters.SMT2LogFile, _optSolver.ToString());
                }

                bool aborted = parameters.QueryAbort();

                if (!aborted)
                {
                    // Start the abort thread
                    AbortWorker abortWorker = new AbortWorker(_context, parameters.QueryAbort);
                    Thread      abortThread = new Thread(abortWorker.Start);
                    abortThread.Start();

                    // Now solve the problem
                    Status status = _optSolver.Check();

                    // Stop the abort thread
                    abortWorker.Stop();
                    abortThread.Join();

                    switch (status)
                    {
                    case Status.SATISFIABLE:
                        Microsoft.Z3.Model model = _optSolver.Model;
                        Debug.Assert(model != null, "Should be able to get Z3 model.");
                        // Remember the solution values
                        foreach (KeyValuePair <int, Expr> pair in _variables)
                        {
                            var value = Utils.ToRational(model.Eval(pair.Value, true));
                            _model.SetValue(pair.Key, value);
                        }
                        // Remember all objective values
                        foreach (var pair in _goals)
                        {
                            var optimalValue = Utils.ToRational(pair.Value.Upper);
                            _model.SetValue(pair.Key.Index, optimalValue);
                        }
                        model.Dispose();
                        setResult(_goals.Any() ? Z3Result.Optimal : Z3Result.Feasible);
                        break;

                    case Status.UNSATISFIABLE:
                        setResult(Z3Result.Infeasible);
                        break;

                    case Status.UNKNOWN:
                        if (abortWorker.Aborted)
                        {
                            Microsoft.Z3.Model subOptimalModel = _optSolver.Model;
                            if (subOptimalModel != null && subOptimalModel.NumConsts != 0)
                            {
                                // Remember the solution values
                                foreach (KeyValuePair <int, Expr> pair in _variables)
                                {
                                    var value = Utils.ToRational(subOptimalModel.Eval(pair.Value, true));
                                    _model.SetValue(pair.Key, value);
                                }
                                // Remember all objective values
                                foreach (var pair in _goals)
                                {
                                    var optimalValue = Utils.ToRational(pair.Value.Upper);
                                    _model.SetValue(pair.Key.Index, optimalValue);
                                }
                                subOptimalModel.Dispose();

                                setResult(Z3Result.LocalOptimal);
                            }
                            else
                            {
                                setResult(Z3Result.Infeasible);
                            }
                        }
                        else
                        {
                            setResult(Z3Result.Interrupted);
                        }
                        break;

                    default:
                        Debug.Assert(false, "Unrecognized Z3 Status");
                        break;
                    }
                }
            }
            finally
            {
                _isSolving = false;
            }

            // Now kill Z3
            DestructSolver(true);
        }