I am working on the file "Python/Chapter-05/Calculator-Frame.ipynb".
The first task is:
The function $\texttt{evaluate}(\texttt{self})$ evaluates the expression that is given by the tokens on the mTokenStack.
There are two phases:
1. The first phase is the reading phase. In this phase
   the tokens are removed from the token stack mTokens.
2. The second phase is the evaluation phase.  In this phase,
   the remaining operators on the operator stack mOperators are evaluated.  Note that some operators are already
   evaluated in the reading phase.
We can describe what happens in the reading phase using
rewrite rules that describe how the three stacks mTokens, mArguments and mOperators
are changed in each step.  Here, a step is one iteration of the first while-loop of the function evaluate.
The following rewrite rules are executed until the token stack mTokens is empty.
1. If the token on top of the token stack is an integer, it is removed from the token stack and pushed onto the argument stack.
   The operator stack remains unchanged in this case.
   $$\begin{array}{lc}
     \texttt{mTokens} = \texttt{mTokensRest} + [\texttt{token} ] & \wedge \
     \texttt{isInteger}(\texttt{token}) & \Rightarrow \[0.2cm]
     \texttt{mArguments}' = \texttt{mArguments} + [\texttt{token}] & \wedge \
     \texttt{mTokens}' = \texttt{mTokensRest} & \wedge \
     \texttt{mOperators}' = \texttt{mOperators}
     \end{array}
   $$
   Here, the primed variable $\texttt{mArguments}'$ refers to the argument stack after  $\texttt{token}$
   has been pushed onto it.
In the following rules we implicitly assume that the token on top of the token stack is not an integer but
   rather a parenthesis or a proper operator.  In order to be more concise, we suppress this precondition from the
   following rewrite rules.
2. If the operator stack is empty, the next token is pushed onto the operator stack.
   $$\begin{array}{lc}
     \texttt{mTokens} = \texttt{mTokensRest} + [\texttt{op} ] & \wedge \
     \texttt{mOperators} = [] & \Rightarrow...
Can you write Python code for it?

def evaluate(self):
    "your code here"

Calculator.evaluate = evaluate
del evaluate


I am working on the file "Python/Chapter-05/Calculator-Frame.ipynb".
The first task is:
The function $\texttt{isConstOperator}(o)$ returns True of $o$ is a constant like eor pi.
Can you write Python code for it?

def isConstOperator(op):
    "your code here"


I am working on the file "Python/Chapter-05/Calculator-Frame.ipynb".
The first task is:
The function $\texttt{isLeftAssociative}(o)$ returns True of $o$ is left associative.
Can you write Python code for it?

def isLeftAssociative(op):
    "your code here"


I am working on the file "Python/Chapter-05/Calculator-Frame.ipynb".
The first task is:
The function $\texttt{isWhiteSpace}(s)$ checks whether $s$ contains only blanks and tabulators.
Can you write Python code for it?

def isWhiteSpace(s):
    whitespace = re.compile(r'[ \t]+')
    return whitespace.fullmatch(s)


I am working on the file "Python/Chapter-05/Calculator-Frame.ipynb".
The first task is:
The function $\texttt{precedence}(o)$ calculates the precedence of the operator $o$.
Can you write Python code for it?

def precedence(op):
    "your code here"


I am working on the file "Python/Chapter-05/Calculator-Frame.ipynb".
The first task is:
The function $\texttt{toInt}(s)$ tries to convert the string $s$ to an integer.  If this works out, the integer is returned.  Otherwise, the string $s$ is returned unchanged.
Can you write Python code for it?

def toInt(s):
    try:
        return int(s)
    except ValueError:
        return s


I am working on the file "Python/Chapter-05/Calculator-Frame.ipynb".
The first task is:
The module math provides a number of mathematical functions like exp, sin, log etc.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
Given an operator $o$, the expression $\texttt{precedence}(o)$ returns the precedence of the operator
$o$.  If $o_1$ and $o_2$ are different operators and the precedence of $\texttt{o}_1$ is at least as high than the
precedence of $\texttt{o}_2$, then the expression
$$ a \;\texttt{o}_1\; b \;\texttt{o}_2\; c $$
should be evaluated as
$$ (a \;\texttt{o}_1\; b) \;\texttt{o}_2\; c. $$
Otherwise, the expression $a \;\texttt{o}_1\; b \;\texttt{o}_2\; c$ should be evaluated as
$$ a \;\texttt{o}_1\; (b \;\texttt{o}_2\; c). $$
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
The Shunting Yard Algorithm (Operator Precedence Parsing)
The function $\texttt{toInt}(s)$ tries to convert the string $s$ to an integer.  If this works out, the integer is returned.  Otherwise, the string $s$ is returned unchanged.
Can you write Python code for it?

def toInt(s):
    try:
        return int(s)
    except ValueError:
        return s

toInt('123')

toInt('**')


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
The class Calculator supports three member variables:
  - the token stack mTokenStack
  - the operator stack mOperators
  - the argument stack mArguments
The constructor takes a string that is tokenized and pushes the tokens onto the token stack such that the first token is on top of the token stack.
Can you write Python code for it?

class Calculator:
    def __init__(self, s):
        self.mTokens    = createStack(tokenize(s))
        self.mOperators = Stack()
        self.mArguments = Stack()


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
The expression isLeftAssociative}(o) is True iff the operator $o$
associates to the left.  If $o$ associates to the right, this expression is False.
If the operator $o$ is unknown, evaluation of the expression results
in an error.
Can you write Python code for it?

def isLeftAssociative(o):
    if o in { '+', '-', '*', '/', '%' }:
        return True
    if o in { '**' }:
        return False
    assert False, f'unknown operator {o}'


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
The function $\texttt{evaluate}(\texttt{self})$ evaluates the expression that is given by the tokens on the mTokenStack.
There are two phases:
1. The first phase is the reading phase. In this phase
   the tokens are removed from the token stack mTokens.
2. The second phase is the evaluation phase.  In this phase,
   the remaining operators on the operator stack mOperators are evaluated.  Note that some operators are already
   evaluated in the reading phase.
We can describe what happens in the reading phase using
rewrite rules that describe how the three stacks mTokens, mArguments and mOperators
are changed in each step.  Here, a step is one iteration of the first while-loop of the function evaluate.
The following rewrite rules are executed until the token stack mTokens is empty.
1. If the token on top of the token stack is an integer, it is removed from the token stack and pushed onto the argument stack.
   The operator stack remains unchanged in this case.
   $$\begin{array}{lc}
     \texttt{mTokens} = \texttt{mTokensRest} + [\texttt{token} ] & \wedge \
     \texttt{isInteger}(\texttt{token}) & \Rightarrow \[0.2cm]
     \texttt{mArguments}' = \texttt{mArguments} + [\texttt{token}] & \wedge \
     \texttt{mTokens}' = \texttt{mTokensRest} & \wedge \
     \texttt{mOperators}' = \texttt{mOperators}
     \end{array}
   $$
   Here, the primed variable $\texttt{mArguments}'$ refers to the argument stack after  $\texttt{token}$
   has been pushed onto it.
In the following rules we implicitly assume that the token on top of the token stack is not an integer but
   rather a parenthesis or a proper operator.  In order to be more concise, we suppress this precondition from the
   following rewrite rules.
2. If the operator stack is empty, the next token is pushed onto the operator stack.
   $$\begin{array}{lc}
     \texttt{mTokens} = \texttt{mTokensRest} + [\texttt{op} ] & \wedge \
     \texttt{mOperators} = [] & \Rightarrow...
Can you write Python code for it?

def evaluate(self):
    while not self.mTokens.isEmpty():
        print(self) # only for debugging
        nextOp = self.mTokens.top(); self.mTokens.pop()
        if isinstance(nextOp, int):
            self.mArguments.push(nextOp)
            continue
        if self.mOperators.isEmpty():
            self.mOperators.push(nextOp)
            continue
        if nextOp == "(":
            self.mOperators.push(nextOp)
            continue
        stackOp = self.mOperators.top()
        if stackOp == "(" and nextOp == ")":
            self.mOperators.pop()
            continue
        if nextOp == ")":
            self.popAndEvaluate()
            self.mTokens.push(nextOp)
            continue
        if stackOp == '(':
            self.mOperators.push(nextOp)
            continue
        if evalBefore(stackOp, nextOp):
            self.popAndEvaluate()
            self.mTokens.push(nextOp)
        else:
            self.mOperators.push(nextOp)
    while not self.mOperators.isEmpty():
        print(self) # only for debugging
        self.popAndEvaluate()
    print(self)
    return self.mArguments.top()

Calculator.evaluate = evaluate
del evaluate


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
The function evalBefore(o1, o2) receives to strings representing arithmetical operators.  It returns True if the operator $o_1$ should be evaluated before the operator $o_2$ in an arithmetical expression of the form $a \;\texttt{o}_1\; b \;\texttt{o}_2\; c$.  In order to determine whether $o_1$ should be evaluated before $o_2$ it uses the precedence and the associativity of the operators.
Its behavior is specified by the following rules:
- $\texttt{precedence}(o_1) > \texttt{precedence}(o_2) \rightarrow \texttt{evalBefore}(\texttt{o}_1, \texttt{o}_2) = \texttt{True}$,
- $o_1 = o_2 \rightarrow \texttt{evalBefore}(\texttt{o}_1, \texttt{o}_2) = \texttt{isLeftAssociative}(o_1)$,
- $\texttt{precedence}(o_1) = \texttt{precedence}(o_2) \wedge o_1 \not= o_2 \rightarrow \texttt{evalBefore}(\texttt{o}_1, \texttt{o}_2) = \texttt{True}$,
- $\texttt{precedence}(o_1) < \texttt{precedence}(o_2) \rightarrow \texttt{evalBefore}(\texttt{o}_1, \texttt{o}_2) = \texttt{False}$.
Can you write Python code for it?

def evalBefore(stackOp, nextOp):
    if precedence(stackOp) > precedence(nextOp):
        return True
    if stackOp == nextOp:
        return isLeftAssociative(stackOp)
    if precedence(stackOp) == precedence(nextOp) and stackOp != nextOp:
        return True
    if precedence(stackOp) < precedence(nextOp):
        return False
    assert False, f'incomplete case distinction in evalBefore({stackOp}, {nextOp})'

%run Stack.ipynb


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
The method $\texttt{popAndEvaluate}(\texttt{self})$ removes the two topmost numbers $\texttt{rhs}$ and $\texttt{lhs}$ from the argument stack and
removes the topmost operator $\texttt{op}$ from the operator stack.  It applies the operator $\texttt{op}$ to these numbers
by computing $\texttt{lhs} \;\texttt{op}\; \texttt{rhs}$
and then pushes this value back on the argument stack.
Can you write Python code for it?

def popAndEvaluate(self):
    rhs = self.mArguments.top(); self.mArguments.pop()
    lhs = self.mArguments.top(); self.mArguments.pop()
    op  = self.mOperators.top(); self.mOperators.pop()
    result = None
    if op == '+':
        result = lhs + rhs
    if op == '-':
        result = lhs - rhs
    if op == '*':
        result = lhs * rhs
    if op == '/':
        result = lhs // rhs
    if op == '%':
        result = lhs % rhs
    if op == '**':
        result = lhs ** rhs
    assert result != None, f'ERROR: *** Unknown Operator *** "{op}"'
    self.mArguments.push(result)

Calculator.popAndEvaluate = popAndEvaluate
del popAndEvaluate

C = Calculator('1*3+4**2-1-2')

C.evaluate()


I am working on the file "Python/Chapter-05/Calculator.ipynb".
The first task is:
The module re provides support for regular expressions.  These are needed for
tokenizing a string.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Dual-Pivot-Quicksort-Array.ipynb".
The first task is:
Again, we sort a million integers.  This time, many of the integers have the same value.
Can you write Python code for it?

%%timeit
k = 1_000_000
L = [ rnd.randrange(1000) for x in range(k) ]
sort(L)


I am working on the file "Python/Chapter-05/Dual-Pivot-Quicksort-Array.ipynb".
The first task is:
The function $\texttt{quickSort}(a, b, L)$ sorts the sublist $L[a:b+1]$ in place.
Can you write Python code for it?

def quickSort(a, b, L):
    if b <= a:
        return  # at most one element, nothing to do
    x1, x2 = L[a], L[b]
    if x1 > x2:
        swap(a, b, L)
    m1, m2 = partition(a, b, L)  # m1 and m2 are the split indices
    quickSort(a, m1 - 1, L)
    if L[m1] != L[m2]:
        quickSort(m1 + 1, m2 - 1, L)
    quickSort(m2 + 1, b, L)


I am working on the file "Python/Chapter-05/Dual-Pivot-Quicksort-Array.ipynb".
The first task is:
The function $\texttt{sort}(L)$ sorts the list $L$ in place.
Can you write Python code for it?

