Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
| Download
Views: 924
import math import random import functools def merge(arr, l, m, r, cmp, reverse): cmp_helper = cmp if reverse: cmp_helper = lambda a, b: not cmp(a, b) left_midpoint = m - l + 1 right_midpoint = r - m temp_arr_left = [arr[l + i] for i in range(left_midpoint)] temp_arr_right = [arr[m + 1 + i] for i in range(right_midpoint)] i = 0 j = 0 k = l while i < left_midpoint and j < right_midpoint : if cmp_helper(temp_arr_left[i], temp_arr_right[j]): arr[k] = temp_arr_left[i] i += 1 else: arr[k] = temp_arr_right[j] j += 1 k += 1 while i < left_midpoint: arr[k] = temp_arr_left[i] i += 1 k += 1 while j < right_midpoint: arr[k] = temp_arr_right[j] j += 1 k += 1 def merge_sort(arr, left_index=None, right_index=None, cmp=lambda a, b: a < b, reverse=False): if left_index is None: left_index = 0 if right_index is None: right_index = len(arr) - 1 if left_index < right_index: midpoint = math.floor((left_index + right_index - 1)/2) merge_sort(arr, left_index, midpoint, cmp, reverse) merge_sort(arr, midpoint + 1, right_index, cmp, reverse) merge(arr, left_index, midpoint, right_index, cmp, reverse) def css_classes(): ''' pseudo-css-class generator!! dictionary mapping { pseudo-classname: inline-style } ''' classes = { 'border-light': 'border: 1px solid #a0aec0;', 'text-light': 'color: #e6fffa;', 'text-dark': 'color: #cbd5e0;', 'rounded': 'border-radius: 3px;', 'bg-dark': 'background-color: #1a202c;', 'w-max': 'width: max-content;', 'flex': 'display: flex;', 'block': 'display: block;', 'justify-between': 'justify-content: space-between;', 'text-center': 'text-align: center;', } spacings = range(0, 12) scale_factor = 1.6 # note, cocalc uses rootFontSize=10px, I want to pretend it's 16px scale_rem = lambda x: str(round(x * scale_factor, 3)) text_prefixes = 'xs sm base lg xl 2xl 3xl 4xl 5xl 6xl'.split() text_rem_sizes = [0.75, 0.875, 1, 1.125, 1.25, 1.5, 1.875, 2.25, 3, 4] classes.update({'text-{}'.format(text_prefixes[i]): 'font-size: {}rem;'.format(scale_rem(text_rem_sizes[i])) for i in range(len(text_prefixes))}) spacing_opts = { 'l': ['left: '], 'r': ['right: '], 't': ['top: '], 'b': ['bottom: '] } spacing_opts.update({ 'x': spacing_opts['l'] + spacing_opts['r'], 'y': spacing_opts['t'] + spacing_opts['b'] }) spacing_opts[''] = spacing_opts['x'] + spacing_opts['y'] for spacing in spacings: space = str(round(spacing * 0.25, 2)) + 'rem; ' for variant in ["margin", "padding"]: for prefix, rule_list in spacing_opts.items(): classes[variant[0] + prefix + '-' + str(spacing)] = "".join([variant + "-" + rule + space for rule in rule_list]) return classes def apply_classes(raw_html, classes=None): ''' pseudo-css-class injector!! note, doesn't actually support bem cause Im not caring about uniqueness (ie. card__header can be affected by card) params: - raw_html: string containing html!! - classes: object mapping classes to further either standard class names or inline styles (simulates @apply behavior from tw) ''' if classes is not None: for k, v in classes.items(): raw_html = raw_html.replace(k, v) rules = css_classes() for rule, definition in rules.items(): raw_html = raw_html.replace(rule, definition) return raw_html def assignment_html(exernum, assignment, func=None, params=None): ''' returns html for the assignment definition! func={ 'name': 'listswitch(lis, i, k)', 'params': ['lis: some list', 'i: index', 'k: index'], 'returns': ['lis with indices i and k switched'] }, params: - exernum: number of the exercise - assignment: problem statement - func: optional dictionary describing function. needs the following key-value pairs - name: <string> - params: <list<string>> - returns: <list<string>> - params: optional list of interactive inputs ''' res = ''' <div style="margin: 4rem 0;"> <h1>Exercise {}</h1> <p><b>Assignment:</b> {}</p> '''.format(exernum, assignment) if func: res += '<p><b>Function:</b><code style="block mt-4 ml-4">{}</code></p>'.format(func.get('name', '')) if 'params' in func: res += '<div style="ml-4"><i>params:</i><ul>' for param in func['params']: res += "<li>{}</li>".format(param) res += "</ul></div>" if 'returns' in func: res += '<div style="ml-4"><i>returns:</i><ul>' for param in func['returns']: res += "<li>{}</li>".format(param) res += "</ul></div>" if params: res += "<p><b>Interactive input:</b></p><ul>" for param in params: res += "<li>{}</li>".format(param) res += "</ul>" res += '</div>' return apply_classes(res) def plot_arr(arr): ''' helper for show so that we can pass lists ''' return functools.reduce(lambda a,b : a + b, arr) def show_all(arr): ''' helper for show so that we can pass lists ''' for el in arr: show(el) def dist(point_a, point_b): ''' helper to give distance between two points of equal dimension params: - point_a: tuple for point a - point_b: tuple for point b ''' return sum([(a - b) ** 2 for a, b in zip(point_a, point_b)]) ** 0.5 def all_indices(arr, el): ''' helper to give all indices of an element in an array ''' return [i for i in range(len(arr)) if arr[i] == el] def random_list(length, min_val=0, max_val=20): ''' makes a random list ''' return [random.randint(min_val, max_val) for _ in range(length)] def assignment_one(): ''' wrapper for problems from assignment one ''' html(assignment_html( '1.3.2', 'Take two points in the plane and plot the points and the line segment between them.', params=['(a) First point. (as, a, tuple)', '(b) Second point. (as, a, tuple)'] )) @interact def plot_line_with_endpoints( a=input_box(default=(2, 3), label='First Point'), b=input_box(default=(3, 2), label='Second Point') ): # define point styles and add points and line to plot array point_size = 30 point_color = "blue" plot_items = [ point(a, rgbcolor=point_color, size=point_size), point(b, rgbcolor=point_color, size=point_size), line([a, b], rgbcolor="red") ] # make things pretty html(apply_classes(''' <div style="card pt-4 text-dark bg-sys-6"> <h1 style="text-3xl">Line with Endpoints</h1> <h2 style="text-xl my-2">Points:</h2> <div style="container"> <div style="card mr-4"> <div style="header">Point a:</div> <strong>{}</strong> </div> <div style="card mx-4"> <div style="header">Point b:</div> <strong>{}</strong> </div> <div style="card ml-4"> <div style="header">Distance(a, b):</div> <strong>{}</strong> </div> </div> </div> ''', { 'bg-sys-6': 'background-color: #1c1c1e;', 'card': 'border-light bg-dark w-max my-4 px-4 py-2 rounded', 'header': 'text-sm mb-1 text-dark', 'container': 'width: 471px; flex justify-between text-light' }).format(a, b, dist(a, b))) # inject plot array show(plot_arr(plot_items), figsize=[5,5]) html(assignment_html( '1.4.2', 'Take two numbers and state if both are greater than 2, or precisely one of them is greater than 2, or neither of them is greater than 2.', params=['(a) A number, denoted a.', '(b) A number, denoted b.'] )) interact(lambda a=input_box(default=1, label='a'), b=input_box(default=1, label='b'): # make it a one liner for fun, using chained ternaries and a lambda # note, we can use `any` for the "precisely one" since we have already covered the case where both are greater than 2 # also note: far less efficient than the standard conditional tree show('both greater than 2' if all([c > 2 for c in [a, b]]) else 'only one greater than 2' if any([c > 2 for c in [a, b]]) else 'none greater')) def assignment_two(): """ 1.5.3 """ html(assignment_html( '1.5.3', 'Take a list and an element and create a new list that is the original list with the element inserted in the middle of the first list if the first list has an even number of elements, and inserted right after the middle element of the first list if the first list has an odd number of elements.', params=[ '(a) A list.', '(b) An element.', '(c) A switch to show the new list; the default is not to show the new list.' ] )) @interact def add_to_list( a=input_box(default=random_list(random.randint(3, 10)), label='Original list'), elem=input_box(default=random_list(random.randint(1, 4)), label='Element'), show_list=('Show new list', False), ): b = a[:] b.insert(int(math.ceil(len(a) / 2)), elem) res = ["List = {}".format(a), "Element = {}".format(elem)] if show_list: res += ["New list = {}".format(b)] show_all(res) def graham_scan_hull(): def ccw(a, b, c): """ determines if three points are clockwise, counterclockwise, or colinear """ area2 = (b['x'] - a['x']) * (c['y'] - a['y']) - (b['y'] - a['y']) * (c['x'] - a['x']) if (area2 < 0): return -1 # clockwise elif (area2 > 0): return 1 # counterclockwise else: return 0 # colinear def make_points(n, max_x=300, max_y=300): """ makes a list of random points! each random point is a dictionary with an x and a y """ return [{'x': random.random() * max_x, 'y': random.random() * max_y} for _ in range(0, n)] def sort_points(a, b): """ sorts by y coordinate, in case of ties, sorts by x """ if a['y'] > b['y']: return 1 if a['y'] < b['y']: return -1 if a['x'] > b['x']: return 1 if a['x'] < b['x']: return -1 return 0 def sort_polar(point): """ returns a comparator for sorting by polar angle (from a given point) """ def sort_stuff(a, b): diff = lambda p1, p2: { 'x': p1['x'] - p2['x'], 'y': p1['y'] - p2['y'] } if diff(a, point)['y'] >= 0 and diff(b, point)['y'] < 0: # if a is above point, and b is below return -1 elif diff(b, point)['y'] >= 0 and diff(a, point)['y'] < 0: # if b is above point, and a is below return 1 elif diff(a, point)['y'] == 0 and diff(b, point)['y'] == 0: # all on same y coord, resort to x coord if diff(a, point)['x'] >= 0 and diff(b, point)['x'] < 0: # if a is right of point, and b is left return -1 elif diff(b, point)['x'] >= 0 and diff(a, point)['x'] < 0: # if b is right of point, and a is left return 1 else: return 0 # they're the same point! else: return -1 * ccw(point, a, b) # otherwise, return clockwise return sort_stuff def make_convex_hull(points): """ returns list representing convex hull boundary vertices using the graham scan method params: - points: list of dictionaries representing points (must have keys=['x', 'y']) """ stack = [] points.sort(key=functools.cmp_to_key(sort_points)) # conversion explanation: https://stackoverflow.com/questions/32752739/python-how-does-the-functools-cmp-to-key-function-works curr = points[0] # start at bottom most (leftmost as tiebreaker) stack.append(curr) points.sort(key=functools.cmp_to_key(sort_polar(curr))) # sort by polar angle for point in points: # remove top of the stack if we must turn clockwise to reach it! while (len(stack) > 1) and (ccw(stack[len(stack) - 2], stack[len(stack) - 1], point) < 0): stack.pop() stack.append(point) # put current point on the stack return stack html(assignment_html( 'having fun', 'make a convex hull for n points' )) @interact def draw_convex_hull(n=input_box(default=20, label='number of points')): """ draws a convex hull """ if n < 3: show("Must have at least 3 points") else: point_size = 30 point_color = "blue" points = make_points(n) hull = make_convex_hull(points) pair = lambda point: (point['x'], point['y']) plot_items = [point(pair(a), rgbcolor=point_color, size=point_size) for a in points] for i, _ in enumerate(hull): plot_items.append(line([pair(hull[i-1]), pair(hull[i])], rgbcolor="red")) show(plot_arr(plot_items), figsize=[5,5]) def assignment_three(): """ wrapper for assignment three """ def swap(arr, i, j): ''' swaps indices i and j in an array! ignores out of bound indices ''' if all([index in range(len(arr)) for index in [i, j]]): tmp = arr[i] arr[j] = arr[i] arr[i] = tmp return arr html(assignment_html( '1.7.3', 'Make a function that takes a list and two indices and switches the elements of the list corresponding to those indices.', func={ 'name': 'listswitch(lis, i, k)', 'params': ['lis: some list', 'i: index', 'k: index'], 'returns': ['lis with indices i and k switched'] }, params=[ '(a) A list, denoted lis.', '(b) The first index, denoted i.', '(c) The second index, denoted k.', '(d) A switch to show or not show the switched list; the default is not to show the switched list.' ] )) @interact def switch_indices( lis=input_box(default=random_list(8), label='List'), i=input_box(default=random.randint(0, 7), label='First index'), k=input_box(default=random.randint(0, 7), label='Second index'), show_list=('Show switched list', False), ): res = [ "List: {}".format(lis), "i: {}".format(i), "k: {}".format(k), ] if show_list: res += ["Switched List: {}".format(swap(lis, i, k))] show_all(res) html(assignment_html( '1.7.4', 'Make a function that takes a list with distinct elements, two cyclically consecutive elements of the list (in either order) and an arbitrary element, and inserts the arbitrary element into the list between the two consecutive elements.', func={ 'name': 'list_insert_between(lis, lis_elt_one, lis_elt_two, any_elt)', 'params': [ 'lis: some list', 'lis_elt_one: an element of the list', 'lis_elt_two: another element of the list (must be directly before or after lis_elt_one)', 'any_elt: an arbitrary element to be inserted', ], 'returns': ['lis with indices i and k switched'] }, params=[ '(a) A list, denoted lis.', '(b) An element of the list, denoted lis_elt_one.', '(c) Another element of the list, denoted lis_elt_two.', '(d) An arbitrary element, denoted any_elt.', '(e) A switch to show or not show the list with the inserted element; the default is not to show that list.' ] )) @interact def insert_between( lis=input_box(default=random_list(8), label='List'), lis_elt_one=input_box(default=3, label='First elem'), lis_elt_two=input_box(default=4, label='Second elem'), any_elt=input_box(default=5, label='To insert'), show_list=('Show original list', False), ): if not all([el in lis for el in [lis_elt_one, lis_elt_two]]): html(apply_classes('<p style="text-center"><b>Not all elements in the list</b></p>')) el_one_indices = all_indices(lis, lis_elt_one) el_two_indices = all_indices(lis, lis_elt_two) inserted = lis[:] for i in el_one_indices: if i+1 in el_two_indices: inserted.insert(i+1, any_elt) break if i-1 in el_two_indices: inserted.insert(i-1, any_elt) break res = [ "List: {}".format(lis), "First Elem: {}".format(lis_elt_one), "Second Elem: {}".format(lis_elt_two), "To Insert: {}".format(any_elt), "Valid Elements?: {}".format(len(inserted) > len(lis)), ] if show_list: res += ["Switched List: {}".format(inserted)] show_all(res) html(assignment_html( '1.8.1', 'Take two numbers a and b, and use a for loop to write all numbers a<sup>2</sup>, (a + 1)<sup>2</sup>,...,b<sup>2</sup>.', params=['(a) First number, denoted a.', '(b) Second number, denoted b.',] )) @interact def square_all_between( a=input_box(default=3, label='a'), b=input_box(default=10, label='b'), ): show_all(['a = {}'.format(a), 'b = {}'.format(b)] + [n ** 2 for n in range(a, b+1)]) def assignment_four(): """ 1.8.2, 1.9.2, 1.9.3 """ html(assignment_html( '1.8.2', 'Take a number a, and use a while loop to write all numbers a<sup>2</sup>, (a + 3)<sup>2</sup>, . . ., stopping when (5a)<sup>2</sup> is passed.', params=['(a) A number, denoted a.'] )) @interact def show_sequence(a=input_box(default=random.randint(0, 7), label='a')): res = ["a = {}".format(a)] lim = (5 * a) ** 2 while (a - 3) ** 2 < lim: res += [a ** 2] a += 3 show_all(res) html(assignment_html( '1.9.2', 'Make a function that takes a list of points in the plane and a number t (that can be either 0 or 1) and returns the list of the t-coordinates of the points.', func={ 'name': 'listcoord(lis, t)', 'params': ['a list off points in the plane <code>lis</code> and a number <code>t</code> (that can be either 0 or 1)'], 'returns': ['the list of the <i>t</i>-coordinates of the points'] }, params=[ '(a) A list of points in the plane.', '(b) A choice of 0 or 1 for the coordinate.', ] )) @interact def show_index( lis=input_box(default=[(0, 1), (1, 2), (4, 3)], label='List'), coordinate=selector([0, 1], label='Coordinate'), ): show_all([ "List: {}".format(lis), "List of {} coordinates: {}".format(coordinate, list(map(lambda a: a[coordinate], lis))) ]) html(assignment_html( '1.9.3', 'Take a list of points in the plane and plot the polygon with vertices in the list.', params=['(a) A list of points in the plane.'] )) @interact def draw_polygon( points=input_box(default=[(0, 1), (1, 2), (4, 3)], label='List of Vertices of Polygon'), ): show(plot_arr([ point(a, rgbcolor="blue", size=30) + text(i, (a[0] + 0.1, a[1] + 0.1), horizontal_alignment='left', color='green') + line([points[i-1], points[i]], rgbcolor="red") for i, a in enumerate(points)]), figsize=[5,5]) ''' define functions and then seperate interactive bits ''' def add_two(a): return a + 2 interact(lambda a=input_box(default=0, label="input"): show(add_two(a))) def main(): pass # assignment_one() # assignment_two() # assignment_three() # assignment_four() # graham_scan_hull() main()
Interact: please open in CoCalc
<smc_sagews.sage_salvus.InteractFunction object at 0x7f2c6d98e910>