#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Для исполнения одного примера задайте его номер
в качестве параметра при запуске программы.
Используйте параметр h или help для просмотра
документации к функциям и классам примеров.
"""

import sys

def pfi():
	"Print function information"
	caller_code = sys._getframe(1).f_code
	example_no = caller_code.co_name[-2:]
	h = '*' * 6
	print('\n       ', h, 'Example', example_no, h)
	print(caller_code.co_consts[0])

def shdr(s):
	"Section header"
	h = '*' * 6
	print('\n   ', h, s, h, '\n')

def phdr(s):
	"Paragraph header"
	h = '=' * 3
	print('   ', h, s, h)

def m(s):
	"Print mark at line begin"
	print(s + ' => ', end='')

def n(s):
	"Print mark at line begin with newline"
	print(s + ' => ')

def info(a, prefix=None):
	"Print array information"
	if prefix:
		print(str(prefix) + ' ', end='')
	print('ndim =', a.ndim, end=', ')
	print('shape =', a.shape, end=', ')
	print('size =', a.size, end=', ')
	print('dtype =', a.dtype, end=', ')
	print('itemsize =', a.itemsize)

def peq(a, b, prefix=None):
	"Print arrays equality"
	if prefix:
		print(str(prefix) + ' ', end='')
	if np.array_equal(a, b):
		print('Arrays are equal')
	else:
		print('Arrays differ')

def create_sample_file():
	"Создание тестового файла"
	with open('sample.txt', 'w') as f:
		f.write(
"""This is sample text for file reading ehamples
Second line
Third line
Четвертая строка по-русски
Line 5 - the end
""")

# Необходимо импортировать модуль numpy
import numpy as np

def example_01():
	"""
	Создание массива
	"""
	pfi()

	# array это alias для ndarray
	a = np.array(
 [
   [ 
     [1, 2, 3], [4, 5, 6]
   ],
   [
     [7, 8, 9], [10, 11, 12]
   ]
 ]
 )
	print(a)
	print('shape =', a.shape) # => (2, 2, 3)
	print('a[0, 1, 2] =', a[0, 1, 2]) # => 6
	print(a.__class__) # => <class 'numpy.ndarray'>

	b = np.array((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)).reshape(2, 2, 3)
	if np.array_equal(b, a):
		print('b == a')
	else:
		print('b != a')

	c = np.arange(1, 13).reshape(2, 2, 3)
	if np.array_equal(c, a):
		print('c == a')
	else:
		print('c != a')
	
	d = np.arange(0.0, 1.1, 0.1) # => [ 0. 0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1. ]
	print('d ==', d)

	# Оператор == позволяет сравнивать только массивы содержащие один элемент
	if np.array(1) == np.array(1):
		print('np.array(1) == np.array(1)')

def example_02():
	"""
	Конструктор массива
	"""
	pfi()

	phdr('Array a')
	a = np.arange(15).reshape(3, 5)
	print(a.__class__, a.dtype, 'elements', '\n', a)
	phdr('Array b')
	# Все элементы будут приведены к типу float, см. 3.3
	b = np.array((1, 3.3, 5, 2, 4, 6)).reshape(2, 3)
	print(b.__class__, b.dtype, 'elements', '\n', b)
	print('b.shape[0] =', b.shape[0]) # => 2
	print('b.shape[1] =', b.shape[1]) # => 3
	print('b[1, 2] =', b[1, 2])       # => 6.0
	print('\nb.flags =', b.flags)
	info(b, 'Array b:')

def example_03():
	"""
	Итерация по массиву
	"""
	pfi()

	a = np.arange(1, 13).reshape(2, 2, 3)
	for e in a:
		print('Subarray:\n', e)
	print('Iterate per element:', end=' ')
	for e in a.flat:
		print(e, end=' ')
	print('\n')

def example_04():
	"""
	Однородное заполнение массива
	"""
	pfi()

	print('empty:',   np.empty((2, 3)))
	print('zeros:',   np.zeros((2, 3)))
	print('ones:',    np.ones((2, 3)))
	print('rand:',    np.random.rand(2, 3))
	print('randn:',   np.random.randn(2, 3))
	print('randint:', np.random.randint(low=100, high=200, size=(2, 3), dtype='l'))
	print('bytes:',   np.random.bytes(16))

def example_05():
	"""
	Функции frombuffer() и fromfile()
	"""
	pfi()

	b = b'\x24\x17\xa4\x1f\x24\x17\xa4\x1f'
	a = np.frombuffer(b, dtype='int16')
	info(a)
	print(a)
	create_sample_file()
	a = np.fromfile(open('sample.txt', 'rb'), dtype='int64')
	info(a)
	print(a)

def example_06():
	"""
	Индексация
	"""
	pfi()

	phdr('1-D Array')
	a1d = np.array((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
	m('a1d')
	print(a1d) # => [ 1  2  3  4  5  6  7  8  9 10 11 12]
	m('a1d[5]')
	print(a1d[5]) # => 6
	m('a1d[2:5]')
	print(a1d[2:5]) # => [3 4 5]
	m('a1d[2:8:2]')
	print(a1d[2:8:2]) # => [3 5 7]
	m('a1d[[1, 3, 9, 2]]')
	print(a1d[[1, 3, 9, 2]]) # => [ 2  4 10  3]
	m('a1d[[i**2 for i in range(4)]]')
	print(a1d[[i**2 for i in range(4)]]) # => [ 1  2  5 10]
	a1d[5] = 18
	a1d[8:11] = (28, 29, 30)
	a1d[[1, 3, 2]] = 101, 103, 102
	m('a1d')
	print(a1d) # => [  1 101 102 103   5  18   7   8  28  29  30  12]
	# del a1d[4] # => ValueError: cannot delete array elements

	phdr('2-D Array')
	a2d = np.array((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)).reshape(3, 4)
	n('a2d')
	print(a2d) # => 
	# [[ 1  2  3  4]
	# [  5  6  7  8]
	# [  9 10 11 12]]
	m('a2d[2, 1]')
	print(a2d[2, 1]) # => 10
	m('a2d[2]')
	print(a2d[2]) # => [ 9 10 11 12]
	n('a2d[0:2, 1:3]')
	print(a2d[0:2, 1:3]) # =>
	# [[2 3]
	#  [6 7]]
	m('a2d[[0,2,1], [1,3,2]]')
	print(a2d[[0,2,1], [1,3,2]]) # => [ 2 12  7]

	phdr('3-D Array')
	a3d = np.array((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)).reshape(2, 2, 3)
	n('a3d')
	print(a3d) # => 
	# [[[ 1  2  3]
	#   [ 4  5  6]]
	# 
	#  [[ 7  8  9]
	#   [10 11 12]]]
	m('a3d[1, 0, 2]')
	print(a3d[1, 0, 2]) # => 9
	n('a3d[1]')
	print(a3d[1]) # =>
	# [[ 7  8  9]
	#  [10 11 12]]
	m('a3d[1, 1]')
	print(a3d[1, 1]) # => [10 11 12]
	n('a3d[0:2, 0:2, 1:3]')
	print(a3d[0:2, 0:2, 1:3]) # =>
	# [[[ 2  3]
	#   [ 5  6]]
	# 
	#  [[ 8  9]
	#   [11 12]]]
	m('a3d[[0,1], [1,0], [2,2]]')
	print(a3d[[0,1], [1,0], [2,2]]) # => [ 6, 9]

def example_07():
	"""
	Ellipsis
	"""
	pfi()

	a = np.arange(720).reshape(2, 3, 4, 2, 3, 5)
	info(a, 'a')

	b1 = a[1,2,:,:,:,:]
	b2 = a[1,2,...]
	peq(b1, b2, 'a[1,2,:,:,:,:] and a[1,2,...]')
	info(b1, 'b1')

	c1 = a[1,:,:,:,:,4]
	c2 = a[1,...,4]
	peq(c1, c2, 'a[1,:,:,:,4] and a[1,...,4]')
	info(c1, 'c1')

	d1 = a[:,:,:,:,2,3]
	d2 = a[...,2,3]
	peq(d1, d2, 'a[:,:,:,:,2,3] and a[...,2,3]')
	info(d1, 'd1')
	
	# e = a[...,0,...] # IndexError: an index can only have a single ellipsis ('...')

def example_08():
	"""
	Выборка по условию
	"""
	pfi()

	a = np.array((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
	m('a')
	print(a) # => [ 1  2  3  4  5  6  7  8  9 10 11 12]
	m('a[a % 3 == 0]')
	print(a[a % 3 == 0]) # => [ 3  6  9 12]
	i = 3
	a[a % i == 0] = (30, 60, 90, 120)
	m('a2')
	print(a) # => [  1   2  30   4   5  60   7   8  90  10  11 120]

def example_09():
	"""
	Матрицы
	"""
	pfi()

	a = np.matrix('1 2 3; 4 5 6; 7 8 9')
	b = np.matrix(((1, 2, 3), (4, 5, 6), (7, 8, 9)))
	peq(a, b)
	n('a')
	print(a, a.__class__)
	n('a.T')
	print(a.T)

def example_10():
	"""
	Операции с массивами
	"""
	pfi()

	a = np.array(((1,2,3), (11, 12, 13), (21, 22, 23)))
	info(a)
	n('a')
	print(a)
	# Расширение массива (broadcast)
	b = a + (2, 3, 4)
	n('b = a + (2, 3, 4)')
	print(b)
	c = np.array((1,2,3))
	m('c'); print(c)
	m('c + 10'); print(c + 10)
	m('c'); print(c)
	# Составное присваивание модифицирует массив
	c += 10
	m('c += 10'); print(c)

def example_11():
	"""
	Метод flatten()
	"""
	pfi()

	a = np.array(((1,2,3), (11, 12, 13), (21, 22, 23)))
	n('a'); print(a)
	# Метод flatten() создает копию массива
	b = a.flatten()
	m('b'); print(b) # => [ 1  2  3 11 12 13 21 22 23]
	phdr('b[3] = 0')
	b[3] = 0
	n('a'); print(a) # Массив не изменился
	m('b'); print(b) # => [ 1  2  3  0 12 13 21 22 23]

def example_12():
	"""
	linspace() и logspace()
	"""
	pfi()

	a = np.linspace(-np.pi, np.pi, 11, endpoint=True)
	print(a)
	b = np.logspace(0, 1, 11, base=np.e, endpoint=True)
	print(b)

	"""
	По умолчанию:
	Число точек == 50
	endpoint=True
	то есть 49 интервалов
	"""
	c1 = np.linspace(0, 100)
	c2 = np.linspace(0, 100, endpoint=True)
	c3 = np.linspace(0, 100, endpoint=False)
	print('c1 =', c1[:3])
	print('c2 =', c2[:3])
	print('c3 =', c3[:3])

def example_13():
	"""
	Функция meshgrid()
	"""
	pfi()

	vx = (1, 2, 3)
	vy = (4, 5, 6)
	ax, ay = np.meshgrid(vx, vy)
	az = np.sqrt(ax**2 + ay**2)
	print('ax =>\n', ax)
	print('ay =>\n', ay)
	print('az =>\n', az)

if len(sys.argv) > 1:
	if sys.argv[1].isdigit() and int(sys.argv[1]) > 0:
		exec('example_%02d()' % int(sys.argv[1]))
	elif sys.argv[1][0].lower() == 'h':
		help(__name__)
	else:
		print(__doc__)
else:
	tuple(map(lambda c: exec(c + '()'),
		(f for f in sys._getframe().f_code.co_names
			if f.startswith('example_'))))
