#!/usr/bin/env python
# coding: UTF-8
#
## @package functions
#
#   Multiplication using shifts.
#
#   Thinking binary:
#   <pre>
#       1011 &times;         11
#       1101           13
#       ----     --------
#       1011 +   1*1*1011  1011 << 0
#      00000     0*2*1011  0000 << 1
#     101100     1*4*1011  1011 << 2
#    1011000     1*8*1011  1011 << 3
#    -------     --------
#   10001111          143
#   </pre>
#   
#   @author Paulo Roma
#   @since 15/10/2011
#

from __future__ import print_function
from functools import reduce
import sys, os

# Fix Python 2.x.
try: input = raw_input
except NameError: pass

sys.path.insert(0, os.path.expanduser("~roma")+"/html/python/labs")
from _10_factorize2 import toString, factorize, condense

##
#
#   Multiplies two integers by using only the + operation.
#
#   @param a first factor.
#   @param b second factor.
#   @param debug print each step.
#   @return a*b
#
def Product (a, b, debug = False):
    p = 0

    if debug:
        print("<pre><code>")
        w  = lambda t=0: len(bin(a))+len(bin(b))+4+t    # add a tab
        wm = max(len(bin(a)),len(bin(b)))-2             # remove 0b
        line = wm*'-'
        print("{:>{width0}} &times; <br> {:>{width}} <br> {:>{width}}".format(bin(a)[2:],bin(b)[2:],line,width0=w(2),width=w(1)))

    while (b != 0):
            if (b & 1):
                p += a
           
            if debug:
               c = bin(a)[2:] if (b & 1) else '0'
               print("{:>{width}}".format(c,width=w(2)))

            a <<= 1
            b >>= 1

    if debug:
        wp  = len(bin(p))-2
        line = wp*'-'
        print(" {:>{width}} <br> {:>{width}} = {}".format(line,bin(p)[2:],p,width=w()))
        print("</pre></code>")

    return p

## Greatest Common Divisor, which returns the highest 
#  number that divides into two other numbers exactly.
#  @param x first integer.
#  @param y second integer.
#  @param debug print each step.
#  @return GCD.
#
def gcd(x, y, debug = False):
    if debug: print("<pre><code>")
    while x:
        if debug:
            print("\t\t{}, {} = {} % {}, {}".format(x,y,y,x,x))
    
        x, y = y % x, x
    
    if debug:
        print ("\t\t{}, {} => return {}".format(x,y,y))
        print("<pre><code>")

    return y

## Least Common Multiple, which returns the smallest
#  number that can be divided by x and y without any remainder.
#  @param x first integer.
#  @param y second integer.
#  @return LCM.
#  @see https://en.wikipedia.org/wiki/Least_common_multiple
#  @see https://www.w3resource.com/python-exercises/challenges/1/python-challenges-1-exercise-37.php
#
lcm = lambda x,y: x*y//gcd(x,y)

## Print "Hello!".
#  @param lf line feed character.
#
def sayHello(lf="\n"):
    print ("<h1>Hello Functions!</h1>" + lf)
    
## Print a string.
#  @param something a string.
#
def saySomething(something):
    print ("<h2>"+something+"</h2>")
    
## Smallest integer number with all factors from 1 to n.
#  @param n limit.
#  @return smallest integer.
#
LCM = lambda n: reduce(lcm,range(1,n+1))
    
## Lambda function for multiplying two numbers.
multiplyTwoNumbers = lambda x,y: x * y

## Factorize the smallest integer number with all factors from 1 to n.
def allFactors(n):
    f = LCM(int(n))
    g = toString(condense(factorize(f)))
    return f, g if sys.version_info.major > 2 else g.encode('utf-8')

## Main function for testing.
 #
 # Usage:
 # - http://www.lcg.ufrj.br/cwdc/11-python/functions.py?2+3 or
 # - http://www.lcg.ufrj.br/cwdc/11-python/functions.py?12
 # 
 # @param argv command line arguments.
 #  argv:
 #  - argv[0]: /Library/WebServer/Documents/cwdc/11-python/functions.py (macOS)
 #  - argv[1]: an integer that will set the range to get all factors.
 #  - argv[2]: another integer that will be the second for Product. (optional)
 #
