From b1012ba0f34199198d9fe0138e7027a5647a4946 Mon Sep 17 00:00:00 2001
From: Victor Stinner <vstinner@python.org>
Date: Fri, 30 Sep 2022 18:28:55 +0200
Subject: [PATCH] gh-97681: Remove the Tools/demo/ directory

Remove the Tools/demo/ directory which contained old demo scripts. A
copy can be found in the old-demos project:
https://github.com/gvanrossum/old-demos

Remove the following old demo scripts:

* beer.py
* eiffel.py
* hanoi.py
* life.py
* markov.py
* mcast.py
* queens.py
* redemo.py
* rpython.py
* rpythond.py
* sortvisu.py
* spreadsheet.py
* vector.py

Changes:

* Remove a reference to the redemo.py script in the regex howto
  documentation.
* Remove a reference to the removed Tools/demo/ directory in the
  curses documentation.
* Update PC/layout/ to remove the reference to Tools/demo/ directory.
---
 Doc/howto/regex.rst                           |   6 +-
 Doc/library/curses.rst                        |   3 -
 Doc/whatsnew/3.12.rst                         |   9 +
 ...2-09-30-18-35-11.gh-issue-97681.-KO1Ba.rst |   3 +
 PC/layout/main.py                             |   2 +-
 Tools/README                                  |   2 -
 Tools/demo/README                             |  16 -
 Tools/demo/beer.py                            |  25 -
 Tools/demo/eiffel.py                          | 146 ---
 Tools/demo/hanoi.py                           | 154 ----
 Tools/demo/life.py                            | 262 ------
 Tools/demo/markov.py                          | 125 ---
 Tools/demo/mcast.py                           |  82 --
 Tools/demo/queens.py                          |  85 --
 Tools/demo/redemo.py                          | 171 ----
 Tools/demo/rpython.py                         |  37 -
 Tools/demo/rpythond.py                        |  58 --
 Tools/demo/sortvisu.py                        | 635 --------------
 Tools/demo/spreadsheet.py                     | 829 ------------------
 Tools/demo/vector.py                          |  94 --
 20 files changed, 14 insertions(+), 2730 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Tools-Demos/2022-09-30-18-35-11.gh-issue-97681.-KO1Ba.rst
 delete mode 100644 Tools/demo/README
 delete mode 100755 Tools/demo/beer.py
 delete mode 100755 Tools/demo/eiffel.py
 delete mode 100755 Tools/demo/hanoi.py
 delete mode 100755 Tools/demo/life.py
 delete mode 100755 Tools/demo/markov.py
 delete mode 100755 Tools/demo/mcast.py
 delete mode 100755 Tools/demo/queens.py
 delete mode 100755 Tools/demo/redemo.py
 delete mode 100755 Tools/demo/rpython.py
 delete mode 100755 Tools/demo/rpythond.py
 delete mode 100755 Tools/demo/sortvisu.py
 delete mode 100755 Tools/demo/spreadsheet.py
 delete mode 100755 Tools/demo/vector.py

diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst
index 5cd6140f19ca2e..655df59e27b641 100644
--- a/Doc/howto/regex.rst
+++ b/Doc/howto/regex.rst
@@ -378,11 +378,7 @@ containing information about the match: where it starts and ends, the substring
 it matched, and more.
 
 You can learn about this by interactively experimenting with the :mod:`re`
-module.  If you have :mod:`tkinter` available, you may also want to look at
-:source:`Tools/demo/redemo.py`, a demonstration program included with the
-Python distribution.  It allows you to enter REs and strings, and displays
-whether the RE matches or fails. :file:`redemo.py` can be quite useful when
-trying to debug a complicated RE.
+module.
 
 This HOWTO uses the standard Python interpreter for its examples. First, run the
 Python interpreter, import the :mod:`re` module, and compile a RE::
diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index a7cc4952778011..bb203c48f19ffb 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -42,9 +42,6 @@ Linux and the BSD variants of Unix.
       Tutorial material on using curses with Python, by Andrew Kuchling and Eric
       Raymond.
 
-   The :source:`Tools/demo/` directory in the Python source distribution contains
-   some example programs using the curses bindings provided by this module.
-
 
 .. _curses-functions:
 
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index e14a2bd0133b8a..1d68e84983fed9 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -170,6 +170,15 @@ CPython bytecode changes
   (Contributed by Ken Jin in :gh:`93429`.)
 
 
+Demos and Tools
+===============
+
+* Remove the ``Tools/demo/`` directory which contained old demo scripts. A copy
+  can be found in the `old-demos project
+  <https://github.com/gvanrossum/old-demos>`_.
+  (Contributed by Victor Stinner in :gh:`97681`.)
+
+
 Deprecated
 ==========
 
diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-09-30-18-35-11.gh-issue-97681.-KO1Ba.rst b/Misc/NEWS.d/next/Tools-Demos/2022-09-30-18-35-11.gh-issue-97681.-KO1Ba.rst
new file mode 100644
index 00000000000000..6f1ec12ce0c365
--- /dev/null
+++ b/Misc/NEWS.d/next/Tools-Demos/2022-09-30-18-35-11.gh-issue-97681.-KO1Ba.rst
@@ -0,0 +1,3 @@
+Remove the ``Tools/demo/`` directory which contained old demo scripts. A copy
+can be found in the `old-demos project
+<https://github.com/gvanrossum/old-demos>`_.  Patch by Victor Stinner.
diff --git a/PC/layout/main.py b/PC/layout/main.py
index 923483ad4a3f71..17d27bba6640c5 100644
--- a/PC/layout/main.py
+++ b/PC/layout/main.py
@@ -58,7 +58,7 @@
 
 DATA_DIRS = FileNameSet("data")
 
-TOOLS_DIRS = FileNameSet("scripts", "i18n", "demo", "parser")
+TOOLS_DIRS = FileNameSet("scripts", "i18n", "parser")
 TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt")
 
 
diff --git a/Tools/README b/Tools/README
index f8bfbc04795816..04612b8013db92 100644
--- a/Tools/README
+++ b/Tools/README
@@ -5,8 +5,6 @@ buildbot        Batchfiles for running on Windows buildbot workers.
 
 ccbench         A Python threads-based concurrency benchmark. (*)
 
-demo            Several Python programming demos.
-
 freeze          Create a stand-alone executable from a Python program.
 
 gdb             Python code to be run inside gdb, to make it easier to
