tag:blogger.com,1999:blog-111493652024-03-11T13:57:01.267+00:00Go deh!Mainly Tech projects on Python and Electronic Design Automation.Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.comBlogger249125tag:blogger.com,1999:blog-11149365.post-80721846817026082682023-12-28T19:03:00.000+00:002023-12-28T19:03:39.158+00:00How not to check for a key in a dictionary.<p> I skim the Linkedin Python group and sometimes comment.</p><p>A few days there was <a href="https://www.linkedin.com/feed/update/urn:li:groupPost:6986570-7144637862850867200?commentUrn=urn%3Ali%3Acomment%3A%28groupPost%3A6986570-7144637862850867200%2C7145270541354954752%29&dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287145270541354954752%2Curn%3Ali%3AgroupPost%3A6986570-7144637862850867200%29" rel="nofollow" target="_blank">a poll</a> asking for the way to check if a key is in a Python dictionary and as I write, more than half of the 900+ respondents have chosen<b><i> dict.get</i></b> rather than<b><i> key in dict</i></b>, which is the correct answer!</p><p>When pushed, it seems they are relying on dict.get returning None if the key is not in the dict, but fail to see tha if the key being tested is in the dict, but has a value of None then their test fails. </p><p>Here's some code:</p><div style="background-color: black; color: white; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #7ca668;"># %%</span></div><div><span style="color: white;">mydict1 </span><span style="color: #d4d4d4;">=</span><span style="color: white;"> {</span><span style="color: #ce9178;">'a'</span><span style="color: white;">: </span><span style="color: #b5cea8;">1</span><span style="color: white;">, </span><span style="color: #ce9178;">'b'</span><span style="color: white;">: </span><span style="color: #b5cea8;">2</span><span style="color: white;">}</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(mydict1.get(</span><span style="color: #ce9178;">'c'</span><span style="color: white;">)) </span><span style="color: #7ca668;"># -> None</span></div><br /><div><span style="color: #7ca668;"># %%</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: white;"> </span><span style="color: #dcdcaa;">key_in_dict1</span><span style="color: white;">(</span><span style="color: #9cdcfe;">key_</span><span style="color: white;">, </span><span style="color: #9cdcfe;">dict_</span><span style="color: white;">: </span><span style="color: #4ec9b0;">dict</span><span style="color: white;">) -> </span><span style="color: #4ec9b0;">bool</span><span style="color: white;">:</span></div><div><span style="color: white;"> </span><span style="color: #ce9178;">"FAULTY IMPLEMENTATION"</span></div><div><span style="color: white;"> </span><span style="color: #c586c0;">return</span><span style="color: white;"> dict_.get(key_) </span><span style="color: #d4d4d4;">!=</span><span style="color: white;"> </span><span style="color: #569cd6;">None</span></div><br /><div><span style="color: #7ca668;"># This works</span></div><div><span style="color: white;">mydict1 </span><span style="color: #d4d4d4;">=</span><span style="color: white;"> {</span><span style="color: #ce9178;">'a'</span><span style="color: white;">: </span><span style="color: #b5cea8;">1</span><span style="color: white;">, </span><span style="color: #ce9178;">'b'</span><span style="color: white;">: </span><span style="color: #b5cea8;">2</span><span style="color: white;">}</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict1(</span><span style="color: #ce9178;">'a'</span><span style="color: white;">, mydict1)) </span><span style="color: #7ca668;"># -> True</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict1(</span><span style="color: #ce9178;">'x'</span><span style="color: white;">, mydict1)) </span><span style="color: #7ca668;"># -> False</span></div><div><span style="color: #7ca668;"># %%</span></div><br /><div><span style="color: #7ca668;"># But adding a key of x with value None gives</span></div><div><span style="color: #7ca668;"># the wrong result</span></div><div><span style="color: white;">mydict2 </span><span style="color: #d4d4d4;">=</span><span style="color: white;"> {</span><span style="color: #ce9178;">'a'</span><span style="color: white;">: </span><span style="color: #b5cea8;">1</span><span style="color: white;">, </span><span style="color: #ce9178;">'b'</span><span style="color: white;">: </span><span style="color: #b5cea8;">2</span><span style="color: white;">, </span><span style="color: #ce9178;">'x'</span><span style="color: white;">: </span><span style="color: #569cd6;">None</span><span style="color: white;">}</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict1(</span><span style="color: #ce9178;">'x'</span><span style="color: white;">, mydict2)) </span><span style="color: #7ca668;"># -> False</span></div><br /><div><span style="color: #7ca668;"># %%</span></div><br /><div><span style="color: #7ca668;"># The correct way is to use 'in'</span></div><div><span style="color: #569cd6;">def</span><span style="color: white;"> </span><span style="color: #dcdcaa;">key_in_dict2</span><span style="color: white;">(</span><span style="color: #9cdcfe;">key_</span><span style="color: white;">, </span><span style="color: #9cdcfe;">dict_</span><span style="color: white;">: </span><span style="color: #4ec9b0;">dict</span><span style="color: white;">) -> </span><span style="color: #4ec9b0;">bool</span><span style="color: white;">:</span></div><div><span style="color: white;"> </span><span style="color: #ce9178;">"Pythonic IMPLEMENTATION"</span></div><div><span style="color: white;"> </span><span style="color: #c586c0;">return</span><span style="color: white;"> key_ </span><span style="color: #569cd6;">in</span><span style="color: white;"> dict_</span></div><br /><div><span style="color: #7ca668;"># Tests:</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict2(</span><span style="color: #ce9178;">'a'</span><span style="color: white;">, mydict1)) </span><span style="color: #7ca668;"># -> True</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict2(</span><span style="color: #ce9178;">'x'</span><span style="color: white;">, mydict1)) </span><span style="color: #7ca668;"># -> False</span></div><br /><div><span style="color: #7ca668;"># %%</span></div><br /><div><span style="color: #7ca668;"># And now for keys with None as a value:</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict2(</span><span style="color: #ce9178;">'x'</span><span style="color: white;">, mydict2)) </span><span style="color: #7ca668;"># -> True</span></div><br /><br /></div><p></p><h2 style="text-align: left;">Ugly, code-golf solution using dict.get()</h2><div style="text-align: left;">You can alter the default value returned from dict.get if a key is not found. This allows one to use two calls of dict.get with different defaults to be used to give a correct solution:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div style="background-color: black; color: white; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: white;"> </span><span style="color: #dcdcaa;">key_in_dict3</span><span style="color: white;">(</span><span style="color: #9cdcfe;">key_</span><span style="color: white;">, </span><span style="color: #9cdcfe;">dict_</span><span style="color: white;">: </span><span style="color: #4ec9b0;">dict</span><span style="color: white;">) -> </span><span style="color: #4ec9b0;">bool</span><span style="color: white;">:</span></div><div><span style="color: white;"> </span><span style="color: #ce9178;">"CODE-GOLF IMPLEMENTATION USING GETs"</span></div><div><span style="color: white;"> default1 </span><span style="color: #d4d4d4;">=</span><span style="color: white;"> </span><span style="color: #569cd6;">None</span></div><div><span style="color: white;"> default2 </span><span style="color: #d4d4d4;">=</span><span style="color: white;"> </span><span style="color: #ce9178;">""</span></div><div><span style="color: white;"> </span><span style="color: #c586c0;">return</span><span style="color: white;"> default1 </span><span style="color: #d4d4d4;">!=</span><span style="color: white;"> default2 \</span></div><div><span style="color: white;"> </span><span style="color: #569cd6;">and</span><span style="color: white;"> ( dict_.get(key_, default1) </span><span style="color: #d4d4d4;">!=</span><span style="color: white;"> default1</span></div><div><span style="color: white;"> </span><span style="color: #569cd6;">or</span><span style="color: white;"> dict_.get(key_, default2) </span><span style="color: #d4d4d4;">!=</span><span style="color: white;"> default2)</span></div><br /><div><span style="color: #7ca668;"># Tests:</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict3(</span><span style="color: #ce9178;">'a'</span><span style="color: white;">, mydict1)) </span><span style="color: #7ca668;"># -> True</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict3(</span><span style="color: #ce9178;">'x'</span><span style="color: white;">, mydict1)) </span><span style="color: #7ca668;"># -> False</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: white;">(key_in_dict3(</span><span style="color: #ce9178;">'x'</span><span style="color: white;">, mydict2)) </span><span style="color: #7ca668;"># -> True</span></div><br /><br /></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Of course, don't use key_in_dict3, use version 2, i.e. <b><i>key in dict</i></b>.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>END.</b></div><div style="text-align: left;"><br /></div>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-64237199707185766202023-11-24T17:18:00.001+00:002023-11-24T17:23:46.759+00:00Google Bard vs Microsoft Bing Chat: Vanity questions<p> I ask two questions about my activities online and compare the outputs from the two AI tools.</p><p><br /></p><h2 style="text-align: left;"><u>Google Bard </u><br /></h2><p> </p><div _ngcontent-ng-c2418868009="" class="user-profile-picture-container ng-tns-c2418868009-59 ng-star-inserted" style="text-align: left;"><h3><img _ngcontent-ng-c1528087857="" alt="profile picture" class="user-icon" src="https://lh3.googleusercontent.com/a/ACg8ocKcZ8yh8ZKTwELKsO44DQugacx1cBV2ecdw1vZtlt6Nytw=s64" style="height: 32px; width: 32px;" /> create a rosettacode.org task in the style of user paddy3118</h3></div><div _ngcontent-ng-c2418868009="" class="button-gutter ng-tns-c2418868009-59 ng-star-inserted"></div><div _ngcontent-ng-c2418868009="" class="user-query-container ng-tns-c2418868009-59 ng-star-inserted"><div _ngcontent-ng-c2418868009="" class="button-gutter ng-tns-c2418868009-59 ng-star-inserted"></div></div><div _ngcontent-ng-c1135461041="" class="ng-tns-c1135461041-60"><div _ngcontent-ng-c3572480037="" class="response-container ng-tns-c3572480037-61"><div _ngcontent-ng-c3572480037="" class="multi-response-container ng-tns-c3572480037-61 tts ng-trigger ng-trigger-responsePreviewsEnter ng-star-inserted" style="height: 0px; opacity: 0;"></div><div _ngcontent-ng-c3572480037="" class="ng-tns-c3572480037-61 ng-trigger ng-trigger-singleResponseEnter ng-star-inserted"><div _ngcontent-ng-c3572480037="" class="single-draft-response-container ng-tns-c3572480037-61 tts ng-star-inserted"></div></div></div></div><div _ngcontent-ng-c3572480037="" class="single-draft-response-container ng-tns-c3572480037-61 tts ng-star-inserted"></div><br /><div _ngcontent-ng-c3572480037="" class="presented-response-container ng-tns-c3572480037-61"><div _ngcontent-ng-c3572480037="" class="logo-gutter ng-tns-c3572480037-61 ng-star-inserted"></div></div><div _ngcontent-ng-c3311821294="" class="gutter ng-star-inserted"></div><div _ngcontent-ng-c3572480037="" class="logo-gutter ng-tns-c3572480037-61 ng-star-inserted"><div _ngcontent-ng-c3311821294="" class="gutter ng-star-inserted"></div><div _ngcontent-ng-c3311821294="" class="resize-observable"></div></div><div aria-hidden="true" class="cdk-visually-hidden cdk-focus-trap-anchor" tabindex="0"></div><div _ngcontent-ng-c3864461475="" aria-atomic="true" aria-live="polite" class="container" role="dialog"><div _ngcontent-ng-c2987619535="" aria-atomic="true" aria-live="polite" class="recitation-tooltip"><a _ngcontent-ng-c2987619535="" class="recitation-link" href="https://medium.com/edureka/python-program-prime-number-28001c0d54f2" rel="noopener noreferrer" target="_blank"><div _ngcontent-ng-c2987619535="" class="text-container"><span _ngcontent-ng-c2987619535="" class="label ellipsis"></span></div><span _ngcontent-ng-c2987619535="" aria-label="Opens in a new window"></span></a></div></div><div aria-hidden="true" class="cdk-visually-hidden cdk-focus-trap-anchor" tabindex="0"></div><div aria-hidden="true" class="cdk-visually-hidden cdk-focus-trap-anchor" tabindex="0"></div><div _ngcontent-ng-c3864461475="" aria-atomic="true" aria-live="polite" class="container" role="dialog"><div _ngcontent-ng-c2987619535="" aria-atomic="true" aria-live="polite" class="recitation-tooltip"><a _ngcontent-ng-c2987619535="" class="recitation-link" href="https://github.com/pratyksha/competitive-programming" rel="noopener noreferrer" target="_blank"><div _ngcontent-ng-c2987619535="" class="text-container"><span _ngcontent-ng-c2987619535="" class="label ellipsis"></span></div><span _ngcontent-ng-c2987619535="" aria-label="Opens in a new window"></span></a></div></div><div aria-hidden="true" class="cdk-visually-hidden cdk-focus-trap-anchor" tabindex="0"></div><div _ngcontent-ng-c3572480037="" class="response-container-content ng-tns-c3572480037-61"><div _ngcontent-ng-c1135461041="" class="response-content ng-trigger ng-trigger-responsePopulation ng-tns-c3572480037-61"><div _ngcontent-ng-c135959469="" class="markdown markdown-main-panel" dir="ltr" style="--animation-duration: 600ms;"><p data-sourcepos="1:1-1:65"><span>Sure,</span><span> here is a Rosetta Code task in the style of user paddy3118:</span></p><p data-sourcepos="3:1-3:9"><b>Task:</b></p><p data-sourcepos="5:1-5:153"><span>Write a program that generates the Godeh series.</span><span> The Godeh series is an infinite sequence of integers that is defined by the following recursive formula:</span></p><div _ngcontent-ng-c931180446="" class="code-block ng-star-inserted"><pre _ngcontent-ng-c931180446=""><code _ngcontent-ng-c931180446="" class="code-container no-decoration-radius" role="text">godeh(n) = {
1, if n = 1
godeh(n // 2), if n is even
godeh(n // 2) + godeh(n // 2 + 1), if n is odd
}
</code></pre></div><p data-sourcepos="15:1-15:10"><b>Input:</b></p><p data-sourcepos="17:1-17:13"><span>An integer n.</span></p><p data-sourcepos="19:1-19:11"><b>Output:</b></p><p data-sourcepos="21:1-21:38"><span>The first n terms of the Godeh series.</span></p><p data-sourcepos="23:1-23:12"><b>Example:</b></p><div _ngcontent-ng-c931180446="" class="code-block ng-star-inserted"><pre _ngcontent-ng-c931180446=""><code _ngcontent-ng-c931180446="" class="code-container no-decoration-radius" role="text">Input: 10
Output:
1
1
2
1
3
2
3
1
4
3
</code></pre></div><p data-sourcepos="41:1-41:13"><b>Solution:</b></p><div _ngcontent-ng-c931180446="" class="code-block ng-star-inserted"><div _ngcontent-ng-c931180446="" class="code-block-decoration header gmat-subhead-2 ng-star-inserted"><span _ngcontent-ng-c931180446="">Python</span></div><pre _ngcontent-ng-c931180446=""><code _ngcontent-ng-c931180446="" class="code-container" role="text"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">godeh</span>(<span class="hljs-params">n</span>):</span>
<span class="hljs-keyword">if</span> (n == <span class="hljs-number">1</span>):
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
<span class="hljs-keyword">elif</span> (n % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>):
<span class="hljs-keyword">return</span> godeh(n // <span class="hljs-number">2</span>)
<span class="hljs-keyword">else</span>:
<span class="hljs-keyword">return</span> godeh(n // <span class="hljs-number">2</span>) + godeh(n // <span class="hljs-number">2</span> + <span class="hljs-number">1</span>)
n = <span class="hljs-number">10</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, n + <span class="hljs-number">1</span>):
print(godeh(i))
</code></pre><div _ngcontent-ng-c931180446="" class="code-block-decoration footer gmat-overline hide-from-message-actions ng-star-inserted"><div _ngcontent-ng-c931180446="">Use code with caution. <a _ngcontent-ng-c931180446="" aria-label="Learn more (opens in a new window)" class="disclaimer-suffix" href="https://bard.google.com/faq#coding" rel="noopener noreferrer" target="_blank">Learn more</a></div></div></div></div></div></div><div _ngcontent-ng-c931180446="" class="code-block ng-star-inserted"><div _ngcontent-ng-c931180446="" class="code-block-decoration footer gmat-overline hide-from-message-actions ng-star-inserted"></div></div><p data-sourcepos="57:1-57:22"><b>Paddy3118's style:</b></p><p data-sourcepos="59:1-59:382"><span>Paddy3118's programs are typically well-written and easy to understand.</span><span> They are also often accompanied by clear and concise explanations of the code.</span><span> In this task,</span><span> Paddy3118 would likely provide a brief explanation of the Godeh series and the recursive formula used to define it.</span><span> He would then provide a Python implementation of the program,</span><span> along with an example of how to use it.</span></p><p data-sourcepos="61:1-61:56"><span>Here is an example of a task that Paddy3118 might write:</span></p><p data-sourcepos="63:1-63:9"><b>Task:</b></p><p data-sourcepos="65:1-65:73"><span>Write a program that generates the prime numbers up to a specified input.</span></p><p data-sourcepos="67:1-67:10"><b>Input:</b></p><p data-sourcepos="69:1-69:13"><span>An integer n.</span></p><p data-sourcepos="71:1-71:11"><b class="animating">Output:</b></p><p data-sourcepos="73:1-73:40"><span class="animating">A list of all the prime numbers up to n.</span></p><p data-sourcepos="75:1-75:12"><b class="animating">Example:</b></p><div _ngcontent-ng-c931180446="" class="code-block ng-star-inserted"><pre _ngcontent-ng-c931180446=""><code _ngcontent-ng-c931180446="" class="code-container no-decoration-radius" role="text">Input: 100
Output:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
</code></pre></div><p data-sourcepos="84:1-84:13"><b class="animating">Solution:</b></p><div _ngcontent-ng-c931180446="" class="code-block ng-star-inserted"><div _ngcontent-ng-c931180446="" class="code-block-decoration header gmat-subhead-2 ng-star-inserted"><span _ngcontent-ng-c931180446="">Python</span></div><pre _ngcontent-ng-c931180446=""><code _ngcontent-ng-c931180446="" class="code-container" role="text"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_prime</span>(<span class="hljs-params">n</span>):</span>
<span class="hljs-keyword">if</span> (n <= <span class="hljs-number">1</span>):
<span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
<span class="hljs-keyword">if</span> (n <= <span class="hljs-number">3</span>):
<span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
<span class="hljs-keyword">if</span> (n % <span class="hljs-number">2</span> == <span class="hljs-number">0</span><div class="dynamic-carousel-wrapper"><span class="citation-0"> </span></div><span class="hljs-keyword"><span class="citation-0">or</span></span><span class="citation-0"> n % </span><span class="hljs-number"><span class="citation-0">3</span></span><span class="citation-0"> == </span><span class="hljs-number"><span class="citation-0">0</span></span><span class="citation-0">):
</span><span class="hljs-keyword"><span class="citation-0">return</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0"> </span></div><span class="hljs-literal"><span class="citation-0">False</span></span><span class="citation-0">
i </span><span class="citation-0 citation-1">= </span><span class="hljs-number"><span class="citation-0 citation-1">5</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1">
</span></div><span class="hljs-keyword"><span class="citation-0 citation-1">while</span></span><span class="citation-0 citation-1"> (i * i <= n):
</span><span class="hljs-keyword"><span class="citation-0 citation-1">if</span></span><span class="citation-0 citation-1"> (n % i == </span><span class="hljs-number"><span class="citation-0 citation-1">0</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1"> </span></div><span class="hljs-keyword"><span class="citation-0 citation-1">or</span></span><span class="citation-0 citation-1"> n % (i + </span><span class="hljs-number"><span class="citation-0 citation-1">2</span></span><span class="citation-0 citation-1">) == </span><span class="hljs-number"><span class="citation-0 citation-1">0</span></span><span class="citation-0 citation-1">):
</span><span class="hljs-keyword"><span class="citation-0 citation-1">return</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1"> </span></div><span class="hljs-literal"><span class="citation-0 citation-1">False</span></span><span class="citation-0 citation-1">
i += </span><span class="hljs-number"><span class="citation-0 citation-1">6</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1">
</span></div><span class="hljs-keyword"><span class="citation-0 citation-1">return</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1"> </span></div><span class="hljs-literal"><span class="citation-0 citation-1">True</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1">
</span></div><span class="hljs-function"><span class="hljs-keyword"><span class="citation-0 citation-1">def</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1"> </span></div><span class="hljs-title"><span class="citation-0 citation-1">prime_numbers_up_to</span></span><span class="citation-0 citation-1">(</span><span class="hljs-params"><span class="citation-0 citation-1">n</span></span><span class="citation-0 citation-1">):</span></span><span class="citation-0 citation-1">
primes = []
</span><span class="hljs-keyword"><span class="citation-0 citation-1">for</span></span><span class="citation-0 citation-1"> i </span><span class="hljs-keyword"><span class="citation-0 citation-1">in</span></span><div class="dynamic-carousel-wrapper"><span class="citation-0 citation-1"> </span></div><span class="hljs-built_in"><span class="citation-0 citation-1">range</span></span><span class="citation-0 citation-1">(</span><span class="hljs-number"><span class="citation-0 citation-1">2</span></span><span class="citation-0 citation-1 citation-end-0">,</span><span class="citation-1"> n + </span><span class="hljs-number"><span class="citation-1">1</span></span><span class="citation-1">):
</span><span class="hljs-keyword"><span class="citation-1">if</span></span><span class="citation-1 citation-end-1"> (is_prime(i)):</span>
primes.append(i)
<span class="hljs-keyword">return</span> primes
n = <span class="hljs-number">100</span>
primes = prime_numbers_up_to(n)
print(primes)
</code></pre><div _ngcontent-ng-c931180446="" class="code-block-decoration footer gmat-overline hide-from-message-actions ng-star-inserted"><div _ngcontent-ng-c931180446="">Use code with caution. <a _ngcontent-ng-c931180446="" aria-label="Learn more (opens in a new window)" class="disclaimer-suffix" href="https://bard.google.com/faq#coding" rel="noopener noreferrer" target="_blank">Learn more</a></div></div></div><div _ngcontent-ng-c135959469="" class="markdown markdown-main-panel" dir="ltr" style="--animation-duration: 600ms;"><div _ngcontent-ng-c931180446="" class="code-block ng-star-inserted"><div _ngcontent-ng-c931180446="" class="code-block-decoration footer gmat-overline hide-from-message-actions ng-star-inserted"></div></div></div><div _ngcontent-ng-c1135461041="" class="response-footer ng-tns-c1135461041-60 gap complete"><div _ngcontent-ng-c474111526="" class="sources-list-container"><div _ngcontent-ng-c474111526="" class="sources-container ng-star-inserted"><div _ngcontent-ng-c474111526="" class="recitations-list-container ng-star-inserted"><div _ngcontent-ng-c474111526="" class="sources-header"><h4 _ngcontent-ng-c474111526="" class="gmat-overline header ng-star-inserted">Sources</h4><a _ngcontent-ng-c474111526="" class="icon-link ng-star-inserted" href="https://bard.google.com/faq#citation" rel="noopener noreferrer" target="_blank"></a></div></div></div></div></div><div _ngcontent-ng-c3572480037="" class="presented-response-container ng-tns-c3572480037-61"><div _ngcontent-ng-c3572480037="" class="response-container-content ng-tns-c3572480037-61"><div _ngcontent-ng-c1135461041="" class="response-content ng-trigger ng-trigger-responsePopulation ng-tns-c3572480037-61"><div _ngcontent-ng-c1135461041="" class="response-footer ng-tns-c1135461041-60 gap complete"><div _ngcontent-ng-c474111526="" class="sources-list-container"><div _ngcontent-ng-c474111526="" class="sources-container ng-star-inserted"><div _ngcontent-ng-c474111526="" class="recitations-list-container ng-star-inserted"><div _ngcontent-ng-c474111526="" class="sources-header"><a _ngcontent-ng-c474111526="" class="icon-link ng-star-inserted" href="https://bard.google.com/faq#citation" rel="noopener noreferrer" target="_blank"><span _ngcontent-ng-c474111526="" aria-label="About sources (opens in a new window)"></span></a></div><ol _ngcontent-ng-c474111526="" class="recitations-list ng-star-inserted" id="message-content-recitation-list" start="1"><li _ngcontent-ng-c474111526="" class="recitation-list-content ng-star-inserted"><div _ngcontent-ng-c474111526=""><div _ngcontent-ng-c474111526="" class="recitation-link"><a _ngcontent-ng-c474111526="" href="https://medium.com/edureka/python-program-prime-number-28001c0d54f2" rel="noopener noreferrer" target="_blank"> medium.com/edureka/python-program-prime-number-28001c0d54f2 <span _ngcontent-ng-c474111526="" aria-label="Opens in a new window"></span></a></div></div></li><li _ngcontent-ng-c474111526="" class="recitation-list-content ng-star-inserted"><div _ngcontent-ng-c474111526=""><div _ngcontent-ng-c474111526="" class="recitation-link"><a _ngcontent-ng-c474111526="" href="https://github.com/pratyksha/competitive-programming" rel="noopener noreferrer" target="_blank"> github.com/pratyksha/competitive-programming <span _ngcontent-ng-c474111526="" aria-label="Opens in a new window"></span></a></div></div></li></ol></div><div _ngcontent-ng-c474111526=""></div></div></div></div></div></div></div><div _ngcontent-ng-c3572480037="" class="response-container-footer ng-tns-c3572480037-61"><div _ngcontent-ng-c4113112242="" class="actions-container-v2 ng-star-inserted"><div _ngcontent-ng-c4113112242="" class="buttons-container-v2 ng-star-inserted"></div></div></div><div _ngcontent-ng-c4113112242="" class="menu-button-wrapper"></div><div _ngcontent-ng-c4113112242="" class="menu-button-wrapper"></div><p></p><h2 style="text-align: left;"> </h2><h2 style="text-align: left;"><u>Microsoft Bing Chat</u></h2><div style="text-align: left;"><div class="main" id="cib-chat-main" style="-webkit-text-stroke-width: 0px; background-color: white; box-sizing: border-box; color: #111111; display: flex; flex-direction: column; flex: 1 1 0%; font-family: -apple-system, Aptos, Roboto, SegoeUI, "Segoe UI", "Helvetica Neue", Helvetica, "Microsoft YaHei", "Meiryo UI", Meiryo, "Arial Unicode MS", sans-serif; font-size: 14px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; justify-content: flex-end; letter-spacing: normal; orphans: 2; padding: 0px 32px; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><div class="message"><div class="content text-message-content" style="display: flex; flex-direction: column; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); gap: 6px; line-height: var(--cib-type-body2-line-height); min-height: var(--cib-type-body2-line-height); padding: 10px 16px; user-select: text; word-break: break-word;" tabindex="0"><div aria-level="2" role="heading" style="white-space: pre-wrap;">create a rosettacode.org task in the style of user paddy3118</div></div></div><button aria-label="Copy" role="button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border: medium; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); flex-direction: row; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: auto; outline: none; padding: 0px; position: relative; width: 36px;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5.5028 4.62704L5.5 6.75V17.2542C5.5 19.0491 6.95507 20.5042 8.75 20.5042L17.3663 20.5045C17.0573 21.3782 16.224 22.0042 15.2444 22.0042H8.75C6.12665 22.0042 4 19.8776 4 17.2542V6.75C4 5.76929 4.62745 4.93512 5.5028 4.62704ZM17.75 2C18.9926 2 20 3.00736 20 4.25V17.25C20 18.4926 18.9926 19.5 17.75 19.5H8.75C7.50736 19.5 6.5 18.4926 6.5 17.25V4.25C6.5 3.00736 7.50736 2 8.75 2H17.75ZM17.75 3.5H8.75C8.33579 3.5 8 3.83579 8 4.25V17.25C8 17.6642 8.33579 18 8.75 18H17.75C18.1642 18 18.5 17.6642 18.5 17.25V4.25C18.5 3.83579 18.1642 3.5 17.75 3.5Z"></path></svg>Copy</button><button aria-label="Rewrite" role="button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border: medium; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); flex-direction: row; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: auto; outline: none; padding: 0px; position: relative; width: 36px;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M15.8911 3.04825L3.94103 14.9997C3.5347 15.4061 3.2491 15.9172 3.116 16.4762L2.02041 21.0777C1.96009 21.3311 2.03552 21.5976 2.21968 21.7817C2.40385 21.9659 2.67037 22.0413 2.92373 21.981L7.52498 20.8855C8.08418 20.7523 8.59546 20.4666 9.00191 20.0601L10.1531 18.9088C10.0528 18.4552 10 17.9838 10 17.5C10 17.3026 10.0088 17.1073 10.026 16.9144L7.94119 18.9995C7.73104 19.2097 7.46668 19.3574 7.17755 19.4263L3.76191 20.2395L4.57521 16.8237C4.64402 16.5346 4.79168 16.2704 5.00175 16.0603L15 6.06084L17.9394 9.00018L15.9137 11.0261C16.1068 11.0088 16.3024 11 16.5 11C16.9835 11 17.4547 11.0528 17.9081 11.1529L20.952 8.10861C22.3493 6.71112 22.3493 4.4455 20.9519 3.0481C19.5543 1.65058 17.2885 1.65064 15.8911 3.04825ZM19.8912 4.10876C20.7028 4.9204 20.7029 6.23632 19.8913 7.04801L19 7.93946L16.0606 5.00012L16.9518 4.10884C17.7634 3.29709 19.0795 3.29705 19.8912 4.10876ZM22 17.5C22 20.5376 19.5376 23 16.5 23C13.4624 23 11 20.5376 11 17.5C11 14.4624 13.4624 12 16.5 12C19.5376 12 22 14.4624 22 17.5ZM15.3536 15.3536C15.5488 15.1583 15.5488 14.8417 15.3536 14.6464C15.1583 14.4512 14.8417 14.4512 14.6464 14.6464L13.1464 16.1464C12.9512 16.3417 12.9512 16.6583 13.1464 16.8536L14.6464 18.3536C14.8417 18.5488 15.1583 18.5488 15.3536 18.3536C15.5488 18.1583 15.5488 17.8417 15.3536 17.6464L14.7071 17H16.75C17.9926 17 19 18.0074 19 19.25V19.5C19 19.7761 19.2239 20 19.5 20C19.7761 20 20 19.7761 20 19.5V19.25C20 17.4551 18.5449 16 16.75 16H14.7071L15.3536 15.3536Z"></path></svg>Rewrite</button><p style="font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); line-height: var(--cib-type-body1-line-height); margin: 0px; user-select: text; word-break: break-word;">…</p><div class="message"><div class="content" style="display: flex; flex-direction: column; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); line-height: var(--cib-type-body2-line-height); min-height: var(--cib-type-body2-line-height); padding: 10px 16px; position: relative; user-select: text; word-break: break-word;" tabindex="0"><div class="ac-container ac-adaptiveCard" id="entity-image-top" style="display: block; flex-direction: column; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); line-height: var(--cib-type-body2-line-height); margin: 0px; padding: 0px; user-select: text; word-break: break-word;"><div><button class="entity-image-button" style="align-items: center; animation: var(--cib-motion-duration-normal) var(--cib-motion-easing-bounce) var(--cib-motion-duration-normal) both message-enter; background: none; border-radius: var(--cib-border-radius-medium); border: medium; cursor: pointer; display: flex; float: right; font-family: var(--cib-font-text); justify-content: flex-end; margin-inline-start: 18px; margin: 6px 0px 18px; max-height: 320px; max-width: 280px; min-width: 120px; overflow: hidden; padding: 0px; position: relative; width: 280px;"><div class="entity-image" style="border-radius: 4px; overflow: hidden; position: relative;"><div class="shower-glass" style="display: flex; filter: blur(40px);"><img alt="Rosetta Code" class="ac-image" src="https://www.bing.com/th?id=OSK.b46aa36ea449b78854d829e4500d1f7b&pid=cdx&w=320&h=189&c=7&rs=1" style="border-radius: var(--cib-border-radius-medium); display: block; height: 189px; margin: 0px; max-width: 100%; min-width: 0px; opacity: 0.5; width: 320px;" /></div><div class="overlay-image" style="align-items: center; display: flex; height: 165.375px; justify-content: center; left: 0px; position: absolute; top: 0px; width: 280px;"><img class="ac-image shower-glass-entity-image" src="https://www.bing.com/th?id=OSK.b46aa36ea449b78854d829e4500d1f7b&pid=cdx&w=320&h=180&c=7" style="border-radius: var(--cib-border-radius-medium); display: block; height: auto; margin: 0px; max-width: 100%; width: auto;" /></div></div><div class="entity-explore-pill" style="align-items: center; background: rgba(0, 0, 0, 0.7); border-radius: var(--cib-border-radius-circular); bottom: 8px; box-sizing: border-box; color: white; display: flex; fill: white; flex-direction: row; font-size: var(--cib-type-caption1-strong-font-size); font-variation-settings: var(--cib-type-caption1-strong-font-variation-settings); font-weight: var(--cib-type-caption1-strong-font-weight); gap: 6px; inset-inline-end: 8px; line-height: var(--cib-type-caption1-strong-line-height); min-height: 28px; padding: 6px 12px; position: absolute;"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M11.4048 9.46478L2.69814 18.1785C1.81212 19.0652 1.81262 20.5022 2.69924 21.3883C3.58585 22.2743 5.02284 22.2738 5.90895 21.3871L14.6148 12.6748L11.4048 9.46478ZM16.8518 15.0068L16.75 15C16.3703 15 16.0565 15.2822 16.0068 15.6482L16 15.75V16.5H15.25C14.8703 16.5 14.5565 16.7822 14.5068 17.1482L14.5 17.25C14.5 17.6297 14.7822 17.9435 15.1482 17.9932L15.25 18H16V18.75C16 19.1297 16.2822 19.4435 16.6482 19.4932L16.75 19.5C17.1297 19.5 17.4435 19.2178 17.4932 18.8518L17.5 18.75V18H18.25C18.6297 18 18.9435 17.7178 18.9932 17.3518L19 17.25C19 16.8703 18.7178 16.5565 18.3518 16.5068L18.25 16.5H17.5V15.75C17.5 15.3703 17.2178 15.0565 16.8518 15.0068L16.75 15L16.8518 15.0068ZM13.3136 7.56538L13.1775 7.69092L12.4658 8.40378L15.6758 11.6138L16.3879 10.9003C17.2654 10.0213 17.2651 8.59738 16.3868 7.71848L16.2304 7.5715C15.3931 6.85388 14.1533 6.85191 13.3136 7.56538ZM6.85177 5.00685L6.75 5C6.3703 5 6.05651 5.28215 6.00685 5.64823L6 5.75V6.5H5.25C4.8703 6.5 4.55651 6.78215 4.50685 7.14823L4.5 7.25C4.5 7.6297 4.78215 7.94349 5.14823 7.99315L5.25 8H6V8.75C6 9.1297 6.28215 9.44349 6.64823 9.49315L6.75 9.5C7.1297 9.5 7.44349 9.21785 7.49315 8.85177L7.5 8.75V8H8.25C8.6297 8 8.94349 7.71785 8.99315 7.35177L9 7.25C9 6.8703 8.71785 6.55651 8.35177 6.50685L8.25 6.5H7.5V5.75C7.5 5.3703 7.21785 5.05651 6.85177 5.00685L6.75 5L6.85177 5.00685ZM18.8518 3.00685L18.75 3C18.3703 3 18.0565 3.28215 18.0068 3.64823L18 3.75V4.5H17.25C16.8703 4.5 16.5565 4.78215 16.5068 5.14823L16.5 5.25C16.5 5.6297 16.7822 5.94349 17.1482 5.99315L17.25 6H18V6.75C18 7.1297 18.2822 7.44349 18.6482 7.49315L18.75 7.5C19.1297 7.5 19.4435 7.21785 19.4932 6.85177L19.5 6.75V6H20.25C20.6297 6 20.9435 5.71785 20.9932 5.35177L21 5.25C21 4.8703 20.7178 4.55651 20.3518 4.50685L20.25 4.5H19.5V3.75C19.5 3.3703 19.2178 3.05651 18.8518 3.00685L18.75 3L18.8518 3.00685Z"></path></svg><div class="entity-explore-text">Explore</div></div></button></div><div class="ac-textBlock" style="color: var(--cib-color-foreground-neutral-primary); display: contents; flex-direction: column;"><p style="font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); line-height: var(--cib-type-body2-line-height); margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px !important; margin: 0px; padding: 0px; user-select: text; word-break: break-word;">I’m sorry, but I am not able to create a Rosetta Code task for you. However, I can provide you with some information about<span> </span><b style="font-size: var(--cib-type-subtitle2-stronger-font-size); font-variation-settings: var(--cib-type-subtitle2-stronger-font-variation-settings); font-weight: var(--cib-type-subtitle2-stronger-font-weight); line-height: var(--cib-type-subtitle2-stronger-line-height);">Paddy3118</b>, a prolific contributor to Rosetta Code who has created many tasks in the past.<span> </span><a class="tooltip-target" data-citationid="9a2eb16e-1782-0556-892b-e161ee897fa7-7-group" href="https://rosettacode.org/wiki/User:Paddy3118" style="border-bottom: 2px solid transparent; color: inherit; position: relative; text-decoration-color: var(--cib-color-foreground-neutral-tertiary); text-decoration-style: dotted; text-underline-offset: 3px;" target="_blank">Paddy3118 is a pseudonym for<span> </span><b style="font-size: var(--cib-type-subtitle2-stronger-font-size); font-variation-settings: var(--cib-type-subtitle2-stronger-font-variation-settings); font-weight: var(--cib-type-subtitle2-stronger-font-weight); line-height: var(--cib-type-subtitle2-stronger-line-height); pointer-events: none;">Donald McCarthy</b>, who works with Electronic Design Automation tools creating design flows, filling in the gaps with novel tools, and generally helping to design and verify integrated circuits</a><a aria-label="1: Donald McCarthy" class="ac-anchor sup-target" data-citationid="9a2eb16e-1782-0556-892b-e161ee897fa7-7" href="https://rosettacode.org/wiki/User:Paddy3118" style="position: relative;" target="_blank"><sup style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-comp-message-citation-border-radius); color: var(--cib-color-foreground-accent-primary); display: inline-flex; font-size: var(--cib-type-caption2-strong-font-size); font-variation-settings: var(--cib-type-caption2-strong-font-variation-settings); font-weight: var(--cib-type-caption2-strong-font-weight); height: 14px; justify-content: center; line-height: var(--cib-type-caption2-strong-line-height); margin: 0px 2px; min-width: 14px; outline: transparent solid 1px; position: relative; text-decoration-color: transparent; top: -1px; vertical-align: top;">1</sup></a>.<span> </span><a class="tooltip-target" data-citationid="9a2eb16e-1782-0556-892b-e161ee897fa7-9-group" href="https://rosettacode.org/wiki/User:Paddy3118" style="border-bottom: 2px solid transparent; color: inherit; position: relative; text-decoration-color: var(--cib-color-foreground-neutral-tertiary); text-decoration-style: dotted; text-underline-offset: 3px;">He has created 392 new pages on Rosetta Code, of which 148 were not Talk: pages, from approximately 4523 edits</a><a aria-label="1: Paddy3118 is a pseudonym for Donald McCarthy, who works with Electronic Design Automation tools creating design flows, filling in the gaps with novel tools, and generally helping to design and verify integrated circuits1" class="ac-anchor sup-target" data-citationid="9a2eb16e-1782-0556-892b-e161ee897fa7-9" href="https://rosettacode.org/wiki/User:Paddy3118" style="position: relative;" target="_blank"><sup style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-comp-message-citation-border-radius); color: var(--cib-color-foreground-accent-primary); display: inline-flex; font-size: var(--cib-type-caption2-strong-font-size); font-variation-settings: var(--cib-type-caption2-strong-font-variation-settings); font-weight: var(--cib-type-caption2-strong-font-weight); height: 14px; justify-content: center; line-height: var(--cib-type-caption2-strong-line-height); margin: 0px 2px; min-width: 14px; outline: transparent solid 1px; position: relative; text-decoration-color: transparent; top: -1px; vertical-align: top;">1</sup></a>.<span> </span><a class="tooltip-target" data-citationid="9a2eb16e-1782-0556-892b-e161ee897fa7-11-group" href="https://rosettacode.org/wiki/User:Paddy3118" style="border-bottom: 2px solid transparent; color: inherit; position: relative; text-decoration-color: var(--cib-color-foreground-neutral-tertiary); text-decoration-style: dotted; text-underline-offset: 3px;" target="_blank">You can find more information about Paddy3118 and his contributions to Rosetta Code on his user page<span> </span></a><a aria-label="1: He has created 392 new pages on Rosetta Code, of which 148 were not Talk: pages, from approximately 4523 edits1" class="ac-anchor sup-target" data-citationid="9a2eb16e-1782-0556-892b-e161ee897fa7-11" href="https://rosettacode.org/wiki/User:Paddy3118" style="position: relative;" target="_blank"><sup style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-comp-message-citation-border-radius); color: var(--cib-color-foreground-accent-primary); display: inline-flex; font-size: var(--cib-type-caption2-strong-font-size); font-variation-settings: var(--cib-type-caption2-strong-font-variation-settings); font-weight: var(--cib-type-caption2-strong-font-weight); height: 14px; justify-content: center; line-height: var(--cib-type-caption2-strong-line-height); margin: 0px 2px; min-width: 14px; outline: transparent solid 1px; position: relative; text-decoration-color: transparent; top: -1px; vertical-align: top;">1</sup></a>. I hope this information helps!</p></div></div></div></div><div class="content footer" style="align-items: flex-start; border-top: 1px solid var(--cib-color-stroke-neutral-primary); display: grid; flex-direction: row; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); grid-template-columns: 1fr auto; justify-content: space-between; line-height: var(--cib-type-body2-line-height); min-height: var(--cib-type-body2-line-height); padding: 0px; user-select: text; word-break: break-word;"><div class="learn-more-root" style="display: flex; flex-direction: row; row-gap: 8px;"><div class="learn-more-control" style="display: flex; flex-wrap: nowrap; max-width: max-content; min-width: max-content;"><p class="learn-more" style="align-self: flex-start; font-size: var(--cib-type-body1-stronger-font-size); font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings); font-weight: var(--cib-type-body1-stronger-font-weight); inset-inline-start: 1px; line-height: var(--cib-type-body1-stronger-line-height); margin-inline-end: 8px; margin: 0px; min-width: fit-content; padding-inline-end: 8px; position: relative; top: 2px;">Learn more</p><button aria-expanded="false" class="v2-expand-button" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); cursor: pointer; display: flex; height: 24px; justify-content: center; margin-inline-end: 6px; width: 24px;"><svg viewbox="0 0 2048 2048" xmlns="http://www.w3.org/2000/svg"><path d="M341 768q0-18 7-33t18-27 27-19 34-7q19 0 33 6t27 20l537 537 537-537q26-26 60-26 18 0 33 7t27 18 19 27 7 34q0 35-25 60l-598 598q-25 25-60 25t-60-25L366 828q-25-25-25-60z"></path></svg></button></div><div class="attribution-container" style="display: flex; flex-direction: row; row-gap: 6px; width: 538.575px;"><div class="attribution-items" style="display: flex; flex-flow: wrap; row-gap: 6px;"><a class="attribution-item" data-citationid="91652ed1-97fe-7d6c-5a38-e643c9f98c6c" data-sig="T3fWXKaxvNM8wPpLAZad8KC2+0AG66ubX5yU3DT62V8=" href="https://rosettacode.org/wiki/User:Paddy3118" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">1</span><img class="learn-more-favicon" src="blob:https://www.bing.com/ed2a7235-0edf-4901-b753-baecec3aa9a0" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">rosettacode.org</span></a><a class="attribution-item" data-citationid="b6a55ddd-4d17-f38a-e2dd-33f0e65b91a7" data-sig="1S7di/ASlY/JOyxp3MswRHJCAnleWZ1ZD4xhHqHfsc4=" href="https://rosettacode.org/wiki/Rosetta_Code:Village_Pump/Suggest_a_programming_task" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">2</span><img class="learn-more-favicon" src="blob:https://www.bing.com/ed2a7235-0edf-4901-b753-baecec3aa9a0" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">rosettacode.org</span></a><a class="attribution-item" data-citationid="514b43ad-5e4f-0405-baad-f71a2eda9af5" data-sig="n7dDrjF4B+WJS4U0UwBC9QgHRmu2f5kRbTHEI62BEYE=" href="https://rosettacode.org/wiki/User_talk:Paddy3118" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">3</span><img class="learn-more-favicon" src="blob:https://www.bing.com/ed2a7235-0edf-4901-b753-baecec3aa9a0" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">rosettacode.org</span></a><a class="attribution-item" data-citationid="086c2648-4e81-8cdb-5517-a964109a80fa" data-sig="HrFokeI0JAXllpzeXDyyXXHOiQxhcVjHyR3LFCRI5oY=" href="https://www.rosettacode.org/wiki/User:Paddy3118/Bye_spam" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">4</span><img class="learn-more-favicon" src="blob:https://www.bing.com/2b42888a-2eaa-4a40-9199-8d4a9aa30368" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">rosettacode.org</span></a><a class="attribution-item" data-citationid="c47ea01f-2873-3529-4b4f-334008e57990" href="https://en.wikipedia.org/wiki/Rosetta_Code" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">5</span><img class="learn-more-favicon" src="blob:https://www.bing.com/a4d1fdb7-08ef-460c-b12a-868a1837d670" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">en.wikipedia.org</span></a></div></div></div><div class="text" style="display: flex; font-size: var(--cib-type-body1-stronger-font-size); font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings); font-weight: var(--cib-type-body1-stronger-font-weight); gap: 3px; line-height: var(--cib-type-body1-stronger-line-height);"><span>8</span><span>of</span><span>30</span></div><div class="indicator" style="background: rgb(44, 130, 71); border-radius: var(--cib-border-radius-circular); height: 10px; width: 10px;"></div><div class="bar complete" role="progressbar" style="animation: 2s linear 0s infinite normal none running borealisBar; background: var(--cib-color-fill-accent-gradient-primary); border-radius: var(--cib-border-radius-circular); bottom: 0px; inset-inline: 0px 251.062px; opacity: 0; position: absolute; top: 0px; transition-duration: var(--cib-motion-duration-slow); transition-timing-function: var(--cib-motion-easing-transition); width: 0px;"></div></div><div class="container" style="display: flex; flex-direction: row;"><button aria-label="Like" aria-pressed="false" class="positive" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16.4996 5.20235C16.4996 2.76041 15.3595 1.00366 13.4932 1.00366C12.467 1.00366 12.1149 1.60503 11.747 3.00324C11.6719 3.29209 11.635 3.43272 11.596 3.57133C11.495 3.93007 11.3192 4.54082 11.069 5.40234C11.0623 5.42542 11.0524 5.44717 11.0396 5.46724L8.17281 9.95291C7.49476 11.0139 6.49429 11.8294 5.31841 12.2796L4.84513 12.4608C3.5984 12.9381 2.87457 14.2419 3.1287 15.5525L3.53319 17.6385C3.77462 18.8836 4.71828 19.8745 5.9501 20.1764L13.5778 22.046C16.109 22.6664 18.6674 21.1314 19.3113 18.6061L20.7262 13.057C21.1697 11.3177 20.1192 9.5482 18.3799 9.10473C18.1175 9.03782 17.8478 9.00398 17.5769 9.00398H15.7536C16.2497 7.37109 16.4996 6.1113 16.4996 5.20235ZM4.60127 15.2669C4.48576 14.6712 4.81477 14.0786 5.38147 13.8616L5.85475 13.6804C7.33036 13.1154 8.58585 12.0921 9.43674 10.7607L12.3035 6.27501C12.3935 6.13412 12.4629 5.98107 12.5095 5.8205C12.7608 4.95549 12.9375 4.3415 13.0399 3.97761C13.083 3.82436 13.1239 3.66891 13.1976 3.38494C13.3875 2.66324 13.4809 2.50366 13.4932 2.50366C14.3609 2.50366 14.9996 3.48773 14.9996 5.20235C14.9996 6.08683 14.6738 7.53779 14.0158 9.51741C13.8544 10.0029 14.2158 10.504 14.7275 10.504H17.5769C17.7228 10.504 17.868 10.5222 18.0093 10.5582C18.9459 10.797 19.5115 11.7498 19.2727 12.6863L17.8578 18.2355C17.4172 19.9634 15.6668 21.0136 13.9349 20.5891L6.30718 18.7196C5.64389 18.557 5.13577 18.0234 5.00577 17.353L4.60127 15.2669Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Dislike" aria-pressed="false" class="negative" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16.4996 17.9849C16.4996 20.4269 15.3595 22.1836 13.4932 22.1836C12.5183 22.1836 12.1518 21.6409 11.8021 20.3878L11.596 19.6159C11.495 19.2572 11.3192 18.6464 11.069 17.7849C11.0623 17.7618 11.0524 17.7401 11.0396 17.72L8.17281 13.2344C7.49476 12.1734 6.49429 11.3579 5.31841 10.9077L4.84513 10.7265C3.5984 10.2492 2.87457 8.94538 3.1287 7.63481L3.53319 5.54873C3.77462 4.30364 4.71828 3.31273 5.9501 3.01082L13.5778 1.14129C16.109 0.520894 18.6674 2.05583 19.3113 4.58116L20.7262 10.1303C21.1697 11.8696 20.1192 13.6391 18.3799 14.0825C18.1175 14.1494 17.8478 14.1833 17.5769 14.1833H15.7536C16.2497 15.8162 16.4996 17.076 16.4996 17.9849ZM4.60127 7.92034C4.48576 8.51606 4.81477 9.10868 5.38147 9.32565L5.85475 9.50686C7.33036 10.0718 8.58585 11.0952 9.43674 12.4266L12.3035 16.9123C12.3935 17.0531 12.4629 17.2062 12.5095 17.3668L13.0614 19.287L13.2731 20.0784C13.4125 20.5664 13.4827 20.6836 13.4932 20.6836C14.3609 20.6836 14.9996 19.6995 14.9996 17.9849C14.9996 17.1004 14.6738 15.6495 14.0158 13.6698C13.8544 13.1843 14.2158 12.6833 14.7275 12.6833H17.5769C17.7228 12.6833 17.868 12.6651 18.0093 12.629C18.9459 12.3902 19.5115 11.4375 19.2727 10.5009L17.8578 4.95176C17.4172 3.2239 15.6668 2.17369 13.9349 2.59816L6.30718 4.46769C5.64389 4.63026 5.13577 5.16383 5.00577 5.83426L4.60127 7.92034Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Copy" is="cib-button" role="menuitem" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5.5028 4.62704L5.5 6.75V17.2542C5.5 19.0491 6.95507 20.5042 8.75 20.5042L17.3663 20.5045C17.0573 21.3782 16.224 22.0042 15.2444 22.0042H8.75C6.12665 22.0042 4 19.8776 4 17.2542V6.75C4 5.76929 4.62745 4.93512 5.5028 4.62704ZM17.75 2C18.9926 2 20 3.00736 20 4.25V17.25C20 18.4926 18.9926 19.5 17.75 19.5H8.75C7.50736 19.5 6.5 18.4926 6.5 17.25V4.25C6.5 3.00736 7.50736 2 8.75 2H17.75ZM17.75 3.5H8.75C8.33579 3.5 8 3.83579 8 4.25V17.25C8 17.6642 8.33579 18 8.75 18H17.75C18.1642 18 18.5 17.6642 18.5 17.25V4.25C18.5 3.83579 18.1642 3.5 17.75 3.5Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-expanded="false" aria-haspopup="true" aria-label="Export" class="export-button" is="cib-button" role="menuitem" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.2498 20.5009C18.664 20.5008 19 20.8365 19 21.2507C19 21.6649 18.6644 22.0008 18.2502 22.0009L5.25022 22.0047C4.836 22.0048 4.5 21.6691 4.5 21.2549C4.5 20.8407 4.83557 20.5048 5.24978 20.5047L18.2498 20.5009ZM11.6482 2.01271L11.75 2.00586C12.1297 2.00586 12.4435 2.28801 12.4932 2.65409L12.5 2.75586L12.499 16.4409L16.2208 12.7205C16.4871 12.4543 16.9038 12.4301 17.1974 12.648L17.2815 12.7206C17.5477 12.9869 17.5719 13.4036 17.354 13.6972L17.2814 13.7813L12.2837 18.7779C12.0176 19.044 11.6012 19.0683 11.3076 18.8507L11.2235 18.7782L6.22003 13.7816C5.92694 13.4889 5.92661 13.014 6.21931 12.7209C6.48539 12.4545 6.90204 12.43 7.1958 12.6477L7.27997 12.7202L10.999 16.4339L11 2.75586C11 2.37616 11.2822 2.06237 11.6482 2.01271L11.75 2.00586L11.6482 2.01271Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-haspopup="true" aria-label="Share" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.7467 4.00003H10.2109C10.6251 4.00003 10.9609 4.33582 10.9609 4.75003C10.9609 5.12972 10.6788 5.44352 10.3127 5.49318L10.2109 5.50003H6.7467C5.55584 5.50003 4.58106 6.42519 4.50189 7.59598L4.4967 7.75003V17.25C4.4967 18.4409 5.42187 19.4157 6.59266 19.4948L6.7467 19.5H16.2474C17.4383 19.5 18.4131 18.5749 18.4922 17.4041L18.4974 17.25V16.7522C18.4974 16.338 18.8332 16.0022 19.2474 16.0022C19.6271 16.0022 19.9409 16.2844 19.9906 16.6505L19.9974 16.7522V17.25C19.9974 19.2543 18.4251 20.8913 16.4466 20.9948L16.2474 21H6.7467C4.74244 21 3.10543 19.4277 3.0019 17.4492L2.9967 17.25V7.75003C2.9967 5.74577 4.56907 4.10876 6.54755 4.00523L6.7467 4.00003H10.2109H6.7467ZM14.5007 6.51988V3.75003C14.5007 3.12606 15.2075 2.78998 15.6877 3.13983L15.7699 3.20877L21.7645 8.95877C22.0442 9.22712 22.0697 9.65814 21.8408 9.9561L21.7646 10.0412L15.77 15.7931C15.3197 16.2251 14.5878 15.9477 14.5078 15.3589L14.5007 15.2519V12.5266L14.1572 12.5567C11.7575 12.807 9.45748 13.8879 7.24265 15.8174C6.72354 16.2696 5.92041 15.842 6.00579 15.1589C6.67058 9.83933 9.45245 6.90733 14.2013 6.53953L14.5007 6.51988V3.75003V6.51988ZM16.0007 5.50867V7.25003C16.0007 7.66424 15.6649 8.00003 15.2507 8.00003C11.3773 8.00003 8.97667 9.67616 7.93943 13.1572L7.86037 13.4358L8.21256 13.199C10.449 11.7372 12.7985 11 15.2507 11C15.6304 11 15.9442 11.2822 15.9939 11.6483L16.0007 11.75V13.4928L20.1619 9.50012L16.0007 5.50867Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Send to your phone" class="send-to-phone-icon" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-comp-feedback-button-border-radius); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.75 2C14.9926 2 16 3.00736 16 4.25L16.0007 10.0189C15.4812 10.0584 14.9788 10.1589 14.5006 10.3133L14.5 4.25C14.5 3.83579 14.1642 3.5 13.75 3.5H6.25C5.83579 3.5 5.5 3.83579 5.5 4.25V19.75C5.5 20.1642 5.83579 20.5 6.25 20.5L11.3768 20.5009C11.8385 21.0912 12.4 21.5998 13.036 22.0011L6.25 22C5.00736 22 4 20.9926 4 19.75V4.25C4 3.00736 5.00736 2 6.25 2H13.75ZM16.5 11C19.5376 11 22 13.4624 22 16.5C22 19.5376 19.5376 22 16.5 22C13.4624 22 11 19.5376 11 16.5C11 13.4624 13.4624 11 16.5 11ZM16.7157 13.5886L16.6464 13.6464L16.5886 13.7157C16.4705 13.8862 16.4705 14.1138 16.5886 14.2843L16.6464 14.3536L18.2917 15.999L13.4937 16L13.4038 16.0081C13.1997 16.0451 13.0388 16.206 13.0018 16.4101L12.9937 16.5L13.0018 16.5899C13.0388 16.794 13.1997 16.9549 13.4038 16.9919L13.4937 17L18.2937 16.999L16.6464 18.6464L16.5886 18.7157C16.4536 18.9106 16.4729 19.18 16.6464 19.3536C16.82 19.5271 17.0894 19.5464 17.2843 19.4114L17.3536 19.3536L19.8832 16.8212L19.9202 16.7711L19.9622 16.691L19.9882 16.6083L19.9981 16.5444V16.4557L19.9883 16.392L19.9624 16.3094L19.9205 16.2293L19.8832 16.1788L17.3536 13.6464L17.2843 13.5886C17.1382 13.4873 16.9501 13.4729 16.7919 13.5452L16.7157 13.5886ZM10.0767 17.5019C10.1576 18.0246 10.3008 18.5268 10.4984 19.0006L8.75113 19.0038C8.33692 19.0044 8.00062 18.6691 8 18.2549C7.99938 17.8407 8.33466 17.5044 8.74887 17.5038L10.0767 17.5019Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Read aloud" class="readaloud-icon" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative; width: 36px;" type="button"><svg viewbox="0 0 20 19" xmlns="http://www.w3.org/2000/svg"><path d="M13 2.25c0-1.079-1.274-1.65-2.08-.934L6.427 5.309a.75.75 0 0 1-.498.19H2.25A2.25 2.25 0 0 0 0 7.749v4.497a2.25 2.25 0 0 0 2.25 2.25h3.68a.75.75 0 0 1 .498.19l4.491 3.994c.806.716 2.081.144 2.081-.934V2.25ZM7.425 6.43 11.5 2.807v14.382l-4.075-3.624a2.25 2.25 0 0 0-1.495-.569H2.25a.75.75 0 0 1-.75-.75V7.75a.75.75 0 0 1 .75-.75h3.68a2.25 2.25 0 0 0 1.495-.569Zm9.567-2.533a.75.75 0 0 1 1.049.157A9.959 9.959 0 0 1 20 10a9.96 9.96 0 0 1-1.96 5.946.75.75 0 0 1-1.205-.892A8.459 8.459 0 0 0 18.5 10a8.459 8.459 0 0 0-1.665-5.054.75.75 0 0 1 .157-1.049ZM15.143 6.37a.75.75 0 0 1 1.017.303c.536.99.84 2.125.84 3.328a6.973 6.973 0 0 1-.84 3.328.75.75 0 0 1-1.32-.714c.42-.777.66-1.666.66-2.614s-.24-1.837-.66-2.614a.75.75 0 0 1 .303-1.017Z"></path></svg></button></div></div><div class="suggestion-items" style="align-items: center; display: flex; flex-flow: wrap; gap: 100% 8px; height: 34px; justify-content: flex-end; order: 1; overflow: hidden; padding-inline-end: 2px;"><button aria-label="Show learn more suggestions" class="rai-button" style="background: none; border: medium; cursor: pointer; padding: 0px;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C10.3817 22 8.81782 21.6146 7.41286 20.888L3.58704 21.9553C2.92212 22.141 2.23258 21.7525 2.04691 21.0876C1.98546 20.8676 1.98549 20.6349 2.04695 20.4151L3.11461 16.5922C2.38637 15.186 2 13.6203 2 12C2 6.47715 6.47715 2 12 2ZM12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 13.4696 3.87277 14.8834 4.57303 16.1375L4.72368 16.4072L3.61096 20.3914L7.59755 19.2792L7.86709 19.4295C9.12006 20.1281 10.5322 20.5 12 20.5C16.6944 20.5 20.5 16.6944 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5ZM12 15.5C12.5523 15.5 13 15.9477 13 16.5C13 17.0523 12.5523 17.5 12 17.5C11.4477 17.5 11 17.0523 11 16.5C11 15.9477 11.4477 15.5 12 15.5ZM12 6.75C13.5188 6.75 14.75 7.98122 14.75 9.5C14.75 10.5108 14.4525 11.074 13.6989 11.8586L13.5303 12.0303C12.9084 12.6522 12.75 12.9163 12.75 13.5C12.75 13.9142 12.4142 14.25 12 14.25C11.5858 14.25 11.25 13.9142 11.25 13.5C11.25 12.4892 11.5475 11.926 12.3011 11.1414L12.4697 10.9697C13.0916 10.3478 13.25 10.0837 13.25 9.5C13.25 8.80964 12.6904 8.25 12 8.25C11.3528 8.25 10.8205 8.74187 10.7565 9.37219L10.75 9.5C10.75 9.91421 10.4142 10.25 10 10.25C9.58579 10.25 9.25 9.91421 9.25 9.5C9.25 7.98122 10.4812 6.75 12 6.75Z"></path></svg></button><button class="container" style="background: var(--cib-color-fill-accent-primary); border-radius: var(--cib-border-radius-large); border: medium; box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); height: 28px; line-height: var(--cib-type-body1-strong-line-height); margin: 0px; max-width: 100%; min-width: unset; overflow: hidden; padding: 0px 12px; text-overflow: ellipsis; white-space: nowrap; width: min-content;" type="button">What is Rosetta Code?</button><button class="container" style="background: var(--cib-color-fill-accent-primary); border-radius: var(--cib-border-radius-large); border: medium; box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); height: 28px; line-height: var(--cib-type-body1-strong-line-height); margin: 0px; max-width: 100%; min-width: unset; overflow: hidden; padding: 0px 12px; text-overflow: ellipsis; white-space: nowrap; width: min-content;" type="button">Can you tell me more about Electronic Design Automation tools?</button><button class="container" style="background: var(--cib-color-fill-accent-primary); border-radius: var(--cib-border-radius-large); border: medium; box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); height: 28px; line-height: var(--cib-type-body1-strong-line-height); margin: 0px; max-width: 100%; min-width: unset; overflow: hidden; padding: 0px 12px; text-overflow: ellipsis; white-space: nowrap; width: min-content;" type="button">Who are some other prolific contributors to Rosetta Code?</button></div><u> </u></div><div style="text-align: left;"><u> </u></div><div style="text-align: left;"><u> </u></div><div style="text-align: left;"><u> </u></div><p></p><p></p><p><br /></p><h2 style="text-align: left;"> Bard vs Bing</h2><div style="text-align: left;">The actual formatting does not copy-paste well in either case.</div><div style="text-align: left;"><ul style="text-align: left;"><li>Bard <i>attempts </i>to write a task in my style, and gives some tips on my style.</li><li>Bing cannot create the task, but does extract some info on me.</li><li><b>Clearly a Bard win.</b></li><li>Flattery will get them nowhere! (But keep it up mind you).</li></ul></div><p> </p><p> </p><h2 style="text-align: left;"><u>Google Bard #2</u></h2><div _ngcontent-ng-c2418868009="" class="user-profile-picture-container ng-tns-c2418868009-71 ng-star-inserted"><img _ngcontent-ng-c1528087857="" alt="profile picture" class="user-icon" src="https://lh3.googleusercontent.com/a/ACg8ocKcZ8yh8ZKTwELKsO44DQugacx1cBV2ecdw1vZtlt6Nytw=s64" style="height: 32px; width: 32px;" /></div><div _ngcontent-ng-c2418868009="" class="query-content ng-tns-c2418868009-71" id="user-query-content-4"><h2 _ngcontent-ng-c2418868009="" class="query-text ng-tns-c2418868009-71 ng-star-inserted" dir="ltr">
what does paddy3118 do online?</h2></div><div _ngcontent-ng-c2163816378="" aria-expanded="false" aria-haspopup="menu" class="mat-mdc-menu-trigger playback-speed-menu-trigger multi" role="menu"></div><div _ngcontent-ng-c3572480037="" class="presented-response-container ng-tns-c3572480037-73"><div _ngcontent-ng-c3572480037="" class="logo-gutter ng-tns-c3572480037-73 ng-star-inserted"></div></div><div _ngcontent-ng-c3572480037="" class="logo-gutter ng-tns-c3572480037-73 ng-star-inserted"><div _ngcontent-ng-c3311821294="" class="resize-observable"></div></div><div _ngcontent-ng-c135959469="" class="markdown markdown-main-panel" dir="ltr" style="--animation-duration: 600ms;"><p data-sourcepos="1:1-1:186"><span>Paddy3118 is an active participant in the online programming community,</span><span> particularly in areas related to mathematics and algorithms.</span><span> They have authored numerous programs and solutions to coding challenges,</span><span> demonstrating expertise in various programming languages and problem-solving techniques.</span><span> Paddy3118's contributions can be found across various platforms,</span><span> including:</span></p><p data-sourcepos="3:1-3:130"><b>GitHub:</b><span> Paddy3118 has a notable presence on GitHub,</span><span> where they have authored over 50 programs covering a wide range of topics,</span><span> including:</span></p><ul data-sourcepos="5:1-5:179"><li data-sourcepos="5:1-5:179"><p data-sourcepos="5:3-5:180"><b>Mathematical algorithms:</b><span> Implementing various mathematical concepts and algorithms,</span><span> such as the Godeh series generator,</span><span> Collatz conjecture tester,</span><span> and prime number generator.</span></p></li><li data-sourcepos="7:1-8:0"><p data-sourcepos="7:3-7:154"><b>Problem-solving challenges:</b><span> Tackling coding challenges from platforms like Codewars,</span><span> showcasing their ability to solve complex problems efficiently.</span></p></li><li data-sourcepos="9:1-10:0"><p data-sourcepos="9:3-9:175"><b>Data manipulation and analysis:</b><span> Demonstrating proficiency in handling and analyzing data through programs like the factorial calculator and Fibonacci sequence generator.</span></p></li></ul><p data-sourcepos="11:1-11:137"><span>Their GitHub repositories often include detailed explanations of the code,</span><span> making them valuable learning resources for other programmers.</span></p><p data-sourcepos="13:1-13:198"><b>Stack Overflow:</b><span> Paddy3118 is an active contributor on Stack Overflow,</span><span> a question-and-answer website for programmers.</span><span> They have answered over 1,</span><span>000 questions with an average score of 4.</span><span>5 out of 5,</span><span> indicating their deep understanding of programming concepts and their willingness to share their knowledge with others.</span></p><p data-sourcepos="15:1-15:305"><b>Codewars:</b><span> Paddy3118 is a skilled competitor on Codewars,</span><span> a coding challenge platform.</span><span> They have solved over 1,</span><span>000 coding challenges and currently rank in the top 1% of users.</span><span> This achievement highlights their ability to think creatively and apply their programming expertise to solve complex problems.</span></p><p data-sourcepos="17:1-17:242"><b>GitHub Discussions:</b><span> Paddy3118 actively engages in discussions on GitHub Discussions,</span><span> a forum where users discuss technical topics.</span><span class="animating"> They provide helpful advice,</span><span class="animating"> share insights,</span><span class="animating"> and contribute to the overall knowledge base of the community.</span></p><p data-sourcepos="19:1-19:206"><span class="animating">Overall,</span><span class="animating"> Paddy3118 is a valuable member of the online programming community,</span><span class="animating"> recognized for their contributions to various platforms and their willingness to share their knowledge and expertise with others.</span></p></div><h2 style="text-align: left;"></h2><h2 style="text-align: left;"><u>Microsoft Bing Chat #2</u></h2><div class="main" id="cib-chat-main" style="-webkit-text-stroke-width: 0px; background-color: white; box-sizing: border-box; color: #111111; display: flex; flex-direction: column; flex: 1 1 0%; font-family: -apple-system, Aptos, Roboto, SegoeUI, "Segoe UI", "Helvetica Neue", Helvetica, "Microsoft YaHei", "Meiryo UI", Meiryo, "Arial Unicode MS", sans-serif; font-size: 14px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; justify-content: flex-end; letter-spacing: normal; orphans: 2; padding: 0px 32px; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><div class="message"><div class="content text-message-content" style="display: flex; flex-direction: column; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); gap: 6px; line-height: var(--cib-type-body2-line-height); min-height: var(--cib-type-body2-line-height); padding: 10px 16px; user-select: text; word-break: break-word;" tabindex="0"><div aria-level="2" role="heading" style="white-space: pre-wrap;">what does paddy3118 do online?</div></div></div><button aria-label="Copy" role="button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border: medium; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); flex-direction: row; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: auto; outline: none; padding: 0px; position: relative; width: 36px;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5.5028 4.62704L5.5 6.75V17.2542C5.5 19.0491 6.95507 20.5042 8.75 20.5042L17.3663 20.5045C17.0573 21.3782 16.224 22.0042 15.2444 22.0042H8.75C6.12665 22.0042 4 19.8776 4 17.2542V6.75C4 5.76929 4.62745 4.93512 5.5028 4.62704ZM17.75 2C18.9926 2 20 3.00736 20 4.25V17.25C20 18.4926 18.9926 19.5 17.75 19.5H8.75C7.50736 19.5 6.5 18.4926 6.5 17.25V4.25C6.5 3.00736 7.50736 2 8.75 2H17.75ZM17.75 3.5H8.75C8.33579 3.5 8 3.83579 8 4.25V17.25C8 17.6642 8.33579 18 8.75 18H17.75C18.1642 18 18.5 17.6642 18.5 17.25V4.25C18.5 3.83579 18.1642 3.5 17.75 3.5Z"></path></svg>Copy</button><button aria-label="Rewrite" role="button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border: medium; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); flex-direction: row; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: auto; outline: none; padding: 0px; position: relative; width: 36px;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M15.8911 3.04825L3.94103 14.9997C3.5347 15.4061 3.2491 15.9172 3.116 16.4762L2.02041 21.0777C1.96009 21.3311 2.03552 21.5976 2.21968 21.7817C2.40385 21.9659 2.67037 22.0413 2.92373 21.981L7.52498 20.8855C8.08418 20.7523 8.59546 20.4666 9.00191 20.0601L10.1531 18.9088C10.0528 18.4552 10 17.9838 10 17.5C10 17.3026 10.0088 17.1073 10.026 16.9144L7.94119 18.9995C7.73104 19.2097 7.46668 19.3574 7.17755 19.4263L3.76191 20.2395L4.57521 16.8237C4.64402 16.5346 4.79168 16.2704 5.00175 16.0603L15 6.06084L17.9394 9.00018L15.9137 11.0261C16.1068 11.0088 16.3024 11 16.5 11C16.9835 11 17.4547 11.0528 17.9081 11.1529L20.952 8.10861C22.3493 6.71112 22.3493 4.4455 20.9519 3.0481C19.5543 1.65058 17.2885 1.65064 15.8911 3.04825ZM19.8912 4.10876C20.7028 4.9204 20.7029 6.23632 19.8913 7.04801L19 7.93946L16.0606 5.00012L16.9518 4.10884C17.7634 3.29709 19.0795 3.29705 19.8912 4.10876ZM22 17.5C22 20.5376 19.5376 23 16.5 23C13.4624 23 11 20.5376 11 17.5C11 14.4624 13.4624 12 16.5 12C19.5376 12 22 14.4624 22 17.5ZM15.3536 15.3536C15.5488 15.1583 15.5488 14.8417 15.3536 14.6464C15.1583 14.4512 14.8417 14.4512 14.6464 14.6464L13.1464 16.1464C12.9512 16.3417 12.9512 16.6583 13.1464 16.8536L14.6464 18.3536C14.8417 18.5488 15.1583 18.5488 15.3536 18.3536C15.5488 18.1583 15.5488 17.8417 15.3536 17.6464L14.7071 17H16.75C17.9926 17 19 18.0074 19 19.25V19.5C19 19.7761 19.2239 20 19.5 20C19.7761 20 20 19.7761 20 19.5V19.25C20 17.4551 18.5449 16 16.75 16H14.7071L15.3536 15.3536Z"></path></svg>Rewrite</button><div class="meta-text" style="font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); line-height: var(--cib-type-body1-line-height); user-select: text; word-break: break-word;"><p style="font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); line-height: var(--cib-type-body1-line-height); margin: 0px; user-select: text; word-break: break-word;">…</p></div><div class="message"><div class="content" style="display: flex; flex-direction: column; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); line-height: var(--cib-type-body2-line-height); min-height: var(--cib-type-body2-line-height); padding: 10px 16px; position: relative; user-select: text; word-break: break-word;" tabindex="0"><div class="ac-container ac-adaptiveCard" style="display: flex; flex-direction: column; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); line-height: var(--cib-type-body2-line-height); margin: 0px; padding: 0px; user-select: text; word-break: break-word;"><div class="ac-textBlock" style="color: var(--cib-color-foreground-neutral-primary); display: flex; flex-direction: column;"><p style="font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); line-height: var(--cib-type-body2-line-height); margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px !important; margin: 0px; padding: 0px; user-select: text; word-break: break-word;">I’m sorry, but I could not find any information about what Paddy3118 does online.<span> </span><a class="tooltip-target" data-citationid="3faaa63a-92fb-935f-a69c-490fba41f6f0-3-group" href="https://you.dj/free-dj-software" style="border-bottom: 2px solid transparent; color: inherit; position: relative; text-decoration-color: var(--cib-color-foreground-neutral-tertiary); text-decoration-style: dotted; text-underline-offset: 3px;" target="_blank">However, I can tell you that Paddy3118 is a pseudonym for<span> </span><b style="font-size: var(--cib-type-subtitle2-stronger-font-size); font-variation-settings: var(--cib-type-subtitle2-stronger-font-variation-settings); font-weight: var(--cib-type-subtitle2-stronger-font-weight); line-height: var(--cib-type-subtitle2-stronger-line-height); pointer-events: none;">Donald McCarthy</b>, who works with Electronic Design Automation tools creating design flows, filling in the gaps with novel tools, and generally helping to design and verify integrated circuits</a><a aria-label="1: Donald McCarthy" class="ac-anchor sup-target" data-citationid="3faaa63a-92fb-935f-a69c-490fba41f6f0-3" href="https://you.dj/free-dj-software" style="position: relative;" target="_blank"><sup style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-comp-message-citation-border-radius); color: var(--cib-color-foreground-accent-primary); display: inline-flex; font-size: var(--cib-type-caption2-strong-font-size); font-variation-settings: var(--cib-type-caption2-strong-font-variation-settings); font-weight: var(--cib-type-caption2-strong-font-weight); height: 14px; justify-content: center; line-height: var(--cib-type-caption2-strong-line-height); margin: 0px 2px; min-width: 14px; outline: transparent solid 1px; position: relative; text-decoration-color: transparent; top: -1px; vertical-align: top;">1</sup></a>.<span> </span><a class="tooltip-target" data-citationid="3faaa63a-92fb-935f-a69c-490fba41f6f0-5-group" href="https://you.dj/free-dj-software" style="border-bottom: 2px solid transparent; color: inherit; position: relative; text-decoration-color: var(--cib-color-foreground-neutral-tertiary); text-decoration-style: dotted; text-underline-offset: 3px;" target="_blank">He has created 392 new pages on Rosetta Code, of which 148 were not Talk: pages, from approximately 4523 edits</a><a aria-label="1: However, I can tell you that Paddy3118 is a pseudonym for Donald McCarthy, who works with Electronic Design Automation tools creating design flows, filling in the gaps with novel tools, and generally helping to design and verify integrated circuits1" class="ac-anchor sup-target" data-citationid="3faaa63a-92fb-935f-a69c-490fba41f6f0-5" href="https://you.dj/free-dj-software" style="position: relative;" target="_blank"><sup style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-comp-message-citation-border-radius); color: var(--cib-color-foreground-accent-primary); display: inline-flex; font-size: var(--cib-type-caption2-strong-font-size); font-variation-settings: var(--cib-type-caption2-strong-font-variation-settings); font-weight: var(--cib-type-caption2-strong-font-weight); height: 14px; justify-content: center; line-height: var(--cib-type-caption2-strong-line-height); margin: 0px 2px; min-width: 14px; outline: transparent solid 1px; position: relative; text-decoration-color: transparent; top: -1px; vertical-align: top;">1</sup></a>.<span> </span><a class="tooltip-target" data-citationid="3faaa63a-92fb-935f-a69c-490fba41f6f0-7-group" href="https://you.dj/free-dj-software" style="border-bottom: 2px solid transparent; color: inherit; position: relative; text-decoration-color: var(--cib-color-foreground-neutral-tertiary); text-decoration-style: dotted; text-underline-offset: 3px;" target="_blank">You can find more information about Paddy3118 and his contributions to Rosetta Code on his user page<span> </span></a><a aria-label="1: He has created 392 new pages on Rosetta Code, of which 148 were not Talk: pages, from approximately 4523 edits1" class="ac-anchor sup-target" data-citationid="3faaa63a-92fb-935f-a69c-490fba41f6f0-7" href="https://you.dj/free-dj-software" style="position: relative;" target="_blank"><sup style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-comp-message-citation-border-radius); color: var(--cib-color-foreground-accent-primary); display: inline-flex; font-size: var(--cib-type-caption2-strong-font-size); font-variation-settings: var(--cib-type-caption2-strong-font-variation-settings); font-weight: var(--cib-type-caption2-strong-font-weight); height: 14px; justify-content: center; line-height: var(--cib-type-caption2-strong-line-height); margin: 0px 2px; min-width: 14px; outline: transparent solid 1px; position: relative; text-decoration-color: transparent; top: -1px; vertical-align: top;">1</sup></a>. I hope this information helps!</p></div></div></div></div><div class="content footer" style="align-items: flex-start; border-top: 1px solid var(--cib-color-stroke-neutral-primary); display: grid; flex-direction: row; font-size: var(--cib-type-body2-font-size); font-variation-settings: var(--cib-type-body2-font-variation-settings); font-weight: var(--cib-type-body2-font-weight); grid-template-columns: 1fr auto; justify-content: space-between; line-height: var(--cib-type-body2-line-height); min-height: var(--cib-type-body2-line-height); padding: 0px; user-select: text; word-break: break-word;"><div class="learn-more-root" style="display: flex; flex-direction: row; row-gap: 8px;"><div class="learn-more-control" style="display: flex; flex-wrap: nowrap; max-width: max-content; min-width: max-content;"><p class="learn-more" style="align-self: flex-start; font-size: var(--cib-type-body1-stronger-font-size); font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings); font-weight: var(--cib-type-body1-stronger-font-weight); inset-inline-start: 1px; line-height: var(--cib-type-body1-stronger-line-height); margin-inline-end: 8px; margin: 0px; min-width: fit-content; padding-inline-end: 8px; position: relative; top: 2px;">Learn more</p></div><div class="attribution-container" style="display: flex; flex-direction: row; row-gap: 6px; width: 434.95px;"><div class="attribution-items" style="display: flex; flex-flow: wrap; row-gap: 6px;"><a class="attribution-item" data-citationid="a5a238ac-884a-133b-8b95-fcef7378c488" data-sig="lT3TnYIW2CNMZZL0zQS4ZBW8lbEf702tOBbfftdafAk=" href="https://you.dj/free-dj-software" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">1</span><img class="learn-more-favicon" src="blob:https://www.bing.com/a28fab2c-caf2-4290-9338-4d8652e3bf48" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">you.dj</span></a><a class="attribution-item" data-citationid="afb0bd9c-0320-ddfa-1705-b56f7250f81d" data-sig="CzeF4mq+nvSpIj5HBBpVApiu+CMlQaF/zRgUO8tzFrw=" href="https://www.photopea.com/" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">2</span><img class="learn-more-favicon" src="blob:https://www.bing.com/aceddf91-9e00-4f82-8c98-11b1d47fef31" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">photopea.com</span></a><a class="attribution-item" data-citationid="68bbdf80-4def-d5a2-fe1e-d23c01f53777" data-sig="xCtNIeOYfe5xH6rlJcyLgF3ybY4IG6uqG62VrTdzWSU=" href="https://silver.urih.com/" style="align-items: center; background: transparent; border-radius: var(--cib-border-radius-medium); border: 1px solid var(--cib-color-fill-accent-alt-primary); box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; display: flex; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); gap: 6px; height: 24px; justify-content: center; line-height: var(--cib-type-body1-strong-line-height); margin-inline-end: 6px; min-width: max-content; padding-inline-end: 8px; text-decoration: none;" target="_blank"><span class="badge" style="align-items: center; background: var(--cib-color-fill-accent-alt-primary); border-radius: var(--cib-border-radius-medium); display: flex; height: 24px; justify-content: center; position: static; text-align: center; width: 24px;">3</span><img class="learn-more-favicon" src="blob:https://www.bing.com/47813e7c-04bc-450a-847f-d1f19bb99390" style="display: block; height: 16px; width: 16px;" /><span class="text-container" style="display: flex; flex-direction: column; flex: 1 1 0%; font-weight: var(--cib-type-body1-font-weight); gap: 6px; justify-content: space-between; line-height: 1rem; min-width: max-content; padding: 4px 0px;">silver.urih.com</span></a></div></div></div><div class="text" style="display: flex; font-size: var(--cib-type-body1-stronger-font-size); font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings); font-weight: var(--cib-type-body1-stronger-font-weight); gap: 3px; line-height: var(--cib-type-body1-stronger-line-height);"><span>10</span><span>of</span><span>30</span></div><div class="indicator" style="background: rgb(44, 130, 71); border-radius: var(--cib-border-radius-circular); height: 10px; width: 10px;"></div><div class="bar complete" role="progressbar" style="animation: 2s linear 0s infinite normal none running borealisBar; background: var(--cib-color-fill-accent-gradient-primary); border-radius: var(--cib-border-radius-circular); bottom: 0px; inset-inline: 0px; opacity: 0; position: absolute; top: 0px; transition-duration: var(--cib-motion-duration-slow); transition-timing-function: var(--cib-motion-easing-transition); width: 0px;"></div></div><div class="container" style="display: flex; flex-direction: row;"><button aria-label="Like" aria-pressed="false" class="positive" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16.4996 5.20235C16.4996 2.76041 15.3595 1.00366 13.4932 1.00366C12.467 1.00366 12.1149 1.60503 11.747 3.00324C11.6719 3.29209 11.635 3.43272 11.596 3.57133C11.495 3.93007 11.3192 4.54082 11.069 5.40234C11.0623 5.42542 11.0524 5.44717 11.0396 5.46724L8.17281 9.95291C7.49476 11.0139 6.49429 11.8294 5.31841 12.2796L4.84513 12.4608C3.5984 12.9381 2.87457 14.2419 3.1287 15.5525L3.53319 17.6385C3.77462 18.8836 4.71828 19.8745 5.9501 20.1764L13.5778 22.046C16.109 22.6664 18.6674 21.1314 19.3113 18.6061L20.7262 13.057C21.1697 11.3177 20.1192 9.5482 18.3799 9.10473C18.1175 9.03782 17.8478 9.00398 17.5769 9.00398H15.7536C16.2497 7.37109 16.4996 6.1113 16.4996 5.20235ZM4.60127 15.2669C4.48576 14.6712 4.81477 14.0786 5.38147 13.8616L5.85475 13.6804C7.33036 13.1154 8.58585 12.0921 9.43674 10.7607L12.3035 6.27501C12.3935 6.13412 12.4629 5.98107 12.5095 5.8205C12.7608 4.95549 12.9375 4.3415 13.0399 3.97761C13.083 3.82436 13.1239 3.66891 13.1976 3.38494C13.3875 2.66324 13.4809 2.50366 13.4932 2.50366C14.3609 2.50366 14.9996 3.48773 14.9996 5.20235C14.9996 6.08683 14.6738 7.53779 14.0158 9.51741C13.8544 10.0029 14.2158 10.504 14.7275 10.504H17.5769C17.7228 10.504 17.868 10.5222 18.0093 10.5582C18.9459 10.797 19.5115 11.7498 19.2727 12.6863L17.8578 18.2355C17.4172 19.9634 15.6668 21.0136 13.9349 20.5891L6.30718 18.7196C5.64389 18.557 5.13577 18.0234 5.00577 17.353L4.60127 15.2669Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Dislike" aria-pressed="false" class="negative" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16.4996 17.9849C16.4996 20.4269 15.3595 22.1836 13.4932 22.1836C12.5183 22.1836 12.1518 21.6409 11.8021 20.3878L11.596 19.6159C11.495 19.2572 11.3192 18.6464 11.069 17.7849C11.0623 17.7618 11.0524 17.7401 11.0396 17.72L8.17281 13.2344C7.49476 12.1734 6.49429 11.3579 5.31841 10.9077L4.84513 10.7265C3.5984 10.2492 2.87457 8.94538 3.1287 7.63481L3.53319 5.54873C3.77462 4.30364 4.71828 3.31273 5.9501 3.01082L13.5778 1.14129C16.109 0.520894 18.6674 2.05583 19.3113 4.58116L20.7262 10.1303C21.1697 11.8696 20.1192 13.6391 18.3799 14.0825C18.1175 14.1494 17.8478 14.1833 17.5769 14.1833H15.7536C16.2497 15.8162 16.4996 17.076 16.4996 17.9849ZM4.60127 7.92034C4.48576 8.51606 4.81477 9.10868 5.38147 9.32565L5.85475 9.50686C7.33036 10.0718 8.58585 11.0952 9.43674 12.4266L12.3035 16.9123C12.3935 17.0531 12.4629 17.2062 12.5095 17.3668L13.0614 19.287L13.2731 20.0784C13.4125 20.5664 13.4827 20.6836 13.4932 20.6836C14.3609 20.6836 14.9996 19.6995 14.9996 17.9849C14.9996 17.1004 14.6738 15.6495 14.0158 13.6698C13.8544 13.1843 14.2158 12.6833 14.7275 12.6833H17.5769C17.7228 12.6833 17.868 12.6651 18.0093 12.629C18.9459 12.3902 19.5115 11.4375 19.2727 10.5009L17.8578 4.95176C17.4172 3.2239 15.6668 2.17369 13.9349 2.59816L6.30718 4.46769C5.64389 4.63026 5.13577 5.16383 5.00577 5.83426L4.60127 7.92034Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Copy" is="cib-button" role="menuitem" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5.5028 4.62704L5.5 6.75V17.2542C5.5 19.0491 6.95507 20.5042 8.75 20.5042L17.3663 20.5045C17.0573 21.3782 16.224 22.0042 15.2444 22.0042H8.75C6.12665 22.0042 4 19.8776 4 17.2542V6.75C4 5.76929 4.62745 4.93512 5.5028 4.62704ZM17.75 2C18.9926 2 20 3.00736 20 4.25V17.25C20 18.4926 18.9926 19.5 17.75 19.5H8.75C7.50736 19.5 6.5 18.4926 6.5 17.25V4.25C6.5 3.00736 7.50736 2 8.75 2H17.75ZM17.75 3.5H8.75C8.33579 3.5 8 3.83579 8 4.25V17.25C8 17.6642 8.33579 18 8.75 18H17.75C18.1642 18 18.5 17.6642 18.5 17.25V4.25C18.5 3.83579 18.1642 3.5 17.75 3.5Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-expanded="false" aria-haspopup="true" aria-label="Export" class="export-button" is="cib-button" role="menuitem" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.2498 20.5009C18.664 20.5008 19 20.8365 19 21.2507C19 21.6649 18.6644 22.0008 18.2502 22.0009L5.25022 22.0047C4.836 22.0048 4.5 21.6691 4.5 21.2549C4.5 20.8407 4.83557 20.5048 5.24978 20.5047L18.2498 20.5009ZM11.6482 2.01271L11.75 2.00586C12.1297 2.00586 12.4435 2.28801 12.4932 2.65409L12.5 2.75586L12.499 16.4409L16.2208 12.7205C16.4871 12.4543 16.9038 12.4301 17.1974 12.648L17.2815 12.7206C17.5477 12.9869 17.5719 13.4036 17.354 13.6972L17.2814 13.7813L12.2837 18.7779C12.0176 19.044 11.6012 19.0683 11.3076 18.8507L11.2235 18.7782L6.22003 13.7816C5.92694 13.4889 5.92661 13.014 6.21931 12.7209C6.48539 12.4545 6.90204 12.43 7.1958 12.6477L7.27997 12.7202L10.999 16.4339L11 2.75586C11 2.37616 11.2822 2.06237 11.6482 2.01271L11.75 2.00586L11.6482 2.01271Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-haspopup="true" aria-label="Share" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.7467 4.00003H10.2109C10.6251 4.00003 10.9609 4.33582 10.9609 4.75003C10.9609 5.12972 10.6788 5.44352 10.3127 5.49318L10.2109 5.50003H6.7467C5.55584 5.50003 4.58106 6.42519 4.50189 7.59598L4.4967 7.75003V17.25C4.4967 18.4409 5.42187 19.4157 6.59266 19.4948L6.7467 19.5H16.2474C17.4383 19.5 18.4131 18.5749 18.4922 17.4041L18.4974 17.25V16.7522C18.4974 16.338 18.8332 16.0022 19.2474 16.0022C19.6271 16.0022 19.9409 16.2844 19.9906 16.6505L19.9974 16.7522V17.25C19.9974 19.2543 18.4251 20.8913 16.4466 20.9948L16.2474 21H6.7467C4.74244 21 3.10543 19.4277 3.0019 17.4492L2.9967 17.25V7.75003C2.9967 5.74577 4.56907 4.10876 6.54755 4.00523L6.7467 4.00003H10.2109H6.7467ZM14.5007 6.51988V3.75003C14.5007 3.12606 15.2075 2.78998 15.6877 3.13983L15.7699 3.20877L21.7645 8.95877C22.0442 9.22712 22.0697 9.65814 21.8408 9.9561L21.7646 10.0412L15.77 15.7931C15.3197 16.2251 14.5878 15.9477 14.5078 15.3589L14.5007 15.2519V12.5266L14.1572 12.5567C11.7575 12.807 9.45748 13.8879 7.24265 15.8174C6.72354 16.2696 5.92041 15.842 6.00579 15.1589C6.67058 9.83933 9.45245 6.90733 14.2013 6.53953L14.5007 6.51988V3.75003V6.51988ZM16.0007 5.50867V7.25003C16.0007 7.66424 15.6649 8.00003 15.2507 8.00003C11.3773 8.00003 8.97667 9.67616 7.93943 13.1572L7.86037 13.4358L8.21256 13.199C10.449 11.7372 12.7985 11 15.2507 11C15.6304 11 15.9442 11.2822 15.9939 11.6483L16.0007 11.75V13.4928L20.1619 9.50012L16.0007 5.50867Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Send to your phone" class="send-to-phone-icon" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-comp-feedback-button-border-radius); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.75 2C14.9926 2 16 3.00736 16 4.25L16.0007 10.0189C15.4812 10.0584 14.9788 10.1589 14.5006 10.3133L14.5 4.25C14.5 3.83579 14.1642 3.5 13.75 3.5H6.25C5.83579 3.5 5.5 3.83579 5.5 4.25V19.75C5.5 20.1642 5.83579 20.5 6.25 20.5L11.3768 20.5009C11.8385 21.0912 12.4 21.5998 13.036 22.0011L6.25 22C5.00736 22 4 20.9926 4 19.75V4.25C4 3.00736 5.00736 2 6.25 2H13.75ZM16.5 11C19.5376 11 22 13.4624 22 16.5C22 19.5376 19.5376 22 16.5 22C13.4624 22 11 19.5376 11 16.5C11 13.4624 13.4624 11 16.5 11ZM16.7157 13.5886L16.6464 13.6464L16.5886 13.7157C16.4705 13.8862 16.4705 14.1138 16.5886 14.2843L16.6464 14.3536L18.2917 15.999L13.4937 16L13.4038 16.0081C13.1997 16.0451 13.0388 16.206 13.0018 16.4101L12.9937 16.5L13.0018 16.5899C13.0388 16.794 13.1997 16.9549 13.4038 16.9919L13.4937 17L18.2937 16.999L16.6464 18.6464L16.5886 18.7157C16.4536 18.9106 16.4729 19.18 16.6464 19.3536C16.82 19.5271 17.0894 19.5464 17.2843 19.4114L17.3536 19.3536L19.8832 16.8212L19.9202 16.7711L19.9622 16.691L19.9882 16.6083L19.9981 16.5444V16.4557L19.9883 16.392L19.9624 16.3094L19.9205 16.2293L19.8832 16.1788L17.3536 13.6464L17.2843 13.5886C17.1382 13.4873 16.9501 13.4729 16.7919 13.5452L16.7157 13.5886ZM10.0767 17.5019C10.1576 18.0246 10.3008 18.5268 10.4984 19.0006L8.75113 19.0038C8.33692 19.0044 8.00062 18.6691 8 18.2549C7.99938 17.8407 8.33466 17.5044 8.74887 17.5038L10.0767 17.5019Z"></path></svg></button><div class="button-divider" style="background: var(--cib-color-stroke-neutral-primary); height: 36px; width: 1px;"></div><button aria-label="Read aloud" class="readaloud-icon" is="cib-button" style="align-items: center; background: var(--cib-color-fill-subtle-primary); border-radius: var(--cib-border-radius-none); border: 1px solid transparent; color: var(--cib-color-foreground-neutral-primary); cursor: pointer; display: flex; fill: var(--cib-color-foreground-neutral-primary); font-family: var(--cib-font-text); font-size: var(--cib-type-body1-font-size); font-variation-settings: var(--cib-type-body1-font-variation-settings); font-weight: var(--cib-type-body1-font-weight); gap: 4px; height: 36px; justify-content: center; line-height: var(--cib-type-body1-line-height); margin: 0px; min-width: 36px; padding: 4px; pointer-events: auto; position: relative; width: 36px;" type="button"><svg viewbox="0 0 20 19" xmlns="http://www.w3.org/2000/svg"><path d="M13 2.25c0-1.079-1.274-1.65-2.08-.934L6.427 5.309a.75.75 0 0 1-.498.19H2.25A2.25 2.25 0 0 0 0 7.749v4.497a2.25 2.25 0 0 0 2.25 2.25h3.68a.75.75 0 0 1 .498.19l4.491 3.994c.806.716 2.081.144 2.081-.934V2.25ZM7.425 6.43 11.5 2.807v14.382l-4.075-3.624a2.25 2.25 0 0 0-1.495-.569H2.25a.75.75 0 0 1-.75-.75V7.75a.75.75 0 0 1 .75-.75h3.68a2.25 2.25 0 0 0 1.495-.569Zm9.567-2.533a.75.75 0 0 1 1.049.157A9.959 9.959 0 0 1 20 10a9.96 9.96 0 0 1-1.96 5.946.75.75 0 0 1-1.205-.892A8.459 8.459 0 0 0 18.5 10a8.459 8.459 0 0 0-1.665-5.054.75.75 0 0 1 .157-1.049ZM15.143 6.37a.75.75 0 0 1 1.017.303c.536.99.84 2.125.84 3.328a6.973 6.973 0 0 1-.84 3.328.75.75 0 0 1-1.32-.714c.42-.777.66-1.666.66-2.614s-.24-1.837-.66-2.614a.75.75 0 0 1 .303-1.017Z"></path></svg></button></div></div><div class="suggestion-items" style="align-items: center; display: flex; flex-flow: wrap; gap: 100% 8px; height: 34px; justify-content: flex-end; order: 1; overflow: hidden; padding-inline-end: 2px;"><button aria-label="Show learn more suggestions" class="rai-button" style="background: none; border: medium; cursor: pointer; padding: 0px;" type="button"><svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C10.3817 22 8.81782 21.6146 7.41286 20.888L3.58704 21.9553C2.92212 22.141 2.23258 21.7525 2.04691 21.0876C1.98546 20.8676 1.98549 20.6349 2.04695 20.4151L3.11461 16.5922C2.38637 15.186 2 13.6203 2 12C2 6.47715 6.47715 2 12 2ZM12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 13.4696 3.87277 14.8834 4.57303 16.1375L4.72368 16.4072L3.61096 20.3914L7.59755 19.2792L7.86709 19.4295C9.12006 20.1281 10.5322 20.5 12 20.5C16.6944 20.5 20.5 16.6944 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5ZM12 15.5C12.5523 15.5 13 15.9477 13 16.5C13 17.0523 12.5523 17.5 12 17.5C11.4477 17.5 11 17.0523 11 16.5C11 15.9477 11.4477 15.5 12 15.5ZM12 6.75C13.5188 6.75 14.75 7.98122 14.75 9.5C14.75 10.5108 14.4525 11.074 13.6989 11.8586L13.5303 12.0303C12.9084 12.6522 12.75 12.9163 12.75 13.5C12.75 13.9142 12.4142 14.25 12 14.25C11.5858 14.25 11.25 13.9142 11.25 13.5C11.25 12.4892 11.5475 11.926 12.3011 11.1414L12.4697 10.9697C13.0916 10.3478 13.25 10.0837 13.25 9.5C13.25 8.80964 12.6904 8.25 12 8.25C11.3528 8.25 10.8205 8.74187 10.7565 9.37219L10.75 9.5C10.75 9.91421 10.4142 10.25 10 10.25C9.58579 10.25 9.25 9.91421 9.25 9.5C9.25 7.98122 10.4812 6.75 12 6.75Z"></path></svg></button><button class="container" style="background: var(--cib-color-fill-accent-primary); border-radius: var(--cib-border-radius-large); border: medium; box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); height: 28px; line-height: var(--cib-type-body1-strong-line-height); margin: 0px; max-width: 100%; min-width: unset; overflow: hidden; padding: 0px 12px; text-overflow: ellipsis; white-space: nowrap; width: min-content;" type="button">What is Rosetta Code?</button><button class="container" style="background: var(--cib-color-fill-accent-primary); border-radius: var(--cib-border-radius-large); border: medium; box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); height: 28px; line-height: var(--cib-type-body1-strong-line-height); margin: 0px; max-width: 100%; min-width: unset; overflow: hidden; padding: 0px 12px; text-overflow: ellipsis; white-space: nowrap; width: min-content;" type="button">Can you tell me more about Electronic Design Automation tools?</button><button class="container" style="background: var(--cib-color-fill-accent-primary); border-radius: var(--cib-border-radius-large); border: medium; box-sizing: border-box; color: var(--cib-color-foreground-accent-primary); cursor: pointer; font-family: var(--cib-font-text); font-size: var(--cib-type-body1-strong-font-size); font-variation-settings: var(--cib-type-body1-strong-font-variation-settings); font-weight: var(--cib-type-body1-strong-font-weight); height: 28px; line-height: var(--cib-type-body1-strong-line-height); margin: 0px; max-width: 100%; min-width: unset; overflow: hidden; padding: 0px 12px; text-overflow: ellipsis; white-space: nowrap; width: min-content;" type="button">Who are some other prolific contributors to Rosetta Code?</button></div><h2 style="text-align: left;"></h2><h2 style="text-align: left;"></h2><p> </p><h2 style="text-align: left;">Bard vs Bing #2</h2><div style="text-align: left;"><ul><li>Bard has found me on numerous sites and attempted to summarise my contributions to them and comments that I like to help</li><li>Bing states up front that it could<b><i> not</i></b> find me but then quotes an extract based on my RosettaCode talk page.</li><li>None mention Python!?! <br /></li><li><b>Another win for Bard.</b><br /></li></ul></div><div style="text-align: left;">At the moment, just using these easy-access AI tools, Bard seems to give better results.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">It's still early days however!</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>END.</b><br /></div><p> </p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-83422704043499394652023-08-26T09:48:00.002+01:002023-08-27T06:51:46.277+01:00"Staring at assorted perms and Python", or "The Ranking and Unranking of Lexical Permutations"<p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgabaF9QYrmnRVE1ABQ_2QQRUbKOBXwP8dyiIaf6ENLGYgBbh2-BlgLmRhossExFSBOsy-Wx6oVdwuhdWvTN07PezsR8jMaHjF_GQB77Ft1Kg3Ado3SNZDLET3V1zJ58bz_SuS6b5ROPj1cVDevh5i659Tf7gDNdDm7juRUSmg-JIxAmTRTO4eD/s351/bwoman_perm.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="351" data-original-width="351" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgabaF9QYrmnRVE1ABQ_2QQRUbKOBXwP8dyiIaf6ENLGYgBbh2-BlgLmRhossExFSBOsy-Wx6oVdwuhdWvTN07PezsR8jMaHjF_GQB77Ft1Kg3Ado3SNZDLET3V1zJ58bz_SuS6b5ROPj1cVDevh5i659Tf7gDNdDm7juRUSmg-JIxAmTRTO4eD/s320/bwoman_perm.jpg" width="320" /></a></div><p></p><p>I have been playing with <a href="https://paddy3118.blogspot.com/2023/08/generalising-simple-encryption-using.html" target="_blank">encryptions </a>and then <a href="https://paddy3118.blogspot.com/2023/08/the-godeh-series-python-and-oeis.html" target="_blank">series </a>that involved permutations. Submitting my Godeh series to the OEIS made me look closer at the <a href="https://docs.python.org/3/library/itertools.html#itertools.permutations" rel="nofollow" target="_blank">permutations </a>that Python produces, and at maybe reducing those permutations to a single integer.</p><p><span style="color: #2b00fe;"><b>(p.s. Post is best read on a larger than portrait phone screen) </b></span><br /></p><p>Here are the start of the permutations used in the Godeh series rows:</p><p>(0,)<br />(0, 1)<br />(1, 0)<br />(0, 1, 2)<br />(0, 2, 1)<br />(1, 0, 2)<br />(1, 2, 0)<br />(2, 0, 1)<br />(2, 1, 0)<br />(0, 1, 2, 3)<br />(0, 1, 3, 2)<br />(0, 2, 1, 3)<br />(0, 2, 3, 1)<br />(0, 3, 1, 2)<br />(0, 3, 2, 1)<br />(1, 0, 2, 3)<br />(1, 0, 3, 2)<br />(1, 2, 0, 3)<br />(1, 2, 3, 0)<br />(1, 3, 0, 2)<br />(1, 3, 2, 0)<br />(2, 0, 1, 3)<br />(2, 0, 3, 1)<br />(2, 1, 0, 3)<br />(2, 1, 3, 0)<br />(2, 3, 0, 1)<br />(2, 3, 1, 0)<br />(3, 0, 1, 2)<br />(3, 0, 2, 1)<br />(3, 1, 0, 2)<br />(3, 1, 2, 0)<br />(3, 2, 0, 1)<br />(3, 2, 1, 0)<br /><br /></p><p>It can be generated by this:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">itertools</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">permutations</span><span style="color: #cccccc;">, </span><span style="color: #4ec9b0;">chain</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">math</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">factorial</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">typing</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">Sequence</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">pprint</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">pp</span></div><br /><div><span style="color: #6a9955;"># %% perms in Python generation order</span></div><div><span style="color: #9cdcfe;">generation_order</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> []</span></div><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">5</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">permutations</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;">)):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">generation_order</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">append</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;">)</span></div></div><p></p><p>I truncate at all the perms for four items, but you get the idea - it could extend in this way without limit.</p><h2 style="text-align: left;">Ordered by...<br /></h2><p>Successive groups of permutations are for incrementing items permuted. Within each group, of permutations of the same amount of items, the order is lexicographical, i.e. the Python sorted order, </p><p>I stared at, and messed around with the ordered perms of four items above, looking for patterns. But lets just confirm the order is sort by width then lexicographical:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">wtl</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">sorted</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">generation_order</span><span style="color: #cccccc;">),</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">key</span><span style="color: #d4d4d4;">=</span><span style="color: #569cd6;">lambda</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">x</span><span style="color: #cccccc;">:(</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">x</span><span style="color: #cccccc;">), </span><span style="color: #9cdcfe;">x</span><span style="color: #cccccc;">))</span></div><div><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">generation_order</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">wtl</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">' == width_then_lexicographical ordering'</span><span style="color: #cccccc;">)</span></div><div><span style="color: #c586c0;">else</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">' != width_then_lexicographical ordering of:'</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">pp</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">wtl</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #6a9955;"># Prints: == width_then_lexicographical ordering</span></div><br /></div><p></p><p><br /></p><h2 style="text-align: left;">Ranks</h2><p>I remembered that I had created a<a href="https://rosettacode.org/wiki/Permutations/Rank_of_a_permutation" rel="nofollow" target="_blank"> Rank of a permutation task</a> some time ago, (2012), and that there was something "off" with the ranking. </p><p>Sure enough the issue was that a rank number gets transformed into a unique permutation, and vice-versa, knowing the start configuration and given any permutation of it then yoy can generate its rank number - <b><i>But the order of permutations for increasing rank number does not have to be that lexical sort used by Python</i></b>, and indeed the <a href="https://rosettacode.org/wiki/Permutations/Rank_of_a_permutation#Python:_Myrvold_&_Ruskey" rel="nofollow" target="_blank">Myrvold & Ruskey</a> or <a href="https://rosettacode.org/wiki/Permutations/Rank_of_a_permutation#Python:_Trotter-Johnson" rel="nofollow" target="_blank">Trottor & Johnson </a>algorithms I converted to Python are <b><i>not </i></b>in lexical order.<br /></p><p>So now I know I needed some way to rank and un-rank a lexically ordered set of permutations, as generated by Python.</p><h2 style="text-align: left;">Something to look at and talk about<br /></h2><p>Lets assign ranks to Python perms and column headings to make it easier to talk about:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">string</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">ascii_uppercase</span></div><br /><div><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># large enough to hopefully show patterns, but not too large</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">' # : '</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">' '</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">join</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">char</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">char</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">ascii_uppercase</span><span style="color: #cccccc;">[:</span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">]))</span></div><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">rank</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">enumerate</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">permutations</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">))):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">rank</span><span style="color: #569cd6;">:>2}</span><span style="color: #ce9178;"> : </span><span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">perm</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #cccccc;">)</span></div><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Outputs:</span></div><br /><div><span style="color: #ce9178;"> # : A B C D</span></div><div><span style="color: #ce9178;"> 0 : (0, 1, 2, 3)</span></div><div><span style="color: #ce9178;"> 1 : (0, 1, 3, 2)</span></div><div><span style="color: #ce9178;"> 2 : (0, 2, 1, 3)</span></div><div><span style="color: #ce9178;"> 3 : (0, 2, 3, 1)</span></div><div><span style="color: #ce9178;"> 4 : (0, 3, 1, 2)</span></div><div><span style="color: #ce9178;"> 5 : (0, 3, 2, 1)</span></div><div><span style="color: #ce9178;"> 6 : (1, 0, 2, 3)</span></div><div><span style="color: #ce9178;"> 7 : (1, 0, 3, 2)</span></div><div><span style="color: #ce9178;"> 8 : (1, 2, 0, 3)</span></div><div><span style="color: #ce9178;"> 9 : (1, 2, 3, 0)</span></div><div><span style="color: #ce9178;">10 : (1, 3, 0, 2)</span></div><div><span style="color: #ce9178;">11 : (1, 3, 2, 0)</span></div><div><span style="color: #ce9178;">12 : (2, 0, 1, 3)</span></div><div><span style="color: #ce9178;">13 : (2, 0, 3, 1)</span></div><div><span style="color: #ce9178;">14 : (2, 1, 0, 3)</span></div><div><span style="color: #ce9178;">15 : (2, 1, 3, 0)</span></div><div><span style="color: #ce9178;">16 : (2, 3, 0, 1)</span></div><div><span style="color: #ce9178;">17 : (2, 3, 1, 0)</span></div><div><span style="color: #ce9178;">18 : (3, 0, 1, 2)</span></div><div><span style="color: #ce9178;">19 : (3, 0, 2, 1)</span></div><div><span style="color: #ce9178;">20 : (3, 1, 0, 2)</span></div><div><span style="color: #ce9178;">21 : (3, 1, 2, 0)</span></div><div><span style="color: #ce9178;">22 : (3, 2, 0, 1)</span></div><div><span style="color: #ce9178;">23 : (3, 2, 1, 0)</span></div><div><span style="color: #ce9178;">"""</span></div></div><p></p><p><br /></p><h3 style="text-align: left;">Patterns<br /></h3><p>I can't go through the many false trails I had in coming to my result, (there were several), but I finally noticed that The digits in column A stayed the same for 6 successive rows and six is three factorial. Column B stays the same for 2 successive rows i.e. 2! , I guessed 1! for column C and D is what's left.</p><p>A lot of deleted code later I found that, kinda, after you pop the first digit out of the starting perm at rank 0, which would be <span style="background-color: #cccccc;">rank // (4 - 1)!</span>; you could take the second digit out using indices using decreasing factorials, <i>but also modulo the decreasing numbers left to index</i>.</p><p>Hard to write a textual description of, but I was very chuffed to work out the following on my own:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">nth_perm</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">Sequence</span><span style="color: #cccccc;">[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">], </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">tuple</span><span style="color: #cccccc;">[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">a</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fac_divs</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">tuple</span><span style="color: #cccccc;">((</span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">factorial</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">a</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">j</span><span style="color: #cccccc;">)) </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> (</span><span style="color: #9cdcfe;">a</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">j</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">j</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">a</span><span style="color: #cccccc;">))</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">tuple</span><span style="color: #cccccc;">([</span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">pop</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">indx</span><span style="color: #cccccc;">) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">indx</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fac_divs</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #dcdcaa;">nth_perm</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">), </span><span style="color: #b5cea8;">11</span><span style="color: #cccccc;">)</span></div><br /></div><p></p><p>And indeed the nth perm of (0, 1, 2 ,3) produced for n = 11 was (1, 3, 2, 0).</p><h3 style="text-align: left;">Check</h3><p>I decided to check my perm-from-rank generator against the Python permutations:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">start</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">)</span></div><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">i</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">enumerate</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">permutations</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">))):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">nth_perm</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">start</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">i</span><span style="color: #cccccc;">)</span></div><br /></div><p></p><p><br /></p><h2 style="text-align: left;">Generate ranking from arbitrary perm</h2><p>This had its own <i>aha </i>moments, and eventually I created the following code generating the rank of the sorted perm:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">s_perm_rank</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">p</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">Sequence</span><span style="color: #cccccc;">[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Ranking of perm p in the sorted sequence of perms of the ints 0 .. len(p)-1</span></div><div><span style="color: #ce9178;"> p must be a permutation of the integers 0 .. (len(p)-1)</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">this</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">p</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">a</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">this</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">a</span><span style="color: #cccccc;">)) </span><span style="color: #6a9955;"># Perm of rank 0</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">this</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;">), </span><span style="color: #ce9178;">"p must be perm of the ints 0 .. (len(p)-1)"</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">f</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">a</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">this</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">-=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+=</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">index</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">this</span><span style="color: #cccccc;">[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">]) </span><span style="color: #d4d4d4;">*</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">factorial</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">f</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">remove</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">this</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">pop</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">))</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">n</span></div><div><span style="color: #6a9955;"><br /></span></div></div><p></p><p><br /></p><h3 style="text-align: left;">Roundtrip Check<br /></h3><p>To check I roundtrip from rank to perm to rank, and also check against the Python permutations ranking</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">4</span></div><div><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">)</span></div><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">i</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">py_perm</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">enumerate</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">permutations</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">))):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">s_perm</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">nth_perm</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">init</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">i</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">i</span><span style="color: #569cd6;">:>2}</span><span style="color: #ce9178;">: </span><span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">py_perm</span><span style="color: #569cd6;">=}</span><span style="color: #ce9178;"> </span><span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">py_perm</span><span style="color: #d4d4d4;">==</span><span style="color: #9cdcfe;">s_perm</span><span style="color: #569cd6;">=}</span><span style="color: #ce9178;"> </span><span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">i</span><span style="color: #d4d4d4;">==</span><span style="color: #dcdcaa;">s_perm_rank</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s_perm</span><span style="color: #cccccc;">)</span><span style="color: #569cd6;">=}</span><span style="color: #ce9178;">"</span><span style="color: #cccccc;">)</span></div><div><span style="color: #ce9178;"><br /></span></div></div><p></p><p><br /></p><h4 style="text-align: left;">Output:</h4><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;"> 0: py_perm=(0, 1, 2, 3) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 1: py_perm=(0, 1, 3, 2) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 2: py_perm=(0, 2, 1, 3) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 3: py_perm=(0, 2, 3, 1) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 4: py_perm=(0, 3, 1, 2) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 5: py_perm=(0, 3, 2, 1) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 6: py_perm=(1, 0, 2, 3) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 7: py_perm=(1, 0, 3, 2) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 8: py_perm=(1, 2, 0, 3) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;"> 9: py_perm=(1, 2, 3, 0) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">10: py_perm=(1, 3, 0, 2) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">11: py_perm=(1, 3, 2, 0) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">12: py_perm=(2, 0, 1, 3) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">13: py_perm=(2, 0, 3, 1) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">14: py_perm=(2, 1, 0, 3) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">15: py_perm=(2, 1, 3, 0) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">16: py_perm=(2, 3, 0, 1) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">17: py_perm=(2, 3, 1, 0) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">18: py_perm=(3, 0, 1, 2) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">19: py_perm=(3, 0, 2, 1) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">20: py_perm=(3, 1, 0, 2) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">21: py_perm=(3, 1, 2, 0) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">22: py_perm=(3, 2, 0, 1) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><div><span style="color: #ce9178;">23: py_perm=(3, 2, 1, 0) py_perm==s_perm=True i==s_perm_rank(s_perm)=True</span></div><br /></div><p></p><p><b>It works!</b></p><h1 style="text-align: left;">Tidy</h1><p style="text-align: left;">I searched online, read more mathematical articles in and around the subject of permutations and decided to tidy things up with renamings and changes to function arguments. For example, I found that <i>lex </i>is used as a short replacement for lexicographic ordering, and that a perm is usually generated from the integer width of the perm and the rank required, rather than giving the perm of rank 0 and the rank</p><p style="text-align: left;">The final, (?), version is:</p><br /><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;">#%% Tidy</span></div><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">lex_perm_unrank</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">rank</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">tuple</span><span style="color: #cccccc;">[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Generate the lexicographic-permutation of ints 0 .. width-1 of given rank.</span></div><div><span style="color: #ce9178;"> </span></div><div><span style="color: #ce9178;"> Author: Donald S. McCarthy "Paddy3118"</span></div><div><span style="color: #ce9178;"> Date: August 2023</span></div><div><span style="color: #ce9178;"> """</span><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;">))</span></div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">indices</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> [(</span><span style="color: #9cdcfe;">rank</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">factorial</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">j</span><span style="color: #cccccc;">)) </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> (</span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">j</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">j</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">width</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)]</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">tuple</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">pop</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">index</span><span style="color: #cccccc;">) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">index</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">indices</span><span style="color: #cccccc;">)</span></div><br /><br /></div><div><span style="color: #6a9955;"># %% Check</span></div><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">w</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">7</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">py_rank</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">py_perm</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">enumerate</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">permutations</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">w</span><span style="color: #cccccc;">))):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">lex_perm</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">lex_perm_unrank</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">w</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">py_rank</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">lex_perm</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">py_perm</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">"lex_perm_unrank consistent with Python permutations order."</span><span style="color: #cccccc;">)</span></div><br /><br /><div><span style="color: #6a9955;"># %% Tidier</span></div><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">lex_perm_rank</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">p</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">Sequence</span><span style="color: #cccccc;">[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Rank of perm p in the lexicographic ordered perms of ints 0 .. len(p)-1</span></div><div><span style="color: #ce9178;"> p must be a permutation of the integers 0 .. (len(p)-1)</span></div><div><span style="color: #ce9178;"> </span></div><div><span style="color: #ce9178;"> Author: Donald S. McCarthy "Paddy3118"</span></div><div><span style="color: #ce9178;"> Date: August 2023</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">p</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">width</span><span style="color: #cccccc;">)) </span><span style="color: #6a9955;"># Perm of rank 0</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;">), \</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"p must be a permutation of the integers 0 .. (len(p)-1)"</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">rank</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">f</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">width</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">-=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">rank</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+=</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">index</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;">[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">]) </span><span style="color: #d4d4d4;">*</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">factorial</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">f</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">initial</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">remove</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">perm</span><span style="color: #cccccc;">.</span><span style="color: #dcdcaa;">pop</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">))</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">rank</span></div><br /><div><span style="color: #6a9955;"># %% Check both</span></div><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">w</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">7</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">py_rank</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">py_perm</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">enumerate</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">permutations</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">range</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">w</span><span style="color: #cccccc;">))):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">lex_perm</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">lex_perm_unrank</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">w</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">py_rank</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">lex_rank</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">lex_perm_rank</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">lex_perm</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">lex_perm</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">py_perm</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">and</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">lex_rank</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">py_rank</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">"lex_perm_rank/unrank consistent with Python permutations order."</span><span style="color: #cccccc;">)</span></div><br /></div><p style="text-align: left;"><b> </b></p><p style="text-align: left;"><b>END.</b></p><p style="text-align: left;"><br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-70416492131048259242023-08-21T21:34:00.000+01:002023-08-21T21:34:17.523+01:00The Godeh Series, Python, and OEIS<p></p><div style="text-align: left;"><p><span style="font-size: small;"><b style="color: #2b00fe;"><span>(Best viewed on screens larger than a portrait phone)</span></b></span></p> </div><p></p><p></p><div style="text-align: left;">In my <a href="https://paddy3118.blogspot.com/2023/08/generalising-simple-encryption-using.html" rel="nofollow" target="_blank">previous post</a> I generalised a form of encryption to do this:</div><p></p><div style="text-align: left;"> </div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">Task Encrypt grouping chars by mod M, N times:</span></div><div><span style="color: #ce9178;"> Given an input string S; Some order of the K integers 0..K-1 called</span></div><div><span style="color: #ce9178;"> M, where K <= length_of(S); and an integer N > 0</span></div><br /><div><span style="color: #ce9178;"> First form groups:</span></div><div><span style="color: #ce9178;"> Form group G[0] by concatenating every K'th item of S starting from index 0</span></div><div><span style="color: #ce9178;"> Form group G[1] by concatenating every K'th item of S starting from index 1</span></div><div><span style="color: #ce9178;"> ...</span></div><div><span style="color: #ce9178;"> Form group G[K-1] by concatenating every K'th item of S starting from index K-1</span></div><br /><div><span style="color: #ce9178;"> Group Concatenation:</span></div><div><span style="color: #ce9178;"> Concatenate all the groups G in the order given M to form the partial result P</span></div><div><span style="color: #ce9178;"> P(1) = concatenate_left_to_right(G[i] for i in M)</span></div><div><span style="color: #ce9178;"> Repetition:</span></div><div><span style="color: #ce9178;"> Substituting P for S, repeat the above process N times to form the final</span></div><div><span style="color: #ce9178;"> encrypted string P(N)</span></div><br /><div><span style="color: #ce9178;"> Corner cases:</span></div><div><span style="color: #ce9178;"> If S is empty or N is 0 then return S.</span></div><div><span style="color: #ce9178;"> If M is empty return the empty version of sequence S (empty string).</span></div><br /><div><span style="color: #ce9178;">Examples:</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 0) => '012345'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 1) => '135024'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 2) => '304152'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 3) => '012345'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 4) => '135024'</span></div><br /><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 0) => '01234567'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 1) => '25147036'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 2) => '10576243'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 3) => '52063174'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 4) => '01234567'</span></div><br /><div><span style="color: #ce9178;">Created from Sun Aug 6 09:10:10 2023</span></div><br /><div><span style="color: #ce9178;">@author: paddy3118</span></div><div><span style="color: #ce9178;"><br /></span></div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h2 style="text-align: left;">Repetitions</h2></div><div style="text-align: left;">In the example of we see that for n = 3, the output of <span style="color: #660000;"> encrypt6('012345', [1, 0], 3) </span>starts repeating for higher n.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">I have found, but have not proved, that </div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #cccccc;">encrypt6(S, M, n) </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> encrypt6(S, M, (n modulo x) )</span></div></div>for some x.<br /></div><div style="text-align: left;">I.e. outputs will eventually equal the input to form a repettitive cycle of outputs.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">The length of the cycle, <b>x </b>before repetition does not seem to be simple.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h1 style="text-align: left;">The Godeh series</h1><div style="text-align: left;">This series calculates the length of the cycles of encrypt6 untill the input is produced again.</div><div style="text-align: left;"> </div><div style="text-align: left;"> <div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">Godeh series - Original definition by Author</span></div><div><span style="color: #ce9178;"> 1. Given:</span></div><div><span style="color: #ce9178;"> * An ordered list of S distinct items, S >= 1.</span></div><div><span style="color: #ce9178;"> * Some permutation of the K integers 0..K-1 called M, where 1 <= K <= S.</span></div><br /><div><span style="color: #ce9178;"> 2. Form groups:</span></div><div><span style="color: #ce9178;"> * Form group G[0] by concatenating every K'th item of S starting from index 0</span></div><div><span style="color: #ce9178;"> * Form group G[1] by concatenating every K'th item of S starting from index 1</span></div><div><span style="color: #ce9178;"> ...</span></div><div><span style="color: #ce9178;"> * Form group G[K-1] by concatenating every K'th item of S starting from index K-1</span></div><br /><div><span style="color: #ce9178;"> 3. Concatenate groups:</span></div><div><span style="color: #ce9178;"> * Concatenate all the groups G in the order given by M to form the first partial</span></div><div><span style="color: #ce9178;"> result P(1)</span></div><div><span style="color: #ce9178;"> P(1) = concatenate_left_to_right(G[i] for i in M)</span></div><br /><div><span style="color: #ce9178;"> 4. Repetition:</span></div><div><span style="color: #ce9178;"> Substituting P for S, repeat the above process until the order of items in P</span></div><div><span style="color: #ce9178;"> equals the initial order, S.</span></div><br /><div><span style="color: #ce9178;"> 5. Result</span></div><div><span style="color: #ce9178;"> * Godeh(S, M) = the number of repetitions needed of steps 1-through-4</span></div><br /><div><span style="color: #ce9178;">Created on Thu Aug 17 19:40:32 2023</span></div><br /><div><span style="color: #ce9178;">@author: paddy3118</span></div><div><span style="color: #ce9178;"><br /></span></div></div></div><div style="text-align: left;"> </div><div style="text-align: left;"><h2 style="text-align: left;">Python code to generate one term</h2></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> collections.abc </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> Sequence</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> collections </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> defaultdict</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> itertools </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> zip_longest, permutations, chain, islice</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> functools </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> cache, lru_cache</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> pprint </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> pp</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> string </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> ascii_letters, digits</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> time </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> sleep</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> typing </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> Callable, Any, overload</span></div><div><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> sys</span></div><div><span style="color: #cccccc;">sys.path.append(</span><span style="color: #569cd6;">r</span><span style="color: #d16969;">'C:</span><span style="color: #d7ba7d;">\U</span><span style="color: #d16969;">sers</span><span style="color: #d7ba7d;">\p</span><span style="color: #d16969;">addy</span><span style="color: #d7ba7d;">\G</span><span style="color: #d16969;">oogle Drive</span><span style="color: #d7ba7d;">\C</span><span style="color: #d16969;">ode</span><span style="color: #d7ba7d;">\o</span><span style="color: #d16969;">eisify'</span><span style="color: #cccccc;">)</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> oeis_request </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> oeis_request</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> antidiagonals </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> antidiag_triangle_indices</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">godeh</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">m</span><span style="color: #cccccc;">: Sequence[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"Godeh function Godeh(s, m)"</span></div><div><span style="color: #cccccc;"> k </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(m)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">>=</span><span style="color: #cccccc;"> k</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(k)) </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(m), \</span></div><div><span style="color: #cccccc;"> </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"Sequence m of length </span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">k</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;"> should contain a permutation of all the "</span><span style="color: #cccccc;"> \</span></div><div><span style="color: #cccccc;"> </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"numbers 0..</span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">k</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;"> inclusive."</span></div><br /><div><span style="color: #cccccc;"> s_init </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(s))</span></div><div><span style="color: #cccccc;"> n, s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">, </span><span style="color: #569cd6;">None</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">!=</span><span style="color: #cccccc;"> s_init:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s_init</span></div><div><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;">+=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">sum</span><span style="color: #cccccc;">((s[offset::k] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> offset </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> m),</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">start</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">[])</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> n</span></div><br /></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h2 style="text-align: left;">Table of values</h2></div><div style="text-align: left;"> Each row should represent M as "successive permutations" - i.e perms of (0,) then perms of (0,1), perms of (0,1,2) ...</div><div style="text-align: left;">Successive values in each row are then godeh(S,M) where S = len(M), len(M)+1, len(M+2), ...</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Code:</b></div><div style="text-align: left;"><b> </b></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #cccccc;">GodehTableType </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> dict[tuple[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">], list[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]]</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">godeh_table</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">max_s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">15</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">max_permed_ints</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">) -> GodehTableType:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Create a table of Godeh series values</span></div><br /><div><span style="color: #ce9178;"> Parameters</span></div><div><span style="color: #ce9178;"> ----------</span></div><div><span style="color: #ce9178;"> max_s : int, optional</span></div><div><span style="color: #ce9178;"> Godeh(s, m) for s=len(m) to len(m) + max_s, for all m. The default is 10.</span></div><div><span style="color: #ce9178;"> max_permed_ints : int, optional</span></div><div><span style="color: #ce9178;"> All perms of (0), (0, 1), (0, 1, 2), ... (0, 1, ..max_permed_ints).</span></div><div><span style="color: #ce9178;"> The default is 3.</span></div><br /><div><span style="color: #ce9178;"> Returns</span></div><div><span style="color: #ce9178;"> -------</span></div><div><span style="color: #ce9178;"> dict[</span></div><div><span style="color: #ce9178;"> tuple[int], # M</span></div><div><span style="color: #ce9178;"> list[int]] # Godeh(S, M) for S = len(M), len(M)+1, ...</span></div><div><span style="color: #ce9178;"> Tablulated Godeh values.</span></div><br /><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> table </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> defaultdict(</span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> m </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> chain(</span><span style="color: #d4d4d4;">*</span><span style="color: #cccccc;">(permutations(</span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(i)) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> i </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, max_permed_ints</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">))):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">((k:=</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(m)), max_s </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> k):</span></div><div><span style="color: #cccccc;"> table[(m)].append(godeh(s, m))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">dict</span><span style="color: #cccccc;">(table)</span></div><br /><div><span style="color: #cccccc;">godeh_series </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> godeh_table(</span><span style="color: #b5cea8;">20</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">)</span></div><div><span style="color: #6a9955;"># %%</span></div><div><span style="color: #cccccc;">pp(godeh_series, </span><span style="color: #9cdcfe;">width</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">222</span><span style="color: #cccccc;">)</span></div></div></div><div style="text-align: left;"> </div><div style="text-align: left;"><b> Output:</b></div><div style="text-align: left;"> </div><div style="text-align: left;"> <div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">{(0,): [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],</span></div><div><span style="color: #ce9178;"> (0, 1): [1, 2, 2, 4, 4, 3, 3, 6, 6, 10, 10, 12, 12, 4, 4, 8, 8, 18, 18, 6],</span></div><div><span style="color: #ce9178;"> (1, 0): [2, 2, 4, 4, 3, 3, 6, 6, 10, 10, 12, 12, 4, 4, 8, 8, 18, 18, 6, 6],</span></div><div><span style="color: #ce9178;"> (0, 1, 2): [1, 3, 4, 4, 6, 2, 2, 6, 5, 5, 11, 6, 6, 15, 16, 16, 52, 4, 4, 38],</span></div><div><span style="color: #ce9178;"> (0, 2, 1): [2, 2, 2, 4, 6, 6, 4, 4, 4, 21, 3, 3, 30, 4, 4, 90, 18, 18, 24, 5],</span></div><div><span style="color: #ce9178;"> (1, 0, 2): [2, 2, 4, 4, 6, 4, 4, 4, 21, 21, 3, 30, 30, 8, 90, 90, 18, 24, 24, 10],</span></div><div><span style="color: #ce9178;"> (1, 2, 0): [3, 3, 4, 6, 6, 4, 6, 6, 5, 11, 11, 6, 15, 15, 16, 52, 52, 4, 38, 38],</span></div><div><span style="color: #ce9178;"> (2, 0, 1): [3, 4, 4, 6, 2, 2, 6, 5, 5, 11, 6, 6, 15, 16, 16, 52, 4, 4, 38, 11],</span></div><div><span style="color: #ce9178;"> (2, 1, 0): [2, 2, 4, 6, 6, 4, 4, 4, 21, 3, 3, 30, 4, 4, 90, 18, 18, 24, 5, 5]}</span></div><div><span style="color: #ce9178;"><br /></span></div></div></div><div style="text-align: left;"> </div><div style="text-align: left;"> </div><div style="text-align: left;"><h2 style="text-align: left;">Duplicate rows? </h2></div><div style="text-align: left;">I suspect that there are duplicate rows of values so generate a larger table and search for row duplication, (I use text searches but it gets the job done).</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Code:</b><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">find_reps_in_godeh</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">g</span><span style="color: #cccccc;">: GodehTableType,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">_slice_start</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">10</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">_slice_end</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">18</span><span style="color: #cccccc;">) -> </span><span style="color: #569cd6;">None</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Prints information on duplicated Godeh table rows.</span></div><br /><div><span style="color: #ce9178;"> Parameters</span></div><div><span style="color: #ce9178;"> ----------</span></div><div><span style="color: #ce9178;"> g : GodehTableType</span></div><div><span style="color: #ce9178;"> Table of Godeh(S,M) values.</span></div><div><span style="color: #ce9178;"> _slice_start : int, optional</span></div><div><span style="color: #ce9178;"> Defines slice of a rows values to search for in other rows of g.</span></div><div><span style="color: #ce9178;"> The default is 10.</span></div><div><span style="color: #ce9178;"> _slice_end : int, optional</span></div><div><span style="color: #ce9178;"> Defines slice of a rows values to search for in other rows of g.</span></div><div><span style="color: #ce9178;"> The default is 18.</span></div><br /><div><span style="color: #ce9178;"> Returns</span></div><div><span style="color: #ce9178;"> -------</span></div><div><span style="color: #ce9178;"> None</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Uses a string search on stringified row values between commas</span></div><div><span style="color: #cccccc;"> searchable </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> {m: </span><span style="color: #ce9178;">','</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">(values)[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">:</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">].replace(</span><span style="color: #ce9178;">' '</span><span style="color: #cccccc;">,</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">','</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> m, values </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> g.items()}</span></div><div><span style="color: #cccccc;"> keys, rows </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(searchable), </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(searchable.values())</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Search for every row in all "higher" rows</span></div><div><span style="color: #cccccc;"> maxrows </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(rows)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> i </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(maxrows </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> isearch </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">','</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">(g[keys[i]][</span><span style="color: #b5cea8;">10</span><span style="color: #cccccc;">:</span><span style="color: #b5cea8;">17</span><span style="color: #cccccc;">])[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">:</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">].replace(</span><span style="color: #ce9178;">' '</span><span style="color: #cccccc;">,</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">','</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> k </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(i</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, maxrows):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> isearch </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> (rowk:=rows[k]):</span></div><div><span style="color: #cccccc;"> offset </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> _slice_start </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> rowk[:rowk.index(isearch)].count(</span><span style="color: #ce9178;">','</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"Found </span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">keys[i]</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;"> in </span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">keys[k]</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;"> at </span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">offset = </span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #cccccc;">)</span></div><br /><br /><div><span style="color: #cccccc;">godeh_series </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> godeh_table(</span><span style="color: #b5cea8;">40</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">5</span><span style="color: #cccccc;">) </span><span style="color: #6a9955;"># more values</span></div><div><span style="color: #cccccc;">find_reps_in_godeh(godeh_series, </span><span style="color: #b5cea8;">10</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">20</span><span style="color: #cccccc;">)</span></div><br /></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Output:</b></div><div style="text-align: left;"><b> </b></div><div style="text-align: left;"><b>Found (0, 1) in (1, 0) at offset = 1<br />Found (0, 1, 2) in (2, 0, 1) at offset = 1<br />Found (0, 2, 1) in (2, 1, 0) at offset = 1<br />Found (0, 1, 2, 3) in (3, 0, 1, 2) at offset = 1<br />Found (0, 1, 3, 2) in (3, 0, 2, 1) at offset = 1<br />Found (0, 2, 1, 3) in (3, 1, 0, 2) at offset = 1<br />Found (0, 2, 3, 1) in (3, 1, 2, 0) at offset = 1<br />Found (0, 3, 1, 2) in (3, 2, 0, 1) at offset = 1<br />Found (0, 3, 2, 1) in (3, 2, 1, 0) at offset = 1<br />Found (0, 1, 2, 3, 4) in (4, 0, 1, 2, 3) at offset = 1<br />Found (0, 1, 2, 4, 3) in (4, 0, 1, 3, 2) at offset = 1<br />Found (0, 1, 3, 2, 4) in (4, 0, 2, 1, 3) at offset = 1<br />Found (0, 1, 3, 4, 2) in (4, 0, 2, 3, 1) at offset = 1<br />Found (0, 1, 4, 2, 3) in (4, 0, 3, 1, 2) at offset = 1<br />Found (0, 1, 4, 3, 2) in (4, 0, 3, 2, 1) at offset = 1<br />Found (0, 2, 1, 3, 4) in (4, 1, 0, 2, 3) at offset = 1<br />Found (0, 2, 1, 4, 3) in (4, 1, 0, 3, 2) at offset = 1<br />Found (0, 2, 3, 1, 4) in (4, 1, 2, 0, 3) at offset = 1<br />Found (0, 2, 3, 4, 1) in (4, 1, 2, 3, 0) at offset = 1<br />Found (0, 2, 4, 1, 3) in (4, 1, 3, 0, 2) at offset = 1<br />Found (0, 2, 4, 3, 1) in (4, 1, 3, 2, 0) at offset = 1<br />Found (0, 3, 1, 2, 4) in (4, 2, 0, 1, 3) at offset = 1<br />Found (0, 3, 1, 4, 2) in (4, 2, 0, 3, 1) at offset = 1<br />Found (0, 3, 2, 1, 4) in (4, 2, 1, 0, 3) at offset = 1<br />Found (0, 3, 2, 4, 1) in (4, 2, 1, 3, 0) at offset = 1<br />Found (0, 3, 4, 1, 2) in (4, 2, 3, 0, 1) at offset = 1<br />Found (0, 3, 4, 2, 1) in (4, 2, 3, 1, 0) at offset = 1<br />Found (0, 4, 1, 2, 3) in (4, 3, 0, 1, 2) at offset = 1<br />Found (0, 4, 1, 3, 2) in (4, 3, 0, 2, 1) at offset = 1<br />Found (0, 4, 2, 1, 3) in (4, 3, 1, 0, 2) at offset = 1<br />Found (0, 4, 2, 3, 1) in (4, 3, 1, 2, 0) at offset = 1<br />Found (0, 4, 3, 1, 2) in (4, 3, 2, 0, 1) at offset = 1<br />Found (0, 4, 3, 2, 1) in (4, 3, 2, 1, 0) at offset = 1<br /><br /></b></div><div style="text-align: left;"><h3><b> Observation, Duals!<br /></b></h3></div><div style="text-align: left;"> The first line is saying that M = (1, 0) values are the same as those for M = (0, 1); just with an offset of being one term over.<br /></div><div style="text-align: left;"> </div><div style="text-align: left;"><ul style="text-align: left;"><li>There are only row duals found. no rows data is replicated in more than one other row.</li><li> All the offsets are one.</li><li>Duals are between rows of the same size perms in M; with the Leftmost value of M being max(M) paired with an M where the leftmost value of M is min(M).<br /></li></ul></div><div style="text-align: left;"><h2 style="text-align: left;">OEIS Searching by hand: Abulsme<br /></h2></div><div style="text-align: left;">I put a few selections of the table rows into OEIS by hand and found that, (but not proved), I can generate all the <a href="https://oeis.org/A105272" rel="nofollow" target="_blank">Abulsme function</a> values from my Godeh function!<br /></div><div style="text-align: left;"> </div><br /><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">a105272_table</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">max_s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">10</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">max_permed_ints</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">40</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Abulsme function by different means: http://www.abulsme.com/function.html</span></div><div><span style="color: #ce9178;"> == Godeh(S, M) where All M's are ints sorted high to low</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> table </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> defaultdict(</span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> m </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> (</span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(i)[::</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> i </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, max_permed_ints</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(k:</span><span style="color: #d4d4d4;">=</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(m), max_s </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> k):</span></div><div><span style="color: #cccccc;"> table[k].append(godeh(s, m))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">dict</span><span style="color: #cccccc;">(table)</span></div><br /><div><span style="color: #cccccc;">a105272 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> a105272_table(</span><span style="color: #b5cea8;">20</span><span style="color: #cccccc;">, </span>15<span style="color: #cccccc;">)</span></div><div><span style="color: #6a9955;"># %%</span></div><div><span style="color: #cccccc;">pp(a105272, </span><span style="color: #9cdcfe;">width</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">222</span><span style="color: #cccccc;">)</span></div><div><span style="color: #6a9955;"><br /></span></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h3 style="text-align: left;">Table of Abulsme values produced</h3></div><div style="text-align: left;"> <div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">{1: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],</span></div><div><span style="color: #ce9178;"> 2: [2, 2, 4, 4, 3, 3, 6, 6, 10, 10, 12, 12, 4, 4, 8, 8, 18, 18, 6, 6],</span></div><div><span style="color: #ce9178;"> 3: [2, 2, 4, 6, 6, 4, 4, 4, 21, 3, 3, 30, 4, 4, 90, 18, 18, 24, 5, 5],</span></div><div><span style="color: #ce9178;"> 4: [2, 2, 4, 7, 3, 3, 8, 10, 6, 6, 20, 4, 4, 4, 132, 30, 3, 3, 24, 252],</span></div><div><span style="color: #ce9178;"> 5: [2, 2, 4, 7, 15, 5, 5, 12, 40, 45, 4, 4, 12, 8, 16, 6, 6, 16, 28, 4],</span></div><div><span style="color: #ce9178;"> 6: [2, 2, 4, 14, 6, 10, 12, 12, 7, 15, 56, 90, 9, 9, 51, 21, 105, 23, 5, 5],</span></div><div><span style="color: #ce9178;"> 7: [2, 2, 4, 14, 6, 12, 30, 4, 4, 20, 15, 70, 30, 18, 10, 10, 48, 72, 24, 25],</span></div><div><span style="color: #ce9178;"> 8: [2, 2, 4, 14, 6, 13, 13, 24, 8, 8, 40, 70, 91, 18, 18, 22, 20, 20, 44, 312],</span></div><div><span style="color: #ce9178;"> 9: [2, 2, 4, 14, 6, 13, 15, 15, 63, 9, 9, 20, 18, 85, 30, 24, 60, 44, 3, 3],</span></div><div><span style="color: #ce9178;"> 10: [2, 2, 4, 14, 6, 26, 16, 10, 18, 12, 6, 6, 42, 21, 22, 42, 68, 165, 182, 30],</span></div><div><span style="color: #ce9178;"> 11: [2, 2, 4, 14, 6, 26, 16, 60, 78, 48, 20, 22, 22, 12, 25, 390, 90, 21, 360, 42],</span></div><div><span style="color: #ce9178;"> 12: [2, 2, 4, 14, 6, 26, 16, 65, 120, 84, 21, 36, 20, 20, 44, 132, 120, 24, 100, 108],</span></div><div><span style="color: #ce9178;"> 13: [2, 2, 4, 14, 6, 26, 16, 65, 168, 168, 120, 20, 25, 9, 9, 60, 24, 187, 616, 84],</span></div><div><span style="color: #ce9178;"> 14: [2, 2, 4, 14, 6, 26, 16, 130, 24, 60, 130, 144, 20, 26, 28, 28, 15, 60, 84, 84],</span></div><div><span style="color: #ce9178;"> 15: [2, 2, 4, 14, 6, 26, 16, 130, 24, 204, 24, 144, 170, 170, 40, 10, 10, 30, 510, 34]}</span></div><div><span style="color: #ce9178;"><br /></span></div></div></div><div style="text-align: left;"> </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h2 style="text-align: left;">Auto-check OEIS</h2></div><div style="text-align: left;">I hooked in my oeis search routine and searched for each row of the Godeh table of values.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Code:</b></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">oeis_check_values</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">table</span><span style="color: #cccccc;">: dict[Any, list[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">_slice_start</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">_slice_end</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">12</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">_secs_between_searches</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">5</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">_show_not_found</span><span style="color: #d4d4d4;">=</span><span style="color: #569cd6;">False</span></div><div><span style="color: #cccccc;"> ) -> </span><span style="color: #569cd6;">None</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"Search for each row of table in OEIS"</span></div><div><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> key, vals </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> table.items():</span></div><div><span style="color: #cccccc;"> oeis </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> oeis_request(</span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">(vals[_slice_start:_slice_end])[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">:</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">])</span></div><div><span style="color: #cccccc;"> found </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> [k </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> k </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> oeis </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> k[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'A'</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> found:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">key</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">: is found in OEIS </span><span style="color: #569cd6;">{</span><span style="color: #ce9178;">', '</span><span style="color: #cccccc;">.join(found)</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">elif</span><span style="color: #cccccc;"> _show_not_found:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">key</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">: Not found in oeis."</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> sleep(_secs_between_searches)</span></div><br /><div><span style="color: #cccccc;">godeh_series </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> godeh_table(</span><span style="color: #b5cea8;">40</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">5</span><span style="color: #cccccc;">) </span><span style="color: #6a9955;"># more values</span></div><div><span style="color: #cccccc;">oeis_check_values(godeh_series, </span><span style="color: #9cdcfe;">_secs_between_searches</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">)</span></div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">It takes some time to run, but eventually I get these results:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Output:</b></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">(0,): is found in OEIS A000012, A055642, A135010, A057427, A138121, A000030, A047999, A071625, A079944, A228351</span></div><div><span style="color: #ce9178;">(0, 1): is found in OEIS A024222</span></div><div><span style="color: #ce9178;">(1, 0): is found in OEIS A024222</span></div><div><span style="color: #ce9178;">(0, 2, 1): is found in OEIS A118960</span></div><div><span style="color: #ce9178;">(2, 1, 0): is found in OEIS A118960</span></div><div><span style="color: #ce9178;">(0, 3, 2, 1): is found in OEIS A120280</span></div><div><span style="color: #ce9178;">(3, 2, 1, 0): is found in OEIS A120280</span></div><div><span style="color: #ce9178;">(0, 4, 3, 2, 1): is found in OEIS A120363</span></div><div><span style="color: #ce9178;">(4, 3, 2, 1, 0): is found in OEIS A120363</span></div><div><span style="color: #ce9178;">(0, 5, 4, 3, 2, 1): is found in OEIS A120654</span></div><div><span style="color: #ce9178;">(5, 4, 3, 2, 1, 0): is found in OEIS A120654</span></div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Ignoring (0,) which is just all the ones; All the other matches are selections from the Abulsme function, <br /></div><div style="text-align: left;">a105272, when M is either reverse sorted, or its <b>dual.</b></div><div style="text-align: left;"><b> </b></div><div style="text-align: left;"><h1 style="text-align: left;"><b>OEIS ENTRY?</b></h1></div><div style="text-align: left;">It seems that the <b>Godeh Series</b> is more general than a105272 so I thought I'd submit it to OEIS. It's submitted and allocated as <a href="https://oeis.org/A365096" rel="nofollow" target="_blank">A365096</a>. Fingers crossed!</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h2 style="text-align: left;">Antidiagonalisation</h2></div><div style="text-align: left;">When you submit a table of values the DATA entry must be in antidiagonalisation format. I did a quick module to provide that functionality:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h3 style="text-align: left;">Module antidiagonals.py:</h3></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Anti-diagonals:</span></div><br /><div><span style="color: #ce9178;">0,0 0,1 0,2 0,3</span></div><div><span style="color: #ce9178;">1,0 1,1 1,2 1,3</span></div><div><span style="color: #ce9178;">2,0 2,1 2,2 2,3</span></div><div><span style="color: #ce9178;">3,0 3,1 3,2 3,3</span></div><br /><div><span style="color: #ce9178;">Of Square:</span></div><div><span style="color: #ce9178;">0,0 0,1 1,0 0,2 1,1 2,0 0,3 1,2 2,1 3,0 1,3 2,2 3,1 2,3 3,2 3,3</span></div><br /><div><span style="color: #ce9178;">of Infinite table:</span></div><div><span style="color: #ce9178;">0,0 0,1 1,0 0,2 1,1 2,0 0,3 1,2 2,1 3,0 0,4 1,3 2,2 3,1 4,0 ...</span></div><br /><br /><div><span style="color: #ce9178;">Created on Mon Aug 21 13:36:31 2023</span></div><br /><div><span style="color: #ce9178;">@author: paddy</span></div><br /><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #6a9955;"># %% Triangles</span></div><br /><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> itertools </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> islice</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">antidiag_triangle_indices</span><span style="color: #cccccc;">() -> tuple[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">, </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> x </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> y </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">True</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">yield</span><span style="color: #cccccc;"> (x, y)</span></div><div><span style="color: #cccccc;"> x, y </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> (x</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, y</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">) </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> y </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;"> (</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">, x</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(islice(antidiag_triangle_indices(), </span><span style="color: #b5cea8;">15</span><span style="color: #cccccc;">))</span></div><br /><br /><div><span style="color: #6a9955;"># %% Rectangles</span></div><br /><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> itertools </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> islice</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">antidiag_rectangle_indices</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">sizex</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">sizey</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">) -> tuple[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">, </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> x </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> y </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">True</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">yield</span><span style="color: #cccccc;"> (x, y)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> (x, y) </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> (sizex </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, sizey </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">break</span></div><div><span style="color: #cccccc;"> x, y </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> (x</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, y</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> x </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> sizex </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> y </span><span style="color: #d4d4d4;"><</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> u </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> x </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> y </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #cccccc;"> x, y </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> (</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">, u) </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> u </span><span style="color: #d4d4d4;"><</span><span style="color: #cccccc;"> sizey </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;"> (u </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> sizey </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, sizey </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)</span></div><br /><br /><div><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(antidiag_rectangle_indices(</span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">))</span></div><br /></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"> </div><div style="text-align: left;"><h4 style="text-align: left;">Antidiagonalisation test </h4></div><div style="text-align: left;"> <div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">antidiagonalise</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">table</span><span style="color: #cccccc;">: dict[Any, list[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]]) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> maxx </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(xkeys:</span><span style="color: #d4d4d4;">=</span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(table))</span></div><div><span style="color: #cccccc;"> maxy </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">min</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(row) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> row </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> table.values())</span></div><div><span style="color: #cccccc;"> antigen </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> antidiag_triangle_indices().</span><span style="color: #dcdcaa;">__next__</span></div><div><span style="color: #cccccc;"> x, y </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> antigen()</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> x </span><span style="color: #d4d4d4;"><</span><span style="color: #cccccc;"> maxx </span><span style="color: #569cd6;">and</span><span style="color: #cccccc;"> y </span><span style="color: #d4d4d4;"><</span><span style="color: #cccccc;"> maxy:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">yield</span><span style="color: #cccccc;"> table[xkeys[x]][y]</span></div><div><span style="color: #cccccc;"> x, y </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> antigen()</span></div><br /><div><span style="color: #cccccc;">t </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> {</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">x = </span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #cccccc;">: [(x, y) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> y </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">7</span><span style="color: #cccccc;">)] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">)}</span></div><br /><div><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">'# Sample table'</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;">pp(t, </span><span style="color: #9cdcfe;">width</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">100</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">sort_dicts</span><span style="color: #d4d4d4;">=</span><span style="color: #569cd6;">False</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">Antidiagonalised</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span><span style="color: #cccccc;">, </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(antidiagonalise(t)))</span></div><div><span style="color: #cccccc;"><br /></span></div></div></div><div style="text-align: left;"> </div><div style="text-align: left;"><b> Test output:</b></div><div style="text-align: left;"> </div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;"># Sample table</span></div><div><span style="color: #ce9178;">{'x = 0': [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)],</span></div><div><span style="color: #ce9178;"> 'x = 1': [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6)],</span></div><div><span style="color: #ce9178;"> 'x = 2': [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6)]}</span></div><br /><div><span style="color: #ce9178;">Antidiagonalised</span></div><div><span style="color: #ce9178;"> [(0, 0), (0, 1), (1, 0), (0, 2), (1, 1), (2, 0), (0, 3), (1, 2), (2, 1)]</span></div><div><span style="color: #ce9178;"><br /></span></div></div> </div><div style="text-align: left;"> </div><div style="text-align: left;"><h2 style="text-align: left;"> Better formatted table for comments</h2></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #cccccc;">godeh_series2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> godeh_table(</span><span style="color: #b5cea8;">20</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> M, v </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> godeh_series2.items():</span></div><div><span style="color: #cccccc;"> m </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">M = </span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">m</span><span style="color: #569cd6;">:>13s}</span><span style="color: #ce9178;">: </span><span style="color: #569cd6;">{</span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">(v)[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">:</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">].replace(</span><span style="color: #ce9178;">' '</span><span style="color: #cccccc;">, </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">)</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">,..."</span><span style="color: #cccccc;">)</span></div></div></div><div style="text-align: left;"> </div><div style="text-align: left;"> <b>Output</b>:</div><div style="text-align: left;"> </div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;"> M = (0,): 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,...</span></div><div><span style="color: #ce9178;"> M = (0, 1): 1,2,2,4,4,3,3,6,6,10,10,12,12,4,4,8,8,18,18,6,...</span></div><div><span style="color: #ce9178;"> M = (1, 0): 2,2,4,4,3,3,6,6,10,10,12,12,4,4,8,8,18,18,6,6,...</span></div><div><span style="color: #ce9178;">M = (0, 1, 2): 1,3,4,4,6,2,2,6,5,5,11,6,6,15,16,16,52,4,4,38,...</span></div><div><span style="color: #ce9178;">M = (0, 2, 1): 2,2,2,4,6,6,4,4,4,21,3,3,30,4,4,90,18,18,24,5,...</span></div><div><span style="color: #ce9178;">M = (1, 0, 2): 2,2,4,4,6,4,4,4,21,21,3,30,30,8,90,90,18,24,24,10,...</span></div><div><span style="color: #ce9178;">M = (1, 2, 0): 3,3,4,6,6,4,6,6,5,11,11,6,15,15,16,52,52,4,38,38,...</span></div><div><span style="color: #ce9178;">M = (2, 0, 1): 3,4,4,6,2,2,6,5,5,11,6,6,15,16,16,52,4,4,38,11,...</span></div><div><span style="color: #ce9178;">M = (2, 1, 0): 2,2,4,6,6,4,4,4,21,3,3,30,4,4,90,18,18,24,5,5,...</span></div></div> </div><div style="text-align: left;"> </div><div style="text-align: left;"> </div><div style="text-align: left;"><b> END.</b></div><div style="text-align: left;"> </div><div style="text-align: left;"><b> </b><br /></div><div style="text-align: left;"><br /></div></div>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-53568430887861026812023-08-20T11:32:00.002+01:002023-08-20T11:54:05.969+01:00Generalising a simple encryption using Python<p></p><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMjvJ5cTfUiXYfO2hqUJqdG0q2nkPiz6bpz6kJJgHo1zKZZcIYpUoJ74mMULZeOQ4iKTNGX8wpRvLtBJaPxtVvu80YliwLlmzqsoHMuze0sPYEjx2BUrmVuY1MzrxedDQ2e_mZQpoR5jg7IhjUwLFmkoUm6zUZVjuvYwnWB4UzUETcQal1S3eK/s3648/IMG_20230820_110353_edit_707480951713920.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1721" data-original-width="3648" height="151" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMjvJ5cTfUiXYfO2hqUJqdG0q2nkPiz6bpz6kJJgHo1zKZZcIYpUoJ74mMULZeOQ4iKTNGX8wpRvLtBJaPxtVvu80YliwLlmzqsoHMuze0sPYEjx2BUrmVuY1MzrxedDQ2e_mZQpoR5jg7IhjUwLFmkoUm6zUZVjuvYwnWB4UzUETcQal1S3eK/s320/IMG_20230820_110353_edit_707480951713920.jpg" width="320" /></a></div><br /> <p></p><p><b style="color: #2b00fe;"><span style="font-size: x-small;">(Best viewed on screens larger than a portrait phone)</span></b></p><h2 style="text-align: left;"> Recap<br /></h2><p> In my last but one <a href="https://paddy3118.blogspot.com/2023/08/exploring-simple-encryption-using-python.html" rel="nofollow" target="_blank">post </a>I coded solutions to the task:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">Implement a pseudo-encryption algorithm which given a string S and an integer N </span></div><div><span style="color: #ce9178;">concatenates all the odd-indexed characters of S with all the even-indexed </span></div><div><span style="color: #ce9178;">characters of S, this process should be repeated N times.</span></div></div><p></p><h3 style="text-align: left;">First implementation</h3><p>My first implementation didn't use index variables to walk through strings, but instead used slicing and zip_longest:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> itertools </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> zip_longest</span></div><div><span style="color: #569cd6;"><br /></span></div></div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(x)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #c586c0;">in</span></div><div><span style="color: #cccccc;"> zip_longest(s[l2:], s[:l2],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fillvalue</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div></div><p></p><h3 style="text-align: left;">N modulo <br /></h3><p>In that same post I found that encryption repeats after some value found to be associated with<br /><b><a href="https://oeis.org/A002326">https://oeis.org/A002326</a></b> So if n is ls large, work will be reduced.<br /></p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> functools </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> cache</span></div><br /><div><span style="color: #dcdcaa;">@cache</span></div><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">a002326</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"Least m > 0 such that 2n+1 divides 2^m-1"</span></div><div><span style="color: #cccccc;"> n21 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">*</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #cccccc;"> m, m2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> (m2 </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> n21:</span></div><div><span style="color: #cccccc;"> m, m2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> m</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, m2 </span><span style="color: #d4d4d4;">*</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> m</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt2</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> a002326(l2)): </span><span style="color: #6a9955;"># Modulo reduction on large n</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt2</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> a002326(l2)): </span><span style="color: #6a9955;"># Modulo reduction on large n</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(x)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #c586c0;">in</span></div><div><span style="color: #cccccc;"> zip_longest(s[l2:], s[:l2],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fillvalue</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /></div><p></p><p><br /></p><h1 style="text-align: left;">New Stuff</h1><p>this marks the end of the previous posts precis. <br /></p><h3 style="text-align: left;">Task generalisation #1: Reverse<br /></h3><p>Reverse the concatenation of the original.</p><p>The new task descripption:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">New Task:</span></div><div><span style="color: #ce9178;"> given a string S and an integer N concatenates all the EVEN-indexed</span></div><div><span style="color: #ce9178;"> characters of S with all the ODD-indexed characters of S, this process</span></div><div><span style="color: #ce9178;"> should be repeated N time. (Prevous was ODD-EVEN).</span></div><br /><div><span style="color: #ce9178;">Examples:</span></div><div><span style="color: #ce9178;"> encrypt('012345', 1) => '024135'</span></div><div><span style="color: #ce9178;"> encrypt('012345', 2) => '043215'</span></div><div><span style="color: #ce9178;"> encrypt('012345', 3) => '031425'</span></div><br /><div><span style="color: #ce9178;"> encrypt('0123456', 1) => '0246135'</span></div><div><span style="color: #ce9178;"> encrypt('0123456', 2) => '0415263'</span></div><div><span style="color: #ce9178;"> encrypt('0123456', 3) => '0123456'</span></div><br /></div><p></p><p><br /></p><h4 style="text-align: left;">Its code<br /></h4><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt3</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s[::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt3</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> (</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s) </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Odd char now in first half for odd len(s)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(x)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #c586c0;">in</span></div><div><span style="color: #cccccc;"> zip_longest(s[:l2], s[l2:],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fillvalue</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /></div><p></p><h4 style="text-align: left;">N modulo for Reverse<br /></h4><p>I looked again for a value for n after which encryptions repeat and found a similar dependency on <b><i>a002326</i></b> but with a subtle change in offset<br /></p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt4</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> (l1:=</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s)) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> a002326((l1 </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">)):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s[::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt4</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> ((l1:=</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s)) </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Odd char now in first half for odd len(s)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> a002326((l1 </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">)):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(x)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #c586c0;">in</span></div><div><span style="color: #cccccc;"> zip_longest(s[:l2], s[l2:],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fillvalue</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /></div><p></p><p><br /></p><h1 style="text-align: left;">Three groups<br /></h1><p>A task description like the first, that gave us encrypt1, but this time for three groups.</p><p>I am thinking about later generalisations and realise that after splitting into three, there could be 3! = 6 possible ways of concatenating groups, so call this encrypt_210 as 210 is the order used here:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">New Task encrypt_210:</span></div><div><span style="color: #ce9178;"> Given a string S and an integer N select chars at positions div 3</span></div><div><span style="color: #ce9178;"> and concatenate all the remainder-2, then rem-1 then rem-0 ordered</span></div><div><span style="color: #ce9178;"> groups of character. This process should be repeated N times to</span></div><div><span style="color: #ce9178;"> form the encrpted string.</span></div><div><span style="color: #ce9178;"> If N is 0 or the string is null return the input string.</span></div><br /><div><span style="color: #ce9178;">Examples:</span></div><div><span style="color: #ce9178;"> encrypt('012345', 0) => '012345'</span></div><div><span style="color: #ce9178;"> encrypt('012345', 1) 03,14,25 => '251403'</span></div><div><span style="color: #ce9178;"> encrypt('012345', 2) 24,50,13 => '135024'</span></div><br /></div><p></p><p><br /></p><h2 style="text-align: left;">Decode is encode!!</h2><div style="text-align: left;">I realised that <i>if you encode individual items to their output positions then the decode is always just the reverse mapping of encoded to starting state</i>.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">The above is easiest done by first encoding a list of integers then doing reverse indexing. </div><div style="text-align: left;">I decided to extend my knowledge of typing too. (Although I want to see if it helps with readability rather than running Mypy).</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> typing </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> overload</span></div><br /><div><span style="color: #dcdcaa;">@overload</span></div><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt5</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">: ... </span><span style="color: #6a9955;"># normal</span></div><div><span style="color: #dcdcaa;">@overload</span></div><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt5</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: list[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">], </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> list[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">]: ... </span><span style="color: #6a9955;"># internal, for decryption</span></div><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt5</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt5</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> In which I realise that for this _class_ of problem, decryption is reversing the encrpt</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> base </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s)))</span></div><div><span style="color: #cccccc;"> enc </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> encrypt5(base, n)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> enc:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(s[enc.index(i)] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> i </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> base)</span></div><br /></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h1 style="text-align: left;">Generalised groupings</h1><div style="text-align: left;">I realized that these encodings might be described in terms of cards and dealings: <br /></div><ul><li style="text-align: left;"> You have a pack of S cards and dealt them into K piles, left to right, starting again at the leftmost pile when you last dealt at the rightmost until you dealt the last card.</li><li style="text-align: left;">Number the piles, left-to-right from zero to K-1, </li><li style="text-align: left;">You also have a permutation M of the integers 0 to K-1 and you concatenate the dealt piles in the order given in M to form the first reordering of S.</li><li style="text-align: left;">Repeat the process N times to form the final ordering of S</li></ul>The picture at the beginning is for K == 3 and using Ace as 1, starting from Ace,2,3,...</div><div style="text-align: left;"></div><div style="text-align: left;"> </div><div style="text-align: left;">The following picture is the first concatenation arrangement of M = 2, 1, 0:</div><div style="text-align: left;"><br /> </div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZPiNB4x0vzoKTdjpkfyy8-dS15YrZYMhry8GI4NsX9N920Wbghr6zvGTWAnRvaj9Qn0fTmeLxhvpYOhMmuSlLaT99jljv9X4rFgmwmINHUeUBrIQQScnlbPCP9FW_qtnjrbL5Bz5RwrLqAroi95U2qgC1FLuLoBQVg9QH_ynoL3YT1YgFFZnO/s3648/IMG_20230820_110353_edit_707480951713920.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1721" data-original-width="3648" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZPiNB4x0vzoKTdjpkfyy8-dS15YrZYMhry8GI4NsX9N920Wbghr6zvGTWAnRvaj9Qn0fTmeLxhvpYOhMmuSlLaT99jljv9X4rFgmwmINHUeUBrIQQScnlbPCP9FW_qtnjrbL5Bz5RwrLqAroi95U2qgC1FLuLoBQVg9QH_ynoL3YT1YgFFZnO/w596-h221/IMG_20230820_110353_edit_707480951713920.jpg" width="596" /></a></div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZufNr8CCyQCL__5iOyAA3s7Y4EvbPdCeiS6awleUyY4PbcSyA9XEM0QoE2CG_9nC4tjP8b3aQ_ZKDodZiHuHKQNd0UFZdWxn6diIwgGflIrjk-Ig_RhS-zGCtsAuWKkhYZyW-tGT_eZYiJR9WzSVjlL7M5tqNg2KEj5ppmoMXh7GZCxix4GXx/s2867/IMG_20230820_110558_edit_707441632721217.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2736" data-original-width="2867" height="410" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZufNr8CCyQCL__5iOyAA3s7Y4EvbPdCeiS6awleUyY4PbcSyA9XEM0QoE2CG_9nC4tjP8b3aQ_ZKDodZiHuHKQNd0UFZdWxn6diIwgGflIrjk-Ig_RhS-zGCtsAuWKkhYZyW-tGT_eZYiJR9WzSVjlL7M5tqNg2KEj5ppmoMXh7GZCxix4GXx/w430-h410/IMG_20230820_110558_edit_707441632721217.jpg" width="430" /></a></div><br /><br /><h3 style="text-align: left;">My more formal definition of the generalised case:</h3><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">Task Encrypt grouping chars by mod M, N times:</span></div><div><span style="color: #ce9178;"> Given an input string S; Some order of the K integers 0..K-1 called</span></div><div><span style="color: #ce9178;"> M, where K <= length_of(S); and an integer N > 0</span></div><div><span style="color: #ce9178;"> </span></div><div><span style="color: #ce9178;"> First form groups:</span></div><div><span style="color: #ce9178;"> Form group G[0] by concatenating every K'th item of S starting from index 0</span></div><div><span style="color: #ce9178;"> Form group G[1] by concatenating every K'th item of S starting from index 1</span></div><div><span style="color: #ce9178;"> ...</span></div><div><span style="color: #ce9178;"> Form group G[K-1] by concatenating every K'th item of S starting from index K-1</span></div><div><span style="color: #ce9178;"> </span></div><div><span style="color: #ce9178;"> Group Concatenation:</span></div><div><span style="color: #ce9178;"> Concatenate all the groups G in the order given M to form the partial result P</span></div><div><span style="color: #ce9178;"> P(1) = concatenate_left_to_right(G[i] for i in M)</span></div><div><span style="color: #ce9178;"> Repetition:</span></div><div><span style="color: #ce9178;"> Substituting P for S, repeat the above process N times to form the final</span></div><div><span style="color: #ce9178;"> encrypted string P(N)</span></div><div><span style="color: #ce9178;"> </span></div><div><span style="color: #ce9178;"> Corner cases:</span></div><div><span style="color: #ce9178;"> If S is empty or N is 0 then return S.</span></div><div><span style="color: #ce9178;"> If M is empty return the empty version of sequence S (empty string).</span></div><br /><div><span style="color: #ce9178;">Examples:</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 0) => '012345'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 1) => '135024'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 2) => '304152'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 3) => '012345'</span></div><div><span style="color: #ce9178;"> encrypt6('012345', [1, 0], 4) => '135024'</span></div><br /><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 0) => '01234567'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 1) => '25147036'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 2) => '10576243'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 3) => '52063174'</span></div><div><span style="color: #ce9178;"> encrypt6('01234567', [2, 1, 0], 4) => '01234567'</span></div><br /></div><p></p><p><br /></p><h3 style="text-align: left;">Past encryptions in its terms</h3><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">Note that:</span></div><div><span style="color: #ce9178;"> encrypt(S, N), encrypt2(S, N) do encrypt6(S, [1, 0], N)</span></div><div><span style="color: #ce9178;"> encrypt3(S, N), encrypt4(S, N) do encrypt6(S, [0, 1], N)</span></div><div><span style="color: #ce9178;"> and</span></div><div><span style="color: #ce9178;"> encrypt5(S, N), does encrypt6(S, [2, 1, 0], N)</span></div><div><span style="color: #ce9178;"><br /></span></div></div><p></p><h2 style="text-align: left;">Generalised Code</h2><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> typing </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> Sequence</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt6</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: Sequence, </span><span style="color: #6a9955;"># Supporting indexing and __add__ </span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">m</span><span style="color: #cccccc;">: Sequence[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">], </span><span style="color: #6a9955;"># perm of ints 0..k-1, k <= len(s)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span></div><div><span style="color: #cccccc;"> ) -> Sequence: </span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> m:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">type</span><span style="color: #cccccc;">(s)() </span><span style="color: #6a9955;"># Empty sequence</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;">>=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> (k:=</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(m)) </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(k)) </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">set</span><span style="color: #cccccc;">(m), \</span></div><div><span style="color: #cccccc;"> </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"List </span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">m = </span><span style="color: #569cd6;">}</span><span style="color: #ce9178;"> should contain a permutation of all the numbers 0..</span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">k</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;"> inclusive."</span></div><div><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> s_type </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">type</span><span style="color: #cccccc;">(s) </span><span style="color: #6a9955;">#[0])</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">issubclass</span><span style="color: #cccccc;">(s_type, </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(s[offset::k] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> offset </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> m)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;">: </span><span style="color: #6a9955;"># non-strings</span></div><div><span style="color: #cccccc;"> empty_val </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s_type()</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">sum</span><span style="color: #cccccc;">((s[offset::k] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> offset </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> m),</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">start</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">empty_val)</span></div><div><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt6</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: Sequence, </span><span style="color: #6a9955;"># Supporting indexing and __add__ </span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">m</span><span style="color: #cccccc;">: Sequence[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">], </span><span style="color: #6a9955;"># perm of ints 0..k-1, k <= len(s)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span></div><div><span style="color: #cccccc;"> ) -> Sequence:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"By a reverse of the encoding"</span></div><div><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Where the encoding maps ordered items</span></div><div><span style="color: #cccccc;"> base </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s)))</span></div><div><span style="color: #cccccc;"> enc </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> encrypt6(base, m, n)</span></div><div><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> enc:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> enc</span></div><div><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Return the revered mapping</span></div><div><span style="color: #cccccc;"> s_type </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">type</span><span style="color: #cccccc;">(s)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">issubclass</span><span style="color: #cccccc;">(s_type, </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(s[enc.index(i)] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> i </span><span style="color: #c586c0;">in</span><span style="color: #cccccc;"> base)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> empty_val </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s_type()</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">sum</span><span style="color: #cccccc;">((s[enc.index(i)] </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> i </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> base),</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">start</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">empty_val)</span></div><div><span style="color: #cccccc;"><br /></span></div></div><p></p><p></p><p><br /></p><p><b>END BIT.</b></p><p>I have looked into the number sequences produced by encrypt6, but this is already a long post so I'll leave that 'till next time</p><p> </p><p><br /></p><p><br /></p><p><br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-85595090441873851742023-08-19T10:04:00.000+01:002023-08-19T10:04:09.105+01:00OEISify<p><span style="font-size: small;"><b><span style="color: #2b00fe;">Best read on larger than a phone screen </span></b><span style="color: #2b00fe;"><br /></span></span></p><p><span style="font-size: x-small;">The title is a rather mean pun on ossify, but it stuck.</span></p><p style="text-align: center;"><span style="font-size: x-small;"> <img alt="" src="data:image/png;base64,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" /></span></p><p>I had been creating series of integers from prior work, and had been looking them up, manually, on <br /></p><div style="text-align: left;"><a href="https://oeis.org" style="color: black;">The On-Line Encyclopedia of Integer Sequences</a>, OEIS. </div><div style="text-align: left;">Work progressed and I was creating a table of related sequences and looked to automate the OEIS searches.</div><div style="text-align: left;">I found a description of the textual internal format <a href="https://oeis.org/eishelp1.html" rel="nofollow" target="_blank">here</a> which seemed steraight-forward and "awkable" - meaning that the format is written in a way that a short awk program could parse. I did find a couple of old python libraries that said they did this, but I wanted to flex my parsing skills and this seemed an enticing.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h2 style="text-align: left;">Mile high looking down</h2></div><div style="text-align: left;">Checking that internal format, the example result seem to be of the form:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-family: Source Code Pro;">%<TAG> A<number> <value></span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">To show search results in the internal format you need to make a slight change to a url:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-family: Source Code Pro;">https://oeis.org/search?<span style="color: #2b00fe;"><b>fmt=text&</b></span>q=3%2C11%2C26%2C50%2C85 (<a href="https://oeis.org/search?fmt=text&q=3%2C11%2C26%2C50%2C85" rel="nofollow" target="_blank">test</a>)<br /></span></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h3><span style="font-family: inherit;">Reading the internal format in more detail </span></h3></div><div style="text-align: left;"><span style="font-family: inherit;">I saw that the <i><b>%S %T and %U lines</b></i> are all giving members of the sequence - there seems to be no reason to keep a distinction, I could parse and return all the values in order.</span></div><div style="text-align: left;"><span style="font-family: inherit;"> </span></div><div style="text-align: left;"><span style="font-family: inherit;">The<b><i> %p, %t, %o</i></b> lines when you concatenate all of the same type, give program sources in three different languages. They can be treated similarly.</span></div><div style="text-align: left;"><span style="font-family: inherit;"> </span></div><div style="text-align: left;"><span style="font-family: inherit;">the<b><i> %k</i></b> keywords are best returned as a list of strings.</span></div><div style="text-align: left;"><span style="font-family: inherit;"> <br /></span></div><div style="text-align: left;"><span style="font-family: inherit;">The other keywords, if they appear more than once in a sequence definition then subsequent values can append as new lines, accumulating a final value.</span></div><div style="text-align: left;"><span style="font-family: inherit;"><br /></span></div><div style="text-align: left;"><h3 style="text-align: left;"><span style="font-family: inherit;">Extra Info given</span></h3></div><div style="text-align: left;"><span style="font-family: inherit;">When you look at a search <a href="https://oeis.org/search?fmt=text&q=3%2C11%2C26%2C50%2C85" rel="nofollow" target="_blank">result </a>in text format you get a <b>Search:</b> and <b>Showing </b>line I might want to return this data and maybe also derive a Next entry from the details of Showing that is what is needed to generate the next page of results for the given search.</span></div><div style="text-align: left;"><span style="font-family: inherit;"><br /></span></div><div style="text-align: left;"><h3 style="text-align: left;"><span style="font-family: inherit;">The Regexp</span></h3></div><div style="text-align: left;"><span style="font-family: inherit;">Yep, I started with a regexp, so put a lot of sample text into regex101 as TEST STRING then<a href="https://regex101.com/r/iNL0ve/2" rel="nofollow" target="_blank"> generated the regexp</a> used in the program. </span></div><div style="text-align: left;"><span style="font-family: inherit;"><br /></span></div><div style="text-align: left;"><h3 style="text-align: left;"><span style="font-family: inherit;">The Output</span></h3></div><div style="text-align: left;"><span style="font-family: inherit;">A Python dict with all string keys. Each found A-series will be a nested dict of keys corresponding to the %-letter tags.</span></div><div style="text-align: left;"><span style="font-family: inherit;">Keys Search, Showing and Next are about the search and so appear in the top dict.</span></div><div style="text-align: left;"><span style="font-family: inherit;"> </span></div><div style="text-align: left;"><h1 style="text-align: left;"><span style="font-family: inherit;">The Code: oeis_request.py<br /></span></h1></div><div style="text-align: left;"><span style="font-family: inherit;"> </span><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;">#!/bin/env python3</span></div><div><span style="color: #6a9955;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #ce9178;">Module to search and return sequences from:</span></div><div><span style="color: #ce9178;"> The On-Line Encyclopedia of Integer Sequences® (OEIS®)</span></div><div><span style="color: #ce9178;"> https://oeis.org/</span></div><div><span style="color: #ce9178;"> https://oeis.org/eishelp1.html</span></div><br /><div><span style="color: #ce9178;">Standalone Usage:</span></div><div><span style="color: #ce9178;"> oeis_request.py <query> <start></span></div><div><span style="color: #ce9178;"> oeis_request.py --help</span></div><div><span style="color: #ce9178;"> oeis_request.py --test</span></div><br /><br /><div><span style="color: #ce9178;">Created on Sat Aug 12 07:57:53 2023</span></div><br /><div><span style="color: #ce9178;">@author: paddy3118, paddy3118.blogspot.com</span></div><br /><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> pprint </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> pp</span></div><div><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> re</span></div><div><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> ssl</span></div><div><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> sys</span></div><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> typing </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> Any</span></div><div><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> urllib.request</span></div><div><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> urllib.parse</span></div><div><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> json </span><span style="color: #6a9955;"># pylint: disable=W0611</span></div><br /><br /><div><span style="color: #cccccc;">URL </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'https://oeis.org/search'</span></div><br /><div><span style="color: #cccccc;">DBG </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">True</span></div><br /><div><span style="color: #cccccc;">finditer </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> re.compile(</span><span style="color: #569cd6;">r</span><span style="color: #d16969;">"""</span></div><div><span style="color: #d16969;"> </span><span style="color: #6a9955;"># https://regex101.com/r/iNL0ve/2</span></div><br /><div><span style="color: #d16969;"> </span><span style="color: #ce9178;">(?:</span><span style="color: #d16969;"> Search: \s</span><span style="color: #d7ba7d;">+</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<SEARCH></span><span style="color: #d16969;"> .</span><span style="color: #d7ba7d;">*</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">))</span></div><div><span style="color: #d16969;"> </span><span style="color: #d4d4d4;">|</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(?:</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<SHOWING></span><span style="color: #d16969;"> ^Showing\s.</span><span style="color: #d7ba7d;">*</span><span style="color: #ce9178;">))</span></div><div><span style="color: #d16969;"> </span><span style="color: #d4d4d4;">|</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(?:</span><span style="color: #d16969;"> ^% </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<_TAG></span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<SEQ></span><span style="color: #d16969;"> </span><span style="color: #ce9178;">[</span><span style="color: #d16969;">STU</span><span style="color: #ce9178;">]</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">)</span><span style="color: #d16969;"> </span><span style="color: #d4d4d4;">|</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<PROG></span><span style="color: #d16969;"> </span><span style="color: #ce9178;">[</span><span style="color: #d16969;">pto</span><span style="color: #ce9178;">]</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">)</span><span style="color: #d16969;"> </span><span style="color: #d4d4d4;">|</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<REST></span><span style="color: #d16969;"> </span><span style="color: #ce9178;">[</span><span style="color: #d16969;">A-Za-z</span><span style="color: #ce9178;">])</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">)</span><span style="color: #d16969;"> \s</span><span style="color: #d7ba7d;">+</span></div><div><span style="color: #d16969;"> </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<ANUMBER></span><span style="color: #d16969;"> A\d</span><span style="color: #d7ba7d;">+</span><span style="color: #ce9178;">)</span><span style="color: #d16969;"> \s</span><span style="color: #d7ba7d;">+</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">(</span><span style="color: #569cd6;">?P<VALUE></span><span style="color: #d16969;"> .</span><span style="color: #d7ba7d;">*</span><span style="color: #ce9178;">)</span><span style="color: #d16969;"> </span><span style="color: #ce9178;">)</span></div><div><span style="color: #d16969;"> """</span><span style="color: #cccccc;">, re.MULTILINE </span><span style="color: #d4d4d4;">|</span><span style="color: #cccccc;"> re.VERBOSE).finditer</span></div><br /><div><span style="color: #cccccc;">OEISTYPE </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> dict[</span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">|</span><span style="color: #cccccc;"> dict[</span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">|</span><span style="color: #cccccc;"> list[</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">|</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">]]]</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">oeis_request</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">query</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">start</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">) -> OEISTYPE:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Search OEIS for query returning a page of results from position start as a Python dict</span></div><br /><br /><div><span style="color: #ce9178;"> Parameters</span></div><div><span style="color: #ce9178;"> ----------</span></div><div><span style="color: #ce9178;"> query : str</span></div><div><span style="color: #ce9178;"> The OEIS query in a format consistent with https://oeis.org/hints.html.</span></div><div><span style="color: #ce9178;"> start : int, optional</span></div><div><span style="color: #ce9178;"> page offset when multiple pages of results are available. The default is 0.</span></div><br /><div><span style="color: #ce9178;"> Returns</span></div><div><span style="color: #ce9178;"> -------</span></div><div><span style="color: #ce9178;"> dict[str, # key0, top level keys: SEARCH, SHOWING, NEXT</span></div><div><span style="color: #ce9178;"> str # string value,</span></div><div><span style="color: #ce9178;"> | dict[ # Or a nested dict for each sequence definition:</span></div><div><span style="color: #ce9178;"> str, # key1, sequence level string keys,</span></div><div><span style="color: #ce9178;"> str | # And either a string value,</span></div><div><span style="color: #ce9178;"> list[int # A list for the int Sequence,</span></div><div><span style="color: #ce9178;"> | str]]] # or a list of the str Keywords; as value.</span></div><br /><div><span style="color: #ce9178;"> Returned Python datastructure representing the (page of) sequences found.</span></div><div><span style="color: #ce9178;"> Top level keys are the 'SEARCH' and what results are 'SHOWING', together</span></div><div><span style="color: #ce9178;"> with the sequence numbers of returned sequences like 'A123456' - which</span></div><div><span style="color: #ce9178;"> all have one sub-dict as its value. Top level key 'NEXT', if present,</span></div><div><span style="color: #ce9178;"> has as its value the query and start value pre-computed to retrieve the</span></div><div><span style="color: #ce9178;"> next page of results from oeis_request.</span></div><br /><div><span style="color: #ce9178;"> All possible keys of a sequence sub-dict are the values of global variable</span></div><div><span style="color: #ce9178;"> RECORD_MAP</span></div><div><span style="color: #ce9178;"> """</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># https://stackoverflow.com/a/60671292/10562</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Security risk as ssl cert checking is skipped for this function</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># pylint: disable=protected-access</span></div><div><span style="color: #cccccc;"> tmp, ssl._create_default_https_context </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> ssl._create_default_https_context, \</span></div><div><span style="color: #cccccc;"> ssl._create_unverified_context</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># pylint: enable=protected-access</span></div><br /><div><span style="color: #cccccc;"> data </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> {}</span></div><div><span style="color: #cccccc;"> data[</span><span style="color: #ce9178;">'fmt'</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'text'</span></div><div><span style="color: #cccccc;"> data[</span><span style="color: #ce9178;">'q'</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> query</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> start:</span></div><div><span style="color: #cccccc;"> data[</span><span style="color: #ce9178;">'start'</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> start</span></div><div><span style="color: #cccccc;"> url_values </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> urllib.parse.urlencode(data)</span></div><div><span style="color: #cccccc;"> url </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> URL </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'?'</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> url_values</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> DBG:</span></div><div><span style="color: #cccccc;"> sys.stderr.write(url</span><span style="color: #d4d4d4;">+</span><span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">'</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> sys.stderr.flush()</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">with</span><span style="color: #cccccc;"> urllib.request.urlopen(url) </span><span style="color: #c586c0;">as</span><span style="color: #cccccc;"> req:</span></div><div><span style="color: #cccccc;"> txt </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">req.read().decode(</span><span style="color: #ce9178;">'utf8'</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Restored:</span></div><div><span style="color: #cccccc;"> ssl._create_default_https_context </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> tmp </span><span style="color: #6a9955;"># pylint: disable=protected-access</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> oeis_parser(txt)</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">oeis_parser</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">text</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">) -> dict[</span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, Any]: </span><span style="color: #6a9955;"># pylint: disable=too-many-branches</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Parse text formatted like https://oeis.org/eishelp1.html</span></div><br /><div><span style="color: #ce9178;"> Parameters</span></div><div><span style="color: #ce9178;"> ----------</span></div><div><span style="color: #ce9178;"> text : str</span></div><div><span style="color: #ce9178;"> text returned from OEIS search.</span></div><br /><div><span style="color: #ce9178;"> Returns</span></div><div><span style="color: #ce9178;"> -------</span></div><div><span style="color: #ce9178;"> dict[str, Any]</span></div><div><span style="color: #ce9178;"> parsed data</span></div><br /><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> data </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> {}</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> matchobj </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> finditer(text):</span></div><div><span style="color: #cccccc;"> group </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> matchobj.groupdict()</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">try</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> anumber </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> group[</span><span style="color: #ce9178;">'ANUMBER'</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> anumber: </span><span style="color: #6a9955;"># Skip None</span></div><div><span style="color: #cccccc;"> adict </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> data.setdefault(anumber, {})</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">except</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">KeyError</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> anumber </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">try</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> value </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> group[</span><span style="color: #ce9178;">'VALUE'</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">except</span><span style="color: #cccccc;"> </span><span style="color: #4ec9b0;">KeyError</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> value </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> val:</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">group[key0:=</span><span style="color: #ce9178;">'SEARCH'</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> data[key0] </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> val</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">elif</span><span style="color: #cccccc;"> val:</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">group[key0:=</span><span style="color: #ce9178;">'SHOWING'</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> data[key0] </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> val</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">elif</span><span style="color: #cccccc;"> val:</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">group[key1:=</span><span style="color: #ce9178;">'SEQ'</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Concat all %S $T and %U lines as list[str]</span></div><div><span style="color: #cccccc;"> adict.setdefault(key1, []).extend(</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">(v) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> v </span><span style="color: #c586c0;">in</span></div><div><span style="color: #cccccc;"> value.strip().strip(</span><span style="color: #ce9178;">','</span><span style="color: #cccccc;">).split(</span><span style="color: #ce9178;">','</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">elif</span><span style="color: #cccccc;"> val:</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">group[</span><span style="color: #ce9178;">'PROG'</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Concat all individual %p %t %o lines as list[str]</span></div><div><span style="color: #cccccc;"> adict.setdefault(RECORD_MAP[val], []).append(value.strip())</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">elif</span><span style="color: #cccccc;"> (val:=group[</span><span style="color: #ce9178;">'REST'</span><span style="color: #cccccc;">]) </span><span style="color: #569cd6;">and</span><span style="color: #cccccc;"> val </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'K'</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Split and concat Keywords, %K lines as list[str]</span></div><div><span style="color: #cccccc;"> adict.setdefault(RECORD_MAP[val], []).extend(value.strip().split(</span><span style="color: #ce9178;">','</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">elif</span><span style="color: #cccccc;"> val:</span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;">group[</span><span style="color: #ce9178;">'REST'</span><span style="color: #cccccc;">]:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Concat other %_ lines as list[str]</span></div><div><span style="color: #cccccc;"> adict.setdefault(RECORD_MAP[val], []).append(value.strip())</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># Should never arrive here from the regexp!</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">assert</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">False</span><span style="color: #cccccc;">, </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"Got </span><span style="color: #569cd6;">{</span><span style="color: #cccccc;">group = </span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;"># fixup</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> key, subd </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> data.items():</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> key[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'A'</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">and</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">issubclass</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">type</span><span style="color: #cccccc;">(subd), </span><span style="color: #4ec9b0;">dict</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> record, value </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> subd.items():</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> record </span><span style="color: #d4d4d4;">!=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Keywords'</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">and</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">issubclass</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">type</span><span style="color: #cccccc;">(value), </span><span style="color: #4ec9b0;">list</span><span style="color: #cccccc;">) \</span></div><div><span style="color: #cccccc;"> </span><span style="color: #569cd6;">and</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">issubclass</span><span style="color: #cccccc;">(</span><span style="color: #4ec9b0;">type</span><span style="color: #cccccc;">(value[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">]), </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> subd[record] </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">'</span><span style="color: #cccccc;">.join(value)</span></div><br /><div><span style="color: #cccccc;"> next_from_showing(data)</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> data</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">next_from_showing</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">parsed</span><span style="color: #cccccc;">: OEISTYPE)-> </span><span style="color: #569cd6;">None</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Parse "SHOWING" key to generate "NEXT" value that would request the next page</span></div><br /><div><span style="color: #ce9178;"> If already at the end then no next key is in data</span></div><div><span style="color: #ce9178;"> parsed is altered in-place</span></div><br /><div><span style="color: #ce9178;"> Parameters</span></div><div><span style="color: #ce9178;"> ----------</span></div><div><span style="color: #ce9178;"> parsed : OEISTYPE</span></div><div><span style="color: #ce9178;"> Parsed data to insert a NEXT key/value.</span></div><br /><div><span style="color: #ce9178;"> Returns</span></div><div><span style="color: #ce9178;"> -------</span></div><div><span style="color: #ce9178;"> None.</span></div><div><span style="color: #ce9178;"> argument updated in-place</span></div><br /><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #cccccc;"> matchobj </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> re.match(</span><span style="color: #569cd6;">r</span><span style="color: #d16969;">"^Showing\s</span><span style="color: #d7ba7d;">+</span><span style="color: #ce9178;">(</span><span style="color: #d16969;">\d</span><span style="color: #d7ba7d;">+</span><span style="color: #ce9178;">)</span><span style="color: #d16969;">-</span><span style="color: #ce9178;">(</span><span style="color: #d16969;">\d</span><span style="color: #d7ba7d;">+</span><span style="color: #ce9178;">)</span><span style="color: #d16969;">\s</span><span style="color: #d7ba7d;">+</span><span style="color: #d16969;">of\s</span><span style="color: #d7ba7d;">+</span><span style="color: #ce9178;">(</span><span style="color: #d16969;">\d</span><span style="color: #d7ba7d;">+</span><span style="color: #ce9178;">)</span><span style="color: #d16969;">\s</span><span style="color: #d7ba7d;">*</span><span style="color: #d16969;">$"</span><span style="color: #cccccc;">, parsed.get(</span><span style="color: #ce9178;">'SHOWING'</span><span style="color: #cccccc;">, </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> matchobj:</span></div><div><span style="color: #cccccc;"> _start, stop, end </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> [</span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">(x) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> matchobj.groups()]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> stop </span><span style="color: #d4d4d4;"><</span><span style="color: #cccccc;"> end:</span></div><div><span style="color: #cccccc;"> search </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> parsed[</span><span style="color: #ce9178;">'SEARCH'</span><span style="color: #cccccc;">].split(</span><span style="color: #ce9178;">':'</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)[</span><span style="color: #d4d4d4;">-</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> parsed[</span><span style="color: #ce9178;">'NEXT'</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> [search, stop]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'NEXT'</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> parsed:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">del</span><span style="color: #cccccc;"> parsed[</span><span style="color: #ce9178;">'NEXT'</span><span style="color: #cccccc;">]</span></div><br /><br /><div><span style="color: #cccccc;">record_info </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">%I A000001 Identification line (required)</span></div><div><span style="color: #ce9178;">%S A000001 First line of sequence (required)</span></div><div><span style="color: #ce9178;">%T A000001 2nd line of sequence.</span></div><div><span style="color: #ce9178;">%U A000001 3rd line of sequence.</span></div><div><span style="color: #ce9178;">%N A000001 Name (required)</span></div><div><span style="color: #ce9178;">%D A000001 Reference Detailed reference line.</span></div><div><span style="color: #ce9178;">%H A000001 Link to other site.</span></div><div><span style="color: #569cd6;">%F</span><span style="color: #ce9178;"> A000001 Formula .</span></div><div><span style="color: #ce9178;">%Y A000001 Cross-references to other sequences.</span></div><div><span style="color: #ce9178;">%A A000001 Author (required)</span></div><div><span style="color: #ce9178;">%O A000001 Offset (required)</span></div><div><span style="color: #569cd6;">%E</span><span style="color: #ce9178;"> A000001 Etc Extensions, errors, etc.</span></div><div><span style="color: #569cd6;">%e</span><span style="color: #ce9178;"> A000001 Examples examples to illustrate initial terms.</span></div><div><span style="color: #ce9178;">%p A000001 Maple program.</span></div><div><span style="color: #ce9178;">%t A000001 Mathematica program.</span></div><div><span style="color: #569cd6;">%o</span><span style="color: #ce9178;"> A000001 OtherProgram in another language.</span></div><div><span style="color: #ce9178;">%K A000001 Keywords (required)</span></div><div><span style="color: #ce9178;">%C A000001 Comments.</span></div><div><span style="color: #ce9178;">"""</span><span style="color: #cccccc;">.strip().splitlines()</span></div><br /><div><span style="color: #cccccc;">RECORD_MAP </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> {words[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">][</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">]: words[</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> line </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> record_info</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> words </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> [line.split()]}</span></div><div><span style="color: #6a9955;"># pp(RECORD_MAP, sort_dicts=False)</span></div><br /><div><span style="color: #cccccc;">HELP </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"""</span><span style="color: #569cd6;">\</span></div><div><span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">__doc__</span><span style="color: #569cd6;">}</span></div><br /><div><span style="color: #ce9178;">function oeis_request</span></div><div><span style="color: #ce9178;">=====================</span></div><br /><div><span style="color: #569cd6;">{</span><span style="color: #cccccc;">oeis_request.</span><span style="color: #9cdcfe;">__doc__</span><span style="color: #569cd6;">}</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">__name__</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'__main__'</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'--help'</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> sys.argv:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(HELP)</span></div><div><span style="color: #cccccc;"> sys.exit(</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'--test'</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> sys.argv[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">:]:</span></div><div><span style="color: #cccccc;"> _req, _start </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'1,2,3,4,5,6,6,7,7,8'</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">elif</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(sys.argv) </span><span style="color: #d4d4d4;">==</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> _req, _start </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> sys.argv[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">:] </span><span style="color: #6a9955;"># pylint: disable=W0632</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(HELP)</span></div><div><span style="color: #cccccc;"> sys.exit(</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">)</span></div><br /><div><span style="color: #cccccc;"> _data </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> oeis_request(_req, _start) </span><span style="color: #6a9955;"># pylint: disable=E0601</span></div><div><span style="color: #cccccc;"> pp(_data, </span><span style="color: #9cdcfe;">width</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">512</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">sort_dicts</span><span style="color: #d4d4d4;">=</span><span style="color: #569cd6;">False</span><span style="color: #cccccc;">)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #6a9955;">#print(json.dumps(_data, indent=2))</span></div><br /><br /></div></div><div style="text-align: left;"><span style="font-family: inherit;"> </span></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h3 style="text-align: left;">Test output</h3></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #f44747;">$</span><span style="color: #cccccc;"> .</span><span style="color: #d4d4d4;">/</span><span style="color: #cccccc;">oeis_request.py </span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">11</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">26</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">50</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">85</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;">{</span><span style="color: #ce9178;">'SEARCH'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'seq:3,11,26,50,85'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'SHOWING'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'Showing 1-1 of 1'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'A051925'</span><span style="color: #cccccc;">: {</span><span style="color: #ce9178;">'Identification'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'#84 Jun 26 2022 03:06:23'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'SEQ'</span><span style="color: #cccccc;">: [</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">11</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">26</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">50</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">85</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">133</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">196</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">276</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">375</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">495</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">638</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">806</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">1001</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">1225</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">1480</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">1768</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">2091</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">2451</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">2850</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">3290</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">3773</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">4301</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">4876</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">5500</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">6175</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">6903</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">7686</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">8526</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">9425</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">10385</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">11408</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">12496</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">13651</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">14875</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">16170</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">17538</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">18981</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">20501</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">22100</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">23780</span><span style="color: #cccccc;">],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Name'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'a(n) = n*(2*n+5)*(n-1)/6.'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Comments.'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'Related to variance of number of inversions of a random permutation of n letters.</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">'</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Zero followed by partial sums of A005563. - _Klaus Brockhaus_, Oct 17 2008</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">'</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'a(n)/12 is the variance of the number of inversions of a random permutation of n letters. See evidence in Mathematica code below. - _Geoffrey Critzer_, May 15 2010</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">'</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'The sequence is related to A033487 by A033487(n-1) = n*a(n) - Sum_{i=0..n-1} a(i) = n*(n+1)*(n+2)*(n+3)/4. - _Bruno Berselli_, Apr 04 2012</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">'</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"Deleting the two 0's leaves row 2 of the convolution array A213750. - _Clark Kimberling_, Jun 20 2012</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'For n>=4, a(n-2) is the number of permutations of 1,2...,n with the distribution of up (1) - down (0) elements 0...0110 (the first n-4 zeros), or, the same, a(n-2) is up-down coefficient {n,6} (see comment in A060351). - _Vladimir Shevelev_, Feb 15 2014'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Reference'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'V. N. Sachkov, Probabilistic Methods in Combinatorial Analysis, Cambridge, 1997.'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Link'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'Vincenzo Librandi, <a href="/A051925/b051925.txt">Table of n, a(n) for n = 0..1000</a></span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">J. Wang and H. Li, <a href="http://dx.doi.org/10.1016/S0012-365X(01)00301-6">The upper bound of essential chromatic numbers of hypergraphs</a>, Discr. Math. 254 (2002), 555-564.</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;"><a href="/index/Rec#order_04">Index entries for linear recurrences with constant coefficients</a>, signature (4,-6,4,-1).'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Formula'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'a(n) = A000330(n) - n. - _Andrey Kostenko_, Nov 30 2008</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">G.f.: x^2*(3-x)/(1-x)^4. - _Colin Barker_, Apr 04 2012</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">a(n) = 4*a(n-1) - 6*a(n-2) + 4*a(n-3) - a(n-4). - _Vincenzo Librandi_, Apr 27 2012</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">E.g.f.: (x^2/6)*(2*x + 9)*exp(x). - _G. C. Greubel_, Jul 19 2017'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Mathematica'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'f[{x_, y_}] := 2 y - x^2; Table[f[Coefficient[ Series[Product[Sum[Exp[i t], {i, 0, m}], {m, 1, n - 1}]/n!, {t, 0, 2}], t, {1, 2}]], {n, 0, 41}]*12 (* _Geoffrey Critzer_, May 15 2010 *)</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">CoefficientList[Series[x^2*(3-x)/(1-x)^4,{x,0,50}],x] (* _Vincenzo Librandi_, Apr 27 2012 *)'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'OtherProgram'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'(PARI) {print1(a=0, ","); for(n=0, 42, print1(a=a+(n+1)^2-1, ","))} </span><span style="color: #d7ba7d;">\\\\</span><span style="color: #ce9178;"> _Klaus Brockhaus_, Oct 17 2008</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">(Magma) I:=[0, 0, 3, 11]; [n le 4 select I[n] else 4*Self(n-1)-6*Self(n-2)+4*Self(n-3)-Self(n-4): n in [1..50]]; // _Vincenzo Librandi_, Apr 27 2012'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Cross-references'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'Cf. A000330, A005563, A033487.'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Keywords'</span><span style="color: #cccccc;">: [</span><span style="color: #ce9178;">'nonn'</span><span style="color: #cccccc;">, </span><span style="color: #ce9178;">'easy'</span><span style="color: #cccccc;">],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Offset'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'0,3'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'Author'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'_N. J. A. Sloane_, Dec 19 1999'</span><span style="color: #cccccc;">}}</span></div><div><span style="color: #f44747;">$</span></div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><h3 style="text-align: left;">A test with paged results</h3></div><div style="text-align: left;">Highlighting the NEXT value of what is needed to get the next page of results</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #f44747;">$</span><span style="color: #cccccc;"> .</span><span style="color: #d4d4d4;">/</span><span style="color: #cccccc;">oeis_request.py </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">4</span><span style="color: #cccccc;">,</span><span style="color: #b5cea8;">5</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;"> </span></div><div><span style="color: #cccccc;">{</span><span style="color: #ce9178;">'SEARCH'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'seq:1,2,3,4,5'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'SHOWING'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'Showing 1-10 of 7513'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'A000027'</span><span style="color: #cccccc;">: {</span><span style="color: #ce9178;">'Identification'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'M0472 N0173 #637 Aug 14 2023 15:10:40'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> ...</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'A007953'</span><span style="color: #cccccc;">: {</span><span style="color: #ce9178;">'Identification'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'#280 Jun 18 2023 11:41:19'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> ...</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'A000961'</span><span style="color: #cccccc;">: </span></div><div><span style="color: #cccccc;"> ...</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'A002260'</span><span style="color: #cccccc;">: {</span><span style="color: #ce9178;">'Identification'</span><span style="color: #cccccc;">: </span><span style="color: #ce9178;">'#205 Feb 03 2023 18:43:52'</span><span style="color: #cccccc;">,</span></div><div><span style="color: #cccccc;"> ...</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'NEXT'</span><span style="color: #cccccc;">: [</span><span style="color: #ce9178;">'1,2,3,4,5'</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">10</span><span style="color: #cccccc;">]}</span></div><div><span style="color: #f44747;">$</span></div></div></div>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-55456624689361932922023-08-04T23:32:00.003+01:002023-08-04T23:32:27.781+01:00Exploring a simple encryption using Python<p><b style="color: #2b00fe;">[Not suitable for viewing on phones]</b></p><p>
</p><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<h1 id="Exploring-a-simple-encryption-using-Python"></h1><p>Someone on Linkedin submitted a code Kata from the Codewars site:</p>
<p><a href="https://www.codewars.com/kata/simple-encryption-number-1-alternating-split"><b>Simple Encryption #1 - Alternating Split</b></a></p>
<blockquote>
<p>Implement a pseudo-encryption algorithm which given a string S and an
integer N concatenates all the odd-indexed characters of S with all the
even-indexed characters of S, this process should be repeated N times.</p>
</blockquote>
<blockquote>
<p><i><b>Examples:</b></i></p>
<p><i><b>encrypt("012345", 1) => "135024"<br />
encrypt("012345", 2) => "135024" -> "304152"<br />
encrypt("012345", 3) => "135024" -> "304152" -> "012345"</b></i></p>
<p><i><b>encrypt("01234", 1) => "13024"<br />
encrypt("01234", 2) => "13024" -> "32104"<br />
encrypt("01234", 3) => "13024" -> "32104" -> "20314"</b></i></p>
</blockquote>
<blockquote>
<p>Together with the encryption function, you should also implement a decryption function which reverses the process.</p>
</blockquote>
<blockquote>
<p>If the string S is an empty value or the integer N is not positive, return the first argument without changes.</p>
</blockquote>
<p>The task author was <code>user5036852</code></p>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>I liked that the task description was compact but that a solution might be fun so attempted it</p>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<h2 id="First-thoughts">First thoughts</h2><p><em>Encryption</em></p>
<ul><li>String slicing: evens are <code>[0::2]</code>, odds are <code>[1::2]</code></li></ul>
<p><em>Decryption</em></p>
<ul><li>Hmm, find halfway of string then use <code>zip(first_half, second_half)</code> with extra magic.</li><li>The examples all have an even number of characters in the string - What happens for odd?<ul><li>We may need <a href="https://docs.python.org/3/library/itertools.html#itertools.zip_longest">zip_longest</a></li></ul>
</li></ul>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<h3 id="The-code">The code</h3><p>Notice the if statements at the beginning of each function that cater for "bad" inputs.</p>
<p><strong>The encoder</strong> flows directly from the task description.</p>
<p><strong>The decoder</strong> did need zip_longest to grab that last odd character by pairing it with the null string, <code>''</code>, as fillvalue.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs "><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Created on Tues Aug 1 17:02:06 2023</span></div><br /><div><span style="color: #ce9178;">@author: paddy3118</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #c586c0;">from</span><span style="color: #cccccc;"> itertools </span><span style="color: #c586c0;">import</span><span style="color: #cccccc;"> zip_longest</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(x)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #c586c0;">in</span></div><div><span style="color: #cccccc;"> zip_longest(s[l2:], s[:l2],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fillvalue</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /></div><pre>Some tests</pre></div></div></div></div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="k">def</span> <span class="nf">test_simple_functionality</span><span class="p">(</span><span class="n">encrypter</span><span class="p">,</span> <span class="n">decrypter</span><span class="p">):</span>
<span class="s2">"Tests for using short, even and odd length strings over a small range of N"</span>
<span class="k">for</span> <span class="n">txt</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">'012345'</span><span class="p">,</span> <span class="s1">'0123456'</span><span class="p">]:</span> <span class="c1"># Notice even then odd-length</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">encrypter</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="n">dec</span> <span class="o">=</span> <span class="n">decrypter</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">txt</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">enc</span><span class="p">,</span><span class="w"> </span><span class="n">dec</span><span class="w"> </span><span class="si">= }</span><span class="s2">"</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">txt</span> <span class="o">==</span> <span class="n">decrypter</span><span class="p">(</span><span class="n">encrypter</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">),</span> <span class="n">n</span><span class="p">)</span>
<span class="n">test_simple_functionality</span><span class="p">(</span><span class="n">encrypt</span><span class="p">,</span> <span class="n">decrypt</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>txt, n, enc, dec = ('012345', 0, '012345', '012345')
txt, n, enc, dec = ('012345', 1, '135024', '012345')
txt, n, enc, dec = ('012345', 2, '304152', '012345')
txt, n, enc, dec = ('012345', 3, '012345', '012345')
txt, n, enc, dec = ('0123456', 0, '0123456', '0123456')
txt, n, enc, dec = ('0123456', 1, '1350246', '0123456')
txt, n, enc, dec = ('0123456', 2, '3041526', '0123456')
txt, n, enc, dec = ('0123456', 3, '0123456', '0123456')
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>The code seems to work!</p>
<p>When submitted to Codewars for their more extensive set of tests, then the code above was accepted.</p>
<h2 id="Patterns!">Patterns!</h2><p>Looking at the test output above, it seems:</p>
<div class="highlight"><pre><span></span><span class="n">encrypter</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="n">txt</span>
</pre></div>
<p>Lets look deeper:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="k">def</span> <span class="nf">test_simple_functionality2</span><span class="p">(</span><span class="n">encrypter</span><span class="p">,</span> <span class="n">decrypter</span><span class="p">,</span> <span class="n">nmax</span><span class="o">=</span><span class="mi">8</span><span class="p">):</span>
<span class="s2">"Tests for using short, even and odd length strings over a small range of N"</span>
<span class="k">for</span> <span class="n">txt</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">'012345'</span><span class="p">,</span> <span class="s1">'0123456'</span><span class="p">]:</span> <span class="c1"># Notice even then odd-length</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nmax</span><span class="p">):</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">encrypter</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="n">dec</span> <span class="o">=</span> <span class="n">decrypter</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">txt</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">enc</span><span class="p">,</span><span class="w"> </span><span class="n">dec</span><span class="w"> </span><span class="si">= }</span><span class="s2">"</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">txt</span> <span class="o">==</span> <span class="n">decrypter</span><span class="p">(</span><span class="n">encrypter</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">),</span> <span class="n">n</span><span class="p">)</span>
<span class="n">test_simple_functionality2</span><span class="p">(</span><span class="n">encrypt</span><span class="p">,</span> <span class="n">decrypt</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>txt, n, enc, dec = ('012345', 0, '012345', '012345')
txt, n, enc, dec = ('012345', 1, '135024', '012345')
txt, n, enc, dec = ('012345', 2, '304152', '012345')
txt, n, enc, dec = ('012345', 3, '012345', '012345')
txt, n, enc, dec = ('012345', 4, '135024', '012345')
txt, n, enc, dec = ('012345', 5, '304152', '012345')
txt, n, enc, dec = ('012345', 6, '012345', '012345')
txt, n, enc, dec = ('012345', 7, '135024', '012345')
txt, n, enc, dec = ('0123456', 0, '0123456', '0123456')
txt, n, enc, dec = ('0123456', 1, '1350246', '0123456')
txt, n, enc, dec = ('0123456', 2, '3041526', '0123456')
txt, n, enc, dec = ('0123456', 3, '0123456', '0123456')
txt, n, enc, dec = ('0123456', 4, '1350246', '0123456')
txt, n, enc, dec = ('0123456', 5, '3041526', '0123456')
txt, n, enc, dec = ('0123456', 6, '0123456', '0123456')
txt, n, enc, dec = ('0123456', 7, '1350246', '0123456')
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>Lets cut the chaff and:</p>
<ul><li>Just show data when the encoding == the input text.</li><li>The txt could be any string without repeats - lets show the length of the string, assuming that it has no repeats</li></ul>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="kn">from</span> <span class="nn">string</span> <span class="kn">import</span> <span class="n">ascii_letters</span><span class="p">,</span> <span class="n">digits</span>
<span class="n">VALID_CHARS</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">digits</span><span class="si">}{</span><span class="n">ascii_letters</span><span class="si">}</span><span class="s2">"</span>
<span class="k">def</span> <span class="nf">show_when_encrypt_equals_input</span><span class="p">(</span><span class="n">encrypter</span><span class="p">,</span> <span class="n">decrypter</span><span class="p">,</span> <span class="n">nmax</span><span class="o">=</span><span class="mi">16</span><span class="p">):</span>
<span class="s2">"Show n when encrypter(txt, n) == txt, for longer txt strings and range of n."</span>
<span class="k">for</span> <span class="n">lentxt</span><span class="p">,</span> <span class="n">txt</span> <span class="ow">in</span> <span class="p">((</span><span class="n">i</span><span class="p">,</span> <span class="n">VALID_CHARS</span><span class="p">[:</span><span class="n">i</span><span class="p">])</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">VALID_CHARS</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)):</span>
<span class="nb">print</span><span class="p">()</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">txt</span><span class="p">),</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">encrypter</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="k">if</span> <span class="n">enc</span> <span class="o">==</span> <span class="n">txt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"enc == txt when: </span><span class="si">{</span><span class="n">lentxt</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="si">= }</span><span class="s2">"</span><span class="p">)</span>
<span class="n">show_when_encrypt_equals_input</span><span class="p">(</span><span class="n">encrypt</span><span class="p">,</span> <span class="n">decrypt</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>enc == txt when: lentxt, n = (0, 0)
enc == txt when: lentxt, n = (0, 1)
enc == txt when: lentxt, n = (0, 2)
enc == txt when: lentxt, n = (0, 3)
enc == txt when: lentxt, n = (0, 4)
enc == txt when: lentxt, n = (1, 0)
enc == txt when: lentxt, n = (1, 1)
enc == txt when: lentxt, n = (1, 2)
enc == txt when: lentxt, n = (1, 3)
enc == txt when: lentxt, n = (1, 4)
enc == txt when: lentxt, n = (2, 0)
enc == txt when: lentxt, n = (2, 2)
enc == txt when: lentxt, n = (2, 4)
enc == txt when: lentxt, n = (2, 6)
enc == txt when: lentxt, n = (2, 8)
enc == txt when: lentxt, n = (3, 0)
enc == txt when: lentxt, n = (3, 2)
enc == txt when: lentxt, n = (3, 4)
enc == txt when: lentxt, n = (3, 6)
enc == txt when: lentxt, n = (3, 8)
enc == txt when: lentxt, n = (3, 10)
enc == txt when: lentxt, n = (3, 12)
enc == txt when: lentxt, n = (4, 0)
enc == txt when: lentxt, n = (4, 4)
enc == txt when: lentxt, n = (4, 8)
enc == txt when: lentxt, n = (4, 12)
enc == txt when: lentxt, n = (4, 16)
enc == txt when: lentxt, n = (5, 0)
enc == txt when: lentxt, n = (5, 4)
enc == txt when: lentxt, n = (5, 8)
enc == txt when: lentxt, n = (5, 12)
enc == txt when: lentxt, n = (5, 16)
enc == txt when: lentxt, n = (5, 20)
enc == txt when: lentxt, n = (6, 0)
enc == txt when: lentxt, n = (6, 3)
enc == txt when: lentxt, n = (6, 6)
enc == txt when: lentxt, n = (6, 9)
enc == txt when: lentxt, n = (6, 12)
enc == txt when: lentxt, n = (6, 15)
enc == txt when: lentxt, n = (6, 18)
enc == txt when: lentxt, n = (6, 21)
enc == txt when: lentxt, n = (6, 24)
enc == txt when: lentxt, n = (7, 0)
enc == txt when: lentxt, n = (7, 3)
enc == txt when: lentxt, n = (7, 6)
enc == txt when: lentxt, n = (7, 9)
enc == txt when: lentxt, n = (7, 12)
enc == txt when: lentxt, n = (7, 15)
enc == txt when: lentxt, n = (7, 18)
enc == txt when: lentxt, n = (7, 21)
enc == txt when: lentxt, n = (7, 24)
enc == txt when: lentxt, n = (7, 27)
enc == txt when: lentxt, n = (8, 0)
enc == txt when: lentxt, n = (8, 6)
enc == txt when: lentxt, n = (8, 12)
enc == txt when: lentxt, n = (8, 18)
enc == txt when: lentxt, n = (8, 24)
enc == txt when: lentxt, n = (8, 30)
enc == txt when: lentxt, n = (9, 0)
enc == txt when: lentxt, n = (9, 6)
enc == txt when: lentxt, n = (9, 12)
enc == txt when: lentxt, n = (9, 18)
enc == txt when: lentxt, n = (9, 24)
enc == txt when: lentxt, n = (9, 30)
enc == txt when: lentxt, n = (9, 36)
enc == txt when: lentxt, n = (10, 0)
enc == txt when: lentxt, n = (10, 10)
enc == txt when: lentxt, n = (10, 20)
enc == txt when: lentxt, n = (10, 30)
enc == txt when: lentxt, n = (10, 40)
...</pre><pre>enc == txt when: lentxt, n = (62, 162)
enc == txt when: lentxt, n = (62, 168)
enc == txt when: lentxt, n = (62, 174)
enc == txt when: lentxt, n = (62, 180)
enc == txt when: lentxt, n = (62, 186)
enc == txt when: lentxt, n = (62, 192)
enc == txt when: lentxt, n = (62, 198)
enc == txt when: lentxt, n = (62, 204)
enc == txt when: lentxt, n = (62, 210)
enc == txt when: lentxt, n = (62, 216)
enc == txt when: lentxt, n = (62, 222)
enc == txt when: lentxt, n = (62, 228)
enc == txt when: lentxt, n = (62, 234)
enc == txt when: lentxt, n = (62, 240)
enc == txt when: lentxt, n = (62, 246)
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>It seems that for each size of input txt, <code>encoding == input when n % some_delta == 0</code></p>
<h3 id="Lets-find-those-deltas">Lets find those deltas</h3>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="kn">from</span> <span class="nn">pprint</span> <span class="kn">import</span> <span class="n">pp</span>
<span class="k">def</span> <span class="nf">show_when_encrypt_equals_input2</span><span class="p">(</span><span class="n">encrypter</span><span class="p">,</span> <span class="n">decrypter</span><span class="p">,</span> <span class="n">nmax</span><span class="o">=</span><span class="mi">16</span><span class="p">):</span>
<span class="s2">"Show n when encrypter(txt, n) == txt, for longer txt strings and range of n."</span>
<span class="k">for</span> <span class="n">lentxt</span><span class="p">,</span> <span class="n">txt</span> <span class="ow">in</span> <span class="p">((</span><span class="n">i</span><span class="p">,</span> <span class="n">VALID_CHARS</span><span class="p">[:</span><span class="n">i</span><span class="p">])</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">VALID_CHARS</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)):</span>
<span class="n">last</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">txt</span><span class="p">),</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">encrypter</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="k">if</span> <span class="n">enc</span> <span class="o">==</span> <span class="n">txt</span><span class="p">:</span>
<span class="n">diff</span><span class="p">,</span> <span class="n">last</span> <span class="o">=</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="n">last</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="k">if</span> <span class="n">last</span> <span class="k">else</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="k">yield</span> <span class="n">lentxt</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">diff</span>
<span class="n">pp</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">show_when_encrypt_equals_input2</span><span class="p">(</span><span class="n">encrypt</span><span class="p">,</span> <span class="n">decrypt</span><span class="p">)))</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>[(0, 0, 0),
(0, 1, 1),
(0, 2, 1),
(0, 3, 1),
(0, 4, 1),
(1, 0, 0),
(1, 1, 1),
(1, 2, 1),
(1, 3, 1),
(1, 4, 1),
(2, 0, 0),
(2, 2, 2),
(2, 4, 2),
(2, 6, 2),
(2, 8, 2),
(3, 0, 0),
(3, 2, 2),
(3, 4, 2),
(3, 6, 2),
(3, 8, 2),
(3, 10, 2),
(3, 12, 2),
(4, 0, 0),
(4, 4, 4),
(4, 8, 4),
(4, 12, 4),
(4, 16, 4),
(5, 0, 0),
(5, 4, 4),
(5, 8, 4),
(5, 12, 4),
(5, 16, 4),
(5, 20, 4),
(6, 0, 0),
(6, 3, 3),
(6, 6, 3),
(6, 9, 3),
(6, 12, 3),
(6, 15, 3),
(6, 18, 3),
(6, 21, 3),
(6, 24, 3),
(7, 0, 0),
(7, 3, 3),
(7, 6, 3),
(7, 9, 3),
(7, 12, 3),
(7, 15, 3),
(7, 18, 3),
(7, 21, 3),
(7, 24, 3),
(7, 27, 3),
(8, 0, 0),
(8, 6, 6),
(8, 12, 6),
(8, 18, 6),
(8, 24, 6),
(8, 30, 6),
(9, 0, 0),
(9, 6, 6),
(9, 12, 6),
(9, 18, 6),
(9, 24, 6),
(9, 30, 6),
(9, 36, 6),
(10, 0, 0),
(10, 10, 10),
(10, 20, 10),
(10, 30, 10),
(10, 40, 10),
(11, 0, 0),
(11, 10, 10),
(11, 20, 10),
...</pre><pre> (62, 150, 6),
(62, 156, 6),
(62, 162, 6),
(62, 168, 6),
(62, 174, 6),
(62, 180, 6),
(62, 186, 6),
(62, 192, 6),
(62, 198, 6),
(62, 204, 6),
(62, 210, 6),
(62, 216, 6),
(62, 222, 6),
(62, 228, 6),
(62, 234, 6),
(62, 240, 6),
(62, 246, 6)]
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<h4 id="Lets-get-those-diffs-w.r.t.-the-string-length">Lets get those diffs w.r.t. the string length</h4>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="n">lentxt2delta</span> <span class="o">=</span> <span class="p">{</span><span class="n">lentxt</span><span class="p">:</span> <span class="n">delta</span>
<span class="k">for</span> <span class="n">lentxt</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">delta</span> <span class="ow">in</span> <span class="n">show_when_encrypt_equals_input2</span><span class="p">(</span><span class="n">encrypt</span><span class="p">,</span> <span class="n">decrypt</span><span class="p">)}</span>
<span class="n">lentxt2delta</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>{0: 1,
1: 1,
2: 2,
3: 2,
4: 4,
5: 4,
6: 3,
7: 3,
8: 6,
9: 6,
10: 10,
11: 10,
12: 12,
13: 12,
14: 4,
15: 4,
16: 8,
17: 8,
18: 18,
19: 18,
20: 6,
21: 6,
22: 11,
23: 11,
...
53: 52,
54: 20,
55: 20,
56: 18,
57: 18,
58: 58,
59: 58,
60: 60,
61: 60,
62: 6}</pre>
</div>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">lentxt2delta</span><span class="o">.</span><span class="n">values</span><span class="p">()))</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>[1, 1, 2, 2, 4, 4, 3, 3, 6, 6, 10, 10, 12, 12, 4, 4, 8, 8, 18, 18, 6, 6, 11, 11, 20, 20, 18, 18, 28, 28, 5, 5, 10, 10, 12, 12, 36, 36, 12, 12, 20, 20, 14, 14, 12, 12, 23, 23, 21, 21, 8, 8, 52, 52, 20, 20, 18, 18, 58, 58, 60, 60, 6]
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>I don't recognise that series. let's look it up</p>
<h3 id="OEIS-series-lookup"><a href="https://oeis.org/">OEIS</a> series lookup</h3><p>Well lets get the output in OEIS form to create a search:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s1">','</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">lentxt2delta</span><span class="o">.</span><span class="n">values</span><span class="p">())[:</span><span class="mi">12</span><span class="p">]))</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>1,1,2,2,4,4,3,3,6,6,10,10
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>That <a href="https://oeis.org/search?q=1%2C1%2C2%2C2%2C4%2C4%2C3%2C3%2C6%2C6%2C10%2C10&language=english&go=Search">search</a> found nothing.</p>
<p>What about removing every other value and doing another search?</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s1">','</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">lentxt2delta</span><span class="o">.</span><span class="n">values</span><span class="p">())[:</span><span class="mi">24</span><span class="p">:</span><span class="mi">2</span><span class="p">]))</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>1,2,4,3,6,10,12,4,8,18,6,11
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>Ha, we have <a href="https://oeis.org/search?q=1%2C2%2C4%2C3%2C6%2C10%2C12%2C4%2C8%2C18%2C6%2C11&language=english&go=Search">one match</a> to A002326., (and subsequent values match our series too).</p>
<h3 id="A002326">A002326</h3><blockquote>
<p><b>least m > 0 such that 2n+1 divides 2^m-1</b></p>
</blockquote>
<ul><li>No code given, but how hard could coding it be :-)</li></ul>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">a002326</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #ce9178;">"Least m > 0 such that 2n+1 divides 2^m-1"</span></div><div><span style="color: #cccccc;"> n21 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;"> </span><span style="color: #d4d4d4;">*</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #cccccc;"> m, m2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">while</span><span style="color: #cccccc;"> (m2 </span><span style="color: #d4d4d4;">-</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">) </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> n21:</span></div><div><span style="color: #cccccc;"> m, m2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> m</span><span style="color: #d4d4d4;">+</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, m2 </span><span style="color: #d4d4d4;">*</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> m</span></div><br /><div><span style="color: #cccccc;">[a002326(x) </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">15</span><span style="color: #cccccc;">)]</span></div></div><pre>[1, 2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, 20, 18, 28]</pre></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<h2 id="New-en/decrpters-using-a002326">New en/decrpters using a002326</h2><h3 id="Why?">Why?</h3><p>Well for a string length of 62 we could need inner loops of no more than 6 iterations - no matter how large <code>n</code> is!</p>
<p>It also seems that <code>a002326(x) <= 2x, for x > 0</code>. I haven't proven that, but lets show it for up to some large string length:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1_000</span><span class="p">)</span> <span class="k">if</span> <span class="n">a002326</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">></span> <span class="n">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>[]</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<h3 id="How?">How?</h3><p>Well,</p>
<ul><li>a002326 needs to be applied to the length of the input string <code>s</code>.<ul><li>Not quite, as, remember, there was an initial doubling of diff values so lencth might need multiplying or dividing by 2.</li></ul>
</li></ul>
<p>Some playing around its a divide by 2 and we get:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs "><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">encrypt2</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> a002326(l2)):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">] </span><span style="color: #d4d4d4;">+</span><span style="color: #cccccc;"> s[</span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">::</span><span style="color: #b5cea8;">2</span><span style="color: #cccccc;">]</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">decrypt2</span><span style="color: #cccccc;">(</span><span style="color: #9cdcfe;">s</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">, </span><span style="color: #9cdcfe;">n</span><span style="color: #cccccc;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #cccccc;">) -> </span><span style="color: #4ec9b0;">str</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #569cd6;">not</span><span style="color: #cccccc;"> s </span><span style="color: #569cd6;">or</span><span style="color: #cccccc;"> n </span><span style="color: #d4d4d4;"><=</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">0</span><span style="color: #cccccc;">:</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><div><span style="color: #cccccc;"> l2 </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">len</span><span style="color: #cccccc;">(s) </span><span style="color: #d4d4d4;">//</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(n </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> a002326(l2)):</span></div><div><span style="color: #cccccc;"> s </span><span style="color: #d4d4d4;">=</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">.join(x)</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> x </span><span style="color: #c586c0;">in</span></div><div><span style="color: #cccccc;"> zip_longest(s[l2:], s[:l2],</span></div><div><span style="color: #cccccc;"> </span><span style="color: #9cdcfe;">fillvalue</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">''</span><span style="color: #cccccc;">))</span></div><div><span style="color: #cccccc;"> </span><span style="color: #c586c0;">return</span><span style="color: #cccccc;"> s</span></div><br /><br /></div><pre></pre></div>
</div>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="k">def</span> <span class="nf">test_new_v_old</span><span class="p">():</span>
<span class="n">fails</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">testcases</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">txt</span> <span class="ow">in</span> <span class="p">[</span><span class="n">VALID_CHARS</span><span class="p">[:</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">VALID_CHARS</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)]:</span>
<span class="c1"># print()</span>
<span class="n">lentxt</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">txt</span><span class="p">)</span>
<span class="n">last</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">txt</span><span class="p">),</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="n">testcases</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">encrypt</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="n">dec</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="n">enc2</span> <span class="o">=</span> <span class="n">encrypt2</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="n">dec2</span> <span class="o">=</span> <span class="n">decrypt2</span><span class="p">(</span><span class="n">enc2</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="k">if</span> <span class="n">enc</span> <span class="o">!=</span> <span class="n">enc2</span> <span class="ow">or</span> <span class="n">dec</span> <span class="o">!=</span> <span class="n">dec2</span><span class="p">:</span>
<span class="n">fails</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">txt</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">enc</span><span class="p">,</span> <span class="n">enc2</span><span class="p">,</span> <span class="n">dec</span><span class="p">,</span> <span class="n">dec2</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="se">\n</span><span class="si">{</span><span class="n">testcases</span><span class="p">,</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="n">fails</span><span class="p">)</span><span class="w"> </span><span class="si">= }</span><span class="s2">"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">fails</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'</span><span class="se">\n</span><span class="s1"> encode2() == encode() and decode2() == decode()'</span><span class="p">)</span>
<span class="o">%</span><span class="k">time</span> test_new_v_old()
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>testcases, len(fails) = (9833, 0)
encode2() == encode() and decode2() == decode()
CPU times: total: 4.23 s
Wall time: 4.34 s
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput " data-mime-type="text/markdown">
<p>Because of our testcases being a string of a particular length being encoded and decode with many values on <code>N</code> lets cache a002326</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">cache</span>
<span class="nd">@cache</span>
<span class="k">def</span> <span class="nf">a002326</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
<span class="s2">"Least m > 0 such that 2n+1 divides 2^m-1"</span>
<span class="n">n21</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">m</span><span class="p">,</span> <span class="n">m2</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span>
<span class="k">while</span> <span class="p">(</span><span class="n">m2</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">n21</span><span class="p">:</span>
<span class="n">m</span><span class="p">,</span> <span class="n">m2</span> <span class="o">=</span> <span class="n">m</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">m2</span> <span class="o">*</span> <span class="mi">2</span>
<span class="k">return</span> <span class="n">m</span>
<span class="o">%</span><span class="k">time</span> test_new_v_old()
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>testcases, len(fails) = (9833, 0)
encode2() == encode() and decode2() == decode()
CPU times: total: 4.31 s
Wall time: 4.47 s
</pre>
</div>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="n">a002326</span><span class="o">.</span><span class="n">cache_info</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>CacheInfo(hits=19498, misses=32, maxsize=None, currsize=32)</pre>
</div>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span><span class="o">%</span><span class="k">time</span> test_new_v_old()
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell "><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>testcases, len(fails) = (9833, 0)
encode2() == encode() and decode2() == decode()
CPU times: total: 4.23 s
Wall time: 4.45 s
</pre>
</div>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs ">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs "><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class=" highlight hl-ipython3"><pre><span></span>
</pre></div>
</div>
</div>
</div>
</div>
</div>
Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-41005181712165880822023-05-14T14:57:00.002+01:002023-05-15T05:22:03.473+01:00Pythons chained conditional expressions (and Fizz Buzz)<p style="text-align: left;"> I was lurking on the LinkedIn Python forum and someone brought up FizzBuzz again and I thought; "Why not try a solution that closely follows the description"</p><p style="text-align: left;">The description given was:</p><p style="text-align: left;"><span color="rgba(0, 0, 0, 0.9)" face="-apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Fira Sans", Ubuntu, Oxygen, "Oxygen Sans", Cantarell, "Droid Sans", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Lucida Grande", Helvetica, Arial, sans-serif" style="-webkit-text-stroke-width: 0px; background-color: white; display: inline; float: none; font-size: 14px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><i></i></span></p><blockquote style="text-align: left;"><i><span style="background-color: #ffe599;">Print a range of numbers from 1 to 100 but for multiples of 3, print "Fizz", for multiples of 5 print "Buzz" and for multiples of both 3 and 5 print "FizzBuzz".</span></i></blockquote><div style="text-align: left;"><h3><span>Common factors </span></h3></div><h3 style="text-align: left;"><span></span></h3><p style="text-align: left;"></p><p style="text-align: left;"><span color="rgba(0, 0, 0, 0.9)" face="-apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Fira Sans", Ubuntu, Oxygen, "Oxygen Sans", Cantarell, "Droid Sans", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Lucida Grande", Helvetica, Arial, sans-serif" style="-webkit-text-stroke-width: 0px; background-color: white; display: inline; float: none; font-size: 14px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span>You know that because fifteen is a factor of three and five,but three and five share no common factors you can test first for fifteen then for either three or five, Since three is mentioned first I chose to test in order of fifteen, three then five.</span></span></p><p style="text-align: left;"><span color="rgba(0, 0, 0, 0.9)" face="-apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Fira Sans", Ubuntu, Oxygen, "Oxygen Sans", Cantarell, "Droid Sans", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Lucida Grande", Helvetica, Arial, sans-serif" style="-webkit-text-stroke-width: 0px; background-color: white; display: inline; float: none; font-size: 14px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span>We are already moving away from the textual description out of necessity. <br /></span></span></p><h3 style="text-align: left;">
Pythons chained conditional expressions
</h3><p style="text-align: left;"><span color="rgba(0, 0, 0, 0.9)" face="-apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Fira Sans", Ubuntu, Oxygen, "Oxygen Sans", Cantarell, "Droid Sans", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Lucida Grande", Helvetica, Arial, sans-serif" style="-webkit-text-stroke-width: 0px; background-color: white; display: inline; float: none; font-size: 14px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span>Hiding in plain site of <a href="https://docs.python.org/3/reference/expressions.html#conditional-expressions" rel="nofollow" target="_blank">the docs</a> you have:</span></span></p><pre id="index-88" style="-webkit-text-stroke-width: 0px; background-color: #eeffcc; border-radius: 3px; border: 1px solid rgb(170, 204, 153); clear: both; color: #333333; font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Liberation Mono", Menlo, Monaco, Consolas, monospace; font-size: 15.44px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; line-height: 18.528px; orphans: 2; overflow: auto hidden; padding: 5px; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px;"><b id="grammar-token-python-grammar-conditional_expression">conditional_expression</b> ::= <a class="reference internal" href="https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-or_test" style="color: #0072aa; text-decoration: none;"><code class="xref docutils literal notranslate" style="background-color: transparent; border-radius: 3px; font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Liberation Mono", Menlo, Monaco, Consolas, monospace; font-size: 14.8996px; font-weight: normal; padding: 0px 1px;"><span class="pre" style="hyphens: none; white-space: nowrap;">or_test</span></code></a> ["if" <a class="reference internal" href="https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-or_test" style="color: #0072aa; text-decoration: none;"><code class="xref docutils literal notranslate" style="background-color: transparent; border-radius: 3px; font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Liberation Mono", Menlo, Monaco, Consolas, monospace; font-size: 14.8996px; font-weight: normal; padding: 0px 1px;"><span class="pre" style="hyphens: none; white-space: nowrap;">or_test</span></code></a> "else" <a class="reference internal" href="https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-expression" style="color: #0072aa; text-decoration: none;"><code class="xref docutils literal notranslate" style="background-color: transparent; border-radius: 3px; font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Liberation Mono", Menlo, Monaco, Consolas, monospace; font-size: 14.8996px; font-weight: normal; padding: 0px 1px;"><span class="pre" style="hyphens: none; white-space: nowrap;">expression</span></code></a>]
<b id="grammar-token-python-grammar-expression"><span id="grammar-token-expression"></span>expression </b> ::= <a class="reference internal" href="https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-conditional_expression" style="color: #0072aa; text-decoration: none;"><code class="xref docutils literal notranslate" style="background-color: transparent; border-radius: 3px; font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Liberation Mono", Menlo, Monaco, Consolas, monospace; font-size: 14.8996px; font-weight: normal; padding: 0px 1px;"><span class="pre" style="hyphens: none; white-space: nowrap;">conditional_expression</span></code></a> | <a class="reference internal" href="https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-lambda_expr" style="color: #0072aa; text-decoration: none;"><code class="xref docutils literal notranslate" style="background-color: transparent; border-radius: 3px; font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Liberation Mono", Menlo, Monaco, Consolas, monospace; font-size: 14.8996px; font-weight: normal; padding: 0px 1px;"><span class="pre" style="hyphens: none; white-space: nowrap;">lambda_expr</span></code></a></pre><p style="text-align: left;"><u><i>The else part of a conditional expression can itself be a conditional expression!</i></u></p><p style="text-align: left;">For FizzBuzz there are multiple conditions. Let's try stringing them along, and I will use F and B instead of Fizz and Buzz to better show the chained conditional expression:</p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; text-align: left; white-space: pre;"><div><span style="color: #c586c0;">for</span><span style="color: #cccccc;"> i </span><span style="color: #569cd6;">in</span><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">range</span><span style="color: #cccccc;">(</span><span style="color: #b5cea8;">1</span><span style="color: #cccccc;">, </span><span style="color: #b5cea8;">101</span><span style="color: #cccccc;">):</span></div><div><span style="color: #cccccc;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #cccccc;">(</span><span style="color: #ce9178;">'FB'</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">not</span><span style="color: #cccccc;"> i </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">15</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'F'</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">not</span><span style="color: #cccccc;"> i </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">3</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;"> </span><span style="color: #ce9178;">'B'</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">if</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">not</span><span style="color: #cccccc;"> i </span><span style="color: #d4d4d4;">%</span><span style="color: #cccccc;"> </span><span style="color: #b5cea8;">5</span><span style="color: #cccccc;"> </span><span style="color: #c586c0;">else</span><span style="color: #cccccc;"> i)</span></div><div><span style="color: #6a9955;"><br /></span></div></div><p style="text-align: left;"></p><p style="text-align: left;">The corresponding English description has changed, but it should be straight-forward in its logic.</p><p style="text-align: left;">Here's a picture of this type of chained conditional expressions in action:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4abqxQSQI1CCBqdyN2VI37xLKNCsvLbU5LY3yEBO2ttCgzsGud9WGycTIULbTKURMVeY_yGUNN7IGgbvShGMEeLCyV8LjmMV2JAPxz2vzY8Dyp46zLdHUhrJj4SjOzD7MHoutMhfwFDF3JctXcb-p61-Av7j95xIMBNILLIsMpn8vMLMw7A/s696/chained_conditional_2023-05-14%20144322.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="648" data-original-width="696" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4abqxQSQI1CCBqdyN2VI37xLKNCsvLbU5LY3yEBO2ttCgzsGud9WGycTIULbTKURMVeY_yGUNN7IGgbvShGMEeLCyV8LjmMV2JAPxz2vzY8Dyp46zLdHUhrJj4SjOzD7MHoutMhfwFDF3JctXcb-p61-Av7j95xIMBNILLIsMpn8vMLMw7A/s320/chained_conditional_2023-05-14%20144322.png" width="320" /></a></div><br /><p style="text-align: left;"><b>END.<br /></b></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-37463226259859007242023-03-03T20:38:00.001+00:002023-03-03T20:43:01.419+00:00Function purity and idempotence<p> Someone mentioned <b><i>idempotence </i></b>at work. I looked it up and noted that it too is a property of functions, like function <b><i>purity</i></b>.</p><p>I decided to see if I could write functions with combinations of those properties and embedded tests for those properties.</p><p>Running my resultant program produces this result:</p><div style="background-color: white; color: black; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: black;">Created on Fri Mar </span><span style="color: #098658;">3</span><span style="color: black;"> </span><span style="color: #098658;">18</span><span style="color: black;">:</span><span style="color: #098658;">0</span><span style="color: #cd3131;">4</span><span style="color: black;">:</span><span style="color: #098658;">0</span><span style="color: #cd3131;">9</span><span style="color: black;"> </span><span style="color: #098658;">2023</span></div><br /><div><span style="color: #795e26;">@author</span><span style="color: #cd3131;">: Paddy3118</span></div><br /><div><span style="color: black;">pure_idempotent.py</span></div><br /><div><span style="color: black;"> Explores Purity </span><span style="color: blue;">and</span><span style="color: black;"> idempotence </span><span style="color: #af00db;">with</span><span style="color: black;"> Python examples</span></div><br /><br /><div><span style="color: black;">Definitions:</span></div><div><span style="color: black;"> Pure:</span></div><div><span style="color: black;"> * Answer relies solely on inputs. Same out </span><span style="color: #af00db;">for</span><span style="color: black;"> same </span><span style="color: blue;">in</span><span style="color: black;">.</span></div><div><span style="color: black;"> I.E: </span><span style="color: #cd3131;">`f(x) </span><span style="color: black;">==</span><span style="color: #cd3131;"> f(x) </span><span style="color: black;">==</span><span style="color: #cd3131;"> f(x) </span><span style="color: black;">==</span><span style="color: #cd3131;"> ...`</span></div><div><span style="color: black;"> * No side-effects.</span></div><br /><div><span style="color: black;"> Idempotent:</span></div><div><span style="color: black;"> * The first answer </span><span style="color: #af00db;">from</span><span style="color: black;"> </span><span style="color: #795e26;">any</span><span style="color: black;"> </span><span style="color: #795e26;">input</span><span style="color: black;">, </span><span style="color: #af00db;">if</span><span style="color: black;"> used </span><span style="color: #af00db;">as</span><span style="color: black;"> </span><span style="color: #795e26;">input</span><span style="color: black;"> to</span></div><div><span style="color: black;"> subsequent runs of the function, will </span><span style="color: #795e26;">all</span><span style="color: black;"> </span><span style="color: #af00db;">yield</span><span style="color: black;"> the same answer.</span></div><div><span style="color: black;"> I.E: </span><span style="color: #cd3131;">`f(x) </span><span style="color: black;">==</span><span style="color: #cd3131;"> f(f(x)) </span><span style="color: black;">==</span><span style="color: #cd3131;"> f(f(f(x))) </span><span style="color: black;">==</span><span style="color: #cd3131;"> ...`</span></div><div><span style="color: black;"> * Any side effect of a first function execution </span><span style="color: blue;">is</span><span style="color: black;"> *preserved* on</span></div><div><span style="color: black;"> subsequent runs of the function using the previous answer.</span></div><br /><div><span style="color: black;"> Side effect:</span></div><div><span style="color: black;"> * A function </span><span style="color: blue;">is</span><span style="color: black;"> said to have side effects </span><span style="color: #af00db;">if</span><span style="color: black;"> it relies apon </span><span style="color: blue;">or</span><span style="color: black;"> modifies</span></div><div><span style="color: black;"> state outside of that *given* by its arguments. Modifying mutable</span></div><div><span style="color: black;"> arguments </span><span style="color: blue;">is</span><span style="color: black;"> also a side effect.</span></div><br /><br /><div><span style="color: green;">#--------</span></div><br /><div><span style="color: blue;">def</span><span style="color: black;"> </span><span style="color: #795e26;">remove_twos</span><span style="color: black;">(</span><span style="color: #001080;">arg</span><span style="color: black;">: list[</span><span style="color: #267f99;">int</span><span style="color: black;">]) -> list[</span><span style="color: #267f99;">int</span><span style="color: black;">]:</span></div><div><span style="color: black;"> </span><span style="color: #a31515;">"Returns a copy of the list with all twos removed."</span></div><div><span style="color: black;"> </span><span style="color: #af00db;">return</span><span style="color: black;"> [x </span><span style="color: #af00db;">for</span><span style="color: black;"> x </span><span style="color: blue;">in</span><span style="color: black;"> arg </span><span style="color: #af00db;">if</span><span style="color: black;"> x != </span><span style="color: #098658;">2</span><span style="color: black;">]</span></div><br /><div><span style="color: black;">Function </span><span style="color: #af00db;">is</span><span style="color: black;">:</span></div><div><span style="color: black;"> Pure</span></div><div><span style="color: black;"> Idempotent</span></div><br /><div><span style="color: green;">#--------</span></div><br /><div><span style="color: blue;">def</span><span style="color: black;"> </span><span style="color: #795e26;">return_first_int</span><span style="color: black;">(</span><span style="color: #001080;">arg</span><span style="color: black;">: </span><span style="color: #267f99;">int</span><span style="color: black;">) -> </span><span style="color: #267f99;">int</span><span style="color: black;">:</span></div><div><span style="color: black;"> </span><span style="color: #a31515;">"Return the int given in its first call"</span></div><div><span style="color: black;"> </span><span style="color: blue;">global</span><span style="color: black;"> external_state</span></div><br /><div><span style="color: black;"> </span><span style="color: #af00db;">if</span><span style="color: black;"> external_state </span><span style="color: blue;">is</span><span style="color: black;"> </span><span style="color: blue;">None</span><span style="color: black;">:</span></div><div><span style="color: black;"> external_state = arg</span></div><div><span style="color: black;"> </span><span style="color: #af00db;">return</span><span style="color: black;"> external_state</span></div><br /><div><span style="color: black;">Function </span><span style="color: #af00db;">is</span><span style="color: black;">:</span></div><div><span style="color: black;"> Impure! External state changed</span></div><div><span style="color: black;"> Idempotent</span></div><br /><div><span style="color: green;">#--------</span></div><br /><div><span style="color: blue;">def</span><span style="color: black;"> </span><span style="color: #795e26;">plus_one</span><span style="color: black;">(</span><span style="color: #001080;">arg</span><span style="color: black;">: </span><span style="color: #267f99;">int</span><span style="color: black;">) -> </span><span style="color: #267f99;">int</span><span style="color: black;">:</span></div><div><span style="color: black;"> </span><span style="color: #a31515;">"Add one to arg"</span></div><div><span style="color: black;"> </span><span style="color: #af00db;">return</span><span style="color: black;"> arg + </span><span style="color: #098658;">1</span></div><br /><div><span style="color: black;">Function </span><span style="color: #af00db;">is</span><span style="color: black;">:</span></div><div><span style="color: black;"> Pure</span></div><div><span style="color: black;"> Non-idempotent! Output changes </span><span style="color: #af00db;">for</span><span style="color: black;"> nested calls</span></div><br /><div><span style="color: green;">#--------</span></div><br /><div><span style="color: blue;">def</span><span style="color: black;"> </span><span style="color: #795e26;">epoc_plus_seconds</span><span style="color: black;">(</span><span style="color: #001080;">secs</span><span style="color: black;">: </span><span style="color: #267f99;">float</span><span style="color: black;">) -> </span><span style="color: #267f99;">float</span><span style="color: black;">:</span></div><div><span style="color: black;"> </span><span style="color: #a31515;">"Return time since epoch + seconds"</span></div><div><span style="color: black;"> time.sleep(</span><span style="color: #098658;">0.1</span><span style="color: black;">)</span></div><div><span style="color: black;"> </span><span style="color: #af00db;">return</span><span style="color: black;"> time.time() + secs</span></div><br /><div><span style="color: black;">Function </span><span style="color: #af00db;">is</span><span style="color: black;">:</span></div><div><span style="color: black;"> Impure! Output changes </span><span style="color: #af00db;">for</span><span style="color: black;"> same </span><span style="color: #795e26;">input</span></div><div><span style="color: black;"> Non-idempotent! Output changes </span><span style="color: #af00db;">for</span><span style="color: black;"> nested calls</span></div><br /><h3 style="text-align: left;">Code</h3></div><p></p><p>The code that produces the above (but not its arbitrary colourising), is the following:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Created on Fri Mar 3 18:04:09 2023</span></div><br /><div><span style="color: #ce9178;">@author: Paddy3118</span></div><br /><div><span style="color: #ce9178;">pure_idempotent.py</span></div><br /><div><span style="color: #ce9178;"> Explores Purity and idempotence with Python examples</span></div><br /><br /><div><span style="color: #ce9178;">Definitions:</span></div><div><span style="color: #ce9178;"> Pure:</span></div><div><span style="color: #ce9178;"> * Answer relies solely on inputs. Same out for same in.</span></div><div><span style="color: #ce9178;"> I.E: `f(x) == f(x) == f(x) == ...`</span></div><div><span style="color: #ce9178;"> * No side-effects.</span></div><br /><div><span style="color: #ce9178;"> Idempotent:</span></div><div><span style="color: #ce9178;"> * The first answer from any input, if used as input to</span></div><div><span style="color: #ce9178;"> subsequent runs of the function, will all yield the same answer.</span></div><div><span style="color: #ce9178;"> I.E: `f(x) == f(f(x)) == f(f(f(x))) == ...`</span></div><div><span style="color: #ce9178;"> * Any side effect of a first function execution is *preserved* on</span></div><div><span style="color: #ce9178;"> subsequent runs of the function using the previous answer.</span></div><br /><div><span style="color: #ce9178;"> Side effect:</span></div><div><span style="color: #ce9178;"> * A function is said to have side effects if it relies apon or modifies</span></div><div><span style="color: #ce9178;"> state outside of that *given* by its arguments. Modifying mutable</span></div><div><span style="color: #ce9178;"> arguments is also a side effect.</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #c586c0;">import</span><span style="color: #d4d4d4;"> inspect</span></div><br /><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">__doc__</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #6a9955;"># %% Pure, idempotent.</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">#--------'</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">remove_twos</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">arg</span><span style="color: #d4d4d4;">: list[</span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">]) -> list[</span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Returns a copy of the list with all twos removed."</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> [x </span><span style="color: #c586c0;">for</span><span style="color: #d4d4d4;"> x </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> arg </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> x != </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">]</span></div><br /><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">\n</span><span style="color: #569cd6;">{</span><span style="color: #d4d4d4;">inspect.getsource(remove_twos)</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">arg0 = [</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">]</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Function is:'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Pure'</span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> remove_twos(arg0.copy()) == remove_twos(arg0.copy())</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">' Impure'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Idempotent'</span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (answer1:=remove_twos(arg0)) == remove_twos(answer1)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">'Non-idempotent'</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #6a9955;"># %% Impure, idempotent.</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">#--------'</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">return_first_int</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">arg</span><span style="color: #d4d4d4;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Return the int given in its first call"</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">global</span><span style="color: #d4d4d4;"> external_state</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> external_state </span><span style="color: #569cd6;">is</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> external_state = arg</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> external_state</span></div><br /><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">\n</span><span style="color: #569cd6;">{</span><span style="color: #d4d4d4;">inspect.getsource(return_first_int)</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #6a9955;"># Purity</span></div><div><span style="color: #d4d4d4;">external_state = initial_state = </span><span style="color: #569cd6;">None</span></div><div><span style="color: #d4d4d4;">arg0 = </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;">same_output = (return_first_int(arg0)) == return_first_int(arg0)</span></div><div><span style="color: #d4d4d4;">same_state = external_state == initial_state</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Function is:'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> same_output </span><span style="color: #569cd6;">and</span><span style="color: #d4d4d4;"> same_state:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Pure'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">not</span><span style="color: #d4d4d4;"> same_output:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Impure! Output changes for same input'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">not</span><span style="color: #d4d4d4;"> same_state:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Impure! External state changed'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #6a9955;"># Idempotence</span></div><div><span style="color: #d4d4d4;">external_state = </span><span style="color: #569cd6;">None</span></div><div><span style="color: #d4d4d4;">answer1, state1 = return_first_int(arg0), external_state</span></div><div><span style="color: #d4d4d4;">answer2, state2 = return_first_int(answer1), external_state</span></div><div><span style="color: #d4d4d4;">same_output = answer1 == answer2</span></div><div><span style="color: #d4d4d4;">same_state = state1 == state2</span></div><div><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> same_output </span><span style="color: #569cd6;">and</span><span style="color: #d4d4d4;"> same_state:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Idempotent'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">not</span><span style="color: #d4d4d4;"> same_output:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Non-idempotent! Output changes for nested calls'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">not</span><span style="color: #d4d4d4;"> same_state:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Non-idempotent! External state changes for nested calls'</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #6a9955;"># %% Pure, non-idempotent.</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">#--------'</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">plus_one</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">arg</span><span style="color: #d4d4d4;">: </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">) -> </span><span style="color: #4ec9b0;">int</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Add one to arg"</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> arg + </span><span style="color: #b5cea8;">1</span></div><br /><br /><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">\n</span><span style="color: #569cd6;">{</span><span style="color: #d4d4d4;">inspect.getsource(plus_one)</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #6a9955;"># Purity</span></div><div><span style="color: #d4d4d4;">arg0 = </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;">same_output = (plus_one(arg0)) == plus_one(arg0)</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Function is:'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> same_output:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Pure'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Impure! Output changes for same input'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #6a9955;"># Idempotence</span></div><div><span style="color: #d4d4d4;">answer1 = plus_one(arg0)</span></div><div><span style="color: #d4d4d4;">answer2 = plus_one(answer1)</span></div><div><span style="color: #d4d4d4;">same_output = answer1 == answer2</span></div><div><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> same_output:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Idempotent'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Non-idempotent! Output changes for nested calls'</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #6a9955;"># %% Impure, non-idempotent.</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">#--------'</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #c586c0;">import</span><span style="color: #d4d4d4;"> time</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">epoc_plus_seconds</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">secs</span><span style="color: #d4d4d4;">: </span><span style="color: #4ec9b0;">float</span><span style="color: #d4d4d4;">) -> </span><span style="color: #4ec9b0;">float</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Return time since epoch + seconds"</span></div><div><span style="color: #d4d4d4;"> time.sleep(</span><span style="color: #b5cea8;">0.1</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> time.time() + secs</span></div><br /><br /><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">\n</span><span style="color: #569cd6;">{</span><span style="color: #d4d4d4;">inspect.getsource(epoc_plus_seconds)</span><span style="color: #569cd6;">}</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #6a9955;"># Purity</span></div><div><span style="color: #d4d4d4;">arg0 = </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;">same_output = (epoc_plus_seconds(arg0)) == epoc_plus_seconds(arg0)</span></div><div><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">'Function is:'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> same_output:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Pure'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Impure! Output changes for same input'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #6a9955;"># Idempotence</span></div><div><span style="color: #d4d4d4;">answer1 = epoc_plus_seconds(arg0)</span></div><div><span style="color: #d4d4d4;">answer2 = epoc_plus_seconds(answer1)</span></div><div><span style="color: #d4d4d4;">same_output = answer1 == answer2</span></div><div><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> same_output:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Idempotent'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">print</span><span style="color: #d4d4d4;">(</span><span style="color: #ce9178;">' Non-idempotent! Output changes for nested calls'</span><span style="color: #d4d4d4;">)</span></div><br /><br /></div><p></p><p><b>END</b>.<br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-29944972209503872362023-02-26T12:39:00.003+00:002023-02-26T12:52:17.459+00:00Chatgpt on "Riffing on triplet sum to a target"<p><span style="font-size: x-small;"><span style="background-color: #fcff01;"><span style="color: #2b00fe;"><i><b>(Best viewed on a larger than phone screen).</b></i></span></span></span><br /></p><p> </p><p>Last year I blogged on a taking a problem and solving it in multiple ways, just for the fun of it.</p><p>Triplet sum is a problem solved by many, and I had my own description of the problem so I decided to investigate it using ChatGPT and see what it said.</p><p>In the following I chose not to use screen grabs of the ChatGPT session, but instead copy text input as <b>me</b> 1 through 5, and responses as <b>c</b> 1 through 6. I use Python comments for my views on its responses.</p><p>Familiarise yourself with<a href="https://paddy3118.blogspot.com/2022/10/riffing-on-triplet-sum-to-target.html" target="_blank"> my earlier blog post</a> to better understand the following interaction: <br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #6a9955;"> </span></div><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">triplet_sum_chatgpt.py</span></div><br /><div><span style="color: #ce9178;">Investigation of Chatgpt and its solution to:</span></div><div><span style="color: #ce9178;"> http://paddy3118.blogspot.com/2022/10/riffing-on-triplet-sum-to-target.html</span></div><br /><div><span style="color: #ce9178;">Created on Sun Feb 26 10:26:41 2023</span></div><br /><div><span style="color: #ce9178;">@author: paddy</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #6a9955;">#%% BEGIN Chatgpt interaction</span></div><br /><div><span style="color: #d4d4d4;">me1 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Given a list of integers and a target integer, return all "normalised" triplets</span></div><div><span style="color: #ce9178;">of numbers from that list that sum to the target value.</span></div><div><span style="color: #ce9178;">Normalised triplets are a tuple of three values in sorted order,</span></div><div><span style="color: #ce9178;">i.e. (t0, t1, t2) where t0 <= t1 <= t2.</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #d4d4d4;">c1 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">To solve this problem, we can start by sorting the given list of integers. Then,</span></div><div><span style="color: #ce9178;">we can use a nested loop to iterate over all possible pairs of numbers from the</span></div><div><span style="color: #ce9178;">list, and for each pair, we can use binary search to find the third number that</span></div><div><span style="color: #ce9178;">would make the sum equal to the target integer. We can then check if the</span></div><div><span style="color: #ce9178;">triplet is in sorted order and add it to the result if it is.</span></div><br /><div><span style="color: #ce9178;">Here's the Python code that implements this approach:</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> find_triplets(arr, target):</span></div><div><span style="color: #d4d4d4;"> arr.sort()</span></div><div><span style="color: #d4d4d4;"> n = len(arr)</span></div><div><span style="color: #d4d4d4;"> res = []</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(n - </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># skip duplicates</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> i > </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">and</span><span style="color: #d4d4d4;"> arr[i] == arr[i-</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">continue</span></div><div><span style="color: #d4d4d4;"> j = i + </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> k = n - </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">while</span><span style="color: #d4d4d4;"> j < k:</span></div><div><span style="color: #d4d4d4;"> s = arr[i] + arr[j] + arr[k]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> s == target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># check if triplet is in sorted order</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> j == i + </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">or</span><span style="color: #d4d4d4;"> arr[j] != arr[j-</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">]:</span></div><div><span style="color: #d4d4d4;"> res.append((arr[i], arr[j], arr[k]))</span></div><div><span style="color: #d4d4d4;"> j += </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> k -= </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">elif</span><span style="color: #d4d4d4;"> s < target:</span></div><div><span style="color: #d4d4d4;"> j += </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> k -= </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> res</span></div><br /><div><span style="color: #d4d4d4;">c2 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">This code has a time complexity of O(n^2), where n is the length of the input</span></div><div><span style="color: #ce9178;">list. The sorting step takes O(n log n) time, and the nested loop takes O(n^2)</span></div><div><span style="color: #ce9178;">time. The binary search inside the loop takes O(log n) time, so the overall</span></div><div><span style="color: #ce9178;">time complexity is O(n^2 log n).</span></div><br /><div><span style="color: #ce9178;">Note that this code assumes that there are no duplicates in the input list.</span></div><div><span style="color: #ce9178;">If there are duplicates, we need to skip them in the loop to avoid generating</span></div><div><span style="color: #ce9178;">duplicate triplets. The code handles this by checking if the current number is</span></div><div><span style="color: #ce9178;">equal to the previous number, and skipping it if it is.</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #6a9955;">#%% My follow-up</span></div><br /><div><span style="color: #6a9955;"># As in original blog post:</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> tests1(funcs, args=</span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> args </span><span style="color: #569cd6;">is</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> args = [([</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([], </span><span style="color: #b5cea8;">99</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ]</span></div><div><span style="color: #d4d4d4;"> seperator, indent = (</span><span style="color: #ce9178;">'---\n'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">' '</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> len(funcs) > </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;"> (</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> lines = []</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> numbers, target </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> args:</span></div><div><span style="color: #d4d4d4;"> line = </span><span style="color: #ce9178;">''</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> func </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> funcs:</span></div><div><span style="color: #d4d4d4;"> line += </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{indent}{func.__name__}</span><span style="color: #ce9178;">(</span><span style="color: #d4d4d4;">{numbers}</span><span style="color: #ce9178;">, </span><span style="color: #d4d4d4;">{target}</span><span style="color: #ce9178;">) = "</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">try</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> answer = func(numbers, target)</span></div><div><span style="color: #d4d4d4;"> line += str(answer)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">except</span><span style="color: #d4d4d4;"> Exception </span><span style="color: #569cd6;">as</span><span style="color: #d4d4d4;"> ex:</span></div><div><span style="color: #d4d4d4;"> line += str(ex)</span></div><div><span style="color: #d4d4d4;"> line += </span><span style="color: #ce9178;">'\n'</span></div><div><span style="color: #d4d4d4;"> lines.append(line)</span></div><div><span style="color: #d4d4d4;"> print(seperator.join(lines))</span></div><br /><div><span style="color: #d4d4d4;">tests1([find_triplets])</span></div><br /><div><span style="color: #6a9955;"># # The above prints the correct results of:</span></div><div><span style="color: #6a9955;"># find_triplets([2, 0, 1, 3], 4) = [(0, 1, 3)]</span></div><div><span style="color: #6a9955;"># find_triplets([0, 1, 3, 2, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]</span></div><div><span style="color: #6a9955;"># find_triplets([0, 1, 2], 6) = []</span></div><div><span style="color: #6a9955;"># find_triplets([0, 1, 2], 3) = [(0, 1, 2)]</span></div><div><span style="color: #6a9955;"># find_triplets([0, 1, 2], 1) = []</span></div><div><span style="color: #6a9955;"># find_triplets([0, 1], 2) = []</span></div><div><span style="color: #6a9955;"># find_triplets([0, 1], 1) = []</span></div><div><span style="color: #6a9955;"># find_triplets([0], 0) = []</span></div><div><span style="color: #6a9955;"># find_triplets([], 99) = []</span></div><br /><div><span style="color: #6a9955;">#%% Time complexities of blog answers</span></div><br /><div><span style="color: #d4d4d4;">me2 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">What is the time complexity of: def triplet_sum1(numbers: list[int], target: int) -> list[tuple[int]]:</span></div><div><span style="color: #ce9178;"> "triplet-sum by filtering all triplet combinations"</span></div><div><span style="color: #ce9178;"> return [triplet for triplet in combinations(sorted(numbers), 3)</span></div><div><span style="color: #ce9178;"> if sum(triplet) == target]</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #d4d4d4;">c3 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">The time complexity of the triplet_sum1 function is O(n^3), where n is the</span></div><div><span style="color: #ce9178;">length of the input list.</span></div><br /><div><span style="color: #ce9178;">The function uses the combinations function from the itertools module to</span></div><div><span style="color: #ce9178;">generate all possible triplets of numbers from the input list. The combinations</span></div><div><span style="color: #ce9178;">function generates all possible combinations of k elements from the input list,</span></div><div><span style="color: #ce9178;">where k is the second argument to the function. In this case, k is 3, so</span></div><div><span style="color: #ce9178;">combinations(sorted(numbers), 3) generates all possible triplets of numbers</span></div><div><span style="color: #ce9178;">from the sorted input list. The number of possible triplets of n elements is n</span></div><div><span style="color: #ce9178;">choose 3, which is equal to (n * (n-1) * (n-2)) / 6. Therefore, the</span></div><div><span style="color: #ce9178;">combinations function generates O(n^3) triplets.</span></div><br /><div><span style="color: #ce9178;">The generated triplets are then filtered using a list comprehension, which</span></div><div><span style="color: #ce9178;">checks if the sum of each triplet is equal to the target integer. Checking the</span></div><div><span style="color: #ce9178;">sum of a triplet takes constant time, so the filtering step takes O(n^3) time.</span></div><br /><div><span style="color: #ce9178;">Therefore, the overall time complexity of the triplet_sum1 function is O(n^3).</span></div><div><span style="color: #ce9178;">This is slower than the approach used in the previous answer, which has a time</span></div><div><span style="color: #ce9178;">complexity of O(n^2 log n).</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #6a9955;"># The answer looks good. triplet_sum1 is written to obviousely generate the</span></div><div><span style="color: #6a9955;"># correct output.</span></div><br /><br /><div><span style="color: #d4d4d4;">me3 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">what is the time complexity of: from itertools import islice</span></div><br /><div><span style="color: #ce9178;">def triplet_sum3(numbers: list[int], target: int) -> list[tuple[int]]:</span></div><div><span style="color: #ce9178;"> "Triplet-sum with short-circuited combinations and less copying of numbers."</span></div><div><span style="color: #ce9178;"> numbers.sort()</span></div><div><span style="color: #ce9178;"> answers = []</span></div><br /><div><span style="color: #ce9178;"> for i, item in enumerate(numbers):</span></div><div><span style="color: #ce9178;"> isum = item</span></div><div><span style="color: #ce9178;"> if isum <= target:</span></div><div><span style="color: #ce9178;"> for j, jtem in enumerate(islice(numbers, i+1, None), i+1):</span></div><div><span style="color: #ce9178;"> jsum = isum + jtem</span></div><div><span style="color: #ce9178;"> if jsum <= target:</span></div><div><span style="color: #ce9178;"> for k, ktem in enumerate(islice(numbers, j+1, None), j+1):</span></div><div><span style="color: #ce9178;"> ksum = jsum + ktem</span></div><div><span style="color: #ce9178;"> if ksum == target:</span></div><div><span style="color: #ce9178;"> answers.append((item, jtem, ktem))</span></div><div><span style="color: #ce9178;"> elif ksum > target:</span></div><div><span style="color: #ce9178;"> break</span></div><div><span style="color: #ce9178;"> else:</span></div><div><span style="color: #ce9178;"> break # Skip as jsum > target</span></div><div><span style="color: #ce9178;"> else:</span></div><div><span style="color: #ce9178;"> break # Skip as item > target</span></div><br /><div><span style="color: #ce9178;"> return answers</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #d4d4d4;">c4 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">The time complexity of the triplet_sum3 function is O(n^2), where n is the</span></div><div><span style="color: #ce9178;">length of the input list.</span></div><br /><div><span style="color: #ce9178;">The function uses a nested loop to generate all possible triplets of numbers</span></div><div><span style="color: #ce9178;">from the sorted input list. The outer loop iterates over each item in the input</span></div><div><span style="color: #ce9178;">list, which takes O(n) time. The inner loop uses the islice function from the</span></div><div><span style="color: #ce9178;">itertools module to iterate over the remaining items in the input list after</span></div><div><span style="color: #ce9178;">the current item, which takes O(n) time. The innermost loop does the same for</span></div><div><span style="color: #ce9178;">the remaining items after the second item in the triplet, which again takes</span></div><div><span style="color: #ce9178;">O(n) time.</span></div><br /><div><span style="color: #ce9178;">Therefore, the overall time complexity of the function is O(n^3). However, the</span></div><div><span style="color: #ce9178;">function uses short-circuiting to avoid iterating over unnecessary</span></div><div><span style="color: #ce9178;">combinations. If the sum of the first item and the second item in a triplet is</span></div><div><span style="color: #ce9178;">greater than the target, the function skips the remaining items in the inner</span></div><div><span style="color: #ce9178;">loop and moves on to the next item in the outer loop. Similarly, if the sum of</span></div><div><span style="color: #ce9178;">the first item is greater than the target, the function skips the remaining</span></div><div><span style="color: #ce9178;">items in the outer loop. This reduces the number of iterations of the inner</span></div><div><span style="color: #ce9178;">loops and makes the overall time complexity O(n^2).</span></div><br /><div><span style="color: #ce9178;">Note that the function sorts the input list before generating the triplets.</span></div><div><span style="color: #ce9178;">Sorting takes O(n log n) time, which is dominated by the O(n^2) time complexity</span></div><div><span style="color: #ce9178;">of the nested loops. Therefore, the overall time complexity of the function is</span></div><div><span style="color: #ce9178;">O(n^2).</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #6a9955;"># The above is wrong, in that all triplets are not generated then tested as</span></div><div><span style="color: #6a9955;"># it states in the second paragraph.</span></div><div><span style="color: #6a9955;"># The calculated time complexity looks plausible.</span></div><br /><div><span style="color: #6a9955;"># Let's try it with my final function that can do more than triplets</span></div><br /><div><span style="color: #d4d4d4;">me4 = </span><span style="color: #ce9178;">'''</span></div><div><span style="color: #ce9178;">what is the time complexity of: def triplet_sum4(numbers: list[int], target: int, n_tuple: int=3) </span><span style="color: #569cd6;">\</span></div><div><span style="color: #ce9178;"> -> list[tuple[int]]:</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #ce9178;"> Generate triplets from numbers that sum to target in sorted order.</span></div><div><span style="color: #ce9178;"> Indexes the sorted numbers without otherwise copying them.</span></div><br /><div><span style="color: #ce9178;"> n_tuple=3 for triplets, 2 for pairs, etc, for any +ve int</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #ce9178;"> numbers.sort()</span></div><div><span style="color: #ce9178;"> n_len = len(numbers)</span></div><div><span style="color: #ce9178;"> answers = []</span></div><div><span style="color: #ce9178;"> indices = [-1] * n_tuple # indices for triples; indices[0] must be -1</span></div><div><span style="color: #ce9178;"> incrementing = 0 # index to increment at any time</span></div><div><span style="color: #ce9178;"> accum = [None] * n_tuple # (partial) sums in accumulator</span></div><br /><div><span style="color: #ce9178;"> while True:</span></div><div><span style="color: #ce9178;"> indices[incrementing] += 1</span></div><div><span style="color: #ce9178;"> if indices[incrementing] >= n_len:</span></div><div><span style="color: #ce9178;"> # maxed out this index so go back one</span></div><div><span style="color: #ce9178;"> incrementing -= 1</span></div><div><span style="color: #ce9178;"> if incrementing < 0:</span></div><div><span style="color: #ce9178;"> break</span></div><div><span style="color: #ce9178;"> continue # continue incrementing 'outer' index</span></div><div><span style="color: #ce9178;"> if incrementing < n_tuple - 1:</span></div><div><span style="color: #ce9178;"> indices[incrementing+1] = indices[incrementing]</span></div><div><span style="color: #ce9178;"> acc = accum[incrementing] = numbers[indices[incrementing]] + (</span></div><div><span style="color: #ce9178;"> 0 if incrementing == 0</span></div><div><span style="color: #ce9178;"> else accum[incrementing - 1])</span></div><div><span style="color: #ce9178;"> if acc > target:</span></div><div><span style="color: #ce9178;"> # maxed out this index so go back one</span></div><div><span style="color: #ce9178;"> incrementing -= 1</span></div><div><span style="color: #ce9178;"> if incrementing < 0:</span></div><div><span style="color: #ce9178;"> break</span></div><div><span style="color: #ce9178;"> continue</span></div><br /><div><span style="color: #ce9178;"> incrementing += 1 # next index</span></div><div><span style="color: #ce9178;"> elif incrementing == n_tuple - 1:</span></div><div><span style="color: #ce9178;"> # updated last index</span></div><div><span style="color: #ce9178;"> acc = numbers[indices[incrementing]] + (</span></div><div><span style="color: #ce9178;"> 0 if incrementing == 0</span></div><div><span style="color: #ce9178;"> else accum[incrementing - 1])</span></div><div><span style="color: #ce9178;"> if acc == target:</span></div><div><span style="color: #ce9178;"> answers.append(tuple(numbers[i] for i in indices))</span></div><div><span style="color: #ce9178;"> return answers</span></div><div><span style="color: #ce9178;">'''</span></div><br /><div><span style="color: #d4d4d4;">c5 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">The time complexity of the triplet_sum4 function is O(n^(k-1)), where n is the</span></div><div><span style="color: #ce9178;">length of the input list and k is the size of the tuple being generated.</span></div><br /><div><span style="color: #ce9178;">The function generates tuples by incrementing indices into the sorted input</span></div><div><span style="color: #ce9178;">list. It uses a single loop that iterates until all tuples of the desired size</span></div><div><span style="color: #ce9178;">have been generated. Each iteration increments one of the indices and updates</span></div><div><span style="color: #ce9178;">an accumulator array that keeps track of the sum of the numbers indexed so far.</span></div><div><span style="color: #ce9178;">If the sum of the numbers indexed so far exceeds the target, the function</span></div><div><span style="color: #ce9178;">backtracks to the previous index and continues incrementing from there. If the</span></div><div><span style="color: #ce9178;">last index is incremented and the sum of the numbers indexed equals the target,</span></div><div><span style="color: #ce9178;">the function adds the tuple to the list of answers.</span></div><br /><div><span style="color: #ce9178;">The time complexity of each iteration of the loop is O(n), since each index can</span></div><div><span style="color: #ce9178;">be incremented at most n times before the loop terminates. Therefore, the time</span></div><div><span style="color: #ce9178;">complexity of generating all tuples of size k is O(n^k).</span></div><br /><div><span style="color: #ce9178;">In the case of generating triplets (k=3), the time complexity is O(n^2), which</span></div><div><span style="color: #ce9178;">is lower than the time complexity of the triplet_sum3 function (also O(n^2))</span></div><div><span style="color: #ce9178;">because the triplet_sum4 function generates fewer tuples. For larger values of</span></div><div><span style="color: #ce9178;">k, the time complexity of triplet_sum4 can become very large, and it may be</span></div><div><span style="color: #ce9178;">more efficient to use a different algorithm that avoids generating all possible</span></div><div><span style="color: #ce9178;">tuples.</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #6a9955;"># Its description of the workings of triplet_sum4 is wrong too!</span></div><div><span style="color: #6a9955;"># All possible tuples are not generated then tested - it short circuits tuple</span></div><div><span style="color: #6a9955;"># generation when it knows it will exceed the target.</span></div><div><span style="color: #6a9955;"># I am unsure of its complexity calculation.</span></div><br /><div><span style="color: #6a9955;"># I like that it compared, (one), previous answers.</span></div><br /><div><span style="color: #6a9955;"># %% What's fastest?</span></div><br /><div><span style="color: #d4d4d4;">me5 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">for finding triplets from the integers in range zero to 665 inclusive, that sum</span></div><div><span style="color: #ce9178;">to six; which function is the fastest?</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #d4d4d4;">c6 = </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">To find the fastest function for finding triplets from integers in range zero</span></div><div><span style="color: #ce9178;">to 665 inclusive that sum to six, we need to test the performance of each</span></div><div><span style="color: #ce9178;">function and compare their execution times. Here's an example test code:</span></div><br /><br /><div><span style="color: #ce9178;">import timeit</span></div><br /><div><span style="color: #ce9178;">def test_function(func):</span></div><div><span style="color: #ce9178;"> numbers = list(range(666))</span></div><div><span style="color: #ce9178;"> target = 6</span></div><div><span style="color: #ce9178;"> result = func(numbers, target)</span></div><div><span style="color: #ce9178;"> assert result == [(0, 2, 4), (0, 3, 3), (1, 1, 4), (1, 2, 3)]</span></div><div><span style="color: #ce9178;"> print(f"{func.__name__} took {timeit.timeit(lambda: func(numbers, target), number=1000):.6f} seconds")</span></div><br /><div><span style="color: #ce9178;">test_function(triplet_sum1)</span></div><div><span style="color: #ce9178;">test_function(triplet_sum2)</span></div><div><span style="color: #ce9178;">test_function(triplet_sum3)</span></div><div><span style="color: #ce9178;">test_function(triplet_sum4)</span></div><br /><br /><div><span style="color: #ce9178;">This code creates a list of integers from 0 to 665, sets the target value to 6,</span></div><div><span style="color: #ce9178;">calls each function with the same arguments, and checks if the output is</span></div><div><span style="color: #ce9178;">correct. Then, it measures the execution time of each function with 1000</span></div><div><span style="color: #ce9178;">iterations and prints the result.</span></div><br /><div><span style="color: #ce9178;">Note that the execution times may vary depending on the machine and the version</span></div><div><span style="color: #ce9178;">of Python used.</span></div><br /><div><span style="color: #ce9178;">Based on the results of the test, the fastest function is likely to be</span></div><div><span style="color: #ce9178;">triplet_sum4, followed by triplet_sum3, triplet_sum2, and triplet_sum1,</span></div><div><span style="color: #ce9178;">in that order. However, the performance difference between the functions is</span></div><div><span style="color: #ce9178;">relatively small for this input size, and other factors such as code</span></div><div><span style="color: #ce9178;">readability and maintainability may also be important considerations.</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #6a9955;"># Whoops! The context established integers are used once from the input array.</span></div><div><span style="color: #6a9955;"># In c6 result assumes input integers can be used twice, when they appear only</span></div><div><span style="color: #6a9955;"># once in the input!</span></div><br /><div><span style="color: #6a9955;"># Functions from prior context are first its generated `find_triplets`, then my</span></div><div><span style="color: #6a9955;"># `triplet_sum` 1, 3, and 4. This last answer invents triplet_sum2 and forgets</span></div><div><span style="color: #6a9955;"># its own find_triplets.</span></div><br /><br /></div><p></p><h3 style="text-align: left;">Conclusion</h3><p>ChatGPT gives no feedback on its level of certainty. It will be wrong with the same authority as its truer statements, Some errors can be caught, but others will <i><b>not </b></i>be if you are relying on Chatgpt to be more knowlegeable than yourself.<br /></p><p> <br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-61359209314111892012022-10-29T10:59:00.000+01:002022-10-29T10:59:55.147+01:00 Ouch, vscode! 😠<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEigBa4tFOV2D-2_LKWMZRZvILo9zWE9AGfx8T_1vS9-XYdQEYN7_mgyKtpG6bM4VjBJ5bYtXVoV6Du4lkwhgeh7-65ySLT80NTvLvmIOavxYOv1Yr2s9uQnkeUWpRmMBm0fbn6BwOYX3KSb6cIyqoji6kPgl9gW1x3TJ-gVobAs_LwL9gEviA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="275" data-original-width="498" height="177" src="https://blogger.googleusercontent.com/img/a/AVvXsEigBa4tFOV2D-2_LKWMZRZvILo9zWE9AGfx8T_1vS9-XYdQEYN7_mgyKtpG6bM4VjBJ5bYtXVoV6Du4lkwhgeh7-65ySLT80NTvLvmIOavxYOv1Yr2s9uQnkeUWpRmMBm0fbn6BwOYX3KSb6cIyqoji6kPgl9gW1x3TJ-gVobAs_LwL9gEviA" width="320" /></a></div><br /><br /><p></p><p>The company cloud has hundreds of machines and I need to get some idea of the hardware so I find <a href="https://linux.die.net/man/8/dmidecode" rel="nofollow" target="_blank">dmidecode </a>for the functionality and need to parse its structured output and generate A Python datastructure, or maybe JSON.</p><p>I decide to access a Linux cloud machine remotely using vscode, as normal and run dmidecode, cut-n-paste a sample of its output into <a href="https://regex101.com/" rel="nofollow" target="_blank">regex101</a>, I do my due diligance and have a VNC session into another machine in the cloud and run dmidecode there on a different machine, take a look, and cut-n-paste an extended section of the data into regex101, scroll to the top of regex101, and write exploratary regexps as I match sections of the dmidecode output. </p><p>Matches start, but stop at the section of data copied from my VNC terminal? </p><p>Regex101 prints grey dots for spaces and a grey line for tabs and it is then plain to see that<b> the commands output is indented with tabs when copied from VNC, but <u>spaces </u>from vscode.</b></p><p>Piping the output and grepping for initial tabs and comparing it to grepping for initial spaces shows that the dmidecode command indents with tabs. A web search showed that vscode terminal has an <a href="https://github.com/microsoft/vscode/issues/85878" rel="nofollow" target="_blank">issue </a>with outputting tabs and silently changes them to spaces. Furthermore, <i>this issue is closed!?!</i></p><p>Vscode. it's a nice tool 'n all; but <span style="background-color: #d5a6bd;">lying </span>about data is a no-no. I expect you to get the fundamentals right as a solid base then add from there. X, vi, gvim make me not even think to check that tabs are visible (and their handling modifiable), </p><p><b>I wonder if other IDE's have this issue, lets start a list.</b></p><p><b>End.</b></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-34416104810165589012022-10-23T17:20:00.001+01:002022-10-23T17:50:19.303+01:00The Kaprekar process<p> </p><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<div style="text-align: left;"><span style="color: #2b00fe;"><b><i>(Best viewed on larger screens)</i></b></span></div><div style="text-align: left;"><span style="color: #2b00fe;"><b><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcP55clwRmPXYZ_8RiVAFaXzZpOdF6OdNQrtkoZjKxBCo9DRIRVRMZryrCOGAeoUwXFtn0Ra23dps4P1tL8cZE34sCZP-k4zEDGzHA095MXUcZhHADf0BL9catVEVLk13t-kBIZrdQWZ4q1UbhY-X0mshXIf9hYu9npYNBxndaLhcEqUktgw/s3648/IMG_20220919_125123.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3648" data-original-width="2736" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcP55clwRmPXYZ_8RiVAFaXzZpOdF6OdNQrtkoZjKxBCo9DRIRVRMZryrCOGAeoUwXFtn0Ra23dps4P1tL8cZE34sCZP-k4zEDGzHA095MXUcZhHADf0BL9catVEVLk13t-kBIZrdQWZ4q1UbhY-X0mshXIf9hYu9npYNBxndaLhcEqUktgw/s320/IMG_20220919_125123.jpg" width="240" /></a></div><br /><i><br /></i></b></span></div><h1 id="The-Kaprekar-process">What is it?</h1>
<p>Given the number of <code>digits</code> to maintain, and a starting positive integer <code>i</code> with no more than that many digits, the repettitive process continues:</p>
<ol>
<li>Form the integer <code>i1</code> from the (zero-extended) digits of <code>i</code> sorted from largest digit to smallest.</li>
<li>Form the integer <code>i2</code> from the (zero-extended) digits of <code>i</code> sorted from smallest digit to largest.</li>
<li>Form a new, replacement <code>i</code> by subtracting the smallest from the largest: <code>i = i1 - i2</code>.</li>
</ol>
<p>If any new <code>i</code> has already been seen then this latest <code>i</code> is termed the Kaprekar constant formed from the initial <code>i</code> and <code>digits</code></p>
<h2 id="Details">Details</h2>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h3 id="Zero-extension">Zero extension</h3><p>If we have <code>i = 21; digits = 4</code>
then zero extension to the given number of digits means that if the
integer is normally written with fewer digits then extra zeroes are
added as padding to the <em>right</em> of the number to fill in the right number of digits. <code>i</code> of <code>21</code> is treated as <code>0021</code> the hundreds and thousands digits are given as zero.</p>
<p>When sorted, <code>i1 = int('2100')</code> and <code>i2 = int('0012')</code> resulting in a new <code>i = 2100 - 12 = 2088</code></p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span><span class="n"></span></pre></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain"><pre><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;"><div>i = <span style="color: #b5cea8;">21</span>; digits = <span style="color: #b5cea8;">4</span></div><div>print(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"Zero-extend </span>{i = }<span style="color: #ce9178;"> by </span>{digits = }<span style="color: #ce9178;"> gives: </span>{i = <span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;">"</span>)</div></div></pre><pre>Zero-extend i = 21 by digits = 4 gives: i = 0021
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h3 id="Sort">Sort</h3><p>If we consider the digits of a number as characters then the normal Python <code>sort</code> returns the digits from smallest to largest, i.e. for <code>i2</code>.</p></div></div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline"><div class="CodeMirror cm-s-jupyter"><div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div style="line-height: 19px;"><div>i_l2r = <span style="color: #ce9178;">''</span>.join(sorted(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{i<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;">"</span>)) <span style="color: #6a9955;"># digits sorted Left-to-Right</span></div><div>print(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{i = }\n<span style="color: #ce9178;">Zero-extended and sorted digits for i2:"</span>, i_l2r)</div><div>print(<span style="color: #ce9178;">"Those same digits reverse-sorted for i1:"</span>, i_l2r[::-<span style="color: #b5cea8;">1</span>])</div><br /><div>new_i = int(i_l2r[::-<span style="color: #b5cea8;">1</span>]) - int(i_l2r)</div><div>print(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{new_i = }<span style="color: #ce9178;">"</span>)</div></div></div></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>i = 21
Zero-extended and sorted digits for i2: 0012
Those same digits reverse-sorted for i1: 2100
new_i = 2088
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h2 id="The-kaprekar-constant">The kaprekar constant</h2><p>An Indian mathematician, Kaprekar, found that with four digits, starting with any number produced either a final value of</p>
<ul>
<li><code>0</code>, Zero for starting numbers that were palindromes. For example if <code>i = 1111</code> then <code>i1 == i2 == 1111</code> and the new <code>i = i1 - i2 = 0</code>.</li>
<li>Or <code>6174</code> for all other numbers of four or less digits.</li>
</ul>
<p><b><code>6174</code> </b>is known as <b><i>the </i></b><strong>Kaprekar constant</strong>.</p>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h2 id="Exploring-the-Kaprekar-process">Exploring the Kaprekar process</h2><p>Now
some code to find constants for differing numbers of digits, as well as
an indication of how many starting integers in the range of that many <code>digits</code> loop on a particular constant.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;"><div><span style="color: #569cd6;">from</span> collections <span style="color: #569cd6;">import</span> defaultdict</div><br /><div><span style="color: #569cd6;">def</span> kaprekar1(digits: int=<span style="color: #b5cea8;">4</span>):</div><div> <span style="color: #ce9178;">"kaprekar function."</span></div><div> base = <span style="color: #b5cea8;">10</span></div><div> konstant2nums = defaultdict(int) <span style="color: #6a9955;"># how many times the constant is seen</span></div><div> <span style="color: #569cd6;">for</span> i <span style="color: #569cd6;">in</span> range(base**digits): <span style="color: #6a9955;"># All ints expressable in digits</span></div><div> i_start = i</div><div> seen = set() <span style="color: #6a9955;"># numbers produced by the process so far</span></div><div> <span style="color: #569cd6;">while</span> i <span style="color: #569cd6;">not</span> <span style="color: #569cd6;">in</span> seen:</div><div> seen.add(i)</div><div> i_l2r = <span style="color: #ce9178;">''</span>.join(sorted(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{i<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;">"</span>)) <span style="color: #6a9955;"># digits sorted L-to-R</span></div><div> i = int(i_l2r[::-<span style="color: #b5cea8;">1</span>]) - int(i_l2r) <span style="color: #6a9955;"># new i</span></div><div> konstant2nums[i] += <span style="color: #b5cea8;">1</span> <span style="color: #6a9955;"># this constant i, seen one more time from this i_start</span></div><br /><div> <span style="color: #569cd6;">return</span> dict(konstant2nums)</div><br /><div>kaprekar1(<span style="color: #b5cea8;">4</span>)</div></div></pre><pre><br /></pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>{0: 10, 6174: 9990}</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>That show that we get the constant <code>0</code> ten times for i = 0, 1111, ..., 9999.</p>
<p>We get the Kaprekar constant of <code>6174</code> for all other starting, foru-digit numbers.</p>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h3 id="Two-digits">Two digits</h3>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;">kaprekar1(<span style="color: #b5cea8;">2</span>)</div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>{0: 10, 9: 26, 63: 22, 27: 26, 45: 14, 81: 2}</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>With two digits we get the obvious constant <code>0</code> and five others: <code>9, 27, 45, 63, 81</code></p>
<h3 id="No-longer-interested-in-constant-frequencies">No longer interested in constant frequencies</h3><p>I'm only really interested in the keys of the returned dict so:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;"><div><span style="color: #569cd6;">def</span> kaprekar2(digits: int=<span style="color: #b5cea8;">4</span>):</div><div> <span style="color: #ce9178;">"kaprekar function."</span></div><div> base = <span style="color: #b5cea8;">10</span></div><div> konstant2nums = defaultdict(int) <span style="color: #6a9955;"># how many times the constant is seen</span></div><div> <span style="color: #569cd6;">for</span> i <span style="color: #569cd6;">in</span> range(base**digits): <span style="color: #6a9955;"># All ints expressable in digits</span></div><div> i_start = i</div><div> seen = set() <span style="color: #6a9955;"># numbers produced by the process so far</span></div><div> <span style="color: #569cd6;">while</span> i <span style="color: #569cd6;">not</span> <span style="color: #569cd6;">in</span> seen:</div><div> seen.add(i)</div><div> i_l2r = <span style="color: #ce9178;">''</span>.join(sorted(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{i<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;">"</span>)) <span style="color: #6a9955;"># digits sorted L-to-R</span></div><div> i = int(i_l2r[::-<span style="color: #b5cea8;">1</span>]) - int(i_l2r) <span style="color: #6a9955;"># new i</span></div><div> konstant2nums[i] += <span style="color: #b5cea8;">1</span> <span style="color: #6a9955;"># this constant i, seen one more time from this i_start</span></div><br /><div> <span style="color: #569cd6;">return</span> sorted(konstant2nums) <span style="color: #6a9955;"># Just the keys</span></div><br /><div>kaprekar2(<span style="color: #b5cea8;">2</span>)</div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>[0, 9, 27, 45, 63, 81]</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h2 id="Optimisations">Optimisations</h2><p>The code takes a minute to produce kaprekar1(6), and looking at the code I chuck away what is <code>seen</code> for every <code>i_start</code>.<br />
What if a later <code>i_start</code> has an <code>i</code> that visits something seen for a previous <code>i_start</code>?</p>
<p>In this next version I keep track of everything <code>seen</code> earlier in <code>seen2</code> which automatically maps to the ending constant found for all numbers of <code>seen</code></p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;"><div><span style="color: #569cd6;">def</span> kaprekar3(digits: int=<span style="color: #b5cea8;">4</span>):</div><div> <span style="color: #ce9178;">"Optimised"</span></div><div> konstants = set()</div><div> seen2 = {} <span style="color: #6a9955;"># Maps iterated i to final constant across all initial i's</span></div><div> <span style="color: #569cd6;">for</span> i <span style="color: #569cd6;">in</span> range(<span style="color: #b5cea8;">10</span>**digits):</div><div> i_start = i</div><div> seen = set()</div><div> <span style="color: #569cd6;">while</span> i <span style="color: #569cd6;">not</span> <span style="color: #569cd6;">in</span> seen <span style="color: #569cd6;">and</span> i <span style="color: #569cd6;">not</span> <span style="color: #569cd6;">in</span> seen2: <span style="color: #6a9955;"># Short-cut via earlier i_starts too.</span></div><div> seen.add(i)</div><div> i_l2r = <span style="color: #ce9178;">''</span>.join(sorted(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{i<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;">"</span>))</div><div> i = int(i_l2r[::-<span style="color: #b5cea8;">1</span>]) - int(i_l2r)</div><div> <span style="color: #569cd6;">if</span> i <span style="color: #569cd6;">in</span> seen2:</div><div> i = seen2[i] <span style="color: #6a9955;"># then this was its final constant</span></div><div> <span style="color: #569cd6;">for</span> s <span style="color: #569cd6;">in</span> seen:</div><div> seen2[s] = i <span style="color: #6a9955;"># Accumulate data from this i_start</span></div><br /><div> konstants.add(i)</div><br /><div> <span style="color: #569cd6;">return</span> sorted(konstants)</div><br /><div>kaprekar3(<span style="color: #b5cea8;">2</span>)</div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>[0, 9]</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h3 id="Whoa!-different-results">Whoa! different results</h3><p><code>kaprekar2(2)</code> gave <code>[0, 9, 27, 45, 63, 81]</code> ?</p>
<p>Lets work through one of the extra results for the <code>digits = 2</code> ones a bit more:</p>
<div class="highlight"><pre><span></span>81 -> 81 - 18 = 63
63 -> 63 - 36 = 27
27 -> 72 - 27 = 45
45 -> 54 - 45 = 9
</pre></div>
<p>So kaprekar1 and 2 did not finish their reductions! All the extra results reduce to the <code>9</code> given in <code>kaprekar3(2)</code>.</p>
<h2 id="Results-for-more-digits">Results for more digits</h2>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;"><div><span style="color: #569cd6;">for</span> d <span style="color: #569cd6;">in</span> range(<span style="color: #b5cea8;">7</span>):</div><div> print()</div><div> %time print(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{d = }<span style="color: #ce9178;">; </span>{kaprekar3(d) = }<span style="color: #ce9178;">"</span>)</div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>d = 0; kaprekar3(d) = [0]
CPU times: total: 0 ns
Wall time: 0 ns
d = 1; kaprekar3(d) = [0]
CPU times: total: 0 ns
Wall time: 0 ns
d = 2; kaprekar3(d) = [0, 9]
CPU times: total: 0 ns
Wall time: 0 ns
d = 3; kaprekar3(d) = [0, 495]
CPU times: total: 0 ns
Wall time: 4.99 ms
d = 4; kaprekar3(d) = [0, 6174]
CPU times: total: 46.9 ms
Wall time: 50 ms
d = 5; kaprekar3(d) = [0, 53955, 63954, 74943]
CPU times: total: 438 ms
Wall time: 444 ms
d = 6; kaprekar3(d) = [0, 549945, 631764, 851742]
CPU times: total: 4.91 s
Wall time: 4.93 s
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Lets check those <code>digits = 5</code> multiple values don't collapse to other constants.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;"><div><span style="color: #569cd6;">def</span> test_multiple_results(kaprekar_func, digits=<span style="color: #b5cea8;">4</span>):</div><div> print(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"TESTING </span>{kaprekar_func.__name__}<span style="color: #ce9178;">(</span>{digits}<span style="color: #ce9178;">) KONSTANTS"</span>, end=<span style="color: #ce9178;">''</span>)</div><div> stated_konstants = kaprekar_func(digits)</div><br /><div> konstants = set(stated_konstants)</div><div> <span style="color: #569cd6;">for</span> i <span style="color: #569cd6;">in</span> stated_konstants:</div><div> i_start = i</div><div> seen = set()</div><div> <span style="color: #6a9955;"># Reduce, visibly</span></div><div> print(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>\n<span style="color: #ce9178;"> Reduce konstant </span>{i}<span style="color: #ce9178;">:"</span>)</div><div> <span style="color: #569cd6;">while</span> i <span style="color: #569cd6;">not</span> <span style="color: #569cd6;">in</span> seen:</div><div> seen.add(i)</div><div> i_l2r = <span style="color: #ce9178;">''</span>.join(sorted(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span>{i<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;">"</span>))</div><div> i_new = (i1:=int(i_l2r[::-<span style="color: #b5cea8;">1</span>])) - (i2:=int(i_l2r))</div><div> print(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">" </span>{i<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;"> -> </span>{i1<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;"> - </span>{i2<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;"> = </span>{i_new<span style="color: #569cd6;">:0</span>{digits}}<span style="color: #ce9178;">"</span>)</div><div> i = i_new</div><div> <span style="color: #569cd6;">if</span> <span style="color: #569cd6;">not</span> seen.intersection(konstants):</div><div> print(<span style="color: #ce9178;">" ERROR! A new constant produced?!?"</span>)</div><div> <span style="color: #569cd6;">elif</span> i <span style="color: #569cd6;">in</span> konstants:</div><div> <span style="color: #569cd6;">if</span> i != i_start:</div><div> print(<span style="color: #ce9178;">" NOTE OK. One of the reduction intermediates occurs in the original konstants."</span>)</div><div> <span style="color: #569cd6;">else</span>:</div><div> print(<span style="color: #ce9178;">" NOTE OK. Reduces to itself."</span>)</div><div> <span style="color: #569cd6;">elif</span> seen.intersection(konstants):</div><div> print(<span style="color: #ce9178;">" NOTE OK. One of the reduction intermediates occurs in the original konstants."</span>)</div><div> <span style="color: #569cd6;">else</span>:</div><div> print(<span style="color: #ce9178;">" ERROR! A new constant produced!"</span>)</div><br /><div>test_multiple_results(kaprekar3, <span style="color: #b5cea8;">5</span>)</div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>TESTING kaprekar3(5) KONSTANTS
Reduce konstant 0:
00000 -> 00000 - 00000 = 00000
NOTE OK. Reduces to itself.
Reduce konstant 53955:
53955 -> 95553 - 35559 = 59994
59994 -> 99954 - 45999 = 53955
NOTE OK. Reduces to itself.
Reduce konstant 63954:
63954 -> 96543 - 34569 = 61974
61974 -> 97641 - 14679 = 82962
82962 -> 98622 - 22689 = 75933
75933 -> 97533 - 33579 = 63954
NOTE OK. Reduces to itself.
Reduce konstant 74943:
74943 -> 97443 - 34479 = 62964
62964 -> 96642 - 24669 = 71973
71973 -> 97731 - 13779 = 83952
83952 -> 98532 - 23589 = 74943
NOTE OK. Reduces to itself.
</pre>
</div>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;">test_multiple_results(kaprekar3, <span style="color: #b5cea8;">6</span>)</div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>TESTING kaprekar3(6) KONSTANTS
Reduce konstant 0:
000000 -> 000000 - 000000 = 000000
NOTE OK. Reduces to itself.
Reduce konstant 549945:
549945 -> 995544 - 445599 = 549945
NOTE OK. Reduces to itself.
Reduce konstant 631764:
631764 -> 766431 - 134667 = 631764
NOTE OK. Reduces to itself.
Reduce konstant 851742:
851742 -> 875421 - 124578 = 750843
750843 -> 875430 - 034578 = 840852
840852 -> 885420 - 024588 = 860832
860832 -> 886320 - 023688 = 862632
862632 -> 866322 - 223668 = 642654
642654 -> 665442 - 244566 = 420876
420876 -> 876420 - 024678 = 851742
NOTE OK. Reduces to itself.
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p><strong>The konstants from <code>kaprekar3</code> are legit.</strong></p>
<p><strong>END.</strong></p>
</div>
</div>
</div>
</div>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-16061881135737574882022-10-12T22:38:00.009+01:002022-10-22T17:04:04.890+01:00Riffing on triplet sum to a target<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/qkSiPKC-8so" title="YouTube video player" width="560"></iframe>
<p>
</p><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown" style="text-align: left;">
<p> </p><p><b><span style="color: #2b00fe;">(Best viewed on a larger screen). </span></b><br /></p><p>There's
a task that goes: </p><div style="margin-left: 40px;"><p style="text-align: left;"><i><span style="font-size: small;"><b>Given a list of integers and a target integer, return all "normalised" triplets of numbers from that list that sum to the target value. <br />Normalised triplets are a tuple of three values in sorted order, i.e. (t0, t1, t2) where t0 <= t1 <= t2.</b></span></i></p></div></div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h2 id="First,-simple,-thoughts">First, simple, thoughts</h2><p>A straight-forward implementation might be to:</p>
<ol><li>Generate all triplets from the numbers.</li><li>Filter out those that sum to the target</li></ol>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> itertools </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> combinations</span></div><br /><div><span style="color: #d4d4d4;">numbers = [</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">]</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{numbers = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{list(combinations(numbers, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{sorted(numbers) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{list(combinations(sorted(numbers), </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div></div></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>numbers = [2, 0, 1, 3]
list(combinations(numbers, 3)) = [(2, 0, 1), (2, 0, 3), (2, 1, 3), (0, 1, 3)]
sorted(numbers) = [0, 1, 2, 3]
list(combinations(sorted(numbers), 3)) = [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>We see that the sorted numbers produce normalised triplets</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> triplet_sum1(numbers: list[int], target: int) -> list[tuple[int]]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"triplet-sum by filtering all triplet combinations"</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> [triplet </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> triplet </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> combinations(sorted(numbers), </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> sum(triplet) == target]</span></div><br /><div><span style="color: #6a9955;"># %%</span></div><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> tests1(funcs, args=</span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> args </span><span style="color: #569cd6;">is</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> args = [([</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([], </span><span style="color: #b5cea8;">99</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ]</span></div><div><span style="color: #d4d4d4;"> seperator, indent = (</span><span style="color: #ce9178;">'---\n'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">' '</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> len(funcs) > </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;"> (</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> lines = []</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> numbers, target </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> args:</span></div><div><span style="color: #d4d4d4;"> line = </span><span style="color: #ce9178;">''</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> func </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> funcs:</span></div><div><span style="color: #d4d4d4;"> line += </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{indent}{func.__name__}</span><span style="color: #ce9178;">(</span><span style="color: #d4d4d4;">{numbers}</span><span style="color: #ce9178;">, </span><span style="color: #d4d4d4;">{target}</span><span style="color: #ce9178;">) = "</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">try</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> answer = func(numbers, target)</span></div><div><span style="color: #d4d4d4;"> line += str(answer)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">except</span><span style="color: #d4d4d4;"> Exception </span><span style="color: #569cd6;">as</span><span style="color: #d4d4d4;"> ex:</span></div><div><span style="color: #d4d4d4;"> line += str(ex)</span></div><div><span style="color: #d4d4d4;"> line += </span><span style="color: #ce9178;">'\n'</span></div><div><span style="color: #d4d4d4;"> lines.append(line)</span></div><div><span style="color: #d4d4d4;"> print(seperator.join(lines))</span></div><br /><div><span style="color: #d4d4d4;">tests1([triplet_sum1])</span></div><br /></div><pre>triplet_sum1([2, 0, 1, 3], 4) = [(0, 1, 3)]
triplet_sum1([0, 1, 3, 2, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]
triplet_sum1([0, 1, 2], 6) = []
triplet_sum1([0, 1, 2], 3) = [(0, 1, 2)]
triplet_sum1([0, 1, 2], 1) = []
triplet_sum1([0, 1], 2) = []
triplet_sum1([0, 1], 1) = []
triplet_sum1([0], 0) = []
triplet_sum1([], 99) = []
</pre></div></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>The above looks correct and <code>triplet_sum1</code> is neat and small.</p>
<p>But that combinations can take a long time even if most of its terms are to be filtered out</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">%time triplet_sum1(list(range(</span><span style="color: #b5cea8;">666</span><span style="color: #d4d4d4;">)), </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)</span></div><br /></div><pre></pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>CPU times: total: 6.59 s
Wall time: 6.58 s
</pre>
</div>
</div>
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>[(0, 1, 5), (0, 2, 4), (1, 2, 3)]</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h2 id="Explicit,-short-circuited,-combinations">Explicit, short-circuited, combinations</h2><p>If we generate our own triples we should be able to short circuit them if they cannot sum to the target.</p>
<p>Let's generate triples from numbers.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">numbers = [</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">20</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">30</span><span style="color: #d4d4d4;">] </span><span style="color: #6a9955;"># </span></div><div><span style="color: #d4d4d4;">numbers.sort()</span></div><br /><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i, item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> j, jtem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], start=i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">): </span><span style="color: #6a9955;"># from left of item</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> k, ktem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], start=j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">): </span><span style="color: #6a9955;"># from left of jtem</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{(i,j,k) = }</span><span style="color: #ce9178;">; </span><span style="color: #d4d4d4;">{(item, jtem, ktem) = }</span><span style="color: #ce9178;">; </span><span style="color: #d4d4d4;">{sum([item, jtem, ktem]) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><br /></div><pre>(i,j,k) = (0, 1, 2); (item, jtem, ktem) = (0, 10, 20); sum([item, jtem, ktem]) = 30
(i,j,k) = (0, 1, 3); (item, jtem, ktem) = (0, 10, 30); sum([item, jtem, ktem]) = 40
(i,j,k) = (0, 2, 3); (item, jtem, ktem) = (0, 20, 30); sum([item, jtem, ktem]) = 50
(i,j,k) = (1, 2, 3); (item, jtem, ktem) = (10, 20, 30); sum([item, jtem, ktem]) = 60
</pre></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Because numbers is sorted, and the triples are produced in this sorted order then, the sum of the triples will never decrease.</p>
<p>We can create the sum with less additions by using partial sums:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">numbers = [</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">20</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">30</span><span style="color: #d4d4d4;">] </span><span style="color: #6a9955;"># </span></div><div><span style="color: #d4d4d4;">numbers.sort()</span></div><br /><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i, item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># isum == item, omitted</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> j, jtem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> jsum = item + jtem </span><span style="color: #6a9955;"># partial sum from item to jtem</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> k, ktem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> ksum = jsum + ktem </span><span style="color: #6a9955;"># sum to ktem</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{(item, jtem, ktem) = }</span><span style="color: #ce9178;">; </span><span style="color: #d4d4d4;">{ksum = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div>
</div></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>(item, jtem, ktem) = (0, 10, 20); ksum = 30
(item, jtem, ktem) = (0, 10, 30); ksum = 40
(item, jtem, ktem) = (0, 20, 30); ksum = 50
(item, jtem, ktem) = (10, 20, 30); ksum = 60
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Those (partial) sums never decrease either so if any of the partial
sums are more than the target then its whole for-loop can be skipped.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">numbers = sorted(range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">))</span></div><div><span style="color: #d4d4d4;">target = </span><span style="color: #b5cea8;">30</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{numbers = }\n{target = }\n</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><br /><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i, item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers):</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"# iloop </span><span style="color: #d4d4d4;">{(item,) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> isum = item</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> isum <= target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> j, jtem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">" # jloop </span><span style="color: #d4d4d4;">{(item, jtem) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> jsum = isum + jtem</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> jsum <= target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> k, ktem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">" # kloop </span><span style="color: #d4d4d4;">{(item, jtem, ktem) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> ksum = jsum + ktem</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> ksum == target:</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #ce9178;">' ANSWER:'</span><span style="color: #d4d4d4;">, (item, jtem, ktem))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">elif</span><span style="color: #d4d4d4;"> ksum > target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># Skip as jsum > target</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># Skip as item > target</span></div><br /></div><pre>numbers = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
target = 30
# iloop (item,) = (0,)
# jloop (item, jtem) = (0, 10)
# kloop (item, jtem, ktem) = (0, 10, 20)
ANSWER: (0, 10, 20)
# kloop (item, jtem, ktem) = (0, 10, 30)
# jloop (item, jtem) = (0, 20)
# kloop (item, jtem, ktem) = (0, 20, 30)
# jloop (item, jtem) = (0, 30)
# kloop (item, jtem, ktem) = (0, 30, 40)
# jloop (item, jtem) = (0, 40)
# iloop (item,) = (10,)
# jloop (item, jtem) = (10, 20)
# kloop (item, jtem, ktem) = (10, 20, 30)
# jloop (item, jtem) = (10, 30)
# iloop (item,) = (20,)
# jloop (item, jtem) = (20, 30)
# iloop (item,) = (30,)
# jloop (item, jtem) = (30, 40)
# iloop (item,) = (40,)
</pre></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Look for the breaking out of inner loops when the (partial) sums exceed <code>target</code>.</p>
<p>We now get our second implementation:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> triplet_sum2(numbers: list[int], target: int) -> list[tuple[int]]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Triplet-sum with explicit, short-circuited combinations generator."</span></div><div><span style="color: #d4d4d4;"> numbers.sort()</span></div><div><span style="color: #d4d4d4;"> answers = []</span></div><div><span style="color: #d4d4d4;"> </span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i, item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers):</span></div><div><span style="color: #d4d4d4;"> isum = item</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> isum <= target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> j, jtem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> jsum = isum + jtem</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> jsum <= target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> k, ktem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers[j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:], j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> ksum = jsum + ktem</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> ksum == target:</span></div><div><span style="color: #d4d4d4;"> answers.append((item, jtem, ktem))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">elif</span><span style="color: #d4d4d4;"> ksum > target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># Skip as jsum > target</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># Skip as item > target</span></div><div><span style="color: #d4d4d4;"> </span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> answers</span></div><br /></div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">tests1([triplet_sum1, triplet_sum2])</span></div><br /></div><pre></pre></div></div></div></div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline"><div class="CodeMirror cm-s-jupyter"><div class="highlight hl-ipython3"></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre> triplet_sum1([2, 0, 1, 3], 4) = [(0, 1, 3)]
triplet_sum2([2, 0, 1, 3], 4) = [(0, 1, 3)]
---
triplet_sum1([0, 1, 3, 2, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]
triplet_sum2([0, 1, 3, 2, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]
---
triplet_sum1([0, 1, 2], 6) = []
triplet_sum2([0, 1, 2], 6) = []
---
triplet_sum1([0, 1, 2], 3) = [(0, 1, 2)]
triplet_sum2([0, 1, 2], 3) = [(0, 1, 2)]
---
triplet_sum1([0, 1, 2], 1) = []
triplet_sum2([0, 1, 2], 1) = []
---
triplet_sum1([0, 1], 2) = []
triplet_sum2([0, 1], 2) = []
---
triplet_sum1([0, 1], 1) = []
triplet_sum2([0, 1], 1) = []
---
triplet_sum1([0], 0) = []
triplet_sum2([0], 0) = []
---
triplet_sum1([], 99) = []
triplet_sum2([], 99) = []
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Check the timings:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">%time triplet_sum1(list(range(</span><span style="color: #b5cea8;">666</span><span style="color: #d4d4d4;">)), </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">%time triplet_sum2(list(range(</span><span style="color: #b5cea8;">666</span><span style="color: #d4d4d4;">)), </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)</span></div><br /></div><br /></div></div></div></div></div><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain"><pre>CPU times: total: 6.59 s
Wall time: 6.6 s
CPU times: total: 0 ns
Wall time: 0 ns
</pre>
</div>
</div>
<div class="jp-OutputArea-child jp-OutputArea-executeResult">
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child jp-OutputArea-executeResult"><div class="jp-RenderedText jp-OutputArea-output jp-OutputArea-executeResult" data-mime-type="text/plain">
<pre>[(0, 1, 5), (0, 2, 4), (1, 2, 3)]</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>lets try timing multiple runs of ttriplet_sum2 as it is so fast:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">%timeit triplet_sum2(list(range(</span><span style="color: #b5cea8;">666</span><span style="color: #d4d4d4;">)), </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)</span></div><br /></div></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain"><pre>29.7 µs ± 89.1 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>So that's microseconds vs seconds.</p>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h2 id="Space---the-final-frontier">Space - the final frontier</h2><p>Looking again at triplet_sum2, I see that after the necessary sorting; many sliced copies of the <code>numbers</code> list are created in both enumerations.</p>
<p>We could use <code>islice</code> to stop those copies.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> itertools </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> islice</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> triplet_sum3(numbers: list[int], target: int) -> list[tuple[int]]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Triplet-sum with short-circuited combinations and less copying of numbers."</span></div><div><span style="color: #d4d4d4;"> numbers.sort()</span></div><div><span style="color: #d4d4d4;"> answers = []</span></div><div><span style="color: #d4d4d4;"> </span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i, item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(numbers):</span></div><div><span style="color: #d4d4d4;"> isum = item</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> isum <= target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> j, jtem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(islice(numbers, i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">), i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> jsum = isum + jtem</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> jsum <= target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> k, ktem </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(islice(numbers, j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">), j+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> ksum = jsum + ktem</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> ksum == target:</span></div><div><span style="color: #d4d4d4;"> answers.append((item, jtem, ktem))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">elif</span><span style="color: #d4d4d4;"> ksum > target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># Skip as jsum > target</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># Skip as item > target</span></div><div><span style="color: #d4d4d4;"> </span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> answers</span></div><br /></div><pre></pre></div>
</div>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">%timeit triplet_sum3(list(range(</span><span style="color: #b5cea8;">666</span><span style="color: #d4d4d4;">)), </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)</span></div><br /></div><pre></pre></div>
</div>
</div>
</div>
</div>
<div class="jp-Cell-outputWrapper">
<div class="jp-OutputArea jp-Cell-outputArea">
<div class="jp-OutputArea-child">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain">
<pre>13.9 µs ± 34.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Hmmm, even faster than triplet_sum2 as it does not have to create extra list slices.</p>
<p>Lets see if it tests the same:</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">tests1([triplet_sum2, triplet_sum3])</span></div><br /></div><br /></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain"><pre> triplet_sum2([2, 0, 1, 3], 4) = [(0, 1, 3)]
triplet_sum3([0, 1, 2, 3], 4) = [(0, 1, 3)]
---
triplet_sum2([0, 1, 3, 2, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]
triplet_sum3([0, 1, 2, 3, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]
---
triplet_sum2([0, 1, 2], 6) = []
triplet_sum3([0, 1, 2], 6) = []
---
triplet_sum2([0, 1, 2], 3) = [(0, 1, 2)]
triplet_sum3([0, 1, 2], 3) = [(0, 1, 2)]
---
triplet_sum2([0, 1, 2], 1) = []
triplet_sum3([0, 1, 2], 1) = []
---
triplet_sum2([0, 1], 2) = []
triplet_sum3([0, 1], 2) = []
---
triplet_sum2([0, 1], 1) = []
triplet_sum3([0, 1], 1) = []
---
triplet_sum2([0], 0) = []
triplet_sum3([0], 0) = []
---
triplet_sum2([], 99) = []
triplet_sum3([], 99) = []
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h1 id="Pairs,-triplets,-quads,-...-Prancing-Pointers">Pairs, triplets, quads, ... Prancing Pointers</h1><p>The indices <code>i, j, k</code>
are fixed at three variables for generating triplets, and rely on a
nesting of a static three for-loops. If I used an array of all these
indices in order, called <code>indices</code> I could have a pointer called <code>incrementing</code> that indexed which of these <code>indices</code> to increment at any time.</p>
<p>Similarly I could keep <code>isum, jsum, ksum</code> the (partial) summs of the elements of <code>numbers</code> pointed to by <code>indices</code>, in its own array called <code>accum</code> - short for accumulator.</p>
<p>Last but not least, I will use an argument <code>n_tuple</code> defaulting to 3 to sum triples, 4 to sum quads, ...</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell jp-mod-noOutputs"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><pre><span></span></pre><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> triplet_sum4(numbers: list[int], target: int, n_tuple: int=</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">) \</span></div><div><span style="color: #d4d4d4;"> -> list[tuple[int]]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Generate triplets from numbers that sum to target in sorted order.</span></div><div><span style="color: #ce9178;"> Indexes the sorted numbers without otherwise copying them.</span></div><br /><div><span style="color: #ce9178;"> n_tuple=3 for triplets, 2 for pairs, etc, for any +ve int</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #d4d4d4;"> numbers.sort()</span></div><div><span style="color: #d4d4d4;"> n_len = len(numbers)</span></div><div><span style="color: #d4d4d4;"> answers = []</span></div><div><span style="color: #d4d4d4;"> indices = [-</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">] * n_tuple </span><span style="color: #6a9955;"># indices for triples; indices[0] must be -1</span></div><div><span style="color: #d4d4d4;"> incrementing = </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># index to increment at any time</span></div><div><span style="color: #d4d4d4;"> accum = [</span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">] * n_tuple </span><span style="color: #6a9955;"># (partial) sums in accumulator</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">while</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">True</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> indices[incrementing] += </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> indices[incrementing] >= n_len:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># maxed out this index so go back one</span></div><div><span style="color: #d4d4d4;"> incrementing -= </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> incrementing < </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">continue</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># continue incrementing 'outer' index</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> incrementing < n_tuple - </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> indices[incrementing+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">] = indices[incrementing]</span></div><div><span style="color: #d4d4d4;"> acc = accum[incrementing] = numbers[indices[incrementing]] + (</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> incrementing == </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;"> accum[incrementing - </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">])</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> acc > target:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># maxed out this index so go back one</span></div><div><span style="color: #d4d4d4;"> incrementing -= </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> incrementing < </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">break</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">continue</span></div><br /><div><span style="color: #d4d4d4;"> incrementing += </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># next index</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">elif</span><span style="color: #d4d4d4;"> incrementing == n_tuple - </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># updated last index</span></div><div><span style="color: #d4d4d4;"> acc = numbers[indices[incrementing]] + (</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> incrementing == </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;"> accum[incrementing - </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">])</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> acc == target:</span></div><div><span style="color: #d4d4d4;"> answers.append(tuple(numbers[i] </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> indices))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> answers</span></div><br /></div><pre>You can see why I mention prancing pointers.</pre></div></div></div></div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Lets check it works for triplets.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">tests1([triplet_sum3, triplet_sum4])</span></div><br /></div><br /></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain"><pre> triplet_sum3([2, 0, 1, 3], 4) = [(0, 1, 3)]
triplet_sum4([0, 1, 2, 3], 4) = [(0, 1, 3)]
---
triplet_sum3([0, 1, 3, 2, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]
triplet_sum4([0, 1, 2, 3, 4, 5, 6], 11) = [(0, 5, 6), (1, 4, 6), (2, 3, 6), (2, 4, 5)]
---
triplet_sum3([0, 1, 2], 6) = []
triplet_sum4([0, 1, 2], 6) = []
---
triplet_sum3([0, 1, 2], 3) = [(0, 1, 2)]
triplet_sum4([0, 1, 2], 3) = [(0, 1, 2)]
---
triplet_sum3([0, 1, 2], 1) = []
triplet_sum4([0, 1, 2], 1) = []
---
triplet_sum3([0, 1], 2) = []
triplet_sum4([0, 1], 2) = []
---
triplet_sum3([0, 1], 1) = []
triplet_sum4([0, 1], 1) = []
---
triplet_sum3([0], 0) = []
triplet_sum4([0], 0) = []
---
triplet_sum3([], 99) = []
triplet_sum4([], 99) = []
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>Can it reject unnecessary triplets?</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">%timeit triplet_sum4(list(range(</span><span style="color: #b5cea8;">666</span><span style="color: #d4d4d4;">)), </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)</span></div><br /></div><br /></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain"><pre>1.42 ms ± 8.19 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p>This time its milliseconds instead of the seconds of triplet_sum1, but not as fast as the microseconds of triplet_sum3.</p>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<h2 id="Versatility">Versatility</h2><p>Lets try other than triplets with this new for-loop-less algorithm.</p>
</div>
</div>
</div>
</div><div class="jp-Cell jp-CodeCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-CodeMirrorEditor jp-Editor jp-InputArea-editor" data-type="inline">
<div class="CodeMirror cm-s-jupyter">
<div class="highlight hl-ipython3"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> test_tuples():</span></div><div><span style="color: #d4d4d4;"> args = [([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ([</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">], </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">),</span></div><div><span style="color: #d4d4d4;"> ]</span></div><div><span style="color: #d4d4d4;"> func = triplet_sum4</span></div><div><span style="color: #d4d4d4;"> help(func)</span></div><br /><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> arg </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> args:</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">" </span><span style="color: #d4d4d4;">{func.__name__}{arg}</span><span style="color: #ce9178;"> ="</span><span style="color: #d4d4d4;">, end=</span><span style="color: #ce9178;">' '</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">try</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> answer = func(*arg)</span></div><div><span style="color: #d4d4d4;"> print(answer)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">except</span><span style="color: #d4d4d4;"> Exception </span><span style="color: #569cd6;">as</span><span style="color: #d4d4d4;"> ex:</span></div><div><span style="color: #d4d4d4;"> print(ex)</span></div><br /><div><span style="color: #d4d4d4;">test_tuples()</span></div><br /></div><br /></div></div></div></div></div><div class="jp-Cell jp-CodeCell jp-Notebook-cell"><div class="jp-Cell-outputWrapper"><div class="jp-OutputArea jp-Cell-outputArea"><div class="jp-OutputArea-child"><div class="jp-RenderedText jp-OutputArea-output" data-mime-type="text/plain"><pre>Help on function triplet_sum4 in module __main__:
triplet_sum4(numbers: list[int], target: int, n_tuple: int = 3) -> list[tuple[int]]
Generate triplets from numbers that sum to target in sorted order.
Indexes the sorted numbers without otherwise copying them.
n_tuple=3 for triplets, 2 for pairs, etc, for any +ve int
triplet_sum4([0, 1, 2, 3, 4], 5, 2) = [(1, 4), (2, 3)]
triplet_sum4([0, 1, 2, 3, 4, 5, 6], 5, 2) = [(0, 5), (1, 4), (2, 3)]
triplet_sum4([0, 1, 2, 3, 4, 5, 6], 5, 3) = [(0, 1, 4), (0, 2, 3)]
triplet_sum4([0, 1, 2, 3, 4, 5, 6], 5, 4) = []
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="jp-Cell jp-MarkdownCell jp-Notebook-cell">
<div class="jp-Cell-inputWrapper">
<div class="jp-InputArea jp-Cell-inputArea">
</div></div></div><div class="jp-Cell jp-MarkdownCell jp-Notebook-cell"><div class="jp-Cell-inputWrapper"><div class="jp-InputArea jp-Cell-inputArea"><div class="jp-RenderedHTMLCommon jp-RenderedMarkdown jp-MarkdownOutput" data-mime-type="text/markdown">
<p><b>END.</b></p>
</div>
</div>
</div>
</div>
Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-72509401429445622022022-09-25T10:01:00.002+01:002022-09-25T10:01:28.588+01:00Answering a Reddit Question with timings.<p><b><span style="color: #2b00fe; font-size: x-small;">Best viewed on larger than a phone screen.</span></b></p><p> </p><p>Someone had <a href="https://www.reddit.com/r/Python/comments/xm7ina/looking_for_a_great_algorithm_to_search_a_string/" rel="nofollow" target="_blank">a problem on Reddit r/python</a>:</p><p><span style="font-family: "Noto Sans", Arial, sans-serif; font-size: 14px;"></span></p><blockquote><span style="background-color: #d9ead3;"><span style="color: #073763;">Hello guys, I want to find a string in a list and this list has 350K elements all they are strings . I want to find out a good algorithm that can find the string very quick . I know linear search but want to figure out other ways if possible.</span></span></blockquote><p></p><p>The problem had already had over seventy answers when I came across it, but I wanted to check:</p><p></p><ol style="text-align: left;"><li>Had people teased more information out of the original poster?</li><li>Was there a pocket of timed goodness in there?</li></ol><h3 style="text-align: left;"><b>I read on.</b></h3><p></p><p>The answers given were polite. Yay, here's to the python community 🍻👌🏾👍🏾! </p><p>The answers did give alternatives to linear search, but so many posts had no timings, and the OP was still pretty vague in the data he gave, considering he was wanting "faster".</p><p>I was late to this party, but wanted to write some Python, as I can find coding relaxing if I write in the spirit of helping, (and remember I too might learn something). It's not the first time I've tried to help out and decided on three points:</p><p></p><ol style="text-align: left;"><li>Get more realistic timings, or a timing framework were they can go in and alter things to get timings that more represent their data.</li><li>The answers mentioned linear search of the text, sets, and tries. I would skip tries as I have already found that its hard to get over their speed of interpretation limitations.</li><li> I'm gonna need several files to do the timings - lets try creating them from one initial python file. (Why not, keeps me interested).</li></ol><p></p><h3 style="text-align: left;">Coding.</h3><p>I chose to code in the Spyder IDE for this, rather than Vscode. YMMVm but I still find Spyder to support a more "dynamic" development style, great for scripting and I am not writing hundreds of lines of code for this.</p><p>I thought of the steps I would need, seemed simple enough so just started coding the first, <span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">string_in_long_list_1.py</span>.</p><h4 style="text-align: left;">Create a text file of 350K sorted "strings"</h4><p>What is meant by strings? I don't know, don't sweat it - I'll make each line several words with one word having an increasing count appended:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Created on Sat Sep 24 10:00:51 2022</span></div><br /><div><span style="color: #ce9178;">@author: paddy3118</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #6a9955;"># %% Create txt file of 350_000 strings</span></div><br /><div><span style="color: #569cd6;">from</span> pathlib <span style="color: #569cd6;">import</span> Path</div><br /><div>lines = <span style="color: #ce9178;">'\n'</span>.join(<span style="color: #569cd6;">f</span><span style="color: #ce9178;">"Fee fi word</span>{i<span style="color: #569cd6;">:06</span>}<span style="color: #ce9178;"> fum"</span> <span style="color: #569cd6;">for</span> i <span style="color: #569cd6;">in</span> range(<span style="color: #b5cea8;">350_000</span>))</div><br /><div>txtfile = Path(<span style="color: #ce9178;">'string_in_long_list_2.txt'</span>)</div><div>txtfile.write_text(lines, <span style="color: #ce9178;">'utf8'</span>)</div><br /></div><p>Notice the <span style="background-color: #1e1e1e; color: #6a9955; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"># %%</span> cell comment. It allows me to quickly run and rerun the code in the cell and check the values of variables in Spyder until I get it right. (Vscode will do this too, but I'm more comfortable with Spyder's implementation).</p><h4 style="text-align: left;">Sets; On your marks...</h4><p>If the OP was going to test for strings in the text file multiple times for the one text file, which I think I read in the original posters replies to others, then I would like to time set inclusion, but reduce the time to create or read the set. </p><p>I decided on creating a module file that assigns the set of lines in the text file to a variable <span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">data</span>. Another prog would load this module and check for string inclusion and compile the module <i>so subsequent loadings would be faster</i>. </p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># %% Create a module of the set of those lines assigned to name 'data'</span></div><br /><div><span style="color: #569cd6;">import</span> pprint</div><br /><div>set_of_strings = set(txtfile.read_text(<span style="color: #ce9178;">'utf8'</span>).split(<span style="color: #ce9178;">'\n'</span>))</div><div>moduletxt = <span style="color: #569cd6;">f</span><span style="color: #ce9178;">"""</span><span style="color: #569cd6;">\</span></div><div><span style="color: #ce9178;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"Set of each line from the text file."</span></div><div><span style="color: #ce9178;">data = </span>{pprint.pformat(set_of_strings, indent=<span style="color: #b5cea8;">2</span>)}</div><div><span style="color: #ce9178;">"""</span></div><br /><div>modulefile = Path(<span style="color: #ce9178;">'string_in_long_list_3.py'</span>)</div><div>modulefile.write_text(moduletxt, <span style="color: #ce9178;">'utf8'</span>)</div></div><p>I ran the cell and checked the generated script until it looked right.</p><h4 style="text-align: left;">Linear search.</h4><p>Although the OP said the text file was sorted, others had already noted that trying binary search would be slowed as you would have to read every line first into a list... you might as well do a comparison as you read each line, so I needed to generate a script to do just that:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># %% Create a script to just search for a line containing its arg whilst reading.</span></div><br /><div>scripttxt = <span style="color: #ce9178;">"""</span><span style="color: #569cd6;">\</span></div><div><span style="color: #ce9178;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"Search for first line in text file matching argument."</span></div><br /><div><span style="color: #ce9178;">import sys</span></div><br /><div><span style="color: #ce9178;">to_find = sys.argv[1] if len(sys.argv) > 1 else ''</span></div><br /><div><span style="color: #ce9178;">with open('string_in_long_list_2.txt', encoding='utf8') as txtfile:</span></div><div><span style="color: #ce9178;"> for line in txtfile:</span></div><div><span style="color: #ce9178;"> if to_find == line.rstrip('\\n'):</span></div><div><span style="color: #ce9178;"> print("True")</span></div><div><span style="color: #ce9178;"> sys.exit(0)</span></div><div><span style="color: #ce9178;"> break</span></div><div><span style="color: #ce9178;"> else:</span></div><div><span style="color: #ce9178;"> print("False")</span></div><div><span style="color: #ce9178;"> sys.exit(1)</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div>linesearchfile = Path(<span style="color: #ce9178;">'string_in_long_list_4.py'</span>)</div><div>linesearchfile.write_text(scripttxt, <span style="color: #ce9178;">'utf8'</span>)</div><br /><div><span style="color: #6a9955;"></span></div></div><p>When writing this, I first had no tripple quotes around what became <span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">scripttxt =</span>, and could rerun the contained code as I debugged, then encapsulate in <span style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">scripttxt = </span><span style="color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">"""</span><span style="color: #569cd6; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">\</span> and add the the last couple of lines to generate the file.</p><h4 style="text-align: left;"> Match against set</h4><p>This generates the python script that when given a string as its argument, will check against <span style="background-color: #1e1e1e; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">data</span> loaded as a set from its module.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># %% Create a script to look for the arg in the modules data set</span></div><br /><div>scripttxt = <span style="color: #ce9178;">"""</span><span style="color: #569cd6;">\</span></div><div><span style="color: #ce9178;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"test if argument is in data loaded from module as a set."</span></div><br /><div><span style="color: #ce9178;">from string_in_long_list_3 import data</span></div><div><span style="color: #ce9178;">import sys</span></div><br /><div><span style="color: #ce9178;">to_find = sys.argv[1] if len(sys.argv) > 1 else ''</span></div><br /><div><span style="color: #ce9178;">if to_find in data:</span></div><div><span style="color: #ce9178;"> print("True")</span></div><div><span style="color: #ce9178;"> sys.exit(0)</span></div><br /><div><span style="color: #ce9178;">print("False")</span></div><div><span style="color: #ce9178;">sys.exit(1)</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div>modulesearchfile = Path(<span style="color: #ce9178;">'string_in_long_list_5.py'</span>)</div><div>modulesearchfile.write_text(scripttxt, <span style="color: #ce9178;">'utf8'</span>)</div></div><p>The end of the Python script!</p><h3 style="text-align: left;">Timing</h3><p>I was always going to time runs of the scripts out in the shell of the OS, for which I, and many others are familiar with the GNU/Linux time command. I would add comments to tell a story and show I had "nothing up my sleeves".</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div>(py310) $</div><div>(py310) $ ls</div><div>string_in_long_list_1.py</div><div>(py310) $</div><div>(py310) $ <span style="color: #6a9955;"># New dir</span></div><div>(py310) $ mkdir work</div><div>(py310) $ cd work</div><div>(py310) $ ls -a</div><div>. ..</div><div>(py310) $ cp ../string_in_long_list_1.py .</div><div>(py310) $ ls -a</div><div>. .. string_in_long_list_1.py</div><div>(py310) $</div><div>(py310) $ <span style="color: #6a9955;"># Create files</span></div><div>(py310) $ python3 string_in_long_list_1.py</div><div>(py310) $</div><div>(py310) $ <span style="color: #6a9955;"># (How many lines)</span></div><div>(py310) $ wc -l *</div><div> 75 string_in_long_list_1.py</div><div> 349999 string_in_long_list_2.txt</div><div> 350002 string_in_long_list_3.py</div><div> 16 string_in_long_list_4.py</div><div> 14 string_in_long_list_5.py</div><div> 700106 total</div><div>(py310) $</div><div>(py310) $ <span style="color: #6a9955;"># time linear search of text file</span></div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_4.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.102s</div><div>user 0m0.058s</div><div>sys 0m0.000s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_4.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.102s</div><div>user 0m0.045s</div><div>sys 0m0.015s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_4.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.094s</div><div>user 0m0.043s</div><div>sys 0m0.009s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_4.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.100s</div><div>user 0m0.056s</div><div>sys 0m0.000s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_4.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.106s</div><div>user 0m0.030s</div><div>sys 0m0.030s</div><div>(py310) $</div><div>(py310) $ ls</div><div>string_in_long_list_1.py string_in_long_list_3.py string_in_long_list_5.py</div><div>string_in_long_list_2.txt string_in_long_list_4.py</div><div>(py310) $</div><div>(py310) $ <span style="color: #6a9955;"># time creation of module .pyc file and search of data in set</span></div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_5.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m1.578s</div><div>user 0m0.869s</div><div>sys 0m0.667s</div><div>(py310) $</div><div>(py310) $ <span style="color: #6a9955;"># time search of data in set from compiled module file</span></div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_5.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.186s</div><div>user 0m0.120s</div><div>sys 0m0.047s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_5.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.220s</div><div>user 0m0.178s</div><div>sys 0m0.021s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_5.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.199s</div><div>user 0m0.155s</div><div>sys 0m0.023s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_5.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.193s</div><div>user 0m0.136s</div><div>sys 0m0.033s</div><div>(py310) $ <span style="color: #569cd6;">time</span> python3 string_in_long_list_5.py <span style="color: #ce9178;">'Fee fi word175000 fum'</span></div><div>True</div><br /><div>real 0m0.187s</div><div>user 0m0.127s</div><div>sys 0m0.040s</div><div>(py310) $</div></div><p>My linear search is faster than the set lookup, even after the set data is precompiled into a .pyc module.</p><p>The OP could adapt to use their text file I guess, and use more typical string searches, but only they have that data.</p><h3 style="text-align: left;">Big O</h3><p>The thing about big O comparisons that cloud their use when people have real data is:</p><p></p><ul style="text-align: left;"><li>People forget that actual timings put values on the constants that are abstracted away when you compare only BigO. If the actual timings of two algorithms are 100x + 100 and 3*x**2 + 2*x + 1; then although the first algorithm is proportional to x and the second is proportional to x**2, there is a range for x, the data, where the second x**2 algorithm is faster.</li><li>Python is interpreted. It makes it harder to get the true BigO dependency for an interpreted algorithm when things like variable name accesses and repeated code interpretation speeds may be significant. </li></ul><p></p><p>If people want <b>faster</b>, then they should ask with an expectation of needing to give <b>data</b>.</p><p><br /></p><p><b> END.</b></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-63076736858934110342022-07-10T11:47:00.000+01:002022-07-10T11:47:42.914+01:00Raw Python vs Python & SQLite vs GNU Linux command line utilities!<p style="text-align: left;"><span style="font-size: x-small;"><u><b><span style="font-family: verdana;">Note: Long lines may reward those reading on larger screens than phones.</span></b></u></span></p><p style="text-align: left;"> <br /></p><p style="text-align: left;">There was an <a href="https://www.reddit.com/r/Python/comments/vk872e/sort_3_000_000_000_lines_by_most_repeated_one_via/" rel="nofollow">interesting problem</a> posted to reddit:</p><ol style="text-align: left;"><li>A large text file has one word per line</li><li>Words have many duplications on other lines</li><li><b>Sort </b>the file in order of lines with most duplications first.</li></ol><p>The size of the file is in comparison with the amount of memory available to sort it - no in-memory sorts of the file can be assumed to work.</p><p>Many commenters ignored the request of producing a sort of the input file and instead considered generating only the unique words of the input in order of their decreasing occurrence. That would not be a sort as duplicate entries would be removed.</p><p>The original poster posted to the r/Python reddit group but said he would prefer a Linux command line solution.</p><h2 style="text-align: left;">sort, uniq, awk</h2><p> I remembered that the original sort was written when memory was in kilobytes and data being larger than memory was much more common. Original sort could use disk to sort files that could not fit in memory quite seemlessly and Gnu sort has that capability too.</p><p>I was going to:</p><ol style="text-align: left;"><li> <span style="font-family: Source Code Pro;">sort </span>the file</li><li>Use <span style="font-family: Source Code Pro;">uniq </span>to count the occurrences</li><li><span style="font-family: Source Code Pro;">sort </span>the word-counts numerically by order of the counts.</li><li>Expand the ordered words by their counts to create the answer using <span style="font-family: Source Code Pro;">awk</span>.</li></ol><p>Here's the one-liner working on some test input:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">$ printf </span><span style="color: #ce9178;">'w1\nw4\nw3\nw1\nw2\nw1\nw3\nw4\nw3\nw2\n'</span><span style="color: #d4d4d4;"> > /tmp/word.lst</span></div><div><span style="color: #d4d4d4;">$ cat /tmp/word.lst</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w4</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w2</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w4</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w2</span></div><div><span style="color: #d4d4d4;">$ sort /tmp/word.lst | uniq -c | sort -n -r</span></div><div><span style="color: #d4d4d4;"> 3 w3</span></div><div><span style="color: #d4d4d4;"> 3 w1</span></div><div><span style="color: #d4d4d4;"> 2 w4</span></div><div><span style="color: #d4d4d4;"> 2 w2</span></div><div><span style="color: #d4d4d4;">$ sort /tmp/word.lst | uniq -c | sort -n -r| awk </span><span style="color: #ce9178;">'{for(i=0;i<$1;i++){print$2}}'</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w4</span></div><div><span style="color: #d4d4d4;">w4</span></div><div><span style="color: #d4d4d4;">w2</span></div><div><span style="color: #d4d4d4;">w2</span></div><div><span style="color: #d4d4d4;">$</span></div></div><p></p><h3 style="text-align: left;">Test data</h3><p>What makes this problem is the size of the data - The input text needs to be individual words per line and have words to differing frequencies of occurrencies.</p><p>I decided to download the text to Huck` Finn and double that up until I had a file larger than my own 8Gigs of memory.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">$ </span><span style="color: #6a9955;"># Download Huck_finn:</span></div><div><span style="color: #d4d4d4;">$ curl https://gutenberg.org/files/76/76-0.txt -o huck_finn.txt</span></div><div><span style="color: #d4d4d4;">$ </span><span style="color: #6a9955;"># One word per line using fmt:</span></div><div><span style="color: #d4d4d4;">$ fmt -w1 huck_finn.txt >huck_word_per_line.txt</span></div><div><span style="color: #d4d4d4;">$ </span><span style="color: #6a9955;"># Double up to increase the file size:</span></div><div><span style="color: #d4d4d4;">$ cp huck_word_per_line.txt words.lst</span></div><div><span style="color: #d4d4d4;">$ cat words.lst words.lst >> words2.lst && mv words2.lst words.lst && wc words.lst && du -h words.lst</span></div><div><span style="color: #d4d4d4;"> 253464 228666 1239542 words.lst</span></div><div><span style="color: #d4d4d4;">1.2M words.lst</span></div><div><span style="color: #d4d4d4;">$ cat words.lst words.lst >> words2.lst && mv words2.lst words.lst && wc words.lst && du -h words.lst</span></div><div><span style="color: #d4d4d4;"> 506928 457332 2479084 words.lst</span></div><div><span style="color: #d4d4d4;">2.4M words.lst</span></div><br /><div><span style="color: #d4d4d4;">...</span></div><br /><div><span style="color: #d4d4d4;">$ cat words.lst words.lst >> words2.lst && mv words2.lst words.lst && wc words.lst && du -h words.lst</span></div><div><span style="color: #d4d4d4;"> 2076377088 1873231872 10154328064 words.lst</span></div><div><span style="color: #d4d4d4;">9.5G words.lst</span></div><br /></div><p></p><p><br /></p><p>The resultant file may have "junk" lines of just whitespace, numbers, or punctuation but it should still serve its purpose.</p><p> </p><h3 style="text-align: left;">sort, uniq, awk: timings<br /></h3><p>I split the command line above into two and timed each half:<br /></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">$ </span><span style="color: #6a9955;"># Find the frequency of each word</span></div><div><span style="color: #d4d4d4;">$ </span><span style="color: #569cd6;">time</span><span style="color: #d4d4d4;"> sort words.lst |uniq -c > words_uniq_count.txt</span></div><br /><div><span style="color: #d4d4d4;">real 20m5.247s</span></div><div><span style="color: #d4d4d4;">user 83m33.507s</span></div><div><span style="color: #d4d4d4;">sys 1m38.863s</span></div><div><span style="color: #d4d4d4;">$ </span><span style="color: #6a9955;"># Now to order by most frequent first and expand the counts into that many lines of words:</span></div><div><span style="color: #d4d4d4;">$ </span><span style="color: #569cd6;">time</span><span style="color: #d4d4d4;"> sort -n -r words_uniq_count.txt | awk </span><span style="color: #ce9178;">'$2~/[a-zA-Z]/{for(i=0;i<$1;i++){print$2}}'</span><span style="color: #d4d4d4;"> > words_ordered.txt</span></div><br /><div><span style="color: #d4d4d4;">real 7m56.900s</span></div><div><span style="color: #d4d4d4;">user 3m34.341s</span></div><div><span style="color: #d4d4d4;">sys 0m33.422s</span></div><br /></div><p></p><p><br /></p><p>That is 28minutes in total.<br /></p><h2 style="text-align: left;">Pure Python</h2><p>When thinking of my purely Python solution, I reread the original post which emphasised the amount of duplication in the large file. I tookj a gamble in assuming that I would always have enough memory to process the <i><b>frequency of each individual word</b></i>.</p><p>The Python script streams the file in counting the frequency of each line then uses Counter.most_common to order the output.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> fileinput</span></div><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> collections </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> Counter</span></div><br /><div><span style="color: #d4d4d4;">word_freq = Counter(line </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> ln </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> fileinput.input()</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> (line := ln.strip())) </span><span style="color: #6a9955;"># No blank lines</span></div><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> word, count </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> word_freq.most_common():</span></div><div><span style="color: #d4d4d4;"> print((word + </span><span style="color: #ce9178;">'\n'</span><span style="color: #d4d4d4;">) * count, end=</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">)</span></div><br /></div><p></p><p><br /></p><h3 style="text-align: left;">Pure Python: timing</h3><p>(For the same 9.5G input file)</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">real 21m53.181s</span></div><div><span style="color: #d4d4d4;">user 16m26.880s</span></div><div><span style="color: #d4d4d4;">sys 0m22.188s</span></div><br /></div><p></p><p><br /></p><h2 style="text-align: left;">Python and sqlite3</h2><p>User bOne123 had a<a href="https://www.reddit.com/r/Python/comments/vk872e/comment/idoiq43/?utm_source=share&utm_medium=web2x&context=3" rel="nofollow"> neat solution</a> that used sqlite3 from Python <i><b>his </b></i>code is reproduced here:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># -*- coding: utf-8 -*-</span></div><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">@author: bOne123</span></div><div><span style="color: #ce9178;">"""</span></div><br /><div><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> sqlite3</span></div><div><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> time</span></div><div><span style="color: #d4d4d4;">con = sqlite3.connect(</span><span style="color: #ce9178;">'/tmp/file.db'</span><span style="color: #d4d4d4;">) </span><span style="color: #6a9955;"># or 'file.db' if not enough ram</span></div><div><span style="color: #d4d4d4;">cur = con.cursor()</span></div><div><span style="color: #d4d4d4;">cur.execute(</span><span style="color: #ce9178;">'''CREATE TABLE IF NOT EXISTS data (word text, count int)'''</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">cur.execute(</span><span style="color: #ce9178;">'''CREATE UNIQUE INDEX IF NOT EXISTS dataidx ON data(word)'''</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">now = time.time()</span></div><div><span style="color: #569cd6;">with</span><span style="color: #d4d4d4;"> open(</span><span style="color: #ce9178;">"words.lst"</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"r"</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">as</span><span style="color: #d4d4d4;"> f:</span></div><div><span style="color: #d4d4d4;"> i = </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #d4d4d4;"> words = []</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> line </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> f:</span></div><div><span style="color: #d4d4d4;"> words.append((line,))</span></div><div><span style="color: #d4d4d4;"> i += </span><span style="color: #b5cea8;">1</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> i % </span><span style="color: #b5cea8;">50_000_000</span><span style="color: #d4d4d4;"> == </span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> print(time.time() - now, i)</span></div><div><span style="color: #d4d4d4;"> cur.executemany(</span><span style="color: #ce9178;">"INSERT INTO data VALUES (?, 1) ON CONFLICT(word) DO UPDATE SET count=count+1"</span><span style="color: #d4d4d4;">, words)</span></div><div><span style="color: #d4d4d4;"> words=[]</span></div><div><span style="color: #d4d4d4;">print(time.time() - now, </span><span style="color: #ce9178;">'index counts'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">cur.execute(</span><span style="color: #ce9178;">'''CREATE INDEX IF NOT EXISTS countidx ON data(count)'''</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #569cd6;">with</span><span style="color: #d4d4d4;"> open(</span><span style="color: #ce9178;">"words_ordered_by_bOne123.txt"</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">"w"</span><span style="color: #d4d4d4;">, newline=</span><span style="color: #ce9178;">'\n'</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">as</span><span style="color: #d4d4d4;"> f:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> row </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> cur.execute(</span><span style="color: #ce9178;">'SELECT word,count FROM data ORDER BY count DESC'</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(row[</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">]):</span></div><div><span style="color: #d4d4d4;"> f.write(row[</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">])</span></div><div><span style="color: #d4d4d4;">print(time.time() - now, </span><span style="color: #ce9178;">'done'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">con.close()</span></div><br /></div><p></p><h2 style="text-align: left;">Python and sqlite3: timing</h2><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">$ </span><span style="color: #569cd6;">time</span><span style="color: #d4d4d4;"> python sort_words_by_bOne123.py</span></div><div><span style="color: #d4d4d4;">22.579142570495605 50000000</span></div><div><span style="color: #d4d4d4;">102.96021580696106 100000000</span></div><div><span style="color: #d4d4d4;">...</span></div><div><span style="color: #d4d4d4;">3125.1875145435333 2050000000</span></div><div><span style="color: #d4d4d4;">3189.4789474010468 index counts</span></div><div><span style="color: #d4d4d4;">3583.4524500370026 </span><span style="color: #569cd6;">done</span></div><br /><div><span style="color: #d4d4d4;">real 59m44.886s</span></div><div><span style="color: #d4d4d4;">user 50m36.011s</span></div><div><span style="color: #d4d4d4;">sys 2m11.008s</span></div></div><h2 style="text-align: left;"></h2><p></p><p>Those timings take over 50 minutes just to <i class="_7s4syPYtk5hfUIjySXcRE"><b class="_12FoOEddL7j_RgMQN0SNeU">insert</b></i> into the DB.</p><h2 style="text-align: left;">SQLite3<br /></h2><p>User mikeblas has <a href="https://www.reddit.com/r/Python/comments/vk872e/comment/idrx2nh/?utm_source=share&utm_medium=web2x&context=3">another solution</a> that this time uses just sqlite3. Unfortunately I could not get it to work, (as he did). I tried his code to read my 9.5G file into an sqlite3 DB and had to kill it after it ran for nearly two hours and seemed to be doing very little after growing its DB to 27 Gigs.</p><h2 style="text-align: left;">Timings so far</h2><div style="text-align: left;">
<table border="0" cellspacing="0">
<colgroup width="26"></colgroup>
<colgroup width="181"></colgroup>
<colgroup width="86"></colgroup>
<tbody><tr>
<td align="center" height="26"><b>id</b></td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;"><b>Algorithm</b></td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;"><b>Minutes</b></td>
</tr>
<tr>
<td align="center" height="26">1</td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">sort, uniq, awk</td>
<td align="right" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">28</td>
</tr>
<tr>
<td align="center" height="26">2</td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">Pure Python</td>
<td align="right" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">22</td>
</tr>
<tr>
<td align="center" height="26">3</td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">Python and sqlite3</td>
<td align="right" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">60</td>
</tr>
</tbody></table> <br /></div><p>That initial sort for the first algorithm is needed to apply the uniq command to and would take a significant amount of time.</p><p>I ruminated on this for a couple of weeks, searched and eventually found GNU datamash<br /></p><h2 style="text-align: left;">datamash</h2><p>I've been using Unix and Linux for several decades, but have learnt to keep an eye out for new, major, command line utilities, you know, like moving from vi to vim. Well this wqas one of those times when I thought that counting word or line frequencies might be useful enough for people like GNU to write some utility, and I found one; <a href="https://www.gnu.org/software/datamash/manual/datamash.html" rel="nofollow">datamash</a>!</p><p>Some reading and tinkering lead me to the following shell <b>one-liner with its timing</b>:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">$ </span><span style="color: #6a9955;"># Test on small file</span></div><div><span style="color: #d4d4d4;">$ datamash --sort --group 1 count 1 < /tmp/word.lst | sort -n -r -k 2 | awk </span><span style="color: #ce9178;">'NF==2{for(i=0;i<$2;i++){print$1}}'</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w3</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w1</span></div><div><span style="color: #d4d4d4;">w4</span></div><div><span style="color: #d4d4d4;">w4</span></div><div><span style="color: #d4d4d4;">w2</span></div><div><span style="color: #d4d4d4;">w2</span></div><div><span style="color: #d4d4d4;">$ </span><span style="color: #6a9955;"># That worked, so run on 9.5G file</span></div><div><span style="color: #d4d4d4;">$ </span><span style="color: #569cd6;">time</span><span style="color: #d4d4d4;"> datamash --sort --group 1 count 1 < words.lst | sort -n -r -k 2 | awk </span><span style="color: #ce9178;">'NF==2{for(i=0;i<$2;i++){print$1}}'</span><span style="color: #d4d4d4;"> > words_ordered_by_datamash.txt</span></div><br /><div><span style="color: #d4d4d4;">real 19m15.608s</span></div><div><span style="color: #d4d4d4;">user 39m11.182s</span></div><div><span style="color: #d4d4d4;">sys 1m39.364s</span></div><br /></div><p></p><div><p><br /></p><h2 style="text-align: left;">All timings</h2><p>
</p><table border="0" cellspacing="0">
<colgroup width="26"></colgroup>
<colgroup width="181"></colgroup>
<colgroup width="86"></colgroup>
<tbody><tr>
<td align="center" height="26"><b>id</b></td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;"><b>Algorithm</b></td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;"><b>Minutes</b></td>
</tr>
<tr>
<td align="center" height="26">1</td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">sort, uniq, awk</td>
<td align="right" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">28</td>
</tr>
<tr>
<td align="center" height="26">2</td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">Pure Python</td>
<td align="right" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">22</td>
</tr>
<tr>
<td align="center" height="26">3</td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">Python and sqlite3</td>
<td align="right" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">60</td>
</tr>
<tr>
<td align="center" height="26">4</td>
<td align="left" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">datamash</td>
<td align="right" style="border-bottom: 1px solid #000000; border-color: rgb(0, 0, 0); border-left: 1px solid #000000; border-right: 1px solid #000000; border-style: solid; border-top: 1px solid #000000; border-width: 1px;">19</td>
</tr>
</tbody></table>
</div><p> The datamash solution was fastest, but is new to me and I don't yet have a true feel for its internals.</p><p>The pure Python and the sort-uniq-awk solution I am most familiar with.</p><p>I take away from this exercise the following:</p><ol style="text-align: left;"><li>When reading solutions to problems then actual code trumps number of replies and upvotes on just a suggestion of tool to use, (AWS, DB's, ...)</li><li>Try and give a sample of test data and expected output with the question; this helps tie down what is needed in a solution, and in this case would help resolve the problem of those who gave a sort of the input and those that stopped with just ordering unique words.</li><li><i><b>The unix command line is still a powerful tool. </b></i>Those who just learn cloud tools capable of handling terabytes of data but only use them on much smaller datasets ('cos they'll scale), put a smile on those cloud vendors faces.<br /></li></ol><p><b> END.</b><br /></p><p> </p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-6281926356700540562022-06-03T22:50:00.000+01:002022-06-03T22:50:43.714+01:00Python: Yield the first item of consecutive series of equal items, from an iterable.<p> I was reading the source to the <a href="https://more-itertools.readthedocs.io/en/stable/_modules/more_itertools/recipes.html#unique_justseen">more_itertools.unique_unseen</a> function. The function takes an iterator that may have regions where successive items are the same, and yields the item if next item is not the same as it.</p><p><img alt="" src="data:image/png;base64,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" /></p><h2 style="text-align: left;">itertools.groupby</h2><p>Since the problem involves grouping, my first thought is to think of ways to apply itertools.groupby; but that is used in the more_itertools function reproduced below:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> unique_justseen(iterable, key=</span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"""Yields elements in order, ignoring serial duplicates</span></div><br /><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(unique_justseen('AAAABBBCCDAABBB'))</span></div><div><span style="color: #ce9178;"> ['A', 'B', 'C', 'D', 'A', 'B']</span></div><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(unique_justseen('ABBCcAD', str.lower))</span></div><div><span style="color: #ce9178;"> ['A', 'B', 'C', 'A', 'D']</span></div><br /><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> map(next, map(operator.itemgetter(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">), groupby(iterable, key)))</span></div></div><p></p><p>It groups, then extracts the first of each group, and looks to be a good, tight implementation. </p><p>I decided to find another way.</p><h2 style="text-align: left;">Successive items method</h2><p>I thought a bit then decided:</p><ul style="text-align: left;"><li>The first item of the iterable is always returned.</li><li>Other items are returned if they are not equal to the item before it.</li></ul><p>You need the item and the item next to it to determine if that item is yielded. I quiclky toyed with thoughts of teeing and dropping one item from a tee'd branch then comparing items from the zip of the tees; but discarded that. I thought that I could use the walrus operator in a comprehension to keep track of the previous item, (called <b><span style="font-family: courier;">this </span></b>below), and doodled this code:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">it = iter(</span><span style="color: #ce9178;">'AAAABBBCCDAABBB'</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(this := next(it), end=</span><span style="color: #ce9178;">' '</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print([(this := item) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> it </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> item != this])</span></div><div><span style="color: #6a9955;"># Outputs: A ['B', 'C', 'D', 'A', 'B']</span></div></div><p><br /></p><p>It seemed to work, and I set about creating my function.</p><h2 style="text-align: left;">My Function first_of_series</h2><p>Looking at the more_itertools.unique_justseen function above, you should note that it has a <span style="font-family: courier;"><b>key </b></span>argument. The key argument is a function used to transmute the items of the iterable prior to comparison, much like its use in the sorted function. I decided to add that functionality too.</p><p><br /></p><p>My final function is as follows:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> first_of_series(iterable, key=</span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Yield the first item of consecutive series of equal items from an iterable.</span></div><div><span style="color: #ce9178;"> Items have the key function applied for the comparison, if given.</span></div><br /><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(first_of_series('AAAABBBCCDAABBB'))</span></div><div><span style="color: #ce9178;"> ['A', 'B', 'C', 'D', 'A', 'B']</span></div><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(first_of_series('ABBCcAD'))</span></div><div><span style="color: #ce9178;"> ['A', 'B', 'C', 'c', 'A', 'D']</span></div><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(first_of_series('ABBCcAD', key=str.lower))</span></div><div><span style="color: #ce9178;"> ['A', 'B', 'C', 'A', 'D']</span></div><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(first_of_series('ABBcCAD', key=str.lower))</span></div><div><span style="color: #ce9178;"> ['A', 'B', 'c', 'A', 'D']</span></div><br /><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #d4d4d4;"> it = iter(iterable) </span><span style="color: #6a9955;"># Change iterable into an iterator!</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">yield</span><span style="color: #d4d4d4;"> (this := next(it))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> key </span><span style="color: #569cd6;">is</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">yield from</span><span style="color: #d4d4d4;"> (item </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> it </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> this != (this := item))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> this = key(this)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">yield from</span><span style="color: #d4d4d4;"> (item </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> item </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> it </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> this != (this := key(item)))</span></div></div><p></p><p>It works like this:</p><ol style="text-align: left;"><li>Form an iterator from any input iterable so you can call next() on it.</li><li>Set this to the first item from the iterable and also yield it.</li><li>If there is no key:</li><ol><li>yield from a comprehension yielding items from the iterable but only if the last i.e <b><span style="font-family: courier;">this </span></b>is not equal to the current, <span style="font-family: courier;"><b>item </b></span>from the list. </li><li>The walrus assignment updates <span style="font-family: courier;"><b>this</b></span>.</li></ol><li>Else if there is a key function:</li><ol><li><span style="font-family: courier;"><b>this </b></span>is now always the value of the key function applied to items from the iterable.</li><li>The <b><i>comparison </i></b>in the comprehension is always of values of the key function applied to items from the iterable; although the plain items are yielded.</li></ol></ol><p>I have to rely on the left-to-right order of evaluation for the if condition in the comprehensions to get the updating of variable <span style="font-family: courier;"><b>this </b></span>correct; and it's more verbose than the groupby version.</p><p><br /></p><p>I did have fun creating it though, (and it reinforces my rule of using groupby when grouping).<br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-12013212502274320912022-06-02T11:59:00.001+01:002022-06-02T12:06:34.968+01:00Python: My nwise compared to more_itertools.sliding_window <p>In my last <a href="https://paddy3118.blogspot.com/2022/06/from-pairwise-to-n-wise.html" target="_blank">post </a>I evolved two n-wise functions from the itertools.pairwise code to create functions that will generate a sliding window of arbitrary width from an input iterator.</p><p>Somone <a href="https://www.reddit.com/r/Python/comments/v2c076/comment/iarxtfg/?utm_source=share&utm_medium=web2x&context=3" target="_blank">commented on reddit </a>about my blog stating:</p><p class="_1qeIAgB0cPwnLhDF9XSiJM" style="margin-left: 40px; text-align: left;">"Is there a reason why not just do it as they do it in <code class="_34q3PgLsx9zIU5BiSOjFoM">more-itertools</code>?"</p><p class="_1qeIAgB0cPwnLhDF9XSiJM" style="margin-left: 40px; text-align: left;">Then gave the <a href="https://more-itertools.readthedocs.io/en/stable/_modules/more_itertools/recipes.html#sliding_window" target="_blank">source from the more itertools site</a> that I extract below:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; margin-left: 40px; text-align: left; white-space: pre;"><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> collections </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> deque</span><span style="color: #d4d4d4;">, Counter</span><span style="color: #d4d4d4;"></span><span style="color: #d4d4d4;"></span></div><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> itertools </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> islice</span></div><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> timeit </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> Timer</span></div><div><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> pandas </span><span style="color: #569cd6;">as</span><span style="color: #d4d4d4;"> pd</span></div></div><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; margin-left: 40px; text-align: left; white-space: pre;"><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> sliding_window(iterable, n):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"""Return a sliding window of width *n* over *iterable*.</span></div><br /><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(sliding_window(range(6), 4))</span></div><div><span style="color: #ce9178;"> [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)]</span></div><br /><div><span style="color: #ce9178;"> If *iterable* has fewer than *n* items, then nothing is yielded:</span></div><br /><div><span style="color: #ce9178;"> </span><span style="color: #569cd6;">>>> </span><span style="color: #ce9178;">list(sliding_window(range(3), 4))</span></div><div><span style="color: #ce9178;"> []</span></div><br /><div><span style="color: #ce9178;"> For a variant with more features, see :func:`windowed`.</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #d4d4d4;"> it = iter(iterable)</span></div><div><span style="color: #d4d4d4;"> window = deque(islice(it, n), maxlen=n)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> len(window) == n:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">yield</span><span style="color: #d4d4d4;"> tuple(window)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> x </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> it:</span></div><div><span style="color: #d4d4d4;"> window.append(x)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">yield</span><span style="color: #d4d4d4;"> tuple(window)</span></div></div><h2 class="_1qeIAgB0cPwnLhDF9XSiJM" style="text-align: left;">Visual Comparison <br /></h2><p class="_1qeIAgB0cPwnLhDF9XSiJM">I'll show my nwise function for comparison:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> nwise(iterable, width):</span></div><div><span style="color: #d4d4d4;"> tees = tee(iterable, width)</span></div><div><span style="color: #d4d4d4;"> skewed = [([next(t) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(position)], t)[</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> position, t </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(tees)]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> zip(*skewed)</span></div></div><p class="_1qeIAgB0cPwnLhDF9XSiJM"></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><b> </b></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><b>sliding_window </b>is nicely commented as it is in a public library; <b>nwise </b>is written to be read as part of its original blog post.</p><p class="_1qeIAgB0cPwnLhDF9XSiJM">It's up to the reader to judge which code is more understandable. more_itertools does not have a blog post to help understand the code. nwise isn't packaged as a module - its a coding example.</p><h2 class="_1qeIAgB0cPwnLhDF9XSiJM" style="text-align: left;">Timing Comparison</h2><p class="_1qeIAgB0cPwnLhDF9XSiJM"> Yes, well, the timing methodology is set to compare run times over a range of inputs iterable sizes and output window widths. A particular application might benefit from more specific timings.</p><p class="_1qeIAgB0cPwnLhDF9XSiJM">I added the following timing code:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #d4d4d4;">data = dict(items=[], window=[], func=[], time=[])</span></div><div><span style="color: #d4d4d4;">fastest = []</span></div><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> n </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> items = range(</span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">**n)</span></div><div><span style="color: #d4d4d4;"> print()</span></div><div><span style="color: #d4d4d4;"> reps = </span><span style="color: #569cd6;">None</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> w </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(</span><span style="color: #b5cea8;">9</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> wdth = </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">**w</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> wdth <= </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">**n:</span></div><div><span style="color: #d4d4d4;"> print()</span></div><div><span style="color: #d4d4d4;"> func_speeds = []</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> func </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> (nwise, nwise_squashed, sliding_window):</span></div><div><span style="color: #d4d4d4;"> data[</span><span style="color: #ce9178;">'items'</span><span style="color: #d4d4d4;">].append(len(items))</span></div><div><span style="color: #d4d4d4;"> data[</span><span style="color: #ce9178;">'window'</span><span style="color: #d4d4d4;">].append(wdth)</span></div><div><span style="color: #d4d4d4;"> data[</span><span style="color: #ce9178;">'func'</span><span style="color: #d4d4d4;">].append(func.__name__)</span></div><div><span style="color: #d4d4d4;"> cmd = </span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"list(</span><span style="color: #d4d4d4;">{func.__name__</span><span style="color: #569cd6;">:<15</span><span style="color: #d4d4d4;">}</span><span style="color: #ce9178;">(</span><span style="color: #d4d4d4;">{str(items)</span><span style="color: #569cd6;">:<12</span><span style="color: #d4d4d4;">}</span><span style="color: #ce9178;">, </span><span style="color: #d4d4d4;">{wdth:</span><span style="color: #f44747;">10_</span><span style="color: #d4d4d4;">}</span><span style="color: #ce9178;">))"</span></div><div><span style="color: #d4d4d4;"> print(cmd, end=</span><span style="color: #ce9178;">' # '</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> timer = Timer(cmd, globals=globals())</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> reps </span><span style="color: #569cd6;">is</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> reps, secs = timer.autorange()</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> reps <</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> reps = </span><span style="color: #b5cea8;">3</span></div><div><span style="color: #d4d4d4;"> secs = min(timer.repeat(</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, reps)) </span><span style="color: #6a9955;"># min of 3 runs of timeit(number=reps)</span></div><div><span style="color: #d4d4d4;"> data[</span><span style="color: #ce9178;">'time'</span><span style="color: #d4d4d4;">].append(secs)</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #ce9178;">"%1.2fs, %i reps"</span><span style="color: #d4d4d4;"> % (secs, reps))</span></div><div><span style="color: #d4d4d4;"> func_speeds.append((secs, func.__name__))</span></div><br /><div><span style="color: #d4d4d4;"> fastest.append(min(func_speeds)[</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">])</span></div><br /><div><span style="color: #d4d4d4;">print(</span><span style="color: #ce9178;">"\nFunction most fast for a particular set of arguments:"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #ce9178;">" "</span><span style="color: #d4d4d4;">, Counter(fastest).most_common())</span></div><br /><br /></div><p class="_1qeIAgB0cPwnLhDF9XSiJM"><br /></p><h3 class="_1qeIAgB0cPwnLhDF9XSiJM" style="text-align: left;">Generated Timings:</h3><p class="_1qeIAgB0cPwnLhDF9XSiJM"></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.20s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.22s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.30s, 100000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.27s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.29s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.29s, 100000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.40s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.43s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.25s, 100000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.76s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.78s, 100000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.20s, 100000 reps</span></span></div><span style="font-size: x-small;"><br /><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.21s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.21s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.90s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.23s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.23s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.94s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.26s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.28s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.96s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.35s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.36s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.03s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.56s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.56s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.23s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">32</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.19s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">32</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.20s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">32</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.57s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">64</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 2.69s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">64</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 2.85s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">64</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 2.41s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 5.74s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 5.80s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 3.35s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">256</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 15.83s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">256</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 15.87s, 5000 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">256</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 4.58s, 5000 reps</span></span></div><span style="font-size: x-small;"><br /><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.30s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.30s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.04s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.34s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.33s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.05s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.36s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.37s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.09s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.44s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.46s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">8</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.22s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.64s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 0.64s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">16</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.39s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">32</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.13s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">32</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.13s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">32</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 1.83s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">64</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 2.67s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">64</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 2.64s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">64</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 3.26s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 4.47s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 4.38s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">128</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 4.88s, 50 reps</span></span></div><span style="font-size: x-small;"><br /></span><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">256</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 8.70s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(nwise_squashed (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">256</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 8.57s, 50 reps</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(sliding_window (range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">100000</span><span style="color: #d4d4d4;">), </span><span style="color: #b5cea8;">256</span><span style="color: #d4d4d4;">)) </span><span style="color: #6a9955;"># 8.55s, 50 reps</span></span></div><div><span style="color: #6a9955;"> </span></div><div><span style="color: #d4d4d4;">Function most fast </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> a particular set of arguments:</span></div><div><span style="color: #d4d4d4;"> [(</span><span style="color: #ce9178;">'nwise'</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'sliding_window'</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'nwise_squashed'</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">)]</span></div></div><p class="_1qeIAgB0cPwnLhDF9XSiJM"></p><ul style="text-align: left;"><li><b>sliding_window </b>is only fastest 6 out of the 22 runs of the groups of three functions running with the same arguments. </li><li>Either the <b>nwise</b>, or <b>nwise_squashed </b>algorithms are fastest most often, and with similar times between these two.</li></ul><p class="_1qeIAgB0cPwnLhDF9XSiJM"><br /></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><b>END.</b><br /></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><br /></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><br /></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><br /></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><br /></p><p class="_1qeIAgB0cPwnLhDF9XSiJM"><br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-18477358330161420202022-06-01T09:00:00.000+01:002022-06-01T09:00:34.652+01:00From pairwise to n-wise<p> Python3.10 introduced the<a href="https://docs.python.org/3/library/itertools.html#itertools.pairwise" rel="nofollow" target="_blank"> itertools.pairwise</a> function:</p><dl class="py function"><dt id="itertools.pairwise">
<code class="sig-prename descclassname">itertools.</code><code class="sig-name descname">pairwise</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">iterable</span></em><span class="sig-paren">)</span><a class="headerlink" href="https://docs.python.org/3/library/itertools.html#itertools.pairwise" title="Permalink to this definition"></a></dt><dd><p>Return successive overlapping pairs taken from the input <em>iterable</em>.</p>
<p>The number of 2-tuples in the output iterator will be one fewer than the
number of inputs. It will be empty if the input iterable has fewer than
two values.</p>
</dd></dl><p> </p><p>Here's a few examples showing what it does:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> itertools </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> pairwise, tee</span></div><br /><div><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{list(pairwise(x)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{list(pairwise(x)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{[</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">.join(w) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> w </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> pairwise(x)] = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print()</span></div></div><p> Which produces output:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(pairwise(x)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">list(pairwise(x)) = [(</span><span style="color: #ce9178;">'A'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'B'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'B'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'C'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'C'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'D'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'D'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'E'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'E'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'F'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'F'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'G'</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></span></div><div><span style="font-size: x-small;"><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">.join(w) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> w </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> pairwise(x)] = [</span><span style="color: #ce9178;">'AB'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'BC'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'CD'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'DE'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'EF'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'FG'</span><span style="color: #d4d4d4;">]</span></span></div></div><p></p><h2 style="text-align: left;"> pairwise equivalent <br /></h2><p>The docs also give a roughly equivalent function that I reproduce below:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> pairwise_eq(iterable):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># pairwise('ABCDEFG') --> AB BC CD DE EF FG</span></div><div><span style="color: #d4d4d4;"> a, b = tee(iterable)</span></div><div><span style="color: #d4d4d4;"> next(b, </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> zip(a, b)</span></div><br /><div><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{list(pairwise_eq(x)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{list(pairwise_eq(x)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{[</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">.join(w) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> w </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> pairwise_eq(x)] = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print()</span></div></div><p></p><p>pairwise_eq output is the same:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(pairwise_eq(x)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(pairwise_eq(x)) = [(</span><span style="color: #ce9178;">'A'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'B'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'B'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'C'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'C'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'D'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'D'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'E'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'E'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'F'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'F'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'G'</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">.join(w) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> w </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> pairwise_eq(x)] = [</span><span style="color: #ce9178;">'AB'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'BC'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'CD'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'DE'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'EF'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'FG'</span><span style="color: #d4d4d4;">]</span></span></div></div><p></p><h2 style="text-align: left;">tri-wise</h2><p>Looking at the code for pairwise_eq it can be easily expanded to generate a similar moving window of <b>three </b>items by:</p><ol style="text-align: left;"><li>tee'ing into three streams.</li><li>dropping <i><b>two </b></i>items from the third stream.</li></ol><p>That produces the following code:</p><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> tri_wise(iterable):</span></div><div><span style="color: #d4d4d4;"> a, b, c = tee(iterable, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> next(b, </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> next(c, </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> zip(a, b, c)</span></div><br /><div><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{list(tri_wise(x)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{list(tri_wise(x)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }\n{[</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">.join(w) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> w </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> tri_wise(x)] = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print()</span></div></div><p>It produces the following output:</p><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(tri_wise(x)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(tri_wise(x)) = [(</span><span style="color: #ce9178;">'A'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'B'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'C'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'B'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'C'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'D'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'C'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'D'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'E'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'D'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'E'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'F'</span><span style="color: #d4d4d4;">), (</span><span style="color: #ce9178;">'E'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'F'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'G'</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = </span><span style="color: #ce9178;">'ABCDEFG'</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">[</span><span style="color: #ce9178;">''</span><span style="color: #d4d4d4;">.join(w) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> w </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> tri_wise(x)] = [</span><span style="color: #ce9178;">'ABC'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'BCD'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'CDE'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'DEF'</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">'EFG'</span><span style="color: #d4d4d4;">]</span></span></div></div><h2 style="text-align: left;">n-wise</h2><p> To generalise the code to generate moving windows of output of a given width I need to:</p><ol style="text-align: left;"><li>Generate several tee'd streams. <br />Easily done with <a href="https://docs.python.org/3/library/itertools.html#itertools.tee" rel="nofollow" target="_blank">tee(stream, n)</a>.<br /></li><li>drop items from the front of each stream to skew each stream progressively.<br />The list comprehensions output expression has a nested list comprehension to drop items from the stream and the stream itself as a tuple, from which is selects, (via the index [1]), the resultant skewed stream<br /></li><li>zip the resultant streams together to form the output.<br /></li></ol><p>Code:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> nwise(iterable, width):</span></div><div><span style="color: #d4d4d4;"> tees = tee(iterable, width)</span></div><div><span style="color: #d4d4d4;"> skewed = [([next(t) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(position)], t)[</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> position, t </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(tees)]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> zip(*skewed)</span></div><br /><div><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> wdth </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(</span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">try</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }</span><span style="color: #ce9178;">; </span><span style="color: #d4d4d4;">{wdth = }\n{list(nwise(x, wdth)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">except</span><span style="color: #d4d4d4;"> StopIteration:</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }</span><span style="color: #ce9178;">; </span><span style="color: #d4d4d4;">{wdth = }\n</span><span style="color: #ce9178;">list(nwise(x, wdth)) Raised StopIteration!"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print()</span></div></div><p></p><p>The output nwise(iterator, 2) matches pairwise(iterator).</p><p>Function nwise has no range checks as it works with general iterators whose length may not be known. it raises errors when the width is one more than the number of items in the iterator.</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">0</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = []</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">1</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">,), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">,), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">,), (</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">,), (</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">,), (</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">,), (</span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">,)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">2</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">3</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">4</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">5</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">6</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">), (</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">7</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = [(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">4</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">6</span><span style="color: #d4d4d4;">)]</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">8</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) = []</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">9</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) Raised StopIteration!</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">); wdth = </span><span style="color: #b5cea8;">10</span></span></div><div><span style="font-size: xx-small;"><span style="color: #d4d4d4;">list(nwise(x, wdth)) Raised StopIteration!</span></span></div></div><p></p><h3 style="text-align: left;">Single expression</h3><p style="text-align: left;">The intermediate variables, tees, and skewed of function nwise can be removed to create a function of just one return expression:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> nwise_squashed(iterable, width):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> zip(*(([next(t) </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> _ </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(position)], t)[</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> position, t </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> enumerate(tee(iterable, width))))</span></div><br /><div><span style="color: #d4d4d4;">x = range(</span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> wdth </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(</span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">try</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }</span><span style="color: #ce9178;">; </span><span style="color: #d4d4d4;">{wdth = }\n{list(nwise_squashed(x, wdth)) = }</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">except</span><span style="color: #d4d4d4;"> Exception </span><span style="color: #569cd6;">as</span><span style="color: #d4d4d4;"> e:</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #569cd6;">f</span><span style="color: #ce9178;">"</span><span style="color: #d4d4d4;">{x = }</span><span style="color: #ce9178;">; </span><span style="color: #d4d4d4;">{wdth = }\n</span><span style="color: #ce9178;">list(nwise_squashed(x, wdth)): </span><span style="color: #d4d4d4;">{e}</span><span style="color: #ce9178;">!"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;">print()</span></div></div><p style="text-align: left;"> It works, but I don't like it - function nwise_squashed is less maintainable for me than function nwise.</p><p style="text-align: left;"><br /></p><h2 style="text-align: left;">End Bit</h2><p style="text-align: left;">I managed to generalise itertools.pairwise. A tricky bit was running next() appropriate times on tee'd streams in a comprehension; I'll remember the trick of using expressions in a tuple to change an iterator before returning the resultant via indexing.</p><p style="text-align: left;"><b>END.</b><br /></p><p style="text-align: left;"> <br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-29044474861267595412022-05-21T15:49:00.000+01:002022-05-21T15:49:30.664+01:00Big nO, How the interpreted nature of Python could skew quoted Big-O use.<h2 style="text-align: left;"> Or: If time is an issue then <u>measure</u>!</h2><p><span style="font-family: inherit;">I was recently introduced to the <a href="https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm#:~:text=The%20Boyer%E2%80%93Moore%20majority%20vote,example%20of%20a%20streaming%20algorithm" rel="nofollow" target="_blank">Boyer-Moore</a> <a href="https://www.cs.ou.edu/~rlpage/dmtools/mjrty.pdf" rel="nofollow" target="_blank">MJRTY algorithm </a>for <span dir="ltr" role="presentation" style="font-size: 16.6px; left: 423.6px; top: 839.907px;"></span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 430px; top: 839.907px; transform: scaleX(1.00115);">determining</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 516.8px; top: 839.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 523.2px; top: 839.907px; transform: scaleX(0.999248);">whic</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 556px; top: 839.907px; transform: scaleX(0.480416);">h </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 569.8px; top: 839.907px;">, </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 576.6px; top: 839.907px; transform: scaleX(1.15663);">if</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 586.2px; top: 839.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 592.6px; top: 839.907px; transform: scaleX(0.953932);">an</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 609.8px; top: 839.907px; transform: scaleX(1.06024);">y,</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 617.2px; top: 839.907px; transform: scaleX(0.235897);"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 621.8px; top: 839.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 628.4px; top: 839.907px; transform: scaleX(0.969259);">of</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 641.8px; top: 839.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 648.4px; top: 839.907px; transform: scaleX(0.95393);">an</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 666px; top: 839.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 672.4px; top: 839.907px; transform: scaleX(1.07806);">arbitrary</span><br role="presentation" /><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 180px; top: 859.907px; transform: scaleX(0.99729);">n</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 188.8px; top: 859.907px; transform: scaleX(0.99783);">um</span>b<span dir="ltr" role="presentation" style="font-size: 16.6px; left: 221px; top: 859.907px; transform: scaleX(0.949153);">er</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 235px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 240px; top: 859.907px; transform: scaleX(0.969257);">of</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 253.4px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 258.4px; top: 859.907px; transform: scaleX(0.957009);">candidates</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 335.2px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 340.2px; top: 859.907px; transform: scaleX(0.904673);">has</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 364.4px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 369.4px; top: 859.907px; transform: scaleX(0.953134);">receiv</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 411.2px; top: 859.907px; transform: scaleX(0.89973);">ed</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 427.8px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 432.6px; top: 859.907px; transform: scaleX(0.910569);">a</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 441px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 446px; top: 859.907px; transform: scaleX(0.963124);">ma</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 469.2px; top: 859.907px; transform: scaleX(1.15888);">jorit</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 499.8px; top: 859.907px; transform: scaleX(1.06024);">y</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 508.6px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 513.4px; top: 859.907px; transform: scaleX(0.969256);">of</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 526.8px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 532px; top: 859.907px; transform: scaleX(0.997829);">the</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 555px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 560.2px; top: 859.907px; transform: scaleX(1.06024);">v</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 568.6px; top: 859.907px; transform: scaleX(0.91866);">otes</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 597.4px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 602.2px; top: 859.907px; transform: scaleX(0.94659);">cast</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 631px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 636px; top: 859.907px; transform: scaleX(1.0677);">in</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 649.8px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 654.8px; top: 859.907px; transform: scaleX(0.95393);">an</span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 672.4px; top: 859.907px;"> </span><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">election .</span></span></p><p><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">The<a href="https://www.linkedin.com/feed/update/urn:li:activity:6925798013516840960/" rel="nofollow" target="_blank"> linkedin post</a> </span></span><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"> by Prof. Wissam Fawaz </span></span> gave an implementation of that algorithm and I gave my own version, reproduced below. </span></span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> typing </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> Any</span></div><div><span style="color: #569cd6;">from</span><span style="color: #d4d4d4;"> collections </span><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> Counter</span></div><div><span style="color: #569cd6;">import</span><span style="color: #d4d4d4;"> random</span></div><br /><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> boyer_moore(seq: list[Any]) -> tuple[bool, Any]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Returns IF there is a majority vote winner, and the winner."</span></div><div><span style="color: #d4d4d4;"> m, i = </span><span style="color: #569cd6;">None</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> x </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> seq:</span></div><div><span style="color: #d4d4d4;"> m, i = ((x, </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> i == </span><span style="color: #b5cea8;">0</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;"> ((m, i+</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> m == x</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;"> (m, i-</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)))</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> seq.count(m) > len(seq) // </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, m</span></div><br /><br /></div><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"> The algorithm, given a list of peoples votes such as if thirteen people are voting between parties A, B, and C then their votes might be represented by the list <span style="font-family: courier;">AAACCBBCCCBCC<span style="font-family: inherit;">.</span></span></span></span><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"> The MJRTY algorithm takes just two passes through the list to determine if some party has more than half the votes, and who that is.</span></span></p><div style="text-align: left;"><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">I decided to also code a solution using collections.Counter, which is the first <i>consideration </i>when a problem includes counting in Python:</span></span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> counted(seq: list[Any]) -> tuple[bool, Any]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Uses collections.Counter"</span></div><div><span style="color: #d4d4d4;"> m, count = Counter(seq).most_common(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)[</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">]</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> count > len(seq) // </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, m</span></div><br /></div><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"></span></span></p><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">Given the problem statement, it is more evident how the counted version works:</span></span></p><ol style="text-align: left;"><li><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">Find the most common vote</span></span></li><li><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">See if it amounts to more than half of all votes.</span></span></li></ol><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">The description of how and why the MJRTY is given in the <a href="https://www.cs.ou.edu/~rlpage/dmtools/mjrty.pdf" rel="nofollow" target="_blank">book</a> and is a little more involved.</span></span></div><div style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"><br /></span></span></div><div style="text-align: left;"><h2><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">Testing.</span></span></h2><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">Like all tests, it has holes, but I decided to concentrate on the run time for longer and longer lists that were randomised, and also had a 50/50 chance of producing a winning candidate.</span></span></p><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">First a generator of suitable data:</span></span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> list_gen(odd_count=</span><span style="color: #b5cea8;">11</span><span style="color: #d4d4d4;">) -> list[int]:</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;"> Generates a list with either one item appearing more</span></div><div><span style="color: #ce9178;"> than all the rest or not.</span></div><div><span style="color: #ce9178;"> """</span></div><div><span style="color: #d4d4d4;"> half_count = odd_count // </span><span style="color: #b5cea8;">2</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">if</span><span style="color: #d4d4d4;"> random.choice((</span><span style="color: #569cd6;">True</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">False</span><span style="color: #d4d4d4;">)):</span></div><div><span style="color: #d4d4d4;"> lst = random.choices([</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">], k=half_count) + [</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">] * (half_count + </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">else</span><span style="color: #d4d4d4;">:</span></div><div><span style="color: #d4d4d4;"> lst = random.choices([</span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">3</span><span style="color: #d4d4d4;">], k=half_count + </span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">) + [</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">] * half_count</span></div><div><span style="color: #d4d4d4;"> random.shuffle(lst)</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">return</span><span style="color: #d4d4d4;"> lst</span></div><br /><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(</span><span style="color: #b5cea8;">5</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> print((lst := list_gen()), boyer_moore(lst), counted(lst))</span></div><br /><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Sample output from loop above:</span></div><div><span style="color: #ce9178;">[1, 2, 1, 2, 1, 3, 1, 3, 1, 2, 2] (False, 2) (False, 1)</span></div><div><span style="color: #ce9178;">[2, 2, 3, 1, 1, 1, 1, 1, 2, 1, 3] (True, 1) (True, 1)</span></div><div><span style="color: #ce9178;">[1, 1, 1, 2, 1, 2, 2, 3, 1, 1, 3] (True, 1) (True, 1)</span></div><div><span style="color: #ce9178;">[1, 1, 1, 2, 1, 2, 3, 1, 2, 2, 1] (True, 1) (True, 1)</span></div><div><span style="color: #ce9178;">[2, 1, 2, 1, 1, 2, 1, 3, 3, 2, 1] (False, 1) (False, 1)</span></div><div><span style="color: #ce9178;">"""</span></div></div><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">Then wrap each implementation and timeit (Thanks IPython/Spyder).<br /></span></span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, 'Courier New', monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;">#%% Timings</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> timed_bm(count=</span><span style="color: #b5cea8;">1_000</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> boyer_moore(list_gen(count))</span></div><br /><div><span style="color: #569cd6;">def</span><span style="color: #d4d4d4;"> timed_c(count=</span><span style="color: #b5cea8;">1_000</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> counted(list_gen(count))</span></div><br /><div><span style="color: #569cd6;">for</span><span style="color: #d4d4d4;"> i </span><span style="color: #569cd6;">in</span><span style="color: #d4d4d4;"> range(</span><span style="color: #b5cea8;">1</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">7</span><span style="color: #d4d4d4;">):</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #ce9178;">'\n##'</span><span style="color: #d4d4d4;">,</span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">**i)</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #ce9178;">"# Boyer-Moore"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> %timeit timed_bm(</span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">**i) </span><span style="color: #6a9955;"># Spyder/IPython IDE specific</span></div><div><span style="color: #d4d4d4;"> print(</span><span style="color: #ce9178;">"# Counted"</span><span style="color: #d4d4d4;">)</span></div><div><span style="color: #d4d4d4;"> %timeit timed_c(</span><span style="color: #b5cea8;">10</span><span style="color: #d4d4d4;">**i)</span></div><br /><div><span style="color: #ce9178;">"""</span></div><div><span style="color: #ce9178;">Sample timed output:</span></div><div><span style="color: #ce9178;">## 10</span></div><div><span style="color: #ce9178;"># Boyer-Moore</span></div><div><span style="color: #ce9178;">10.8 µs ± 44.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)</span></div><div><span style="color: #ce9178;"># Counted</span></div><div><span style="color: #ce9178;">13.6 µs ± 44.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)</span></div><br /><div><span style="color: #ce9178;">## 100</span></div><div><span style="color: #ce9178;"># Boyer-Moore</span></div><div><span style="color: #ce9178;">73.7 µs ± 89.5 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)</span></div><div><span style="color: #ce9178;"># Counted</span></div><div><span style="color: #ce9178;">69.9 µs ± 518 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)</span></div><br /><div><span style="color: #ce9178;">## 1000</span></div><div><span style="color: #ce9178;"># Boyer-Moore</span></div><div><span style="color: #ce9178;">739 µs ± 2.37 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)</span></div><div><span style="color: #ce9178;"># Counted</span></div><div><span style="color: #ce9178;">661 µs ± 1.55 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)</span></div><br /><div><span style="color: #ce9178;">## 10000</span></div><div><span style="color: #ce9178;"># Boyer-Moore</span></div><div><span style="color: #ce9178;">7.58 ms ± 24.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)</span></div><div><span style="color: #ce9178;"># Counted</span></div><div><span style="color: #ce9178;">6.91 ms ± 80.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)</span></div><br /><div><span style="color: #ce9178;">## 100000</span></div><div><span style="color: #ce9178;"># Boyer-Moore</span></div><div><span style="color: #ce9178;">75.3 ms ± 219 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)</span></div><div><span style="color: #ce9178;"># Counted</span></div><div><span style="color: #ce9178;">67.8 ms ± 464 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)</span></div><br /><div><span style="color: #ce9178;">## 1000000</span></div><div><span style="color: #ce9178;"># Boyer-Moore</span></div><div><span style="color: #ce9178;">780 ms ± 5.54 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)</span></div><div><span style="color: #ce9178;"># Counted</span></div><div><span style="color: #ce9178;">710 ms ± 11.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)</span></div><div><span style="color: #ce9178;">"""</span></div><br /></div><h3 style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">Results</span></span></h3><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">The run times are comparable over from ten to a million voters given the restrictions of the test strategy.</span></span></p><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">the vote generator restricts the number of parties to three which favours the dict underlying Counter. A more accurate comparison might be made by taking different sized samples from actual data.</span></span></p><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);">END.<br /></span></span></p><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"><br /></span></span></p><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"><br /></span></span></p><p style="text-align: left;"><span style="font-family: inherit;"><span dir="ltr" role="presentation" style="font-size: 16.6px; left: 677.6px; top: 859.907px; transform: scaleX(0.782269);"><br /></span></span></p></div><div><span dir="ltr" role="presentation" style="font-family: sans-serif; font-size: 16.6px; left: 180px; top: 879.707px; transform: scaleX(1);"></span></div>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-36743224831690556912022-04-03T18:16:00.004+01:002022-04-04T07:13:50.408+01:00Easier Regexps<div><div><div><div><p> Python, although a scripting language, does <i>not </i>bake regular expressions into the language syntax like other scripting languages such as AWK, Perl and Ruby. This can make people pause before using the re library that Python supplies, but it has its place, and regular expressions are a handy tool in results.</p><p>Over the decades, I have refined my use of regular expressions and want to pass on a useful style, and tools that I find very useful.</p><h2 style="text-align: left;">1. Use <a href="https://regex101.com" rel="nofollow" target="_blank">Regex101.com</a>!</h2><ul style="text-align: left;"><li>Open the site in another tab.</li><li>Reading down the main textual menu to the left, select the Python language.</li></ul><h2 style="text-align: left;">2. Make regexps readable.</h2><ul style="text-align: left;"><li>In the "REGULAR EXPRESSIONS" section, top centre:</li><ul><li>Hover over the <b><span style="font-family: courier;">r"</span></b> text to the left of the entry box and it will say "Change delimiter". Click and change the delimiter to<span style="font-family: courier;"><b> r"""</b></span>.</li><li>Hover over the """gm text to the left of the central window which should then say "Set Regex Options". Select the<b> g</b>, <b>m</b> and<b> x</b> options. </li></ul></ul></div><p>The above allows you to enter a regexp over multiple lines and with comments. Spaces are ignored on the whole, and you should use the pattern \s or \n etc to represent whatever whitespace you wish to match.</p><h2 style="text-align: left;">3. Get a representative selection of what you wish to match.</h2><p>Know your data! Read it, and select sections that convey all the intricacies that your regexp needs to match. Careful when taking several excerpts from, say, a log, to ensure that the excerpts, together are valid.</p><ul style="text-align: left;"><li>Paste your sample text in the main "TEST STRING" section. <br /></li></ul></div><h2 style="text-align: left;">4. Use named capture groups.</h2><p>When creating your exression in the top centre section any capture-group should be a <i><b>named </b></i>capture-group.</p><ul style="text-align: left;"><li>Use uppercase group names - this usually contrasts with other parts of the regexp aiding readability.</li><li>Use short, meaningful group names.</li></ul></div><p>These names might become lowercased variable names in a program.</p><h2 style="text-align: left;">5. You can comment in the regexp!!!</h2><p>Yes you can.</p><h2 style="text-align: left;">6. Debug on site.</h2><p>Regex101 explains the regexp and all the matches of the regexp on the test string as you hover on parts of the regexp or test string. There is extra info in the right hand side sections too.</p><h2 style="text-align: left;">7. Export the code.</h2><ul style="text-align: left;"><li>Under the lower LHS <b>TOOLS </b>menu select <b>Code Generator</b>.</li></ul></div><p>Cut-n-paste the code into your favourite editor.</p><p>I tend to take only parts of the generated code: the regexp, the test_str and the re compile options are useful.</p><h1 style="text-align: left;">An Example: Harvesting colours.</h1><p>I was looking for a list of distinctive colours and found this page: <a href="https://sashamaps.net/docs/resources/20-colors/" rel="nofollow" target="_blank">List of 20 Simple, Distinct Colors</a></p><p>If you click on the <b>convenient</b> button, the colours adjust to a nice order, but selecting a region around the colour table and pasting into an editor gives one long concatenation of the table information amongst surrounding text.</p><p>I wanted something more readable so pasted the long string into <a href="https://regex101.com/r/GQxbma/2" rel="nofollow" target="_blank">regex101 here</a> and created the regular expression to parse it. (Oh, that's another feature - you can create a regex101 account and save regexps). <br /></p><h4 style="text-align: left;">The final script creates a nicely formatted Python list of the data:</h4><!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="background-color: #0f140f; color: #008800; font-style: italic;"># -*- coding: utf-8 -*-</span>
<span style="color: #0086d2;">"""</span>
<span style="color: #0086d2;">Parse Table grab from https://sashamaps.net/docs/resources/20-colors/</span>
<span style="color: #0086d2;">Created on Sat Apr 2 19:36:07 2022</span>
<span style="color: #0086d2;">@author: paddy</span>
<span style="color: #0086d2;">"""</span>
<span style="color: white;">_initial_screen_cut</span> <span style="color: white;">=</span> <span style="color: #0086d2;">"""</span>
<span style="color: #0086d2;">Accessibility: Red#e6194B1Green#3cb44b2Yellow#ffe1193Blue#4363d84Orange#f582315Purple#911eb46Cyan#42d4f47Magenta#f032e68Lime#bfef459Pink#fabed410Teal#46999011Lavender#dcbeff12Brown#9A632413Beige#fffac814Maroon#80000015Mint#aaffc316Olive#80800017Apricot#ffd8b118Navy#00007519Grey#a9a9a920White#ffffff21Black#00000022</span>
<span style="color: #0086d2;">'#e6194B', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#42d4f4', '#f032e6', '#bfef45', '#fabed4', '#469990', '#dcbeff', '#9A6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#a9a9a9', '#ffffff', '#000000'</span>
<span style="color: #0086d2;">Test:</span>
<span style="color: #0086d2;">"""</span>
<span style="color: #fb660a; font-weight: bold;">import</span> <span style="color: white;">re</span>
<span style="color: white;">regex</span> <span style="color: white;">=</span> <span style="color: #0086d2;">r"""</span>
<span style="color: #0086d2;"> # Parse Table grab from https://sashamaps.net/docs/resources/20-colors/</span>
<span style="color: #0086d2;"> (?:</span>
<span style="color: #0086d2;"> (?P<NAME>[A-Za-z]+)</span>
<span style="color: #0086d2;"> \# (?P<HEX>[0-9a-fA-F]{6})</span>
<span style="color: #0086d2;"> (?P<ID>\d+)</span>
<span style="color: #0086d2;"> )</span>
<span style="color: #0086d2;">"""</span>
<span style="color: white;">test_str</span> <span style="color: white;">=</span> <span style="color: white;">(</span><span style="color: #0086d2;">"Red#e6194B1Green#3cb44b2Yellow#ffe1193Blue#4363d84Orange#f582315Pu"</span>
<span style="color: #0086d2;">"rple#911eb46Cyan#42d4f47Magenta#f032e68Lime#bfef459Pink#fabed410Te"</span>
<span style="color: #0086d2;">"al#46999011Lavender#dcbeff12Brown#9A632413Beige#fffac814Maroon#800"</span>
<span style="color: #0086d2;">"00015Mint#aaffc316Olive#80800017Apricot#ffd8b118Navy#00007519Grey#"</span>
<span style="color: #0086d2;">"a9a9a920White#ffffff21Black#00000022"</span><span style="color: white;">)</span>
<span style="color: white;">distinct</span> <span style="color: white;">=</span> <span style="color: white;">[(int(match.groupdict()[</span><span style="color: #0086d2;">'ID'</span><span style="color: white;">]),</span> <span style="color: white;">match.groupdict()[</span><span style="color: #0086d2;">'NAME'</span><span style="color: white;">],</span> <span style="color: white;">int(match.groupdict()[</span><span style="color: #0086d2;">'HEX'</span><span style="color: white;">],</span> <span style="color: #0086f7; font-weight: bold;">16</span><span style="color: white;">))</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">match</span> <span style="color: white;">in</span> <span style="color: white;">re.finditer(regex,</span> <span style="color: white;">test_str,</span> <span style="color: white;">re.MULTILINE</span> <span style="color: white;">|</span> <span style="color: white;">re.VERBOSE)]</span>
<span style="background-color: #0f140f; color: #008800; font-style: italic;">#print(f"{distinct=}")</span>
<span style="color: white;">print(</span><span style="color: #0086d2;">'\ndistinct_colours = [\n# NAME HEX_CODE'</span><span style="color: white;">)</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">id,</span> <span style="color: white;">name,</span> <span style="color: white;">num</span> <span style="color: white;">in</span> <span style="color: white;">distinct:</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">" ({repr(name)+',':<12} 0x{num:06x}),"</span><span style="color: white;">)</span>
<span style="color: white;">print(</span><span style="color: #0086d2;">']'</span><span style="color: white;">)</span>
</pre></div><p></p><h4 style="text-align: left;">The output:</h4><p></p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">distinct_colours</span> <span style="color: white;">=</span> <span style="color: white;">[</span>
<span style="background-color: #0f140f; color: #008800; font-style: italic;"># NAME HEX_CODE</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Red'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xe6194b</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Green'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x3cb44b</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Yellow'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xffe119</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Blue'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x4363d8</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Orange'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xf58231</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Purple'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x911eb4</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Cyan'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x42d4f4</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Magenta'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xf032e6</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Lime'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xbfef45</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Pink'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xfabed4</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Teal'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x469990</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Lavender'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xdcbeff</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Brown'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x9a6324</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Beige'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xfffac8</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Maroon'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x800000</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Mint'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xaaffc3</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Olive'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x808000</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Apricot'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xffd8b1</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Navy'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x000075</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Grey'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xa9a9a9</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'White'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0xffffff</span><span style="color: white;">),</span>
<span style="color: white;">(</span><span style="color: #0086d2;">'Black'</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0x000000</span><span style="color: white;">),</span>
<span style="color: white;">]</span>
</pre></div><p> </p><h2 style="text-align: left;">END.
</h2>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-49092936782797837262021-10-31T17:44:00.000+00:002021-10-31T17:44:53.947+00:00Python: *args, **kwargs is hard work!<p> <b>Normally </b>one would start such a post with why <a href="https://docs.python.org/3/glossary.html#term-parameter" rel="nofollow" target="_blank">parameters </a>*args and **kwargs shouldn't be used, but that is incorrect I think you should <i>use them as much as you can document their use</i>!</p><p>Everywhere they are used, you should document:<br /></p><ol style="text-align: left;"><li>Just what the expected values are.</li><li>What they do;.</li><li>And how they interact.</li></ol>That's the information usually missing when it comes to maintenance time that should be present. <p></p><p><b>If </b>your use comes from incorporating a library then at least document the args and kwargs that you use in your source code. This should put a damper on routines that pass the same *args and **kwargs to two different modules as the effort to trace used values and ensure no negative interactions between the two called modules is a lot of work and would take some time to document.</p><p>If you are <b>not </b>willing to document properly than that, there, is an indication that you are adding technical debt to your code, and that thought should have the effect of limiting their use.</p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-91626233766858837512021-10-08T15:58:00.001+01:002021-10-08T16:08:52.216+01:00Converting an algorithm to use Pythons' new Structural Pattern Matching<p> Python 3.10 is out and browsing what's new, I decided to convert<a href="https://rosettacode.org/wiki/Ackermann_function#Python:_Without_recursive_function_calls" rel="nofollow" target="_blank"> one of my existing routines</a> to use the new Structural Pattern Matching statement to try it out.</p><p>I used <span style="font-family: courier;">fgrep -c elif</span>, as part of a long unix command to find my algorithms with a lot of elif statements, and then chose my routine that evaluates Ackermann's function without explicit recursive function calls and with a lot of optimisations.</p><p><br /></p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><table><tbody><tr><td><pre style="line-height: 125%; margin: 0px;"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25</pre></td><td><pre style="line-height: 125%; margin: 0px;"><span style="color: #fb660a; font-weight: bold;">from</span> <span style="color: white;">collections</span> <span style="color: #fb660a; font-weight: bold;">import</span> <span style="color: white;">deque</span>
<span style="color: #fb660a; font-weight: bold;">def</span> <span style="color: #ff0086; font-weight: bold;">ack_ix</span><span style="color: white;">(m,</span> <span style="color: white;">n):</span>
<span style="color: #0086d2;">"Paddy3118's iterative with optimisations on m"</span>
<span style="color: white;">stack</span> <span style="color: white;">=</span> <span style="color: white;">deque([])</span>
<span style="color: white;">stack.extend([m,</span> <span style="color: white;">n])</span>
<span style="color: #fb660a; font-weight: bold;">while</span> <span style="color: white;">len(stack)</span> <span style="color: white;">></span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">:</span>
<span style="color: white;">n,</span> <span style="color: white;">m</span> <span style="color: white;">=</span> <span style="color: white;">stack.pop(),</span> <span style="color: white;">stack.pop()</span>
<span style="color: #fb660a; font-weight: bold;">if</span> <span style="color: white;">m</span> <span style="color: white;">==</span> <span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">:</span>
<span style="color: white;">stack.append(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">)</span>
<span style="color: #fb660a; font-weight: bold;">elif</span> <span style="color: white;">m</span> <span style="color: white;">==</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">:</span>
<span style="color: white;">stack.append(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)</span>
<span style="color: #fb660a; font-weight: bold;">elif</span> <span style="color: white;">m</span> <span style="color: white;">==</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">:</span>
<span style="color: white;">stack.append(</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">*n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span>
<span style="color: #fb660a; font-weight: bold;">elif</span> <span style="color: white;">m</span> <span style="color: white;">==</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">:</span>
<span style="color: white;">stack.append(</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">**(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span> <span style="color: white;">-</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span>
<span style="color: #fb660a; font-weight: bold;">elif</span> <span style="color: white;">n</span> <span style="color: white;">==</span> <span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">:</span>
<span style="color: white;">stack.extend([m-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">])</span>
<span style="color: #fb660a; font-weight: bold;">else</span><span style="color: white;">:</span>
<span style="color: white;">stack.extend([m-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: white;">m,</span> <span style="color: white;">n-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">])</span>
<span style="color: #fb660a; font-weight: bold;">return</span> <span style="color: white;">stack[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]</span>
</pre></td></tr></tbody></table></div>
<p>Note that first n, then m is popped off the stack in the first line within the while loop.</p><p>Checking the match statement examples <a href="https://www.python.org/dev/peps/pep-0634/" rel="nofollow" target="_blank">PEP-634</a>, I create a match subject expression as a list of first the n then the m value and replace the if statement with case statements covering each of the if conditionals and also assigning to n and m as appropriate, to create:</p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><table><tbody><tr><td><pre style="line-height: 125%; margin: 0px;"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22</pre></td><td><pre style="line-height: 125%; margin: 0px;"><span style="color: #fb660a; font-weight: bold;">def</span> <span style="color: #ff0086; font-weight: bold;">ack_im</span><span style="color: white;">(m,</span> <span style="color: white;">n):</span>
<span style="color: #0086d2;">"Paddy3118's iterative with optimisations on m using match of lists"</span>
<span style="color: white;">stack</span> <span style="color: white;">=</span> <span style="color: white;">deque([])</span>
<span style="color: white;">stack.extend([m,</span> <span style="color: white;">n])</span>
<span style="color: #fb660a; font-weight: bold;">while</span> <span style="color: white;">len(stack)</span> <span style="color: white;">></span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">:</span>
<span style="color: white;">match</span> <span style="color: white;">[stack.pop(),</span> <span style="color: white;">stack.pop()]:</span>
<span style="color: white;">case</span> <span style="color: white;">[n,</span> <span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]:</span>
<span style="color: white;">stack.append(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">[n,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">]:</span>
<span style="color: white;">stack.append(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">[n,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">]:</span>
<span style="color: white;">stack.append(</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">*n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">[n,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">]:</span>
<span style="color: white;">stack.append(</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">**(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span> <span style="color: white;">-</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">,</span> <span style="color: white;">m]:</span>
<span style="color: white;">stack.extend([m-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">])</span>
<span style="color: white;">case</span> <span style="color: white;">[n,</span> <span style="color: white;">m]:</span>
<span style="color: white;">stack.extend([m-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: white;">m,</span> <span style="color: white;">n-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">])</span>
<span style="color: #fb660a; font-weight: bold;">return</span> <span style="color: white;">stack[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]</span>
</pre></td></tr></tbody></table></div>
<p>I had thought that it would be more natural to use tuples rather than lists for the subject expressions and case patterns as there are always only two things being matched, so created a third version:</p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><table><tbody><tr><td><pre style="line-height: 125%; margin: 0px;"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22</pre></td><td><pre style="line-height: 125%; margin: 0px;"><span style="color: #fb660a; font-weight: bold;">def</span> <span style="color: #ff0086; font-weight: bold;">ack_imt</span><span style="color: white;">(m,</span> <span style="color: white;">n):</span>
<span style="color: #0086d2;">"Paddy3118's iterative with optimisations on m using match of tuples"</span>
<span style="color: white;">stack</span> <span style="color: white;">=</span> <span style="color: white;">deque([])</span>
<span style="color: white;">stack.extend([m,</span> <span style="color: white;">n])</span>
<span style="color: #fb660a; font-weight: bold;">while</span> <span style="color: white;">len(stack)</span> <span style="color: white;">></span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">:</span>
<span style="color: white;">match</span> <span style="color: white;">(stack.pop(),</span> <span style="color: white;">stack.pop()):</span>
<span style="color: white;">case</span> <span style="color: white;">(n,</span> <span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">):</span>
<span style="color: white;">stack.append(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">(n,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">):</span>
<span style="color: white;">stack.append(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">(n,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">):</span>
<span style="color: white;">stack.append(</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">*n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">(n,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">):</span>
<span style="color: white;">stack.append(</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">**(n</span> <span style="color: white;">+</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span> <span style="color: white;">-</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">(</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">,</span> <span style="color: white;">m):</span>
<span style="color: white;">stack.extend([m-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">])</span>
<span style="color: white;">case</span> <span style="color: white;">(n,</span> <span style="color: white;">m):</span>
<span style="color: white;">stack.extend([m-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: white;">m,</span> <span style="color: white;">n-</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">])</span>
<span style="color: #fb660a; font-weight: bold;">return</span> <span style="color: white;">stack[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]</span>
</pre></td></tr></tbody></table></div>
<p><br /></p><h4 style="text-align: left;">Checks/Timings<br /></h4>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: #fb660a; font-weight: bold;">if</span> <span style="color: white;">__name__</span> <span style="color: white;">==</span> <span style="color: #0086d2;">"__main__"</span><span style="color: white;">:</span>
<span style="color: #fb660a; font-weight: bold;">import</span> <span style="color: white;">timeit</span>
<span style="color: #fb660a; font-weight: bold;">assert</span> <span style="color: white;">(ix</span> <span style="color: white;">:=</span> <span style="color: white;">ack_ix(</span><span style="color: #0086f7; font-weight: bold;">4</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">))</span> <span style="color: white;">==</span> <span style="color: white;">(im</span> <span style="color: white;">:=</span> <span style="color: white;">ack_im(</span><span style="color: #0086f7; font-weight: bold;">4</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">))</span> <span style="color: white;">==</span> <span style="color: white;">(im</span> <span style="color: white;">:=</span> <span style="color: white;">ack_imt(</span><span style="color: #0086f7; font-weight: bold;">4</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)),</span> <span style="color: #0086d2;">"Whoops!"</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"Answer has {len(str(ix))} decimal digits"</span><span style="color: white;">)</span>
<span style="color: white;">n</span> <span style="color: white;">=</span> <span style="color: #0086f7; font-weight: bold;">100</span><span style="color: white;">_000</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"Timings for {n:_} repetitions of ack(4, 2)"</span><span style="color: white;">)</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">func</span> <span style="color: white;">in</span> <span style="color: #0086d2;">'ack_ix ack_im ack_imt'</span><span style="color: white;">.split():</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">" {func+':':8}"</span><span style="color: white;">,</span>
<span style="color: white;">timeit.timeit(f</span><span style="color: #0086d2;">"{func}(4, 2)"</span><span style="color: white;">,</span>
<span style="color: white;">number=n,</span>
<span style="color: white;">setup=f</span><span style="color: #0086d2;">"from __main__ import {func}"</span><span style="color: white;">))</span>
</pre></div><p></p><p>Running the code produces the following output:</p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">Answer</span> <span style="color: white;">has</span> <span style="color: #0086f7; font-weight: bold;">19729</span> <span style="color: white;">decimal</span> <span style="color: white;">digits</span>
<span style="color: white;">Timings</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: #0086f7; font-weight: bold;">100</span><span style="color: white;">_000</span> <span style="color: white;">repetitions</span> <span style="color: white;">of</span> <span style="color: white;">ack(</span><span style="color: #0086f7; font-weight: bold;">4</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)</span>
<span style="color: white;">ack_ix:</span> <span style="color: #0086f7; font-weight: bold;">15.179398099950049</span>
<span style="color: white;">ack_im:</span> <span style="color: #0086f7; font-weight: bold;">15.368259999959264</span>
<span style="color: white;">ack_imt:</span> <span style="color: #0086f7; font-weight: bold;">15.472230200015474</span>
</pre></div>
<h3 style="text-align: left;"> </h3><h3 style="text-align: left;">Personal conclusion<br /></h3><p>There isn't much difference in runtime between if and match statements. This example only touches on the match statements capabilities, and so seems equally expressive as the if statement variant to me.<br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-71519992056149014022021-06-09T13:28:00.001+01:002021-06-09T14:29:24.411+01:00Python hack: Creating local variables in a comprehension<div><div><p> Lets say you have data and want to create a list of the running sum of the data. For example if the data is [1,2,3] the running sum is [1, 3, 6]</p><p>Doing this outside a comprehension:</p><!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">data</span> <span style="color: white;">=</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">]</span>
<span style="color: white;">s</span> <span style="color: white;">=</span> <span style="color: #0086f7; font-weight: bold;">0</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># accumulator</span>
<span style="color: white;">sums</span> <span style="color: white;">=</span> <span style="color: white;">[]</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data:</span>
<span style="color: white;">s</span> <span style="color: white;">+=</span> <span style="color: white;">x</span>
<span style="color: white;">sums.append(s)</span>
<span style="color: white;">print(sums)</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># [1, 3, 6]</span>
<br /></pre></div><p>Now this needs the accumulator <b>s,</b> initialised to zero, but comprehensions create their own local variables and the syntax does <i><b>not </b></i>allow you to write a simple assignment within it, and each item from the comprehension is stated first in its syntax .</p><p><br /></p><h2 style="text-align: left;">The Hack</h2><p>I'll just show the hack then work through it afterwards.</p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">]:</span> <span style="color: white;">data</span> <span style="color: white;">=</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">]</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">]:</span> <span style="color: white;">[s</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[s</span> <span style="color: white;">+</span> <span style="color: white;">x]]</span>
<span style="color: white;">Out[</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">]:</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">6</span><span style="color: white;">]</span>
</pre></div><p>
<br />When converting a comprehension into similar <b>for </b>statements then the output expression at the beginning of the comprehension is thought of as moving to inside the rightmost <b>if </b>or <b>for </b>section of the comprehension, so we get:</p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="background-color: #0f140f; color: #008800; font-style: italic;"># Comprehension over many lines</span>
<span style="color: white;">[s</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># output expression</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># For clauses (nested)</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[s</span> <span style="color: white;">+</span> <span style="color: white;">x]]</span>
<span style="background-color: #0f140f; color: #008800; font-style: italic;"># Is similar too...</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]:</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># For clauses (nested)</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data:</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[s</span> <span style="color: white;">+</span> <span style="color: white;">x]:</span>
<span style="color: white;">print(s,</span> <span style="color: white;">end=</span><span style="color: #0086d2;">' '</span><span style="color: white;">)</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># output expresion: 1 3 6</span>
</pre></div><p>
<br /></p><h2 style="text-align: left;">Explanation</h2><p>In the comprehension, the <b>initial</b><br /></p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">[s</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]</span> <span style="color: white;">...</span>
</pre></div><p></p><p>says:</p><ul style="text-align: left;"><li>Individual items of the comprehension will be the expression <b>s</b>. <br />(Remember the output expression is stated <i>first </i>, but from the environment at the <i>right </i>of the comprehension).</li><li>In the comprehensions local scope we use the <b>one-entry outer for loop </b>to set local <b>s</b> to zero.</li></ul></div><p><b>The middle for loop</b> of the comprehension just iterates over the data</p><p><b>The final for loop</b> of the comprehension is special:</p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">...</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[s</span> <span style="color: white;">+</span> <span style="color: white;">x]]</span>
</pre></div><p></p><p><b>s </b>is set to itself plus the next item of data, <b>x</b>, using iteration over a one element list [<b>s + x</b>]:</p><ul style="text-align: left;"><li>For the first x, s was initialised to zero in the local scope via the outermost for.</li><li>s becomes <b>0 + data[0]</b> in the inner loop and becomes the first output expression value, <b>1</b>.<br /></li><li>For the second iteration of the middle loop, x = data[1], so s then becomes <b>0 + data[0] + data[1]</b>. The second evaluation of the the output expression for the comprehension, <b>3</b>.</li><li>And so on...</li></ul><h2 style="text-align: left;">Multiple local variables<br /></h2><p> We can generalise this. Here we generate running sums, and running sums of the squares which needs two local variables s and s2:</p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">]:</span> <span style="color: white;">data</span> <span style="color: white;">=</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">]</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">4</span><span style="color: white;">]:</span> <span style="color: white;">[(s,</span> <span style="color: white;">s2)</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s,</span> <span style="color: white;">s2</span> <span style="color: white;">in</span> <span style="color: white;">[(</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">)]</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s,</span> <span style="color: white;">s2</span> <span style="color: white;">in</span> <span style="color: white;">[(s</span> <span style="color: white;">+</span> <span style="color: white;">x,</span> <span style="color: white;">s2</span> <span style="color: white;">+</span> <span style="color: white;">x**</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)]]</span>
<span style="color: white;">Out[</span><span style="color: #0086f7; font-weight: bold;">4</span><span style="color: white;">]:</span> <span style="color: white;">[(</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">),</span> <span style="color: white;">(</span><span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">5</span><span style="color: white;">),</span> <span style="color: white;">(</span><span style="color: #0086f7; font-weight: bold;">6</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">14</span><span style="color: white;">)]</span>
</pre></div><p></p><h2 style="text-align: left;">Summary</h2><ol style="text-align: left;"><li><span style="color: #2b00fe;">You can satisfy the need for local variables in comprehensions.</span></li><li><span style="color: #2b00fe;">Its a hard to understand hack!</span></li></ol> </div><div><h2 style="text-align: left;"><span style="color: red;">UPDATE: Added Walrus:</span></h2></div><div>The walrus operator can now be used to give a more readable equivalent. </div><div>This introduces the external initialised variable into the comprehension as well as keeping the running sums.</div><div><br /></div><div>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">5</span><span style="color: white;">]:</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># We had:</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">6</span><span style="color: white;">]:</span> <span style="color: white;">data</span> <span style="color: white;">=</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">]</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">7</span><span style="color: white;">]:</span> <span style="color: white;">[s</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">]</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s</span> <span style="color: white;">in</span> <span style="color: white;">[s</span> <span style="color: white;">+</span> <span style="color: white;">x]]</span>
<span style="color: white;">Out[</span><span style="color: #0086f7; font-weight: bold;">7</span><span style="color: white;">]:</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">6</span><span style="color: white;">]</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">9</span><span style="color: white;">]:</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># With :=</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">10</span><span style="color: white;">]:</span> <span style="color: white;">s</span> <span style="color: white;">=</span> <span style="color: #0086f7; font-weight: bold;">0</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">11</span><span style="color: white;">]:</span> <span style="color: white;">[s</span> <span style="color: white;">:=</span> <span style="color: white;">(s</span> <span style="color: white;">+</span> <span style="color: white;">x)</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data]</span>
<span style="color: white;">Out[</span><span style="color: #0086f7; font-weight: bold;">11</span><span style="color: white;">]:</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">6</span><span style="color: white;">]</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">12</span><span style="color: white;">]:</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">13</span><span style="color: white;">]:</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># We then had:</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">14</span><span style="color: white;">]:</span> <span style="color: #fb660a; font-weight: bold;">del</span> <span style="color: white;">s</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">15</span><span style="color: white;">]:</span> <span style="color: white;">[(s,</span> <span style="color: white;">s2)</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s,</span> <span style="color: white;">s2</span> <span style="color: white;">in</span> <span style="color: white;">[(</span><span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">0</span><span style="color: white;">)]</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">s,</span> <span style="color: white;">s2</span> <span style="color: white;">in</span> <span style="color: white;">[(s</span> <span style="color: white;">+</span> <span style="color: white;">x,</span> <span style="color: white;">s2</span> <span style="color: white;">+</span> <span style="color: white;">x**</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)]]</span>
<span style="color: white;">Out[</span><span style="color: #0086f7; font-weight: bold;">15</span><span style="color: white;">]:</span> <span style="color: white;">[(</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">),</span> <span style="color: white;">(</span><span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">5</span><span style="color: white;">),</span> <span style="color: white;">(</span><span style="color: #0086f7; font-weight: bold;">6</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">14</span><span style="color: white;">)]</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">16</span><span style="color: white;">]:</span> <span style="background-color: #0f140f; color: #008800; font-style: italic;"># Which becomes:</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">17</span><span style="color: white;">]:</span> <span style="color: white;">s</span> <span style="color: white;">=</span> <span style="color: white;">s2</span> <span style="color: white;">=</span> <span style="color: #0086f7; font-weight: bold;">0</span>
<span style="color: white;">In</span> <span style="color: white;">[</span><span style="color: #0086f7; font-weight: bold;">18</span><span style="color: white;">]:</span> <span style="color: white;">[(s</span> <span style="color: white;">:=</span> <span style="color: white;">s</span> <span style="color: white;">+</span> <span style="color: white;">x,</span> <span style="color: white;">s2</span> <span style="color: white;">:=</span> <span style="color: white;">s2</span> <span style="color: white;">+</span> <span style="color: white;">x**</span><span style="color: #0086f7; font-weight: bold;">2</span><span style="color: white;">)</span> <span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">x</span> <span style="color: white;">in</span> <span style="color: white;">data]</span>
<span style="color: white;">Out[</span><span style="color: #0086f7; font-weight: bold;">18</span><span style="color: white;">]:</span> <span style="color: white;">[(</span><span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">1</span><span style="color: white;">),</span> <span style="color: white;">(</span><span style="color: #0086f7; font-weight: bold;">3</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">5</span><span style="color: white;">),</span> <span style="color: white;">(</span><span style="color: #0086f7; font-weight: bold;">6</span><span style="color: white;">,</span> <span style="color: #0086f7; font-weight: bold;">14</span><span style="color: white;">)]</span>
</pre></div>
</div><div>(Those external variables s and s2 are updated to the last running sum).<br /></div><div><h1 style="text-align: left;">Source</h1></div><div> This all came about because I re-read the "Whats new in Python 3.9" doc after upgrading Anaconda and came across <a href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions" rel="nofollow" target="_blank">code </a>I couldn't initially fathom.<br /></div><h1 style="text-align: left;">End.</h1>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com1tag:blogger.com,1999:blog-11149365.post-31011077365421051092021-04-27T14:51:00.001+01:002021-04-27T14:54:47.681+01:00Read PEP 636 on Structural Pattern Matching: The Tutorial<p> Hi, I am just reading through <a href="https://www.python.org/dev/peps/pep-0636/" rel="nofollow" target="_blank">PEP 636 -- Structural Pattern Matching: Tutorial</a>. I'll give my views as I go along...</p><p>I really liked the choice of example - a<a href="https://en.wikipedia.org/wiki/Interactive_fiction" rel="nofollow" target="_blank"> text adventure</a>. I am old enough to relate to it, and I guess a quick google would inform others. <br /></p><p>Section "Matching sequences" serves as a gentle introduction to a match statement with one case</p><p>Subsequent sections expand on aspects of match: literals seem to match literally; names, (also called variables), when matched are also bind-to for use in the stastements, (plueral), of the case <b>block</b>.</p><p>skipping a few sections to something that is stated, that is nevertheless a departure from previous uses, that is<b> the treatment of the underscore</b>.</p><h3 style="text-align: left;">Underscores</h3><p>In section "Adding a wildcard" underscore becomes more than just a valid name, as it is outside the match statement. </p><p>Underscore always matches an object - just like any other identifier name; but does not get assigned the object it matches - like any other name.</p><p>Their example:</p><pre class="literal-block" style="-webkit-text-stroke-width: 0px; background-attachment: inherit; background-clip: inherit; background-color: #e6e8ea; background-image: inherit; background-origin: inherit; background-position: inherit; background-repeat: inherit; background-size: inherit; border: 1px solid rgb(221, 221, 221); box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 1em; box-sizing: border-box; color: #444444; font-family: Consolas, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, sans-serif; font-size: 15px; font-stretch: inherit; font-style: normal; font-variant-caps: normal; font-variant-east-asian: inherit; font-variant-ligatures: normal; font-variant-numeric: inherit; font-weight: 400; letter-spacing: normal; line-height: 1.875; margin: 0px 0px 1.3125em; orphans: 2; overflow-wrap: break-word; padding: 1em; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; vertical-align: baseline; white-space: pre-wrap; widows: 2; word-spacing: 0px;">match command.split():
case ["quit"]: ... # Code omitted for brevity
case ["go", direction]: ...
case ["drop", *objects]: ...
... # Other cases
case _:
print(f"Sorry, I couldn't understand {<span style="background-color: #b6d7a8;">command</span>!r}")</pre><p>You cannot write the following</p><pre class="literal-block" style="-webkit-text-stroke-width: 0px; background-attachment: inherit; background-clip: inherit; background-color: #e6e8ea; background-image: inherit; background-origin: inherit; background-position: inherit; background-repeat: inherit; background-size: inherit; border: 1px solid rgb(221, 221, 221); box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 1em; box-sizing: border-box; color: #444444; font-family: Consolas, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, sans-serif; font-size: 15px; font-stretch: inherit; font-style: normal; font-variant-caps: normal; font-variant-east-asian: inherit; font-variant-ligatures: normal; font-variant-numeric: inherit; font-weight: 400; letter-spacing: normal; line-height: 1.875; margin: 0px 0px 1.3125em; orphans: 2; overflow-wrap: break-word; padding: 1em; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; vertical-align: baseline; white-space: pre-wrap; widows: 2; word-spacing: 0px;">match command.split():
case ["quit"]: ... # Code omitted for brevity
case ["go", direction]: ...
case ["drop", *objects]: ...
... # Other cases
case _:
print(f"Sorry, I couldn't understand {<span style="background-color: #b6d7a8;"><span style="color: red;">_</span></span>!r}")</pre><p>As _ is not assigned.<b> Is it de-assigned in this block or will an earlier assignment outside the match statement still exist</b>?</p><p><br /></p><p>I am curious as to the decision for this extra non-assignment behaviour for underscore?</p><h3 style="text-align: left;">Testing Scope and Assignment</h3><p style="text-align: left;">Lets expand on their example assigning names globally and trying different commands to match different case clauses:</p><p style="text-align: left;"><br /></p>
<!--HTML generated using hilite.me--><div style="background: rgb(17, 17, 17) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="color: white;">_,</span> <span style="color: white;">direction,</span> <span style="color: white;">objects</span> <span style="color: white;">=</span> <span style="color: #0086d2;">"_ global"</span><span style="color: white;">,</span> <span style="color: #0086d2;">"direction global"</span><span style="color: white;">,</span> <span style="color: #0086d2;">"objects global"</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"{(_, direction, objects)=}"</span><span style="color: white;">)</span>
<span style="color: white;">command</span> <span style="color: white;">=</span> <span style="color: #0086d2;">"Nothing to match"</span>
<span style="color: #fb660a; font-weight: bold;">for</span> <span style="color: white;">command</span> <span style="color: white;">in</span> <span style="color: white;">[</span><span style="color: #0086d2;">"quit"</span><span style="color: white;">,</span> <span style="color: #0086d2;">"quit me"</span><span style="color: white;">,</span> <span style="color: #0086d2;">"go south"</span><span style="color: white;">,</span> <span style="color: #0086d2;">"drop anvil arrows"</span><span style="color: white;">]:</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"\n## COMMAND IS: {command!r}\n"</span><span style="color: white;">)</span>
<span style="color: white;">match</span> <span style="color: white;">command.split():</span>
<span style="color: white;">case</span> <span style="color: white;">[</span><span style="color: #0086d2;">"quit"</span><span style="color: white;">]:</span>
<span style="color: white;">print(</span><span style="color: #0086d2;">"quit"</span><span style="color: white;">)</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"{(_, direction, objects)=}"</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">[</span><span style="color: #0086d2;">"go"</span><span style="color: white;">,</span> <span style="color: white;">direction]:</span>
<span style="color: white;">print(</span><span style="color: #0086d2;">"go"</span><span style="color: white;">,</span> <span style="color: white;">direction)</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"{(_, direction, objects)=}"</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">[</span><span style="color: #0086d2;">"drop"</span><span style="color: white;">,</span> <span style="color: white;">*objects]:</span>
<span style="color: white;">print(</span><span style="color: #0086d2;">"drop"</span><span style="color: white;">,</span> <span style="color: white;">objects)</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"{(_, direction, objects)=}"</span><span style="color: white;">)</span>
<span style="color: white;">case</span> <span style="color: white;">_:</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"Sorry, I couldn't understand {command!r}"</span><span style="color: white;">)</span>
<span style="color: white;">print(f</span><span style="color: #0086d2;">"{(_, direction, objects)=}"</span><span style="color: white;">)</span>
</pre></div>
<p style="text-align: left;">Output:</p><p style="text-align: left;">xx<br /></p>
<!--HTML generated using hilite.me--><div style="background: rgb(255, 255, 255) none repeat scroll 0% 0%; border-color: gray; border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.1em 0.1em 0.1em 0.8em; border: medium solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;"><pre style="line-height: 125%; margin: 0px;"><span style="background-color: #93c47d;">Python 3.10<b>.</b>0a7</span> (tags/v3.10.0a7:53e5529, Apr 6 2021, 10:20:47) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
<span style="font-weight: bold;">>>> </span>
======== RESTART: C:/Users/paddy/Google Drive/Code/python_3_10_match.py ========
(_, direction, objects)=('_ global', 'direction global', 'objects global')
## COMMAND IS: 'quit'
quit
(_, direction, objects)=('_ global', 'direction global', 'objects global')
## COMMAND IS: 'quit me'
Sorry, I couldn't understand 'quit me'
(_, direction, objects)=('_ global', 'direction global', 'objects global')
## COMMAND IS: 'go south'
go south
(_, direction, objects)=('_ global', 'south', 'objects global')
## COMMAND IS: 'drop anvil arrows'
drop ['anvil', 'arrows']
(_, direction, objects)=('_ global', 'south', ['anvil', 'arrows'])
<span style="font-weight: bold;">>>> </span>
</pre></div>
<p> Concentrating on underscore, we see that it always has the initially assigned global value.</p><p>Once direction is changed in the case statement, however, it is this global value that is changed and the next iteration of the loop that does not match to it shows its new changed value.</p><p><br /></p><p>The special treatment of underscore seems odd to me? Why not <b>assign to it like normal and have only the special treatment of checking that if it is the only pattern then that pattern is the last pattern</b>?</p><h3 style="text-align: left;"><a href="https://www.youtube.com/watch?v=GYQfWJNWe3I" rel="nofollow" target="_blank">As</a></h3><p>Reading section "Capturing matched sub-patterns" I kind of thought ahead and was thinking "another use for the walrus operator", until the use of the as keyword revealed itself :-)</p><p>"as" reads well, I like it.</p><p><br /></p><h3 style="text-align: left;">Type and attribute matching</h3><p>Yay! Duck type friendly matching of attribute names.</p><p>Outside of a match statement a <i>call </i>of MyObject might require arguments, but it seems that MyObject() is valid in a match statement, regardless - something to remember.<br /></p><p><br /></p><h2 style="text-align: left;">END?</h2><p>I'll stop here for now although there is more of the doc to read.<br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0tag:blogger.com,1999:blog-11149365.post-23405259631016546342021-04-06T09:36:00.000+01:002021-04-06T09:36:45.535+01:00Practical numbers<p> </p><p><span style="color: #2b00fe;">
Best viewed on larger screens.</span></p><p> <br />
<script src="https://gist.github.com/Paddy3118/fff1acac2772e8030619172e32db4446.js"></script></p>Paddy3118http://www.blogger.com/profile/06899509753521482267noreply@blogger.com0