{\em Sarah: In this pass, we need to make our proof sketches
solid proofs.}

The previous section assumed that there was no particular semantics 
to version order. That of course is false in practice.
In real software,
if Pi.vi calls Pj.vj and fails then Pj.vj is missing a function
that Pi.vi needs. 
Functions are ofren added 
to packages (Pj in this case) as versions increase,
but functions are normally not removed. 
This model is encoded in the CUDF dependencies of the form
Pi version 8 depends on Pj version 10 or greater.\footnote{
NB. Consider a dependency set  D =
that Pi.1 calls and is successful on Pj.1 and Pi.3 calls 
and is successful on Pj.3,
but there are no other compatibilities.
D would not satisfy the CUDF model, because we wouldn't have
Pi.1 being able to call Pj.3 successfully.
}

Further we assume that newer versions of a caller will depend
on newer versions of a callee. To continue our example from the previous
paragraph,
Pi version 9 depends on Pj version 13 or greater is more reasonable than
Pi version 9 depends on Pj version 7 or greater.

We incorporate all this in the following assumption.

Dependency monotonicity:
Suppose that Pi.vi succeeds on a call to Pj.vj.
If Pi.vi' depends on vj' or greater of Pj
and vi $<$ vi', then Pi.vi will succeed on a call to Pj.y for all 
y $\ge$ vj'.


\textbf{Lemma 1}: If Pi.vi fails on a call to Pj.vj, then
for all x $\ge$ vi, y $\le$ vj, Pi.x will fail on a call to Pj.y.

\textbf{Proof}:  
Consider x $>$ vi, for Pi.x to succeed on a call to Pj.y,
there must be an x $\ge$ x' $>$ vi and a y' $\le$ y such that
Pi.x' depends on y' or greater.
But in that case by dependency monotonicity and the fact that
 x $\ge$ x' $>$ vi and y' $\le$ y $\le$ vj, Pi.vi will succeed on Pj.vj.
Contradiction.
Done.

Our algorithm doesn't know whether or not these dependencies hold.
However, if the (hidden) dependencies
satisfy dependency monotonicity, the following will achieve
a lexicographically maximum configuration.

Algorithm: StrongMono LiquidClimber

{\em Sarah: the simplest way to state the algorithm is as a variant of the previous one with a few changes marked below as CHANGED}



\begin{small}
\begin{verbatim}
current    := package version pairs in the initial 
              configuration
todolist   := packages requiring maximization in 
              descending order of priority
constraints := some query constraints on versions of 
               various packages
sourcemap := for each package the versions that 
             exist and satisfy query constraints
memo := calls for some Pi.vi to some Pj.vj that fail. 
             Initially this is empty

LiquidClimber()
  for each package p in todolist in descending order of priority
      update current to the highest version of p that works 
      # use git binary on sourcemap
       if current has less than highest version of p 
       in sourcemap then 
        versionstodo := find all greater versions 
                         of p or major releases in 
                         ascending order of version 
                         number within sourcemap
       for each v in versionstodo in order
          temp := current with p set to v 
          ret := TryToMakeWork(p, temp) 
          if ret is not null then # we've had success
             current:= ret

# adjust temp to make it work
# p is the package we're trying to push
# Knowing that a version will fail 
# makes use of strong monotonicity:
# if Pk.vk calling Pm.vm fails then for all x >= vk and y <= vm,
# Pk.x will fail on Pm.y.
TryToMakeWork(p,temp)
  execute temp unless we know from memo and strong
    monotonicity that temp will fail # CHANGED
  if the execution of temp fails
    Find the first call, say from Pi.vi' to Pj.vj', 
    that fails
    Record in memo that Pi.vi' calling Pj.vj' fails
    keepfixed := {x | x == p
       or a package earlier than p in the todolist}
    if Pi in keepfixed then
      possible(Pi) := {vi'}
    else
      possible(Pi) := {all versions of Pi in sourcemap 
        that are less than or equal to vi} # CHANGED
    end if
    if Pj in keepfixed then
      possible(Pj) := {vj'}
    else
      possible(Pj) := {all versions of Pj in sourcemap
       that are greater than or equal to vj} # CHANGED
    end if
    for each untried configuration c that can
      be constructed from the cross-product of possible(Pi) 
      and possible(Pj) such that c does not 
      include some pair (Pk.vk, Pm.vm) that strong
      monotonoicity would eliminate based on memo # CHANGED
          ret := TryToMakeWork(p,c)
          if (ret is not null) then return ret
    end for
    return null
  else return temp # this configuration works
\end{verbatim}
\end{small}




{\em Sarah: and here is a new algorithm invented from scratch. I don't
think we need it.}


\begin{small}
\begin{verbatim}

current := package version pairs in the initial configuration
todolist := packages requiring maximization in descending order of priority
constraints := some constraints on versions of various packages
sourcemap := for each package the versions that exist and satisfy constraints
lasttried:= configurations (package version pairs) 
	that have been tried whether or not they have succeeded
successful:= configurations that have succeeded
memo:= calls from some Pi.vi to Pj.vj that failed

function strongmono_liquidclimber()
  for each package p in todolist in descending order of priority
      update current to the highest version of p that works 
		# use git binary on sourcemap
      lastried := current 
      if lasttried has less than last version of p then
	versionstodo = find all greater versions of p or major releases 
		in ascending order of version number within sourcemap
	for each v in versionstodo in order
		lasttried:= lasttried with p set to v # maximization push
		ret := strongmonotrytomakework(lasttried) 
		if ret[0] is true then # we've had success
			current:= ret
			add current to successful
		lasttried := ret[1] # no matter what, we advance versions
  return current

# adjust temp to make it work
function strongmonotrytomakework(p, temp)
	execute temp unless we know it fails because of strong monotonicity
	while temp doesn't work
	  In the execution of temp,
	  find the first call, say from Pi.vi' to Pj.vj', that fails.
	  Add (Pi,vi',Pj,vj') to memo.
	  Form a new version of temp with Pj.vj'' which is the next version
	  above vj' in Pj, 
	  if there is no such version vj'' or if Pj == p
		return (False, temp)
 	  execute temp unless we know it fails because of strong monotonicity
	endwhile
	return (True, temp)

\end{verbatim}
\end{small}

\textbf{Complexity}: 
every execution advances the version of at least one package
(either Pi or Pj)
so complexity is linear with the number of versions.

\textbf{Lemma 2}: Under the two assumptions of local/global and strong monotonicity,
any configuration that is ignored will fail.

\textbf{Proof Sketch}: The main thing to justify is 
that the set of versions that are still tried can be ignored.
Let us say that Pi.vi has called Pj.vj and fails.
Then for vi' $\ge$ vi, Pi.vi' will also fail on Pj.y for y $\le$ vj,  by lemma 1.
Done.

\textbf{Theorem}: 
Under the two assumptions of local/global and strong monotonicity,
the algorithm finds the lexicographically maximum
configuration that works.

\textbf{Proof Sketch}: 
LiquidClimber will go in priority order through
the packages to be maximized. Any configuration that is skipped
will fail by lemma 2.


Overall Algorithm


Start with the strong monotonicity algorithm and record all 
successful executions as well as all unsuccessful pairs (not the
inferred ones, just the ones directly tried).

Then if in the order of the todolist, we have achieved
the maximum out of the first L $\le$ K,
take those as fixed and call the earlier version
of LiquidClimber. 

So, if strong monotonicity holds, we have a linear algorithm,
but even if it doesn't, we have an algorithm that works.