def main(argv=None):
    if argv is None:
        argv = sys.argv
    
    HTML = 'REQUEST_METHOD' in os.environ
    AJAX = len(argv) == 2
    
    if not HTML:
        try:
            a,b=str(input('Type two positive integers: ')).split()
            print (Product (int(a),int(b)))
        except ValueError:
            main() 
    else:
        # Entry point for the ajax call.
        if AJAX:
            print("Content-Type: text/html\n")
            n,f = allFactors(argv[1])
            print ( "%s = %s" % (n, f) )
            return
        print ("""Content-type: text/html; charset=utf-8\r\n\r\n
            <html>
            <head>
                <link rel="stylesheet" type="text/css" href="../mainPage/LCG.css">
                <title>Functions</title>
                <style>
                    label {
                        display: block;
                        text-align:center;
                        width: 600px;
                    }
                    ins {
                        color:red;
                        text-decoration: none;
                    }
                    span.subsup {
                        position: relative;
                    }
                    span.subsup sub {
                        position: absolute;
                        left: 0em;
                        bottom: -0.4em;
                    }
                </style>
            </head>
            <body>
        """)

        newline = '<br>'

        try:
            a = int(argv[1])
            b = int(argv[2])
        except ValueError as e:
            print(e)
            a = 11
            b = 13

        sayHello(newline)

        saySomething('<a href="https://www.youtube.com/watch?v=MfLw72g2DYE">Good Morning, CWDC students!</a>' + newline)
        
        print(2*newline)

        # -------------------------------------------------------------------------------------------------------

        print ("An anonymous <b><a href='../../python/laboratorios.html#lambda'>lambda function</a></b>: {} * {} = {}{}".format(a,b,multiplyTwoNumbers(a, b), newline))
        
        print ("""
        <pre>
            # A lambda function can take any number of arguments, 
            # but can only have one expression.

            multiplyTwoNumbers = lambda x,y: x * y
            print(multiplyTwoNumbers({},{}))
        </pre>
        """.format(a,b))
        
        print(2*newline)

        # -------------------------------------------------------------------------------------------------------

        print ("<b>Greatest Common Divisor</b> of {}, {} = {} (Euclides' <a href='../../python/laboratorios.html#gcd'>GCD</a>){}".format(a,b,gcd(a, b),newline))
        
        print ("""
        <pre>
            def <mark>gcd</mark>(x, y):
                while x:
                    x, y = y % x, x
                return y
            </pre>
        """)
        gcd(a, b, True)


        # -------------------------------------------------------------------------------------------------------

        print("<h3>Thinking binary.</h3>")
        print ("{} * {} = {} (Product using shifts - example){}".format(11,13,Product(11,13), newline))
        
        print ("""
        <pre>
            def Product (a, b):
                p = 0

                while (b != 0):
                    if (b & 1):
                        p += a
                    a <<= 1
                    b >>= 1

                return p


                1011 &times;         11 &times;                                    11 &times;
                1101           13                                      13
                ----     --------                                --------
                1011 &#43;   1*1*1011 &#43; 1011 << 0                     3*01*11 &#43; 11 << 0
                   0     0*2*1011   0000 << 1                     1*10*11   11 << 1
              101100     1*4*1011   1011 << 2                    --------
             1011000     1*8*1011   1011 << 3                         143
             -------     --------
            10001111          143
        </pre>
        """)

        print ("{} * {} = {} (Product using shifts){}".format(a,b,Product(a,b), newline))
        Product(a,b,True)

        # -------------------------------------------------------------------------------------------------------

        print ("<h1>Python is a functional language!!</h1>")

        print("""<p><a href='iterables.jpg'> <img src='iterables.jpg' width=550px></a></p>""")
        print("""<p><a href='generators.png'> <img src='generators.png' width=550px></a></p>""")
        print("""<p><a href='iter.jpg'> <img src='iter.jpg' width=250px></a></p><h3><a href="https://nvie.com/posts/iterators-vs-generators/">Iterables vs. Iterators vs. Generators</a></h3>""")

        print ("""The smallest integer divisible by all integers in the range(1, <ins>i</ins>+1=<span id="i1"> </span>) is:
            <p>
            LCM = lambda m: <a href="https://realpython.com/python-reduce-function/">reduce</a>(lambda a,b:a*b//<mark>gcd</mark>(a,b),range(1,m+1))
            </p>
            <p>
            In <a href="https://portingguide.readthedocs.io/en/latest/numbers.html">Python 3</a>, <strong>int</strong> corresponds to <strong>long</strong> of Python 2, and there is no maximum / minimum limit.
            </p>
            <p>
            LCM(<ins><span id="i">i</span></ins>) = <span id="prod"> </span> 
            </p>
        """)

        print ("""
        <form id="form" method="post">
            <label for="n"><ins>i</ins> <span id="range"></span></label>
            1<input type="range" min="1" max="100" value="43" step="1" id="n" name="n" style="width: 400px;" oninput="getFactors()">100
        </form>
        """)

        print("""
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
        <script>
            function getFactors() {
                let n = document.getElementById('n');
                let m = document.getElementsByTagName('ins');
                if (n) {
                    let factors  = n.value;
                    // Python2 sys.maxint = 9223372036854775807
                    // factors = 44    ->   9419588158802421600
                    // In Python3, int corresponds to long of Python2, and there is no maximum / minimum limit.
                    let mcolor = factors > 43? "red": "green";
                    for ( i of m ) {
                        i.style.color = mcolor;
                    }
                    // python runs on the server, and javascript on the browser
                    $.ajax({
                        type: "GET",
                        url: "./functions.py",
                        data: factors,
                        success: function(response) {
                            let f = response;
                            document.getElementById('prod').textContent = `${f}`;
                            let bits = Math.trunc(Math.log2(f.split(" = ")[0])) + 1
                            document.getElementById("range").innerHTML = `(${bits} bits)`
                        }
                    }).fail(function() {
                        console.log("Could not get data");
                    });
                    document.getElementById('i1').textContent = (n.valueAsNumber+1).toString();
                    document.getElementById('i').textContent = (n.valueAsNumber).toString();
                }
            }
            getFactors();
        </script>

        </body> 
        </html>
        """)

if __name__=="__main__":
    sys.exit(main())