(*<*)
theory Puzzle
imports "lib/Lib"
begin
(*>*)

text \<open>

Meet Schr\"odinger, who travels the world with an unusually clever clowder of
$n$ talking cats. In their latest show, the cats stand in a line.
Schr\"odinger asks a volunteer to take $n+1$ hats, numbered zero to $n$, and
randomly assign one to each cat, so that there is one spare. Each cat sees all
of the hats in front of it, but not its own hat, nor those behind, nor the
spare hat. The cats then take turns, each saying a single number from the set
$\setc*{i}{0 \leq i \leq n}$, without repeating any number said previously, and
without any other communication. The cats are allowed a single incorrect guess,
but otherwise every cat must say the number on its own hat.

\<close>

section \<open>Introduction\<close>

text \<open>

In this article, we will figure out how the cats do this. We'll start with some
informal analysis, deriving the solution by asking what properties it must
have, and refining these properties until we can realise them with a concrete
algorithm. We'll also develop a formal proof that the method always works,
using Isabelle/HOL.

Along the way, we'll rediscover a fundamental property of permutation groups,
and we'll gain some familiarity with some basic methods of formal mathematical
proof.

Although this is not intended as an Isabelle/HOL tutorial, we hope that it is
accessible to readers with no formal theorem proving experience. We do assume
familiarity with some fundamentals of functional programming and classical
logic.\footnote{Some exposure to Haskell, ML or similar, predicate logic with
quantifiers, and naive set theory should be adequate.} We won't explain the
detailed steps required to prove each lemma, but we will explain how each lemma
fits into the overall progression of the proof.

For the informal analysis, we'll work from the top down, gradually unfolding
the solution. Each refinement will be small, and may seem like it is the only
possible step. As we do this, we'll use Isabelle/HOL to make each step of the
analysis more precise, and to check that our reasoning is sound.

However, there's a problem with this approach: our proof is inherently bottom
up, building from the solution we ultimately identify, to a theorem that it
solves the puzzle. We do not attempt to show that our solution is the
\emph{only} possible solution, although our informal analysis suggests that it
is.

To develop the proof as we work top down, we need a way to invert the proof.
We'll do this by temporarily \emph{assuming} things we believe must be true for
the puzzle to have a solution, but which we don't yet know how to prove. But
we'll typically need to carry these assumptions through many lemmas. So, to
avoid repeating assumptions, we'll use the \isacommand{locale} mechanism of
Isabelle/HOL to create named bundles of assumptions. Later, we'll discharge our
assumptions using locale \emph{interpretation}.

Our first locale, @{text hats}, describes the basic setup of the
puzzle:\footnote{When reading Isabelle/HOL, you may ignore double quotes. They
are there for technical reasons which have very little to do with the
specification or proof you are reading.}

\<close>

locale hats =
  -- "the spare hat"
  fixes spare :: "nat"
  -- "hats assigned to cats, in order from back to front"
  fixes assigned :: "nat list"
  -- "the set of all hat numbers"
  assumes assign: "set (spare # assigned) = {0 .. length assigned}"

text \<open>

The @{term hats} locale takes two \emph{parameters} introduced with
\isacommand{fixes} declarations:

\begin{itemize}

  \item @{text spare}, of type @{typ nat}, represents the number of the spare
  hat,

  \item @{text assigned}, of type @{typ nat} @{text "list"},\footnote{In
  Isabelle/HOL, type constructor application is written right-to-left, so a
  @{typ nat} @{text "list"} is a list of natural numbers.} represents the hats
  assigned to cats, in order from back to front.

\end{itemize}

By \emph{parameter}, we mean a placeholder for an arbitrary value of the given
type. It is bound, or fixed, to the locale, so every mention of it in the
locale context refers to the same hypothetical value. The \isacommand{fixes}
declaration does not specify \emph{which} value, although subsequent locale
assumptions may have the effect of restricting the possible values of
parameters.

The @{term hats} locale also has an \isacommand{assumes} declaration, which
introduces an assumption named @{text assign}. It asserts that if we take the
set of all hats, including the @{text spare} and @{text assigned}
hats,\footnote{The @{text #} constructor builds a new list from an existing
element and list; and the @{text set} function converts a @{text list} to an
unordered @{text set} type.} then we have the set of natural numers from @{text
"0"} up to the number of @{text assigned} hats, inclusive.  This specifies the
possible hat numbers, but because we use unordered sets, it does not say
anything about the order of @{text assigned} hats, nor which is the @{text
spare} hat.

We can now prove lemmas in the context of the @{term hats} locale, which means
that these lemmas can talk about the @{text spare} and @{text assigned} hats,
and implicitly make the @{text assign} assumption.

For example, from this assumption, we can show that the hats all have distinct
numbers:

\<close>

lemma (in hats) "distinct (spare # assigned)"
  proof -
    -- "We start by restating our locale assumption."
    have "set (spare # assigned) = {0 .. length assigned}"
      by (rule assign)
    -- "We apply the @{text card} (set cardinality) function to both sides."
    hence "card (set (spare # assigned)) = card {0 .. length assigned}"
      by (rule arg_cong[where f=card])
    -- "We substitute an equivalent right-hand side, using built-in simplifications."
    hence "card (set (spare # assigned)) = 1 + length assigned"
      by simp
    -- "We substitute another right-hand side."
    hence "card (set (spare # assigned)) = length (spare # assigned)"
      by simp
    -- "The library fact @{text card_distinct} says a list is distinct
        if its length equals the cardinality of its set."
    thus "distinct (spare # assigned)"
      by (rule card_distinct)
  qed

text \<open>

The above proof contains much more detail than Isabelle/HOL requires. In the
rest of this article, we'll write much terser individual proofs, since we want
to focus on the higher-level development.

For example, we could shorten this proof as follows:

\<close>

lemma (in hats) distinct_hats: "distinct (spare # assigned)"
  by (rule card_distinct, subst assign, simp)

text \<open>

We've also given the lemma a name, @{text distinct_hats}, so we can refer to
the proven fact later.

\<close>

section \<open>Taking turns\<close>

text \<open>

The rules require each cat to say exactly one hat number, but do not specify
the order in which they do this. We can see that the order we choose affects
the distribution of information:

\begin{itemize}

  \item Visible information remains constant over time, but cats towards the
  rear see more than cats towards the front.

  \item Audible information accumulates over time, but at any particular point
  in time, all cats have heard the same things.

\end{itemize}

We observe that the cats can only ever communicate information \emph{forwards},
never backwards:

\begin{itemize}

  \item When a cat chooses a number, all of the information available to it is
  already known to all the cats behind it. Therefore, cats towards the rear can
  never learn anything from the choices made by cats towards the front.

  \item However, cats towards the front \emph{can} learn things from choices
  made by cats towards the rear, because those choices might encode knowledge
  of hats which are not visible from the front.

\end{itemize}

We propose that the cats should take turns from the rearmost towards the front,
ensuring that:

\begin{itemize}

  \item The cat making the choice is always the one with the most information.

  \item We maximise the amount each cat can learn before it makes its choice.

\end{itemize}

We'll use another locale, and some definitions in the locale context, to
describe the information flow:

\<close>

locale cats = hats +
  -- "Numbers said by the cats, in order from back to front."
  fixes spoken :: "nat list"
  -- "Each cat speaks exactly once."
  assumes length: "length spoken = length assigned"

-- "Each cat hears what was said by the cats behind it."
definition (in cats) "heard k \<equiv> take k spoken"

-- "Each cat sees the @{text assigned} hats in front of it."
definition (in cats) "seen k \<equiv> drop (Suc k) assigned"

text \<open>

Informally, the declaration says that there is a list of numbers @{text spoken}
by the cats, which is just as long as the list of @{text assigned} hats. We
define functions @{term heard} and @{term seen}, such that @{text heard} @{text
k} and @{text seen} @{text k} are the lists of numbers heard and seen by cat
@{text k}.

The only remarkable thing about the @{term cats} locale is that it
\emph{extends} the @{term hats} locale. This means that the \isacommand{fixes}
and \isacommand{assumes} declarations from the @{term hats} locale become
available in the context of the @{term cats} locale. Lemmas proved in the
@{term hats} locale also become available in the @{term cats} locale, whether
they were proved before or after the @{term cats} locale declaration.

Why did we not make these \isacommand{fixes} and \isacommand{assumes}
declarations in the @{term hats} locale? Eventually, we want to discharge the
@{text length} assumption, but we can never discharge the @{text assign}
assumption. As we'll see later, this means we need a separate locale.

\<close>

section \<open>The rearmost cat\<close>

text \<open>

Each cat sees the hats in front of it, and hears the calls made by those behind
it, but otherwise receives no information. In particular, no cat knows the
rearmost cat's number. Until Schr\"odinger reveals it at the end of the
performance, it could be either of the two hats that are invisible to all cats.

To guarantee success, the cats must therefore assume the worst: that the
rearmost cat got it wrong. But this means that all the other cats \emph{must}
get it right!

The role of the rearmost cat is therefore not to try to guess his own hat, but
to pass the right information to the other cats.

\<close>

section \<open>Reasoning by induction\<close>

text \<open>

Knowing which cats must get it right makes our job easier, since we don't need
to keep track of whether the cats have used up their free pass. When
considering how some cat $k$ makes its choice, we can assume that all the cats
$\setc{i}{0 < i < k}$, i.e.\ those behind it, except the rearmost, have already
made the right choices.

This might seem like circular reasoning, but it's not. In principle, we build
up what we know from the rearmost cat, one cat at a time towards the front,
using what we've already shown about cats $\setc{i}{0 \leq i < k}$ when we're
proving that cat $k$ makes the right choice. Mathematical induction merely says
that if all steps are alike, we can take an arbitrary number of them all at
once, by considering an arbitrary cat $k$, and assuming we've already
considered all the cats $\setc{i}{0 \leq i < k}$ behind it.

We'll use a locale to package up the so-called \emph{induction hypothesis}.
That is, we'll fix some cat @{text k}, which is not the rearmost cat, and
assume that all the cats behind it, except the rearmost cat, said the correct
number:\footnote{Infix operator @{text "!"} retrieves the @{text nth} element
from a @{text list}; and @{text "{a..<b}"} is the set $\setc{n}{a \leq n <
b}$.}

\<close>

locale cat_k = cats +
  fixes k :: "nat"
  assumes k_min: "0 < k"
  assumes k_max: "k < length assigned"
  assumes IH: "\<forall>i \<in> {1 ..< k}. spoken ! i = assigned ! i"

text \<open>

Using this, we can already formalise the induction argument:

\<close>

lemma (in cats) cat_k_induct:
  assumes "\<And>k. cat_k spare assigned spoken k \<Longrightarrow> spoken ! k = assigned ! k"
  shows "k \<in> {1 ..< length assigned} \<Longrightarrow> spoken ! k = assigned ! k"
  apply (induct k rule: nat_less_induct)
  apply (rule assms)
  apply (unfold_locales)
  by auto

text \<open>

This says that, in the @{term cats} locale, if every cat satisfying @{term
cat_k} says the correct number, then every cat except the rearmost says the
correct number. We get the induction hypothesis for free!

Note the keywords \isacommand{assumes} and \isacommand{shows} in the
\isacommand{lemma} statement. The first allows us to make additional
assumptions for this lemma. The second introduces the thing we want to prove
from the assumptions. If we have no local \isacommand{assumes} declarations, we
can omit the \isacommand{shows} keyword, as we did in @{text distict_hats}.
Logically, an assumption introduced with \isacommand{assumes} is identical to
one introduced with the implication arrow (@{text "\<Longrightarrow>"}). Which
to use is a matter of aesthetics.

In @{text cat_k_induct}, we've slightly abused the locale mechanism, by using
the @{term cat_k} locale as a logical predicate applied to some arguments. We
only do this a couple of times, but it saves us from having to repeat the
induction hypothesis many times.

As an example of something we can prove \emph{within} the @{term cat_k} locale,
we show that the tail of @{text heard} @{text k} can be rewritten in terms of
the @{text assigned} hats:\footnote{Keyword @{text op} turns an infix operator
into a prefix function.}

\<close>

lemma (in cat_k) k_max_spoken: "k < length spoken"
  using k_max length by simp

lemma (in cat_k) heard_k:
  "heard k = spoken ! 0 # map (op ! assigned) [Suc 0 ..< k]"
  using heard_def[of k] IH
        take_map_nth[OF less_imp_le, OF k_max_spoken]
        range_extract_head[OF k_min]
  by auto

section \<open>Candidate selection\<close>

text \<open>

According to the rules, no cat may repeat a number already said by another cat
behind it. With a little thought, we can also say that no cat may call a number
that it can see ahead of it. If it did, there would be at least two incorrect
calls.

To see this, suppose some cat $i$ said a number that it saw on the hat of $j$
who is in front of $i$. Hat numbers are unique, so $i$'s number must be
different from $j$'s, and therefore $i$'s choice is wrong. But $j$ may not
repeat the number that $i$ said, so $j$ is also wrong.

Each cat $i$ therefore has to choose between exactly two hats: those remaining
after excluding all the numbers it has seen and heard. We'll call these the
@{text candidates}, and we'll make our definition outside our locales, since it
will form part of our final solution:

\<close>

definition
  candidates_excluding :: "nat list \<Rightarrow> nat list \<Rightarrow> nat set"
where
  "candidates_excluding heard seen \<equiv>
    let excluded = heard @ seen in {0 .. 1 + length excluded} - set excluded"

text \<open>

We \emph{calculate} the set of all hats by counting the number of @{text heard}
and @{text seen}, so we're relying on the fact that the set of all hats is
always the set containing @{text "0"} up to the number of cats.

For convenience, we'll make a corresponding definition in the @{term cats}
locale:

\<close>

definition (in cats)
  "candidates i \<equiv> candidates_excluding (heard i) (seen i)"

text \<open>

We now want to prove that @{text candidates} produces the right results.
Consider cat @{text i}. If we take @{text heard} @{text i} and @{text seen}
@{text i}, we know that we need to add two more hats to make up the complete
set. Conversely, if we start with @{text heard} @{text i} and @{text seen}
@{text i}, and add two hypothetical hats @{text a} and @{text b}, such that the
result is the complete set of hats, then @{text candidates} @{text i} should be
those hats @{text a} and @{text b}. Formally:

\<close>

lemma (in cats) candidates_i:
  fixes a b i
  defines "view \<equiv> (a # heard i @ b # seen i)"
  assumes i_length: "i < length assigned"
  assumes distinct_view: "distinct view"
  assumes set_view: "set view = {0..length assigned}"
  shows "candidates i = {a,b}"
  proof -
    let ?excluded = "heard i @ seen i"
    have len: "1 + length ?excluded = length assigned"
      unfolding heard_def seen_def using i_length length by auto
    have set: "set ?excluded = {0..length assigned} - {a,b}"
      apply (rule subset_minusI)
      using distinct_view set_view unfolding view_def by auto
    show ?thesis
      unfolding candidates_def candidates_excluding_def Let_def
      unfolding len set
      unfolding Diff_Diff_Int subset_absorb_r
      using set_view unfolding view_def
      by auto
  qed

text \<open>

Here, we've introduced the idea of a \emph{view}. This is an hypothetical
ordering of the complete set of hats, seen from the perspective of some cat.
In this case, cat @{text i} imagines some hat @{text b} on its own head,
between the hats it has @{text heard} and @{text seen}, and imagines the
remaining hat @{text a} on the floor behind the rearmost cat, where no cat can
see it. The order of the list does not matter here, though it will later, but
it is still a nice visualisation. Here, we just need to know that the hats in
the list are exactly those in full set of hats, and we capture this in the
assumptions @{text distinct_view} and @{text set_view}.

\<close>

section \<open>The rejected hat\<close>

text \<open>

We now return to cat $k$ of the @{term cat_k} locale. Since none of the cats
$\setc{i}{0 \leq i < k}$ previously said $k$'s number, $k$'s own number must be
one of its @{text candidates}. Taking into account our assumption that all
those $\setc{i}{0 < i < k}$ except the rearmost said their own numbers, we can
also say that the other candidate will be the same number which the rearmost
cat chose \emph{not} to call.

To solve the puzzle, we therefore just need to ensure that every cat $k$
rejects the same number that the rearmost cat rejected. We'll call this the
\emph{rejected} hat.

To formalise this, we'll need to somehow define the rejected hat. We'll define
the @{text rejected} hat in term of the choice made by cat @{text "0"}. We
don't yet know how the cats choose their hats, but we can talk about their
@{text candidates}. Before we can consider the rearmost cat, we first need to
know that it exists, so let's make an assumption:

\<close>

locale cat_0 = cats +
  assumes exists_0: "0 < length assigned"

text \<open>

With this, we can safely extract the first @{text assigned} hat, and prove that
the rearmost cat's @{text candidates} are as we expect. We make use of the
@{text candidates_i} lemma, first defining a view, and proving lemmas to
satisfy the @{text distinct_vew} and @{text set_view} premises.

\<close>

abbreviation (in cat_0) (input) "view_0 \<equiv> spare # assigned ! 0 # seen 0"

lemma (in cat_0)
  distinct_0: "distinct view_0" and
  set_0: "set view_0 = {0..length assigned}"
  using distinct_hats assign
  unfolding seen_def Cons_nth_drop_Suc[OF exists_0]
  by auto

lemma (in cat_0) candidates_0: "candidates 0 = {spare, assigned ! 0}"
  using candidates_i exists_0 distinct_0 set_0
  unfolding heard_def seen_def
  by auto

text \<open>

Now we can define the @{text rejected} hat as whichever of those the rearmost
cat does \emph{not} say:

\<close>

definition (in cat_0)
  "rejected \<equiv> if spoken ! 0 = spare then assigned ! 0 else spare"

text \<open>

We now want to prove that @{text candidates} @{text k} consists of @{text k}'s
@{text assigned} hat, and the @{text rejected} hat, but there's a problem.
Since we defined @{text rejected} in the @{term cat_0} locale, it is not
currently visible in @{text cat_k}. To make it visible, we need to
\emph{interpret} the @{term cat_0} locale in the context of @{term cat_k}.

To interpret a locale means to make all the \emph{consequences} of that locale
available in some new context, including definitions and lemmas proved. But for
that to be logically sound, this means we need to \emph{prove the assumptions}
of the locale we are interpreting, in that same context.

In this case, we want to make the consequences of @{term cat_0} available in
@{term cat_k}, so we need to prove the assumptions of @{term cat_0} in the
context of @{term cat_k}. Thankfully, in @{term cat_k}, we can use the
assumptions of @{term cat_k}, and @{text exists_0} follows easily from @{text
k_max}.

To interpret one locale within another, we use the \isacommand{sublocale}
command:

\<close>

sublocale cat_k < cat_0
  using k_max by unfold_locales auto

text \<open>

Why did we not prove @{text exists_0} in locale @{term cat_k} in the first
place? The reason is that later, we'll need @{text distinct_0} and @{text
set_0} in a context where we don't have a cat @{text k}, but where we can prove
@{text exists_0} by other means.

