17db96d56Sopenharmony_ci#!/usr/bin/env python3
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci"""
47db96d56Sopenharmony_ciSorting algorithms visualizer using Tkinter.
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ciThis module is comprised of three ``components'':
77db96d56Sopenharmony_ci
87db96d56Sopenharmony_ci- an array visualizer with methods that implement basic sorting
97db96d56Sopenharmony_cioperations (compare, swap) as well as methods for ``annotating'' the
107db96d56Sopenharmony_cisorting algorithm (e.g. to show the pivot element);
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ci- a number of sorting algorithms (currently quicksort, insertion sort,
137db96d56Sopenharmony_ciselection sort and bubble sort, as well as a randomization function),
147db96d56Sopenharmony_ciall using the array visualizer for its basic operations and with calls
157db96d56Sopenharmony_cito its annotation methods;
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ci- and a ``driver'' class which can be used as a Grail applet or as a
187db96d56Sopenharmony_cistand-alone application.
197db96d56Sopenharmony_ci"""
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_cifrom tkinter import *
227db96d56Sopenharmony_ciimport random
237db96d56Sopenharmony_ci
247db96d56Sopenharmony_ciXGRID = 10
257db96d56Sopenharmony_ciYGRID = 10
267db96d56Sopenharmony_ciWIDTH = 6
277db96d56Sopenharmony_ci
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciclass Array:
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci    class Cancelled(BaseException):
327db96d56Sopenharmony_ci        pass
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ci    def __init__(self, master, data=None):
357db96d56Sopenharmony_ci        self.master = master
367db96d56Sopenharmony_ci        self.frame = Frame(self.master)
377db96d56Sopenharmony_ci        self.frame.pack(fill=X)
387db96d56Sopenharmony_ci        self.label = Label(self.frame)
397db96d56Sopenharmony_ci        self.label.pack()
407db96d56Sopenharmony_ci        self.canvas = Canvas(self.frame)
417db96d56Sopenharmony_ci        self.canvas.pack()
427db96d56Sopenharmony_ci        self.report = Label(self.frame)
437db96d56Sopenharmony_ci        self.report.pack()
447db96d56Sopenharmony_ci        self.left = self.canvas.create_line(0, 0, 0, 0)
457db96d56Sopenharmony_ci        self.right = self.canvas.create_line(0, 0, 0, 0)
467db96d56Sopenharmony_ci        self.pivot = self.canvas.create_line(0, 0, 0, 0)
477db96d56Sopenharmony_ci        self.items = []
487db96d56Sopenharmony_ci        self.size = self.maxvalue = 0
497db96d56Sopenharmony_ci        if data:
507db96d56Sopenharmony_ci            self.setdata(data)
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci    def setdata(self, data):
537db96d56Sopenharmony_ci        olditems = self.items
547db96d56Sopenharmony_ci        self.items = []
557db96d56Sopenharmony_ci        for item in olditems:
567db96d56Sopenharmony_ci            item.delete()
577db96d56Sopenharmony_ci        self.size = len(data)
587db96d56Sopenharmony_ci        self.maxvalue = max(data)
597db96d56Sopenharmony_ci        self.canvas.config(width=(self.size+1)*XGRID,
607db96d56Sopenharmony_ci                           height=(self.maxvalue+1)*YGRID)
617db96d56Sopenharmony_ci        for i in range(self.size):
627db96d56Sopenharmony_ci            self.items.append(ArrayItem(self, i, data[i]))
637db96d56Sopenharmony_ci        self.reset("Sort demo, size %d" % self.size)
647db96d56Sopenharmony_ci
657db96d56Sopenharmony_ci    speed = "normal"
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci    def setspeed(self, speed):
687db96d56Sopenharmony_ci        self.speed = speed
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci    def destroy(self):
717db96d56Sopenharmony_ci        self.frame.destroy()
727db96d56Sopenharmony_ci
737db96d56Sopenharmony_ci    in_mainloop = 0
747db96d56Sopenharmony_ci    stop_mainloop = 0
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_ci    def cancel(self):
777db96d56Sopenharmony_ci        self.stop_mainloop = 1
787db96d56Sopenharmony_ci        if self.in_mainloop:
797db96d56Sopenharmony_ci            self.master.quit()
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_ci    def step(self):
827db96d56Sopenharmony_ci        if self.in_mainloop:
837db96d56Sopenharmony_ci            self.master.quit()
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_ci    def wait(self, msecs):
867db96d56Sopenharmony_ci        if self.speed == "fastest":
877db96d56Sopenharmony_ci            msecs = 0
887db96d56Sopenharmony_ci        elif self.speed == "fast":
897db96d56Sopenharmony_ci            msecs = msecs//10
907db96d56Sopenharmony_ci        elif self.speed == "single-step":
917db96d56Sopenharmony_ci            msecs = 1000000000
927db96d56Sopenharmony_ci        if not self.stop_mainloop:
937db96d56Sopenharmony_ci            self.master.update()
947db96d56Sopenharmony_ci            id = self.master.after(msecs, self.master.quit)
957db96d56Sopenharmony_ci            self.in_mainloop = 1
967db96d56Sopenharmony_ci            self.master.mainloop()
977db96d56Sopenharmony_ci            self.master.after_cancel(id)
987db96d56Sopenharmony_ci            self.in_mainloop = 0
997db96d56Sopenharmony_ci        if self.stop_mainloop:
1007db96d56Sopenharmony_ci            self.stop_mainloop = 0
1017db96d56Sopenharmony_ci            self.message("Cancelled")
1027db96d56Sopenharmony_ci            raise Array.Cancelled
1037db96d56Sopenharmony_ci
1047db96d56Sopenharmony_ci    def getsize(self):
1057db96d56Sopenharmony_ci        return self.size
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_ci    def show_partition(self, first, last):
1087db96d56Sopenharmony_ci        for i in range(self.size):
1097db96d56Sopenharmony_ci            item = self.items[i]
1107db96d56Sopenharmony_ci            if first <= i < last:
1117db96d56Sopenharmony_ci                self.canvas.itemconfig(item, fill='red')
1127db96d56Sopenharmony_ci            else:
1137db96d56Sopenharmony_ci                self.canvas.itemconfig(item, fill='orange')
1147db96d56Sopenharmony_ci        self.hide_left_right_pivot()
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    def hide_partition(self):
1177db96d56Sopenharmony_ci        for i in range(self.size):
1187db96d56Sopenharmony_ci            item = self.items[i]
1197db96d56Sopenharmony_ci            self.canvas.itemconfig(item, fill='red')
1207db96d56Sopenharmony_ci        self.hide_left_right_pivot()
1217db96d56Sopenharmony_ci
1227db96d56Sopenharmony_ci    def show_left(self, left):
1237db96d56Sopenharmony_ci        if not 0 <= left < self.size:
1247db96d56Sopenharmony_ci            self.hide_left()
1257db96d56Sopenharmony_ci            return
1267db96d56Sopenharmony_ci        x1, y1, x2, y2 = self.items[left].position()
1277db96d56Sopenharmony_ci##      top, bot = HIRO
1287db96d56Sopenharmony_ci        self.canvas.coords(self.left, (x1 - 2, 0, x1 - 2, 9999))
1297db96d56Sopenharmony_ci        self.master.update()
1307db96d56Sopenharmony_ci
1317db96d56Sopenharmony_ci    def show_right(self, right):
1327db96d56Sopenharmony_ci        if not 0 <= right < self.size:
1337db96d56Sopenharmony_ci            self.hide_right()
1347db96d56Sopenharmony_ci            return
1357db96d56Sopenharmony_ci        x1, y1, x2, y2 = self.items[right].position()
1367db96d56Sopenharmony_ci        self.canvas.coords(self.right, (x2 + 2, 0, x2 + 2, 9999))
1377db96d56Sopenharmony_ci        self.master.update()
1387db96d56Sopenharmony_ci
1397db96d56Sopenharmony_ci    def hide_left_right_pivot(self):
1407db96d56Sopenharmony_ci        self.hide_left()
1417db96d56Sopenharmony_ci        self.hide_right()
1427db96d56Sopenharmony_ci        self.hide_pivot()
1437db96d56Sopenharmony_ci
1447db96d56Sopenharmony_ci    def hide_left(self):
1457db96d56Sopenharmony_ci        self.canvas.coords(self.left, (0, 0, 0, 0))
1467db96d56Sopenharmony_ci
1477db96d56Sopenharmony_ci    def hide_right(self):
1487db96d56Sopenharmony_ci        self.canvas.coords(self.right, (0, 0, 0, 0))
1497db96d56Sopenharmony_ci
1507db96d56Sopenharmony_ci    def show_pivot(self, pivot):
1517db96d56Sopenharmony_ci        x1, y1, x2, y2 = self.items[pivot].position()
1527db96d56Sopenharmony_ci        self.canvas.coords(self.pivot, (0, y1 - 2, 9999, y1 - 2))
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci    def hide_pivot(self):
1557db96d56Sopenharmony_ci        self.canvas.coords(self.pivot, (0, 0, 0, 0))
1567db96d56Sopenharmony_ci
1577db96d56Sopenharmony_ci    def swap(self, i, j):
1587db96d56Sopenharmony_ci        if i == j: return
1597db96d56Sopenharmony_ci        self.countswap()
1607db96d56Sopenharmony_ci        item = self.items[i]
1617db96d56Sopenharmony_ci        other = self.items[j]
1627db96d56Sopenharmony_ci        self.items[i], self.items[j] = other, item
1637db96d56Sopenharmony_ci        item.swapwith(other)
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci    def compare(self, i, j):
1667db96d56Sopenharmony_ci        self.countcompare()
1677db96d56Sopenharmony_ci        item = self.items[i]
1687db96d56Sopenharmony_ci        other = self.items[j]
1697db96d56Sopenharmony_ci        return item.compareto(other)
1707db96d56Sopenharmony_ci
1717db96d56Sopenharmony_ci    def reset(self, msg):
1727db96d56Sopenharmony_ci        self.ncompares = 0
1737db96d56Sopenharmony_ci        self.nswaps = 0
1747db96d56Sopenharmony_ci        self.message(msg)
1757db96d56Sopenharmony_ci        self.updatereport()
1767db96d56Sopenharmony_ci        self.hide_partition()
1777db96d56Sopenharmony_ci
1787db96d56Sopenharmony_ci    def message(self, msg):
1797db96d56Sopenharmony_ci        self.label.config(text=msg)
1807db96d56Sopenharmony_ci
1817db96d56Sopenharmony_ci    def countswap(self):
1827db96d56Sopenharmony_ci        self.nswaps = self.nswaps + 1
1837db96d56Sopenharmony_ci        self.updatereport()
1847db96d56Sopenharmony_ci
1857db96d56Sopenharmony_ci    def countcompare(self):
1867db96d56Sopenharmony_ci        self.ncompares = self.ncompares + 1
1877db96d56Sopenharmony_ci        self.updatereport()
1887db96d56Sopenharmony_ci
1897db96d56Sopenharmony_ci    def updatereport(self):
1907db96d56Sopenharmony_ci        text = "%d cmps, %d swaps" % (self.ncompares, self.nswaps)
1917db96d56Sopenharmony_ci        self.report.config(text=text)
1927db96d56Sopenharmony_ci
1937db96d56Sopenharmony_ci
1947db96d56Sopenharmony_ciclass ArrayItem:
1957db96d56Sopenharmony_ci
1967db96d56Sopenharmony_ci    def __init__(self, array, index, value):
1977db96d56Sopenharmony_ci        self.array = array
1987db96d56Sopenharmony_ci        self.index = index
1997db96d56Sopenharmony_ci        self.value = value
2007db96d56Sopenharmony_ci        self.canvas = array.canvas
2017db96d56Sopenharmony_ci        x1, y1, x2, y2 = self.position()
2027db96d56Sopenharmony_ci        self.item_id = array.canvas.create_rectangle(x1, y1, x2, y2,
2037db96d56Sopenharmony_ci            fill='red', outline='black', width=1)
2047db96d56Sopenharmony_ci        self.canvas.tag_bind(self.item_id, '<Button-1>', self.mouse_down)
2057db96d56Sopenharmony_ci        self.canvas.tag_bind(self.item_id, '<Button1-Motion>', self.mouse_move)
2067db96d56Sopenharmony_ci        self.canvas.tag_bind(self.item_id, '<ButtonRelease-1>', self.mouse_up)
2077db96d56Sopenharmony_ci
2087db96d56Sopenharmony_ci    def delete(self):
2097db96d56Sopenharmony_ci        item_id = self.item_id
2107db96d56Sopenharmony_ci        self.array = None
2117db96d56Sopenharmony_ci        self.item_id = None
2127db96d56Sopenharmony_ci        self.canvas.delete(item_id)
2137db96d56Sopenharmony_ci
2147db96d56Sopenharmony_ci    def mouse_down(self, event):
2157db96d56Sopenharmony_ci        self.lastx = event.x
2167db96d56Sopenharmony_ci        self.lasty = event.y
2177db96d56Sopenharmony_ci        self.origx = event.x
2187db96d56Sopenharmony_ci        self.origy = event.y
2197db96d56Sopenharmony_ci        self.canvas.tag_raise(self.item_id)
2207db96d56Sopenharmony_ci
2217db96d56Sopenharmony_ci    def mouse_move(self, event):
2227db96d56Sopenharmony_ci        self.canvas.move(self.item_id,
2237db96d56Sopenharmony_ci                         event.x - self.lastx, event.y - self.lasty)
2247db96d56Sopenharmony_ci        self.lastx = event.x
2257db96d56Sopenharmony_ci        self.lasty = event.y
2267db96d56Sopenharmony_ci
2277db96d56Sopenharmony_ci    def mouse_up(self, event):
2287db96d56Sopenharmony_ci        i = self.nearestindex(event.x)
2297db96d56Sopenharmony_ci        if i >= self.array.getsize():
2307db96d56Sopenharmony_ci            i = self.array.getsize() - 1
2317db96d56Sopenharmony_ci        if i < 0:
2327db96d56Sopenharmony_ci            i = 0
2337db96d56Sopenharmony_ci        other = self.array.items[i]
2347db96d56Sopenharmony_ci        here = self.index
2357db96d56Sopenharmony_ci        self.array.items[here], self.array.items[i] = other, self
2367db96d56Sopenharmony_ci        self.index = i
2377db96d56Sopenharmony_ci        x1, y1, x2, y2 = self.position()
2387db96d56Sopenharmony_ci        self.canvas.coords(self.item_id, (x1, y1, x2, y2))
2397db96d56Sopenharmony_ci        other.setindex(here)
2407db96d56Sopenharmony_ci
2417db96d56Sopenharmony_ci    def setindex(self, index):
2427db96d56Sopenharmony_ci        nsteps = steps(self.index, index)
2437db96d56Sopenharmony_ci        if not nsteps: return
2447db96d56Sopenharmony_ci        if self.array.speed == "fastest":
2457db96d56Sopenharmony_ci            nsteps = 0
2467db96d56Sopenharmony_ci        oldpts = self.position()
2477db96d56Sopenharmony_ci        self.index = index
2487db96d56Sopenharmony_ci        newpts = self.position()
2497db96d56Sopenharmony_ci        trajectory = interpolate(oldpts, newpts, nsteps)
2507db96d56Sopenharmony_ci        self.canvas.tag_raise(self.item_id)
2517db96d56Sopenharmony_ci        for pts in trajectory:
2527db96d56Sopenharmony_ci            self.canvas.coords(self.item_id, pts)
2537db96d56Sopenharmony_ci            self.array.wait(50)
2547db96d56Sopenharmony_ci
2557db96d56Sopenharmony_ci    def swapwith(self, other):
2567db96d56Sopenharmony_ci        nsteps = steps(self.index, other.index)
2577db96d56Sopenharmony_ci        if not nsteps: return
2587db96d56Sopenharmony_ci        if self.array.speed == "fastest":
2597db96d56Sopenharmony_ci            nsteps = 0
2607db96d56Sopenharmony_ci        myoldpts = self.position()
2617db96d56Sopenharmony_ci        otheroldpts = other.position()
2627db96d56Sopenharmony_ci        self.index, other.index = other.index, self.index
2637db96d56Sopenharmony_ci        mynewpts = self.position()
2647db96d56Sopenharmony_ci        othernewpts = other.position()
2657db96d56Sopenharmony_ci        myfill = self.canvas.itemcget(self.item_id, 'fill')
2667db96d56Sopenharmony_ci        otherfill = self.canvas.itemcget(other.item_id, 'fill')
2677db96d56Sopenharmony_ci        self.canvas.itemconfig(self.item_id, fill='green')
2687db96d56Sopenharmony_ci        self.canvas.itemconfig(other.item_id, fill='yellow')
2697db96d56Sopenharmony_ci        self.array.master.update()
2707db96d56Sopenharmony_ci        if self.array.speed == "single-step":
2717db96d56Sopenharmony_ci            self.canvas.coords(self.item_id, mynewpts)
2727db96d56Sopenharmony_ci            self.canvas.coords(other.item_id, othernewpts)
2737db96d56Sopenharmony_ci            self.array.master.update()
2747db96d56Sopenharmony_ci            self.canvas.itemconfig(self.item_id, fill=myfill)
2757db96d56Sopenharmony_ci            self.canvas.itemconfig(other.item_id, fill=otherfill)
2767db96d56Sopenharmony_ci            self.array.wait(0)
2777db96d56Sopenharmony_ci            return
2787db96d56Sopenharmony_ci        mytrajectory = interpolate(myoldpts, mynewpts, nsteps)
2797db96d56Sopenharmony_ci        othertrajectory = interpolate(otheroldpts, othernewpts, nsteps)
2807db96d56Sopenharmony_ci        if self.value > other.value:
2817db96d56Sopenharmony_ci            self.canvas.tag_raise(self.item_id)
2827db96d56Sopenharmony_ci            self.canvas.tag_raise(other.item_id)
2837db96d56Sopenharmony_ci        else:
2847db96d56Sopenharmony_ci            self.canvas.tag_raise(other.item_id)
2857db96d56Sopenharmony_ci            self.canvas.tag_raise(self.item_id)
2867db96d56Sopenharmony_ci        try:
2877db96d56Sopenharmony_ci            for i in range(len(mytrajectory)):
2887db96d56Sopenharmony_ci                mypts = mytrajectory[i]
2897db96d56Sopenharmony_ci                otherpts = othertrajectory[i]
2907db96d56Sopenharmony_ci                self.canvas.coords(self.item_id, mypts)
2917db96d56Sopenharmony_ci                self.canvas.coords(other.item_id, otherpts)
2927db96d56Sopenharmony_ci                self.array.wait(50)
2937db96d56Sopenharmony_ci        finally:
2947db96d56Sopenharmony_ci            mypts = mytrajectory[-1]
2957db96d56Sopenharmony_ci            otherpts = othertrajectory[-1]
2967db96d56Sopenharmony_ci            self.canvas.coords(self.item_id, mypts)
2977db96d56Sopenharmony_ci            self.canvas.coords(other.item_id, otherpts)
2987db96d56Sopenharmony_ci            self.canvas.itemconfig(self.item_id, fill=myfill)
2997db96d56Sopenharmony_ci            self.canvas.itemconfig(other.item_id, fill=otherfill)
3007db96d56Sopenharmony_ci
3017db96d56Sopenharmony_ci    def compareto(self, other):
3027db96d56Sopenharmony_ci        myfill = self.canvas.itemcget(self.item_id, 'fill')
3037db96d56Sopenharmony_ci        otherfill = self.canvas.itemcget(other.item_id, 'fill')
3047db96d56Sopenharmony_ci        if self.value < other.value:
3057db96d56Sopenharmony_ci            myflash = 'white'
3067db96d56Sopenharmony_ci            otherflash = 'black'
3077db96d56Sopenharmony_ci            outcome = -1
3087db96d56Sopenharmony_ci        elif self.value > other.value:
3097db96d56Sopenharmony_ci            myflash = 'black'
3107db96d56Sopenharmony_ci            otherflash = 'white'
3117db96d56Sopenharmony_ci            outcome = 1
3127db96d56Sopenharmony_ci        else:
3137db96d56Sopenharmony_ci            myflash = otherflash = 'grey'
3147db96d56Sopenharmony_ci            outcome = 0
3157db96d56Sopenharmony_ci        try:
3167db96d56Sopenharmony_ci            self.canvas.itemconfig(self.item_id, fill=myflash)
3177db96d56Sopenharmony_ci            self.canvas.itemconfig(other.item_id, fill=otherflash)
3187db96d56Sopenharmony_ci            self.array.wait(500)
3197db96d56Sopenharmony_ci        finally:
3207db96d56Sopenharmony_ci            self.canvas.itemconfig(self.item_id, fill=myfill)
3217db96d56Sopenharmony_ci            self.canvas.itemconfig(other.item_id, fill=otherfill)
3227db96d56Sopenharmony_ci        return outcome
3237db96d56Sopenharmony_ci
3247db96d56Sopenharmony_ci    def position(self):
3257db96d56Sopenharmony_ci        x1 = (self.index+1)*XGRID - WIDTH//2
3267db96d56Sopenharmony_ci        x2 = x1+WIDTH
3277db96d56Sopenharmony_ci        y2 = (self.array.maxvalue+1)*YGRID
3287db96d56Sopenharmony_ci        y1 = y2 - (self.value)*YGRID
3297db96d56Sopenharmony_ci        return x1, y1, x2, y2
3307db96d56Sopenharmony_ci
3317db96d56Sopenharmony_ci    def nearestindex(self, x):
3327db96d56Sopenharmony_ci        return int(round(float(x)/XGRID)) - 1
3337db96d56Sopenharmony_ci
3347db96d56Sopenharmony_ci
3357db96d56Sopenharmony_ci# Subroutines that don't need an object
3367db96d56Sopenharmony_ci
3377db96d56Sopenharmony_cidef steps(here, there):
3387db96d56Sopenharmony_ci    nsteps = abs(here - there)
3397db96d56Sopenharmony_ci    if nsteps <= 3:
3407db96d56Sopenharmony_ci        nsteps = nsteps * 3
3417db96d56Sopenharmony_ci    elif nsteps <= 5:
3427db96d56Sopenharmony_ci        nsteps = nsteps * 2
3437db96d56Sopenharmony_ci    elif nsteps > 10:
3447db96d56Sopenharmony_ci        nsteps = 10
3457db96d56Sopenharmony_ci    return nsteps
3467db96d56Sopenharmony_ci
3477db96d56Sopenharmony_cidef interpolate(oldpts, newpts, n):
3487db96d56Sopenharmony_ci    if len(oldpts) != len(newpts):
3497db96d56Sopenharmony_ci        raise ValueError("can't interpolate arrays of different length")
3507db96d56Sopenharmony_ci    pts = [0]*len(oldpts)
3517db96d56Sopenharmony_ci    res = [tuple(oldpts)]
3527db96d56Sopenharmony_ci    for i in range(1, n):
3537db96d56Sopenharmony_ci        for k in range(len(pts)):
3547db96d56Sopenharmony_ci            pts[k] = oldpts[k] + (newpts[k] - oldpts[k])*i//n
3557db96d56Sopenharmony_ci        res.append(tuple(pts))
3567db96d56Sopenharmony_ci    res.append(tuple(newpts))
3577db96d56Sopenharmony_ci    return res
3587db96d56Sopenharmony_ci
3597db96d56Sopenharmony_ci
3607db96d56Sopenharmony_ci# Various (un)sorting algorithms
3617db96d56Sopenharmony_ci
3627db96d56Sopenharmony_cidef uniform(array):
3637db96d56Sopenharmony_ci    size = array.getsize()
3647db96d56Sopenharmony_ci    array.setdata([(size+1)//2] * size)
3657db96d56Sopenharmony_ci    array.reset("Uniform data, size %d" % size)
3667db96d56Sopenharmony_ci
3677db96d56Sopenharmony_cidef distinct(array):
3687db96d56Sopenharmony_ci    size = array.getsize()
3697db96d56Sopenharmony_ci    array.setdata(range(1, size+1))
3707db96d56Sopenharmony_ci    array.reset("Distinct data, size %d" % size)
3717db96d56Sopenharmony_ci
3727db96d56Sopenharmony_cidef randomize(array):
3737db96d56Sopenharmony_ci    array.reset("Randomizing")
3747db96d56Sopenharmony_ci    n = array.getsize()
3757db96d56Sopenharmony_ci    for i in range(n):
3767db96d56Sopenharmony_ci        j = random.randint(0, n-1)
3777db96d56Sopenharmony_ci        array.swap(i, j)
3787db96d56Sopenharmony_ci    array.message("Randomized")
3797db96d56Sopenharmony_ci
3807db96d56Sopenharmony_cidef insertionsort(array):
3817db96d56Sopenharmony_ci    size = array.getsize()
3827db96d56Sopenharmony_ci    array.reset("Insertion sort")
3837db96d56Sopenharmony_ci    for i in range(1, size):
3847db96d56Sopenharmony_ci        j = i-1
3857db96d56Sopenharmony_ci        while j >= 0:
3867db96d56Sopenharmony_ci            if array.compare(j, j+1) <= 0:
3877db96d56Sopenharmony_ci                break
3887db96d56Sopenharmony_ci            array.swap(j, j+1)
3897db96d56Sopenharmony_ci            j = j-1
3907db96d56Sopenharmony_ci    array.message("Sorted")
3917db96d56Sopenharmony_ci
3927db96d56Sopenharmony_cidef selectionsort(array):
3937db96d56Sopenharmony_ci    size = array.getsize()
3947db96d56Sopenharmony_ci    array.reset("Selection sort")
3957db96d56Sopenharmony_ci    try:
3967db96d56Sopenharmony_ci        for i in range(size):
3977db96d56Sopenharmony_ci            array.show_partition(i, size)
3987db96d56Sopenharmony_ci            for j in range(i+1, size):
3997db96d56Sopenharmony_ci                if array.compare(i, j) > 0:
4007db96d56Sopenharmony_ci                    array.swap(i, j)
4017db96d56Sopenharmony_ci        array.message("Sorted")
4027db96d56Sopenharmony_ci    finally:
4037db96d56Sopenharmony_ci        array.hide_partition()
4047db96d56Sopenharmony_ci
4057db96d56Sopenharmony_cidef bubblesort(array):
4067db96d56Sopenharmony_ci    size = array.getsize()
4077db96d56Sopenharmony_ci    array.reset("Bubble sort")
4087db96d56Sopenharmony_ci    for i in range(size):
4097db96d56Sopenharmony_ci        for j in range(1, size):
4107db96d56Sopenharmony_ci            if array.compare(j-1, j) > 0:
4117db96d56Sopenharmony_ci                array.swap(j-1, j)
4127db96d56Sopenharmony_ci    array.message("Sorted")
4137db96d56Sopenharmony_ci
4147db96d56Sopenharmony_cidef quicksort(array):
4157db96d56Sopenharmony_ci    size = array.getsize()
4167db96d56Sopenharmony_ci    array.reset("Quicksort")
4177db96d56Sopenharmony_ci    try:
4187db96d56Sopenharmony_ci        stack = [(0, size)]
4197db96d56Sopenharmony_ci        while stack:
4207db96d56Sopenharmony_ci            first, last = stack[-1]
4217db96d56Sopenharmony_ci            del stack[-1]
4227db96d56Sopenharmony_ci            array.show_partition(first, last)
4237db96d56Sopenharmony_ci            if last-first < 5:
4247db96d56Sopenharmony_ci                array.message("Insertion sort")
4257db96d56Sopenharmony_ci                for i in range(first+1, last):
4267db96d56Sopenharmony_ci                    j = i-1
4277db96d56Sopenharmony_ci                    while j >= first:
4287db96d56Sopenharmony_ci                        if array.compare(j, j+1) <= 0:
4297db96d56Sopenharmony_ci                            break
4307db96d56Sopenharmony_ci                        array.swap(j, j+1)
4317db96d56Sopenharmony_ci                        j = j-1
4327db96d56Sopenharmony_ci                continue
4337db96d56Sopenharmony_ci            array.message("Choosing pivot")
4347db96d56Sopenharmony_ci            j, i, k = first, (first+last) // 2, last-1
4357db96d56Sopenharmony_ci            if array.compare(k, i) < 0:
4367db96d56Sopenharmony_ci                array.swap(k, i)
4377db96d56Sopenharmony_ci            if array.compare(k, j) < 0:
4387db96d56Sopenharmony_ci                array.swap(k, j)
4397db96d56Sopenharmony_ci            if array.compare(j, i) < 0:
4407db96d56Sopenharmony_ci                array.swap(j, i)
4417db96d56Sopenharmony_ci            pivot = j
4427db96d56Sopenharmony_ci            array.show_pivot(pivot)
4437db96d56Sopenharmony_ci            array.message("Pivot at left of partition")
4447db96d56Sopenharmony_ci            array.wait(1000)
4457db96d56Sopenharmony_ci            left = first
4467db96d56Sopenharmony_ci            right = last
4477db96d56Sopenharmony_ci            while True:
4487db96d56Sopenharmony_ci                array.message("Sweep right pointer")
4497db96d56Sopenharmony_ci                right = right-1
4507db96d56Sopenharmony_ci                array.show_right(right)
4517db96d56Sopenharmony_ci                while right > first and array.compare(right, pivot) >= 0:
4527db96d56Sopenharmony_ci                    right = right-1
4537db96d56Sopenharmony_ci                    array.show_right(right)
4547db96d56Sopenharmony_ci                array.message("Sweep left pointer")
4557db96d56Sopenharmony_ci                left = left+1
4567db96d56Sopenharmony_ci                array.show_left(left)
4577db96d56Sopenharmony_ci                while left < last and array.compare(left, pivot) <= 0:
4587db96d56Sopenharmony_ci                    left = left+1
4597db96d56Sopenharmony_ci                    array.show_left(left)
4607db96d56Sopenharmony_ci                if left > right:
4617db96d56Sopenharmony_ci                    array.message("End of partition")
4627db96d56Sopenharmony_ci                    break
4637db96d56Sopenharmony_ci                array.message("Swap items")
4647db96d56Sopenharmony_ci                array.swap(left, right)
4657db96d56Sopenharmony_ci            array.message("Swap pivot back")
4667db96d56Sopenharmony_ci            array.swap(pivot, right)
4677db96d56Sopenharmony_ci            n1 = right-first
4687db96d56Sopenharmony_ci            n2 = last-left
4697db96d56Sopenharmony_ci            if n1 > 1: stack.append((first, right))
4707db96d56Sopenharmony_ci            if n2 > 1: stack.append((left, last))
4717db96d56Sopenharmony_ci        array.message("Sorted")
4727db96d56Sopenharmony_ci    finally:
4737db96d56Sopenharmony_ci        array.hide_partition()
4747db96d56Sopenharmony_ci
4757db96d56Sopenharmony_cidef demosort(array):
4767db96d56Sopenharmony_ci    while True:
4777db96d56Sopenharmony_ci        for alg in [quicksort, insertionsort, selectionsort, bubblesort]:
4787db96d56Sopenharmony_ci            randomize(array)
4797db96d56Sopenharmony_ci            alg(array)
4807db96d56Sopenharmony_ci
4817db96d56Sopenharmony_ci
4827db96d56Sopenharmony_ci# Sort demo class -- usable as a Grail applet
4837db96d56Sopenharmony_ci
4847db96d56Sopenharmony_ciclass SortDemo:
4857db96d56Sopenharmony_ci
4867db96d56Sopenharmony_ci    def __init__(self, master, size=15):
4877db96d56Sopenharmony_ci        self.master = master
4887db96d56Sopenharmony_ci        self.size = size
4897db96d56Sopenharmony_ci        self.busy = 0
4907db96d56Sopenharmony_ci        self.array = Array(self.master)
4917db96d56Sopenharmony_ci
4927db96d56Sopenharmony_ci        self.botframe = Frame(master)
4937db96d56Sopenharmony_ci        self.botframe.pack(side=BOTTOM)
4947db96d56Sopenharmony_ci        self.botleftframe = Frame(self.botframe)
4957db96d56Sopenharmony_ci        self.botleftframe.pack(side=LEFT, fill=Y)
4967db96d56Sopenharmony_ci        self.botrightframe = Frame(self.botframe)
4977db96d56Sopenharmony_ci        self.botrightframe.pack(side=RIGHT, fill=Y)
4987db96d56Sopenharmony_ci
4997db96d56Sopenharmony_ci        self.b_qsort = Button(self.botleftframe,
5007db96d56Sopenharmony_ci                              text="Quicksort", command=self.c_qsort)
5017db96d56Sopenharmony_ci        self.b_qsort.pack(fill=X)
5027db96d56Sopenharmony_ci        self.b_isort = Button(self.botleftframe,
5037db96d56Sopenharmony_ci                              text="Insertion sort", command=self.c_isort)
5047db96d56Sopenharmony_ci        self.b_isort.pack(fill=X)
5057db96d56Sopenharmony_ci        self.b_ssort = Button(self.botleftframe,
5067db96d56Sopenharmony_ci                              text="Selection sort", command=self.c_ssort)
5077db96d56Sopenharmony_ci        self.b_ssort.pack(fill=X)
5087db96d56Sopenharmony_ci        self.b_bsort = Button(self.botleftframe,
5097db96d56Sopenharmony_ci                              text="Bubble sort", command=self.c_bsort)
5107db96d56Sopenharmony_ci        self.b_bsort.pack(fill=X)
5117db96d56Sopenharmony_ci
5127db96d56Sopenharmony_ci        # Terrible hack to overcome limitation of OptionMenu...
5137db96d56Sopenharmony_ci        class MyIntVar(IntVar):
5147db96d56Sopenharmony_ci            def __init__(self, master, demo):
5157db96d56Sopenharmony_ci                self.demo = demo
5167db96d56Sopenharmony_ci                IntVar.__init__(self, master)
5177db96d56Sopenharmony_ci            def set(self, value):
5187db96d56Sopenharmony_ci                IntVar.set(self, value)
5197db96d56Sopenharmony_ci                if str(value) != '0':
5207db96d56Sopenharmony_ci                    self.demo.resize(value)
5217db96d56Sopenharmony_ci
5227db96d56Sopenharmony_ci        self.v_size = MyIntVar(self.master, self)
5237db96d56Sopenharmony_ci        self.v_size.set(size)
5247db96d56Sopenharmony_ci        sizes = [1, 2, 3, 4] + list(range(5, 55, 5))
5257db96d56Sopenharmony_ci        if self.size not in sizes:
5267db96d56Sopenharmony_ci            sizes.append(self.size)
5277db96d56Sopenharmony_ci            sizes.sort()
5287db96d56Sopenharmony_ci        self.m_size = OptionMenu(self.botleftframe, self.v_size, *sizes)
5297db96d56Sopenharmony_ci        self.m_size.pack(fill=X)
5307db96d56Sopenharmony_ci
5317db96d56Sopenharmony_ci        self.v_speed = StringVar(self.master)
5327db96d56Sopenharmony_ci        self.v_speed.set("normal")
5337db96d56Sopenharmony_ci        self.m_speed = OptionMenu(self.botleftframe, self.v_speed,
5347db96d56Sopenharmony_ci                                  "single-step", "normal", "fast", "fastest")
5357db96d56Sopenharmony_ci        self.m_speed.pack(fill=X)
5367db96d56Sopenharmony_ci
5377db96d56Sopenharmony_ci        self.b_step = Button(self.botleftframe,
5387db96d56Sopenharmony_ci                             text="Step", command=self.c_step)
5397db96d56Sopenharmony_ci        self.b_step.pack(fill=X)
5407db96d56Sopenharmony_ci
5417db96d56Sopenharmony_ci        self.b_randomize = Button(self.botrightframe,
5427db96d56Sopenharmony_ci                                  text="Randomize", command=self.c_randomize)
5437db96d56Sopenharmony_ci        self.b_randomize.pack(fill=X)
5447db96d56Sopenharmony_ci        self.b_uniform = Button(self.botrightframe,
5457db96d56Sopenharmony_ci                                  text="Uniform", command=self.c_uniform)
5467db96d56Sopenharmony_ci        self.b_uniform.pack(fill=X)
5477db96d56Sopenharmony_ci        self.b_distinct = Button(self.botrightframe,
5487db96d56Sopenharmony_ci                                  text="Distinct", command=self.c_distinct)
5497db96d56Sopenharmony_ci        self.b_distinct.pack(fill=X)
5507db96d56Sopenharmony_ci        self.b_demo = Button(self.botrightframe,
5517db96d56Sopenharmony_ci                             text="Demo", command=self.c_demo)
5527db96d56Sopenharmony_ci        self.b_demo.pack(fill=X)
5537db96d56Sopenharmony_ci        self.b_cancel = Button(self.botrightframe,
5547db96d56Sopenharmony_ci                               text="Cancel", command=self.c_cancel)
5557db96d56Sopenharmony_ci        self.b_cancel.pack(fill=X)
5567db96d56Sopenharmony_ci        self.b_cancel.config(state=DISABLED)
5577db96d56Sopenharmony_ci        self.b_quit = Button(self.botrightframe,
5587db96d56Sopenharmony_ci                             text="Quit", command=self.c_quit)
5597db96d56Sopenharmony_ci        self.b_quit.pack(fill=X)
5607db96d56Sopenharmony_ci
5617db96d56Sopenharmony_ci    def resize(self, newsize):
5627db96d56Sopenharmony_ci        if self.busy:
5637db96d56Sopenharmony_ci            self.master.bell()
5647db96d56Sopenharmony_ci            return
5657db96d56Sopenharmony_ci        self.size = newsize
5667db96d56Sopenharmony_ci        self.array.setdata(range(1, self.size+1))
5677db96d56Sopenharmony_ci
5687db96d56Sopenharmony_ci    def c_qsort(self):
5697db96d56Sopenharmony_ci        self.run(quicksort)
5707db96d56Sopenharmony_ci
5717db96d56Sopenharmony_ci    def c_isort(self):
5727db96d56Sopenharmony_ci        self.run(insertionsort)
5737db96d56Sopenharmony_ci
5747db96d56Sopenharmony_ci    def c_ssort(self):
5757db96d56Sopenharmony_ci        self.run(selectionsort)
5767db96d56Sopenharmony_ci
5777db96d56Sopenharmony_ci    def c_bsort(self):
5787db96d56Sopenharmony_ci        self.run(bubblesort)
5797db96d56Sopenharmony_ci
5807db96d56Sopenharmony_ci    def c_demo(self):
5817db96d56Sopenharmony_ci        self.run(demosort)
5827db96d56Sopenharmony_ci
5837db96d56Sopenharmony_ci    def c_randomize(self):
5847db96d56Sopenharmony_ci        self.run(randomize)
5857db96d56Sopenharmony_ci
5867db96d56Sopenharmony_ci    def c_uniform(self):
5877db96d56Sopenharmony_ci        self.run(uniform)
5887db96d56Sopenharmony_ci
5897db96d56Sopenharmony_ci    def c_distinct(self):
5907db96d56Sopenharmony_ci        self.run(distinct)
5917db96d56Sopenharmony_ci
5927db96d56Sopenharmony_ci    def run(self, func):
5937db96d56Sopenharmony_ci        if self.busy:
5947db96d56Sopenharmony_ci            self.master.bell()
5957db96d56Sopenharmony_ci            return
5967db96d56Sopenharmony_ci        self.busy = 1
5977db96d56Sopenharmony_ci        self.array.setspeed(self.v_speed.get())
5987db96d56Sopenharmony_ci        self.b_cancel.config(state=NORMAL)
5997db96d56Sopenharmony_ci        try:
6007db96d56Sopenharmony_ci            func(self.array)
6017db96d56Sopenharmony_ci        except Array.Cancelled:
6027db96d56Sopenharmony_ci            pass
6037db96d56Sopenharmony_ci        self.b_cancel.config(state=DISABLED)
6047db96d56Sopenharmony_ci        self.busy = 0
6057db96d56Sopenharmony_ci
6067db96d56Sopenharmony_ci    def c_cancel(self):
6077db96d56Sopenharmony_ci        if not self.busy:
6087db96d56Sopenharmony_ci            self.master.bell()
6097db96d56Sopenharmony_ci            return
6107db96d56Sopenharmony_ci        self.array.cancel()
6117db96d56Sopenharmony_ci
6127db96d56Sopenharmony_ci    def c_step(self):
6137db96d56Sopenharmony_ci        if not self.busy:
6147db96d56Sopenharmony_ci            self.master.bell()
6157db96d56Sopenharmony_ci            return
6167db96d56Sopenharmony_ci        self.v_speed.set("single-step")
6177db96d56Sopenharmony_ci        self.array.setspeed("single-step")
6187db96d56Sopenharmony_ci        self.array.step()
6197db96d56Sopenharmony_ci
6207db96d56Sopenharmony_ci    def c_quit(self):
6217db96d56Sopenharmony_ci        if self.busy:
6227db96d56Sopenharmony_ci            self.array.cancel()
6237db96d56Sopenharmony_ci        self.master.after_idle(self.master.quit)
6247db96d56Sopenharmony_ci
6257db96d56Sopenharmony_ci
6267db96d56Sopenharmony_ci# Main program -- for stand-alone operation outside Grail
6277db96d56Sopenharmony_ci
6287db96d56Sopenharmony_cidef main():
6297db96d56Sopenharmony_ci    root = Tk()
6307db96d56Sopenharmony_ci    demo = SortDemo(root)
6317db96d56Sopenharmony_ci    root.protocol('WM_DELETE_WINDOW', demo.c_quit)
6327db96d56Sopenharmony_ci    root.mainloop()
6337db96d56Sopenharmony_ci
6347db96d56Sopenharmony_ciif __name__ == '__main__':
6357db96d56Sopenharmony_ci    main()
636