# File lib/dep_selector/selector.rb, line 61
    def find_solution(solution_constraints, valid_packages = nil)
      # this is a performance optimization so that packages that are
      # completely unreachable by the solution constraints don't get
      # added to the CSP
      packages_to_include_in_solve = trim_unreachable_packages(dep_graph, solution_constraints)

      begin
        # first, try to solve the whole set of constraints
        solve(dep_graph.clone, solution_constraints, valid_packages, packages_to_include_in_solve)
      rescue Exceptions::NoSolutionFound
        # since we're here, solving the whole system failed, so add
        # the solution_constraints one-by-one and try to solve in
        # order to find the constraint that breaks the system in order
        # to give helpful debugging info
        #
        # TODO [cw,2010/11/28]: for an efficiency gain, instead of
        # continually re-building the problem and looking for a
        # solution, turn solution_constraints into a Generator and
        # iteratively add and solve in order to re-use
        # propagations. This will require separating setting up the
        # constraints from searching for the solution.
        solution_constraints.each_index do |idx|
          workspace = dep_graph.clone
          begin
            solve(workspace, solution_constraints[0..idx], valid_packages, packages_to_include_in_solve)
          rescue Exceptions::NoSolutionFound => nsf
            disabled_packages =
              packages_to_include_in_solve.inject([]) do |acc, elt|
                pkg = workspace.package(elt.name)
                acc << pkg if nsf.unsatisfiable_problem.is_package_disabled?(pkg.gecode_package_id)
                acc
              end
            # disambiguate between packages disabled becuase they
            # don't exist and those that have otherwise problematic
            # constraints
            disabled_non_existent_packages = []
            disabled_most_constrained_packages = []
            disabled_packages.each do |disabled_pkg|
              disabled_collection =
                if disabled_pkg.valid? || (valid_packages && valid_packages.include?(disabled_pkg))
                  disabled_most_constrained_packages
                else
                  disabled_non_existent_packages
                end
              disabled_collection << disabled_pkg
            end

            # Pick the first non-existent or most-constrained package
            # that was required or the package whose constraints had
            # to be disabled in order to find a solution and generate
            # feedback for it. We only report feedback for one
            # package, because it is in fact actionable and dispalying
            # feedback for every disabled package would probably be
            # too long. The full set of disabled packages is
            # accessible in the NoSolutionExists exception.
            disabled_package_to_report_on = disabled_non_existent_packages.first ||
                                            disabled_most_constrained_packages.first
            feedback = error_reporter.give_feedback(dep_graph, solution_constraints, idx,
                                                    disabled_package_to_report_on)

            raise Exceptions::NoSolutionExists.new(feedback, solution_constraints[idx],
                                                   disabled_non_existent_packages,
                                                   disabled_most_constrained_packages)
          end
        end
      end
    end