<div dir="ltr"><div>My long history on this edu-sig listserv @ <a href="http://python.org">python.org</a> has included an agenda around STEM reform.  I've wanted to change US K-16 by displacing math track scientific calculators with true REPLs, then altering the math content based on the new freedom a REPL provides.</div><div><br></div><div>Using the I-Python REPL:</div><div><br></div><div><div><font face="monospace, monospace">In [129]: from px_class import *</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">In [130]: p = P(range(10)).shuffle()</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">In [131]: p</font></div><div><font face="monospace, monospace">Out[131]: P class: ((0, 6), (1, 4), (2, 0))...</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">In [132]: p._code</font></div><div><font face="monospace, monospace">Out[132]: {0: 6, 1: 4, 2: 0, 3: 3, 4: 8, 5: 9, 6: 1, 7: 5, 8: 2, 9: 7}</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">In [133]: p.cyclic()</font></div><div><font face="monospace, monospace">Out[133]: ((0, 6, 1, 4, 8, 2), (3,), (5, 9, 7))</font></div></div><div><font face="monospace, monospace"><br></font></div><div><font face="arial, helvetica, sans-serif">My __repr__ for the P class actually uses ... i.e. this is unedited output.  The idea of a Permutation (P) is very primitive:  a mapping of elements to themselves i.e. a dict where keys and values are the same elements, but with values in any order.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">The identity permutation maps every element to itself. p= P() starts out with the identity permutation, but then any p instance has a .shuffle method to return a new instance with a permuted version of the original dict.  p = P().shuffle() creates a random instance in one step.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">By default, my Permutations use the lowercase alphabet plus space, to form a 27-element self._code (the dict).  The encrypt and decrypt methods may be fed ordinary strings of lowercase letters.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">From:</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><div><font face="monospace, monospace">    p = P().shuffle()</font></div><div><font face="monospace, monospace">    s = "able was i ere i saw elba"</font></div><div><font face="monospace, monospace">    c = p(s)</font></div><div><font face="monospace, monospace">    print("Plain:  ", s)</font></div><div><font face="monospace, monospace">    print("Cipher: ", c)</font></div><div><font face="monospace, monospace">    try:</font></div><div><font face="monospace, monospace">        assert p.decrypt(c) == s</font></div><div><font face="monospace, monospace">        print("Third Test Succeeds")</font></div><div><font face="monospace, monospace">    except AssertionError:</font></div><div><font face="monospace, monospace">        print("Third Test Fails")</font></div></div><div><font face="monospace, monospace"><br></font></div><div><font face="arial, helvetica, sans-serif">comes the output:</font></div><div><font face="monospace, monospace"><br></font></div><div><div style="font-family:monospace,monospace">Plain:   able was i ere i saw elba</div><div style="font-family:monospace,monospace">Cipher:  fp vnufcnlnvjvnlncfunv pf</div><div style="font-family:monospace,monospace">Third Test Succeeds</div><div style="font-family:monospace,monospace"><br></div><div><font face="arial, helvetica, sans-serif">I think it's an economic reality the curriculum publishers do not want to compete with their own products. </font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Computer science in high school has, historically speaking, been a back-of-the-bus second-class citizen, earning only "elective credit" for its course takers, if there's more than a "computer club" (i.e. if the content is not extra-curricular), plus a chance to pass the AP test.  That made sense until the languages got so friendly and easy to use.</font></div></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Now that we're redoing the mathematics track itself, in light of these changes in technology, we have an opportunity to bring in new topics such as Group Theory and Euclid's Algorithm (EA) for the greatest common divisor (GCD).  </font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">With GCD in the picture, we're ready for relative primality and therefore concepts of totative / totient.  I've harped on these topics before.  They're simple and accessible, pre-college, not much background required.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Unless one grasps the concept of Group, there's no getting Field, with Group, Ring and Field being fundamental algebraic structures.  There's no arguing these concepts are irrelevant, even in K-12.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Permutations may be multiplied (the operation), plus each has an inverse, defined as the key->value mapping that goes the other way i.e. the dict is inverted.  How does one invert a dictionary in Python code? Good exercise, so again we're not just earning math credit, we're improving our Python skills.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">The properties of a Group are sometimes given as 'CAIN':</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif"><b>(C)</b>  Closure:  p = P().shuffle(); q = P().shuffle(); p * q is always another P.</font></div><div><font face="arial, helvetica, sans-serif"><b>(A)</b>  Associativity:  (p * q) * r == p * (q * r) for all Permutations p, q, r</font></div><div><font face="arial, helvetica, sans-serif"><b>(I)</b>  Inverse:  every p = P().shuffle() has an inverse such that p * ~p == p ** 0</font></div><div><font face="arial, helvetica, sans-serif"><b>(N)</b> Neutral element:  every group has an Identity element with respect to its operation (*) such that p * P() == P() * p == p.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">I'm currently teaching a course in Python for the State of California.  My adult students join me in real time for 10 four hour segments.  I've just completed my fourth.  We're coding classes (types) already, were looking at simple class constructs from Day One.  I've suggested they adopt the mindset of a future high school student, privileged to have a real REPL.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Last night, for a lab, I had the class tackle adding __pow__ to the P class, given I'd already implemented __mul__.  Playing with a Permutation class is just a tiny fraction of all we do.  I spiral back to it in each session as I introduce new Python features.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Today, I added more methods to the Permutation so I can simply lecture on them tomorrow evening and invite downloading and reuse.  </font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Here's the source code as of now, pretty well tested:</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><div><font face="monospace, monospace"># -*- coding: utf-8 -*-</font></div><div><font face="monospace, monospace">"""</font></div><div><font face="monospace, monospace">Created on Tue Nov 10 17:39:09 2015</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">@author: Kirby Urner</font></div><div><font face="monospace, monospace">(c) MIT License  (fine to reuse / alter / share)</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">Fun for Group Theory + Python</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">"""</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">from random import shuffle </font></div><div><font face="monospace, monospace">from string import ascii_lowercase  # all lowercase letters</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">class P:</font></div><div><font face="monospace, monospace">    """</font></div><div><font face="monospace, monospace">    A Permutation</font></div><div><font face="monospace, monospace">    </font></div><div><font face="monospace, monospace">    self._code: a dict, is a mapping of iterable elements </font></div><div><font face="monospace, monospace">    to themselves in any order.</font></div><div><font face="monospace, monospace">    """   </font></div><div><font face="monospace, monospace">    </font></div><div><font face="monospace, monospace">    def __init__(self, iterable = ascii_lowercase + ' '):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        start out with Identity</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        self._code = dict(zip(iterable, iterable))</font></div><div><font face="monospace, monospace">        </font></div><div><font face="monospace, monospace">    def shuffle(self):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        return a random permutation of this permutation</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        # use shuffle</font></div><div><font face="monospace, monospace">        # something like</font></div><div><font face="monospace, monospace">        the_keys = list(self._code.keys()) # grab keys</font></div><div><font face="monospace, monospace">        shuffle(the_keys)  # shuffles</font></div><div><font face="monospace, monospace">        newP = P()</font></div><div><font face="monospace, monospace">        newP._code = dict(zip(self._code.keys(), the_keys))</font></div><div><font face="monospace, monospace">        return newP</font></div><div><font face="monospace, monospace">        </font></div><div><font face="monospace, monospace">    def encrypt(self, plain):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        turn plaintext into cyphertext using self._code</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        output = ""  # empty string</font></div><div><font face="monospace, monospace">        for c in plain:</font></div><div><font face="monospace, monospace">            output = output + self._code.get(c, c) </font></div><div><font face="monospace, monospace">        return output</font></div><div><font face="monospace, monospace">            </font></div><div><font face="monospace, monospace">    def decrypt(self, cypher):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        Turn cyphertext into plaintext using ~self</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        reverse_P = ~self  # invert me!</font></div><div><font face="monospace, monospace">        output = ""</font></div><div><font face="monospace, monospace">        for c in cypher:</font></div><div><font face="monospace, monospace">            output = output + reverse_P._code.get(c, c)</font></div><div><font face="monospace, monospace">        return output</font></div><div><font face="monospace, monospace"> </font></div><div><font face="monospace, monospace">    def __getitem__(self, key):</font></div><div><font face="monospace, monospace">        return self._code.get(key, None)</font></div><div><font face="monospace, monospace">               </font></div><div><font face="monospace, monospace">    def __repr__(self):</font></div><div><font face="monospace, monospace">        return "P class: " + str(tuple(self._code.items())[:3]) + "..."</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    def cyclic(self):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        cyclic notation, a compact view of a group</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        output = []</font></div><div><font face="monospace, monospace">        the_dict = self._code.copy()</font></div><div><font face="monospace, monospace">        while the_dict:</font></div><div><font face="monospace, monospace">            start = tuple(the_dict.keys())[0]</font></div><div><font face="monospace, monospace">            the_cycle = [start]</font></div><div><font face="monospace, monospace">            the_next = the_dict.pop(start)</font></div><div><font face="monospace, monospace">            while the_next != start:</font></div><div><font face="monospace, monospace">                the_cycle.append(the_next)</font></div><div><font face="monospace, monospace">                the_next = the_dict.pop(the_next)</font></div><div><font face="monospace, monospace">            output.append(tuple(the_cycle))</font></div><div><font face="monospace, monospace">        return tuple(output)</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    def __mul__(self, other): </font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        look up my keys to get values that serve</font></div><div><font face="monospace, monospace">        as keys to get others "target" values</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        new_code = {}</font></div><div><font face="monospace, monospace">        for c in self._code:  # going through my keys</font></div><div><font face="monospace, monospace">            target = other._code[ self._code[c] ]</font></div><div><font face="monospace, monospace">            new_code[c] = target</font></div><div><font face="monospace, monospace">        new_P = P(' ') </font></div><div><font face="monospace, monospace">        new_P._code = new_code</font></div><div><font face="monospace, monospace">        return new_P</font></div><div><font face="monospace, monospace">                </font></div><div><font face="monospace, monospace">    def __pow__(self, exp):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        multiply self * self the right number of times</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        if exp == 0:</font></div><div><font face="monospace, monospace">            output = P()</font></div><div><font face="monospace, monospace">        else:</font></div><div><font face="monospace, monospace">            output = self</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">        for x in range(1, abs(exp)):</font></div><div><font face="monospace, monospace">            output *= self</font></div><div><font face="monospace, monospace">        </font></div><div><font face="monospace, monospace">        if exp < 0:</font></div><div><font face="monospace, monospace">            output = ~output</font></div><div><font face="monospace, monospace">            </font></div><div><font face="monospace, monospace">        return output</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    def __call__(self, s): </font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        another way to encrypt</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        return self.encrypt(s)  </font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    def __invert__(self):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        create new P with reversed dict</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        newP = P(' ')</font></div><div><font face="monospace, monospace">        newP._code = dict(zip(self._code.values(), self._code.keys()))</font></div><div><font face="monospace, monospace">        return newP</font></div><div><font face="monospace, monospace">        </font></div><div><font face="monospace, monospace">    def __eq__(self, other):</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        are these permutation the same?  </font></div><div><font face="monospace, monospace">        Yes if self._code == other._code</font></div><div><font face="monospace, monospace">        """</font></div><div><font face="monospace, monospace">        return self._code == other._code</font></div><div><font face="monospace, monospace">        </font></div><div><font face="monospace, monospace">if __name__ == "__main__":</font></div><div><font face="monospace, monospace">    p = P() # identity permutation</font></div><div><font face="monospace, monospace">    new_p = p.shuffle()</font></div><div><font face="monospace, monospace">    inv_p = ~new_p </font></div><div><font face="monospace, monospace">    try:</font></div><div><font face="monospace, monospace">        assert p == inv_p * new_p   # should be True</font></div><div><font face="monospace, monospace">        print("First Test Succeeds")</font></div><div><font face="monospace, monospace">    except AssertionError:</font></div><div><font face="monospace, monospace">        print("First Test Fails")</font></div><div><font face="monospace, monospace">    #==========    </font></div><div><font face="monospace, monospace">    p = P().shuffle()</font></div><div><font face="monospace, monospace">    try:</font></div><div><font face="monospace, monospace">        assert p ** -1 == ~p</font></div><div><font face="monospace, monospace">        assert p ** -2 == ~(p * p)</font></div><div><font face="monospace, monospace">        assert p ** -2 == (~p * ~p)</font></div><div><font face="monospace, monospace">        print("Second Test Succeeds")</font></div><div><font face="monospace, monospace">    except AssertionError:</font></div><div><font face="monospace, monospace">        print("Second Test Fails")</font></div><div><font face="monospace, monospace">    #========== </font></div><div><font face="monospace, monospace">    p = P().shuffle()</font></div><div><font face="monospace, monospace">    s = "able was i ere i saw elba"</font></div><div><font face="monospace, monospace">    c = p(s)</font></div><div><font face="monospace, monospace">    print("Plain:  ", s)</font></div><div><font face="monospace, monospace">    print("Cipher: ", c)</font></div><div><font face="monospace, monospace">    try:</font></div><div><font face="monospace, monospace">        assert p.decrypt(c) == s</font></div><div><font face="monospace, monospace">        print("Third Test Succeeds")</font></div><div><font face="monospace, monospace">    except AssertionError:</font></div><div><font face="monospace, monospace">        print("Third Test Fails")</font></div></div></div>