#simple rounding
round(1.23, 1)

round(-1.27, 1)

#rounding at .5 goes to the even number
round(1.5,0)

round(2.5,0)

#rounding negative numbers rounds out the tens,hundreds...
a= 1627731
round(a, -1)

round(a, -2)

"""
Explanation: Python Numbers, Dates & Times Quick Reference
Table of contents

<a href="#1.-Rounding">Rounding</a>
<a href="#2.-Decimal">Decimal</a>
<a href="#3.-Infinity-and-NaN">Infinity and NaN</a>
<a href="#1.-Large-Numerical-Arrays-(numpy)">Large Numerical Arrays (numpy)</a>
<a href="#1.-Random">Random</a>

1. Rounding
End of explanation
"""


# example of the problem
a = 4.2
b = 2.1
a + b

(a + b) == 6.3

# with Decimal
from decimal import Decimal

# Decimal is constructed with strings!
a = Decimal('4.2')
b = Decimal('2.1')
a + b

(a + b) == Decimal('6.3')

# to change precision use a scoped local context
from decimal import localcontext

a = Decimal('1.3')
b = Decimal('1.7')
print(a / b) #0.7647058823529411764705882353

with localcontext() as ctx:
    ctx.prec = 3
    print(a / b)

"""
Explanation: 2. Decimal
Use decimal for floating points that handle high precision base-10 numbers better
End of explanation
"""


import math

a = float('inf')
b = float('-inf')
c = float('nan')
a

b

c

a+3

3+b

36/c

math.isinf(b)

math.isnan(c)

"""
Explanation: 3. Infinity and NaN
End of explanation
"""


# it is traditional to import numpy as np
import numpy as np

# python lists
x = [1, 2, 3, 4]
y = [5, 6, 7, 8]
x * 2

# numpy array is a perf-optimized math-centric array
ax = np.array([1, 2, 3, 4])
ay = np.array([5, 6, 7, 8])
ax * 2

ax + 10

ax + ay

ax * ay

# numpy provides some universal math functions that work on arrays
np.sqrt(ax)

np.cos(ax)

#numpy arrays are stored like c arrays (contiguous blocks of same-sized memory) and so very large arrays can be used
grid = np.zeros(shape=(10000,10000), dtype=float)
grid

grid += 10
grid

np.sin(grid)

"""
Explanation: 4. Large Numerical Arrays (numpy)
End of explanation
"""


a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
a

# Select row 1
a[1]

# Select column 1
a[:,1]

# Select a subregion and change it
a[1:3, 1:3]

a[1:3, 1:3] += 10
a

# Broadcast a row vector across an operation on all rows
a + [100, 101, 102, 103]

# Conditional assignment on an array
np.where(a < 10, a, 10) # syntax is where( condition, if_true, if_false)

"""
Explanation: multidimensional arrays
End of explanation
"""


import random

values = [1, 2, 3, 4, 5, 6]

# seed the random function
random.seed() # Seed based on system time or os.urandom()
random.seed(12345) # Seed based on integer given

# pick a random item out of a sequence
random.choice(values)

# take a sampling of N items where selected items are removed from further consideration
random.sample(values, 5)

# shuffle items in a sequence in place
random.shuffle(values)
values

# produce a random integer between 0 and 10 inclusive 
random.randint(0,10) 

# produce a random floating point number between zero and one
random.random()

"""
Explanation: 5. Random
End of explanation
"""