def sort(L):
    quickSort(0, len(L) - 1, L)


I am working on the file "Python/Chapter-05/Dual-Pivot-Quicksort-Array.ipynb".
The first task is:
The function $\texttt{swap}(x, y, L)$ swaps the elements at index $x$ and $y$ in $L$.
Can you write Python code for it?

def swap(x, y, L):
    L[x], L[y] = L[y], L[x]


I am working on the file "Python/Chapter-05/Dual-Pivot-Quicksort-Array.ipynb".
The first task is:
The function $\texttt{testSort}(n, k)$ generates $n$ random lists of length $k$, sorts them, and checks whether the output is sorted and contains the same elements as the input.
Can you write Python code for it?

def testSort(n, k):
    for i in range(n):
        L = [ rnd.randrange(2*k) for x in range(k) ]
        oldL = L[:]
        sort(L)
        isOrdered(L)
        sameElements(oldL, L)
        assert len(L) == len(oldL)
        print('.', end='')
    print()
    print("All tests successful!")

%%time
testSort(100, 20_000)


I am working on the file "Python/Chapter-05/Insertion-Sort.ipynb".
The first task is:
Insertion Sort
The function sort is specified via two equations:

$\mathtt{sort}([]) = []$
$\mathtt{sort}\bigl([x] + R\bigr) =
      \mathtt{insert}\bigl(x, \mathtt{sort}(R)\bigr)$

This is most easily implemented in a recursive fashion.
Can you write Python code for it?

def sort(L):
    if L == []:
        return []
    x, *R = L
    return insert(x, sort(R))


I am working on the file "Python/Chapter-05/Insertion-Sort.ipynb".
The first task is:
The auxiliary function insert is specified as follows:

$\mathtt{insert}(x,[]) = [x]$
$x \preceq y \rightarrow \mathtt{insert}\bigl(x, [y] + R\bigr) = [x,y] + R$
$\neg x \preceq y \rightarrow
      \mathtt{insert}\bigl(x, [y] + R\bigr) = [y] + \mathtt{insert}(x,R)$

Again, a recursive implementation is straightforward.
Can you write Python code for it?

def insert(x, L):
    if L == []:
        return [x]
    y, *R = L
    if x <= y:
        return [x, y] + R
    else:
        return [y] + insert(x, R)

insert(5, [1, 3, 4, 7, 9])

sort([7, 8, 11, 12, 2, 5, 3, 7, 9])


I am working on the file "Python/Chapter-05/Merge-Sort-Array.ipynb".
The first task is:
Merge Sort: A Recursive, Array Based Implementation
The function $\texttt{sort}(L)$ sorts the list $L$ in place using merge sort.
It takes advantage of the fact that, in Python, lists are stored internally as arrays.
The function sort is a wrapper for the function merge_sort.  Its sole purpose is to allocate the auxiliary array A,
which has the same size as the array holding L.
Can you write Python code for it?

def sort(L):
    A = L[:]
    mergeSort(L, 0, len(L), A)


I am working on the file "Python/Chapter-05/Merge-Sort-Array.ipynb".
The first task is:
The function isOrdered(L) checks that the list L is sorted in ascending order.
Can you write Python code for it?

def isOrdered(L):
    for i in range(len(L) - 1):
        assert L[i] <= L[i+1], f'{L} not sorted at index {i}'


I am working on the file "Python/Chapter-05/Merge-Sort-Array.ipynb".
The first task is:
The function merge takes five arguments.
  - L      is a list,
  - start  is an integer such that $\texttt{start}  \in {0, \cdots, \texttt{len}(L)-1 }$,
  - middle is an integer such that $\texttt{middle} \in {0, \cdots, \texttt{len}(L)-1 }$,
  - end    is an integer such that $\texttt{end}    \in {0, \cdots, \texttt{len}(L)-1 }$,
  - A      is a list of the same length as L.
Furthermore, the indices start, middle, and end have to satisfy the following:
$$ 0 \leq \texttt{start} < \texttt{middle} < \texttt{end} \leq \texttt{len}(L) $$
The function assumes that the sublists L[start:middle] and L[middle:end] are already sorted.
The function merges these sublists so that when the call returns the sublist L[start:end]
is sorted.  The last argument A is used as auxiliary memory.
Can you write Python code for it?

def merge(L, start, middle, end, A):
    A[start:end] = L[start:end]
    idx1 = start
    idx2 = middle
    i    = start
    while idx1 < middle and idx2 < end:
        if A[idx1] <= A[idx2]:
            L[i]  = A[idx1]
            idx1 += 1
        else:
            L[i]  = A[idx2]
            idx2 += 1
        i += 1
    if idx1 < middle:
        L[i:end] = A[idx1:middle]
    if idx2 < end:
        L[i:end] = A[idx2:end]

L = [7, 8, 11, 12, 2, 5, 3, 7, 9, 3, 2]
sort(L)
L


I am working on the file "Python/Chapter-05/Merge-Sort-Array.ipynb".
The first task is:
The function sameElements(L, S) returns Trueif the lists L and S contain the same elements and, furthermore, each
element $x$ occurring in L occurs in S the same number of times it occurs in L.
Can you write Python code for it?

def sameElements(L, S):
    assert Counter(L) == Counter(S), f'{Counter(L)} != {Counter(S)}'


I am working on the file "Python/Chapter-05/Merge-Sort-Array.ipynb".
The first task is:
We import the class Counter from the module collections.  This module provides us with a dictionary that keeps count
how many times an item occurs in a list.
Can you write Python code for it?

from collections import Counter

Counter(['a', 'b', 'a', 'b', 'c', 'a'])

def demo():
    L = [ rnd.randrange(1, 99+1) for n in range(1, 19+1) ]
    print("L = ", L)
    S = L[:]
    sort(S)
    print("S = ", S)
    print(Counter(L))
    print(Counter(S))
    print(Counter(L) == Counter(S))

demo()


I am working on the file "Python/Chapter-05/Merge-Sort-Iterative.ipynb".
The first task is:
Python offers a predefined function sorted that can be used to sort a list of numbers.
Let us see how it compares to our implementation.
Can you write Python code for it?

%%timeit
k = 1_000_000
L = [ rnd.randrange(2*k) for x in range(k) ]
S = sorted(L)

help(sorted)


I am working on the file "Python/Chapter-05/Merge-Sort-Iterative.ipynb".
The first task is:
Testing
Can you write Python code for it?

import random as rnd
from collections import Counter

def demo():
    L = [ rnd.randrange(1, 100) for n in range(1, 20) ]
    print("L = ", L)
    S = L[:]
    sort(S)
    print("S = ", S)
    print(Counter(L))
    print(Counter(S))
    print(Counter(L) == Counter(S))

demo()


I am working on the file "Python/Chapter-05/Merge-Sort-Iterative.ipynb".
The first task is:
The function isOrdered(L) checks that the list L is sorted in ascending order.
Can you write Python code for it?

def isOrdered(L):
    for i in range(len(L) - 1):
        assert L[i] <= L[i+1]


I am working on the file "Python/Chapter-05/Merge-Sort-Iterative.ipynb".
The first task is:
The function merge takes five arguments.
  - L      is a list,
  - start  is an integer such that $\texttt{start}  \in {0, \cdots, \texttt{len}(L)-1 }$,
  - middle is an integer such that $\texttt{middle} \in {0, \cdots, \texttt{len}(L)-1 }$,
  - end    is an integer such that $\texttt{end}    \in {0, \cdots, \texttt{len}(L)-1 }$,
  - A      is a list of the same length as L.
Furthermore, the indices start, middle and end have to satisfy the following inequations:
$$ 0 \leq \texttt{start} < \texttt{middle} < \texttt{end} \leq \texttt{len}(L) $$
The function assumes that the sublists L[start:middle] and L[middle:end] are already sorted.
The function merges these sublists so that when the call returns the sublist L[start:end]
is sorted.  The last argument A is used as auxiliary memory.
Can you write Python code for it?

def merge(L, start, middle, end, A):
    A[start:end] = L[start:end]
    idx1 = start
    idx2 = middle
    i    = start
    while idx1 < middle and idx2 < end:
        if A[idx1] <= A[idx2]:
            L[i]  = A[idx1]
            idx1 += 1
        else:
            L[i]  = A[idx2]
            idx2 += 1
        i += 1
    if idx1 < middle:
        L[i:end] = A[idx1:middle]
    if idx2 < end:
        L[i:end] = A[idx2:end]


I am working on the file "Python/Chapter-05/Merge-Sort-Iterative.ipynb".
The first task is:
The function mergeSort is called with 2 arguments.
  - The first parameter $\texttt{L}$ is the list that is to be sorted.
  - The second parameter $\texttt{A}$ is used as an auxiliary array.  This array is needed
    as temporary storage and is required to have the same size as the list $\texttt{L}$.
The implementation uses two loops:
* The outer while loop sorts sublists of length n. Before the $\texttt{n}^{\mbox{th}}$ iteration of the outer while
  loop, all sublists of the form L[n*k:n*(k+1)] are sorted.  After the $\texttt{n}^{\mbox{th}}$ iteration, all
  sublists of the form L[2*n*k:2*n*(k+1)] are sorted.
* The inner while loop merges the sublists L[n*k:n*(k+1)] and L[n*(k+1):n*(k+2)] for even values of k.
Can you write Python code for it?

def mergeSort(L, A):
    n = 1
    while n < len(L):
        k = 0
        while n * k + n < len(L):
            top = min(n * k + 2 * n, len(L))
            merge(L, n * k, n * k + n, top, A)
            k += 2
        n *= 2


I am working on the file "Python/Chapter-05/Merge-Sort-Iterative.ipynb".
The first task is:
The function sameElements(L, S) returns Trueif the lists L and S contain the same elements and, furthermore, each
element $x$ occurring in L occurs in S the same number of times it occurs in L.
Can you write Python code for it?

def sameElements(L, S):
    assert Counter(L) == Counter(S)


I am working on the file "Python/Chapter-05/Merge-Sort.ipynb".
The first task is:
Merge Sort
In order to sort a list $L$ using merge sort we proceed as follows:

If $L$ has less than two elements, then $L$ is already sorted.  Therefore we have:
   $$ #L < 2 \rightarrow \mathtt{sort}(L) = L $$
