#!/usr/bin/python

# inspired by MATLAB-Tutorial by
#     Sebastian Scherer sebastian.scherer@uni-tuebingen.de
# adapted for python by
#     Jan-Peter Hohloch jan-peter.hohloch@uni-tuebingen.de
# CC BY-SA 3.0 ( http://creativecommons.org/licenses/by-sa/3.0/ )

# this is only for getting an impression, for deeper understanding
# and more extensive use see the python references u can find in the web

# in UNIX: 'chmod +x tutorial.py', then executable './tutorial.py'
# alternative 'python tutorial.py'
# there are IDEs, e.g. Spyder
# others see:
#     https://wiki.python.org/moin/IntegratedDevelopmentEnvironments

# 'import' to include libraries
# as allows to define aliases
import numpy as np

# Calculator
# ==========
#(best in a python-shell 'python' and use there,
#   in the shell there is also no need for the print command)
print 5+3
print 3**2 # ** is 3^2
print np.sqrt(2) #sqrt is from numpy library
print 1/np.sqrt(2) # Vorwahl von TU

print np.cos(np.pi) #numpy contains constants
print 1/np.inf
print

# Variables and functions
# ========================
# variables are used dynamically
a=3
b=1/np.sqrt(2)
print type(a) #int
print type(b) #float

# functions can be defined as lambda-abstraction
parabel= lambda (x) : 5*x**2+2*x+1
print parabel(5)
# alternatively they can be defined like this
def collatz(a):
    if np.mod(a,2) == 0:
        return a/2
    else:
        return 3*a+1
#IMPORTANT: there are no braces, indentation decides where the function ends!
print collatz(5)
print

# Linear algebra
# ==============
# there are matrices in numpy
M=np.matrix('1,2;3,4')
# but often arrays are sufficient
N=np.array([[1,2],[3,4]])

print M
print N
## transpose
Mt = M.T
Nt = N.T
# be careful: operators might describe different operations on
#  matrices and array
## matrix multiplication or element-wise multiplication
print Mt*M # matrix multiplication
print Nt*N # element-wise(!!!)
## multiplication with scalar
print 5*M
print 5*N
## concatenation with vstack and hstack (only one argument, so use tuples)
##    dimensions must match
print np.hstack((M,Mt))
print np.vstack((N,Nt))
# ... lots of other operations
# (see http://docs.scipy.org/doc/numpy/reference/index.html)
print

# Built in matrices
I=np.identity(3)
Z=np.zeros((3,3))
print I
print Z
print np.ones((3,3))

# many functions are also applicable to matrices
print np.sin(I)

# example from robotics
rot = lambda phi : np.matrix([[np.cos(phi),-np.sin(phi),0],[np.sin(phi),np.cos(phi),0],[0,0,1]])
print rot(np.pi)

# Access via index
# ================
print I[0,0]
print I[0:2,1] # 0 to 2, 2 excluded
print I[:,-1] # begin to end, last row(-1)

a=range(-10,10,2)
print a
print a[2:-4:2] # 3rd elem to 4th last in steps of 2
print

#for plotting you can use e.g. matplotlib

# Distributions and probabilities
# ===============================
# Random variable
X=np.random.rand(1000) # 4 random values, uniformly distributed in [0,1)
Xn=np.random.randn(4) # 4 random values ~N(0,1)
Xu=np.random.uniform(-1,1,2) # 2 rand val, uniformly dist in [-1,1)
Xu=np.random.normal(42,2,3) # 3 rand val, normally dist: ~N(42,2)

print np.mean(X)

mu=np.array([-1,1])
cov=np.array([[1,3],[3,9]])
r2=np.random.multivariate_normal(mu, cov, 3)
print r2

