# Name:		mathdictPyDemo.py
# Author:	Dave Renner		dmrenne@comcast.net
# Date:		09/03/11	2345
# Rev:		09/24/12	1945
# Note:		this is a called module of 'pyDemo.py'
#
# Note:		'mathdictPyDemo'
#
#
# ############################ BOILERPLATE ##########################
import pickle
import random
import math

myConfigFile = 'pyDemoConfig.dat'

# version has been stored in our calling module 'pyDemo.py'
PythonVersFile = 'version.obj'
versfile = open(PythonVersFile, 'rb')
PythonVers = pickle.load(versfile)
versfile.close()

if PythonVers == '30':
    import configparser
    config = configparser.ConfigParser()
    input_cmd = 'input'
else:
    import ConfigParser
    config = ConfigParser.ConfigParser()
    input_cmd = 'raw_input'

config.read(myConfigFile)
interactive = config.get('Mode', 'interactive')
continue_cmd = config.get('Strings', 'continue_cmd')

# easier to just include this boilerplate function into every module 
# than to import it
def printf(arg):
    cmdText = "print "
    arg_nonSlash = arg.strip('\\')
    if PythonVers == '30':
        cmdFull = cmdText + '("' + arg_nonSlash + '")'
    else:
        cmdFull = cmdText + '"' + arg_nonSlash + '"'
    exec( cmdFull )

# ###################################################################
# DICTIONARIES
#
printf("")
printf("We are now in 'mathdictPyDemo.py' in which we will demo\
\\nsome of Python's handling of mathematical expressions\
\\nbut first, we'll show how dictionaries work,\
\\nsince they're a basic, and often used Python type.\
\\n\
\\nA dictionary is a key-value pairing.\
\\nThe C equivalent might be a structure like this:\
\\n	struct myDict {\
\\n		int myKey;\
\\n		char myValue[16];\
\\n	}\
\\n\
\\nIn Python, we create a dictionary like this:\
\\n\
\\n	myComedyDict = { 1 : 'Abbott', 2 : 'Moe', 3 : 'Curly',\
\\n4: 'Costello', 5: 'Larry', 6 :'Costello' }\
\\n\
\\nAs shown, key-value pairs are separated by colons,\
\\nand pairs are separated from one another by commas.\
\\n")

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")
	
printf("A dictionary may contain any data type:\
\\ninteger, string, list, even another embedded dictionary.\
\\n\
\\nKeys may even be of different data types!\
\\nthough there's probably no good reason to do this.\
\\n\
\\nDictionaries are 'hash tables', that is, their entries\
\\nare allocated in a pseudo-random fashion\
\\nfor efficient memory lookup, though\
\\ninefficient storage.\
\\nDictionaries, then, are meant to store data that's\
\\nlooked up frequently, but not changed frequently.")

printf("")
if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	

printf("")
printf("Typically, a dictionary wouldn't be loaded manually,\
\\nas in the example above, but by converting pre-constructed data.\
\\nThere can be many ways of doing this.\
\\nAn easy way is to start with two lists of corresponding strings.\
\\n\
Like this:\
\\n\
\\nstr FrenchStr = 'chez, deux, adeiu' \
\\nstr EnglishStr = 'house, two, hello' \
\\nfrenchList = FrenchStr.split(',') \
\\nenglishList = EnglishStr.split(',') \
\\nfrenchEnglishDict = {} \
\\ni = 0 \
\\nfor fr in frenchList: \
\\nfrenchEnglishDict[ fr ] = englishList[i] \
\\ni += \
\\n")

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")
	
	printf("So you see, we start with an empty dictionary.\
\\nThen we split the corresponding strings into separate indices\
\\nwith the 'split' method.\
\\nThe Python dictionary object provides a 'matching' syntax by\
\\nassigning keys with values. Python provides the required colon\
\\nto separate keys from values.\
\\nAnd Python knows by context whether the values are integers or\
\\nin this case strings, which require quotes.\
\\nPython supplies the quotes when populating the dictionary.\
\\nPretty slick.\
\\nOf course, we must be sure that the corresponding strings\
\\nare in the correct order, compared to one another.\
\\nThey should not be sorted, for that defeats the value of\
\\nthe hash which averages out the time for lookups.")

printf("")
if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")
	
printf("So here it is, in practice, using the above code:")	
FrenchStr = 'chez, deux, adeiu'
EnglishStr = 'house, two, hello'
frenchList = FrenchStr.split(',')
englishList = EnglishStr.split(',')
frenchEnglishDict = {}
i = 0
for fr in frenchList:
	frenchEnglishDict[ fr ] = englishList[i]
	i += 1
printf("Here's our resulting dictionary")
printf("")
if PythonVers == 30:
    print(frenchEnglishDict)
else:
    print (frenchEnglishDict) 

	
# #########################################################################
# MATH
# (just a little)

printf("")
if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")

printf("OK, we'll just go over a couple of math-related things\
\\nin Python. Most of Python's arithmetic procedures, syntax\
\\nare a lot like the C language. Most of them are handled\
\\nwithout additional imports.\
\\nFor more specialized help, Python offers the modules:\
\\ncmath, decimal, gmpy, math, numbers, operator, and random.\
\\n\
\\nRandom numbers, for instance, are used a lot in\
\\ncomputer gaming and security.\
\\n\
\\n	import random\
\\n	myRandom = random.random()\
\\nwill result in a random number between 0 and 1.\
\\nLet's try it. The result is:")

myRandom = random.random()
if PythonVers == '30':
    print(myRandom)
else:
    print (myRandom)

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	

printf("")

printf("Factorials -- that is the product of all integers less than\
\\nor equal to 'x' -- are obtained by\
\\nthe 'factorial(x) function.\
\\nThe factorial of number 7, using this function is:")
myFactorial7 = math.factorial(7)
if PythonVers == '30':
    print( myFactorial7 )
else:
    print (myFactorial7)
printf("")
printf("")

printf("Now we'll do logarithms.")
printf("We'll first get the logarithm for 100, base 2")
myLog100 = math.log(100,2)
if PythonVers == '30':
    print(myLog100)
else:
    print (myLog100)
printf("")
printf("OK. The number of times that 100 is repeatedly\
\\ndivisible by 2 is about 6 and 2/3")
printf("")
printf("Using base ten for number 100, we get logarithm:")
myLog100 = math.log(100,10)
if PythonVers == '30':
    print(myLog100)
else:
    print (myLog100)
printf("that is, 100 / 2 / 2 = 1, which means divisible by 2 twice")

printf("")
if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")
printf("Next, we'll see how Python handles division.\
\\nBy default, Python uses strict numeric division,\
\\nleaving no fractional remainder.\
\\nThis is usually not what you want.\
\\nTo obtain both a quotient and remainder,\
\\nuse the 'divmod' function, like: 'divmod(88, 7)\
\\nto get a tuple containing the quotient and remainder,\
\\n\
\\nThe same result could be obtained with the expressions\
\\n'a // b' and 'a % b', but 'divmod()' is easier.\
\\nOK, here's the resulting tuple, as quotient and remainder:")

q, r = divmod(88, 7)
if PythonVers == '30':
    print(q)
else:
    print (q)
if PythonVers == '30':
    print(r)
else:
    print (r)