Otherwise, the list $L$ is split into two lists that have approximately the same size.
   These lists are sorted recursively.  Then, the sorted lists are merged in a way that the
   resulting list is sorted:
   $$ #L \geq 2 \wedge \mathtt{n} := #L \rightarrow \mathtt{sort}(L) =
         \mathtt{merge}\bigl(\mathtt{sort}\bigl(\texttt{L[:n//2]}\bigr),
         \mathtt{sort}\bigl(\texttt{L[n//2:]}\bigr)\bigr)
   $$
   Here, $\texttt{L[:n//2]}$ is the first part of the list, while
   $\texttt{L[n//2:]}$ is the second part.  If the length of $L$ is even, both part have the same    number of elements, otherwise the second part has one element more than the first part.
Can you write Python code for it?

def sort(L):
    n = len(L)
    if n < 2:
        return L
    L1, L2 = L[:n//2], L[n//2:]
    return merge(sort(L1), sort(L2))


I am working on the file "Python/Chapter-05/Merge-Sort.ipynb".
The first task is:
The function isOrdered(L) checks that the list L is sorted ascendingly.
Can you write Python code for it?

def isOrdered(L):
    for i in range(len(L) - 1):
        assert L[i] <= L[i+1], f'{L} not ordered at {i}'


I am working on the file "Python/Chapter-05/Merge-Sort.ipynb".
The first task is:
The function sameElements(L, S) returns Trueif the lists L and S contain the same elements and, furthermore, each
element $x$ occurring in L occurs in S the same number of times it occurs in L.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Merge-Sort.ipynb".
The first task is:
We import the class Counter from the module collections.  This module provides us with a dictionary that keeps count
how many times an item occurs in a list.
Can you write Python code for it?

from collections import Counter

Counter(['a', 'b', 'a', 'b', 'c', 'a'])

def demo():
    L = [ rnd.randrange(1, 99+1) for n in range(1, 19+1) ]
    print("L = ", L)
    S = L[:]
    S = sort(S)
    print("S = ", S)
    print(Counter(L))
    print(Counter(S))
    print(Counter(L) == Counter(S))

demo()


I am working on the file "Python/Chapter-05/Merge-Sort.ipynb".
The first task is:
We still need to specify how two sorted lists $L_1$ and $L_2$ are merged in a way that the resulting
list is also sorted.

If the list $L_1$ is empty, the result is $L_2$:
   $$ \mathtt{merge}([], L_2) = L_2 $$
If the list $L_2$  is empty, the result is $L_1$:
   $$ \mathtt{merge}(L_1, []) = L_1 $$
Otherwise, $L_1$ must have the form $[x_1] + R_1$ and $L_2$ has the form $[x_2] + R_2$.
   Then there is a case distinction with respect to the comparison of $x_1$ and $x_2$:
$x_1 \leq x_2$.
     In this case, we merge $R_1$ and $L_2$ and put $x_1$ at the beginning of this list:
     $$x_1 \leq x_2 \rightarrow \mathtt{merge}\bigl([x_1] + R_1, [x_2] + R_2\bigr) =
     \bigl[x_1\bigr] + \mathtt{merge}\bigl(R_1,[x_2] + R_2\bigr)
     $$
$\neg (x_1 \leq x_2)$.
     In this case, we merge $L_1$ and $R_2$ and put $x_2$ at the beginning of this list:
     $$\neg (x_1 \preceq x_2) \rightarrow \mathtt{merge}\bigl([x_1] + R_1, [x_2] + R_2\bigr)
       = \bigl[x_2 \bigr] + \mathtt{merge}\bigl([x_1] + R_1, R_2\bigr)
     $$
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Quick-Select-Array.ipynb".
The first task is:
An Array-Based Implementation of QuickSelect
The function quickSelect takes two arguments:
* L is a list of numbers,
* k is a natural number such that k <= len(L).
The function call quickSelect(L, k) returns a list of length k that contains the k smallest
elements of L, i.e. it satisfies the following specification:
- len(quickSelect(L, k)) = k,
- set(quickSelect(L, k)) = set(sorted(L)[:k]).
Can you write Python code for it?

def select(L, k):
    quickSelect(0, len(L) - 1, L, k)
    return L[:k]


I am working on the file "Python/Chapter-05/Quick-Select-Array.ipynb".
The first task is:
Performance Testing
Can you write Python code for it?

k = 1_000_000
L = [ rnd.randrange(1000 * k) for x in range(k) ]

%%time
S = select(L, 1500)


I am working on the file "Python/Chapter-05/Quick-Select-Array.ipynb".
The first task is:
The function $\texttt{partition}(\texttt{start}, \texttt{end}, L)$ returns an index $m$ into the list $L$ and
regroups the elements of $L$ such that after the function returns the following holds:

$\forall i \in {\texttt{start}, \cdots, m-1} : L[i] \leq L[m]$,
$\forall i \in { m+1, \cdots, \texttt{end} }  : L[m] <    L[i]$,
$L[m] = \texttt{pivot}$.

Here, $\texttt{pivot}$ is the element that is at the index $\texttt{end}$ at the time of the invocation
of the function, i.e. we have

$L[\texttt{end}] = \texttt{pivot}$

at invocation time.
The for-loop of partition maintains the following invariants:

$\forall i \in {\texttt{start}, \cdots, \texttt{left} } : L[i] \leq \texttt{pivot}$,
$\forall i \in {\texttt{left}+1, \cdots, \texttt{idx}-1} : \texttt{pivot} < L[i]$,
$L[\texttt{end}] = \texttt{pivot}$.

This algorithm has been suggested by Nico Lomuto.  It is not the most efficient implementation of partition, but
it is easier to understand than the algorithm given by Tony Hoare that uses two separate loops.
Can you write Python code for it?

def partition(start, end, L):
    pivot = L[end]
    left  = start - 1
    for idx in range(start, end):
        if L[idx] <= pivot:
            left += 1
            swap(left, idx, L)
    swap(left + 1, end, L)
    return left + 1


I am working on the file "Python/Chapter-05/Quick-Select-Array.ipynb".
The first task is:
The function test_list(n, m) generates n random lists L of length m and checks whether
quickSelect(L, k) satisfies its specification.  Here, the number k is a random number less
than or equal to the length of the list.
Can you write Python code for it?

def testSelect(n, m):
    for i in range(n):
        L = [ rnd.randrange(2*m) for x in range(m) ]
        k = rnd.randrange(m)
        C = L[:]
        S = select(C, k)
        assert len(S) == k, f'{S} = select({L}, {k})'
        assert set(sorted(L)[:k]) == set(S)
        print('.', end='')
    print()
    print("All tests successful!")

%%time
testSelect(100, 100_000)


I am working on the file "Python/Chapter-05/Quick-Select-Array.ipynb".
The first task is:
This is quite a lot faster than to sort the list and extract the first $k$ elements from the sorted list.
Can you write Python code for it?

k = 1_000_000
L = [ rnd.randrange(1000 * k) for x in range(k) ]

%%time
S = sorted(L)[:1500]


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
An Array-Based Implementation of Quick-Sort
The function $\texttt{sort}(L)$ sorts the list $L$ in place.
Can you write Python code for it?

def sort(L):
    quickSort(0, len(L) - 1, L)


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
Finally, we test the worst case and sort 5000 integers that are sorted ascendingly.  Since quicksort is recursive, we have to increment the recursion limit of Python, because otherwise we would get an error telling us that we exceed the maximum recursion depth.
Can you write Python code for it?

import sys

sys.setrecursionlimit(20000)
sys.version

L = list(range(5000))

%%time
sort(L)


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
If we shuffle the list that is to be sorted before calling sort, the worst case behaviour disappears.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
Next, we sort a hundred thousand integers.  This time, many of the integers have the same value.
Can you write Python code for it?

L = [ rnd.randrange(100) for x in range(100_000) ]

%%time
sort(L)


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
Next, we sort a million random integers.
Can you write Python code for it?

%%time
k = 1_000_000
L = [ rnd.randrange(1000) for x in range(k) ]
sort(L)


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
The function $\texttt{partition}(\texttt{start}, \texttt{end}, L)$ returns an index $m$ into the list $L$ and
regroups the elements of $L$ such that after the function returns the following holds:

$\forall i \in {\texttt{start}, \cdots, m-1} : L[i] \leq L[m]$,
$\forall i \in { m+1, \cdots, \texttt{end} }  : L[m] <    L[i]$,
$L[m] = \texttt{pivot}$.

Here, pivot is the element that is at the index end at the time of the invocation
of the function, i.e. we have

$L[\texttt{end}] = \texttt{pivot}$

at invocation time.
The for-loop of partition maintains the following invariants:

$\forall i \in {\texttt{start}, \cdots, \texttt{left} } : L[i] \leq \texttt{pivot}$,
$\forall i \in {\texttt{left}+1, \cdots, \texttt{idx}-1} : \texttt{pivot} < L[i]$,
$L[\texttt{end}] = \texttt{pivot}$.

These invariants are depicted below:

This algorithm has been suggested by Nico Lomuto.  It is not the most efficient implementation of partition, but
it is easier to understand than the algorithm given by Tony Hoare that uses two separate loops.
Can you write Python code for it?

#@logging
def partition(start, end, L):
    pivot = L[end]
    left  = start - 1
    for idx in range(start, end):
        if L[idx] <= pivot:
            left += 1
            swap(left, idx, L)
    swap(left + 1, end, L)
    return left + 1


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
The function $\texttt{testSort}(n, k)$ generates $n$ random lists of length $k$, sorts them, and checks whether the output is sorted and contains the same elements as the input.
Can you write Python code for it?

def testSort(n, k):
    for i in range(n):
        L = [ rnd.randrange(2*k) for x in range(k) ]
        oldL = L[:]
        sort(L)
        isOrdered(L)
        sameElements(oldL, L)
        assert len(L) == len(oldL)
        print('.', end='')
    print()
    print("All tests successful!")

%%time
testSort(100, 20000)


I am working on the file "Python/Chapter-05/Quick-Sort-Array.ipynb".
The first task is:
The function quickSort(start, end, L) sorts the sublist L[start:end+1] in place.
Can you write Python code for it?

def quickSort(start, end, L):
    if end <= start:
        return  # at most one element, nothing to do
    m = partition(start, end, L)  # m is the split index
    quickSort(start, m - 1, L)
    quickSort(m + 1, end  , L)


I am working on the file "Python/Chapter-05/Radix-Sort.ipynb".
The first task is:
Radix Sort
As radix sort is based on counting sort, we have to start our implementation of radix sort by defining the function countingSort that we have already discussed previously.  The easiest way to do this is by using the %run magic that Juypter notebooks provide.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Radix-Sort.ipynb".
The first task is:
Testing
Can you write Python code for it?

import random as rnd

def demo():
    L = [ rnd.randrange(1, 1000) for n in range(1, 16) ]
    print("L = ", L)
    S = radixSort(L)
    print("S = ", S)

demo()

def isOrdered(L):
    for i in range(len(L) - 1):
        assert L[i] <= L[i+1], f'L = {L}, i = {i}'

from collections import Counter

def sameElements(L, S):
    assert Counter(L) == Counter(S)


I am working on the file "Python/Chapter-05/Radix-Sort.ipynb".
The first task is:
The function $\texttt{extractByte}(n, k)$ takes a natural number $n < 2^{32}$ and a number $k\in {1,2,3,4}$ as arguments.  It returns the $k$-th byte of $n$.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Radix-Sort.ipynb".
The first task is:
The function $\texttt{testSort}(n, k)$ generates $n$ random lists of length $k$, sorts them, and checks whether the output is sorted and contains the same elements as the input.
Can you write Python code for it?

def testSort(n, k):
    for i in range(n):
        L = [ rnd.randrange(2**31) for x in range(k) ]
        oldL = L[:]
        L = radixSort(L)
        isOrdered(L)
        sameElements(oldL, L)
        print('.', end='')
    print()
    print("All tests successful!")

%%time
testSort(100, 20000)

%%timeit
k = 1_000_000
L = [ rnd.randrange(2**32-1) for x in range(k) ]
S = radixSort(L)


I am working on the file "Python/Chapter-05/Selection-Sort.ipynb".
The first task is:
Selection Sort
The algorithm selection sort is specified via two equations:

If $L$ is empty, $\texttt{sort}(L)$ is the empty list:
    $$ \mathtt{sort}([]) = [] $$
Otherwise, we compute the smallest element of the list $L$ and we remove the first occurrence of
    this element from $L$.  Next, the remaining list is sorted recursively.  Finally, the smallest
    element is added to the front of the sorted list:
    $$ L \not= [] \rightarrow \mathtt{sort}\bigl(L\bigr) =
       \bigl[\texttt{min}(L)\bigr] + \mathtt{sort}\bigl(\mathtt{delete}(\texttt{min}(L), L)\bigr)
    $$
Can you write Python code for it?

def sort(L):
    if L == []:
        return []
    x = min(L)
    return [x] + sort(delete(x, L))


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
By defining the function S.__repr__() for stack objects, we can print stacks in Jupyter notebooks without calling the function print.
Can you write Python code for it?

Stack.__repr__ = Stack.__str__

S

for i in range(10):
    print(S.top())
    S.pop()
    print(S)


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
Implementing a Stack Class
First, we define an empty class Stack.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
Next, we add the method push to the class Stack.  The method $\texttt{push}(S, e)$ pushes $e$ onto the stack $S$.
Can you write Python code for it?

def push(S, e):
    S.mStackElements += [e]


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
Testing
The method createStack(L) takes a list L and pushes all of its elements on a newly created stack, which is
then returned.
Can you write Python code for it?

def createStack(L):
    S = Stack()
    for x in L:
        S.push(x)
        print(S)
    return S

S = createStack(range(10))

S


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
The method S.copy() creates a shallow copy of the given stack, i.e. the copy contains the same objects as
the stack S.
Can you write Python code for it?

def copy(S):
    C = Stack()
    C.mStackElements = S.mStackElements[:]
    return C

Stack.copy = copy
del copy


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
The method S.toStr() converts a stack S into a string.
Can you write Python code for it?

def toStr(S):
    C = S.copy()
    result = C._convert()
    dashes = "-" * len(result)
    return '\n'.join([dashes, result, dashes])

Stack.__str__ = toStr
del toStr


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
The method convert converts a stack into a string.
Can you write Python code for it?

def convert(S):
    if S.isEmpty():
        return '|'
    top = S.top()
    S.pop()
    return S._convert() + ' ' + str(top) + ' |'

Stack._convert = convert
del convert


I am working on the file "Python/Chapter-05/Stack.ipynb".
The first task is:
We add this method to the class Stack.  Since we add it under the name __init__, this method will be the
constructor.  Furthermore, to keep the environment tidy, we delete the function stack.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort-Array.ipynb".
The first task is:
3-Way Merge Sort: An Array-Based Implementation
The function $\texttt{sort}(L)$ sorts the list $L$ in place using merge sort.
It takes advantage of the fact that, in Python, lists are stored internally as arrays.
The function sort is a wrapper for the function merge_sort.  Its sole purpose is to allocate the auxiliary array A, which has the same size as the array holding L.
Can you write Python code for it?

def sort(L):
    A = L[:]
    mergeSort(L, 0, len(L), A)


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort-Array.ipynb".
The first task is:
Testing
We import the module random in order to be able to create lists of random numbers that are then sorted.
Can you write Python code for it?

import random as rnd

from collections import Counter


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort-Array.ipynb".
The first task is:
The function isOrdered(L) checks that the list L is sorted in ascending order.
Can you write Python code for it?

def isOrdered(L):
    for i in range(len(L) - 1):
        assert L[i] <= L[i+1], f'{L} not sorted at index {i}'


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort-Array.ipynb".
The first task is:
The function merge3 takes six arguments.
  - L      is a list,
  - start  is an integer such that $\texttt{start} \in {0, \cdots, \texttt{len}(L)-1 }$,
  - left   is an integer such that $\texttt{left}  \in {0, \cdots, \texttt{len}(L)-1 }$,
  - right  is an integer such that $\texttt{right} \in {0, \cdots, \texttt{len}(L)-1 }$,
  - end    is an integer such that $\texttt{end}   \in {0, \cdots, \texttt{len}(L)-1 }$,
  - A      is a list of the same length as L.
Furthermore, the indices start, left, right, and end have to satisfy the following:
$$ 0 \leq \texttt{start} \leq \texttt{left} \leq \texttt{right} \leq \texttt{end} \leq \texttt{len}(L) $$
The function assumes that the sublists L[start:left], L[left:right], and L[right:end] are already
sorted. The function merges these sublists so that when the call returns the sublist L[start:end]
is sorted.  The last argument A is used as auxiliary memory.
Can you write Python code for it?

def merge3(L, start, left, right, end, A):
    A[start:end] = L[start:end]
    idx1 = start
    idx2 = left
    idx3 = right
    i    = start
    while idx1 < left and idx2 < right and idx3 < end:
        if A[idx1] <= A[idx2]:
            if A[idx1] <= A[idx3]:
                L[i]  = A[idx1]
                idx1 += 1
            else:
                L[i]  = A[idx3]
                idx3 +=1
        elif A[idx2] <= A[idx3]:
            L[i]  = A[idx2]
            idx2 += 1
        else:
            L[i]  = A[idx3]
            idx3 += 1
        i += 1
    if idx1 == left:  # first list empty, merge second list and third list
        while idx2 < right and idx3 < end:
            if A[idx2] <= A[idx3]:
                L[i]  = A[idx2]
                idx2 += 1
            else:
                L[i]  = A[idx3]
                idx3 += 1
            i += 1
    elif idx2 == right: # second list empty, merge first list and third list
        while idx1 < left and idx3 < end:
            if A[idx1] <= A[idx3]:
                L[i]  = A[idx1]
                idx1 += 1
            else:
                L[i]  = A[idx3]
                idx3 += 1
            i += 1
    elif idx3 == end:  # third list empty, merge first list and second list
        while idx1 < left and idx2 < right:
            if A[idx1] <= A[idx2]:
                L[i]  = A[idx1]
                idx1 += 1
            else:
                L[i]  = A[idx2]
                idx2 += 1
            i += 1
    if idx1 < left:  # second list and third list are empty
        L[i:end] = A[idx1:left]
    if idx2 < right: # first list and third list are empty
        L[i:end] = A[idx2:right]
    if idx3 < end:   # first list and second list are empty
        L[i:end] = A[idx3:end]

L = [7, 8, 11, 12, 2, 5, 3, 7, 9, 3, 2]
sort(L)
L


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort-Array.ipynb".
The first task is:
The function mergeSort is called with 4 arguments.
  - The first parameter L is the list that is to be sorted.
    However, the task of mergeSort is not to sort the entire list L but only
    the part of L that is given as L[start:end].
  - Hence, the parameters start and end are indices specifying the
    subarray that needs to be sorted.
  - The final parameter A is used as an auxiliary array.  This array is needed
    as temporary storage and is required to have the same size as the list L.
Can you write Python code for it?

def mergeSort(L, start, end, A):
    if end - start < 2:
        return
    left  = start +     (end - start) // 3
    right = start + 2 * (end - start) // 3
    mergeSort(L, start, left , A)
    mergeSort(L, left,  right, A)
    mergeSort(L, right, end  , A)
    merge3(L, start, left, right, end, A)


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort.ipynb".
The first task is:
Due to the recursion limit in Python we can only sort lists of length 2000.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort.ipynb".
The first task is:
Testing
We import the module random in order to be able to create lists of random numbers that are used for testing.
Can you write Python code for it?


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort.ipynb".
The first task is:
The function $\texttt{testSort}(n, k)$ generates $n$ random lists of length $k$, sorts them, and checks whether the output is sorted and contains the same elements as the input.
Can you write Python code for it?

def testSort(n, k):
    for i in range(n):
        L = [ rnd.randrange(2*k) for x in range(k) ]
        S = sort(L)
        isOrdered(S)
        sameElements(L, S)
        print('.', end='')
    print()
    print("All tests successful!")


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort.ipynb".
The first task is:
The function sameElements(L, S) returns Trueif the lists L and S contain the same elements and, furthermore, each
element $x$ occurring in L occurs in S the same number of times it occurs in L.
Can you write Python code for it?

def sameElements(L, S):
    assert Counter(L) == Counter(S)


I am working on the file "Python/Chapter-05/Three-Way-Merge-Sort.ipynb".
The first task is:
Three-Way Merge Sort
In order to sort a list $L$ using merge sort we proceed as follows:

If $L$ has less than two elements, then $L$ is already sorted.  Therefore we have:
   $$ #L < 2 \rightarrow \mathtt{sort}(L) = L $$
Otherwise, the list $L$ is split into three lists that have approximately the same size.
   These lists are sorted recursively.  Then, the sorted lists are merged in a way that the
   resulting list is sorted:
   $$ #L \geq 2 \rightarrow \mathtt{sort}(L) =
         \mathtt{merge3}\bigl(\mathtt{sort}\bigl(\texttt{L[:n//3]}\bigr),
                              \mathtt{sort}\bigl(\texttt{L[n//3:2$\cdot$n//3]}\bigr),
                              \mathtt{sort}\bigl(\texttt{L[2$\cdot$n//3:]}\bigr)\bigr)
   $$
   Here, L[:n//3] is the first part of the list, while
   L[n//3:2*n//3]} is the second part and L[2*n//3:] is the last part.
Can you write Python code for it?

def sort(L):
    n = len(L)
    if n < 2:
        return L
    L1, L2, L3 = L[:n//3], L[n//3:2*n//3], L[2*n//3:]
    return merge3(sort(L1), sort(L2), sort(L3))


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Three}(L, k_L, \texttt{Three}(A, k_1, B, k_2, C), k_R, \texttt{One}(D)).\texttt{repair}() =
     \texttt{Three}(L, k_L, \texttt{Two}(A, k_1, B), k_2, \texttt{Two}(C, k_R, D))$

The equation specifying the method repairfor the class Nilis:

$\texttt{Nil}.\texttt{repair}() = \texttt{Nil}$
Can you write Python code for it?

def _repair(self):
    "your code here"

Nil._repair = _repair


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Three}(L, k_L, \texttt{Two}(A, k, B), k_R, \texttt{One}(C)).\texttt{repair}() = \texttt{Two}(L, k_L, \texttt{Three}(A, k, B, k_R, C))$
Can you write Python code for it?

toDotList([Three(One(Tree('A')), 'kL', Three(Tree('B'), 'k1', Tree('C'), 'k2', Tree('D')), 'kR', Tree('R')),
           Method('.repair()'),
           Three(Two(Tree('A'), 'kL', Tree('B')), 'k1', Two(Tree('C'), 'k2', Tree('D')), 'kR', Tree('R'))
          ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Three}(\texttt{One}(A), k_L, \texttt{Three}(B, k_1, C, k_2, D), k_R, R).\texttt{repair}() =
     \texttt{Three}(\texttt{Two}(A, k_L, B), k_1, \texttt{Two}(C, k_2, D), k_R, R)$
Can you write Python code for it?

toDotList([Three(Three(Tree('A'), 'k1', Tree('B'), 'k2', Tree('C')), 'kL', One(Tree('D')), 'kR', Tree('R')),
           Method('.repair()'),
           Three(Two(Tree('A'), 'k1', Tree('B')), 'k2', Two(Tree('C'), 'kL', Tree('D')), 'kR', Tree('R'))
          ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Three}(\texttt{Two}(A, k, B), k_L, \texttt{One}(C), k_R, R).\texttt{repair}() = \texttt{Two}(\texttt{Three}(A, k, B, k_L, C), k_R, R)$
Can you write Python code for it?

toDotList([Three(Tree('L'), 'kL', Two(Tree('A'), 'k', Tree('B')), 'kR', One(Tree('C'))),
           Method('.repair()'),
           Two(Tree('L'), 'kL', Three(Tree('A'), 'k', Tree('B'), 'kR', Tree('C')))
          ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Three}\bigl(\texttt{Four}(l_1,k_1,m_l,k_2,m_r,k_3,r_1), k_l, m, k_r, r\bigr).\texttt{restore}() =
     \texttt{Four}\bigl(\texttt{Two}(l_1, k_1, m_l), k_2, \texttt{Two}(m_r, k_3, r_1), k_l, m, k_r, r\bigr) $
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Three}\bigl(l, k_l, \texttt{Four}(l_1,k_1,m_l,k_2,m_r,k_3,r_1), k_r, r\bigr).\texttt{restore}() =
     \texttt{Four}\bigl(l, k_l, \texttt{Two}(l_1, k_1, m_l), k_2, \texttt{Two}(m_r, k_3, r_1), k_r, r\bigr) $
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Three}\bigl(l, k_l, m, k_r, \texttt{Four}(l_1,k_1,m_l,k_2,m_r,k_3,r_1)\bigr).\texttt{restore}() =
     \texttt{Four}\bigl(l, k_l, m, k_r, \texttt{Two}(l_1, k_1, m_l), k_2, \texttt{Two}(m_r, k_3, r_1)\bigr) $

Below I have collected all the equations specifying the implementation of restore for 3-nodes.
  - $\texttt{Three}\bigl(\texttt{Four}(l_1,k_1,m_l,k_2,m_r,k_3,r_1), k_l, m, k_r, r\bigr).\texttt{restore}() =
     \texttt{Four}\bigl(\texttt{Two}(l_1, k_1, m_l), k_2, \texttt{Two}(m_r, k_3, r_1), k_l, m, k_r, r\bigr) $
  - $\texttt{Three}\bigl(l, k_l, \texttt{Four}(l_1,k_1,m_l,k_2,m_r,k_3,r_1), k_r, r\bigr).\texttt{restore}() =
     \texttt{Four}\bigl(l, k_l, \texttt{Two}(l_1, k_1, m_l), k_2, \texttt{Two}(m_r, k_3, r_1), k_r, r\bigr) $
  - $\texttt{Three}\bigl(l, k_l, m, k_r, \texttt{Four}(l_1,k_1,m_l,k_2,m_r,k_3,r_1)\bigr).\texttt{restore}() =
     \texttt{Four}\bigl(l, k_l, m, k_r, \texttt{Two}(l_1, k_1, m_l), k_2, \texttt{Two}(m_r, k_3, r_1)\bigr) $
If neither of the child nodes of a 3-node is a 4-node, the node is returned unchanged.
Can you write Python code for it?

def _restore(self):
    l, kl, m, kr, r = self._extract()
    if l.isFour():
        l1, k1, ml, k2, mr, k3, r1 = l._extract()
        return Four(Two(l1, k1, ml), k2, Two( mr, k3, r1), kl, m, kr, r)
    if m.isFour():
        l1, k1, ml, k2, mr, k3, r1 = m._extract()
        return Four(l, kl, Two( l1, k1, ml), k2, Two( mr, k3, r1), kr, r)
    if r.isFour():
        l1, k1, ml, k2, mr, k3, r1 = r._extract()
        return Four(l, kl, m, kr, Two( l1, k1, ml), k2, Two( mr, k3, r1))
    return self

Three._restore = _restore


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Two}(\texttt{One}(A), k, \texttt{Three}(B, k_1, C, k_2, D)).\texttt{repair}() = \texttt{Two}(\texttt{Two}(A, k, B), k_1, \texttt{Two}(C, k_2, D))$
Can you write Python code for it?

toDotList([Two(Three(Tree('A'), 'k1', Tree('B'), 'k2', Tree('C')), 'k', One(Tree('D'))),
           Method('.repair()'),
           Two(Two(Tree('A'), 'k1', Tree('B')), 'k2', Two(Tree('C'), 'k', Tree('D')))
          ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Two}(\texttt{One}(L_1), k, \texttt{Two}(M_1, k_1, R_1)).\texttt{repair}() = \texttt{One}(\texttt{Three}(L_1, k, M_1, k_1, R_1))$
Can you write Python code for it?

toDotList([Two(Two(Tree('L1'), 'k1', Tree('M1')), 'k', One(Tree('R1'))), Method('.repair()'), One(Three(Tree('L1'), 'k1', Tree('M1'), 'k', Tree('R1')))])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Two}(\texttt{Three}(A, k_1, B, k_2, C), k, \texttt{One}(D)).\texttt{repair}() = \texttt{Two}(\texttt{Two}(A, k_1, B), k_2, \texttt{Two}(C, k, D))$
Can you write Python code for it?

toDotList([Three(One(Tree('A')), 'kL', Two(Tree('B'), 'k', Tree('C')), 'kR', Tree('R')),
           Method('.repair()'),
           Two(Three(Tree('A'), 'kL', Tree('B'), 'k', Tree('C')), 'kR', Tree('R'))
          ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Two}(\texttt{Two}(L_1, k_1, M_1), k, \texttt{One}(R_1)).\texttt{repair}() = \texttt{One}(\texttt{Three}(L_1, k_1, M_1, k, R_1))$
Can you write Python code for it?

toDotList([Two(One(Tree('A')), 'k', Three(Tree('B'), 'k1', Tree('C'), 'k2', Tree('D'))),
           Method('.repair()'),
           Two(Two(Tree('A'), 'k', Tree('B')), 'k1', Two(Tree('C'), 'k2', Tree('D')))
          ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$\texttt{Two}\bigl(\texttt{Four}(l_1,k_l,m_l,k_m,m_r,k_r,r_1), k, r\bigr).\texttt{restore}() =
     \texttt{Three}\bigl(\texttt{Two}(l_1, k_l, m_l), k_m, \texttt{Two}(m_r, k_r, r_1), k, r\bigr) $
Can you write Python code for it?

toDotList([Two(Tree('l'), 'k', Four(Tree('l1'),'kl',Tree('ml'),'km', Tree('mr'),'kr',Tree('r1'))),
           Method('.restore()'),
           Three(Tree('l'), 'k', Two(Tree('l1'),'kl',Tree('ml')), 'km', Two(Tree('mr'),'kr',Tree('r1')))])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$k_1 < k_2 \rightarrow \texttt{Two}(\texttt{Nil},k_1,\texttt{Nil}).\texttt{ins}(k_2) = \texttt{Three}(\texttt{Nil},k_1,\texttt{Nil},k_2,\texttt{Nil})$
Can you write Python code for it?

toDotList([Method('k2 < k1:'), Two(Nil(), 'k1', Nil()), Method('.ins(k2)'), Three(Nil(), 'k2', Nil(), 'k1', Nil()) ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$k_1 < k_2 \wedge l \not= \texttt{Nil} \wedge r \not= \texttt{Nil} \rightarrow \texttt{Two}(l,k_1,r).\texttt{ins}(k_2) = \texttt{Two}(l,k_1,r.\texttt{ins}(k)).\texttt{restore}()$
Can you write Python code for it?

toDotList([Method('k2 < k1:'), Two(Tree('l'), 'k1', Tree('r')), Method('.ins(k2)'), Two(Tree('l.ins(k2)'), 'k1', Tree('r')) ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
$k_2 < k_1 \wedge l \not= \texttt{Nil} \wedge r \not= \texttt{Nil} \rightarrow \texttt{Two}(l,k_1,r).\texttt{ins}(k_2) = \texttt{Two}(l.\texttt{ins}(k),k_1,r).\texttt{restore}()$

I have collected all of these equations below:
  - $\texttt{Two}(l,k,r).\texttt{ins}(k) = \texttt{Two}(l,k,r)$
  - $k_1 < k_2 \rightarrow \texttt{Two}(\texttt{Nil},k_1,\texttt{Nil}).\texttt{ins}(k_2) = \texttt{Three}(\texttt{Nil},k_1,\texttt{Nil},k_2,\texttt{Nil})$
  - $k_2 < k_1 \rightarrow \texttt{Two}(\texttt{Nil},k_1,\texttt{Nil}).\texttt{ins}(k_2) = \texttt{Three}(\texttt{Nil},k_2,\texttt{Nil},k_1,\texttt{Nil})$
  - $k_1 < k_2 \wedge l \not= \texttt{Nil} \wedge r \not= \texttt{Nil} \rightarrow \texttt{Two}(l,k_1,r).\texttt{ins}(k_2) = \texttt{Two}(l,k_1,r.\texttt{ins}(k)).\texttt{restore}()$
  - $k_2 < k_1 \wedge l \not= \texttt{Nil} \wedge r \not= \texttt{Nil} \rightarrow \texttt{Two}(l,k_1,r).\texttt{ins}(k_2) = \texttt{Two}(l.\texttt{ins}(k),k_1,r).\texttt{restore}()$
Using these equations, the implementation of ins is straightforward.
Can you write Python code for it?

def _ins(self, key):
    l, k, r = self._extract()
    if k == key:
        return self
    elif l.isNil() and r.isNil():
        if key < k:
            return Three(Nil(), key, Nil(), k, Nil())
        else:
            return Three(Nil(), k, Nil(), key, Nil())
    elif not l.isNil() and not r.isNil():
        if key < k:
            self.mLeft = l._ins(key)._restore()
        else:
            self.mRight = r._ins(key)._restore()
        return self
    assert False, f'Unbalanced node {self}'

Two._ins = _ins


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
2-3 Trees
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Finally, we specify the method shrink.  The only class where the implementation of shrink is nontrivial is the class One.
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
For the class Three the method delMin is specified via the following equations:
  - $\texttt{Three}(\texttt{Nil}, k_L, \texttt{Nil}, k_R, \texttt{Nil}).\texttt{delMin}() = \langle\texttt{Two}(\texttt{Nil}, k_R, \texttt{Nil}), k_L\rangle$
  - $L \not= \texttt{Nil} \wedge M \not= \texttt{Nil} \wedge R \not= \texttt{Nil} \wedge L.\texttt{delMin}() = \langle L', k_M\rangle \rightarrow
    \texttt{Three}(L, k_L, M, k_R, R).\texttt{delMin}() = \langle\texttt{Three}(L', k_L, M, k_R, R).\texttt{repair}(), k_M\rangle$
Can you write Python code for it?

def _delMin(self):
    "your code here"

Three._delMin = _delMin


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
For the class Two the method del is specified as follows:
  - $\texttt{Two}(\texttt{Nil}, k, \texttt{Nil}).\texttt{del}(k) = \texttt{One}(\texttt{Nil})$
  - $k \not= e \rightarrow \texttt{Two}(\texttt{Nil}, k, \texttt{Nil}).\texttt{del}(e) = \texttt{Two}(\texttt{Nil}, k, \texttt{Nil})$

$L \not= \texttt{Nil} \wedge R \not= \texttt{Nil} \wedge R.\texttt{delMin}() = \langle R', kM\rangle \rightarrow
     \texttt{Two}(L, k, R).\texttt{del}(k) = \texttt{Two}(L, kM, R').\texttt{repair}()$
$e < k \rightarrow
     \texttt{Two}(L, k, R).\texttt{del}(e) = \texttt{Two}(L.\texttt{del}(e), k, R).\texttt{repair}()$
$k < e \rightarrow
     \texttt{Two}(L, k, R).\texttt{del}(e) = \texttt{Two}(L, k, R.\texttt{del}(e)).\texttt{repair}()$
Can you write Python code for it?

def _del(self, e):
    "your code here"

Two._del = _del


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Given a 2-node $t$ and a key $k$, the method $t.\texttt{member}(k)$ checks whether the key $k$ occurs in $t$.  It is specified as follows:
  - $\texttt{Two}(l,k,r).\texttt{member}(k) = \texttt{True}$,
  - $k_1 < k_2 \rightarrow \texttt{Two}(l,k_1,r).\texttt{member}(k_2) = r.\texttt{member}(k_2)$,
  - $k_1 > k_2 \rightarrow \texttt{Two}(l,k_1,r).\texttt{member}(k_2) = l.\texttt{member}(k_2)$.
Can you write Python code for it?

def member(self, key):
    l, k, r = self._extract()
    if k == key:
        return True
    elif key < k:
        return l.member(key)
    elif key > self.mKey:
        return r.member(key)

Two.member = member


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Given a 3-node $t$ and a key $k$, the method $t.\texttt{member}(k)$ checks whether the key $k$ occurs in $t$.  It is specified as follows:
  - $k = k_l \vee k = k_r \rightarrow \texttt{Three}(l,k_l,m,k_r,r).\texttt{member}(k) = \texttt{True}$,
  - $k < k_l \rightarrow \texttt{Three}(l,k_l,m,k_r,r).\texttt{member}(k) = l.\texttt{member}(k)$,
  - $k_l < k < k_r \rightarrow \texttt{Three}(l,k_l,m,k_r,r).\texttt{member}(k) = m.\texttt{member}(k)$,
  - $k_r < k \rightarrow \texttt{Three}(l,k_l,m,k_r,r).\texttt{member}(k) = r.\texttt{member}(k)$.
Can you write Python code for it?

def member(self, key):
    l, kL, m, kR, r = self._extract()
    if key == kL or key == kR:
        return True
    if key < kL:
        return l.member(key)
    if kL < key < kR:
        return m.member(key)
    if kR < key:
        return self.mRight.member(key)

Three.member = member


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Graphically, the node $\texttt{Two}(l, k, r)$ is represented as shown below:
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Let's generate 2-3 tree with random keys.
Can you write Python code for it?

import random as rnd

t = Nil()
for k in range(30):
    k = rnd.randrange(100)
    t = t.insert(k)
t.toDot()


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Mathematically, this can be written as follows:
$$ \texttt{Nil}.\texttt{ins}(k) = \texttt{Two}(\texttt{Nil}, k, \texttt{Nil}) $$
The implementation is straightforward as shown below.
Can you write Python code for it?

def _ins(self, k):
    return Two(Nil(), k, Nil())

Nil._ins = _ins


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Methods of the Class Four
The method extract returns the member variables stored in a 4-node.
Can you write Python code for it?

def _extract(self):
    return self.mLeft, self.mKeyL, self.mMiddleL, self.mKeyM, self.mMiddleR, self.mKeyR, self.mRight

Four._extract = _extract


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Methods of the Class Nil
The empty tree does not contain any keys:
$$ \texttt{Nil}.\texttt{member}(k) = \texttt{False} $$
Can you write Python code for it?

def member(self, k):
    return False

Nil.member = member


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Methods of the Class Three
The method extract returns the member variables stored in a 3-node.
Can you write Python code for it?

def _extract(self):
    return self.mLeft, self.mKeyL, self.mMiddle, self.mKeyR, self.mRight

Three._extract = _extract


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Methods of the Class Two
The method extract returns the member variables stored in a 2-node.  This is usefull to shorten the code since when we use this method, we don't have to prefix all variable names with self..
Can you write Python code for it?

def _extract(self):
    return self.mLeft, self.mKey, self.mRight

Two._extract = _extract


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Testing
Can you write Python code for it?

m = Nil()
m.isNil()
m.toDot()

m = m.insert("anton")
m.toDot()

m = m.insert("hugo" )
m.toDot()

m = m.insert("gustav")
m.toDot()

m = m.insert("jens")
m.toDot()

m = m.insert("hubert")
m.toDot()

m = m.insert("andre")
m.toDot()

m = m.insert("philipp")
m.toDot()

m = m.insert("rene")
m.toDot()

m = m.insert("walter")
m.toDot()


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The class Method is not used in the implementation of 2-3 trees. It is only used for displaying method calls in equations. It is displayed as a rectangle containing the string that is stored in the member variable mLabel.
Can you write Python code for it?

class Method(TwoThreeTree):
    def __init__(self, label):
        TwoThreeTree.__init__(self)
        self.mLabel = label

    def __str__(self):
        return self.mLabel

    def isMethod(self):
        return True


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The class Nil represents an empty tree.  It has no member variables of its own.
Can you write Python code for it?

class Nil(TwoThreeTree):
    def __init__(self):
        TwoThreeTree.__init__(self)

    def isNil(self):
        return True

    def __str__(self):
        return 'Nil()'


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The class Three represents a 3-node of the form $\texttt{Three}(l, k_L, m, k_R, r)$.  It manages 5 member variables:
  - mLeft   is the left subtree $l$,
  - mKeyL   is the left key $k_L$,
  - mMiddle is the middle subtree $m$,
  - mKeyR   is the right key $k_r$,
  - mRight  is the right subtree.
Can you write Python code for it?

class Three(TwoThreeTree):
    def __init__(self, left, keyL, middle, keyR, right):
        TwoThreeTree.__init__(self)
        self.mLeft   = left
        self.mKeyL   = keyL
        self.mMiddle = middle
        self.mKeyR   = keyR
        self.mRight  = right

    def __str__(self):
        return self._make_string(['mLeft', 'mKeyL', 'mMiddle', 'mKeyR', 'mRight'])

    def isThree(self):
        return True


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The class Tree is not used in the implementation of 2-3 trees.  It is only used for displaying abstract subtrees in equations.
It is displayed as a triangle containing the string that is stored in the member variable mName.
Can you write Python code for it?

class Tree(TwoThreeTree):
    def __init__(self, name):
        TwoThreeTree.__init__(self)
        self.mName = name

    def __str__(self):
        return self.mName

    def isTree(self):
        return True


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The equation specifying the method repair for the class One is:

$\texttt{One}(t).\texttt{repair}() = \texttt{One}(t)$
Can you write Python code for it?

def _repair(self):
    "your code here"

One._repair = _repair


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The equations specifying the method repair for the class Three:
  - $\texttt{Three}(\texttt{Nil}, k_L, \texttt{Nil}, k_R, \texttt{Nil}).\texttt{repair}() = \texttt{Three}(\texttt{Nil}, k_L, \texttt{Nil}, k_R, \texttt{Nil})$
  - $\texttt{Three}(\texttt{One}(A), k_L, \texttt{Two}(B, k, C), k_R, R).\texttt{repair}() = \texttt{Two}(\texttt{Three}(A, k_L, B, k, C), k_R, R)$
  - $\texttt{Three}(\texttt{Two}(A, k, B), k_L, \texttt{One}(C), k_R, R).\texttt{repair}() = \texttt{Two}(\texttt{Three}(A, k, B, k_L, C), k_R, R)$
  - $\texttt{Three}(L, k_L, \texttt{Two}(A, k, B), k_R, \texttt{One}(C)).\texttt{repair}() = \texttt{Two}(L, k_L, \texttt{Three}(A, k, B, k_R, C))$

$\texttt{Three}(\texttt{One}(A), k_L, \texttt{Three}(B, k_1, C, k_2, D), k_R, R).\texttt{repair}() =
     \texttt{Three}(\texttt{Two}(A, k_L, B), k_1, \texttt{Two}(C, k_2, D), k_R, R)$
$\texttt{Three}(\texttt{Three}(A, k_1, B, k_2, C), k_L, \texttt{One}(D), k_R, R).\texttt{repair}() =
     \texttt{Three}(\texttt{Two}(A, k_1, B), k_2, \texttt{Two}(C, k_L, D), k_R, R)$
$\texttt{Three}(L, k_L, \texttt{Three}(A, k_1, B, k_2, C), k_R, \texttt{One}(D)).\texttt{repair}() =
     \texttt{Three}(L, k_L, \texttt{Two}(A, k_1, B), k_2, \texttt{Two}(C, k_R, D))$
Can you write Python code for it?

def _repair(self):
    "your code here"

Three._repair = _repair


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The equations specifying the method repair for the class Two are:
  - $\texttt{Two}(\texttt{Nil}, k, \texttt{Nil}).\texttt{repair}() = \texttt{Two}(\texttt{Nil}, k, \texttt{Nil})$
  - $\texttt{Two}(\texttt{One}(L_1), k, \texttt{Two}(M_1, k_1, R_1)).\texttt{repair}() = \texttt{One}(\texttt{Three}(L_1, k, M_1, k_1, R_1))$
  - $\texttt{Two}(\texttt{Two}(L_1, k_1, M_1), k, \texttt{One}(R_1)).\texttt{repair}() = \texttt{One}(\texttt{Three}(L_1, k_1, M_1, k, R_1))$

$\texttt{Two}(\texttt{One}(A), k, \texttt{Three}(B, k_1, C, k_2, D)).\texttt{repair}() = \texttt{Two}(\texttt{Two}(A, k, B), k_1, \texttt{Two}(C, k_2, D))$
$\texttt{Two}(\texttt{Three}(A, k_1, B, k_2, C), k, \texttt{One}(D)).\texttt{repair}() = \texttt{Two}(\texttt{Two}(A, k_1, B), k_2, \texttt{Two}(C, k, D))$
Can you write Python code for it?

def _repair(self):
    "your code here"

Two._repair = _repair


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The function $\texttt{toDotList}(\texttt{NodeList})$ takes a list of trees and displays them one by one.
Can you write Python code for it?

def toDotList(NodeList):
    dot = gv.Digraph(node_attr={'shape': 'record', 'style': 'rounded'})
    nodeDict = {}
    for node in NodeList:
        node._collectIDs(nodeDict)
    for n, t in nodeDict.items():
        if t.isNil():
            dot.node(str(n), label='', shape='point')
        elif t.isOne():
            dot.node(str(n), label='', shape='point')
        elif t.isTwo():
            dot.node(str(n), label=str(t.mKey))
        elif t.isThree():
            dot.node(str(n), label=str(t.mKeyL) + '|' + str(t.mKeyR))
        elif t.isFour():
            dot.node(str(n), label=str(t.mKeyL) + '|' + str(t.mKeyM) + '|' + str(t.mKeyR))
        elif t.isTree():
            dot.node(str(n), label=str(t.mName), shape='triangle', style='solid')
        elif t.isMethod():
            dot.node(str(n), label=str(t.mLabel), shape='rectangle', style='dotted')
        else:
            assert False, f'toDotList: Unknown node {str(t)}'
    for n, t in nodeDict.items():
        if t.isOne():
            dot.edge(str(n), str(t.mChild.getID()))
        if t.isTwo():
            dot.edge(str(n), str(t.mLeft .getID()))
            dot.edge(str(n), str(t.mRight.getID()))
        if t.isThree():
            dot.edge(str(n), str(t.mLeft  .getID()))
            dot.edge(str(n), str(t.mMiddle.getID()))
            dot.edge(str(n), str(t.mRight .getID()))
        if t.isFour():
            dot.edge(str(n), str(t.mLeft   .getID()))
            dot.edge(str(n), str(t.mMiddleL.getID()))
            dot.edge(str(n), str(t.mMiddleR.getID()))
            dot.edge(str(n), str(t.mRight  .getID()))
    return dot


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The function call $t.\texttt{restore}()$ takes a 2-3-4 tree $t$ that has at most one 4-node. This 4-node has to be a child
of the root.  It returns a 2-3-4 tree that has at most one 4-node.  This 4-node has to be the root node.
Graphically, it is specified as shown below.
Can you write Python code for it?

toDotList([Two(Four(Tree('l1'),'kl',Tree('ml'),'km', Tree('mr'),'kr',Tree('r1')), 'k', Tree('r')),
           Method('.restore()'),
           Three(Two(Tree('l1'),'kl',Tree('ml')), 'km', Two(Tree('mr'),'kr',Tree('r1')), 'k', Tree('r'))])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The function grow turns a 4-node into 3 2-nodes.  Graphically, it is specified as follows:
Can you write Python code for it?

toDotList([Four(Tree('l'),'kl', Tree('ml'), 'km', Tree('mr'), 'kr', Tree('r')),
           Method('.grow()'),
           Two(Two(Tree('l'),'kl', Tree('ml')), 'km', Two(Tree('mr'), 'kr', Tree('r')))
          ])


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The function make_string is a helper function used to shorten the implementation of __str__.
  - obj is the object that is to be rendered as a string
  - attributes is a list of those member variables that are used to produce the string
Can you write Python code for it?

def _make_string(self, attributes):
        # map the function __str__ to all attributes and join them with a comma
        name = self.__class__.__name__
        return f"{name}({', '.join(map(str, [getattr(self, at) for at in attributes]))})"

TwoThreeTree._make_string = _make_string


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The method $t.\texttt{collectIDs}(d)$ takes a tree $t$ and a dictionary $d$ and updates the dictionary so that the following holds:
$$ d[\texttt{id}] = n \quad \mbox{for every node $n$ in $t$.} $$
Here, $\texttt{id}$ is the unique identifier of the node $n$, i.e. $d$ associates the identifiers with the corresponding nodes.
Can you write Python code for it?

def _collectIDs(self, nodeDict):
    nodeDict[self.getID()] = self
    if self.isOne():
        self.mChild._collectIDs(nodeDict)
    elif self.isTwo():
        self.mLeft ._collectIDs(nodeDict)
        self.mRight._collectIDs(nodeDict)
    elif self.isThree():
        self.mLeft  ._collectIDs(nodeDict)
        self.mMiddle._collectIDs(nodeDict)
        self.mRight ._collectIDs(nodeDict)
    elif self.isFour():
        self.mLeft   ._collectIDs(nodeDict)
        self.mMiddleL._collectIDs(nodeDict)
        self.mMiddleR._collectIDs(nodeDict)
        self.mRight  ._collectIDs(nodeDict)

TwoThreeTree._collectIDs = _collectIDs


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The method $t.\texttt{delMin}()$ removes the smallest key $k_M$ from the tree $t$ and returns a pair $\langle t', k_M \rangle$
where $t'$ is the tree that results from deleting $k_M$ in $t$.  The nodes $t'$ and $t$ have the same height.
For the class Two the method delMin is specified via the following equations:
  - $\texttt{Two}(\texttt{Nil}, k, \texttt{Nil}).\texttt{delMin}() = \langle\texttt{One}(\texttt{Nil}), k\rangle$
  - $L \not= \texttt{Nil} \wedge R \not= \texttt{Nil} \wedge L.\texttt{delMin}() = \langle L', k_M\rangle \rightarrow
    \texttt{Two}(L, k, R).\texttt{delMin}() = \langle\texttt{Two}(L', k, R).\texttt{repair}(), k_M \rangle$
Can you write Python code for it?

def _delMin(self):
    "your code here"

Two._delMin = _delMin


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
The method restore returns a 4-node unchanged.
Can you write Python code for it?

def _restore(self):
    return self

Four._restore = _restore


I am working on the file "Python/Chapter-06/2-3-Trees-Frame.ipynb".
The first task is:
Ths notebook presents 2-3 trees.  We define these trees inductively as follows:
  - $\texttt{Nil}$ is a 2-3 tree that represents the empty set.
  - $\texttt{Two}(l, k, r)$ is a 2-3 tree provided that
      - $l$ is a 2-3 tree,
      - $k$ is a key,
      - $r$ is a 2-3 tree,
      - all keys stored in $l$ are less than k and all keys stored in $r$ are bigger than $k$, that is we have
        $$ l < k < r. $$
      - $l$ and $r$ have the same height.
      A node of the form  $\texttt{Two}(l, k, r)$ is called a 2-node.  Except for the fact
      that there is no value, a 2-node is
      interpreted in the same way as we have interpreted the term $\texttt{Node}(k, v, l, r)$.
  - $\texttt{Three}(l, k_l, m, k_r, r)$ is a 2-3 tree provided
      - $l$, $m$, and $r$ are 2-3 trees,
      - $k_l$ and $k_r$ are keys,
      - $l < k_l < m < k_r < r$,
      - $l$, $m$, and $r$ have the same height.
      A node of the form  $\texttt{Three}(l, k_l, m, k_r, r)$ is called a 3-node.
In order to keep 2-3 trees balanced when inserting new keys we use a fourth constructor of the form
$\texttt{Four}(l,k_1,m_l, k_2, m_r, k_3, r)$.
A term of the form $\texttt{Four}(l,k_1,m_l, k_2, m_r, k_3, r)$ is a 2-3-4 tree iff
  - $l$, $m_l$, $m_r$, and $r$ are 2-3 trees,
  - $k_l$, $k_m$, and $k_r$ are keys,
  - $l < k_l < m_l < k_m < m_r < k_r < r$,
  - $l$, $m_l$, $m_r$, and $r$ all have the same height.
Nodes of this form are called 4-nodes and the key $k_m$ is called the middle key.
Trees containing 2-nodes, 3-node, and 4-nodes are called 2-3-4 trees.
In order to keep 2-3 trees balanced when deleting keys we use a fifth constructor of the form
$\texttt{One}(t)$.
A term of the form $\texttt{One}(t)$ is a 1-2-3 tree iff $t$ is a 2-3 tree.
The class TwoThreeTree is a superclass for constructing the nodes of 2-3-4 trees.  It has one static variable sNodeCount.  This variable is used to equip all nodes with a unique identifier.  This identifier is used to draw the trees using...
Can you write Python code for it?

class TwoThreeTree:
    sNodeCount = 0

    def __init__(self):
        TwoThreeTree.sNodeCount += 1
        self.mID = TwoThreeTree.sNodeCount

    def getID(self):
        return self.mID

    def isNil(self):
        return False

    def isOne(self):
        return False

    def isTwo(self):
        return False

    def isThree(self):
        return False

    def isFour(self):
        return False

    def isTree(self):
        return False

    def isMethod(self):
        return False

    def insert(self, k):
        return self._ins(k)._restore()._grow()

    def delete(self, k):
        return self._del(k)._repair()._shrink()

    def _grow(self):
        return self

    def _shrink(self):
        return self


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
2-3 Trees
This notebook contains the code to visualize 2-3 trees.
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
Finally, we specify the method shrink.  The only class where the implementation of shrink is nontrivial is the class One.
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
Graphically, the node $\texttt{Four}(l, k_L, m_L, k_M, m_R, k_R, r)$ is represented as shown below:
Can you write Python code for it?

toDotList([Four(Tree('l'), 'kL', Tree('mL'), 'kM', Tree('mR'), 'kR', Tree('r'))])


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
Graphically, the node $\texttt{One}(t)$ is represented as shown below:
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
Graphically, the node $\texttt{Three}(l, k_L, m, k_R, r)$ is represented as shown below:
Can you write Python code for it?

toDotList([Three(Tree('l'), 'kL', Tree('m'), 'kR', Tree('r'))])


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
Graphically, the node $\texttt{Two}(l, k, r)$ is represented as shown below:
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
Methods of the Class Four
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
Methods of the Class Two
The method extract returns the member variables stored in a 2-node.  This is usefull to shorten the code since when we use this method, we don't have to prefix all variable names with self..
The method $t.\texttt{ins}(k)$ takes a 2-3 tree $t$ and and a key $k$ and inserts the key $k$ into $t$.
It returns a 2-3-4 tree that has at most one 4-node, which has to be a child of the root
node.  The function $\texttt{ins}$ is recursive and uses the function $\texttt{restore}$ defined below.
The most important invariant satisfied by the method call $t.\texttt{ins}(k)$ is the fact that the tree
$t.\texttt{ins}(k)$ has the same height as the tree $t$.
The different cases that need to be handled by ins are shown graphically below:
Can you write Python code for it?

toDotList([Two(Tree('l'), 'k', Tree('r')), Method('.ins(k)'), Two(Tree('l'), 'k', Tree('r')) ])

toDotList([Method('k1 < k2:'), Two(Nil(), 'k1', Nil()), Method('.ins(k2)'), Three(Nil(), 'k1', Nil(), 'k2', Nil()) ])

toDotList([Method('k2 < k1:'), Two(Nil(), 'k1', Nil()), Method('.ins(k2)'), Three(Nil(), 'k2', Nil(), 'k1', Nil()) ])

toDotList([Method('k1 < k2:'), Two(Tree('l'), 'k1', Tree('r')), Method('.ins(k2)'), Two(Tree('l'), 'k1', Tree('r.ins(k2)')) ])

toDotList([Method('k2 < k1:'), Two(Tree('l'), 'k1', Tree('r')), Method('.ins(k2)'), Two(Tree('l.ins(k2)'), 'k1', Tree('r')) ])


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
The class Nil represents an empty tree.  It has no member variables of its own.
Can you write Python code for it?

class Nil(TwoThreeTree):
    def __init__(self):
        TwoThreeTree.__init__(self)

    def isNil(self):
        return True

    def __str__(self):
        return 'Nil()'


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
The class Onerepresents a 1-node.  These are nodes without a key that have only a single child.
Can you write Python code for it?

class One(TwoThreeTree):
    def __init__(self, child):
        TwoThreeTree.__init__(self)
        self.mChild = child

    def isOne(self):
        return True

    def __str__(self):
        return _make_string(self, ['mChild'])


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
The class Three represents a 3-node of the form $\texttt{Three}(l, k_L, m, k_R, r)$.  It manages 5 member variables:
  - mLeft   is the left subtree $l$,
  - mKeyL   is the left key $k_L$,
  - mMiddle is the middle subtree $m$,
  - mKeyR   is the right key $k_r$,
  - mRight  is the right subtree.
Can you write Python code for it?

class Three(TwoThreeTree):
    def __init__(self, left, keyL, middle, keyR, right):
        TwoThreeTree.__init__(self)
        self.mLeft   = left
        self.mKeyL   = keyL
        self.mMiddle = middle
        self.mKeyR   = keyR
        self.mRight  = right

    def __str__(self):
        return self._make_string(['mLeft', 'mKeyL', 'mMiddle', 'mKeyR', 'mRight'])

    def isThree(self):
        return True


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
The class Tree is not used in the implementation of 2-3 trees.  It is only used for displaying abstract subtrees in equations.
It is displayed as a triangle containing the string that is stored in the member variable mName.
Can you write Python code for it?

class Tree(TwoThreeTree):
    def __init__(self, name):
        TwoThreeTree.__init__(self)
        self.mName = name

    def __str__(self):
        return self.mName

    def isTree(self):
        return True


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
The function $\texttt{toDotList}(\texttt{NodeList})$ takes a list of trees and displays them one by one.
Can you write Python code for it?


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
The function call $t.\texttt{restore}()$ takes a 2-3-4 tree $t$ that has at most one 4-node. This 4-node has to be a child
of the root.  It returns a 2-3-4 tree that has at most one 4-node.  This 4-node has to be the root node.
Graphically, it is specified as shown below.
Can you write Python code for it?

toDotList([Two(Four(Tree('l1'),'kl',Tree('ml'),'km', Tree('mr'),'kr',Tree('r1')), 'k', Tree('r')),
           Method('.restore()'),
           Three(Two(Tree('l1'),'kl',Tree('ml')), 'km', Two(Tree('mr'),'kr',Tree('r1')), 'k', Tree('r'))])

toDotList([Two(Tree('l'), 'k', Four(Tree('l1'),'kl',Tree('ml'),'km', Tree('mr'),'kr',Tree('r1'))),
           Method('.restore()'),
           Three(Tree('l'), 'k', Two(Tree('l1'),'kl',Tree('ml')), 'km', Two(Tree('mr'),'kr',Tree('r1')))])


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
The method $t.\texttt{toDot}()$ takes a 2-3-4 tree $t$ and returns a graph that depicts the tree $t$.
Can you write Python code for it?

def toDot(self):
    dot = gv.Digraph(node_attr={'shape': 'record', 'style': 'rounded'})
    nodeDict = {}
    self._collectIDs(nodeDict)
    for n, t in nodeDict.items():
        if t.isNil():
            dot.node(str(n), label='', shape='point')
        elif t.isOne():
            dot.node(str(n), label='', shape='point')
        elif t.isTwo():
            dot.node(str(n), label=str(t.mKey))
        elif t.isThree():
            dot.node(str(n), label=str(t.mKeyL) + '|' + str(t.mKeyR))
        elif t.isFour():
            dot.node(str(n), label=str(t.mKeyL) + '|' + str(t.mKeyM) + '|' + str(t.mKeyR))
        elif t.isTree():
            dot.node(str(n), label=str(t.mName), shape='triangle')
        else:
            assert False, f'Unknown node {t}'
    for n, t in nodeDict.items():
        if t.isOne():
            dot.edge(str(n), str(t.mChild.getID()))
        if t.isTwo():
            dot.edge(str(n), str(t.mLeft .getID()))
            dot.edge(str(n), str(t.mRight.getID()))
        if t.isThree():
            dot.edge(str(n), str(t.mLeft  .getID()))
            dot.edge(str(n), str(t.mMiddle.getID()))
            dot.edge(str(n), str(t.mRight .getID()))
        if t.isFour():
            dot.edge(str(n), str(t.mLeft   .getID()))
            dot.edge(str(n), str(t.mMiddleL.getID()))
            dot.edge(str(n), str(t.mMiddleR.getID()))
            dot.edge(str(n), str(t.mRight  .getID()))
    return dot

TwoThreeTree.toDot = toDot


I am working on the file "Python/Chapter-06/2-3-Trees-Visualization.ipynb".
The first task is:
This notebook presents 2-3 trees.  We define these trees inductively as follows:
  - $\texttt{Nil}$ is a 2-3 tree that represents the empty set.
  - $\texttt{Two}(l, k, r)$ is a 2-3 tree provided that
      - $l$ is a 2-3 tree,
      - $k$ is a key,
      - $r$ is a 2-3 tree,
      - all keys stored in $l$ are less than k and all keys stored in $r$ are bigger than $k$, that is we have
        $$ l < k < r. $$
      - $l$ and $r$ have the same height.
      A node of the form  $\texttt{Two}(l, k, r)$ is called a 2-node.  Except for the fact
      that there is no value, a 2-node is
      interpreted in the same way as we have interpreted the term $\texttt{Node}(k, v, l, r)$.
  - $\texttt{Three}(l, k_l, m, k_r, r)$ is a 2-3 tree provided
      - $l$, $m$, and $r$ are 2-3 trees,
      - $k_l$ and $k_r$ are keys,
      - $l < k_l < m < k_r < r$,
      - $l$, $m$, and $r$ have the same height.
      A node of the form  $\texttt{Three}(l, k_l, m, k_r, r)$ is called a 3-node.
In order to keep 2-3 trees balanced when inserting new keys we use a fourth constructor of the form
$\texttt{Four}(l,k_1,m_l, k_2, m_r, k_3, r)$.
A term of the form $\texttt{Four}(l,k_1,m_l, k_2, m_r, k_3, r)$ is a 2-3-4 tree iff
  - $l$, $m_l$, $m_r$, and $r$ are 2-3 trees,
  - $k_l$, $k_m$, and $k_r$ are keys,
  - $l < k_l < m_l < k_m < m_r < k_r < r$,
  - $l$, $m_l$, $m_r$, and $r$ all have the same height.
Nodes of this form are called 4-nodes and the key $k_m$ is called the middle key.
Trees containing 2-nodes, 3-node, and 4-nodes are called 2-3-4 trees.
In order to keep 2-3 trees balanced when deleting keys we use a fifth constructor of the form
$\texttt{One}(t)$.
A term of the form $\texttt{One}(t)$ is a 1-2-3 tree iff $t$ is a 2-3 tree.
The class TwoThreeTree is a superclass for constructing the nodes of 2-3-4 trees.  It has one static variable sNodeCount.  This variable is used to equip all nodes with a unique identifier.  This identifier is used to draw the trees using graphviz.
Every node has a uniques id mID that is stored as a...
Can you write Python code for it?


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
Given an ordered binary tree $t$ and a key $k$, the expression $t.\texttt{find}(k)$ returns the value stored unter the key $k$.
The method find is defined inductively as follows:
  - $\texttt{Nil}.\texttt{find}(k) = \Omega$,
because the empty tree is interpreted as the empty map.

$\texttt{Node}(k, v, l, r).\texttt{find}(k) = v$,
because the node $\texttt{Node}(k,v,l,r)$ stores the assignment $k \mapsto v$.
  - $k_1 < k_2 \rightarrow \texttt{Node}(k_2, v, l, r).\texttt{find}(k_1) = l.\texttt{find}(k_1)$,
because if $k_1$ is less than $k_2$, then any mapping for $k_1$ has to be stored in the left subtree  $l$.
  - $k_1 > k_2 \rightarrow \texttt{Node}(k_2, v, l, r).\texttt{find}(k_1) = r.\texttt{find}(k_1)$,
because if $k_1$ is greater than $k_2$, then any mapping for $k_1$ has to be stored in the right subtree  $r$.
Can you write Python code for it?

def find(self, key):
    if self.isEmpty():
        return
    elif self.mKey == key:
        return self.mValue
    elif key < self.mKey:
        return self.mLeft.find(key)
    else:
        return self.mRight.find(key)

AVLTree.find = find


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
Given an ordered binary tree $t$, the expression $t.\texttt{isEmpty}()$ checks whether $t$ is the empty tree.
Can you write Python code for it?

def isEmpty(self):
    return self.mHeight == 0

AVLTree.isEmpty = isEmpty


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
Given an ordered binary tree, this function renders the tree graphically using graphviz.
Can you write Python code for it?

def toDot(self):
    AVLTree.sNodeCount = 0 # this is a static variable of the class AVLTree
    dot = gv.Digraph(node_attr={'shape': 'record', 'style': 'rounded'})
    NodeDict = {}
    self._assignIDs(NodeDict)
    for n, t in NodeDict.items():
        if t.mValue != None:
            dot.node(str(n), label='{' + str(t.mKey) + '|' + str(t.mValue) + '}')
        elif t.mKey != None:
            dot.node(str(n), label=str(t.mKey))
        else:
            dot.node(str(n), label='', shape='point')
    for n, t in NodeDict.items():
        if not t.mLeft == None:
            dot.edge(str(n), str(t.mLeft.mID))
        if not t.mRight == None:
            dot.edge(str(n), str(t.mRight.mID))
    return dot

AVLTree.toDot = toDot


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
Given two ordered binary trees $s$ and $t$, the expression $s.\texttt{update}(t)$ overwrites the attributes of $s$ with the corresponding attributes of $t$.
Can you write Python code for it?

def _update(self, t):
    self.mKey    = t.mKey
    self.mValue  = t.mValue
    self.mLeft   = t.mLeft
    self.mRight  = t.mRight
    self.mHeight = t.mHeight

AVLTree._update = _update


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
Let's generate an ordered binary tree with random keys.
Can you write Python code for it?

import random as rnd

t = AVLTree()
for k in range(30):
    k = rnd.randrange(100)
    t.insert(k, None)
t.toDot()


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The function $\texttt{createNode}(k, v, l, r)$ creates an AVL-tree of that has the pair $(k, v)$ stored at its root, left subtree $l$ and right subtree $r$.
Can you write Python code for it?

def createNode(key, value, left, right):
    node         = AVLTree()
    node.mKey    = key
    node.mValue  = value
    node.mLeft   = left
    node.mRight  = right
    node.mHeight = max(left.mHeight, right.mHeight) + 1
    return node

import graphviz as gv


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The function $\texttt{demo}()$ creates a small ordered binary tree.
Can you write Python code for it?

def demo():
    m = AVLTree()
    m.insert("anton",  123)
    m.insert("hugo" ,  345)
    m.insert("gustav", 789)
    m.insert("jens",   234)
    m.insert("hubert", 432)
    m.insert("andre",  342)
    m.insert("philip", 342)
    m.insert("rene",   345)
    return m

t = demo()
print(t.toDot())

t.toDot()

t.delete('gustav')
t.toDot()

t.delete('hubert')
t.toDot()


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The function $\texttt{leanTree}(h, k)$ computes an AVL tree of height $h$ that is as lean as possible.
All key in the tree will be integers that are bigger than $k$.  The definition by induction:
  - $\texttt{leanTree}(0, k) = \texttt{Nil}$,
because there is only one AVL tree of height $0$ and this is the tree $\texttt{Nil}$.

$\texttt{leanTree}(1, k) = \texttt{Node}(k+1,0,\texttt{Nil}, \texttt{Nil})$,
since, if we abstract from the actual keys and values, there is exactly one AVL tree of height $1$.
  - $\texttt{leanTree}(h+1,k).\texttt{maxKey}() = l \rightarrow
 \texttt{leanTree}(h+2,k) = \texttt{Node}\bigl(l+1,\,0,\,\texttt{leanTree}(h+1,k),\,\texttt{leanTree}(h, l+1)\bigr)
$.
Can you write Python code for it?

def leanTree(h, k):
    if h == 0:
        return AVLTree()
    if h == 1:
        return createNode(k + 1, None, AVLTree(), AVLTree())
    left = leanTree(h - 1, k)
    l    = left.maxKey()
    return createNode(l + 1, None, left, leanTree(h - 2, l + 1))

l = leanTree(6, 0)
l.toDot()

for k in range(6):
    l = leanTree(k, 0)
    print(f'Height {k}:')
    display(l.toDot())


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The function $\texttt{restore}(\texttt{self})$ restores the balancing condition of the given binary tree
at the root node and recompute the variable $\texttt{mHeight}$.
The method $\texttt{restore}$ is specified via conditional equations.

$\texttt{Nil}.\texttt{restore}() = \texttt{Nil}$,
because the empty tree already is an  AVL tree.
  - $|l.\texttt{height}() - r.\texttt{height}()| \leq 1 \rightarrow
   \texttt{Node}(k,v,l,r).\texttt{restore}() = \texttt{Node}(k,v,l,r)$.
If the balancing condition is satisfied, then nothing needs to be done.
  - $\begin{array}[t]{cl}
          & l_1.\texttt{height}() = r_1.\texttt{height}() + 2    \
   \wedge & l_1 = \texttt{Node}(k_2,v_2,l_2,r_2)                 \
   \wedge & l_2.\texttt{height}() \geq r_2.\texttt{height}()     \[0.2cm]
   \rightarrow & \texttt{Node}(k_1,v_1,l_1,r_1).\texttt{restore}() =
                 \texttt{Node}\bigl(k_2,v_2,l_2,\texttt{Node}(k_1,v_1,r_2,r_1)\bigr)
   \end{array}
$
  - $\begin{array}[t]{cl}
           & l_1.\texttt{height}() = r_1.\texttt{height}() + 2    \
    \wedge & l_1 = \texttt{Node}(k_2,v_2,l_2,r_2)               \
    \wedge & l_2.\texttt{height}() < r_2.\texttt{height}()     \
    \wedge & r_2 = \texttt{Node}(k_3,v_3,l_3,r_3)               \
    \rightarrow & \texttt{Node}(k_1,v_1,l_1,r_1).\texttt{restore}() =
                  \texttt{Node}\bigl(k_3,v_3,\texttt{Node}(k_2,v_2,l_2,l_3),\texttt{Node}(k_1,v_1,r_3,r_1) \bigr)
    \end{array}
$

$\begin{array}[t]{cl}
              & r_1.\texttt{height}() = l_1.\texttt{height}() + 2    \
       \wedge & r_1 = \texttt{Node}(k_2,v_2,l_2,r_2)               \
       \wedge & r_2.\texttt{height}() \geq l_2.\texttt{height}()     \[0.2cm]
       \rightarrow & \texttt{Node}(k_1,v_1,l_1,r_1).\texttt{restore}() =
                     \texttt{Node}\bigl(k_2,v_2,\texttt{Node}(k_1,v_1,l_1,l_2),r_2\bigr)
       \end{array}
    $

$\begin{array}[t]{cl}
               & r_1.\texttt{height}() = l_1.\texttt{height}() + 2    \
        \wedge & r_1 = \texttt{Node}(k_2,v_2,l_2,r_2)         ...
Can you write Python code for it?

def _restore(self):
    if abs(self.mLeft.mHeight - self.mRight.mHeight) <= 1:
        self._restoreHeight()
        return
    if self.mLeft.mHeight > self.mRight.mHeight:
        k1, v1, l1, r1 = self.mKey, self.mValue, self.mLeft, self.mRight
        k2, v2, l2, r2 = l1.mKey, l1.mValue, l1.mLeft, l1.mRight
        if l2.mHeight >= r2.mHeight:
            self._setValues(k2, v2, l2, createNode(k1, v1, r2, r1))
        else:
            k3, v3, l3, r3 = r2.mKey, r2.mValue, r2.mLeft, r2.mRight
            self._setValues(k3, v3, createNode(k2, v2, l2, l3),
                                    createNode(k1, v1, r3, r1))
    elif self.mRight.mHeight > self.mLeft.mHeight:
        k1, v1, l1, r1 = self.mKey, self.mValue, self.mLeft, self.mRight
        k2, v2, l2, r2 = r1.mKey, r1.mValue, r1.mLeft, r1.mRight
        if r2.mHeight >= l2.mHeight:
            self._setValues(k2, v2, createNode(k1, v1, l1, l2), r2)
        else:
            k3, v3, l3, r3 = l2.mKey, l2.mValue, l2.mLeft, l2.mRight
            self._setValues(k3, v3, createNode(k1, v1, l1, l3),
                                    createNode(k2, v2, r3, r2))
    self._restoreHeight()

AVLTree._restore = _restore


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The function $\texttt{self}.\texttt{_setValues}(k, v, l, r)$ overwrites the member variables of the node $\texttt{self}$ with the given values.
Can you write Python code for it?

def _setValues(self, k, v, l, r):
    self.mKey   = k
    self.mValue = v
    self.mLeft  = l
    self.mRight = r

AVLTree._setValues = _setValues

def _restoreHeight(self):
    self.mHeight = max(self.mLeft.mHeight, self.mRight.mHeight) + 1

AVLTree._restoreHeight = _restoreHeight


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The function $t.\texttt{maxKey}()$ returns the biggest key in the tree $t$.  It is defined inductively:
  - $\texttt{Nil}.\texttt{maxKey}() = \Omega$,
  - $\texttt{Node}(k,v,l,\texttt{Nil}).\texttt{maxKey}() = k$,
  - $r \not= \texttt{Nil} \rightarrow \texttt{Node}(k,v,l,r).\texttt{maxKey}() = r.\texttt{maxKey}()$.
Can you write Python code for it?

def maxKey(self):
    if self.isEmpty():
        return None
    if self.mRight.isEmpty():
        return self.mKey
    return self.mRight.maxKey()

AVLTree.maxKey = maxKey


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The method  $\texttt{insert}()$ is specified via recursive equations.
  - $\texttt{Nil}.\texttt{insert}(k,v) = \texttt{Node}(k,v, \texttt{Nil}, \texttt{Nil})$,
  - $\texttt{Node}(k, v_2, l, r).\texttt{insert}(k,v_1) = \texttt{Node}(k, v_1, l, r)$,
  - $k_1 < k_2 \rightarrow
          \texttt{Node}(k_2, v_2, l, r).\texttt{insert}(k_1, v_1) =
          \texttt{Node}\bigl(k_2, v_2, l.\texttt{insert}(k_1,v_1), r\bigr).\texttt{restore}()$,
  - $k_1 > k_2 \rightarrow
         \texttt{Node}(k_2, v_2, l, r).\texttt{insert}\bigl(k_1, v_1\bigr) =
         \texttt{Node}\bigl(k_2, v_2, l, r.\texttt{insert}(k_1,v_1)\bigr).\texttt{restore}()$.
The function $\texttt{restore}$ is an auxiliary function that is defined below.  This function restores the balancing condition if it is violated after an insertion.
Can you write Python code for it?

def insert(self, key, value):
    if self.isEmpty():
        self.mKey    = key
        self.mValue  = value
        self.mLeft   = AVLTree()
        self.mRight  = AVLTree()
        self.mHeight = 1
    elif self.mKey == key:
        self.mValue = value
    elif key < self.mKey:
        self.mLeft.insert(key, value)
        self._restore()
    else:
        self.mRight.insert(key, value)
        self._restore()

AVLTree.insert = insert


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The method $\texttt{self}.\texttt{delMin}()$ removes the smallest key from the given tree $\texttt{self}$
and returns a triple of the form
$$ (\texttt{self}, k_m, v_m) $$
where $\texttt{self}$ is the tree that remains after removing the smallest key, while $k_m$ is the smallest key that has been found and $v_m$ is the associated value.
The function is defined as follows:

$\texttt{Node}(k, v, \texttt{Nil}, r).\texttt{delMin}() = \langle r, k, v \rangle$,
$l\not= \texttt{Nil} \wedge \langle l',k_{min}, v_{min}\rangle := l.\texttt{delMin}()
       \;\rightarrow\;
     \texttt{Node}(k, v, l, r).\texttt{delMin}() =
     \langle \texttt{Node}(k, v, l', r).\texttt{restore}(), k_{min}, v_{min} \rangle
    $
Can you write Python code for it?

def _delMin(self):
    if self.mLeft.isEmpty():
        return self.mRight, self.mKey, self.mValue
    else:
        ls, km, vm = self.mLeft._delMin()
        self.mLeft = ls
        self._restore()
        return self, km, vm

AVLTree._delMin = _delMin


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
The method $\texttt{self}.\texttt{delete}(k)$ removes the key $k$ from the tree $\texttt{self}$.  It is defined as follows:

$\texttt{Nil}.\texttt{delete}(k) = \texttt{Nil}$,
$\texttt{Node}(k,v,\texttt{Nil},r).\texttt{delete}(k) = r$,
$\texttt{Node}(k,v,l,\texttt{Nil}).\texttt{delete}(k) = l$,
$l \not= \texttt{Nil} \,\wedge\, r \not= \texttt{Nil} \,\wedge\,
       \langle r',k_{min}, v_{min} \rangle := r.\texttt{delMin}()  \;\rightarrow\;
      \texttt{Node}(k,v,l,r).\texttt{delete}(k) = \texttt{Node}(k_{min},v_{min},l,r').\texttt{restore}()$
$k_1 < k_2 \rightarrow \texttt{Node}(k_2,v_2,l,r).\texttt{delete}(k_1) =
    \texttt{Node}\bigl(k_2,v_2,l.\texttt{delete}(k_1),r\bigr).\texttt{restore}()$,
$k_1 > k_2 \rightarrow \texttt{Node}(k_2,v_2,l,r).\texttt{delete}(k_1) =
     \texttt{Node}\bigl(k_2,v_2,l,r.\texttt{delete}(k_1)\bigr).\texttt{restore}()$.
Can you write Python code for it?

def delete(self, key):
    if self.isEmpty():
        return
    if key == self.mKey:
        if self.mLeft.isEmpty():
            self._update(self.mRight)
        elif self.mRight.isEmpty():
            self._update(self.mLeft)
        else:
            self.mRight, self.mKey, self.mValue = self.mRight._delMin()
            self._restore()
    elif key < self.mKey:
        self.mLeft.delete(key)
        self._restore()
    else:
        self.mRight.delete(key)
        self._restore()

AVLTree.delete = delete


I am working on the file "Python/Chapter-06/AVL-Trees.ipynb".
The first task is:
This method assigns a unique identifier with each node.  The dictionary NodeDict maps these identifiers to the nodes where they occur.
Can you write Python code for it?

def _assignIDs(self, NodeDict):
    AVLTree.sNodeCount += 1
    self.mID = AVLTree.sNodeCount
    NodeDict[self.mID] = self
    if self.isEmpty():
        return
    self.mLeft ._assignIDs(NodeDict)
    self.mRight._assignIDs(NodeDict)

AVLTree._assignIDs = _assignIDs