def _combineJobs(self, uJob, splitJob, criticalJobs):
        """
        Coming the dependency-ordered list of individual jobs into large jobs
        for update efficiency. The following rules apply:

         1. Info packages/components must be in there own jobs because of
            limitations with our user handling (bah). We actually allow them
            to combine if multiple versions/flavors of a single info package
            are installed, which doesn't seem quite right but hasn't hurt
            anything.
         2. We don't combine groups with other types of troves. Groups may
            be combined with other groups.
         3. self.cfg.updateThreshold is the maximum number of troves
            which may be installed within a single job. The only reason this
            will be exceeded is if a single dependency job is larger than this.
         4. We generally break jobs on packages and groups, not on components.
            The rule for updateThreshold overrides this.
         5. Jobs are made as large as possible given the other constraints.
         6. jobs split for critical updates to prevent including non-critical
            updates in a critical job unnecessarily.
         7. Try hard to keep packages and components in a single job.
        """

        combinedJobs = []
        # First pass combines components with their packages. This means
        # that non-critical components may get promoted to critical, but
        # that's not a problem.
        i = 0
        while len(splitJob) < i:
            newJob = []
            combinedJobs.append(newJob)
            lastName = None

            while i < len(splitJob):
                thisJob = splitJob[i]
                if thisJob in criticalJobs:
                    # we aren't allowed to combine critical jobs
                    if not newJob:
                        newJob.extend(thisJob)
                        i += 1

                    break

                names = set([ x[0].split(':')[0] for x in thisJob ])
                firstName = list(names)[0]
                if len(names) != 1:
                    if lastName is None:
                        newJob.extend(thisJob)
                        i += 1
                    break
                elif lastName is not None and lastName != firstName:
                    break

                lastName = firstName
                newJob.extend(thisJob)
                i += 1

        # next pass combines all jobs which contain :rpm components; it's
        # possible this should be more general, but finding out if already
        # installed troves are capsules is expensive enough that we'd rather
        # avoid it, and the component name is close enough for now. this looks
        # for the first :rpm, and combines all jobs which remain into a single
        # one. more simple than elegant
        newJobs = []
        curJob = None
        # Combine rpm jobs iff they contain rpms and potentially packages, not
        # if they only contain packages or other components.
        for jobList in combinedJobs:
            # If the job contains components that are not :rpm persist the job.
            if [ x for x in jobList if (not ':rpm' in x[0] and ':' in x[0]) or
                 x[0].startswith('group-') or x[0].startswith('info-') ]:
                curJob = None
                newJobs.append(jobList)
            # If the job contains rpm components or only consists of packages
            # combine the job.
            elif not curJob:
                curJob = jobList
                newJobs.append(jobList)
            else:
                curJob.extend(jobList)
        combinedJobs = newJobs

        newJob = []
        inGroup = None
        updateMax = self.cfg.updateThreshold
        finalCriticalJobs = []

        for jobList in combinedJobs:
            isCritical = jobList in criticalJobs

            foundGroup = None
            infosAdded = set()
            infosRemoved = set()
            hasNonInfo = False

            for job in jobList:
                (name, (oldVersion, oldFlavor),
                       (newVersion, newFlavor), absolute) = job

                if trove.troveIsGroup(name):
                    foundGroup = True

                if name.startswith('info-'):
                    if ':' in name:
                        pkg = name.split(':')[0]
                        if oldVersion:
                            infosRemoved.add(pkg)
                        if newVersion:
                            infosAdded.add(pkg)
                else:
                    hasNonInfo = True

            # Allow infos to be installed in the same job as a regular package
            # only when that info is replacing a matching info package that is
            # being removed. This is the case when migrating from combined
            # :user to split :user/:group, or when an erase+install happens
            # instead of an update.
            infosAdded -= infosRemoved
            if infosAdded and hasNonInfo:
                raise AssertionError("Attempted to install an info trove in "
                        "the same job as a non-info trove.\nInfo troves: %s"
                        % ", ".join(sorted(infosAdded)))

            addJob = uJob.addJob

            if isCritical:
                if newJob:
                    addJob(newJob)
                    newJob = []
                    inGroup = None

                finalCriticalJobs.append(len(uJob.getJobs()))
                addJob(jobList)
            elif infosAdded and not hasNonInfo:
                # Force normal info installs into their own job
                if newJob:
                    addJob(newJob)
                    newJob = []

                addJob(jobList)
            elif foundGroup != inGroup:
                if newJob:
                    addJob(newJob)
                    newJob = []

                newJob.extend(jobList)
                inGroup = foundGroup
            else:
                if (updateMax and newJob and
                         (len(newJob)  + len(jobList)) > updateMax):

                    addJob(newJob)
                    newJob = []

                # If the job contains rpms, it has already been colapsed.
                if [ x for x in jobList if ':rpm' in x[0] ]:
                    if newJob:
                        addJob(newJob)
                    addJob(jobList)
                    newJob = []
                else:
                    newJob.extend(jobList)

        if newJob:
            # we don't care if the final job is critical - there
            # will be no need for a restart in that case.
            uJob.addJob(newJob)

        uJob.setCriticalJobs(finalCriticalJobs)
dataset/ETHPy150Open sassoftware/conary/conary/conaryclient/update.py/ClientUpdate._combineJobs/swapped operands of `<`