Now, we want to use @{text candidates_i}, but can't immediately satisfy the
@{text distinct_view} and @{text set_view} premises of @{text candidate_i}, for
cat @{text k}'s view. However, we notice that there is an ordering of the full
set of hats which is a view for both cat @{text "0"} and cat @{text k}:

\<close>

abbreviation (in cat_0) (input) "view_r \<equiv> rejected # spoken ! 0 # seen 0"
abbreviation (in cat_k) (input) "view_k \<equiv> rejected # heard k @ assigned ! k # seen k"

text \<open>

We expect these lists should be equal, because:

\begin{itemize}

  \item the first thing that cat @{text k} would have @{text heard} was @{text
  spoken}~@{text "!"}~@{text "0"}, and

  \item under our @{term cat_k} assumptions, the rest of @{text view_k} is what
  cat @{text 0} had @{text seen}.

\end{itemize}

This is interesting, because @{text view_r} gets us closer to @{text view_0},
for which we have already proved the @{text candidates_i} premises. If we can
show that:

\begin{itemize}

  \item @{text view_r} and @{text view_k} are equal, and that

  \item the first two hats in @{text view_r} are the same as the first two in
  @{text view_0},

\end{itemize}

then we are close to proving the @{text candidates_i} premises for @{text
view_k}. We can prove the first of these:

\<close>

lemmas (in cat_k) drop_maps =
  drop_map_nth[OF less_imp_le_nat, OF k_max]
  drop_map_nth[OF Suc_leI[OF exists_0]]

lemma (in cat_k) view_eq: "view_r = view_k"
  unfolding heard_k seen_def
  apply (simp add: k_max Cons_nth_drop_Suc drop_maps)
  apply (subst map_append[symmetric])
  apply (rule arg_cong[where f="map _"])
  apply (rule range_app[symmetric])
  using k_max k_min less_imp_le Suc_le_eq by auto

text \<open>

To prove the second, we need to know something about @{text spoken} @{text "!"}
@{text "0"}. We haven't yet figured out how that choice is made, so we'll just
assume it's one of the @{text candidates}. Then we can prove the @{text view_r}
lemmas directly:

\<close>

locale cat_0_spoken = cat_0 +
  assumes spoken_candidate_0: "spoken ! 0 \<in> candidates 0"

lemma (in cat_0_spoken)
  distinct_r: "distinct view_r" and
  set_r: "set view_r = {0..length assigned}"
  using spoken_candidate_0 distinct_0 set_0
  unfolding candidates_0 rejected_def
  by fastforce+

text \<open>

Again, we're keeping a separate @{term cat_0} locale hierarchy, because we'll
need this later. In any case, we can always recombine locales, as we do now to
prove the @{text view_k} lemmas, and finally, the lemma for @{text
candidates}~@{term k}:

\<close>

locale cat_k_view = cat_k + cat_0_spoken

lemma (in cat_k_view)
  distinct_k: "distinct view_k" and
  set_k: "set view_k = {0..length assigned}"
  using distinct_r set_r view_eq by auto

lemma (in cat_k_view) candidates_k: "candidates k = {rejected, assigned ! k}"
  using candidates_i[OF k_max] distinct_k set_k by simp

text \<open>

If we additionally assumed that cat $k$ chooses one of it's @{text candidates},
but somehow avoids the @{text rejected} hat, it would trivially follow that cat
$k$ chooses its @{text assigned} hat. We don't gain much from formalising that
now, but hopefully it's clear that the remaining task is to ensure that cat
@{text k} does indeed reject the same hat as the rearmost cat.

\<close>

section \<open>The choice function\<close>

text \<open>

We'll now derive the method the cats use to ensure all of them reject the same
hat. We assume that the cats have agreed beforehand on the algorithm each cat
will \emph{individually} apply, and have convinced themselves that the agreed
algorithm will bring them \emph{collective} success, no matter how the hats are
assigned to them.

We'll represent the individual algorithm as a function of the information an
individual cat receives. We don't yet know its definition, but we can write its
type:

\<close>

type_synonym choice = "nat list \<Rightarrow> nat list \<Rightarrow> nat"

text \<open>

That is, when it is cat $k$'s turn, we give the list of calls @{text heard}
from behind, and the list of hats @{text seen} in front, both in order, and the
function returns the number the cat should call. The lengths of the lists give
the position of the cat in the line, so we can use a single function to
represent the choices of all cats, without loss of generality.

We can partially implement the @{typ choice} function, first calculating the
@{text candidates}, and deferring the remaining work to a @{text classifier}
function, which we'll take as a locale parameter until we know how to implement
it:

\<close>

type_synonym classifier = "nat \<Rightarrow> nat list \<Rightarrow> nat \<Rightarrow> nat list \<Rightarrow> bool"

locale classifier =
  fixes classify :: "classifier"

definition (in classifier)
  choice :: "choice"
where
  "choice heard seen \<equiv>
    case sorted_list_of_set (candidates_excluding heard seen) of
      [a,b] \<Rightarrow> if (classify a heard b seen) then b else a"

text \<open>

We'll say more about the @{typ classifier} in the next section. First, we'll
define a function which assembles the choices of all the cats into a list.
We'll need this to instantiate the @{text spoken} parameter of the @{term cats}
locale.

We define the @{text choices} function in two steps. First, we recursively
define @{text "choices'"}, taking two arguments: the numbers @{text heard} by
the current cat; and the remaining @{text assigned} hats for the current cat
and all cats towards the front. It recurses on the @{text assigned} hats, while
building up the list of numbers @{text heard}. In the second case, we discard
the hat @{text assigned} to the current cat, giving us exactly what is @{text
seen} by the current cat, and which is also the remainder of the @{text
assigned} hats for recursive call.

\<close>

primrec (in classifier)
  choices' :: "nat list \<Rightarrow> nat list \<Rightarrow> nat list"
where
  "choices' heard [] = []"
| "choices' heard (_ # seen)
    = (let c = choice heard seen in c # choices' (heard @ [c]) seen)"

text \<open>

The @{text choices} function then specialises to the initial state where the
rearmost cat begins having @{text heard} nothing:

\<close>

definition (in classifier) "choices \<equiv> choices' []"

text \<open>

We can prove, in two steps, that the number of @{text choices} is the same as
the number of @{text assigned} hats:

\<close>

lemma (in classifier) choices'_length: "length (choices' heard assigned) = length assigned"
  by (induct assigned arbitrary: heard) (auto simp: Let_def)