diff --git a/Tools/demo/README b/Tools/demo/README
deleted file mode 100644
index 9fccb97d956cba..00000000000000
--- a/Tools/demo/README
+++ /dev/null
@@ -1,16 +0,0 @@
-This directory contains a collection of demonstration scripts for
-various aspects of Python programming.
-
-beer.py           Well-known programming example: Bottles of beer.
-eiffel.py         Python advanced magic: A metaclass for Eiffel post/preconditions.
-hanoi.py          Well-known programming example: Towers of Hanoi.
-life.py           Curses programming: Simple game-of-life.
-markov.py         Algorithms: Markov chain simulation.
-mcast.py          Network programming: Send and receive UDP multicast packets.
-queens.py         Well-known programming example: N-Queens problem.
-redemo.py         Regular Expressions: GUI script to test regexes.
-rpython.py        Network programming: Small client for remote code execution.
-rpythond.py       Network programming: Small server for remote code execution.
-sortvisu.py       GUI programming: Visualization of different sort algorithms.
-spreadsheet.py    GUI/Application programming: A simple spreadsheet application.
-vector.py         Python basics: A vector class demonstrating special methods.
diff --git a/Tools/demo/beer.py b/Tools/demo/beer.py
deleted file mode 100755
index af58380e0f5a1a..00000000000000
--- a/Tools/demo/beer.py
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-A Python version of the classic "bottles of beer on the wall" programming
-example.
-
-By Guido van Rossum, demystified after a version by Fredrik Lundh.
-"""
-
-import sys
-
-n = 100
-if sys.argv[1:]:
-    n = int(sys.argv[1])
-
-def bottle(n):
-    if n == 0: return "no more bottles of beer"
-    if n == 1: return "one bottle of beer"
-    return str(n) + " bottles of beer"
-
-for i in range(n, 0, -1):
-    print(bottle(i), "on the wall,")
-    print(bottle(i) + ".")
-    print("Take one down, pass it around,")
-    print(bottle(i-1), "on the wall.")
diff --git a/Tools/demo/eiffel.py b/Tools/demo/eiffel.py
deleted file mode 100755
index a76c2324dd6a67..00000000000000
--- a/Tools/demo/eiffel.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Support Eiffel-style preconditions and postconditions for functions.
-
-An example for Python metaclasses.
-"""
-
-import unittest
-from types import FunctionType as function
-
-class EiffelBaseMetaClass(type):
-
-    def __new__(meta, name, bases, dict):
-        meta.convert_methods(dict)
-        return super(EiffelBaseMetaClass, meta).__new__(
-            meta, name, bases, dict)
-
-    @classmethod
-    def convert_methods(cls, dict):
-        """Replace functions in dict with EiffelMethod wrappers.
-
-        The dict is modified in place.
-
-        If a method ends in _pre or _post, it is removed from the dict
-        regardless of whether there is a corresponding method.
-        """
-        # find methods with pre or post conditions
-        methods = []
-        for k, v in dict.items():
-            if k.endswith('_pre') or k.endswith('_post'):
-                assert isinstance(v, function)
-            elif isinstance(v, function):
-                methods.append(k)
-        for m in methods:
-            pre = dict.get("%s_pre" % m)
-            post = dict.get("%s_post" % m)
-            if pre or post:
-                dict[m] = cls.make_eiffel_method(dict[m], pre, post)
-
-
-class EiffelMetaClass1(EiffelBaseMetaClass):
-    # an implementation of the "eiffel" meta class that uses nested functions
-
-    @staticmethod
-    def make_eiffel_method(func, pre, post):
-        def method(self, *args, **kwargs):
-            if pre:
-                pre(self, *args, **kwargs)
-            rv = func(self, *args, **kwargs)
-            if post:
-                post(self, rv, *args, **kwargs)
-            return rv
-
-        if func.__doc__:
-            method.__doc__ = func.__doc__
-
-        return method
-
-
-class EiffelMethodWrapper:
-
-    def __init__(self, inst, descr):
-        self._inst = inst
-        self._descr = descr
-
-    def __call__(self, *args, **kwargs):
-        return self._descr.callmethod(self._inst, args, kwargs)
-
-
-class EiffelDescriptor:
-
-    def __init__(self, func, pre, post):
-        self._func = func
-        self._pre = pre
-        self._post = post
-
-        self.__name__ = func.__name__
-        self.__doc__ = func.__doc__
-
-    def __get__(self, obj, cls=None):
-        return EiffelMethodWrapper(obj, self)
-
-    def callmethod(self, inst, args, kwargs):
-        if self._pre:
-            self._pre(inst, *args, **kwargs)
-        x = self._func(inst, *args, **kwargs)
-        if self._post:
-            self._post(inst, x, *args, **kwargs)
-        return x
-
-
-class EiffelMetaClass2(EiffelBaseMetaClass):
-    # an implementation of the "eiffel" meta class that uses descriptors
-
-    make_eiffel_method = EiffelDescriptor
-
-
-class Tests(unittest.TestCase):
-
-    def testEiffelMetaClass1(self):
-        self._test(EiffelMetaClass1)
-
-    def testEiffelMetaClass2(self):
-        self._test(EiffelMetaClass2)
-
-    def _test(self, metaclass):
-        class Eiffel(metaclass=metaclass):
-            pass
-
-        class Test(Eiffel):
-            def m(self, arg):
-                """Make it a little larger"""
-                return arg + 1
-
-            def m2(self, arg):
-                """Make it a little larger"""
-                return arg + 1
-
-            def m2_pre(self, arg):
-                assert arg > 0
-
-            def m2_post(self, result, arg):
-                assert result > arg
-
-        class Sub(Test):
-            def m2(self, arg):
-                return arg**2
-
-            def m2_post(self, Result, arg):
-                super(Sub, self).m2_post(Result, arg)
-                assert Result < 100
-
-        t = Test()
-        self.assertEqual(t.m(1), 2)
-        self.assertEqual(t.m2(1), 2)
-        self.assertRaises(AssertionError, t.m2, 0)
-
-        s = Sub()
-        self.assertRaises(AssertionError, s.m2, 1)
-        self.assertRaises(AssertionError, s.m2, 10)
-        self.assertEqual(s.m2(5), 25)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Tools/demo/hanoi.py b/Tools/demo/hanoi.py
deleted file mode 100755
index 8db895c2445810..00000000000000
--- a/Tools/demo/hanoi.py
+++ /dev/null
@@ -1,154 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Animated Towers of Hanoi using Tk with optional bitmap file in background.
-
-Usage: hanoi.py [n [bitmapfile]]
-
-n is the number of pieces to animate; default is 4, maximum 15.
-
-The bitmap file can be any X11 bitmap file (look in /usr/include/X11/bitmaps for
-samples); it is displayed as the background of the animation.  Default is no
-bitmap.
-"""
-
-from tkinter import Tk, Canvas
-
-# Basic Towers-of-Hanoi algorithm: move n pieces from a to b, using c
-# as temporary.  For each move, call report()
-def hanoi(n, a, b, c, report):
-    if n <= 0: return
-    hanoi(n-1, a, c, b, report)
-    report(n, a, b)
-    hanoi(n-1, c, b, a, report)
-
-
-# The graphical interface
-class Tkhanoi:
-
-    # Create our objects
-    def __init__(self, n, bitmap=None):
-        self.n = n
-        self.tk = tk = Tk()
-        self.canvas = c = Canvas(tk)
-        c.pack()
-        width, height = tk.getint(c['width']), tk.getint(c['height'])
-
-        # Add background bitmap
-        if bitmap:
-            self.bitmap = c.create_bitmap(width//2, height//2,
-                                          bitmap=bitmap,
-                                          foreground='blue')
-
-        # Generate pegs
-        pegwidth = 10
-        pegheight = height//2
-        pegdist = width//3
-        x1, y1 = (pegdist-pegwidth)//2, height*1//3
-        x2, y2 = x1+pegwidth, y1+pegheight
-        self.pegs = []
-        p = c.create_rectangle(x1, y1, x2, y2, fill='black')
-        self.pegs.append(p)
-        x1, x2 = x1+pegdist, x2+pegdist
-        p = c.create_rectangle(x1, y1, x2, y2, fill='black')
-        self.pegs.append(p)
-        x1, x2 = x1+pegdist, x2+pegdist
-        p = c.create_rectangle(x1, y1, x2, y2, fill='black')
-        self.pegs.append(p)
-        self.tk.update()
-
-        # Generate pieces
-        pieceheight = pegheight//16
-        maxpiecewidth = pegdist*2//3
-        minpiecewidth = 2*pegwidth
-        self.pegstate = [[], [], []]
-        self.pieces = {}
-        x1, y1 = (pegdist-maxpiecewidth)//2, y2-pieceheight-2
-        x2, y2 = x1+maxpiecewidth, y1+pieceheight
-        dx = (maxpiecewidth-minpiecewidth) // (2*max(1, n-1))
-        for i in range(n, 0, -1):
-            p = c.create_rectangle(x1, y1, x2, y2, fill='red')
-            self.pieces[i] = p
-            self.pegstate[0].append(i)
-            x1, x2 = x1 + dx, x2-dx
-            y1, y2 = y1 - pieceheight-2, y2-pieceheight-2
-            self.tk.update()
-            self.tk.after(25)
-
-    # Run -- never returns
-    def run(self):
-        while True:
-            hanoi(self.n, 0, 1, 2, self.report)
-            hanoi(self.n, 1, 2, 0, self.report)
-            hanoi(self.n, 2, 0, 1, self.report)
-            hanoi(self.n, 0, 2, 1, self.report)
-            hanoi(self.n, 2, 1, 0, self.report)
-            hanoi(self.n, 1, 0, 2, self.report)
-
-    # Reporting callback for the actual hanoi function
-    def report(self, i, a, b):
-        if self.pegstate[a][-1] != i: raise RuntimeError # Assertion
-        del self.pegstate[a][-1]
-        p = self.pieces[i]
-        c = self.canvas
-
-        # Lift the piece above peg a
-        ax1, ay1, ax2, ay2 = c.bbox(self.pegs[a])
-        while True:
-            x1, y1, x2, y2 = c.bbox(p)
-            if y2 < ay1: break
-            c.move(p, 0, -1)
-            self.tk.update()
-
-        # Move it towards peg b
-        bx1, by1, bx2, by2 = c.bbox(self.pegs[b])
-        newcenter = (bx1+bx2)//2
-        while True:
-            x1, y1, x2, y2 = c.bbox(p)
-            center = (x1+x2)//2
-            if center == newcenter: break
-            if center > newcenter: c.move(p, -1, 0)
-            else: c.move(p, 1, 0)
-            self.tk.update()
-
-        # Move it down on top of the previous piece
-        pieceheight = y2-y1
-        newbottom = by2 - pieceheight*len(self.pegstate[b]) - 2
-        while True:
-            x1, y1, x2, y2 = c.bbox(p)
-            if y2 >= newbottom: break
-            c.move(p, 0, 1)
-            self.tk.update()
-
-        # Update peg state
-        self.pegstate[b].append(i)
-
-
-def main():
-    import sys
-
-    # First argument is number of pegs, default 4
-    if sys.argv[1:]:
-        n = int(sys.argv[1])
-    else:
-        n = 4
-
-    # Second argument is bitmap file, default none
-    if sys.argv[2:]:
-        bitmap = sys.argv[2]
-        # Reverse meaning of leading '@' compared to Tk
-        if bitmap[0] == '@': bitmap = bitmap[1:]
-        else: bitmap = '@' + bitmap
-    else:
-        bitmap = None
-
-    # Create the graphical objects...
-    h = Tkhanoi(n, bitmap)
-
-    # ...and run!
-    h.run()
-
-
-# Call main when run as script
-if __name__ == '__main__':
-    main()
diff --git a/Tools/demo/life.py b/Tools/demo/life.py
deleted file mode 100755
index fc4cb4952dce69..00000000000000
--- a/Tools/demo/life.py
+++ /dev/null
@@ -1,262 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-A curses-based version of Conway's Game of Life.
-
-An empty board will be displayed, and the following commands are available:
- E : Erase the board
- R : Fill the board randomly
- S : Step for a single generation
- C : Update continuously until a key is struck
- Q : Quit
- Cursor keys :  Move the cursor around the board
- Space or Enter : Toggle the contents of the cursor's position
-
-Contributed by Andrew Kuchling, Mouse support and color by Dafydd Crosby.
-"""
-
-import curses
-import random
-
-
-class LifeBoard:
-    """Encapsulates a Life board
-
-    Attributes:
-    X,Y : horizontal and vertical size of the board
-    state : dictionary mapping (x,y) to 0 or 1
-
-    Methods:
-    display(update_board) -- If update_board is true, compute the
-                             next generation.  Then display the state
-                             of the board and refresh the screen.
-    erase() -- clear the entire board
-    make_random() -- fill the board randomly
-    set(y,x) -- set the given cell to Live; doesn't refresh the screen
-    toggle(y,x) -- change the given cell from live to dead, or vice
-                   versa, and refresh the screen display
-
-    """
-    def __init__(self, scr, char=ord('*')):
-        """Create a new LifeBoard instance.
-
-        scr -- curses screen object to use for display
-        char -- character used to render live cells (default: '*')
-        """
-        self.state = {}
-        self.scr = scr
-        Y, X = self.scr.getmaxyx()
-        self.X, self.Y = X - 2, Y - 2 - 1
-        self.char = char
-        self.scr.clear()
-
-        # Draw a border around the board
-        border_line = '+' + (self.X * '-') + '+'
-        self.scr.addstr(0, 0, border_line)
-        self.scr.addstr(self.Y + 1, 0, border_line)
-        for y in range(0, self.Y):
-            self.scr.addstr(1 + y, 0, '|')
-            self.scr.addstr(1 + y, self.X + 1, '|')
-        self.scr.refresh()
-
-    def set(self, y, x):
-        """Set a cell to the live state"""
-        if x < 0 or self.X <= x or y < 0 or self.Y <= y:
-            raise ValueError("Coordinates out of range %i,%i" % (y, x))
-        self.state[x, y] = 1
-
-    def toggle(self, y, x):
-        """Toggle a cell's state between live and dead"""
-        if x < 0 or self.X <= x or y < 0 or self.Y <= y:
-            raise ValueError("Coordinates out of range %i,%i" % (y, x))
-        if (x, y) in self.state:
-            del self.state[x, y]
-            self.scr.addch(y + 1, x + 1, ' ')
-        else:
-            self.state[x, y] = 1
-            if curses.has_colors():
-                # Let's pick a random color!
-                self.scr.attrset(curses.color_pair(random.randrange(1, 7)))
-            self.scr.addch(y + 1, x + 1, self.char)
-            self.scr.attrset(0)
-        self.scr.refresh()
-
-    def erase(self):
-        """Clear the entire board and update the board display"""
-        self.state = {}
-        self.display(update_board=False)
-
-    def display(self, update_board=True):
-        """Display the whole board, optionally computing one generation"""
-        M, N = self.X, self.Y
-        if not update_board:
-            for i in range(0, M):
-                for j in range(0, N):
-                    if (i, j) in self.state:
-                        self.scr.addch(j + 1, i + 1, self.char)
-                    else:
-                        self.scr.addch(j + 1, i + 1, ' ')
-            self.scr.refresh()
-            return
-
-        d = {}
-        self.boring = 1
-        for i in range(0, M):
-            L = range(max(0, i - 1), min(M, i + 2))
-            for j in range(0, N):
-                s = 0
-                live = (i, j) in self.state
-                for k in range(max(0, j - 1), min(N, j + 2)):
-                    for l in L:
-                        if (l, k) in self.state:
-                            s += 1
-                s -= live
-                if s == 3:
-                    # Birth
-                    d[i, j] = 1
-                    if curses.has_colors():
-                        # Let's pick a random color!
-                        self.scr.attrset(curses.color_pair(
-                            random.randrange(1, 7)))
-                    self.scr.addch(j + 1, i + 1, self.char)
-                    self.scr.attrset(0)
-                    if not live:
-                        self.boring = 0
-                elif s == 2 and live:
-                    # Survival
-                    d[i, j] = 1
-                elif live:
-                    # Death
-                    self.scr.addch(j + 1, i + 1, ' ')
-                    self.boring = 0
-        self.state = d
-        self.scr.refresh()
-
-    def make_random(self):
-        "Fill the board with a random pattern"
-        self.state = {}
-        for i in range(0, self.X):
-            for j in range(0, self.Y):
-                if random.random() > 0.5:
-                    self.set(j, i)
-
-
-def erase_menu(stdscr, menu_y):
-    "Clear the space where the menu resides"
-    stdscr.move(menu_y, 0)
-    stdscr.clrtoeol()
-    stdscr.move(menu_y + 1, 0)
-    stdscr.clrtoeol()
-
-
-def display_menu(stdscr, menu_y):
-    "Display the menu of possible keystroke commands"
-    erase_menu(stdscr, menu_y)
-
-    # If color, then light the menu up :-)
-    if curses.has_colors():
-        stdscr.attrset(curses.color_pair(1))
-    stdscr.addstr(menu_y, 4,
-        'Use the cursor keys to move, and space or Enter to toggle a cell.')
-    stdscr.addstr(menu_y + 1, 4,
-        'E)rase the board, R)andom fill, S)tep once or C)ontinuously, Q)uit')
-    stdscr.attrset(0)
-
-
-def keyloop(stdscr):
-    # Clear the screen and display the menu of keys
-    stdscr.clear()
-    stdscr_y, stdscr_x = stdscr.getmaxyx()
-    menu_y = (stdscr_y - 3) - 1
-    display_menu(stdscr, menu_y)
-
-    # If color, then initialize the color pairs
-    if curses.has_colors():
-        curses.init_pair(1, curses.COLOR_BLUE, 0)
-        curses.init_pair(2, curses.COLOR_CYAN, 0)
-        curses.init_pair(3, curses.COLOR_GREEN, 0)
-        curses.init_pair(4, curses.COLOR_MAGENTA, 0)
-        curses.init_pair(5, curses.COLOR_RED, 0)
-        curses.init_pair(6, curses.COLOR_YELLOW, 0)
-        curses.init_pair(7, curses.COLOR_WHITE, 0)
-
-    # Set up the mask to listen for mouse events
-    curses.mousemask(curses.BUTTON1_CLICKED)
-
-    # Allocate a subwindow for the Life board and create the board object
-    subwin = stdscr.subwin(stdscr_y - 3, stdscr_x, 0, 0)
-    board = LifeBoard(subwin, char=ord('*'))
-    board.display(update_board=False)
-
-    # xpos, ypos are the cursor's position
-    xpos, ypos = board.X // 2, board.Y // 2
-
-    # Main loop:
-    while True:
-        stdscr.move(1 + ypos, 1 + xpos)   # Move the cursor
-        c = stdscr.getch()                # Get a keystroke
-        if 0 < c < 256:
-            c = chr(c)
-            if c in ' \n':
-                board.toggle(ypos, xpos)
-            elif c in 'Cc':
-                erase_menu(stdscr, menu_y)
-                stdscr.addstr(menu_y, 6, ' Hit any key to stop continuously '
-                              'updating the screen.')
-                stdscr.refresh()
-                # Activate nodelay mode; getch() will return -1
-                # if no keystroke is available, instead of waiting.
-                stdscr.nodelay(1)
-                while True:
-                    c = stdscr.getch()
-                    if c != -1:
-                        break
-                    stdscr.addstr(0, 0, '/')
-                    stdscr.refresh()
-                    board.display()
-                    stdscr.addstr(0, 0, '+')
-                    stdscr.refresh()
-
-                stdscr.nodelay(0)       # Disable nodelay mode
-                display_menu(stdscr, menu_y)
-
-            elif c in 'Ee':
-                board.erase()
-            elif c in 'Qq':
-                break
-            elif c in 'Rr':
-                board.make_random()
-                board.display(update_board=False)
-            elif c in 'Ss':
-                board.display()
-            else:
-                # Ignore incorrect keys
-                pass
-        elif c == curses.KEY_UP and ypos > 0:
-            ypos -= 1
-        elif c == curses.KEY_DOWN and ypos + 1 < board.Y:
-            ypos += 1
-        elif c == curses.KEY_LEFT and xpos > 0:
-            xpos -= 1
-        elif c == curses.KEY_RIGHT and xpos + 1 < board.X:
-            xpos += 1
-        elif c == curses.KEY_MOUSE:
-            mouse_id, mouse_x, mouse_y, mouse_z, button_state = curses.getmouse()
-            if (mouse_x > 0 and mouse_x < board.X + 1 and
-                mouse_y > 0 and mouse_y < board.Y + 1):
-                xpos = mouse_x - 1
-                ypos = mouse_y - 1
-                board.toggle(ypos, xpos)
-            else:
-                # They've clicked outside the board
-                curses.flash()
-        else:
-            # Ignore incorrect keys
-            pass
-
-
-def main(stdscr):
-    keyloop(stdscr)                 # Enter the main loop
-
-if __name__ == '__main__':
-    curses.wrapper(main)
diff --git a/Tools/demo/markov.py b/Tools/demo/markov.py
deleted file mode 100755
index 9729f3820fa026..00000000000000
--- a/Tools/demo/markov.py
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Markov chain simulation of words or characters.
-"""
-
-class Markov:
-    def __init__(self, histsize, choice):
-        self.histsize = histsize
-        self.choice = choice
-        self.trans = {}
-
-    def add(self, state, next):
-        self.trans.setdefault(state, []).append(next)
-
-    def put(self, seq):
-        n = self.histsize
-        add = self.add
-        add(None, seq[:0])
-        for i in range(len(seq)):
-            add(seq[max(0, i-n):i], seq[i:i+1])
-        add(seq[len(seq)-n:], None)
-
-    def get(self):
-        choice = self.choice
-        trans = self.trans
-        n = self.histsize
-        seq = choice(trans[None])
-        while True:
-            subseq = seq[max(0, len(seq)-n):]
-            options = trans[subseq]
-            next = choice(options)
-            if not next:
-                break
-            seq += next
-        return seq
-
-
-def test():
-    import sys, random, getopt
-    args = sys.argv[1:]
-    try:
-        opts, args = getopt.getopt(args, '0123456789cdwq')
-    except getopt.error:
-        print('Usage: %s [-#] [-cddqw] [file] ...' % sys.argv[0])
-        print('Options:')
-        print('-#: 1-digit history size (default 2)')
-        print('-c: characters (default)')
-        print('-w: words')
-        print('-d: more debugging output')
-        print('-q: no debugging output')
-        print('Input files (default stdin) are split in paragraphs')
-        print('separated blank lines and each paragraph is split')
-        print('in words by whitespace, then reconcatenated with')
-        print('exactly one space separating words.')
-        print('Output consists of paragraphs separated by blank')
-        print('lines, where lines are no longer than 72 characters.')
-        sys.exit(2)
-    histsize = 2
-    do_words = False
-    debug = 1
-    for o, a in opts:
-        if '-0' <= o <= '-9': histsize = int(o[1:])
-        if o == '-c': do_words = False
-        if o == '-d': debug += 1
-        if o == '-q': debug = 0
-        if o == '-w': do_words = True
-    if not args:
-        args = ['-']
-
-    m = Markov(histsize, random.choice)
-    try:
-        for filename in args:
-            if filename == '-':
-                f = sys.stdin
-                if f.isatty():
-                    print('Sorry, need stdin from file')
-                    continue
-            else:
-                f = open(filename, 'r')
-            with f:
-                if debug: print('processing', filename, '...')
-                text = f.read()
-            paralist = text.split('\n\n')
-            for para in paralist:
-                if debug > 1: print('feeding ...')
-                words = para.split()
-                if words:
-                    if do_words:
-                        data = tuple(words)
-                    else:
-                        data = ' '.join(words)
-                    m.put(data)
-    except KeyboardInterrupt:
-        print('Interrupted -- continue with data read so far')
-    if not m.trans:
-        print('No valid input files')
-        return
-    if debug: print('done.')
-
-    if debug > 1:
-        for key in m.trans.keys():
-            if key is None or len(key) < histsize:
-                print(repr(key), m.trans[key])
-        if histsize == 0: print(repr(''), m.trans[''])
-        print()
-    while True:
-        data = m.get()
-        if do_words:
-            words = data
-        else:
-            words = data.split()
-        n = 0
-        limit = 72
-        for w in words:
-            if n + len(w) > limit:
-                print()
-                n = 0
-            print(w, end=' ')
-            n += len(w) + 1
-        print()
-        print()
-
-if __name__ == "__main__":
-    test()
diff --git a/Tools/demo/mcast.py b/Tools/demo/mcast.py
deleted file mode 100755
index 924c7c3e80e7d2..00000000000000
--- a/Tools/demo/mcast.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Send/receive UDP multicast packets.
-Requires that your OS kernel supports IP multicast.
-
-Usage:
-  mcast -s (sender, IPv4)
-  mcast -s -6 (sender, IPv6)
-  mcast    (receivers, IPv4)
-  mcast  -6  (receivers, IPv6)
-"""
-
-MYPORT = 8123
-MYGROUP_4 = '225.0.0.250'
-MYGROUP_6 = 'ff15:7079:7468:6f6e:6465:6d6f:6d63:6173'
-MYTTL = 1 # Increase to reach other networks
-
-import time
-import struct
-import socket
-import sys
-
-def main():
-    group = MYGROUP_6 if "-6" in sys.argv[1:] else MYGROUP_4
-
-    if "-s" in sys.argv[1:]:
-        sender(group)
-    else:
-        receiver(group)
-
-
-def sender(group):
-    addrinfo = socket.getaddrinfo(group, None)[0]
-
-    s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
-
-    # Set Time-to-live (optional)
-    ttl_bin = struct.pack('@i', MYTTL)
-    if addrinfo[0] == socket.AF_INET: # IPv4
-        s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_bin)
-    else:
-        s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin)
-
-    while True:
-        data = repr(time.time()).encode('utf-8') + b'\0'
-        s.sendto(data, (addrinfo[4][0], MYPORT))
-        time.sleep(1)
-
-
-def receiver(group):
-    # Look up multicast group address in name server and find out IP version
-    addrinfo = socket.getaddrinfo(group, None)[0]
-
-    # Create a socket
-    s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
-
-    # Allow multiple copies of this program on one machine
-    # (not strictly needed)
-    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-
-    # Bind it to the port
-    s.bind(('', MYPORT))
-
-    group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0])
-    # Join group
-    if addrinfo[0] == socket.AF_INET: # IPv4
-        mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
-        s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
-    else:
-        mreq = group_bin + struct.pack('@I', 0)
-        s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
-
-    # Loop, printing any data we receive
-    while True:
-        data, sender = s.recvfrom(1500)
-        while data[-1:] == '\0': data = data[:-1] # Strip trailing \0's
-        print(str(sender) + '  ' + repr(data))
-
-
-if __name__ == '__main__':
-    main()
diff --git a/Tools/demo/queens.py b/Tools/demo/queens.py
deleted file mode 100755
index dcc1bae1ab332b..00000000000000
--- a/Tools/demo/queens.py
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-N queens problem.
-
-The (well-known) problem is due to Niklaus Wirth.
-
-This solution is inspired by Dijkstra (Structured Programming).  It is
-a classic recursive backtracking approach.
-"""
-
-N = 8                                   # Default; command line overrides
-
-class Queens:
-
-    def __init__(self, n=N):
-        self.n = n
-        self.reset()
-
-    def reset(self):
-        n = self.n
-        self.y = [None] * n             # Where is the queen in column x
-        self.row = [0] * n              # Is row[y] safe?
-        self.up = [0] * (2*n-1)         # Is upward diagonal[x-y] safe?
-        self.down = [0] * (2*n-1)       # Is downward diagonal[x+y] safe?
-        self.nfound = 0                 # Instrumentation
-
-    def solve(self, x=0):               # Recursive solver
-        for y in range(self.n):
-            if self.safe(x, y):
-                self.place(x, y)
-                if x+1 == self.n:
-                    self.display()
-                else:
-                    self.solve(x+1)
-                self.remove(x, y)
-
-    def safe(self, x, y):
-        return not self.row[y] and not self.up[x-y] and not self.down[x+y]
-
-    def place(self, x, y):
-        self.y[x] = y
-        self.row[y] = 1
-        self.up[x-y] = 1
-        self.down[x+y] = 1
-
-    def remove(self, x, y):
-        self.y[x] = None
-        self.row[y] = 0
-        self.up[x-y] = 0
-        self.down[x+y] = 0
-
-    silent = 0                          # If true, count solutions only
-
-    def display(self):
-        self.nfound = self.nfound + 1
-        if self.silent:
-            return
-        print('+-' + '--'*self.n + '+')
-        for y in range(self.n-1, -1, -1):
-            print('|', end=' ')
-            for x in range(self.n):
-                if self.y[x] == y:
-                    print("Q", end=' ')
-                else:
-                    print(".", end=' ')
-            print('|')
-        print('+-' + '--'*self.n + '+')
-
-def main():
-    import sys
-    silent = 0
-    n = N
-    if sys.argv[1:2] == ['-n']:
-        silent = 1
-        del sys.argv[1]
-    if sys.argv[1:]:
-        n = int(sys.argv[1])
-    q = Queens(n)
-    q.silent = silent
-    q.solve()
-    print("Found", q.nfound, "solutions.")
-
-if __name__ == "__main__":
-    main()
diff --git a/Tools/demo/redemo.py b/Tools/demo/redemo.py
deleted file mode 100755
index f801dfce5fe1a3..00000000000000
--- a/Tools/demo/redemo.py
+++ /dev/null
@@ -1,171 +0,0 @@
-#!/usr/bin/env python3
-
-"""Basic regular expression demonstration facility (Perl style syntax)."""
-
-from tkinter import *
-import re
-
-class ReDemo:
-
-    def __init__(self, master):
-        self.master = master
-
-        self.promptdisplay = Label(self.master, anchor=W,
-                text="Enter a Perl-style regular expression:")
-        self.promptdisplay.pack(side=TOP, fill=X)
-
-        self.regexdisplay = Entry(self.master)
-        self.regexdisplay.pack(fill=X)
-        self.regexdisplay.focus_set()
-
-        self.addoptions()
-
-        self.statusdisplay = Label(self.master, text="", anchor=W)
-        self.statusdisplay.pack(side=TOP, fill=X)
-
-        self.labeldisplay = Label(self.master, anchor=W,
-                text="Enter a string to search:")
-        self.labeldisplay.pack(fill=X)
-        self.labeldisplay.pack(fill=X)
-
-        self.showframe = Frame(master)
-        self.showframe.pack(fill=X, anchor=W)
-
-        self.showvar = StringVar(master)
-        self.showvar.set("first")
-
-        self.showfirstradio = Radiobutton(self.showframe,
-                                         text="Highlight first match",
-                                          variable=self.showvar,
-                                          value="first",
-                                          command=self.recompile)
-        self.showfirstradio.pack(side=LEFT)
-
-        self.showallradio = Radiobutton(self.showframe,
-                                        text="Highlight all matches",
-                                        variable=self.showvar,
-                                        value="all",
-                                        command=self.recompile)
-        self.showallradio.pack(side=LEFT)
-
-        self.stringdisplay = Text(self.master, width=60, height=4)
-        self.stringdisplay.pack(fill=BOTH, expand=1)
-        self.stringdisplay.tag_configure("hit", background="yellow")
-
-        self.grouplabel = Label(self.master, text="Groups:", anchor=W)
-        self.grouplabel.pack(fill=X)
-
-        self.grouplist = Listbox(self.master)
-        self.grouplist.pack(expand=1, fill=BOTH)
-
-        self.regexdisplay.bind('<Key>', self.recompile)
-        self.stringdisplay.bind('<Key>', self.reevaluate)
-
-        self.compiled = None
-        self.recompile()
-
-        btags = self.regexdisplay.bindtags()
-        self.regexdisplay.bindtags(btags[1:] + btags[:1])
-
-        btags = self.stringdisplay.bindtags()
-        self.stringdisplay.bindtags(btags[1:] + btags[:1])
-
-    def addoptions(self):
-        self.frames = []
-        self.boxes = []
-        self.vars = []
-        for name in ('IGNORECASE',
-                     'MULTILINE',
-                     'DOTALL',
-                     'VERBOSE'):
-            if len(self.boxes) % 3 == 0:
-                frame = Frame(self.master)
-                frame.pack(fill=X)
-                self.frames.append(frame)
-            val = getattr(re, name).value
-            var = IntVar()
-            box = Checkbutton(frame,
-                    variable=var, text=name,
-                    offvalue=0, onvalue=val,
-                    command=self.recompile)
-            box.pack(side=LEFT)
-            self.boxes.append(box)
-            self.vars.append(var)
-
-    def getflags(self):
-        flags = 0
-        for var in self.vars:
-            flags = flags | var.get()
-        return flags
-
-    def recompile(self, event=None):
-        try:
-            self.compiled = re.compile(self.regexdisplay.get(),
-                                       self.getflags())
-            bg = self.promptdisplay['background']
-            self.statusdisplay.config(text="", background=bg)
-        except re.error as msg:
-            self.compiled = None
-            self.statusdisplay.config(
-                    text="re.error: %s" % str(msg),
-                    background="red")
-        self.reevaluate()
-
-    def reevaluate(self, event=None):
-        try:
-            self.stringdisplay.tag_remove("hit", "1.0", END)
-        except TclError:
-            pass
-        try:
-            self.stringdisplay.tag_remove("hit0", "1.0", END)
-        except TclError:
-            pass
-        self.grouplist.delete(0, END)
-        if not self.compiled:
-            return
-        self.stringdisplay.tag_configure("hit", background="yellow")
-        self.stringdisplay.tag_configure("hit0", background="orange")
-        text = self.stringdisplay.get("1.0", END)
-        last = 0
-        nmatches = 0
-        while last <= len(text):
-            m = self.compiled.search(text, last)
-            if m is None:
-                break
-            first, last = m.span()
-            if last == first:
-                last = first+1
-                tag = "hit0"
-            else:
-                tag = "hit"
-            pfirst = "1.0 + %d chars" % first
-            plast = "1.0 + %d chars" % last
-            self.stringdisplay.tag_add(tag, pfirst, plast)
-            if nmatches == 0:
-                self.stringdisplay.yview_pickplace(pfirst)
-                groups = list(m.groups())
-                groups.insert(0, m.group())
-                for i in range(len(groups)):
-                    g = "%2d: %r" % (i, groups[i])
-                    self.grouplist.insert(END, g)
-            nmatches = nmatches + 1
-            if self.showvar.get() == "first":
-                break
-
-        if nmatches == 0:
-            self.statusdisplay.config(text="(no match)",
-                                      background="yellow")
-        else:
-            self.statusdisplay.config(text="")
-
-
-# Main function, run when invoked as a stand-alone Python program.
-
-def main():
-    root = Tk()
-    demo = ReDemo(root)
-    root.protocol('WM_DELETE_WINDOW', root.quit)
-    root.mainloop()
-
-if __name__ == '__main__':
-    main()
diff --git a/Tools/demo/rpython.py b/Tools/demo/rpython.py
deleted file mode 100755
index 11f72cb3dd26a6..00000000000000
--- a/Tools/demo/rpython.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Remote python client.
-Execute Python commands remotely and send output back.
-"""
-
-import sys
-from socket import socket, AF_INET, SOCK_STREAM, SHUT_WR
-
-PORT = 4127
-BUFSIZE = 1024
-
-def main():
-    if len(sys.argv) < 3:
-        print("usage: rpython host command")
-        sys.exit(2)
-    host = sys.argv[1]
-    port = PORT
-    i = host.find(':')
-    if i >= 0:
-        port = int(host[i+1:])
-        host = host[:i]
-    command = ' '.join(sys.argv[2:])
-    with socket(AF_INET, SOCK_STREAM) as s:
-        s.connect((host, port))
-        s.send(command.encode())
-        s.shutdown(SHUT_WR)
-        reply = b''
-        while True:
-            data = s.recv(BUFSIZE)
-            if not data:
-                break
-            reply += data
-        print(reply.decode(), end=' ')
-
-main()
diff --git a/Tools/demo/rpythond.py b/Tools/demo/rpythond.py
deleted file mode 100755
index 4e47fb9ec41563..00000000000000
--- a/Tools/demo/rpythond.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Remote python server.
-Execute Python commands remotely and send output back.
-
-WARNING: This version has a gaping security hole -- it accepts requests
-from any host on the internet!
-"""
-
-import sys
-from socket import socket, AF_INET, SOCK_STREAM
-import io
-import traceback
-
-PORT = 4127
-BUFSIZE = 1024
-
-def main():
-    if len(sys.argv) > 1:
-        port = int(sys.argv[1])
-    else:
-        port = PORT
-    s = socket(AF_INET, SOCK_STREAM)
-    s.bind(('', port))
-    s.listen(1)
-    while True:
-        conn, (remotehost, remoteport) = s.accept()
-        with conn:
-            print('connection from', remotehost, remoteport)
-            request = b''
-            while True:
-                data = conn.recv(BUFSIZE)
-                if not data:
-                    break
-                request += data
-            reply = execute(request.decode())
-            conn.send(reply.encode())
-
-def execute(request):
-    stdout = sys.stdout
-    stderr = sys.stderr
-    sys.stdout = sys.stderr = fakefile = io.StringIO()
-    try:
-        try:
-            exec(request, {}, {})
-        except:
-            print()
-            traceback.print_exc(100)
-    finally:
-        sys.stderr = stderr
-        sys.stdout = stdout
-    return fakefile.getvalue()
-
-try:
-    main()
-except KeyboardInterrupt:
-    pass
diff --git a/Tools/demo/sortvisu.py b/Tools/demo/sortvisu.py
deleted file mode 100755
index 056a0e05fb1ed2..00000000000000
--- a/Tools/demo/sortvisu.py
+++ /dev/null
@@ -1,635 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-Sorting algorithms visualizer using Tkinter.
-
-This module is comprised of three ``components'':
-
-- an array visualizer with methods that implement basic sorting
-operations (compare, swap) as well as methods for ``annotating'' the
-sorting algorithm (e.g. to show the pivot element);
-
-- a number of sorting algorithms (currently quicksort, insertion sort,
-selection sort and bubble sort, as well as a randomization function),
-all using the array visualizer for its basic operations and with calls
-to its annotation methods;
-
-- and a ``driver'' class which can be used as a Grail applet or as a
-stand-alone application.
-"""
-
-from tkinter import *
-import random
-
-XGRID = 10
-YGRID = 10
-WIDTH = 6
-
-
-class Array:
-
-    class Cancelled(BaseException):
-        pass
-
-    def __init__(self, master, data=None):
-        self.master = master
-        self.frame = Frame(self.master)
-        self.frame.pack(fill=X)
-        self.label = Label(self.frame)
-        self.label.pack()
-        self.canvas = Canvas(self.frame)
-        self.canvas.pack()
-        self.report = Label(self.frame)
-        self.report.pack()
-        self.left = self.canvas.create_line(0, 0, 0, 0)
-        self.right = self.canvas.create_line(0, 0, 0, 0)
-        self.pivot = self.canvas.create_line(0, 0, 0, 0)
-        self.items = []
-        self.size = self.maxvalue = 0
-        if data:
-            self.setdata(data)
-
-    def setdata(self, data):
-        olditems = self.items
-        self.items = []
-        for item in olditems:
-            item.delete()
-        self.size = len(data)
-        self.maxvalue = max(data)
-        self.canvas.config(width=(self.size+1)*XGRID,
-                           height=(self.maxvalue+1)*YGRID)
-        for i in range(self.size):
-            self.items.append(ArrayItem(self, i, data[i]))
-        self.reset("Sort demo, size %d" % self.size)
-
-    speed = "normal"
-
-    def setspeed(self, speed):
-        self.speed = speed
-
-    def destroy(self):
-        self.frame.destroy()
-
-    in_mainloop = 0
-    stop_mainloop = 0
-
-    def cancel(self):
-        self.stop_mainloop = 1
-        if self.in_mainloop:
-            self.master.quit()
-
-    def step(self):
-        if self.in_mainloop:
-            self.master.quit()
-
-    def wait(self, msecs):
-        if self.speed == "fastest":
-            msecs = 0
-        elif self.speed == "fast":
-            msecs = msecs//10
-        elif self.speed == "single-step":
-            msecs = 1000000000
-        if not self.stop_mainloop:
-            self.master.update()
-            id = self.master.after(msecs, self.master.quit)
-            self.in_mainloop = 1
-            self.master.mainloop()
-            self.master.after_cancel(id)
-            self.in_mainloop = 0
-        if self.stop_mainloop:
-            self.stop_mainloop = 0
-            self.message("Cancelled")
-            raise Array.Cancelled
-
-    def getsize(self):
-        return self.size
-
-    def show_partition(self, first, last):
-        for i in range(self.size):
-            item = self.items[i]
-            if first <= i < last:
-                self.canvas.itemconfig(item, fill='red')
-            else:
-                self.canvas.itemconfig(item, fill='orange')
-        self.hide_left_right_pivot()
-
-    def hide_partition(self):
-        for i in range(self.size):
-            item = self.items[i]
-            self.canvas.itemconfig(item, fill='red')
-        self.hide_left_right_pivot()
-
-    def show_left(self, left):
-        if not 0 <= left < self.size:
-            self.hide_left()
-            return
-        x1, y1, x2, y2 = self.items[left].position()
-##      top, bot = HIRO
-        self.canvas.coords(self.left, (x1 - 2, 0, x1 - 2, 9999))
-        self.master.update()
-
-    def show_right(self, right):
-        if not 0 <= right < self.size:
-            self.hide_right()
-            return
-        x1, y1, x2, y2 = self.items[right].position()
-        self.canvas.coords(self.right, (x2 + 2, 0, x2 + 2, 9999))
-        self.master.update()
-
-    def hide_left_right_pivot(self):
-        self.hide_left()
-        self.hide_right()
-        self.hide_pivot()
-
-    def hide_left(self):
-        self.canvas.coords(self.left, (0, 0, 0, 0))
-
-    def hide_right(self):
-        self.canvas.coords(self.right, (0, 0, 0, 0))
-
-    def show_pivot(self, pivot):
-        x1, y1, x2, y2 = self.items[pivot].position()
-        self.canvas.coords(self.pivot, (0, y1 - 2, 9999, y1 - 2))
-
-    def hide_pivot(self):
-        self.canvas.coords(self.pivot, (0, 0, 0, 0))
-
-    def swap(self, i, j):
-        if i == j: return
-        self.countswap()
-        item = self.items[i]
-        other = self.items[j]
-        self.items[i], self.items[j] = other, item
-        item.swapwith(other)
-
-    def compare(self, i, j):
-        self.countcompare()
-        item = self.items[i]
-        other = self.items[j]
-        return item.compareto(other)
-
-    def reset(self, msg):
-        self.ncompares = 0
-        self.nswaps = 0
-        self.message(msg)
-        self.updatereport()
-        self.hide_partition()
-
-    def message(self, msg):
-        self.label.config(text=msg)
-
-    def countswap(self):
-        self.nswaps = self.nswaps + 1
-        self.updatereport()
-
-    def countcompare(self):
-        self.ncompares = self.ncompares + 1
-        self.updatereport()
-
-    def updatereport(self):
-        text = "%d cmps, %d swaps" % (self.ncompares, self.nswaps)
-        self.report.config(text=text)
-
-
-class ArrayItem:
-
-    def __init__(self, array, index, value):
-        self.array = array
-        self.index = index
-        self.value = value
-        self.canvas = array.canvas
-        x1, y1, x2, y2 = self.position()
-        self.item_id = array.canvas.create_rectangle(x1, y1, x2, y2,
-            fill='red', outline='black', width=1)
-        self.canvas.tag_bind(self.item_id, '<Button-1>', self.mouse_down)
-        self.canvas.tag_bind(self.item_id, '<Button1-Motion>', self.mouse_move)
-        self.canvas.tag_bind(self.item_id, '<ButtonRelease-1>', self.mouse_up)
-
-    def delete(self):
-        item_id = self.item_id
-        self.array = None
-        self.item_id = None
-        self.canvas.delete(item_id)
-
-    def mouse_down(self, event):
-        self.lastx = event.x
-        self.lasty = event.y
-        self.origx = event.x
-        self.origy = event.y
-        self.canvas.tag_raise(self.item_id)
-
-    def mouse_move(self, event):
-        self.canvas.move(self.item_id,
-                         event.x - self.lastx, event.y - self.lasty)
-        self.lastx = event.x
-        self.lasty = event.y
-
-    def mouse_up(self, event):
-        i = self.nearestindex(event.x)
-        if i >= self.array.getsize():
-            i = self.array.getsize() - 1
-        if i < 0:
-            i = 0
-        other = self.array.items[i]
-        here = self.index
-        self.array.items[here], self.array.items[i] = other, self
-        self.index = i
-        x1, y1, x2, y2 = self.position()
-        self.canvas.coords(self.item_id, (x1, y1, x2, y2))
-        other.setindex(here)
-
-    def setindex(self, index):
-        nsteps = steps(self.index, index)
-        if not nsteps: return
-        if self.array.speed == "fastest":
-            nsteps = 0
-        oldpts = self.position()
-        self.index = index
-        newpts = self.position()
-        trajectory = interpolate(oldpts, newpts, nsteps)
-        self.canvas.tag_raise(self.item_id)
-        for pts in trajectory:
-            self.canvas.coords(self.item_id, pts)
-            self.array.wait(50)
-
-    def swapwith(self, other):
-        nsteps = steps(self.index, other.index)
-        if not nsteps: return
-        if self.array.speed == "fastest":
-            nsteps = 0
-        myoldpts = self.position()
-        otheroldpts = other.position()
-        self.index, other.index = other.index, self.index
-        mynewpts = self.position()
-        othernewpts = other.position()
-        myfill = self.canvas.itemcget(self.item_id, 'fill')
-        otherfill = self.canvas.itemcget(other.item_id, 'fill')
-        self.canvas.itemconfig(self.item_id, fill='green')
-        self.canvas.itemconfig(other.item_id, fill='yellow')
-        self.array.master.update()
-        if self.array.speed == "single-step":
-            self.canvas.coords(self.item_id, mynewpts)
-            self.canvas.coords(other.item_id, othernewpts)
-            self.array.master.update()
-            self.canvas.itemconfig(self.item_id, fill=myfill)
-            self.canvas.itemconfig(other.item_id, fill=otherfill)
-            self.array.wait(0)
-            return
-        mytrajectory = interpolate(myoldpts, mynewpts, nsteps)
-        othertrajectory = interpolate(otheroldpts, othernewpts, nsteps)
-        if self.value > other.value:
-            self.canvas.tag_raise(self.item_id)
-            self.canvas.tag_raise(other.item_id)
-        else:
-            self.canvas.tag_raise(other.item_id)
-            self.canvas.tag_raise(self.item_id)
-        try:
-            for i in range(len(mytrajectory)):
-                mypts = mytrajectory[i]
-                otherpts = othertrajectory[i]
-                self.canvas.coords(self.item_id, mypts)
-                self.canvas.coords(other.item_id, otherpts)
-                self.array.wait(50)
-        finally:
-            mypts = mytrajectory[-1]
-            otherpts = othertrajectory[-1]
-            self.canvas.coords(self.item_id, mypts)
-            self.canvas.coords(other.item_id, otherpts)
-            self.canvas.itemconfig(self.item_id, fill=myfill)
-            self.canvas.itemconfig(other.item_id, fill=otherfill)
-
-    def compareto(self, other):
-        myfill = self.canvas.itemcget(self.item_id, 'fill')
-        otherfill = self.canvas.itemcget(other.item_id, 'fill')
-        if self.value < other.value:
-            myflash = 'white'
-            otherflash = 'black'
-            outcome = -1
-        elif self.value > other.value:
-            myflash = 'black'
-            otherflash = 'white'
-            outcome = 1
-        else:
-            myflash = otherflash = 'grey'
-            outcome = 0
-        try:
-            self.canvas.itemconfig(self.item_id, fill=myflash)
-            self.canvas.itemconfig(other.item_id, fill=otherflash)
-            self.array.wait(500)
-        finally:
-            self.canvas.itemconfig(self.item_id, fill=myfill)
-            self.canvas.itemconfig(other.item_id, fill=otherfill)
-        return outcome
-
-    def position(self):
-        x1 = (self.index+1)*XGRID - WIDTH//2
-        x2 = x1+WIDTH
-        y2 = (self.array.maxvalue+1)*YGRID
-        y1 = y2 - (self.value)*YGRID
-        return x1, y1, x2, y2
-
-    def nearestindex(self, x):
-        return int(round(float(x)/XGRID)) - 1
-
-
-# Subroutines that don't need an object
-
-def steps(here, there):
-    nsteps = abs(here - there)
-    if nsteps <= 3:
-        nsteps = nsteps * 3
-    elif nsteps <= 5:
-        nsteps = nsteps * 2
-    elif nsteps > 10:
-        nsteps = 10
-    return nsteps
-
-def interpolate(oldpts, newpts, n):
-    if len(oldpts) != len(newpts):
-        raise ValueError("can't interpolate arrays of different length")
-    pts = [0]*len(oldpts)
-    res = [tuple(oldpts)]
-    for i in range(1, n):
-        for k in range(len(pts)):
-            pts[k] = oldpts[k] + (newpts[k] - oldpts[k])*i//n
-        res.append(tuple(pts))
-    res.append(tuple(newpts))
-    return res
-
-
-# Various (un)sorting algorithms
-
-def uniform(array):
-    size = array.getsize()
-    array.setdata([(size+1)//2] * size)
-    array.reset("Uniform data, size %d" % size)
-
-def distinct(array):
-    size = array.getsize()
-    array.setdata(range(1, size+1))
-    array.reset("Distinct data, size %d" % size)
-
-def randomize(array):
-    array.reset("Randomizing")
-    n = array.getsize()
-    for i in range(n):
-        j = random.randint(0, n-1)
-        array.swap(i, j)
-    array.message("Randomized")
-
-def insertionsort(array):
-    size = array.getsize()
-    array.reset("Insertion sort")
-    for i in range(1, size):
-        j = i-1
-        while j >= 0:
-            if array.compare(j, j+1) <= 0:
-                break
-            array.swap(j, j+1)
-            j = j-1
-    array.message("Sorted")
-
-def selectionsort(array):
-    size = array.getsize()
-    array.reset("Selection sort")
-    try:
-        for i in range(size):
-            array.show_partition(i, size)
-            for j in range(i+1, size):
-                if array.compare(i, j) > 0:
-                    array.swap(i, j)
-        array.message("Sorted")
-    finally:
-        array.hide_partition()
-
-def bubblesort(array):
-    size = array.getsize()
-    array.reset("Bubble sort")
-    for i in range(size):
-        for j in range(1, size):
-            if array.compare(j-1, j) > 0:
-                array.swap(j-1, j)
-    array.message("Sorted")
-
-def quicksort(array):
-    size = array.getsize()
-    array.reset("Quicksort")
-    try:
-        stack = [(0, size)]
-        while stack:
-            first, last = stack[-1]
-            del stack[-1]
-            array.show_partition(first, last)
-            if last-first < 5:
-                array.message("Insertion sort")
-                for i in range(first+1, last):
-                    j = i-1
-                    while j >= first:
-                        if array.compare(j, j+1) <= 0:
-                            break
-                        array.swap(j, j+1)
-                        j = j-1
-                continue
-            array.message("Choosing pivot")
-            j, i, k = first, (first+last) // 2, last-1
-            if array.compare(k, i) < 0:
-                array.swap(k, i)
-            if array.compare(k, j) < 0:
-                array.swap(k, j)
-            if array.compare(j, i) < 0:
-                array.swap(j, i)
-            pivot = j
-            array.show_pivot(pivot)
-            array.message("Pivot at left of partition")
-            array.wait(1000)
-            left = first
-            right = last
-            while True:
-                array.message("Sweep right pointer")
-                right = right-1
-                array.show_right(right)
-                while right > first and array.compare(right, pivot) >= 0:
-                    right = right-1
-                    array.show_right(right)
-                array.message("Sweep left pointer")
-                left = left+1
-                array.show_left(left)
-                while left < last and array.compare(left, pivot) <= 0:
-                    left = left+1
-                    array.show_left(left)
-                if left > right:
-                    array.message("End of partition")
-                    break
-                array.message("Swap items")
-                array.swap(left, right)
-            array.message("Swap pivot back")
-            array.swap(pivot, right)
-            n1 = right-first
-            n2 = last-left
-            if n1 > 1: stack.append((first, right))
-            if n2 > 1: stack.append((left, last))
-        array.message("Sorted")
-    finally:
-        array.hide_partition()
-
-def demosort(array):
-    while True:
-        for alg in [quicksort, insertionsort, selectionsort, bubblesort]:
-            randomize(array)
-            alg(array)
-
-
-# Sort demo class -- usable as a Grail applet
-
-class SortDemo:
-
-    def __init__(self, master, size=15):
-        self.master = master
-        self.size = size
-        self.busy = 0
-        self.array = Array(self.master)
-
-        self.botframe = Frame(master)
-        self.botframe.pack(side=BOTTOM)
-        self.botleftframe = Frame(self.botframe)
-        self.botleftframe.pack(side=LEFT, fill=Y)
-        self.botrightframe = Frame(self.botframe)
-        self.botrightframe.pack(side=RIGHT, fill=Y)
-
-        self.b_qsort = Button(self.botleftframe,
-                              text="Quicksort", command=self.c_qsort)
-        self.b_qsort.pack(fill=X)
-        self.b_isort = Button(self.botleftframe,
-                              text="Insertion sort", command=self.c_isort)
-        self.b_isort.pack(fill=X)
-        self.b_ssort = Button(self.botleftframe,
-                              text="Selection sort", command=self.c_ssort)
-        self.b_ssort.pack(fill=X)
-        self.b_bsort = Button(self.botleftframe,
-                              text="Bubble sort", command=self.c_bsort)
-        self.b_bsort.pack(fill=X)
-
-        # Terrible hack to overcome limitation of OptionMenu...
-        class MyIntVar(IntVar):
-            def __init__(self, master, demo):
-                self.demo = demo
-                IntVar.__init__(self, master)
-            def set(self, value):
-                IntVar.set(self, value)
-                if str(value) != '0':
-                    self.demo.resize(value)
-
-        self.v_size = MyIntVar(self.master, self)
-        self.v_size.set(size)
-        sizes = [1, 2, 3, 4] + list(range(5, 55, 5))
-        if self.size not in sizes:
-            sizes.append(self.size)
-            sizes.sort()
-        self.m_size = OptionMenu(self.botleftframe, self.v_size, *sizes)
-        self.m_size.pack(fill=X)
-
-        self.v_speed = StringVar(self.master)
-        self.v_speed.set("normal")
-        self.m_speed = OptionMenu(self.botleftframe, self.v_speed,
-                                  "single-step", "normal", "fast", "fastest")
-        self.m_speed.pack(fill=X)
-
-        self.b_step = Button(self.botleftframe,
-                             text="Step", command=self.c_step)
-        self.b_step.pack(fill=X)
-
-        self.b_randomize = Button(self.botrightframe,
-                                  text="Randomize", command=self.c_randomize)
-        self.b_randomize.pack(fill=X)
-        self.b_uniform = Button(self.botrightframe,
-                                  text="Uniform", command=self.c_uniform)
-        self.b_uniform.pack(fill=X)
-        self.b_distinct = Button(self.botrightframe,
-                                  text="Distinct", command=self.c_distinct)
-        self.b_distinct.pack(fill=X)
-        self.b_demo = Button(self.botrightframe,
-                             text="Demo", command=self.c_demo)
-        self.b_demo.pack(fill=X)
-        self.b_cancel = Button(self.botrightframe,
-                               text="Cancel", command=self.c_cancel)
-        self.b_cancel.pack(fill=X)
-        self.b_cancel.config(state=DISABLED)
-        self.b_quit = Button(self.botrightframe,
-                             text="Quit", command=self.c_quit)
-        self.b_quit.pack(fill=X)
-
-    def resize(self, newsize):
-        if self.busy:
-            self.master.bell()
-            return
-        self.size = newsize
-        self.array.setdata(range(1, self.size+1))
-
-    def c_qsort(self):
-        self.run(quicksort)
-
-    def c_isort(self):
-        self.run(insertionsort)
-
-    def c_ssort(self):
-        self.run(selectionsort)
-
-    def c_bsort(self):
-        self.run(bubblesort)
-
-    def c_demo(self):
-        self.run(demosort)
-
-    def c_randomize(self):
-        self.run(randomize)
-
-    def c_uniform(self):
-        self.run(uniform)
-
-    def c_distinct(self):
-        self.run(distinct)
-
-    def run(self, func):
-        if self.busy:
-            self.master.bell()
-            return
-        self.busy = 1
-        self.array.setspeed(self.v_speed.get())
-        self.b_cancel.config(state=NORMAL)
-        try:
-            func(self.array)
-        except Array.Cancelled:
-            pass
-        self.b_cancel.config(state=DISABLED)
-        self.busy = 0
-
-    def c_cancel(self):
-        if not self.busy:
-            self.master.bell()
-            return
-        self.array.cancel()
-
-    def c_step(self):
-        if not self.busy:
-            self.master.bell()
-            return
-        self.v_speed.set("single-step")
-        self.array.setspeed("single-step")
-        self.array.step()
-
-    def c_quit(self):
-        if self.busy:
-            self.array.cancel()
-        self.master.after_idle(self.master.quit)
-
-
-# Main program -- for stand-alone operation outside Grail
-
-def main():
-    root = Tk()
-    demo = SortDemo(root)
-    root.protocol('WM_DELETE_WINDOW', demo.c_quit)
-    root.mainloop()
-
-if __name__ == '__main__':
-    main()
diff --git a/Tools/demo/spreadsheet.py b/Tools/demo/spreadsheet.py
deleted file mode 100755
index bf88820dca3471..00000000000000
--- a/Tools/demo/spreadsheet.py
+++ /dev/null
@@ -1,829 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-SS1 -- a spreadsheet-like application.
-"""
-
-import os
-import re
-import sys
-from xml.parsers import expat
-from xml.sax.saxutils import escape
-
-LEFT, CENTER, RIGHT = "LEFT", "CENTER", "RIGHT"
-
-def ljust(x, n):
-    return x.ljust(n)
-def center(x, n):
-    return x.center(n)
-def rjust(x, n):
-    return x.rjust(n)
-align2action = {LEFT: ljust, CENTER: center, RIGHT: rjust}
-
-align2xml = {LEFT: "left", CENTER: "center", RIGHT: "right"}
-xml2align = {"left": LEFT, "center": CENTER, "right": RIGHT}
-
-align2anchor = {LEFT: "w", CENTER: "center", RIGHT: "e"}
-
-def sum(seq):
-    total = 0
-    for x in seq:
-        if x is not None:
-            total += x
-    return total
-
-class Sheet:
-
-    def __init__(self):
-        self.cells = {} # {(x, y): cell, ...}
-        self.ns = dict(
-            cell = self.cellvalue,
-            cells = self.multicellvalue,
-            sum = sum,
-        )
-
-    def cellvalue(self, x, y):
-        cell = self.getcell(x, y)
-        if hasattr(cell, 'recalc'):
-            return cell.recalc(self.ns)
-        else:
-            return cell
-
-    def multicellvalue(self, x1, y1, x2, y2):
-        if x1 > x2:
-            x1, x2 = x2, x1
-        if y1 > y2:
-            y1, y2 = y2, y1
-        seq = []
-        for y in range(y1, y2+1):
-            for x in range(x1, x2+1):
-                seq.append(self.cellvalue(x, y))
-        return seq
-
-    def getcell(self, x, y):
-        return self.cells.get((x, y))
-
-    def setcell(self, x, y, cell):
-        assert x > 0 and y > 0
-        assert isinstance(cell, BaseCell)
-        self.cells[x, y] = cell
-
-    def clearcell(self, x, y):
-        try:
-            del self.cells[x, y]
-        except KeyError:
-            pass
-
-    def clearcells(self, x1, y1, x2, y2):
-        for xy in self.selectcells(x1, y1, x2, y2):
-            del self.cells[xy]
-
-    def clearrows(self, y1, y2):
-        self.clearcells(0, y1, sys.maxsize, y2)
-
-    def clearcolumns(self, x1, x2):
-        self.clearcells(x1, 0, x2, sys.maxsize)
-
-    def selectcells(self, x1, y1, x2, y2):
-        if x1 > x2:
-            x1, x2 = x2, x1
-        if y1 > y2:
-            y1, y2 = y2, y1
-        return [(x, y) for x, y in self.cells
-                if x1 <= x <= x2 and y1 <= y <= y2]
-
-    def movecells(self, x1, y1, x2, y2, dx, dy):
-        if dx == 0 and dy == 0:
-            return
-        if x1 > x2:
-            x1, x2 = x2, x1
-        if y1 > y2:
-            y1, y2 = y2, y1
-        assert x1+dx > 0 and y1+dy > 0
-        new = {}
-        for x, y in self.cells:
-            cell = self.cells[x, y]
-            if hasattr(cell, 'renumber'):
-                cell = cell.renumber(x1, y1, x2, y2, dx, dy)
-            if x1 <= x <= x2 and y1 <= y <= y2:
-                x += dx
-                y += dy
-            new[x, y] = cell
-        self.cells = new
-
-    def insertrows(self, y, n):
-        assert n > 0
-        self.movecells(0, y, sys.maxsize, sys.maxsize, 0, n)
-
-    def deleterows(self, y1, y2):
-        if y1 > y2:
-            y1, y2 = y2, y1
-        self.clearrows(y1, y2)
-        self.movecells(0, y2+1, sys.maxsize, sys.maxsize, 0, y1-y2-1)
-
-    def insertcolumns(self, x, n):
-        assert n > 0
-        self.movecells(x, 0, sys.maxsize, sys.maxsize, n, 0)
-
-    def deletecolumns(self, x1, x2):
-        if x1 > x2:
-            x1, x2 = x2, x1
-        self.clearcells(x1, x2)
-        self.movecells(x2+1, 0, sys.maxsize, sys.maxsize, x1-x2-1, 0)
-
-    def getsize(self):
-        maxx = maxy = 0
-        for x, y in self.cells:
-            maxx = max(maxx, x)
-            maxy = max(maxy, y)
-        return maxx, maxy
-
-    def reset(self):
-        for cell in self.cells.values():
-            if hasattr(cell, 'reset'):
-                cell.reset()
-
-    def recalc(self):
-        self.reset()
-        for cell in self.cells.values():
-            if hasattr(cell, 'recalc'):
-                cell.recalc(self.ns)
-
-    def display(self):
-        maxx, maxy = self.getsize()
-        width, height = maxx+1, maxy+1
-        colwidth = [1] * width
-        full = {}
-        # Add column heading labels in row 0
-        for x in range(1, width):
-            full[x, 0] = text, alignment = colnum2name(x), RIGHT
-            colwidth[x] = max(colwidth[x], len(text))
-        # Add row labels in column 0
-        for y in range(1, height):
-            full[0, y] = text, alignment = str(y), RIGHT
-            colwidth[0] = max(colwidth[0], len(text))
-        # Add sheet cells in columns with x>0 and y>0
-        for (x, y), cell in self.cells.items():
-            if x <= 0 or y <= 0:
-                continue
-            if hasattr(cell, 'recalc'):
-                cell.recalc(self.ns)
-            if hasattr(cell, 'format'):
-                text, alignment = cell.format()
-                assert isinstance(text, str)
-                assert alignment in (LEFT, CENTER, RIGHT)
-            else:
-                text = str(cell)
-                if isinstance(cell, str):
-                    alignment = LEFT
-                else:
-                    alignment = RIGHT
-            full[x, y] = (text, alignment)
-            colwidth[x] = max(colwidth[x], len(text))
-        # Calculate the horizontal separator line (dashes and dots)
-        sep = ""
-        for x in range(width):
-            if sep:
-                sep += "+"
-            sep += "-"*colwidth[x]
-        # Now print The full grid
-        for y in range(height):
-            line = ""
-            for x in range(width):
-                text, alignment = full.get((x, y)) or ("", LEFT)
-                text = align2action[alignment](text, colwidth[x])
-                if line:
-                    line += '|'
-                line += text
-            print(line)
-            if y == 0:
-                print(sep)
-
-    def xml(self):
-        out = ['<spreadsheet>']
-        for (x, y), cell in self.cells.items():
-            if hasattr(cell, 'xml'):
-                cellxml = cell.xml()
-            else:
-                cellxml = '<value>%s</value>' % escape(cell)
-            out.append('<cell row="%s" col="%s">\n  %s\n</cell>' %
-                       (y, x, cellxml))
-        out.append('</spreadsheet>')
-        return '\n'.join(out)
-
-    def save(self, filename):
-        text = self.xml()
-        with open(filename, "w", encoding='utf-8') as f:
-            f.write(text)
-            if text and not text.endswith('\n'):
-                f.write('\n')
-
-    def load(self, filename):
-        with open(filename, 'rb') as f:
-            SheetParser(self).parsefile(f)
-
-class SheetParser:
-
-    def __init__(self, sheet):
-        self.sheet = sheet
-
-    def parsefile(self, f):
-        parser = expat.ParserCreate()
-        parser.StartElementHandler = self.startelement
-        parser.EndElementHandler = self.endelement
-        parser.CharacterDataHandler = self.data
-        parser.ParseFile(f)
-
-    def startelement(self, tag, attrs):
-        method = getattr(self, 'start_'+tag, None)
-        if method:
-            method(attrs)
-        self.texts = []
-
-    def data(self, text):
-        self.texts.append(text)
-
-    def endelement(self, tag):
-        method = getattr(self, 'end_'+tag, None)
-        if method:
-            method("".join(self.texts))
-
-    def start_cell(self, attrs):
-        self.y = int(attrs.get("row"))
-        self.x = int(attrs.get("col"))
-
-    def start_value(self, attrs):
-        self.fmt = attrs.get('format')
-        self.alignment = xml2align.get(attrs.get('align'))
-
-    start_formula = start_value
-
-    def end_int(self, text):
-        try:
-            self.value = int(text)
-        except (TypeError, ValueError):
-            self.value = None
-
-    end_long = end_int
-
-    def end_double(self, text):
-        try:
-            self.value = float(text)
-        except (TypeError, ValueError):
-            self.value = None
-
-    def end_complex(self, text):
-        try:
-            self.value = complex(text)
-        except (TypeError, ValueError):
-            self.value = None
-
-    def end_string(self, text):
-        self.value = text
-
-    def end_value(self, text):
-        if isinstance(self.value, BaseCell):
-            self.cell = self.value
-        elif isinstance(self.value, str):
-            self.cell = StringCell(self.value,
-                                   self.fmt or "%s",
-                                   self.alignment or LEFT)
-        else:
-            self.cell = NumericCell(self.value,
-                                    self.fmt or "%s",
-                                    self.alignment or RIGHT)
-
-    def end_formula(self, text):
-        self.cell = FormulaCell(text,
-                                self.fmt or "%s",
-                                self.alignment or RIGHT)
-
-    def end_cell(self, text):
-        self.sheet.setcell(self.x, self.y, self.cell)
-
-class BaseCell:
-    __init__ = None # Must provide
-    """Abstract base class for sheet cells.
-
-    Subclasses may but needn't provide the following APIs:
-
-    cell.reset() -- prepare for recalculation
-    cell.recalc(ns) -> value -- recalculate formula
-    cell.format() -> (value, alignment) -- return formatted value
-    cell.xml() -> string -- return XML
-    """
-
-class NumericCell(BaseCell):
-
-    def __init__(self, value, fmt="%s", alignment=RIGHT):
-        assert isinstance(value, (int, float, complex))
-        assert alignment in (LEFT, CENTER, RIGHT)
-        self.value = value
-        self.fmt = fmt
-        self.alignment = alignment
-
-    def recalc(self, ns):
-        return self.value
-
-    def format(self):
-        try:
-            text = self.fmt % self.value
-        except:
-            text = str(self.value)
-        return text, self.alignment
-
-    def xml(self):
-        method = getattr(self, '_xml_' + type(self.value).__name__)
-        return '<value align="%s" format="%s">%s</value>' % (
-                align2xml[self.alignment],
-                self.fmt,
-                method())
-
-    def _xml_int(self):
-        if -2**31 <= self.value < 2**31:
-            return '<int>%s</int>' % self.value
-        else:
-            return '<long>%s</long>' % self.value
-
-    def _xml_float(self):
-        return '<double>%r</double>' % self.value
-
-    def _xml_complex(self):
-        return '<complex>%r</complex>' % self.value
-
-class StringCell(BaseCell):
-
-    def __init__(self, text, fmt="%s", alignment=LEFT):
-        assert isinstance(text, str)
-        assert alignment in (LEFT, CENTER, RIGHT)
-        self.text = text
-        self.fmt = fmt
-        self.alignment = alignment
-
-    def recalc(self, ns):
-        return self.text
-
-    def format(self):
-        return self.text, self.alignment
-
-    def xml(self):
-        s = '<value align="%s" format="%s"><string>%s</string></value>'
-        return s % (
-            align2xml[self.alignment],
-            self.fmt,
-            escape(self.text))
-
-class FormulaCell(BaseCell):
-
-    def __init__(self, formula, fmt="%s", alignment=RIGHT):
-        assert alignment in (LEFT, CENTER, RIGHT)
-        self.formula = formula
-        self.translated = translate(self.formula)
-        self.fmt = fmt
-        self.alignment = alignment
-        self.reset()
-
-    def reset(self):
-        self.value = None
-
-    def recalc(self, ns):
-        if self.value is None:
-            try:
-                self.value = eval(self.translated, ns)
-            except:
-                exc = sys.exc_info()[0]
-                if hasattr(exc, "__name__"):
-                    self.value = exc.__name__
-                else:
-                    self.value = str(exc)
-        return self.value
-
-    def format(self):
-        try:
-            text = self.fmt % self.value
-        except:
-            text = str(self.value)
-        return text, self.alignment
-
-    def xml(self):
-        return '<formula align="%s" format="%s">%s</formula>' % (
-            align2xml[self.alignment],
-            self.fmt,
-            escape(self.formula))
-
-    def renumber(self, x1, y1, x2, y2, dx, dy):
-        out = []
-        for part in re.split(r'(\w+)', self.formula):
-            m = re.match('^([A-Z]+)([1-9][0-9]*)$', part)
-            if m is not None:
-                sx, sy = m.groups()
-                x = colname2num(sx)
-                y = int(sy)
-                if x1 <= x <= x2 and y1 <= y <= y2:
-                    part = cellname(x+dx, y+dy)
-            out.append(part)
-        return FormulaCell("".join(out), self.fmt, self.alignment)
-
-def translate(formula):
-    """Translate a formula containing fancy cell names to valid Python code.
-
-    Examples:
-        B4 -> cell(2, 4)
-        B4:Z100 -> cells(2, 4, 26, 100)
-    """
-    out = []
-    for part in re.split(r"(\w+(?::\w+)?)", formula):
-        m = re.match(r"^([A-Z]+)([1-9][0-9]*)(?::([A-Z]+)([1-9][0-9]*))?$", part)
-        if m is None:
-            out.append(part)
-        else:
-            x1, y1, x2, y2 = m.groups()
-            x1 = colname2num(x1)
-            if x2 is None:
-                s = "cell(%s, %s)" % (x1, y1)
-            else:
-                x2 = colname2num(x2)
-                s = "cells(%s, %s, %s, %s)" % (x1, y1, x2, y2)
-            out.append(s)
-    return "".join(out)
-
-def cellname(x, y):
-    "Translate a cell coordinate to a fancy cell name (e.g. (1, 1)->'A1')."
-    assert x > 0 # Column 0 has an empty name, so can't use that
-    return colnum2name(x) + str(y)
-
-def colname2num(s):
-    "Translate a column name to number (e.g. 'A'->1, 'Z'->26, 'AA'->27)."
-    s = s.upper()
-    n = 0
-    for c in s:
-        assert 'A' <= c <= 'Z'
-        n = n*26 + ord(c) - ord('A') + 1
-    return n
-
-def colnum2name(n):
-    "Translate a column number to name (e.g. 1->'A', etc.)."
-    assert n > 0
-    s = ""
-    while n:
-        n, m = divmod(n-1, 26)
-        s = chr(m+ord('A')) + s
-    return s
-
-import tkinter as Tk
-
-class SheetGUI:
-
-    """Beginnings of a GUI for a spreadsheet.
-
-    TO DO:
-    - clear multiple cells
-    - Insert, clear, remove rows or columns
-    - Show new contents while typing
-    - Scroll bars
-    - Grow grid when window is grown
-    - Proper menus
-    - Undo, redo
-    - Cut, copy and paste
-    - Formatting and alignment
-    """
-
-    def __init__(self, filename="sheet1.xml", rows=10, columns=5):
-        """Constructor.
-
-        Load the sheet from the filename argument.
-        Set up the Tk widget tree.
-        """
-        # Create and load the sheet
-        self.filename = filename
-        self.sheet = Sheet()
-        if os.path.isfile(filename):
-            self.sheet.load(filename)
-        # Calculate the needed grid size
-        maxx, maxy = self.sheet.getsize()
-        rows = max(rows, maxy)
-        columns = max(columns, maxx)
-        # Create the widgets
-        self.root = Tk.Tk()
-        self.root.wm_title("Spreadsheet: %s" % self.filename)
-        self.beacon = Tk.Label(self.root, text="A1",
-                               font=('helvetica', 16, 'bold'))
-        self.entry = Tk.Entry(self.root)
-        self.savebutton = Tk.Button(self.root, text="Save",
-                                    command=self.save)
-        self.cellgrid = Tk.Frame(self.root)
-        # Configure the widget lay-out
-        self.cellgrid.pack(side="bottom", expand=1, fill="both")
-        self.beacon.pack(side="left")
-        self.savebutton.pack(side="right")
-        self.entry.pack(side="left", expand=1, fill="x")
-        # Bind some events
-        self.entry.bind("<Return>", self.return_event)
-        self.entry.bind("<Shift-Return>", self.shift_return_event)
-        self.entry.bind("<Tab>", self.tab_event)
-        self.entry.bind("<Shift-Tab>", self.shift_tab_event)
-        self.entry.bind("<Delete>", self.delete_event)
-        self.entry.bind("<Escape>", self.escape_event)
-        # Now create the cell grid
-        self.makegrid(rows, columns)
-        # Select the top-left cell
-        self.currentxy = None
-        self.cornerxy = None
-        self.setcurrent(1, 1)
-        # Copy the sheet cells to the GUI cells
-        self.sync()
-
-    def delete_event(self, event):
-        if self.cornerxy != self.currentxy and self.cornerxy is not None:
-            self.sheet.clearcells(*(self.currentxy + self.cornerxy))
-        else:
-            self.sheet.clearcell(*self.currentxy)
-        self.sync()
-        self.entry.delete(0, 'end')
-        return "break"
-
-    def escape_event(self, event):
-        x, y = self.currentxy
-        self.load_entry(x, y)
-
-    def load_entry(self, x, y):
-        cell = self.sheet.getcell(x, y)
-        if cell is None:
-            text = ""
-        elif isinstance(cell, FormulaCell):
-            text = '=' + cell.formula
-        else:
-            text, alignment = cell.format()
-        self.entry.delete(0, 'end')
-        self.entry.insert(0, text)
-        self.entry.selection_range(0, 'end')
-
-    def makegrid(self, rows, columns):
-        """Helper to create the grid of GUI cells.
-
-        The edge (x==0 or y==0) is filled with labels; the rest is real cells.
-        """
-        self.rows = rows
-        self.columns = columns
-        self.gridcells = {}
-        # Create the top left corner cell (which selects all)
-        cell = Tk.Label(self.cellgrid, relief='raised')
-        cell.grid_configure(column=0, row=0, sticky='NSWE')
-        cell.bind("<ButtonPress-1>", self.selectall)
-        # Create the top row of labels, and configure the grid columns
-        for x in range(1, columns+1):
-            self.cellgrid.grid_columnconfigure(x, minsize=64)
-            cell = Tk.Label(self.cellgrid, text=colnum2name(x), relief='raised')
-            cell.grid_configure(column=x, row=0, sticky='WE')
-            self.gridcells[x, 0] = cell
-            cell.__x = x
-            cell.__y = 0
-            cell.bind("<ButtonPress-1>", self.selectcolumn)
-            cell.bind("<B1-Motion>", self.extendcolumn)
-            cell.bind("<ButtonRelease-1>", self.extendcolumn)
-            cell.bind("<Shift-Button-1>", self.extendcolumn)
-        # Create the leftmost column of labels
-        for y in range(1, rows+1):
-            cell = Tk.Label(self.cellgrid, text=str(y), relief='raised')
-            cell.grid_configure(column=0, row=y, sticky='WE')
-            self.gridcells[0, y] = cell
-            cell.__x = 0
-            cell.__y = y
-            cell.bind("<ButtonPress-1>", self.selectrow)
-            cell.bind("<B1-Motion>", self.extendrow)
-            cell.bind("<ButtonRelease-1>", self.extendrow)
-            cell.bind("<Shift-Button-1>", self.extendrow)
-        # Create the real cells
-        for x in range(1, columns+1):
-            for y in range(1, rows+1):
-                cell = Tk.Label(self.cellgrid, relief='sunken',
-                                bg='white', fg='black')
-                cell.grid_configure(column=x, row=y, sticky='NSWE')
-                self.gridcells[x, y] = cell
-                cell.__x = x
-                cell.__y = y
-                # Bind mouse events
-                cell.bind("<ButtonPress-1>", self.press)
-                cell.bind("<B1-Motion>", self.motion)
-                cell.bind("<ButtonRelease-1>", self.release)
-                cell.bind("<Shift-Button-1>", self.release)
-
-    def selectall(self, event):
-        self.setcurrent(1, 1)
-        self.setcorner(sys.maxsize, sys.maxsize)
-
-    def selectcolumn(self, event):
-        x, y = self.whichxy(event)
-        self.setcurrent(x, 1)
-        self.setcorner(x, sys.maxsize)
-
-    def extendcolumn(self, event):
-        x, y = self.whichxy(event)
-        if x > 0:
-            self.setcurrent(self.currentxy[0], 1)
-            self.setcorner(x, sys.maxsize)
-
-    def selectrow(self, event):
-        x, y = self.whichxy(event)
-        self.setcurrent(1, y)
-        self.setcorner(sys.maxsize, y)
-
-    def extendrow(self, event):
-        x, y = self.whichxy(event)
-        if y > 0:
-            self.setcurrent(1, self.currentxy[1])
-            self.setcorner(sys.maxsize, y)
-
-    def press(self, event):
-        x, y = self.whichxy(event)
-        if x > 0 and y > 0:
-            self.setcurrent(x, y)
-
-    def motion(self, event):
-        x, y = self.whichxy(event)
-        if x > 0 and y > 0:
-            self.setcorner(x, y)
-
-    release = motion
-
-    def whichxy(self, event):
-        w = self.cellgrid.winfo_containing(event.x_root, event.y_root)
-        if w is not None and isinstance(w, Tk.Label):
-            try:
-                return w.__x, w.__y
-            except AttributeError:
-                pass
-        return 0, 0
-
-    def save(self):
-        self.sheet.save(self.filename)
-
-    def setcurrent(self, x, y):
-        "Make (x, y) the current cell."
-        if self.currentxy is not None:
-            self.change_cell()
-        self.clearfocus()
-        self.beacon['text'] = cellname(x, y)
-        self.load_entry(x, y)
-        self.entry.focus_set()
-        self.currentxy = x, y
-        self.cornerxy = None
-        gridcell = self.gridcells.get(self.currentxy)
-        if gridcell is not None:
-            gridcell['bg'] = 'yellow'
-
-    def setcorner(self, x, y):
-        if self.currentxy is None or self.currentxy == (x, y):
-            self.setcurrent(x, y)
-            return
-        self.clearfocus()
-        self.cornerxy = x, y
-        x1, y1 = self.currentxy
-        x2, y2 = self.cornerxy or self.currentxy
-        if x1 > x2:
-            x1, x2 = x2, x1
-        if y1 > y2:
-            y1, y2 = y2, y1
-        for (x, y), cell in self.gridcells.items():
-            if x1 <= x <= x2 and y1 <= y <= y2:
-                cell['bg'] = 'lightBlue'
-        gridcell = self.gridcells.get(self.currentxy)
-        if gridcell is not None:
-            gridcell['bg'] = 'yellow'
-        self.setbeacon(x1, y1, x2, y2)
-
-    def setbeacon(self, x1, y1, x2, y2):
-        if x1 == y1 == 1 and x2 == y2 == sys.maxsize:
-            name = ":"
-        elif (x1, x2) == (1, sys.maxsize):
-            if y1 == y2:
-                name = "%d" % y1
-            else:
-                name = "%d:%d" % (y1, y2)
-        elif (y1, y2) == (1, sys.maxsize):
-            if x1 == x2:
-                name = "%s" % colnum2name(x1)
-            else:
-                name = "%s:%s" % (colnum2name(x1), colnum2name(x2))
-        else:
-            name1 = cellname(*self.currentxy)
-            name2 = cellname(*self.cornerxy)
-            name = "%s:%s" % (name1, name2)
-        self.beacon['text'] = name
-
-
-    def clearfocus(self):
-        if self.currentxy is not None:
-            x1, y1 = self.currentxy
-            x2, y2 = self.cornerxy or self.currentxy
-            if x1 > x2:
-                x1, x2 = x2, x1
-            if y1 > y2:
-                y1, y2 = y2, y1
-            for (x, y), cell in self.gridcells.items():
-                if x1 <= x <= x2 and y1 <= y <= y2:
-                    cell['bg'] = 'white'
-
-    def return_event(self, event):
-        "Callback for the Return key."
-        self.change_cell()
-        x, y = self.currentxy
-        self.setcurrent(x, y+1)
-        return "break"
-
-    def shift_return_event(self, event):
-        "Callback for the Return key with Shift modifier."
-        self.change_cell()
-        x, y = self.currentxy
-        self.setcurrent(x, max(1, y-1))
-        return "break"
-
-    def tab_event(self, event):
-        "Callback for the Tab key."
-        self.change_cell()
-        x, y = self.currentxy
-        self.setcurrent(x+1, y)
-        return "break"
-
-    def shift_tab_event(self, event):
-        "Callback for the Tab key with Shift modifier."
-        self.change_cell()
-        x, y = self.currentxy
-        self.setcurrent(max(1, x-1), y)
-        return "break"
-
-    def change_cell(self):
-        "Set the current cell from the entry widget."
-        x, y = self.currentxy
-        text = self.entry.get()
-        cell = None
-        if text.startswith('='):
-            cell = FormulaCell(text[1:])
-        else:
-            for cls in int, float, complex:
-                try:
-                    value = cls(text)
-                except (TypeError, ValueError):
-                    continue
-                else:
-                    cell = NumericCell(value)
-                    break
-        if cell is None and text:
-            cell = StringCell(text)
-        if cell is None:
-            self.sheet.clearcell(x, y)
-        else:
-            self.sheet.setcell(x, y, cell)
-        self.sync()
-
-    def sync(self):
-        "Fill the GUI cells from the sheet cells."
-        self.sheet.recalc()
-        for (x, y), gridcell in self.gridcells.items():
-            if x == 0 or y == 0:
-                continue
-            cell = self.sheet.getcell(x, y)
-            if cell is None:
-                gridcell['text'] = ""
-            else:
-                if hasattr(cell, 'format'):
-                    text, alignment = cell.format()
-                else:
-                    text, alignment = str(cell), LEFT
-                gridcell['text'] = text
-                gridcell['anchor'] = align2anchor[alignment]
-
-
-def test_basic():
-    "Basic non-gui self-test."
-    a = Sheet()
-    for x in range(1, 11):
-        for y in range(1, 11):
-            if x == 1:
-                cell = NumericCell(y)
-            elif y == 1:
-                cell = NumericCell(x)
-            else:
-                c1 = cellname(x, 1)
-                c2 = cellname(1, y)
-                formula = "%s*%s" % (c1, c2)
-                cell = FormulaCell(formula)
-            a.setcell(x, y, cell)
-##    if os.path.isfile("sheet1.xml"):
-##        print "Loading from sheet1.xml"
-##        a.load("sheet1.xml")
-    a.display()
-    a.save("sheet1.xml")
-
-def test_gui():
-    "GUI test."
-    if sys.argv[1:]:
-        filename = sys.argv[1]
-    else:
-        filename = "sheet1.xml"
-    g = SheetGUI(filename)
-    g.root.mainloop()
-
-if __name__ == '__main__':
-    #test_basic()
-    test_gui()
diff --git a/Tools/demo/vector.py b/Tools/demo/vector.py
deleted file mode 100755
index 6df1f50a8998e1..00000000000000
--- a/Tools/demo/vector.py
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-A demonstration of classes and their special methods in Python.
-"""
-
-class Vec:
-    """A simple vector class.
-
-    Instances of the Vec class can be constructed from numbers
-
-    >>> a = Vec(1, 2, 3)
-    >>> b = Vec(3, 2, 1)
-
-    added
-    >>> a + b
-    Vec(4, 4, 4)
-
-    subtracted
-    >>> a - b
-    Vec(-2, 0, 2)
-
-    and multiplied by a scalar on the left
-    >>> 3.0 * a
-    Vec(3.0, 6.0, 9.0)
-
-    or on the right
-    >>> a * 3.0
-    Vec(3.0, 6.0, 9.0)
-
-    and dot product
-    >>> a.dot(b)
-    10
-
-    and printed in vector notation
-    >>> print(a)
-    <1 2 3>
-
-    """
-
-    def __init__(self, *v):
-        self.v = list(v)
-
-    @classmethod
-    def fromlist(cls, v):
-        if not isinstance(v, list):
-            raise TypeError
-        inst = cls()
-        inst.v = v
-        return inst
-
-    def __repr__(self):
-        args = ', '.join([repr(x) for x in self.v])
-        return f'{type(self).__name__}({args})'
-
-    def __str__(self):
-        components = ' '.join([str(x) for x in self.v])
-        return f'<{components}>'
-
-    def __len__(self):
-        return len(self.v)
-
-    def __getitem__(self, i):
-        return self.v[i]
-
-    def __add__(self, other):
-        "Element-wise addition"
-        v = [x + y for x, y in zip(self.v, other.v)]
-        return Vec.fromlist(v)
-
-    def __sub__(self, other):
-        "Element-wise subtraction"
-        v = [x - y for x, y in zip(self.v, other.v)]
-        return Vec.fromlist(v)
-
-    def __mul__(self, scalar):
-        "Multiply by scalar"
-        v = [x * scalar for x in self.v]
-        return Vec.fromlist(v)
-
-    __rmul__ = __mul__
-
-    def dot(self, other):
-        "Vector dot product"
-        if not isinstance(other, Vec):
-            raise TypeError
-        return sum(x_i * y_i for (x_i, y_i) in zip(self, other))
-
-
-def test():
-    import doctest
-    doctest.testmod()
-
-test()