lemma (in classifier) choices_length: "length (choices assigned) = length assigned"
  by (simp add: choices_def choices'_length)

text \<open>

We can also prove that the individual @{text choices} are as we expect. The
@{text choices} lemma is important, because it makes clear that the @{text
choices} function does not cheat. It agrees with the @{text choice} function,
which is given exactly the information available to the respective cat. We know
that @{text choice} cannot cheat, because the @{text choices} lemma is
parametric in the list of @{text assigned} hats.

\<close>

lemma (in classifier) choices':
  assumes "i < length assigned"
  assumes "spoken = choices' heard assigned"
  shows "spoken ! i = choice (heard @ take i spoken) (drop (Suc i) assigned)"
  using assms proof (induct assigned arbitrary: i spoken heard)
    case Cons thus ?case by (cases i) (auto simp: Let_def)
  qed simp

lemma (in classifier) choices:
  assumes "i < length assigned"
  assumes "spoken = choices assigned"
  shows "spoken ! i = choice (take i spoken) (drop (Suc i) assigned)"
  using assms choices' by (simp add: choices_def)

section \<open>The classifier\<close>

text \<open>

Like the views we used in the @{text candidates} lemmas, the order we pass
arguments to the @{typ classifier} is suggestive of one of the two possible
orderings of the full set of hats that is consistent with what was @{text
heard} and @{text seen} by the cat making the @{typ choice}, with hat @{text b}
in the position of the cat, and hat @{text a} on the floor behind the rearmost
cat.

Rather than return a hat number, the @{typ classifier} returns a @{typ bool}
that indicates whether the given ordering should be accepted or rejected. If
accepted, the cat says the number it had imagined in its place. If rejected, it
says the other.

Since there must always be exactly one correct call, we require that the
classifier accepts an ordering if and only if it would reject the alternative:

\<close>

locale classifier_swap = classifier +
  assumes classifier_swap:
    "\<And>a heard b seen.
      distinct (a # heard @ b # seen) \<Longrightarrow>
        classify a heard b seen \<longleftrightarrow> \<not> classify b heard a seen"

text \<open>

This means that we can say which is the accepted ordering, regardless of which
ordering we actually passed to the classifier.

Although it's a small refinement from choice to classifier, it gives us a new
way of looking at the problem. Instead of asking what is the correct hat
number, which is different for each cat, we can consider orderings of the
complete set of hats, and ask which is the ordering that is consistent with the
information available to \emph{every} cat.

In particular, we notice that for all but the rearmost cat to choose the
correct hats, the accepted orderings must be the same for all cats. This is
because the correct call for any cat must be what was @{text seen} by all cats
to the rear, and will also be @{text heard} by all cats towards the front.

Surprisingly, this is true even for the rearmost cat! The only thing special
about the rearmost cat is that its assigned number is irrelevant. The task of
the rearmost cat is not to guess its assigned number, but to inform the other
cats which ordering is both consistent with the information they will have when
their turns come, and also accepted by their shared classifier.

We can write down the required property that the accepted orderings must be
consistent:

\<close>

locale classifier_consistent = classifier_swap +
  assumes classifier_consistent:
    "\<And>a heard b seen a' heard' b' seen'.
      a # heard @ b # seen = a' # heard' @ b' # seen'
        \<Longrightarrow> classify a heard b seen = classify a' heard' b' seen'"

text \<open>

So far, we have investigated some properties that a @{typ classifier} must
have, but have not thrown away any information. The classifier is given
everything known to each cat. The lengths of the arguments @{text heard} and
@{text seen} encode the cat's position in the line, so we even allow the
classifier to behave differently for each cat.

But the property @{term classifier_consistent} suggests that the position in
the line is redundant, and we can collapse the classifier's arguments into a
single list. We define a @{text parity_classifier} locale which does just this.
It is a specialisation of the @{term classifier_swap} locale, in which we
instantiate the @{text classify} parameter with a classifier based on an
arbitrary but fixed @{text parity} function. As a specialisation of the @{term
classifier_swap} locale, @{text parity_classifier} assumes a specialised
version of @{text classifier_swap}.

\<close>

type_synonym parity = "nat list \<Rightarrow> bool"

abbreviation (input)
  "classifier_of_parity parity \<equiv> \<lambda>a heard b seen. parity (a # heard @ b # seen)"

locale parity_classifier = classifier_swap "classifier_of_parity parity"
  for parity :: "parity"

text \<open>

We can show that @{term parity_classifier} satisfies the @{text
classifier_consistent} requirement, with a \isacommand{sublocale} proof. This
means that the only thing we require of our @{typ parity} function is that it
satisfies the @{text classifier_swap} property.

\<close>

sublocale parity_classifier < classifier_consistent "classifier_of_parity parity"
  by unfold_locales simp

section \<open>Solving the puzzle\<close>

text \<open>

Based on the informal derivation so far, our claim is that any @{typ parity}
function satisfying @{text classifier_swap} is sufficient to solve the puzzle.
Let's first prove this is the case, and then finally, we'll derive a @{typ
parity} function.

First, we need a locale which combines @{term hats} and @{term
parity_classifier}:

\<close>

locale hats_parity = hats + parity_classifier

text \<open>

Previously, in the @{term cats} locale and its descendents, we had to take the
numbers @{text spoken} by the cats as a locale parameter, since we did not know
how they made their choices. Now, we want to instantiate this parameter with
@{text choices} @{text assigned}, so we'll make a fresh batch of locales which
do this. We'll also discharge the @{term cats} locale assumption for this
instantiation, with a \isacommand{sublocale} proof:

\<close>

sublocale hats_parity < cats spare assigned "choices assigned"
  using choices_length by unfold_locales

locale cat_0_parity = hats_parity spare assigned parity
  + cat_0 spare assigned "choices assigned"
  for spare assigned parity

locale cat_k_parity = cat_0_parity spare assigned parity
  + cat_k spare assigned "choices assigned" k
  for spare assigned parity k

text \<open>

The following are just restatements of things we've already proved, but in
terms slightly more convenient for the proofs further on.

\<close>

lemma (in cat_0) candidates_excluding_0:
  "candidates_excluding [] (seen 0) = {spare, assigned ! 0}"
  using candidates_0 unfolding candidates_def heard_def take_0 by simp

lemma (in cat_k_view) candidates_excluding_k:
  "candidates_excluding (heard k) (seen k) = {rejected, assigned ! k}"
  using candidates_k unfolding candidates_def by simp

lemma (in cat_0_parity) parity_swap_0:
  "parity (spare # assigned ! 0 # seen 0) \<longleftrightarrow> \<not> parity (assigned ! 0 # spare # seen 0)"
  using classifier_swap[of spare "[]"] distinct_0 by simp

lemma (in cat_0_parity) choices_0: "choices assigned ! 0 = choice [] (seen 0)"
  using choices[OF exists_0] unfolding seen_def by simp

lemma (in cat_k_parity) choices_k:
  "choices assigned ! k = choice (heard k) (seen k)"
  unfolding heard_def seen_def using choices[OF k_max] by simp

text \<open>

Since cat @{text "0"} uses the @{typ parity} function to make its choice, we
can prove a couple of results about how its choice relates to @{text view_0}
and @{text view_r}. Note that the @{typ parity} of @{text view_r} is always
true!

\<close>

lemma (in cat_0_parity) choice_0:
  "choices assigned ! 0 = (if parity view_0 then assigned ! 0 else spare)"
  using distinct_0 parity_swap_0
  unfolding choices_0 choice_def candidates_excluding_0
  by (subst sorted_list_of_set_distinct_pair) auto

lemma (in cat_0_parity) parity_r: "parity view_r"
  using distinct_0 parity_swap_0
  unfolding choices_0 choice_def candidates_excluding_0 rejected_def
  by auto

text \<open>

Since @{text view_r} and @{text view_k} are equal, we also have that @{typ
parity} @{text view_k} is always true:

\<close>

lemma (in cat_k_parity) parity_k: "parity view_k"
  using parity_r view_eq by simp

text \<open>

At long last, we are almost ready to prove in @{term cat_k_parity} that cat
@{text k} makes the right choice! But first, we need to make certain results
about @{text view_k} available in the @{term cat_k_parity} locale. As usual,
we'll use a sublocale proof:

\<close>

sublocale cat_k_parity < cat_k_view spare assigned "choices assigned" k
  using choice_0 candidates_0 by unfold_locales simp

lemma (in cat_k_parity) choice_k: "choices assigned ! k = assigned ! k"
  using classifier_swap[OF distinct_k] distinct_k parity_k
  unfolding choices_k choice_def candidates_excluding_k
  by (subst sorted_list_of_set_distinct_pair) auto

text \<open>

Recall that our induction proof, @{text cat_k_induct}, showed that if every cat
satisfying @{term cat_k} says the correct number, then every cat except the
rearmost says the correct number. We've just shown that every cat satisfying
@{term cat_k_parity} says the correct number, so to apply the induction lemma,
we need to show that every cat satisfying @{term cat_k} also satisfies @{term
cat_k_parity}. The only undischarged assumptions in @{term cat_k_parity},
relative to to @{term cat_k}, are the ones we make in @{term hats_parity}, so
this implication is easy to prove in @{term hats_parity}:

\<close>

lemma (in hats_parity) cat_k_parity:
  assumes "cat_k spare assigned (choices assigned) k"
  shows "cat_k_parity spare assigned parity k"
  proof -
    interpret cat_k spare assigned "choices assigned" k by (rule assms)
    show ?thesis by unfold_locales
  qed

text \<open>

Finally, using our induction lemma, we get that in @{term hats_parity}, every
cat except the rearmost says its assigned hat number.

\<close>

lemma (in hats_parity) choices_correct:
  "k \<in> {1..<length assigned} \<Longrightarrow> choices assigned ! k = assigned ! k"
  by (rule cat_k_induct[OF cat_k_parity.choice_k, OF cat_k_parity])

section \<open>Legalities\<close>

text \<open>

There are a couple of rules which we've observed in our formal analysis, but
for which we, so far, have no proof: every cat must say the number of some hat,
and every cat must say a distinct number. We present the proofs without further
comment.

\<close>

lemma (in cats) distinct_pointwise:
  assumes "i < length assigned"
  shows "spare \<noteq> assigned ! i
           \<and> (\<forall> j < length assigned. i \<noteq> j \<longrightarrow> assigned ! i \<noteq> assigned ! j)"
  using assms distinct_hats by (auto simp: nth_eq_iff_index_eq)

lemma (in hats_parity) choices_distinct: "distinct (choices assigned)"
  proof (cases "0 < length assigned")
    case True
    interpret cat_0_parity spare assigned parity
      using True by unfold_locales
    show ?thesis
      apply (clarsimp simp: distinct_conv_nth_less choices_length)
      apply (case_tac "i = 0")
      using True choices_correct choice_0 distinct_pointwise
      by (auto split: if_splits)
  next
    case False
    thus ?thesis using choices_length[of assigned] by simp
  qed

lemma (in hats_parity) choice_legal:
  assumes "i < length assigned"
  shows "choices assigned ! i \<in> set (spare # assigned)"
  proof (cases "i = 0")
    case True
    interpret cat_0_parity spare assigned parity
      using assms True by unfold_locales simp
    show ?thesis using choice_0 using assms True by simp
  next
    case False
    thus ?thesis using assms choices_correct by auto
  qed

lemma (in hats_parity) choices_legal:
  "set (choices assigned) \<subseteq> set (spare # assigned)"
  using choices_length choice_legal subsetI in_set_conv_nth
  by metis

section \<open>Deriving the parity function\<close>

text \<open>

We have come a long way, but there is still one missing piece of the puzzle: a
@{typ parity} function which satisfies the @{text classifier_swap} property.
Informally, the property requires that if we take a list of distinct naturals,
and swap the \emph{first} number with \emph{any other number}, then the @{typ
parity} is inverted.

If we had such a function, what other properties must it have? For example,
what happens to the @{term parity} when we swap two elements not including the
first? By performing a sequence of three swaps with the first element, we can
get the effect of an arbitrary swap, and derive the following property. This
means that we actually require that if we swap \emph{any} two elements, then
the @{typ parity} is inverted.

\<close>

lemma (in parity_classifier) parity_swap_any:
  assumes "distinct (as @ b # cs @ d # es)"
  shows "parity (as @ b # cs @ d # es) \<longleftrightarrow> \<not> parity (as @ d # cs @ b # es)"
  proof (cases as)
    case Nil thus ?thesis using assms classifier_swap[of b] by simp
  next
    case (Cons a as)
    hence "parity (a # as @ b # cs @ d # es) \<longleftrightarrow> \<not> parity (b # as @ a # cs @ d # es)"
      using assms classifier_swap[of a as b "cs @ d # es"] by simp
    hence "parity (a # as @ b # cs @ d # es) \<longleftrightarrow> parity (d # as @ a # cs @ b # es)"
      using Cons assms classifier_swap[of b "as @ a # cs" d es] by simp
    hence "parity (a # as @ b # cs @ d # es) \<longleftrightarrow> \<not> parity (a # as @ d # cs @ b # es)"
      using Cons assms classifier_swap[of d as a "cs @ b # es"] by simp
    then show ?thesis using Cons by simp
  qed

text \<open>

How might we construct such a function? Let's start small, and consider only
lists of exactly two distinct numbers. There are only two ways to order the
elements, and four functions to a @{typ bool} result. Two of those are constant
functions which don't satisfy the @{text classifier_swap} property. One of the
non-constant functions tests whether the numbers are in ascending order, and
the other, descending order. They are mutual inverse, and both satisfy @{text
classifier_swap}. We arbitrarily choose the first:

\<close>

definition "parity_of_two xs \<equiv> case xs of [a,b] \<Rightarrow> a \<le> b"

text \<open>

Let's move on to lists of three distinct elements. There are six ways of
ordering three numbers, and 64 possible functions to @{typ bool}, but there are
still only two mutually inverse functions that satisfy the @{text
classifier_swap} property! We won't formalise this claim, but we can understand
it by laying out the six permutations in a graph, as in
figure~\ref{fig:permute-3}. Each node shows one of the six possible orderings
of the digits $1$ to $3$ at the top. Connecting lines indicate swaps of two
elements: solid lines for the leftmost two digits, dashed lines for the
rightmost two digits, and dotted lines for the outermost two digits.

\begin{figure}
\centering
\include{permute-3}
\caption{Permutations of three elements, by swaps of pairs.}
\label{fig:permute-3}
\end{figure}

If we choose a node, and assign it an arbitrary @{text parity}, then the @{text
parity_swap_any} property tells us that we must assign the opposite @{text
parity} to any node at the other end of a shared edge. We can continue
traversing edges this way, and find that every parity is determined by our
initial arbitrary choice. In the figure, we represent a @{typ parity} which is
@{term True} with a white fill, and @{term False} with a black fill.

Before we can extend this to lists of any length, we need to identify the
pattern. For a list of length two, we performed a single comparison. With three
elements, there are three comparisons we can perform, and for $n$ elements,
$\binom{n}{2}$ comparisons.\footnote{The \emph{binomial coefficient},
$\binom{n}{k} = \frac{n!}{k!(n-k)!}$ is the number of ways one can choose $k$
things from $n$ things.} If we are to use comparisons to calculate the @{text
parity}, we need to find a way to combine many @{typ bool} results into one.

In figure~\ref{fig:permute-3}, the number at the bottom of each node counts the
number of \emph{inversions} in the list. An inversion occurs when a list
element is greater than some other element to the right of it. For example, in
the list 2$\cdot$3$\cdot$1, the pairs 2$\cdot$1 and 3$\cdot$1 are inversions,
but the pair 2$\cdot$3 is not. We notice that all the white nodes have an even
number of inversions, and the black nodes have an odd number of inversions.

So perhaps we can define the @{typ parity} by counting the number of
inversions, and defining the @{typ parity} as whether or not the total number
of inversions is even. This seems plausible, because when we swap two numbers
within a distinct list:

\begin{itemize}

  \item The swapped pair itself will change the number of inversions by one.

  \item The change in the number of inversions caused by moving one of the pair
  over the intervening numbers will be odd if and only the change caused by the
  other is also odd.

\end{itemize}

\<close>

section \<open>Defining the parity function\<close>

text \<open>

We are now ready to write a recursive definition of a @{typ parity} function!
For the base case, we choose @{term True} as the @{typ parity} of an empty
list. For the recursive case, we calculate the @{typ parity} of the tail, and
also the number of inversions between the head and the tail. If both of these
are odd, then the overall @{typ parity} is even. Likewise, if both are even.
However, if one is even and the other is odd, then the overall @{typ parity}
is odd.

\<close>

primrec
  parity :: "parity"
where
  "parity [] = True"
| "parity (x # ys) = (parity ys = even (length [y \<leftarrow> ys. x > y]))"

text \<open>

We can prove that swapping two adjacent elements inverts the parity. Since the
function performs a pattern match at the head of the list argument, we prove
this by induction over the list preceding the first element being swapped.

\<close>

lemma parity_swap_adj:
  "b \<noteq> c \<Longrightarrow> parity (as @ b # c # ds) \<longleftrightarrow> \<not> parity (as @ c # b # ds)"
  proof (induct as)
    case Nil
    -- "In the @{text Nil} case, the @{term parity} function application simplifies away,"
    -- "because @{term b} and @{term c} are at the head of the list."
    thus "parity ([] @ b # c # ds) \<longleftrightarrow> \<not> parity ([] @ c # b # ds)"
      by auto
  next
    case (Cons a as)
    -- "In the @{text Cons} case, @{term b} and @{term c} are not at the head of the list, so we can't simplify directly."
    -- "However, we get the following from the induction hypothesis."
    hence "parity (as @ b # c # ds) \<longleftrightarrow> \<not> parity (as @ c # b # ds)"
      by simp
    -- "Using the induction hypothesis, we can now prove the @{text Cons} case by simplification."
    thus "parity ((a # as) @ b # c # ds) \<longleftrightarrow> \<not> parity ((a # as) @ c # b # ds)"
      by auto
  qed

text \<open>

To prove that swapping any two elements inverts the parity, we use @{text
parity_swap_adj} as the base case, and reason by induction on the list between
the two elements we are swapping.

\<close>

lemma parity_swap:
  assumes "b \<noteq> d \<and> b \<notin> set cs \<and> d \<notin> set cs"
  shows "parity (as @ b # cs @ d # es) \<longleftrightarrow> \<not> parity (as @ d # cs @ b # es)"
  using assms
  proof (induct cs arbitrary: as)
    case Nil
    -- "We get the following from the assumptions."
    hence "b \<noteq> d" by simp
    -- "From that and @{text parity_swap_adj}, we get:"
    hence "parity (as @ b # d # es) \<longleftrightarrow> \<not> parity (as @ d # b # es)"
      using parity_swap_adj[of b d as es] by simp
    -- "The @{text Nil} case then follows by simplification:"
    thus "parity (as @ b # [] @ d # es) \<longleftrightarrow> \<not> parity (as @ d # [] @ b # es)"
      by simp
  next
    case (Cons c cs')
    -- "By swapping @{term b} and @{term c}, which are adjacent, we get:"
    have " parity (as @ b # c # cs' @ d # es) \<longleftrightarrow> \<not> parity (as @ c # b # cs' @ d # es)"
      using Cons parity_swap_adj[of b c as "cs' @ d # es"] by simp
    moreover
    -- "From the induction hypothesis, we get:"
    have "parity (as @ c # b # cs' @ d # es) \<longleftrightarrow> \<not> parity (as @ c # d # cs' @ b # es)"
      using Cons(1)[where as="as @ [c]"] Cons(2) by simp
    moreover
    -- "By swapping @{term c} and @{term d}, which are adjacent, we get:"
    have "parity (as @ c # d # cs' @ b # es) \<longleftrightarrow> \<not> parity (as @ d # c # cs' @ b # es)"
      using Cons parity_swap_adj[of c d as "cs' @ b # es"] by simp
    ultimately
    -- "By combining the previous three swaps, we can prove the @{text Cons} case."
    show "parity (as @ b # (c # cs') @ d # es) \<longleftrightarrow> \<not> parity (as @ d # (c # cs') @ b # es)"
      by simp
  qed

section \<open>Top-level theorems\<close>

text \<open>

We now have what we need to discharge our remaining assumptions. By performing
a \isacommand{global\_interpretation} of the @{term parity_classifier} locale,
specialised using our concrete @{term parity} function, we make all the
theorems and definitions of that locale available globally:

\<close>

global_interpretation parity_classifier parity
  using parity_swap[where as="[]"] by unfold_locales simp

text \<open>

By interpreting the @{term hats_parity}, specialised using our concrete @{term
parity} function, within the @{term hats} locale, we make all the theorems of
@{term hats_parity} available in the @{term hats} locale:

\<close>

sublocale hats < hats_parity spare assigned parity
  by unfold_locales

text \<open>

We can then plumb the important theorems into the global context, by locally
assuming the same things as the @{term hats} locale:

\<close>

context
  fixes spare assigned
  assumes assign: "set (spare # assigned) = {0 .. length assigned}"
begin
  interpretation hats using assign by unfold_locales
  lemmas legal = choices_legal
  lemmas distinct = choices_distinct
  lemmas correct = choices_correct
end
(*<*)
thm choices legal distinct correct

lemma example_odd: "choices [2,0,5,4,3] = [1,0,5,4,3]"
  unfolding choices_def choices'_def Let_def choice_def
  by eval

lemma example_even: "choices [2,4,5,0,3] = [2,4,5,0,3]"
  unfolding choices_def choices'_def Let_def choice_def
  by eval
(*>*)

text \<open>

We now have four top-level theorems which show that we have solved the puzzle.
We'll present them in the traditional \emph{rule} format, with premises above
the line, and conclusions below the line. The first shows that we have not
cheated:

\begin{center}
@{thm[mode=Rule] choices[no_vars]}
{\sc choices}
\end{center}

We don't need to look at the implementation of @{term choices} or @{term
choice} to know this! The theorem is parametric in the set of @{text spare} and
@{text assigned} hats, so the @{term choice} function can only use what appears
in its arguments. Even if @{term choices} cheats, it agrees with @{term
choice}, which cannot.

The next two show that the @{text choices} are legal. That is, every cat
chooses the number of some hat, and no number is repeated:

\begin{center}
@{thm[mode=Rule] legal[no_vars]}
{\sc legal}
\end{center}

\begin{center}
@{thm[mode=Rule] distinct[no_vars]}
{\sc distinct}
\end{center}

Finally, every cat except the rearmost chooses the number of its assigned hat:

\begin{center}
@{thm[mode=Rule] correct[no_vars]}
{\sc correct}
\end{center}

\<close>

section \<open>Conclusion\<close>

text \<open>

Solving algorithmic problems requires precise thinking. It requires us to keep
account of the properties established by and required by any algorithm we are
constructing. It helps enormously to have a language in which we can write down
what we know and what we have assumed, and which allows us to check that our
reasoning is logically sound. A mechanised theorem prover like Isabelle/HOL
gives us such a language.

But precise thinking also requires \emph{practice}. My hope in writing this is
to convince you that exercising using a formal theorem prover is a useful
personal discipline for developing some aspects of algorithmic problem-solving
ability.

To learn more about theorem proving using Isabelle/HOL, read Tobias Nipkow and
Gerwin Klein, \emph{Concrete Semantics}, Springer 2014. There is a free PDF
available here:

\url{http://concrete-semantics.org/}

This article was written as a literate Isabelle/HOL theory, so all definitions
have been type-checked, and all proofs checked for validity. The source, with
some other bits and pieces, is available here:

\url{https://github.com/mbrcknl/puzzle-parity-permutations}

In particular, there is a more direct bottom-up version of the proof, which is
slightly shorter, because we did not need to invert the reasoning.

\<close>

(*<*)
end
(*>*)


(* Author: Lukas Koller *)
theory EulerianTour
  imports Main tsp.Misc tsp.MultiGraph (* "../misc/Select" *)
begin

text \<open>A graph is eulerian iff all vertices have even degree.\<close>
definition "is_eulerian E \<equiv> (\<forall>v \<in> mVs E. even' (mdegree E v))"

lemma is_eulerianI: "(\<And>v. v \<in> mVs E \<Longrightarrow> even' (mdegree E v)) \<Longrightarrow> is_eulerian E"
  unfolding is_eulerian_def by auto

text \<open>Definition of a Eulerian tour on multigraphs.\<close>
definition "is_et E T \<equiv> mpath E T \<and> hd T = last T \<and> E = mset (edges_of_path T)"

lemma is_etE:
  assumes "is_et E T"
  shows "mpath E T" "hd T = last T" "E = mset (edges_of_path T)"
  using assms[unfolded is_et_def] by auto

lemma et_nil: "is_et E [] \<Longrightarrow> E = {#}"
  unfolding is_et_def by auto

lemma et_nil1: "is_et E [v] \<Longrightarrow> E = {#}"
  unfolding is_et_def by auto

lemma et_edges:
  assumes "is_et E T"
  shows "E = mset (edges_of_path T)"
  using assms by (auto elim: is_etE)

lemma et_length_neq_1_2:
  assumes "mgraph_invar E" "is_et E T"
  shows "length T \<noteq> 1" "length T \<noteq> 2"
proof -
  show "length T \<noteq> 1"
  proof (rule ccontr; simp only: not_not)
    assume "length T = 1"
    moreover then obtain v where "T = [v]"
      using list_hd_singleton by fastforce
    moreover hence "v \<in> mVs E"
      using assms mem_mpath_mVs[of E T] by (auto simp: is_etE)
    moreover then obtain e where "e \<in># E" "v \<in> e"
      by (auto elim: mVs_memberE)
    moreover have "E = {#}"
      using assms calculation et_nil1 by auto
    ultimately show "False"
      by auto
  qed

  show "length T \<noteq> 2"
  proof (rule ccontr; simp only: not_not)
    assume "length T = 2"
    moreover then obtain u v where "T = [u,v]"
      using list_hd_singleton by (metis Suc_1 length_Suc_conv)
    moreover hence "u = v"
      using assms is_etE by fastforce
    moreover have "{u,v} \<in># E"
      using assms calculation mpath_edges_subset[of E T] by (auto simp: is_etE)
    moreover hence "u \<noteq> v"
      using assms by auto
    ultimately show "False"
      by auto
  qed
qed

lemma et_vertices_len_neq_1:
  assumes "is_et E T" "length T \<noteq> 1" (* there is no Eulerian Tour with length 1 *)
  shows "set T = mVs E"
  using assms
proof (induction T rule: edges_of_path.induct)
  case 1
  then show ?case
    using et_nil[of E] by (auto simp: mVs_def Vs_def)
next
  case (3 u v T)
  show ?case
  proof
    show "set (u#v#T) \<subseteq> mVs E"
    proof
      fix w
      assume "w \<in> set (u#v#T)"
      hence "2 \<le> length (u#v#T)"
        by auto
      then obtain e where "e \<in> set (edges_of_path (u#v#T))" "w \<in> e"
        using path_vertex_has_edge[of "u#v#T" w, OF _ \<open>w \<in> set (u#v#T)\<close>] by auto
      hence "e \<in># E"
        using "3.prems"[unfolded is_et_def] by auto
      then show "w \<in> mVs E"
        unfolding mVs_def Vs_def using \<open>w \<in> e\<close> by auto
    qed
  next
    show "mVs E \<subseteq> set (u#v#T)"
    proof
      fix w
      assume "w \<in> mVs E"
      then obtain e where "e \<in># E" "w \<in> e"
        unfolding mVs_def using vs_member by metis
      hence "e \<in> set (edges_of_path (u#v#T))"
        using "3.prems"[unfolded is_et_def] by auto
      then show "w \<in> set (u#v#T)"
        using v_in_edge_in_path_gen[of e "u#v#T", OF _ \<open>w \<in> e\<close>] by auto
    qed
  qed
qed auto

lemma double_graph_degree:
  assumes "E\<^sub>2\<^sub>x = mset_set E + mset_set E" "v \<in> mVs E\<^sub>2\<^sub>x"
  shows "mdegree E\<^sub>2\<^sub>x v = 2 * degree E v"
proof -
  have "mdegree E\<^sub>2\<^sub>x v = mdegree (mset_set E) v + mdegree (mset_set E) v"
    using assms mdegree_add by auto
  also have "... = 2 * degree E v"
    using mdegree_eq_degree[of E v] by (auto simp: semiring_numeral_class.mult_2)
  finally show ?thesis .
qed

lemma double_graph_eulerian:
  assumes "finite E" "E\<^sub>2\<^sub>x = mset_set E + mset_set E"
  shows "is_eulerian E\<^sub>2\<^sub>x"
proof (rule is_eulerianI)
  fix v
  assume "v \<in> mVs E\<^sub>2\<^sub>x"
  then show "even' (mdegree E\<^sub>2\<^sub>x v)"
    using assms non_inf_degr[of E v] double_graph_degree[of E\<^sub>2\<^sub>x E v]
          even_enat_mult2[of "degree E v"] by auto
qed

locale eulerian =
  fixes comp_et :: "'a mgraph \<Rightarrow> 'a list"
  assumes eulerian: "\<And>E. is_eulerian E \<Longrightarrow> is_et E (comp_et E)"

(* TODO: implement algorithm to compute Eulerian Tour? *)

end


(*
  File:    Summatory_Divisor_Sigma_Bounds.thy
  Author:  Manuel Eberl, TU München

  Asymptotic expansions for the sums over the divisor function \<sigma>_x.
*)
section \<open>The asymptotics of the summatory divisor $\sigma$ function\<close>
theory Summatory_Divisor_Sigma_Bounds
  imports Partial_Zeta_Bounds More_Dirichlet_Misc
begin

text \<open>
  In this section, we analyse the asymptotic behaviour of the summatory divisor functions
  $\sum_{n\leq x} \sigma_\alpha(n)$ for real \<open>\<alpha>\<close>. This essentially tells us what the average value
  of these functions is for large \<open>x\<close>.

  The case \<open>\<alpha> = 0\<close> is not treated here since $\sigma_0$ is simply the divisor function,
  for which precise asymptotics are already available in the AFP.
\<close>

subsection \<open>Case 1: $\alpha = 1$\<close>

text \<open>
  If \<open>\<alpha> = 1\<close>, $\sigma_\alpha(n)$ is simply the sum of all divisors of \<open>n\<close>. Here, the asymptotics is
  \[\sum_{n\leq x} \sigma_1(n) = \frac{\pi^2}{12} x^2 + O(x\ln x)\ .\]
\<close>
theorem summatory_divisor_sum_asymptotics:
  "sum_upto divisor_sum =o (\<lambda>x. pi\<^sup>2 / 12 * x ^ 2) +o O(\<lambda>x. x * ln x)"
proof -
  define \<zeta> where "\<zeta> = Re (zeta 2)"
  define R1 where "R1 = (\<lambda>x. sum_upto real x - x\<^sup>2 / 2)"
  define R2 where "R2 = (\<lambda>x. sum_upto (\<lambda>d. 1 / d\<^sup>2) x - (\<zeta> - 1 / x))"
  obtain c1 where c1: "c1 > 0" "\<And>x. x \<ge> 1 \<Longrightarrow> \<bar>R1 x\<bar> \<le> c1 * x"
    using zeta_partial_sum_le_neg[of 1] by (auto simp: R1_def)
  obtain c2 where c2: "c2 > 0" "\<And>x. x \<ge> 1 \<Longrightarrow> \<bar>R2 x\<bar> \<le> c2 / x\<^sup>2"
    using zeta_partial_sum_le_pos[of 2]
     by (auto simp: \<zeta>_def R2_def powr_minus field_simps
              simp del: div_mult_self3 div_mult_self4 div_mult_self2 div_mult_self1)

  have le: "\<bar>sum_upto divisor_sum x - \<zeta> / 2 * x\<^sup>2\<bar> \<le> c2 / 2 + x / 2 + c1 * x * (ln x + 1)"
    if x: "x \<ge> 1" for x
  proof -
    have div_le: "real (a div b) \<le> x" if "a \<le> x" for a b :: nat
      by (rule order.trans[OF _ that(1)]) auto

    have "real (sum_upto divisor_sum x) = sum_upto (dirichlet_prod real (\<lambda>_. 1)) x"
      by (simp add: divisor_sigma_conv_dirichlet_prod [abs_def]
                    sum_upto_def divisor_sigma_1_left [symmetric])
    also have "\<dots> = sum_upto (\<lambda>n. \<Sum>d | d dvd n. real d) x"
      by (simp add: dirichlet_prod_def)
    also have "\<dots> = (\<Sum>(n, d) \<in> (SIGMA n:{n. n > 0 \<and> real n \<le> x}. {d. d dvd n}). real d)"
      unfolding sum_upto_def by (subst sum.Sigma) auto
    also have "\<dots> = (\<Sum>(d, q) \<in> (SIGMA d:{d. d > 0 \<and> real d \<le> x}. {q. q > 0 \<and> real q \<le> x / d}). real q)"
      by (rule sum.reindex_bij_witness[of _ "\<lambda>(d, q). (d * q, q)" "\<lambda>(n, d). (n div d, d)"])
         (use div_le in \<open>auto simp: field_simps\<close>)
    also have "\<dots> = sum_upto (\<lambda>d. sum_upto real (x / d)) x"
      by (simp add: sum_upto_def sum.Sigma)
    also have "\<dots> = x\<^sup>2 * sum_upto (\<lambda>d. 1 / d\<^sup>2) x / 2 + sum_upto (\<lambda>d. R1 (x / d)) x"
      by (simp add: R1_def sum_upto_def sum.distrib sum_subtractf sum_divide_distrib
                    power_divide sum_distrib_left)
    also have "sum_upto (\<lambda>d. 1 / d\<^sup>2) x = \<zeta> - 1 / x + R2 x"
      by (simp add: R2_def)
    finally have eq: "real (sum_upto divisor_sum x) =
                        x\<^sup>2 * (\<zeta> - 1 / x + R2 x) / 2 + sum_upto (\<lambda>d. R1 (x / real d)) x" .

    have "real (sum_upto divisor_sum x) - \<zeta> / 2 * x\<^sup>2 =
            x\<^sup>2 / 2 * R2 x - x / 2 + sum_upto (\<lambda>d. R1 (x / real d)) x" using x
      by (subst eq)
         (simp add: field_simps power2_eq_square del: div_diff div_add
               del: div_mult_self3 div_mult_self4 div_mult_self2 div_mult_self1)
    also have "\<bar>\<dots>\<bar> \<le> c2 / 2 + x / 2 + c1 * x * (ln x + 1)"
    proof (rule order.trans[OF abs_triangle_ineq] order.trans[OF abs_triangle_ineq4] add_mono)+
      have "\<bar>x\<^sup>2 / 2 * R2 x\<bar> = x\<^sup>2 / 2 * \<bar>R2 x\<bar>"
        using x by (simp add: abs_mult)
      also have "\<dots> \<le> x\<^sup>2 / 2 * (c2 / x\<^sup>2)"
        using x by (intro mult_left_mono c2) auto
      finally show "\<bar>x\<^sup>2 / 2 * R2 x\<bar> \<le> c2 / 2"
        using x by simp
    next
      have "\<bar>sum_upto (\<lambda>d. R1 (x / real d)) x\<bar> \<le> sum_upto (\<lambda>d. \<bar>R1 (x / real d)\<bar>) x"
        unfolding sum_upto_def by (rule sum_abs)
      also have "\<dots> \<le> sum_upto (\<lambda>d. c1 * (x / real d)) x"
        unfolding sum_upto_def by (intro sum_mono c1) auto
      also have "\<dots> = c1 * x * sum_upto (\<lambda>d. 1 / real d) x"
        by (simp add: sum_upto_def sum_distrib_left)
      also have "sum_upto (\<lambda>d. 1 / real d) x = harm (nat \<lfloor>x\<rfloor>)"
        unfolding sum_upto_altdef harm_def by (intro sum.cong) (auto simp: field_simps)
      also have "\<dots> \<le> ln (nat \<lfloor>x\<rfloor>) + 1"
        by (rule harm_le) (use x in \<open>auto simp: le_nat_iff\<close>)
      also have "ln (nat \<lfloor>x\<rfloor>) \<le> ln x" using x by simp
      finally show "\<bar>sum_upto (\<lambda>d. R1 (x / real d)) x\<bar> \<le> c1 * x * (ln x + 1)"
        using c1(1) x by simp
    qed (use x in auto)
    finally show "\<bar>sum_upto divisor_sum x - \<zeta> / 2 * x\<^sup>2\<bar> \<le> c2 / 2 + x / 2 + c1 * x * (ln x + 1)" .
  qed

  have "eventually (\<lambda>x. \<bar>sum_upto divisor_sum x - \<zeta> / 2 * x\<^sup>2\<bar> \<le>
                            c2 / 2 + x / 2 + c1 * x * (ln x + 1)) at_top"
    using eventually_ge_at_top[of 1] by eventually_elim (use le in auto)
  hence "eventually (\<lambda>x. \<bar>sum_upto divisor_sum x - \<zeta> / 2 * x\<^sup>2\<bar> \<le>
                              \<bar>c2 / 2 + x / 2 + c1 * x * (ln x + 1)\<bar>) at_top"
    by eventually_elim linarith
  hence "(\<lambda>x. sum_upto divisor_sum x - \<zeta> / 2 * x\<^sup>2) \<in> O(\<lambda>x. c2 / 2 + x / 2 + c1 * x * (ln x + 1))"
    by (intro landau_o.bigI[of 1]) auto
  also have "(\<lambda>x. c2 / 2 + x / 2 + c1 * x * (ln x + 1)) \<in> O(\<lambda>x. x * ln x)"
    by real_asymp
  finally show ?thesis
    by (subst set_minus_plus [symmetric])
       (simp_all add: fun_diff_def algebra_simps \<zeta>_def zeta_even_numeral)
qed

subsection \<open>Case 2: $\alpha > 0$, $\alpha \neq 1$\<close>

(* TODO: Unify *)
text \<open>
  Next, we consider the case $\alpha > 0$ and $\alpha\neq 1$. We then have:
  \[\sum_{n\leq x} \sigma_\alpha(n) = \frac{\zeta(\alpha + 1)}{\alpha + 1} x^{\alpha + 1} +
       O\left(x^{\text{max}(1,\alpha)}\right)\]
\<close>

  have le: "\<bar>sum_upto (divisor_sigma \<alpha>) x - \<zeta> / (\<alpha> + 1) * x powr (\<alpha> + 1)\<bar> \<le> ub x"
    if x: "x \<ge> 1" for x
  proof -
    have div_le: "real (a div b) \<le> x" if "a \<le> x" for a b :: nat
      by (rule order.trans[OF _ that(1)]) auto

    have "sum_upto (divisor_sigma \<alpha>) x =
            sum_upto (dirichlet_prod (\<lambda>n. real n powr \<alpha>) (\<lambda>_. 1)) x"
      by (simp add: divisor_sigma_conv_dirichlet_prod [abs_def]
                    sum_upto_def divisor_sigma_1_left [symmetric])
    also have "\<dots> = sum_upto (\<lambda>n. \<Sum>d | d dvd n. real d powr \<alpha>) x"
      by (simp add: dirichlet_prod_def)
    also have "\<dots> = (\<Sum>(n, d) \<in> (SIGMA n:{n. n > 0 \<and> real n \<le> x}. {d. d dvd n}). real d powr \<alpha>)"
      unfolding sum_upto_def by (subst sum.Sigma) auto
    also have "\<dots> = (\<Sum>(d, q) \<in> (SIGMA d:{d. d > 0 \<and> real d \<le> x}. {q. q > 0 \<and> real q \<le> x / d}). real q powr \<alpha>)"
      by (rule sum.reindex_bij_witness[of _ "\<lambda>(d, q). (d * q, q)" "\<lambda>(n, d). (n div d, d)"])
         (use div_le in \<open>auto simp: field_simps\<close>)
    also have "\<dots> = sum_upto (\<lambda>d. sum_upto (\<lambda>q. q powr \<alpha>) (x / d)) x"
      by (simp add: sum_upto_def sum.Sigma)
    also have "\<dots> = x powr (\<alpha> + 1) * sum_upto (\<lambda>d. 1 / d powr (\<alpha> + 1)) x / (\<alpha> + 1) +
                    sum_upto (\<lambda>d. R1 (x / d)) x"
      by (simp add: R1_def sum_upto_def sum.distrib sum_subtractf sum_divide_distrib
                    powr_divide sum_distrib_left)
    also have "sum_upto (\<lambda>d. 1 / d powr (\<alpha> + 1)) x = \<zeta> - x powr -\<alpha> / \<alpha> + R2 x"
      by (simp add: R2_def powr_minus field_simps powr_diff powr_add)
    finally have eq: "sum_upto (divisor_sigma \<alpha>) x =
       x powr (\<alpha> + 1) * (\<zeta> - x powr -\<alpha> / \<alpha> + R2 x) / (\<alpha> + 1) + sum_upto (\<lambda>d. R1 (x / d)) x" .

    have "sum_upto (divisor_sigma \<alpha>) x - \<zeta> / (\<alpha> + 1) * x powr (\<alpha> + 1) =
            -x / (\<alpha> * (\<alpha> + 1)) + x powr (\<alpha> + 1) / (\<alpha> + 1) * R2 x + sum_upto (\<lambda>d. R1 (x / d)) x"
      using x \<alpha>
      by (subst eq, simp add: divide_simps
                         del: div_mult_self3 div_mult_self4 div_mult_self2 div_mult_self1)
         (simp add: field_simps power2_eq_square powr_add powr_minus del: div_diff div_add
               del: div_mult_self3 div_mult_self4 div_mult_self2 div_mult_self1)
    also have "\<bar>\<dots>\<bar> \<le> ub x" unfolding ub_def
    proof (rule order.trans[OF abs_triangle_ineq] order.trans[OF abs_triangle_ineq4] add_mono)+
      have "\<bar>x powr (\<alpha> + 1) / (\<alpha> + 1) * R2 x\<bar> = x powr (\<alpha> + 1) / (\<alpha> + 1) * \<bar>R2 x\<bar>"
        using x \<alpha> by (simp add: abs_mult)
      also have "\<dots> \<le> x powr (\<alpha> + 1) / (\<alpha> + 1) * (c2 * x powr (-\<alpha>-1))"
        using x \<alpha> by (intro mult_left_mono c2) auto
      also have "\<dots> = c2 / (\<alpha> + 1)"
        using \<alpha> x by (simp add: field_simps powr_diff powr_minus powr_add)
      finally show "\<bar>x powr (\<alpha> + 1) / (\<alpha> + 1) * R2 x\<bar> \<le> c2 / (\<alpha> + 1)" .
    next
      have "\<bar>sum_upto (\<lambda>d. R1 (x / real d)) x\<bar> \<le> sum_upto (\<lambda>d. \<bar>R1 (x / real d)\<bar>) x"
        unfolding sum_upto_def by (rule sum_abs)
      also have "\<dots> \<le> sum_upto (\<lambda>d. c1 * (x / real d) powr \<alpha>) x"
        unfolding sum_upto_def by (intro sum_mono c1) auto
      also have "\<dots> = c1 * x powr \<alpha> * sum_upto (\<lambda>d. 1 / real d powr \<alpha>) x"
        by (simp add: sum_upto_def sum_distrib_left powr_divide)
      also have "sum_upto (\<lambda>d. 1 / real d powr \<alpha>) x = x powr (1-\<alpha>) / (1-\<alpha>) + R3 x"
        using x by (simp add: R3_def powr_minus field_simps)
      also have "c1 * x powr \<alpha> * (x powr (1 - \<alpha>) / (1 - \<alpha>) + R3 x) =
                   c1 / (1 - \<alpha>) * x + c1 * x powr \<alpha> * R3 x"
        using x by (simp add: powr_diff divide_simps
                    del: div_mult_self3 div_mult_self4 div_mult_self2 div_mult_self1)
                   (simp add: field_simps)
      also have "c1 * x powr \<alpha> * R3 x \<le> c1 * x powr \<alpha> * c3"
        using x c1(1) c3(2)[of x] by (intro mult_left_mono) auto
      finally show "\<bar>sum_upto (\<lambda>d. R1 (x / d)) x\<bar> \<le> c1 * (1 / (1 - \<alpha>) * x + c3 * x powr \<alpha>)"
        by (simp add: field_simps)
    qed (use \<alpha> x in simp_all)
    finally show "\<bar>sum_upto (divisor_sigma \<alpha>) x - \<zeta> / (\<alpha> + 1) * x powr (\<alpha> + 1)\<bar> \<le> ub x" .
  qed

  have "eventually (\<lambda>x. \<bar>sum_upto (divisor_sigma \<alpha>) x - \<zeta> / (\<alpha>+1) * x powr (\<alpha>+1)\<bar> \<le> ub x) at_top"
    using eventually_ge_at_top[of 1] by eventually_elim (use le in auto)
  hence "eventually (\<lambda>x. \<bar>sum_upto (divisor_sigma \<alpha>) x - \<zeta>/(\<alpha>+1) * x powr (\<alpha>+1)\<bar> \<le> \<bar>ub x\<bar>) at_top"
    by eventually_elim linarith
  hence "(\<lambda>x. sum_upto (divisor_sigma \<alpha>) x - \<zeta>/(\<alpha>+1) * x powr (\<alpha>+1)) \<in> O(ub)"
    by (intro landau_o.bigI[of 1]) auto
  also have "ub \<in> O(\<lambda>x. x powr max 1 \<alpha>)"
    using \<alpha> unfolding ub_def by (cases "\<alpha> \<ge> 1"; real_asymp)
  finally show ?thesis
    by (subst set_minus_plus [symmetric])
       (simp_all add: fun_diff_def algebra_simps \<zeta>_def zeta_even_numeral)
qed

subsection \<open>Case 3: $\alpha < 0$\<close>

text \<open>
  Last, we consider the case of a negative exponent. We have for $\alpha > 0$:
  \[\sum_{n\leq x} \sigma_{-\alpha}(n) = \zeta(\alpha + 1)x + O(R(x))\]
  where $R(x) = \ln x$ if $\alpha = 1$ and $R(x) = x^{\text{max}(0, 1-\alpha)}$ otherwise.
\<close>
theorem summatory_divisor_sigma_asymptotics_neg:
  fixes \<alpha> :: real
  assumes \<alpha>: "\<alpha> > 0"
  defines "\<delta> \<equiv> max 0 (1 - \<alpha>)"
  defines "\<zeta> \<equiv> Re (zeta (\<alpha> + 1))"
  shows  "sum_upto (divisor_sigma (-\<alpha>)) =o (if \<alpha> = 1 then (\<lambda>x. pi\<^sup>2/6 * x) +o O(ln)
                                                     else (\<lambda>x. \<zeta> * x) +o O(\<lambda>x. x powr \<delta>))"
proof -
  define Ra where "Ra = (\<lambda>x. -sum_upto (\<lambda>d. d powr (-\<alpha>) * frac (x / d)) x)"
  define R1 where "R1 = (\<lambda>x. sum_upto (\<lambda>d. real d powr (-\<alpha>)) x - (x powr (1 - \<alpha>) / (1 - \<alpha>) + \<zeta>))"
  define R2 where "R2 = (\<lambda>x. sum_upto (\<lambda>d. d powr (-\<alpha> - 1)) x - (\<zeta> - x powr -\<alpha> / \<alpha>))"
  define R3 where "R3 = (\<lambda>x. sum_upto (\<lambda>d. d powr -\<alpha>) x - x powr (1 - \<alpha>) / (1 - \<alpha>))"
  obtain c2 where c2: "c2 > 0" "\<And>x. x \<ge> 1 \<Longrightarrow> \<bar>R2 x\<bar> \<le> c2 * x powr (-\<alpha>-1)"
    using zeta_partial_sum_le_pos[of "\<alpha> + 1"] \<alpha> by (auto simp: \<zeta>_def R2_def)
  define ub :: "real \<Rightarrow> real" where
    "ub = (\<lambda>x. x powr (1 - \<alpha>) / \<alpha> + c2 * x powr - \<alpha> + \<bar>Ra x\<bar>)"

  have le: "\<bar>sum_upto (divisor_sigma (-\<alpha>)) x - \<zeta> * x\<bar> \<le> ub x"
    if x: "x \<ge> 1" for x
  proof -
    have div_le: "real (a div b) \<le> x" if "a \<le> x" for a b :: nat
      by (rule order.trans[OF _ that(1)]) auto

    have "sum_upto (divisor_sigma (-\<alpha>)) x =
            sum_upto (dirichlet_prod (\<lambda>n. real n powr (-\<alpha>)) (\<lambda>_. 1)) x"
      by (simp add: divisor_sigma_conv_dirichlet_prod [abs_def]
                    sum_upto_def divisor_sigma_1_left [symmetric])
    also have "\<dots> = sum_upto (\<lambda>n. \<Sum>d | d dvd n. real d powr (-\<alpha>)) x"
      by (simp add: dirichlet_prod_def)
    also have "\<dots> = (\<Sum>(n, d) \<in> (SIGMA n:{n. n > 0 \<and> real n \<le> x}. {d. d dvd n}). real d powr (-\<alpha>))"
      unfolding sum_upto_def by (subst sum.Sigma) auto
    also have "\<dots> = (\<Sum>(d, q) \<in> (SIGMA d:{d. d > 0 \<and> real d \<le> x}. {q. q > 0 \<and> real q \<le> x / d}).
                         real d powr (-\<alpha>))"
      by (rule sum.reindex_bij_witness[of _ "\<lambda>(d, q). (d * q, d)" "\<lambda>(n, d). (d, n div d)"])
         (use div_le in \<open>auto simp: field_simps dest: dvd_imp_le\<close>)
    also have "\<dots> = sum_upto (\<lambda>d. sum_upto (\<lambda>q. d powr (-\<alpha>)) (x / d)) x"
      by (simp add: sum_upto_def sum.Sigma [symmetric])
    also have "\<dots> = sum_upto (\<lambda>d. d powr (-\<alpha>) * \<lfloor>x / d\<rfloor>) x"
      using x by (simp add: sum_upto_altdef mult_ac)
    also have "\<dots> = x * sum_upto (\<lambda>d. d powr (-\<alpha>) / d) x + Ra x"
      by (simp add: frac_def sum_distrib_left sum_distrib_right
                    sum_subtractf sum_upto_def algebra_simps Ra_def)
    also have "sum_upto (\<lambda>d. d powr (-\<alpha>) / d) x = sum_upto (\<lambda>d. d powr (-\<alpha> - 1)) x"
      by (simp add: powr_diff powr_minus powr_add field_simps)
    also have "\<dots> = \<zeta> - x powr - \<alpha> / \<alpha> + R2 x"
      by (simp add: R2_def)
    finally have "sum_upto (divisor_sigma (-\<alpha>)) x - \<zeta> * x = -(x powr (1 - \<alpha>) / \<alpha>) + x * R2 x + Ra x"
      using x \<alpha> by (simp add: powr_diff powr_minus field_simps)

    also have "\<bar>\<dots>\<bar> \<le> x powr (1 - \<alpha>) / \<alpha> + c2 * x powr -\<alpha> + \<bar>Ra x\<bar>"
    proof (rule order.trans[OF abs_triangle_ineq] order.trans[OF abs_triangle_ineq4] add_mono)+
      from x have "\<bar>x * R2 x\<bar> \<le> x * \<bar>R2 x\<bar>"
        by (simp add: abs_mult)
      also from x have "\<dots> \<le> x * (c2 * x powr (-\<alpha> - 1))"
        by (intro mult_left_mono c2) auto
      also have "\<dots> = c2 * x powr -\<alpha>"
        using x by (simp add: field_simps powr_minus powr_diff)
      finally show "\<bar>x * R2 x\<bar> \<le> \<dots>" .
    qed (use x \<alpha> in auto)
    finally show "\<bar>sum_upto (divisor_sigma (- \<alpha>)) x - \<zeta> * x\<bar> \<le> ub x"
      by (simp add: ub_def)
  qed

  have "eventually (\<lambda>x. \<bar>sum_upto (divisor_sigma (-\<alpha>)) x - \<zeta> * x\<bar> \<le> ub x) at_top"
    using eventually_ge_at_top[of 1] by eventually_elim (use le in auto)
  hence "eventually (\<lambda>x. \<bar>sum_upto (divisor_sigma (-\<alpha>)) x - \<zeta> * x\<bar> \<le> \<bar>ub x\<bar>) at_top"
    by eventually_elim linarith
  hence bigo: "(\<lambda>x. sum_upto (divisor_sigma (-\<alpha>)) x - \<zeta> * x) \<in> O(ub)"
    by (intro landau_o.bigI[of 1]) auto

  define ub' :: "real \<Rightarrow> real" where "ub' = sum_upto (\<lambda>n. real n powr - \<alpha>)"
  have "\<bar>Ra x\<bar> \<le> \<bar>ub' x\<bar>" if "x \<ge> 1" for x
  proof -
    have "\<bar>Ra x\<bar> \<le> sum_upto (\<lambda>n. \<bar>real n powr -\<alpha> * frac (x / n)\<bar>) x"
      unfolding Ra_def abs_minus sum_upto_def by (rule sum_abs)
    also have "\<dots> \<le> sum_upto (\<lambda>n. real n powr -\<alpha> * 1) x"
      unfolding abs_mult sum_upto_def
      by (intro sum_mono mult_mono) (auto intro: less_imp_le[OF frac_lt_1])
    finally show ?thesis by (simp add: ub'_def)
  qed
  hence "Ra \<in> O(ub')"
    by (intro bigoI[of _ 1] eventually_mono[OF eventually_ge_at_top[of 1]]) auto
  also have "ub' \<in> O(\<lambda>x. if \<alpha> = 1 then ln x else x powr \<delta>)"
  proof (cases "\<alpha> = 1")
    case [simp]: True
    have "sum_upto (\<lambda>n. 1 / n) \<in> O(ln)"
      by (intro asymp_equiv_imp_bigo harm_asymp_equiv)
    thus ?thesis by (simp add: ub'_def powr_minus field_simps)
  next
    case False
    have "sum_upto (\<lambda>n. real n powr - \<alpha>) \<in> O(\<lambda>x. x powr \<delta>)"
      using assms False unfolding \<delta>_def by (intro zeta_partial_sum_pos_bigtheta bigthetaD1)
    thus ?thesis
      using zeta_partial_sum_neg_asymp_equiv[of \<alpha>] \<alpha> False by (simp add: ub'_def)
  qed
  finally have Ra_bigo: "Ra \<in> \<dots>" .

  show ?thesis
  proof (cases "\<alpha> = 1")
    case [simp]: True
    with Ra_bigo have Ra: "(\<lambda>x. \<bar>Ra x\<bar>) \<in> O(ln)" by simp
    note bigo
    also have "ub \<in> O(\<lambda>x. ln x)"
      unfolding ub_def by (intro sum_in_bigo Ra) real_asymp+
    finally have "sum_upto (divisor_sigma (-\<alpha>)) =o (\<lambda>x. (pi\<^sup>2 / 6) * x) +o O(ln)"
      by (subst set_minus_plus [symmetric])
         (simp_all add: fun_diff_def algebra_simps \<zeta>_def zeta_even_numeral)
    thus ?thesis by (simp only: True refl if_True)
  next
    case False
    with Ra_bigo have Ra: "(\<lambda>x. \<bar>Ra x\<bar>) \<in> O(\<lambda>x. x powr \<delta>)" by simp
    have *: "(\<lambda>x. x powr (1 - \<alpha>) / \<alpha>) \<in> O(\<lambda>x. x powr \<delta>)"
            "(\<lambda>x. c2 * x powr - \<alpha>) \<in> O(\<lambda>x. x powr \<delta>)"
      unfolding \<delta>_def using \<alpha> False by (cases "\<alpha> > 1"; real_asymp)+

    note bigo
    also have "ub \<in> O(\<lambda>x. x powr \<delta>)"
      unfolding ub_def using \<alpha> False by (intro sum_in_bigo Ra *)
    finally have "sum_upto (divisor_sigma (-\<alpha>)) =o (\<lambda>x. \<zeta> * x) +o O(\<lambda>x. x powr \<delta>)"
      by (subst set_minus_plus [symmetric])
         (simp_all add: fun_diff_def algebra_simps \<zeta>_def zeta_even_numeral)
    thus ?thesis by (simp only: False refl if_False)
  qed
qed

end


theory IntervalLists
  imports Main

begin
datatype intvl = Bd nat nat | Ub nat

fun first_term :: "intvl \<Rightarrow> nat" where
"first_term (Bd a b) = a"
|"first_term (Ub a) = a"

fun fusion :: "intvl \<Rightarrow> intvl \<Rightarrow>intvl" where
"fusion (Bd a b) (Bd c d) = (Bd a d)"
|"fusion (Bd a b) (Ub c) = (Ub a)"

fun inv' :: "nat \<Rightarrow> intvl list \<Rightarrow> bool" where
"inv' n [] = True"
| " inv' a (Ub x# v) = ((v=[])\<and>(a\<le>x))"
|"inv' a ((Bd x y)#v) =((x\<le>y)\<and>(a\<le>x)\<and>(inv' (y+2) (v)))"

definition inv where "inv\<equiv>inv' 0"

fun set_of :: "intvl list \<Rightarrow> nat set" where
"set_of [] = {}"
|"set_of (Ub vb # vc) = {vb..} \<union> set_of (vc)"
|"set_of (Bd va vd # vc) = {va..vd} \<union> set_of (vc)"

fun add :: "nat \<Rightarrow> intvl list \<Rightarrow> intvl list" where
"add n [] = [Bd n n]"
|"add n [Bd x y] = (if n+1=x then [Bd n y] else (if n+1<x then (Bd n n)#[Bd x y] else (if n=y+1 then [Bd x n] else (if n>y+1 then [Bd x y]@[Bd n n] else [Bd x y]))))"
|"add a (Ub vb # va) = (if a+1=vb then (Ub a)#va else (if a\<ge>vb then (Ub vb # va) else (Bd a a)#(Ub vb # va) ))"
|"add a (Bd va vd # vb # vc) = (if a+1<va then (Bd a a)#(Bd va vd # vb # vc) else (if a+1=va then (Bd a vd # vb # vc) else (if a=vd+1 then(if ((first_term vb)=a+1) then (fusion (Bd va vd) vb )#vc else (Bd va a # vb # vc))else (Bd va vd)#(add a (vb # vc)) )))"

value "add (19::nat) [(Bd 0 0),(Bd 5 6),(Bd 8 9),(Bd 11 18),(Ub 20)]"

value "add (0::nat) [Bd 0 0, Bd 1 1]"

lemma lemma1:"inv [u]\<Longrightarrow>(first_term u) = y+2 \<Longrightarrow> x\<le>y \<Longrightarrow> set_of ([fusion (Bd x y) u ]) = (set_of [Bd x y]) \<union> {y+1}  \<union>   set_of [u] "
 proof (cases u)
   case (Bd x11 x12)
   assume "inv [u]" and"(first_term u) = y+2"and" x\<le>y"
   with Bd
 show ?thesis by (auto simp:inv_def)
next
  case (Ub x2)
assume "inv [u]" and"(first_term u) = y+2"and" x\<le>y"
   with Ub
 show ?thesis by (auto simp:inv_def)
qed

theorem "(inv' c  xs) \<Longrightarrow> (a\<ge>c) \<Longrightarrow> set_of (add a xs) = {a} \<union> set_of xs"
proof (induction xs arbitrary: a c rule: add.induct )
  case (1 n)
  then show ?case  by (auto  split :  if_splits)
next
  case (2 n x y)
  then show ?case  by (auto  split :  if_splits simp:inv_def)
next
  case (3 a vb va)
  then show ?case by (auto  split :  if_splits simp:inv_def)
next
  case (4 a va vd vb vc)
  then show ?case apply (auto  split :  if_splits simp:inv_def lemma1)
qed

theorem "(a\<ge>c)\<and> inv' c xs \<Longrightarrow> inv' c (add a xs)"
proof (induction a xs arbitrary: c rule: add.induct )
  case (1 n)
  then show ?case by (auto  split :  if_splits)
next
  case (2 n x y)
  then show ?case by (auto  split :  if_splits)
next
  case (3 a vb va)
  then show ?case by (auto  split :  if_splits)
next
  case (4 a va vd vb vc)
  show ?case
  proof (cases vb)
  case (Bd l r)
  with 4 show ?thesis apply (auto  split : if_splits )
qed

qed

end


theory CompArithDefs
imports Main

begin

fun bool2nat :: "bool \<Rightarrow> nat" ("\<lbrakk> _ \<rbrakk>\<^sub>N" 70) where
  "\<lbrakk> True \<rbrakk>\<^sub>N = 1" |
  "\<lbrakk> False \<rbrakk>\<^sub>N = 0"

fun bool2int :: "bool \<Rightarrow> int" ("\<lbrakk> _ \<rbrakk>\<^sub>Z" 70) where
  "\<lbrakk> True \<rbrakk>\<^sub>Z = 1" |
  "\<lbrakk> False \<rbrakk>\<^sub>Z = 0"

lemma bool2int_eq_bool2nat : "int (\<lbrakk> x \<rbrakk>\<^sub>N) = \<lbrakk> x \<rbrakk>\<^sub>Z" by(cases x, simp_all)

fun nat2bool :: "nat \<Rightarrow> bool" ("\<lbrakk> _ \<rbrakk>\<^sub>B" 70) where
  "\<lbrakk> 0 \<rbrakk>\<^sub>B = False" |
  "\<lbrakk> (Suc 0) \<rbrakk>\<^sub>B = True" |
  "\<lbrakk> _ \<rbrakk>\<^sub>B = undefined"

fun DAplus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list \<times> bool" ("DA\<^sup>+") where
  "DA\<^sup>+ [] [] = ([] , False)" |
  "DA\<^sup>+ (_ # _) [] = undefined" |
  "DA\<^sup>+ [] (_ # _) = undefined" |
  "DA\<^sup>+ (a # as) (b # bs) =
    ( \<lbrakk>(\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>b\<rbrakk>\<^sub>N + \<lbrakk>snd (DA\<^sup>+ as bs)\<rbrakk>\<^sub>N) mod 2\<rbrakk>\<^sub>B # fst (DA\<^sup>+ as bs) ,
      \<lbrakk>(\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>b\<rbrakk>\<^sub>N + \<lbrakk>snd (DA\<^sup>+ as bs)\<rbrakk>\<^sub>N) div 2\<rbrakk>\<^sub>B )"

fun DAminus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list \<times> bool" ("DA\<^sup>-") where
  "DA\<^sup>- [] [] = ([] , True)" |
  "DA\<^sup>- (_ # _) [] = undefined" |
  "DA\<^sup>- [] (_ # _) = undefined" |
  "DA\<^sup>- (a # as) (b # bs) =
    ( \<lbrakk>(\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>\<not>b\<rbrakk>\<^sub>N + \<lbrakk>snd (DA\<^sup>- as bs)\<rbrakk>\<^sub>N) mod 2\<rbrakk>\<^sub>B # fst (DA\<^sup>- as bs) ,
      \<lbrakk>(\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>\<not>b\<rbrakk>\<^sub>N + \<lbrakk>snd (DA\<^sup>- as bs)\<rbrakk>\<^sub>N) div 2\<rbrakk>\<^sub>B )"

definition uplus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list" (infixl "+\<^sub>Z\<^sub>A" 90) where
  "as +\<^sub>Z\<^sub>A bs = fst (DA\<^sup>+ (False # as) (False # bs))"

fun splus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list" (infixl "+\<^sub>S\<^sub>A" 90) where
  "[] +\<^sub>S\<^sub>A [] = []" |
  "as +\<^sub>S\<^sub>A bs = fst (DA\<^sup>+ (hd as # as) (hd bs # bs))"

definition tplus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list" (infixl "+\<^sub>A" 90) where
  "as +\<^sub>A bs = fst (DA\<^sup>+ as bs)"

definition uminus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list" (infixl "-\<^sub>Z\<^sub>A" 90) where
  "as -\<^sub>Z\<^sub>A bs = fst (DA\<^sup>- (False # as) (False # bs))"

fun sminus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list" (infixl "-\<^sub>S\<^sub>A" 90) where
  "[] -\<^sub>S\<^sub>A [] = []" |
  "as -\<^sub>S\<^sub>A bs = fst (DA\<^sup>- (hd as # as) (hd bs # bs))"

definition tminus :: "bool list \<Rightarrow> bool list \<Rightarrow> bool list" (infixl "-\<^sub>A" 90) where
  "as -\<^sub>A bs = fst (DA\<^sup>- as bs)"

fun ueval :: "bool list \<Rightarrow> nat" ("\<lbrakk> _ \<rbrakk>") where
  "\<lbrakk> [] \<rbrakk> = 0" |
  "\<lbrakk> a # as \<rbrakk> = \<lbrakk>a\<rbrakk>\<^sub>N * 2 ^ length as + \<lbrakk>as\<rbrakk>"

lemma ueval_upper_bound: "\<lbrakk> l \<rbrakk> \<le> 2 ^ (length l) - 1"
  apply(induct l)
   apply simp
  unfolding ueval.simps
  apply (case_tac a)
  by simp_all

lemma ueval_upper_bound2 : "length a = k \<Longrightarrow> \<lbrakk> a \<rbrakk> \<le> (2 ^ k) - 1"
  apply (induct a arbitrary: k)
   apply simp_all
  apply (case_tac a1)
  by fastforce+

lemma ueval_upper_bound3 : "\<lbrakk> l \<rbrakk> < 2 ^ (length l)"
  apply(induct l)
   apply simp
  unfolding ueval.simps
  apply (case_tac a)
  by simp_all

fun seval :: "bool list \<Rightarrow> int" ("\<lparr> _ \<rparr>") where
  "\<lparr> [] \<rparr> = 0" |
  "\<lparr> a # as \<rparr> = - int (\<lbrakk>a\<rbrakk>\<^sub>N * 2 ^ length as) + int \<lbrakk>as\<rbrakk>"

lemma seval_upper_bound: "\<lparr> (x#xs) \<rparr> \<le> 2 ^ (length xs) - 1"
  unfolding seval.simps
  apply (cases x)
   apply simp_all
   apply(subst(3) nat_0_le[symmetric], simp)
   apply(subst of_nat_less_iff, subst nat_mult_distrib, simp_all, subst nat_power_eq, simp_all)
   apply (meson less_trans one_less_numeral_iff power_less_power_Suc semiring_norm(76) ueval_upper_bound3)
  by (simp add: ueval_upper_bound3)

lemma seval_lower_bound: "-(2 ^ (length xs)) \<le> \<lparr> (x#xs) \<rparr>"
  unfolding seval.simps
  apply (cases x)
   apply simp_all
  by (metis negative_zle of_nat_numeral of_nat_power)

lemma DAplus_eq_len :
  fixes a b :: "bool list"
  shows "length a = length b \<Longrightarrow> length (fst (DA\<^sup>+ a b)) = length b"
  by (induct a b rule: List.list_induct2) auto

lemma DAminus_eq_len :
  fixes a b :: "bool list"
  shows "length a = length b \<Longrightarrow> length (fst (DA\<^sup>- a b)) = length b"
  by (induct a b rule: List.list_induct2) auto

lemma plus_DA :
  fixes a b :: "bool list"
  shows "length a = length b \<Longrightarrow> (fst (DA\<^sup>+ (False # a) (False # b))) = (snd (DA\<^sup>+ a b)) # (fst (DA\<^sup>+ a b))"
proof (induct a b rule: List.list_induct2)
case Nil
  thus ?case by simp
next
case (Cons a as b bs)
  thus ?case
    unfolding DAplus.simps prod.sel(1) prod.sel(2)
    apply (cases a ; cases b ; cases "(snd (DA\<^sup>+ as bs))")
    by simp+
qed

lemma to_from_div_id3: "\<And> a b c. \<lbrakk> \<lbrakk>(\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>b\<rbrakk>\<^sub>N + \<lbrakk>c\<rbrakk>\<^sub>N) div 2\<rbrakk>\<^sub>B \<rbrakk>\<^sub>N = (\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>b\<rbrakk>\<^sub>N + \<lbrakk>c\<rbrakk>\<^sub>N) div 2"
  apply (case_tac a ; case_tac b ; case_tac c) unfolding bool2nat.simps nat2bool.simps by auto

lemma to_from_mod_id3: "\<And> a b c. \<lbrakk> \<lbrakk>(\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>b\<rbrakk>\<^sub>N + \<lbrakk>c\<rbrakk>\<^sub>N) mod 2\<rbrakk>\<^sub>B \<rbrakk>\<^sub>N = (\<lbrakk>a\<rbrakk>\<^sub>N + \<lbrakk>b\<rbrakk>\<^sub>N + \<lbrakk>c\<rbrakk>\<^sub>N) mod 2"
  apply (case_tac a ; case_tac b ; case_tac c) unfolding bool2nat.simps nat2bool.simps by auto

definition sigma :: "bool list \<Rightarrow> bool" ("\<sigma>") where "sigma = hd"

lemma to_from_mod_id2: "\<And> a :: nat. \<lbrakk> \<lbrakk> a mod 2 \<rbrakk>\<^sub>B \<rbrakk>\<^sub>N = a mod 2"
  by (case_tac "a mod 2", simp , case_tac nat) auto

lemma to_from_mod_id: "\<And> a. \<lbrakk> \<lbrakk> a \<rbrakk>\<^sub>N mod 2 \<rbrakk>\<^sub>B = a" by (case_tac a) auto

lemma to_from_mod_id': "\<And> a. \<lbrakk> \<lbrakk> \<lbrakk> a \<rbrakk>\<^sub>N mod 2 \<rbrakk>\<^sub>B \<rbrakk>\<^sub>N = \<lbrakk> a \<rbrakk>\<^sub>N" by (case_tac a) auto

lemma to_from_div_False: "\<And> a. \<lbrakk> \<lbrakk> \<lbrakk> a \<rbrakk>\<^sub>N div 2 \<rbrakk>\<^sub>B \<rbrakk>\<^sub>N = \<lbrakk> False \<rbrakk>\<^sub>N" by (case_tac a) auto

lemma eval_eq_seval: "\<And> a. int \<lbrakk> a \<rbrakk> = \<lparr> False # a \<rparr>" unfolding seval.simps by simp

lemma DAplus_DAminus_compl: "length x = length y \<Longrightarrow> snd (DA\<^sup>+ (fst (DA\<^sup>- x y)) y) = (\<not> (snd (DA\<^sup>- x y)))"
  apply (induct x y rule:List.list_induct2)
   apply simp
  unfolding DAminus.simps DAplus.simps prod.sel
  apply(case_tac x; case_tac y ; case_tac "snd (DA\<^sup>- xs ys)")
  by auto

lemma DAminus_DAplus_compl: "length x = length y \<Longrightarrow> snd (DA\<^sup>- (fst (DA\<^sup>+ x y)) y) = (\<not> (snd (DA\<^sup>+ x y)))"
  apply (induct x y rule:List.list_induct2)
   apply simp
  unfolding DAminus.simps DAplus.simps prod.sel
  apply(case_tac x; case_tac y ; case_tac "snd (DA\<^sup>- xs ys)")
  by auto

definition triple :: "bool \<Rightarrow> bool \<Rightarrow> bool \<Rightarrow> bool list \<Rightarrow> bool list \<Rightarrow> bool list \<Rightarrow> bool" where
  "triple a b r as bs rs = (a = hd as \<and> b = hd bs \<and> r = hd rs)"

definition bot ("\<bottom>") where "bot = False"

lemma bot_unfold[simp]: "\<bottom> = False" unfolding bot_def by simp

definition top ("\<top>") where "top = True"

lemma top_unfold[simp]: "\<top> = True" unfolding top_def by simp

lemma nat_transfer: "\<And>x y. (int x) + (int y) = int (x + y)" by simp

lemma nat_transfer2: "(int x) * (int y) = int (x * y)" by simp

definition compl where "compl xs = map (\<lambda>x. \<not> x) xs"

lemma compl_unfold[simp]: "compl xs = map (\<lambda>x. \<not> x) xs" unfolding compl_def by rule

fun one_list :: "bool list \<Rightarrow> bool list" ("\<zero>\<^sup>\<rightarrow>\<one>") where
  "\<zero>\<^sup>\<rightarrow>\<one> [] = []" |
  "\<zero>\<^sup>\<rightarrow>\<one> [_] = [True]" |
  "\<zero>\<^sup>\<rightarrow>\<one> (_#xs) = False # \<zero>\<^sup>\<rightarrow>\<one> xs"

definition zero ("\<zero>\<^sup>\<rightarrow>") where "\<zero>\<^sup>\<rightarrow> n = replicate n False"

lemma zero_unfold[simp]: "\<zero>\<^sup>\<rightarrow> n = replicate n False" unfolding zero_def by rule

definition one ("\<one>\<^sup>\<rightarrow>") where "\<one>\<^sup>\<rightarrow> n = replicate n True"

lemma one_unfold[simp]: "\<one>\<^sup>\<rightarrow> n = replicate n True" unfolding one_def by rule

definition neg ("\<not>\<^sub>A") where "\<not>\<^sub>A x = (compl x) +\<^sub>A (\<zero>\<^sup>\<rightarrow>\<one> x)"

definition sneg ("\<not>\<^sub>S\<^sub>A") where "\<not>\<^sub>S\<^sub>A x = (compl x) +\<^sub>S\<^sub>A (\<zero>\<^sup>\<rightarrow>\<one> x)"

lemma length_comp_one: "length (compl a) = length (\<zero>\<^sup>\<rightarrow>\<one> a)"
  by(induct a, simp, case_tac a2, simp_all)

lemma length_sneg :
  fixes k :: nat
  assumes "length a = Suc k"
  shows "Suc (length a) = length (\<not>\<^sub>S\<^sub>A a)"
  unfolding sneg_def
  using assms apply(induct a arbitrary: k)
   apply simp_all
  apply(case_tac a2; case_tac a1)
     apply simp_all unfolding splus.simps
  using DAplus_eq_len length_comp_one by auto

lemma length_sminus:
  fixes a b :: "bool list"
  assumes "length a = Suc k"
  shows "length a = length b \<Longrightarrow> length (a -\<^sub>S\<^sub>A b) = Suc (length b)"
  apply (cases a; cases b)
  using assms apply simp
    apply (simp add:assms)+
  using DAminus_eq_len by simp

lemma length_uplus:
  fixes a b :: "bool list"
  assumes "length a = Suc k"
  shows "length a = length b \<Longrightarrow> length (a +\<^sub>Z\<^sub>A b) = Suc (length b)"
  apply (cases a; cases b)
  using assms apply simp
    apply (simp add:assms)+
  by (simp add: DAplus_eq_len uplus_def)

lemma one_seval:
  fixes k :: nat
  assumes "length a = Suc (Suc k)"
  shows "\<lparr> \<zero>\<^sup>\<rightarrow>\<one> a \<rparr> = 1"
  using assms apply (induct a arbitrary: k)
   apply simp
  apply (case_tac a2)
   apply simp+
  apply (case_tac list)
  by auto

(*sign extend*)

fun sx :: "'a list \<Rightarrow> 'a list" where
  "sx [] = undefined" |
  "sx (a#as) = (a#a#as)"

lemma sx_seval: "length (a -\<^sub>A b) = Suc k \<Longrightarrow> \<lparr> a -\<^sub>A b \<rparr> = \<lparr>sx (a -\<^sub>A b)\<rparr>" by (cases "a -\<^sub>A b") simp_all

end


section \<open>Example usage of the ``sturm'' method\<close>
(* Author: Manuel Eberl <eberlm@in.tum.de> *)
theory Sturm_Ex
imports "../Sturm"
begin

text \<open>
  In this section, we give a variety of statements about real polynomials that can b
  proven by the \emph{sturm} method.
\<close>

lemma
 "\<forall>x::real. x^2 + 1 \<noteq> 0"
by sturm

lemma
  fixes x :: real
  shows "x^2 + 1 \<noteq> 0" by sturm

lemma "(x::real) > 1 \<Longrightarrow> x^3 > 1" by sturm

lemma "\<forall>x::real. x*x \<noteq> -1" by sturm

schematic_goal A:
"card {x::real. -0.010831 < x \<and> x < 0.010831 \<and>
    1/120*x^5 + 1/24*x^4 +1/6*x^3 - 49/16777216*x^2 - 17/2097152*x = 0}
  = ?n"
  by sturm

lemma "card {x::real. x^3 + x = 2*x^2 \<and> x^3 - 6*x^2 + 11*x = 6} = 1"
by sturm

schematic_goal "card {x::real. x^3 + x = 2*x^2 \<or> x^3 - 6*x^2 + 11*x = 6} = ?n" by sturm

lemma
  "card {x::real. -0.010831 < x \<and> x < 0.010831 \<and>
     poly [:0, -17/2097152, -49/16777216, 1/6, 1/24, 1/120:] x = 0} = 3"
  by sturm

lemma "\<forall>x::real. x*x \<noteq> 0 \<or> x*x - 1 \<noteq> 2*x" by sturm

lemma "(x::real)*x+1 \<noteq> 0 \<and> (x^2+1)*(x^2+2) \<noteq> 0" by sturm

text\<open>3 examples related to continued fraction approximants to exp: LCP\<close>
lemma fixes x::real
  shows "-7.29347719 \<le> x \<Longrightarrow> 0 < x^5 + 30*x^4 + 420*x^3 + 3360*x^2 + 15120*x + 30240"
by sturm

lemma fixes x::real
  shows "0 < x^6 + 42*x^5 + 840*x^4 + 10080*x^3 + 75600*x\<^sup>2 + 332640*x + 665280"
by sturm

schematic_goal "card {x::real. x^7 + 56*x^6 + 1512*x^5 + 25200*x^4 + 277200*x^3 + 1995840*x^2 + 8648640*x = -17297280} = ?n"
by sturm

end


(* This Isabelle theory is produced using the TIP tool offered at the following website:
     https://github.com/tip-org/tools
   This file was originally provided as part of TIP benchmark at the following website:
     https://github.com/tip-org/benchmarks
   Yutaka Nagashima at CIIRC, CTU changed the TIP output theory file slightly
   to make it compatible with Isabelle2017.*)
  theory TIP_sort_HSortPermutes
  imports "../../Test_Base"
begin

datatype 'a list = nil2 | cons2 "'a" "'a list"

datatype Heap = Node "Heap" "int" "Heap" | Nil

fun toHeap :: "int list => Heap list" where
  "toHeap (nil2) = nil2"
| "toHeap (cons2 y z) = cons2 (Node Nil y Nil) (toHeap z)"

fun hmerge :: "Heap => Heap => Heap" where
  "hmerge (Node z x2 x3) (Node x4 x5 x6) =
   (if x2 <= x5 then Node (hmerge x3 (Node x4 x5 x6)) x2 z else
      Node (hmerge (Node z x2 x3) x6) x5 x4)"
| "hmerge (Node z x2 x3) (Nil) = Node z x2 x3"
| "hmerge (Nil) y = y"

fun hpairwise :: "Heap list => Heap list" where
  "hpairwise (nil2) = nil2"
| "hpairwise (cons2 p (nil2)) = cons2 p (nil2)"
| "hpairwise (cons2 p (cons2 q qs)) =
     cons2 (hmerge p q) (hpairwise qs)"

(*fun did not finish the proof*)
function hmerging :: "Heap list => Heap" where
  "hmerging (nil2) = Nil"
| "hmerging (cons2 p (nil2)) = p"
| "hmerging (cons2 p (cons2 z x2)) =
     hmerging (hpairwise (cons2 p (cons2 z x2)))"
  by pat_completeness auto

fun toHeap2 :: "int list => Heap" where
  "toHeap2 x = hmerging (toHeap x)"

(*fun did not finish the proof*)
function toList :: "Heap => int list" where
  "toList (Node p y q) = cons2 y (toList (hmerge p q))"
| "toList (Nil) = nil2"
  by pat_completeness auto

fun hsort :: "int list => int list" where
  "hsort x = toList (toHeap2 x)"

fun elem :: "'a => 'a list => bool" where
  "elem x (nil2) = False"
| "elem x (cons2 z xs) = ((z = x) | (elem x xs))"

fun deleteBy :: "('a => ('a => bool)) => 'a => 'a list =>
                 'a list" where
  "deleteBy x y (nil2) = nil2"
| "deleteBy x y (cons2 y2 ys) =
     (if (x y) y2 then ys else cons2 y2 (deleteBy x y ys))"

fun isPermutation :: "'a list => 'a list => bool" where
  "isPermutation (nil2) (nil2) = True"
| "isPermutation (nil2) (cons2 z x2) = False"
| "isPermutation (cons2 x3 xs) y =
     ((elem x3 y) &
        (isPermutation
           xs (deleteBy (% (x4 :: 'a) => % (x5 :: 'a) => (x4 = x5)) x3 y)))"

theorem property0 :
  "isPermutation (hsort xs) xs"
  oops

end


header {* Normalizing Derivative *}

theory NDerivative
imports
  Regular_Exp
begin

subsection {* Normalizing operations *}

text {* associativity, commutativity, idempotence, zero *}

fun nPlus :: "'a::order rexp \<Rightarrow> 'a rexp \<Rightarrow> 'a rexp"
where
  "nPlus Zero r = r"
| "nPlus r Zero = r"
| "nPlus (Plus r s) t = nPlus r (nPlus s t)"
| "nPlus r (Plus s t) =
     (if r = s then (Plus s t)
     else if le_rexp r s then Plus r (Plus s t)
     else Plus s (nPlus r t))"
| "nPlus r s =
     (if r = s then r
      else if le_rexp r s then Plus r s
      else Plus s r)"

lemma lang_nPlus[simp]: "lang (nPlus r s) = lang (Plus r s)"
by (induction r s rule: nPlus.induct) auto

text {* associativity, zero, one *}

fun nTimes :: "'a::order rexp \<Rightarrow> 'a rexp \<Rightarrow> 'a rexp"
where
  "nTimes Zero _ = Zero"
| "nTimes _ Zero = Zero"
| "nTimes One r = r"
| "nTimes r One = r"
| "nTimes (Times r s) t = Times r (nTimes s t)"
| "nTimes r s = Times r s"

lemma lang_nTimes[simp]: "lang (nTimes r s) = lang (Times r s)"
by (induction r s rule: nTimes.induct) (auto simp: conc_assoc)

primrec norm :: "'a::order rexp \<Rightarrow> 'a rexp"
where
  "norm Zero = Zero"
| "norm One = One"
| "norm (Atom a) = Atom a"
| "norm (Plus r s) = nPlus (norm r) (norm s)"
| "norm (Times r s) = nTimes (norm r) (norm s)"
| "norm (Star r) = Star (norm r)"

lemma lang_norm[simp]: "lang (norm r) = lang r"
by (induct r) auto

primrec nderiv :: "'a::order \<Rightarrow> 'a rexp \<Rightarrow> 'a rexp"
where
  "nderiv _ Zero = Zero"
| "nderiv _ One = Zero"
| "nderiv a (Atom b) = (if a = b then One else Zero)"
| "nderiv a (Plus r s) = nPlus (nderiv a r) (nderiv a s)"
| "nderiv a (Times r s) =
    (let r's = nTimes (nderiv a r) s
     in if nullable r then nPlus r's (nderiv a s) else r's)"
| "nderiv a (Star r) = nTimes (nderiv a r) (Star r)"

lemma lang_nderiv: "lang (nderiv a r) = Deriv a (lang r)"
by (induction r) (auto simp: Let_def nullable_iff)

lemma deriv_no_occurrence:
  "x \<notin> atoms r \<Longrightarrow> nderiv x r = Zero"
by (induction r) auto

lemma atoms_nPlus[simp]: "atoms (nPlus r s) = atoms r \<union> atoms s"
by (induction r s rule: nPlus.induct) auto

lemma atoms_nTimes: "atoms (nTimes r s) \<subseteq> atoms r \<union> atoms s"
by (induction r s rule: nTimes.induct) auto

lemma atoms_norm: "atoms (norm r) \<subseteq> atoms r"
by (induction r) (auto dest!:subsetD[OF atoms_nTimes])

lemma atoms_nderiv: "atoms (nderiv a r) \<subseteq> atoms r"
by (induction r) (auto simp: Let_def dest!:subsetD[OF atoms_nTimes])

end


theory answer
  imports   "~~/src/HOL/Hoare/Separation"
begin

(*

f()

code:

 unsigned int max = 0;

        if(max < 82)
                {
                        max = 82;
                }

        if(max < 43)
                {
                        max = 43;
                }

*)

(*

Goal:

prove that the variable max equals 82.

*)

lemma "VARS max::nat
{ True }
max:=0;
IF max < 82 THEN max := 82 ELSE SKIP FI;
IF max < 43 THEN max := 43 ELSE SKIP FI
{max = 82}"
  apply vcg
  apply simp
    done

(*

g()

code:
        unsigned int max = 0;
        unsigned int a[3] ;

        a[0] = 45;
        a[1] = 36;
        a[2] = 12;

        unsigned int i = 0;

        while(i < 3)
                {
                        if(max < a[i])
                                {
                                        max = a[i];
                                }
                        i = i+1;
                }

*)

(*

Goal:

prove that the variable max is bigger than  element of array a or equals
 some element of array a.

*)

lemma "VARS (max::nat) (a::nat list) (i::nat)
{length a = 3}
max:=0;
a[0]:=45;
a[1]:=36;
a[2]:=12;
i :=0 ;
WHILE i < 3
INV{if i = 0 then max = 0 else (\<forall>j<i. a!j \<le> max)}
DO
IF max < (a!i) THEN max:=(a!i) ELSE SKIP FI;
i:=i+1
OD
{\<forall>j<i. max\<ge>(a!j)}"
apply vcg
    apply simp
   apply simp
    apply (rule conjI)
    apply (rule impI)
    apply rule
    apply (rule impI)
    apply (erule less_SucE)
     apply auto
  apply (erule less_SucE)
   apply auto
  done

end