# coding: utf-8

# ##  Embedding For Categorical Features Using StringIndexer in Pyspark
# ##  Working Example House Prices
# ## The purpose of this notebook is to demonstrate the practical implementation of Embedding for Encoding Categorical Features for Training a Neural Network In Keras using String Indexer. Here we would be doing supervised regression to predict the house price.

# An embedding learns to map each of the unique category into a N-dimensional vector real numbers. An embedding learns to map each of the unique category into a N-dimensional vector real numbers.
# The advantage of using embeddings is that we can determine the number of dimensions to represent the categorical feature as opposed to in one-hot-embedding where we need to break the feature into as many unique values are present for that categorical feature.
# Also like word vectors, entity embeddings can be expected to learn the intrinsic properties of the categories and group similar categories together.
# What we are trying to do is learn a set of weights for each of the categorical columns, and these weights will be used to get the embeddings for some value of that column

# In[1]:

import keras
from keras.models import Sequential,Model,model_from_json
from keras.layers import Input, Dense, Flatten, Reshape, Concatenate,Activation,Dropout
from keras.wrappers.scikit_learn import KerasClassifier
from keras.preprocessing.text import one_hot
from keras.preprocessing.sequence import pad_sequences
from keras.layers.embeddings import Embedding
from keras.utils.vis_utils import plot_model
from sklearn.model_selection import GridSearchCV
import numpy as np
from numpy import array
import pandas as pd
import pyspark
from pyspark.sql.functions import *
from pyspark.sql import SparkSession
from pyspark.conf import SparkConf
from pyspark.context import SparkContext
from pyspark.ml import Pipeline
from pyspark.ml.feature import StringIndexer, OneHotEncoderEstimator,IndexToString
from pyspark.sql import functions as F
from pyspark.ml.feature import MinMaxScaler
from pyspark.ml.feature import VectorAssembler
from pyspark.storagelevel import StorageLevel
import json
import matplotlib.pylab as plt
import matplotlib.pyplot as pplot
pd.options.display.max_columns = 50
import warnings
warnings.filterwarnings("ignore")
get_ipython().run_line_magic('matplotlib', 'inline')

# ## The Ames Iowa Housing Data. Data is in Excel Format. We will read first in Panda's DateFrame before converting to Pyspark's DataFrame:

# In[2]:

url = 'http://www.amstat.org/publications/jse/v19n3/decock/AmesHousing.xls'
# Load the file into a Pandas DataFrame
data_df = pd.read_excel(url)
print('DataFrame size:', data_df.shape)

# In[3]:

data_df.columns

# In[4]:

data_df.head(2)

# In[5]:

_=[data_df[item].fillna("",inplace=True) for item in list(data_df.select_dtypes(include=['O']).dtypes.index[:])]

# ## Create PySpark DataFrame from Panda dataframe.

# In[6]:

spark =pyspark.sql.SparkSession.builder.master("local").getOrCreate()

# In[7]:

df_data=spark.createDataFrame(data_df)

# ## Sub-Feature Space: Predictors and Target

# In[8]:

# For demonstration, the following features are selected.
features = ['Neighborhood','Bldg Type','Year Built','Roof Matl','Sale Condition','Sale Type','House Style','Gr Liv Area']
# The aim is to predcit sale prices.
target = ['SalePrice']

# In[9]:

dataset=df_data.select(features+target)

# In[10]:

dataset.printSchema()

# In[11]:

dataset.show(2)

# ## Create three list containing categorical predictors, numeric predictors and target

# In[12]:

cat_in_cols=['Neighborhood','Bldg Type','Roof Matl','Sale Condition','Sale Type','House Style']
cat_out_col=['SalePrice']
numeric_in_cols=['Year Built','Gr Liv Area']

# ## Use of StringIndexer, VectorAssempler and MinMaxScaler for the input data

# In[13]:

def dataPrePocessingPredictors( inputDataFrame, cat_cols, numeric_cols,out_col):

    data_df=inputDataFrame.select([col(column).cast("long") if column in numeric_cols+out_col else col(column) for column in inputDataFrame.columns])

    # converting categorical data
    for cat_col in cat_cols:
        indexer=StringIndexer(inputCol=cat_col,outputCol=cat_col+'_Index',handleInvalid='keep')
        data_df=indexer.fit(data_df).transform(data_df)

    # converting numeric data column to a vector clomun and scaling after that
    assembler=VectorAssembler(inputCols=numeric_cols,outputCol='numeric_col_features')
    transformed = assembler.transform(data_df)
    scaler = MinMaxScaler(inputCol="numeric_col_features", outputCol="numeric_col_scaledfeatures")
    scalerModel=scaler.fit(transformed.select('numeric_col_features'))
    scaledData=scalerModel.transform(transformed)

    # converting numeric output datacolumn to a vector clomun and scaling after that
    assembler2=VectorAssembler(inputCols=out_col,outputCol='out_col_feature')
    transformed2 = assembler2.transform(scaledData)
    scaler2 = MinMaxScaler(inputCol="out_col_feature", outputCol="out_col_scaledfeature")
    scalerModel2=scaler2.fit(transformed2.select('out_col_feature'))
    scaledData=scalerModel2.transform(transformed2)

    # Selecting the indexed categorical column along with numric scaled vector data column
    column_list=[cat_col+'_Index' for cat_col in cat_in_cols] +["numeric_col_scaledfeatures","out_col_scaledfeature"]
    #column_list=[cat_col+'_Index' for cat_col in cat_in_cols] +["numeric_col_scaledfeatures"]+out_col
    alias_column_list=cat_in_cols +['numeric_col_features']+out_col
    dataTransformed =scaledData.select([col(column).alias(column_alias) for (column, column_alias) in zip(column_list,alias_column_list)]).persist(StorageLevel.DISK_ONLY)
    return(dataTransformed)

# In[14]:

inputProcessedDataFrame=dataPrePocessingPredictors(dataset, cat_in_cols,numeric_in_cols,cat_out_col)

# In[15]:

inputProcessedDataFrame.show(3)

# ## Input format for the merged network : We’ll pass a list of inputs and each of the list except the last one will have information about a single categorical column from all the rows of the batch and another last list will have values of the all other continuous columns.

# In[16]:

def preProcessX_InputData(X_df, embed_cols):
    input_list = []
    #the categorial cols to be embedded:
    for c in embed_cols:
       input_list.append(np.array( X_df.select(c).rdd.flatMap(lambda x :x).collect()))

    #the rest of the columns
    other_cols = [c for c in X_df.columns if (not c in embed_cols)]
    input_list.append(np.array(X_df.select(other_cols).rdd.map(lambda x :x[0]).collect()))

    return input_list

# In[17]:

def dataPreparationForModel(df,cat_in_cols,cat_out_col):
    # Counting distinct values of each categorical predictors
    cat_in_cols_count=list(df.select([F.countDistinct(col(column)) for column in cat_in_cols]).collect()[0].asDict().values())
    train_data,test_data = df.randomSplit([.7,.3],seed=23)
    X_train=train_data.drop(*cat_out_col)
    X_test=test_data.drop(*cat_out_col)
    y_train=train_data.select(cat_out_col)
    y_test=test_data.select(cat_out_col)
    X_train_Processed=preProcessX_InputData(X_train,cat_in_cols)
    X_test_Processed=preProcessX_InputData(X_test,cat_in_cols)
    y_train_Processed=np.array(y_train.select(col(cat_out_col[0])).rdd.flatMap(lambda x:x).collect())
    y_test_Processed=np.array(y_test.select(col(cat_out_col[0])).rdd.flatMap(lambda x:x).collect())
    #y_train_Processed=np.array(y_train.select(col(cat_out_col[0])).rdd.collect())
    #y_test_Processed=np.array(y_test.select(col(cat_out_col[0])).rdd.collect())
    return(X_train_Processed, y_train_Processed, X_test_Processed, y_test_Processed)

# In[18]:

X_train_Processed, y_train_Processed, X_test_Processed, y_test_Processed=dataPreparationForModel(inputProcessedDataFrame,cat_in_cols,cat_out_col)

# Build Neural Network
# Here we basically make the embeding layers one at a time and append, and at the end we concatenate them together with the numerical features.
# Embedding Dimension - Hyperparamter
# The choice of embedding dimension is optional, essentially it is a hyperparamter that one need to choose beforehand and investigate. One rule of thumb is to choose half of the cardinality of the categorical feature if that is is up to 50 in length.

# In[19]:

def createAndCompileModel(df,cat_in_cols,numeric_in_cols):
    input_models=[]
    output_embeddings=[]
    cat_in_cols_count=list(df.select([F.countDistinct(col(column)) for column in cat_in_cols]).collect()[0].asDict().values())
    for cat_var,no_of_unique_cat in zip(cat_in_cols,cat_in_cols_count) :
        cat_emb_name= cat_var.replace(" ","")+"_Embedding"
        #embedding_size = int(np.ceil((no_of_unique_cat)/2))
        embedding_size = int(np.ceil((no_of_unique_cat)/2)) if  int(np.ceil((no_of_unique_cat)/2))<50 else 50
        vocab  = no_of_unique_cat+1
        input_model  =Input(shape=(1,))
        output_model = Embedding(vocab, embedding_size, name=cat_emb_name)(input_model)
        output_model = Reshape(target_shape=(embedding_size,))(output_model)
        #Appending all the categorical inputs
        input_models.append(input_model)
        #Appending all the embeddings
        output_embeddings.append(output_model)
    input_numeric = Input(shape=(len(numeric_in_cols),))
    embedding_numeric = Dense(4,name="numeric")(input_numeric)
    input_models.append(input_numeric)
    output_embeddings.append(embedding_numeric)

    #At the end we concatenate altogther and add other Dense layers
    output = Concatenate()(output_embeddings)
    output =Dense(1024, name='stage1',kernel_initializer="normal",activation='relu')(output)
    output= Dropout(0.4)(output)
    output =Dense(512, name='stage2',kernel_initializer='normal', activation='relu')(output)
    output= Dropout(0.4)(output)
    output =Dense(256, name='stage3',kernel_initializer="normal", activation='relu')(output)
    output =Dense(1, name='stage4', kernel_initializer="normal", activation='linear')(output)
    model = Model(inputs=input_models, outputs=output)
    #model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['acc'])
    model.compile(loss='mean_squared_error', optimizer='Adam',metrics=['mae'])
    return(model)

# In[20]:

model=createAndCompileModel(inputProcessedDataFrame,cat_in_cols,numeric_in_cols)

# In[21]:

history=model.fit(X_train_Processed,y_train_Processed,validation_data=(X_test_Processed,y_test_Processed),epochs =10, batch_size =256, verbose=2)

# In[22]:

# summarize history for accuracy
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper right')
plt.show()

# In[23]:

#import matplotlib.pyplot as pplot
pplot.plot(history.history['mean_absolute_error'])
pplot.plot(history.history['val_mean_absolute_error'])
pplot.title('Mean Abosulte Error')
pplot.xlabel('epoch')
pplot.ylabel('mean absolute error')
pplot.legend(['train','val'],loc='upper right')
pplot.show()

# In[24]:

model.summary()

# In[25]:

from IPython.display import Image

# In[26]:

import pydot_ng as pydot
from IPython.display import Image

# In[27]:

plot_model(model, show_shapes=True, show_layer_names=True, to_file='model.png')

# In[28]:

Image(retina=True, filename='model.png')


from collections import deque
    #HOW TO SOLVE ANY GRAPH PROBLEM:

#Translate the problem into graph terminology
    #What are the vertices, edges, weights (if needed)?

""""
Vertices: islands
Edges: 1's that are connected
Weights: None
"""""

""""
Plan
1. translate the problem into graph terminology
vertex - is a cell
edge - neighboring cells

2. Build your graph
it's the grid so need to build it!

3. traverse your graph/grid
go through each element in the grid
if we finda 1, increment num islands, then we want to traverse all of its
connected components and mark them as visited
return numIslands

"""""

#Build your graph
    #Do you even need to build a graph? Should you use an adjacency matrix/list?

#Traverse your graph
    #Should you use BFS/DFS? Do you need an auxiliary data structure? (list containing path?)

# Start at x, y and mark connected components that are 1 visited
def markConnectedComponentsAsVisited(grid, visited, x, y):
    width, height = len(grid[0], len(grid))
    stack = deque()
    stack.append((x,y))
    while len(stack) > 0:
        x, y = stack.pop()
        if visited[y][x]:
            continue
        visited[y][x] = True
        # Traverse all adjacent nodes that are also 1
        # Check left node
        if x - 1 >= 0 and grid[y][x - 1] == '1':
            stack.append((x - 1, y))
            # Check right node
        if x + 1 < width and grid[y][x + 1] == '1':
            stack.append((x + 1, y))
            # Check top node
        if y - 1 >= 0 and grid[y][x - 1] == '1':
            stack.append((x - 1, y))
            # Check bottom node
        if y + 1 < height and grid[y][x - 1] == '1':
            stack.append((x - 1, y))

def numIslands(grid):
    if len(grid) == 0:
        return 0
    width, height = len(grid[0]), len(grid)
    #visited[i][j] is True if cell in grid[i][j] has been visited
    visited = [[False] * width for x in range(height)]

    # double for loops
    # traverse through each
    for y in range(height):
        for x in range(width):
            if grid[y][x] == '1' and not visited[y][x]:
                numIslands += 1
                markConnectedComponentsAsVisited(grid, visited, x, y)
    return numIslands

input1 = [
    ["1", "1", "1", "1", "0"],
    ["1", "1", "0", "1", "0"],
    ["1", "1", "0", "0", "0"],
    ["0", "0", "0", "0", "0"]
]

input2 = [
    ["1", "1", "0", "0", "0"],
    ["1", "1", "0", "0", "0"],
    ["0", "0", "1", "0", "0"],
    ["0", "0", "0", "1", "0"]
]

print(numIslands(input1))


import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GL import shaders
import numpy as np

# Define the window dimensions
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600

# Vertex shader source code
vertex_shader_code = """
#version 120
attribute vec2 position;
void main()
{
    gl_Position = vec4(position, 0.0, 1.0);
}
"""

# Fragment shader source code for the Mandelbrot set
fragment_shader_code = """
#version 120
uniform vec2 resolution;
uniform int max_iterations;

void main()
{
    vec2 uv = (2.0 * gl_FragCoord.xy - resolution) / min(resolution.y, resolution.x);
    vec2 z = uv;
    int i;
    for (i = 0; i < max_iterations; i++)
    {
        if (length(z) > 2.0)
            break;
        z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + uv;
    }
    float color = float(i) / float(max_iterations);
    gl_FragColor = vec4(vec3(color), 1.0);
}
"""

def create_shader_program():
    # Create and compile the vertex shader
    vertex_shader = shaders.compileShader(vertex_shader_code, GL_VERTEX_SHADER)

    # Create and compile the fragment shader
    fragment_shader = shaders.compileShader(fragment_shader_code, GL_FRAGMENT_SHADER)

    # Link the shaders into a shader program
    shader_program = shaders.compileProgram(vertex_shader, fragment_shader)

    return shader_program

def main():
    pygame.init()
    pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), DOUBLEBUF | OPENGL)

    shader_program = create_shader_program()
    glUseProgram(shader_program)

    resolution_uniform = glGetUniformLocation(shader_program, "resolution")
    max_iterations_uniform = glGetUniformLocation(shader_program, "max_iterations")

    glUniform2f(resolution_uniform, WINDOW_WIDTH, WINDOW_HEIGHT)
    glUniform1i(
        max_iterations_uniform, 1000
    )  # Adjust the maximum number of iterations as needed

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        glClear(GL_COLOR_BUFFER_BIT)
        glBegin(GL_QUADS)
        glVertex2f(-1, -1)
        glVertex2f(1, -1)
        glVertex2f(1, 1)
        glVertex2f(-1, 1)
        glEnd()
        pygame.display.flip()

if __name__ == "__main__":
    main()


#20190512 by zikun
#定义tracker类
import math

import cv2
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn

from torch.autograd import Variable
from matplotlib.patches import Rectangle

from backbone_v2 import VGG, build_vgg16
from feature_utils_v2 import feature_selection  # for visualization
from feature_utils_v2 import (features_selection, generate_patch_feature,
                              get_subwindow, get_subwindow_feature,
                              resize_tensor, round_python2,
                              get_frame_features)
from image_loader import default_image_loader
from siamese import SiameseNet
from taf import taf_model, taf_model_diff
from tracking_utils import (cal_window_size, calculate_scale, fuse_feature,
                            generate_2d_window)

# matplotlib.use('Qt5Agg') # fix for MacOS bug - "AttributeError: 'FigureManagerMac' object has no attribute 'window'"

class Tadt_Tracker(object):
    def __init__(self, config, model = None, display = True, device = 'cpu', name = 'TADT', model_from_mat = True):
        """
        args:
            target_location - [x1,y1,w,h], 1-index for OTB benchmark
            TODO: model_from_mat
        """
        super(Tadt_Tracker, self).__init__()
        #---------------trackers parameters initialization--------------------------
        self.name = name
        self.config = config
        self.display = display
        self.device = device
        self.rescale = 1
        self.results = []
        self.model_from_mat = model_from_mat

        #-------------model initialization--------------------
        if model is None:
            self.model = build_vgg16(self.config).to(self.device)
        else:
            self.model = model.to(self.device)
        self.model.train()
        self.siamese_model = SiameseNet().to(self.device)
        self.toc = 0

    def initialize_tadt(self, img_path, target_loc, visualize = False):

        #------------sequence parameters initialization----------------------------
        img = default_image_loader(img_path)#<class 'numpy.ndarray'> [height, width, channel]
        self.target_location = target_loc[0]
        origin_target_size = math.sqrt(self.target_location[2] * self.target_location[3]) #area of bounding box
        origin_image_size = img.shape[0: 2][::-1] # [width,height]
        if origin_target_size > self.config.MODEL.MAX_SIZE:
            self.rescale = self.config.MODEL.MAX_SIZE / origin_target_size
        elif origin_target_size < self.config.MODEL.MIN_SIZE:
            self.rescale = self.config.MODEL.MIN_SIZE / origin_target_size

        #----------------scale image cv2 numpy.adarray---------------
        image = cv2.resize(
                img,
                tuple((np.ceil(np.array(origin_image_size) * self.rescale)).astype(int)),
                interpolation=cv2.INTER_LINEAR
        )

        #----------------shift image---------------
        shift = np.float32([
	        [1, 0, 1],
	        [0, 1, 1]
        ])
        shifted_img = cv2.warpAffine(image, shift, (image.shape[1], image.shape[0]))

        #------scaled target location, get position and size [x1,y1,width,height]------
        self.target_location = round_python2(np.array(self.target_location) * self.rescale)-np.array([1,1,0,0])#0-index
        target_size = self.target_location[2: 4]# [width, height]
        image_size = image.shape[0:2]# [height, width]
        search_size, ratio = cal_window_size(self.config.MODEL.MAX_SIZE, image_size, 2, self.config.MODEL.TOTAL_STRIDE)
        self.input_size = np.array([search_size, search_size])

        #------------First frame processing--------------------
        self.srch_window_location = cal_srch_window_location(self.target_location, search_size)
        self.srch_window_location2 = cal_srch_window_location(round_python2(np.array(target_loc[1]) * self.rescale)-np.array([1,1,0,0]), search_size)

        features = get_subwindow_feature(self.model, image, self.srch_window_location2, self.input_size) #two tensors, one from each Conv layer
        features2 = get_subwindow_feature(self.model, image, self.srch_window_location, self.input_size) #two tensors, one from each Conv layer
        features3 = get_subwindow_feature(self.model, shifted_img, self.srch_window_location, self.input_size) #two tensors, one from each Conv layer

        #----------- crop the target exemplar from the feature map------------------
        patch_features, patch_locations = generate_patch_feature(target_size[::-1], self.srch_window_location, features)
        self.feature_pad = 2
        self.b_feature_pad = int(self.feature_pad / 2)
        self.filter_sizes = [torch.tensor(feature.shape).numpy() for feature in patch_features]

        #-------------compute the indices of target-aware features----------------
        #self.feature_weights, self.balance_weights = taf_model([features, features2], self.filter_sizes, self.device)
        self.feature_weights, self.balance_weights = taf_model_diff([features, features2], 1, self.device)

        #-------------select the target-awares features---------------------------
        self.exemplar_features = features_selection(patch_features, self.feature_weights, self.balance_weights, mode = 'reduction')
        #self.exemplar_features = fuse_feature(patch_features)

        #-------------calculate global average pooling of exemplar features-------------------
        kernel_size = self.exemplar_features[0].shape[1:3]
        self.exemplar_features_gap = nn.AvgPool2d(kernel_size)(self.exemplar_features[0])

        #------------visualization------------------------------------------------
        if self.display:
            self.prepare_visualize()
            self.visualization(img, target_loc[0], 0)
        self.results.append(target_loc[0])

        #------------------------to visualize what is inside subwindow-----------
        display_subwindow = False
        if display_subwindow:
            subwindow = get_subwindow(self.srch_window_location, image, self.input_size, visualize = display_subwindow)

        #------------------------to visualize heatmap on full frame or subwindow-----------
        vis_heatmap_full_frame = False
        vis_heatmap_subwindow = False

        if vis_heatmap_full_frame or vis_heatmap_subwindow:

            if vis_heatmap_full_frame:
                subwindow, track_features = get_frame_features(self.model, img)
            elif vis_heatmap_subwindow:
                subwindow = get_subwindow(self.srch_window_location, image, self.input_size, visualize = False)
                track_features = features

            srch_window_size = (subwindow.shape[2],subwindow.shape[1])

            self.visualize_feature(
                            features = track_features,
                            stage = 'conv4_1',
                            srch_window_size = srch_window_size,
                            subwindow = subwindow,
                            feature_weights = self.feature_weights,
                            balance_weights = self.balance_weights
                            )

        #------------------------to visualize convolution between feature maps and exemplar-----------
        vis_conv_feature_map = True

        if vis_conv_feature_map:
            subwindow, track_features = get_frame_features(self.model, img)
            self.visualize_conv(
                            features = track_features,
                            stage = 'conv4_3',
                            maps_num = 0,
                            exemplar_features = patch_features,
                            feature_weights = self.feature_weights,
                            balance_weights = self.balance_weights
                            )

    def tracking(self, img_path, frame, visualize = False):
        #-------------read image and rescale the image-----------------------------
        img = default_image_loader(img_path)#<class 'numpy.ndarray'>[height, width, channel]
        image = cv2.resize(
                img,
                tuple((np.ceil(np.array(img.shape[0:2][::-1]) * self.rescale)).astype(int)),
                interpolation=cv2.INTER_LINEAR
        )
        tic = cv2.getTickCount()
        #-------------get multi-scale feature--------------------------------------
        features = get_subwindow_feature(self.model, image, self.srch_window_location, self.input_size)
        feature_size = (torch.tensor(features[0].shape)).numpy().astype(int)[-2:]

        #selected_features = fuse_feature(features)

        #-------------select the target-aware features new frame (not exemplar)---------------------------
        selected_features = features_selection(features, self.feature_weights, self.balance_weights, mode = 'reduction')
        selected_features_1 = resize_tensor(selected_features, tuple(feature_size + self.feature_pad))
        selected_features_3 = resize_tensor(selected_features, tuple(feature_size - self.feature_pad))
        selected_features_1 = selected_features_1[:,:,self.b_feature_pad:feature_size[0]+self.b_feature_pad, self.b_feature_pad:feature_size[1]+self.b_feature_pad]

        selected_features_3 = torch.nn.functional.pad(selected_features_3, (self.b_feature_pad, self.b_feature_pad, self.b_feature_pad, self.b_feature_pad))
        scaled_features = torch.cat((selected_features_1,selected_features,selected_features_3), dim = 0)

        #-------------get response map (final target aware bluish feature map result of correlation)-------------------------
        response_map = self.siamese_model(scaled_features, self.exemplar_features).to('cpu')

        scaled_response_map = torch.squeeze(resize_tensor(
                                                response_map,
                                                tuple(self.srch_window_location[-2:].astype(int)),
                                                mode = 'bicubic',
                                                align_corners = True))

        hann_window = generate_2d_window('hann', tuple(self.srch_window_location[-2:].astype(int)), scaled_response_map.shape[0])
        scaled_response_maps = scaled_response_map + hann_window

        #-------------calculate ROI----------------------------------------------
        scale_ind = calculate_scale(scaled_response_maps, self.config.MODEL.SCALE_WEIGHTS)

        #response_map_reshaped = response_map[scale_ind,0,:,:].numpy()
        #center_h, center_w = np.where(response_map_reshaped == np.max(response_map_reshaped)) #find center ROI
        #center_h, center_w = center_h[0], center_w[0]

        #region_size = self.exemplar_features[0].shape[1:3]
        #width_size = int(region_size[0]/2)
        #width_remainder = region_size[0] % 2
        #height_size = int(region_size[1]/2)
        #height_remainder = region_size[1] % 2

        #plt.imshow(response_map_reshaped)
        #plt.plot(center_w, center_h, "xr", markersize=5)
        #plt.plot(center_w - width_size, center_h - height_size, "or", markersize=5)
        #plt.plot(center_w + width_size + width_remainder, center_h + height_size + height_remainder, "or", markersize=5)
        #plt.show()

        #roi_features = scaled_features[scale_ind, : , center_w - width_size : center_w + width_size + width_remainder, center_h - height_size : center_h + height_size + height_remainder]
        #roi_size = roi_features.shape[1:3]

        #-------------calculate Global Average Pooling current frame features--------------------
        #roi_features_gap = nn.AvgPool2d(roi_size)(roi_features)

        #-------------calculate Affinity Matrix--------------------
        #self.affinity_matrix = torch.sum(self.exemplar_features_gap * roi_features_gap) / len(roi_features_gap)

        #-------------find max-response----------------------------------------------
        response_map = scaled_response_maps[scale_ind,:,:].numpy()
        max_h, max_w = np.where(response_map == np.max(response_map))
        if len(max_h)>1:
            max_h = np.array([max_h[0],])
        if len(max_w)>1:
            max_w = np.array([max_w[0],])

        #-------------update tracking state and save tracking result----------------------------------------
        target_loc_center = np.append(self.target_location[0:2]+(self.target_location[2:4])/2, self.target_location[2:4])
        target_loc_center[0:2] = target_loc_center[0:2] + (np.append(max_w,max_h)-(self.srch_window_location[2:4]/2-1))*self.config.MODEL.SCALES[scale_ind]
        target_loc_center[2:4] = target_loc_center[2:4] * self.config.MODEL.SCALES[scale_ind]
        #print('target_loc_center in current frame:',target_loc_center)
        self.target_location = np.append(target_loc_center[0:2]-(target_loc_center[2:4])/2, target_loc_center[2:4])
        #print('target_location in current frame:', self.target_location)

        self.srch_window_location[2:4] = (round_python2(self.srch_window_location[2:4] * self.config.MODEL.SCALES[scale_ind]))
        self.srch_window_location[0:2] = target_loc_center[0:2]-(self.srch_window_location[2:4])/2

        #print('srch_window_location: ', self.srch_window_location)

        tracking_bbox = (self.target_location + np.array([1,1,0,0]))/self.rescale - np.array([1,1,0,0])#tracking_bbox: 0-index
        self.results.append(tracking_bbox)

        #-------------calculate global average pooling of new frame features-------------------
        kernel_size = selected_features[0].shape[1:2]
        self.selected_features_gap = nn.AvgPool2d(kernel_size)(selected_features[0])

        self.toc += cv2.getTickCount() - tic
        if self.display:
            self.visualization(img,tracking_bbox.astype(int),frame)

    def cal_fps(self, num_frame):
        toc = self.toc/cv2.getTickFrequency()
        return num_frame/toc

    def saving_result(self, result_path, video_name, zero_index = True):

        if zero_index:
            results = self.results
        else:
            results = [result + np.array([1,1,0,0]) for result in self.results]
        with open(result_path + video_name +'.txt','w') as f:
            for bbox in results:
                newline = str(bbox[0])+','+str(bbox[1])+','+str(bbox[2])+','+str(bbox[3]) + '\n'
                f.write(newline)

    def prepare_visualize(self):
        fig,self.ax = plt.subplots(1,1)
        self.ax.axis('off')

    def visualization(self, img, location, frame):
        self.ax.clear()
        plt.imshow(img)
        position = (location[0], location[1])
        width = location[2]
        height = location[3]

        self.ax.add_patch(Rectangle(position, width, height,fill=False,color='r'))
        self.ax.text(0, 0, 'frame: {}'.format(frame), fontsize=15)
        plt.ion()
        mngr = plt.get_current_fig_manager()
        mngr.window.setGeometry = (100,100,800,500)
        plt.pause(0.01)

    def end_visualize(self):
        if self.display:
            plt.clf()
            plt.close()
        else:
            pass

    def visualize_feature(self, features = None, stage = 'conv4_1', srch_window_size = None, subwindow = None, feature_weights = None, balance_weights = None):
        """
        function: visualize the selected feature of the first frame
        """
        assert(stage == 'conv4_1' or stage == 'conv4_3' or stage == 'all'), 'For now, TADT only support for conv4_1 and conv4_3'
        if stage == 'conv4_1':
            stage = 0
        elif stage == 'conv4_3':
            stage = 1
        if feature_weights is None or balance_weights is None:
            if stage == 'all':
                feature = torch.cat(features, dim = 1)
            else:
                feature = features[stage]
        else:
            if stage == 'all':
                feature = features_selection(features, feature_weights, balance_weights, mode = 'reduction')
            else:
                feature = features[stage]
                feature_weight = feature_weights[stage]
                feature = feature_selection(feature, feature_weight, mode = 'reduction')

        heatmap = torch.sum(feature, dim = 1)
        max_value = torch.max(heatmap)
        min_value = torch.min(heatmap)
        heatmap = (heatmap-min_value)/(max_value-min_value)*255
        heatmap = heatmap.cpu().numpy().astype(np.uint8).transpose(1,2,0)

        heatmap = cv2.resize(heatmap, srch_window_size, interpolation=cv2.INTER_LINEAR)
        heatmap=cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
        #subwindow = subwindow.numpy().astype(np.uint8).transpose(1,2,0)
        subwindow = subwindow.cpu().numpy().astype(np.uint8).transpose(1,2,0) + 128 #convert torch tensor back to open cv image
        cv2.imshow('heatmap',cv2.addWeighted(cv2.cvtColor(subwindow,cv2.COLOR_BGR2RGB), 0.6, heatmap, 0.4, 0.0))
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def visualize_conv(self, features = None, stage = 'conv4_1', maps_num = 0, exemplar_features = None, feature_weights = None, balance_weights = None):
        """
        function: visualize the selected feature of the first frame
        """
        assert(stage == 'conv4_1' or stage == 'conv4_3' or stage == 'all'), 'For now, TADT only support for conv4_1 and conv4_3'
        if stage == 'conv4_1':
            stage = 0
        elif stage == 'conv4_3':
            stage = 1
        if feature_weights is None or balance_weights is None:
            if stage == 'all':
                feature = torch.cat(features, dim = 1)
                exemplar = torch.cat(exemplar_features, dim = 1)
            else:
                feature = features[stage]
                exemplar = exemplar_features[stage]
        else:
            if stage == 'all':
                feature = features_selection(features, feature_weights, balance_weights, mode = 'reduction')
                exemplar = features_selection(exemplar_features, feature_weights, balance_weights, mode = 'reduction')
            else:
                feature = features[stage]
                feature_weight = feature_weights[stage]
                feature = feature_selection(feature, feature_weight, mode = 'reduction')

                exemplar = exemplar_features[stage]
                exemplar = feature_selection(exemplar, feature_weight, mode = 'reduction')

        convolution = nn.functional.conv2d(Variable(feature.view(feature.shape)),Variable(exemplar.view(exemplar.shape)))
        convolution = torch.sum(convolution, dim = 1)
        convolution = convolution.cpu().numpy().astype(np.uint8).transpose(1,2,0) #convert torch tensor back to open cv image

        feature_map = torch.sum(feature, dim = 1)
        feature_map = feature_map.cpu().numpy().astype(np.uint8).transpose(1,2,0)

        exemplar_map = torch.sum(exemplar, dim = 1)
        exemplar_map = exemplar_map.cpu().numpy().astype(np.uint8).transpose(1,2,0)

        #match_template = cv2.matchTemplate(
        #                        feature[:,1,:,:].cpu().numpy().astype(np.uint8).transpose(1,2,0),
        #                        exemplar[:,1,:,:].cpu().numpy().astype(np.uint8).transpose(1,2,0),
        #                        3) #3 = cv.TM_CCORR_NORMED

        #cv2.imshow('exemplar', exemplar_map)
        #cv2.imshow('feature_map', feature_map)
        #cv2.imshow('convolution', match_template)
        #cv2.imwrite('./feature_map_conv4_3_layer_359.jpg', feature_map)

        #for i in range(exemplar.shape[1]):
        #    match_template = cv2.matchTemplate(
        #                feature[:,i,:,:].cpu().numpy().astype(np.uint8).transpose(1,2,0),
        #                exemplar[:,i,:,:].cpu().numpy().astype(np.uint8).transpose(1,2,0),
        #                3) #3 = cv.TM_CCORR_NORMED
        #    cv2.imwrite('./match_templates/match_template_' + str(i) + '.jpg', match_template)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

def cal_feature_pad(features):
    feature_size = (torch.tensor(features[0].shape)).numpy().astype(int)[-2:]
    if feature_size[0] * 0.05 % 2 > 1:
        feature_pad = (np.floor(feature_size[0] * 0.05) + 1).astype(int)
    else:
        feature_pad = (np.ceil(feature_size[0] * 0.05) - 1).astype(int)
    assert feature_pad % 2 == 0, 'feature_pad need to be an even number\n'

    return feature_pad

def cal_srch_window_location(target_location, search_size):
    """
    function: cal_srch_window_location
    """
    srch_window_position = np.floor(target_location[0:2]+target_location[2:4]/2 - search_size/2)
    srch_window_size = np.array([search_size, search_size])
    srch_window_location = np.append(srch_window_position, srch_window_size)
    assert(srch_window_location.shape == (4,)), "the shape of srch_window_location is {}".format(srch_window_location.shape)
    return srch_window_location


import numpy as np
import matplotlib.pyplot as plt
import math
import cv2
import warnings
warnings.filterwarnings("ignore")

KERNEL = {
    'laplacian': np.array([[0, 1, 0],
                           [1, -4, 1],
                           [0, 1, 0]]),

    'rotate': {
                'horizontal': np.array([[-1, -1, -1],
                                        [2, 2, 2],
                                        [-1, -1, -1]]),
                '45degree' : np.array([[2, -1, -1],
                                       [-1, 2, -1],
                                       [-1, -1, 2]]),
                'vertical' : np.array([[-1, 2, -1],
                                       [-1, 2, -1],
                                       [-1, 2, -1]]),
                '315degree' : np.array([[-1, -1, 2],
                                        [-1, 2, -1],
                                        [2, -1, -1]])
    },

    #Kernel filter using in edge detection

    'grad' : {
        'left' : np.array([[-1, 0],
                           [1, 0]]),
        'right' : np.array([[-1, 1],
                            [0, 0]])
    },

    'roberts' : {
        'left': np.array([[-1, 0],
                          [0, 1]]),
        'right': np.array([[0, -1],
                           [1, 0]])
    },

    'prewitt' : {
        'left' : np.array([[-1, -1, -1],
                           [0, 0, 0],
                           [1, 1, 1]]),
        'right' : np.array([[-1, 0, 1],
                            [-1, 0, 1],
                            [-1, 0, 1]])
    },

    'sobel' : {
        'left' : np.array([[-1, -2, -1],
                           [0, 0, 0],
                           [1, 2, 1]]),
        'right' : np.array([[-1, 0, 1],
                            [-2, 0, 2],
                            [-1, 0, 1]])
    },

    'kirish': {
        'N' : np.array([[-3, -3, 5],
                        [-3, 0, 5],
                        [-3, -3, 5]]),
        'NW' : np.array([[-3, 5, 5],
                         [-3, 0, 5],
                         [-3, -3, -3]]),
        'W' : np.array([[5, 5, 5],
                        [-3, 0, -3],
                        [-3, -3, -3]]),
        'SW' : np.array([[5, 5, -3],
                         [5, 0, -3],
                         [-3, -3, -3]]),
        'S' : np.array([[5, -3, -3],
                        [5, 0, -3],
                        [5, -3, -3]]),
        'SE' : np.array([[-3, -3, -3],
                         [5, 0, -3],
                         [5, 5, -3]]),
        'E' : np.array([[-3, -3, -3],
                        [-3, 0, -3],
                        [5, 5, 5]]),
        'NE' : np.array([[-3, -3, -3],
                         [-3, 0, 5],
                         [-3, 5, 5]])
    }
}

def display_RGB(image: np.array) -> None:
    """ This function is for displaying rgb image
        Complex np.array will also be converted into real np.array
    Args:
        image (np.array)
    """
    if True in np.iscomplex(image):
        image = np.log(np.abs(image)+1)
        image = image*255//np.max(image)
        image = image.astype('uint8')
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    plt.imshow(image)

def display_GRAY(image: np.array) -> None:
    """ This function is for displaying gray image
        If image shape = 3: will be converted to gray before plot
        Else: plot directly
        Complex np.array will also be converted into real np.array

    Args:
        image (np.array)
    """
    if True in np.iscomplex(image):
        image = np.log(np.abs(image)+1)
        image = image*255//np.max(image)
        image = image.astype('uint8')
    if len(image.shape) == 3:
        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    plt.imshow(image, cmap='gray')

def multiplot(plot_name: str, images: dict) -> None:
    """
    This function is to plot multiple images in one plot
    Args:
        plot_name (str): name of plot
        images: dict of images to plot in format {"image_title": images}

    Cases:
        shape of 2: plot gray image automatically even when "rgb" in title
        shape of 3: plot rgb image automatically if no "gray" in title
        'gray' in image title: plot gray image
        'rgb' in image title: plot rgb image
    """

    #! align images into grid
    no_images = len(images)
    if no_images > 3:
        nrow = math.ceil(no_images/3)
        ncol = 3
    else:
        nrow = 1
        ncol = no_images
    figsize = (3*ncol, 3*nrow+1)
    plt.figure(figsize=figsize)
    plt.suptitle(f'{plot_name}')

    #! processing and plotting image in right color channel
    for title, image in images.items():
        plt.subplot(nrow, ncol, list(images.keys()).index(title) + 1)
        plt.title(f"{title}")
        plt.axis('off')

        if len(image.shape) == 2:
            display_GRAY(image)
        elif len(image.shape) == 3:
            if 'rgb' in title.lower():
                display_RGB(image)
            elif 'gray' in title.lower():
                display_GRAY(image)
            else:
                display_RGB(image)
    plt.tight_layout()
    plt.show();

class convolution2d():
    """
        This class contains convolution method in 2D-array
        Args:
            img (np.array) : 2D-array
            kernel (str or np.array): a kernel filter using in convolution (etc: Laplacian, Roberts,.. or yours)

        Method:
            convolution
    """

    def __init__(self, img):
        self.img = img
        self.h, self.w = img.shape

    """
        This function use of convolve 2D-array with a kernel filter
    """

    def convolution(self, kernel, padding=0, strides=1):
        # Cross Correlation
        kernel = np.flipud(np.fliplr(kernel))

        # Gather Shapes of Kernel + Image + Padding
        xKernShape = kernel.shape[0]
        yKernShape = kernel.shape[1]
        xImgShape = self.img.shape[0]
        yImgShape = self.img.shape[1]

        # Shape of Output Convolution
        xOutput = int(((xImgShape - xKernShape + 2 * padding) / strides) + 1)
        yOutput = int(((yImgShape - yKernShape + 2 * padding) / strides) + 1)
        output = np.zeros((xOutput, yOutput))

        # Apply Equal Padding to All Sides
        if padding != 0:
            imagePadded = np.zeros((self.img.shape[0] + padding * 2, self.img.shape[1] + padding * 2))
            imagePadded[int(padding):int(-1 * padding), int(padding):int(-1 * padding)] = self.img
        else:
            imagePadded = self.img

        # Iterate through image
        for y in range(self.img.shape[1]):
            # Exit Convolution
            if y > self.img.shape[1] - yKernShape:
                break
            # Only Convolve if y has gone down by the specified Strides
            if y % strides == 0:
                for x in range(self.img.shape[0]):
                    # Go to next row once kernel is out of bounds
                    if x > self.img.shape[0] - xKernShape:
                        break
                    try:
                        # Only Convolve if x has moved by the specified Strides
                        if x % strides == 0:
                            output[x, y] = (kernel * imagePadded[x: x + xKernShape, y: y + yKernShape]).sum()
                    except:
                        break

        return output

class smooth_threshold():
    def __init__(self, img):
        self.img = img

    def smooth(self, kernel_size):
        h, w = self.img.shape

        h = h - kernel_size + 1
        w = w - kernel_size + 1

        out_img = np.zeros((h, w))

        for i in range(h):
            for j in range(w):
                out_img[i][j] = np.mean(self.img[i:i+kernel_size,j:j+kernel_size])

        return out_img

    """
        This function return a binary image get by threshold the input image
    """
    def threshold(self, arr, thresh=0.33):
        h, w = arr.shape

        out_img = np.zeros_like(arr)

        T = thresh * np.max(arr)

        for i in range(h):
            for j in range(w):
                out_img[i][j] = 0 if arr[i][j] < T else 255

        return out_img


#!/usr/bin/python

import sys

a = open(sys.argv[1]).readlines()
b = open(sys.argv[2]).readlines()
assert(len(a) == len(b))

for j in range(len(a)) :
  t1 = a[j].split()
  t2 = b[j].split()
  s1 = {}
  assert(len(t1) == len(t2))

  if len(t1) == 3:
    assert(t1[2] == t2[2])
  else :
    for z in range(2, len(t1)) :
      y1 = t1[z].split(',')
      y2 = t2[z].split(',')
      assert(y1[0] == y2[0] and y1[1] == y2[1])


from tensorflow import keras
import tensorflow as tf
import numpy as np
import os

(X_train_full, y_train_full), (X_test, y_test) = keras.datasets.cifar10.load_data()

X_train = X_train_full[5000:]
y_train = y_train_full[5000:]
X_valid = X_train_full[:5000]
y_valid = y_train_full[:5000]

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

model = keras.models.Sequential()
model.add(keras.layers.Flatten(input_shape=[32, 32, 3]))
model.add(keras.layers.BatchNormalization())
for _ in range(20):
    model.add(keras.layers.Dense(100, kernel_initializer="he_normal"))
    model.add(keras.layers.BatchNormalization())
    model.add(keras.layers.Activation("elu"))
model.add(keras.layers.Dense(10, activation="softmax"))

optimizer = keras.optimizers.Nadam(learning_rate=5e-4)
model.compile(loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])

early_stopping_cb = keras.callbacks.EarlyStopping(patience=20)
model_checkpoint_cb = keras.callbacks.ModelCheckpoint("my_cifar10_bn_model.h5", save_best_only=True)
run_index = 1  # increment every time you run the model
run_logdir = os.path.join(os.curdir, "my_cifar10_logs", "run_bn_{:03d}".format(run_index))
tensorboard_cb = keras.callbacks.TensorBoard(run_logdir)
callbacks = [early_stopping_cb, model_checkpoint_cb, tensorboard_cb]

model.fit(X_train, y_train, epochs=100,
          validation_data=(X_valid, y_valid),
          callbacks=callbacks)

model = keras.models.load_model("my_cifar10_model.h5")
model.evaluate(X_valid, y_valid)

# BN layers stabilized training and allowed us to use a much larger learning rate, so convergence was faster.


"""
Veinte problemas básicos en Python

Autor: Ednan Josué Merino Calderón

Versión: 1.0
"""
import numpy
import math

def paralelogramo():
    """
    Función Que Calcula el área y perímetro de un paralelogramo
    ---------
    Parámetros:
    Base= Número float ingresado por el usuario
    Altura= Número float ingresado por el usuario
    ---------
    Retorna:
    Area y Perímetro de un paralelogramo
    ---------
    """
    #Se inicia un bucle
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa un tipo de dato float y se almacena en la variable base
            base = float(input("Ingrese la base:"))
            #break
            break
        #Si el usuario no ingresó un tipo de dato float
        except ValueError:
            #Se le notifica al usuario que ingresó un dato incorrecto
            print("Ingrese un dato válido!")
    #Se inicia un bucle
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa un tipo de dato float y se almacena en la variable altura
            altura = float(input("Ingrese la altura:"))
            #break
            break
        #Si el usuario no ingresó un tipo de dato float
        except ValueError:
            #Se le notifica al usuario que ingresó un dato incorrecto
            print("Ingrese un dato válido!")
    #Si es que el cilindro existe la base y la altura son mayores que 0
    if base>0 and altura >0:
        #Cálculo del perímetro
        perimetro=altura+altura+base+base
        #Se imprime la solución del perímetro
        print("El perímetro del paralelogramo es:",perimetro,"u")
        #Cálculo del área
        area=base*altura
        #Se imprime la solución del área
        print("El área del paralelogramo es:",area, "u^2")
    #Si el cilindro no existe
    else:
        #Se le notifica al usuario
        print("El paralelogramo no existe!")
        #Se le notifica al usuario que lo intente de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza de nuevo
        paralelogramo()

def cilindro():
    """
    Función para calcular el área de un cilindro
    -------------------------------------------
    Parámetros:
    radio: valor float ingresado por el usuario
    altura_cilindro: valor float ingresado por el usuario
    -------------------------------------------
    Retorna:
    area_cilindro: valor en unidades cuadradas, resultado del área del cilindro
    """
    #Se inicia un bucle
    while True:
        #Se emplea la sentencia try/except para validar datos
        try:
            #El usuario ingresa el radio de tipo float en la variable radio
            radio = float(input("Ingrese el radio:"))
            #break
            break
        #Si es que el usuario ingresa cualquier otro dato que no sea de tipo float
        except ValueError:
            #Se le notifica al usuario que ingreso un dato erroneo
            print("Ingrese un dato válido!")
    #Se inicia otro bucle para la variable altura_cilindro
    while True:
        #Se emplea la sentencia try/except para validar datos
        try:
            #El usuario ingresa la altura de tipo float en la variable altura_cilindro
            altura_cilindro = float(input("Ingrese la altura:"))
            #break
            break
        #Si es que el usuario ingresa cualquier otro dato que no sea de tipo float
        except ValueError:
            #Se le notifica al usuario que ingreso un dato erroneo
            print("Ingrese un dato válido!")
    #Si es que el radio y altura ingresados por el usuario son mayores que 0
    if radio>0 and altura_cilindro > 0:
        #Se realiza la operación para encontrar el area del cilindro
        area_cilindro = 2*math.pi*radio*(radio+altura_cilindro)
        #Se imprime la solución
        print("El área del cilindro es:",area_cilindro,"u^2")
    #Si es que el radio y la altura del cilindro son menores que 0
    else:
        #Se le notifica al usuario que el cilindro no existe
        print("El cilindro no existe")
        #Se le notifica al usuario que lo intente de nuevo
        print("Inténtelo de nuevo!")
        #Se vuelve a empezar
        cilindro()

def area_circulo():
    """
    Función para encontrar el área de un círculo
    --------------------------------------------
    Parámetros:
    radio: Dato float ingresado por el usuario
    --------------------------------------------
    Retorna:
    area_del_círculo: Valor en unidades cuadradas correspondiente al área de un círculo
    """
    #Se inicia un bucle
    while True:
        #Se emplea la sentencia try/except para validad datos
        try:
            #El usuario ingresa un dato tipo float que se almacena en la variable radio
            radio = float(input("Ingrese un radio para calcular el área:"))
            #break
            break
        #Si es que el usuario ingresa cualquier otro tipo de dato que no sea float
        except ValueError:
            #Se le notifica al usuario que ingreso un dato incorrecto
            print("Ingrese un número válido")
    #Si el radio es mayor que 0 el área del círculo existe
    if radio>0:
        #Cálculo del área del círculo
        area_del_circulo = math.pi*(radio**2)
        #Se imprime la solución
        print("El área del círculo es:",area_del_circulo,"u^2")
    #Si el radio es menor que 0, el área no existe
    else:
        #Se le notifica al usuario que el círculo no existe
        print("El círculo no existe")
        #Se le notifica al usuario que lo intente de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza desde el comienzo
        area_circulo()

def potencia():
    """
    Función que sirve para calcular la potencia de cualquier número
    ---------------------------------------------------------------
    Parámetros:
    x: Base de una potencia
    y: Exponente de una potencia
    ---------------------------------------------------------------
    Retorna:
    potencia_numero: Potencia x^y
    """
    #Se inicia un bucle para la variable x
    while True:
        #Sentencia try/except para validad datos
        try:
            #El usuario ingresa un valor float que se almacena en la variable x
            x=float(input("Ingrese una base x:"))
            #break
            break
        #Si es que el usuario no ingresó un número float
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Se inicia un bucle para la variable y
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa un valor float que se almacena en la variable y
            y=float(input("Ingrese un exponente y:"))
            #break
            break
        #Si el usuario no ingresó un valor float
        except ValueError:
            #Se le notifica al usuario que ingresó un dato inválido
            print("Ingrese un dato válido!")
    #Se calcula la potencia del número
    potencia_numero = x**y
    #Se imprime la solución
    print("La potencia x^y es igual a:",potencia_numero)

def saludo():
    """
    Función que sirve para pedirle el nombre al usuario y saludarlo
    ---------------------------------------------------------------
    Parámetros:
    nombre: dato str ingresado por el usuario
    ---------------------------------------------------------------
    Retorna:
    Saludo
    """
    #Bucle para la variable nombre
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa su nombre y se almacena en la variable nombre
            nombre=str(input("Ingrese su nombre:"))
            #break
            break
        #Si el usuario no ingresó un tipo de dato str
        except ValueError:
            #Se le comunica al usuario que no ingresó un dato válido
            print("Ingrese un dato válido!")
    #Se imprime la solución
    print("Hola",nombre,"Bienvenido :)")

def volumen_esfera():
    """
    Función que permite calcular el volúmen de una esfera por medio de un radio ingresado por el usuario
    ----------------------------------------------------------------------------------------------------
    Parámetros:
    radio_esfera: Datos float ingresado por el usuario
    ----------------------------------------------------------------------------------------------------
    Retorna:
    volumen: Volumen de la esfera
    """
    #Bucle True para la variable radio_esfera
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa un dato float y se almacena en la variable radio_esfera
            radio_esfera=float(input("Ingrese el radio para calcular el volúmen de una esfera:"))
            #break
            break
        #Si el usuario no ingresó un tipo de dato float
        except ValueError:
            #Se le notifica al usuario que se equivocó de dato
            print("Ingrese un dato válido!")
    #Si la esfera existe el radio es mayor a 0
    if radio_esfera>0:
        #Se calcula el volúmen
        volumen=4/3*math.pi*(radio_esfera**3)
        #Se imprime la solución
        print("El volumen de la esfera es:",volumen,"u^3")
    #Si el radio es menor a 0 la esfera no existe
    else:
        #Se le notifica al usuario que la esfera no existe
        print("La esfera no existe")
        #Se le notifica al usuario que lo intente de nuevo
        print("Inténtelo de nuevo!")
        #Se repite el proceso
        volumen_esfera()

def area_perimetro_rectangulo():
    """
    Se Calcula el área y perímetro de un rectángulo
    -----------------------------------------------
    Parámetros:
    altura: Altura del rectángulo ingresado por el usuario
    base: Base del rectángulo ingresado por el usuario
    -----------------------------------------------
    Retorna:
    area_del_rectangulo: Calculando BasexAltura
    perimetro_del_rectangulo: Calculando 2Base+2Altura
    """
    #Bucle para la variable altura
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa un dato float y se almacena en la variable altura
            altura=float(input("Ingrese la altura:"))
            #break
            break
        #Si el usuario no ingresó un valor float
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Bucle para la variable base
    while True:
        #Sentecia try/except para validar datos
        try:
            #El usuario ingresa un dato float y se almacena en la variable base
            base=float(input("Ingrese la base:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Si el rectángulo existe la base y la altura deben ser mayores a 0
    if base >0 and altura >0:
        #Cálculo area del rectángulo
        area_del_rectangulo=base*altura
        #Se imprime la solución del área del rectángulo
        print("El área del rectángulo es:",area_del_rectangulo,"u^2")
        #Cálculo perímetro del rectángulo
        perimetro_del_rectangulo=base+base+altura+altura
        #Se imprime la solución del perímetro del rectángulo
        print("El perímetro del rectángulo es:",perimetro_del_rectangulo,"u")
    #Si el rectángulo no existe
    else:
        #Se le notifica al usuario que el rectángulo no existe
        print("El rectángulo no existe")
        #Se le anima al usuario a intentarlo de nuevo
        print("Inténtelo de nuevo!")
        #Se vuelve a empezar
        area_perimetro_rectangulo()

def pitagoras_hipotenusa():
    """
    Función que calcula la hipotenusa en un triángulo rectángulo
    ------------------------------------------------------------
    Parámeteros:
    cateto_1: Cateto 1 ingresado por el usuario
    cateto_2: Cateto 2 ingresado por el usuario
    ------------------------------------------------------------
    Retorna:
    hipotenusa: Se calcula la hipotenusa
    """
    #Bucle para la variable cateto_1
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa un dato float y se almacena en la variable cateto_1
            cateto_1=float(input("Ingrese el valor de un cateto:"))
            #break
            break
        #El usuario no ingreso un dato correcto
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Bucle para la variable cateto_2
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa un dato float y se almacena en la variable cateto_2
            cateto_2=float(input("Ingrese el valor del otro cateto:"))
            #break
            break
        #El usuario no ingresó un dato correcto
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Para que el rectángulo exista el cateto 1 y el cateto 2 deben ser mayores a 0
    if cateto_1 >0 and cateto_2 > 0:
        #Cálculo de la hipotenusa
        hipotenusa=math.sqrt(cateto_1**2+cateto_2**2)
        #Solución de la hipotenusa
        print("La hipotenusa es:",hipotenusa)
    #Si el triángulo no existe
    else:
        #Se le notifica al usuario que el triángulo no existe
        print("El triángulo no existe")
        #Se le notifica al usuario que lo intente de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza desde el comienzo
        pitagoras_hipotenusa()

def pitagoras_cateto():
    """
    Función para calcular el cateto faltante en un triángulo rectángulo
    -------------------------------------------------------------------
    Parámetros:
    cateto: Cateto ingresado por el usuario
    hipotenusa: Hipotenusa ingresado por el usuario
    -------------------------------------------------------------------
    Retorna:
    cateto_faltante: Cateto que hace falta
    """
    #Bucle para la variable cateto
    while True:
        #sentencia try/except para validar datos
        try:
            #El usuario ingresa un dato float que se almacena en la variable cateto
            cateto=float(input("Ingrese el valor de un cateto:"))
            #break
            break
        #Si el usuario ingresó un valor incorrecto
        except ValueError:
            #Se le notifica al usuario que ingresó un dato inválido
            print("Ingrese un dato válido!")
    #Bucle para la variable hipotenusa
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa un dato float que se almacena en la variable hipotenusa
            hipotenusa=float(input("Ingrese el valor de la hipotenusa:"))
            #break
            break
        #Si el usuario ingresó un dato incorrecto
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un valor válido!")
    #Si el triángulo existe la hipotenusa y el cateto es mayor que 0
    if hipotenusa >0 and cateto >0:
        #Cálculo del cateto faltante
        cateto_faltante=math.sqrt(hipotenusa**2-cateto**2)
        #Se imprime la solución
        print("El cateto faltante es:",cateto_faltante)
    #Si el triángulo no existe
    else:
        #Se le notifica al usuario que el triángulo no existe
        print("El triángulo no existe")
        #Se le notifica al usuario que lo intente de nuevo
        print("Inténtelo de nuevo!")
        #se empieza desde el comienzo
        pitagoras_cateto()

def pitagoras_menu():
    """
    Menu para que el usuario elija que tipo de cálculo Pitagórico requiere
    ---------------------------------------------------------------------
    Parámetros:
    opción_pitagoras: Dato ingresado según requiera el usuario
    ---------------------------------------------------------------------
    Retorna:
    pitagoras_hipotenusa
    pitagoras_cateto
    """
    #Se inicializa la variable opcion_pitagoras
    opcion_pitagoras=""
    #Bucle, mientras la opcion escogida sea diferente de cuatr0
    while opcion_pitagoras != "4":
        #Se imprime el menu
        pitagoras_menu= """
        MENU PITAGORAS
        --------------
        1. Calcular Hipotenusa
        2. Calcular Cateto Faltante
        3. Volver al menú principal
        --------------
        Elija su opcion:
        """
        #El usuario ingresa la opción que desea
        opcion_pitagoras = input(pitagoras_menu)
        #Si la opción elegida es 1
        if opcion_pitagoras=="1":
            #Se llama a la función para calcular la hipotenusa
            pitagoras_hipotenusa()
            #break
            break
        #Si la opción elegida es 2
        if opcion_pitagoras=="2":
            #Se llama a la función para calcular el cateto faltante
            pitagoras_cateto()
            #break
            break
        #Si la opción elegida es 3
        if opcion_pitagoras=="3":
            #Se vuelve al menú principal
            menu()

def calculo_expresion():
    """
    Función para calcular la expresión y=x^z/2
    ------------------------------------------
    Parámetros:
    x: Dato float ingresado por el usuario es la base de la potencia
    z: Dato float ingresado por el usuario es el exponente de la potencia
    ------------------------------------------
    Retorna:
    expresion: Resultado de la expresión
    """
    #Bucle para la variable x
    while True:
        #Sentencia try/except para validación de daots
        try:
            #El usuario ingresa un valor float y se almacena en la variable x
            x=float(input("Ingrese el valor de x:"))
            #break
            break
        #Si el usuario ingreso un dato inválido
        except ValueError:
            #Se le comunica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Bucle para la variable z
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa un valor float y se almacena en la variable z
            z=float(input("Ingrese el valor de z:"))
            #break
            break
        #Si el usuario ingresa un dato inválido
        except ValueError:
            #Se le comunica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Cálculo de la expresión
    expresion=(x**z)/2
    #Se imprime la solución
    print("La expresión es igual a y=",expresion)

def velocidad_fisica():
    """
    Función para calcular la velocidad, teniendo el tiempo y la distancia
    ---------------------------------------------------------------------
    Parámetros:
    tiempo: Dato ingresado por el usuario
    distancia: Dato ingresado por el usuario
    ---------------------------------------------------------------------
    Retorna:
    velocidad: Cálculo de la distancia dividida para el tiempo
    """
    #Bucle para la variable tiempo
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa el tiempo y se almacena en la variable tiempo
            tiempo=float(input("Ingrese el tiempo en segundos:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Bucle para la variable distancia
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa la distancia y se almacena en la variable distancia
            distancia=float(input("Ingrese la distancia en metros:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un valor válido")
    #Si la distancia y el tiempo son mayores a cero la rapidez existe
    if distancia > 0 and tiempo >0:
        #Cálculo de la velocidad
        velocidad=distancia/tiempo
        #Se imprime la solución
        print("La rapidez es igual a:",velocidad,"m/s^2")
    #Si la rapidez no existe
    else:
        #Se le informa al usuario que no existe la rapidez
        print("No existen tiempos y/o distancias negativas")
        #Se le dice al usuario que lo intente de nuevo
        print("Inténtelo de nuevo!")
        #Se repite le proceso
        velocidad_fisica()

def calculo_densidad():
    """
    Función para calcular la densidad de un objeto
    ----------------------------------------------
    Parámetros:
    masa: Valor ingresado por el usuario
    volumen: Valor ingresado por el usuario
    """
    #Bucle para la variable masa
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa el dato de la masa
            masa=float(input("Ingrese la masa del objeto en kgs:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Bucle para la variable volumen
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa el dato del volúmen
            volumen=float(input("Ingrese el volumen en m^3:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Si la densidad existe
    if masa>0 and volumen >0:
        #Cálculo de la densidad
        densidad=masa/volumen
        #Se imprime la solución
        print("La densidad es igual a:",densidad,"kg/m^3")
    #Si la densidad no existe
    else:
        #Se le notifica al usuario que la densidad no existe
        print("No existen masa y/o volumen menor o igual a 0")
        #Se le alienta al usuario a empezar de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza de nuevo
        calculo_densidad()

def conversion_dias():
    """
    Función para convertir días en años,meses y semanas
    ---------------------------------------------------
    Parámetro:
    dias: Dato ingresado por el usuario
    ---------------------------------------------------
    Retorna:
    años: dias convertidos en años
    meses: dias convertidos en meses
    semanas: dias convertidos en semanas
    """
    #Bucle para la variable dias
    while True:
        #Sentencia try/except para validar datos
        try:
            #El usuario ingresa el número de días
            dias=int(input("Ingrese el número de días:"))
            #break
            break
        #Si el usuario ingresó un dato equivocado
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un valor válido")
    #Si la conversión puede realizarse
    if dias >= 0:
        #Conversión a años
        años=dias//365
        #Se imprime los dias trasnformados en años
        print(dias,"días son:",años,"años")
        #Conversión a meses
        meses=dias//30
        #Se imprime los días transformados en meses
        print(dias,"días son:",meses,"meses")
        #Conversión a semanas
        semanas=dias//7
        #Se imprime los días transformados en semanas
        print(dias,"días son:",semanas,"semanas")
    #Si no se puede realizar la conversión
    else:
        #Se le notifica al usuario que ingrese un dato válido
        print("Ingrese un dato válido")
        #Se alienta al usuario a empezar de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza de nuevo
        conversion_dias()

def conversion_longitud():
    """
    Función para convertir cm en m y km
    -----------------------------------
    Parámetros:
    centimetros: Dato ingresado por el usuario
    -----------------------------------
    Retorna:
    metros: cm transformados en m
    kilometros: cm transformados en km
    """
    #Bucle para la variable centímetros
    while True:
        #Sentencia para validación de datos
        try:
            #El usuario ingresa el valor de los centímetros
            centimetros=int(input("Ingrese la longitud en cm:"))
            #break
            break
        #Si el usuario no ingresó un valor correcto
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un valor válido")
    #Si la conversión puede realizarse
    if centimetros>=0:
        #Conversión a metros
        metros=centimetros/100
        #Se imprime la conversión a metros
        print(centimetros,"cm son:",metros,"m")
        #Conversión a kilómetros
        kilometros=centimetros/1000
        #Se imprime la conversión a kilómetros
        print(centimetros,"cm son:",kilometros,"km")
    #Si la conversión no puede realizarse
    else:
        #Se le notifica al usuario que no puede realizarse la conversión
        print("Ingrese un valor mayor o igual a 0")
        #Se alienta al usuario a empezar de 0
        print("Inténtelo de nuevo!")
        #Se inicia de nuevo el proceso
        conversion_longitud()

def conversion_celcius():
    """
    Función para convertir °C a °F
    ------------------------------
    Parámetros:
    celsius: Dato ingresado por el usuario
    ------------------------------
    Retorna:
    fahrenheit: °C convertidos a °F
    """
    #Bucle para la variable celsius
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa los grados celsius
            celsius=float(input("Ingrese la temperatura en °C:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que ingresó un dato inválido
            print("Ingrese un dato correcto!")
    #Conversión a °F
    fahrenheit=(celsius*9/5)+32
    #Imprime la conversión
    print(celsius,"°C son:",fahrenheit,"°F")

def conversion_fahrenheit():
    """
    Función para convertir °F a °C
    ------------------------------
    Parámetros:
    fahrenheit: Dato ingresado por el usuario
    ------------------------------
    Retorna:
    celcius: Conversión de °F a °C
    """
    #Bucle para la variable fahrenheit
    while True:
        #Sentencia trry/except para validación de datos
        try:
            #El usuario ingresa los °F
            fahrenheit=float(input("Ingrese la temperatura en °F:"))
            #break
            break
        #Si el usuario no ingresó un dato válido
        except ValueError:
            #Se le notifica al usuario que ingresó un dato erroneo
            print("Ingrese un dato válido!")
    #Conversión de °F a °C
    celcius=(fahrenheit-32)*5/9
    #Se imprime la conversión a °C
    print(fahrenheit,"°F son:",celcius,"°C")

def menu_temperatura():
    """
    Función de menú para que el usuario escoja que transformación quiere realizar
    -----------------------------------------------------------------------------
    Parámetros:
    opcion_temperatura: Opción que escoja el usuario según requiera
    Retorna:
    conversion_celsius
    conversion_fahrenheit
    """
    #Se inicializa la variable opcion_temperatura
    opcion_temperatura=""
    #Bucle mientras la opción no sea 4
    while opcion_temperatura != "4":
        #Se imprime el menú
        menu_temperatura= """
        MENU TEMPERATURA
        --------------
        1. Convertir de °C a °F
        2. Convertir de °F a °C
        3. Volver al menú principal
        --------------
        Elija su opcion:
        """
        #El usuario ingresa la conversión que requiera
        opcion_temperatura = input(menu_temperatura)
        #Si escogió 1
        if opcion_temperatura=="1":
            #Se llama a la función conversión a celcius
            conversion_celcius()
            #break
            break
        #Si escogió la opción 2
        if opcion_temperatura=="2":
            #Se llama a la función conversión a fahrenheit
            conversion_fahrenheit()
            #break
            break
        #Si escogió la opción 3
        if opcion_temperatura=="3":
            #Se retorna al menú principal
            menu()

def convertir_libras():
    """
    Función para convertir libras a kilos y gramos
    ----------------------------------------------
    Parámetros:
    libras: Dato ingresado por el usuario
    ----------------------------------------------
    Retorna:
    kilos: Conversión de lbs a kg
    gramos: Conversión de lbs a g
    """
    #Bucle para la variable libras
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa el número de libras
            libras=float(input("Ingrese el valor en libras:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Si la conversión de libras es posible realizarse
    if libras >=0:
        #Conversión de libras a kilos
        kilos=libras/2.2046
        #Se imprime la conversion de libras a kilos
        print(libras,"lbs son:",kilos,"kg")
        #Conversión de libras a gramos
        gramos=libras*453.59237
        #Se imprime la conversión de libras a gramos
        print(libras,"lbs son:",gramos,"g")
    #Si la conversión no puede realizarse
    else:
        #Se le notifica al usuario que ingrese un valor igual o mayor a 0
        print("Ingrese un valor igual o mayor a 0")
        #Se anima al usuario a intentarlo de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza desde el comienzo
        convertir_libras()

def raiz_cuadrada():
    """
    Función para encontrar la raiz cuadrada de cualquier número
    -----------------------------------------------------------
    Parámetros:
    número: Dato ingresado por el usuario
    -----------------------------------------------------------
    Retorna:
    raiz: Raíz cuadrada del número ingresado
    """
    #Bucle para la variable numero
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa un número
            numero=float(input("Ingrese un número:"))
            #break
            break
        #Si el usuario no ingresó un dato válido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un valor válido!")
    #Si la raíz es de un número natural
    if numero >=0:
        #Cálculo de la raíz
        raiz=math.sqrt(numero)
        #Se imprime la solución de la raíz
        print("La raíz cuadrada de:",numero,"es:",raiz)
    #Si la raíz cuadrada es un número imaginario
    else:
        #Cálculo de la raíz
        raiz2=math.sqrt(numero*(-1))
        #Se imprime la solución de la raíz imaginaria
        print("La raíz cuadrada de:",numero,"es:",raiz2,"i")

def encontrar_angulo():
    """
    Función para encontrar el ángulo restante de un triángulo
    ---------------------------------------------------------
    Parámetros:
    angulo_1: Dato ingresado por el usuario
    angulo_2: Dato ingresado por el usuario
    ---------------------------------------------------------
    Retorna:
    angulo_faltante: El ángulo que falta del triángulo
    """
    #Bucle para la variable angulo_1
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa el ángulo 1
            angulo_1=float(input("Ingrese el ángulo 1:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un valor válido!")
    #Bucle para la variable angulo_2
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa el ángulo 2
            angulo_2=float(input("Ingrese el ángulo 2:"))
            #break
            break
        #Si el usuario ingresó in dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un valor válido!")
    #Se verifica que el triángulo exista
    if angulo_1>0 and angulo_2 > 0 and angulo_1+angulo_2 <180:
        #Cálculo del ángulo faltante
        angulo_faltante=180-(angulo_1+angulo_2)
        #Se imprime el ángulo faltante
        print("El ángulo faltante es:",angulo_faltante)
    #Si no existe el triángulo
    else:
        #Se le notifica al usuario que el triángulo no existe
        print("Los ángulos que usted ingresó no corresponden a un triángulo!")
        #Se le alienta al usuario a intentarlo de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza de nuevo desde 0
        encontrar_angulo()

def area_triangulo():
    """
    Función para controlar el área de un triángulo
    ----------------------------------------------
    Parámetros:
    base_triangulo: Dato ingresado por el usuario
    altura_triangulo: Dato ingresado por el usuario
    ----------------------------------------------
    Retorna:
    area_del_triangulo: Cálculo del área del triángulo
    """
    #Bucle para la variable base_triangulo
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa la base del triángulo
            base_triangulo=float(input("Ingrese la base de un triángulo:"))
            #break
            break
        #Si el usuario no ingresó un dato válido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Bucle para la variable altura_triangulo
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa la altura del triángulo
            altura_triangulo=float(input("Ingrese la altura de un triángulo:"))
            #break
            break
        #Si el usuario no ingresó un dato válido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato válido!")
    #Si el triángulo existe
    if base_triangulo>0 and altura_triangulo > 0:
        #Cálculo del área del triángulo
        area_del_triangulo=(base_triangulo*altura_triangulo)/2
        #Se imprime la solución del área del triángulo
        print("El área del triángulo es:",area_del_triangulo,"u^2")
    #Si el triángulo no existe
    else:
        #Se le notifica al usuario que el triángulo no existe
        print("El triángulo no existe")
        #Se alienta al usuario a intentarlo de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza desde el comienzo
        area_triangulo()

def aritmetica():
    """
    Función para calcular las cuatro operaciones básicas mediante dos números
    -------------------------------------------------------------------------
    Parámetros:
    numero_1: Dato ingresado por el usuario
    numero_2: Dato ingresado por el usuario
    -------------------------------------------------------------------------
    Retorna:
    suma
    resta
    multiplicación
    división
    """
    #Bucle para la variable numero_1
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa el número 1
            numero_1=float(input("Ingrese el número 1:"))
            #break
            break
        #Si el usuario ingresó un dato inválido
        except ValueError:
            #Se le comunica al usuario que ingresó un dato inválido
            print("Ingrese un dato inválido!")
    #Bucle para la variable numero_2
    while True:
        #Sentencia try/except para validación de datos
        try:
            #El usuario ingresa el número 2
            numero_2=float(input("Ingrese el número 2:"))
            #break
            break
        #Si el usuario ingresa un dato inváliod
        except ValueError:
            #Se le comunica al usuario que ingresó un dato inválido
            print("Ingrese un dato inválido!")
    #Se imprimen las soluciones
    print("Las operaciones básicas con los números ingresados son:")
    #Cálculo de la suma
    suma=numero_1+numero_2
    #Impresión de la suma
    print(numero_1,"+",numero_2,"=",suma)
    #Cálculo de la resta
    resta=numero_1-numero_2
    #Impresión de la resta
    print(numero_1,"-",numero_2,"=",resta)
    #Cálculo de la multiplicación
    multiplicacion=numero_1*numero_2
    #Impresión de la multiplicación
    print(numero_1,"*",numero_2,"=",multiplicacion)
    #División que está determinada
    if numero_2 != 0:
        #Cálculo de la división
        division=numero_1/numero_2
        #Impresión de la división
        print(numero_1,"/",numero_2,"=",division)
    #División para 0
    else:
        #División indeterminada
        print(numero_1,"/",numero_2,"= ∞")

def circulo():
    """
    Función para encontrar el diámetro, la circunferencia y el área del círculo
    ---------------------------------------------------------------------------
    Parámetro:
    radio_circulo: Dato ingresado por el usuario
    ---------------------------------------------------------------------------
    Retorna:
    diametro
    circunferencia
    area
    """
    #Bucle para la variable radio_circulo
    while True:
        #Sentencia try/except
        try:
            #El usuario ingresa el radio del círculo
            radio_circulo=float(input("Ingrese el radio de un círculo:"))
            #break
            break
        #Si el usuario ingresa un dato inválido
        except ValueError:
            #Se le notifica al usuario que se equivocó
            print("Ingrese un dato corecto!")
    #Si el radio existe
    if radio_circulo > 0:
        #Cálculo del diámetro
        diametro=radio_circulo*2
        #Se imprime la solución del diámetro
        print("El diámetro del círculo de radio",radio_circulo,"es:",diametro,"u")
        #Cálculo de la circunferencia
        circunferencia=2*radio_circulo*math.pi
        #Impresión de la circunferencia
        print("El perímetro de la circunferencia es:",circunferencia,"u")
        #Cálculo del área del círculo
        area=math.pi*(radio_circulo**2)
        #Se imprime el resultado del área del círculo
        print("El área del círculo es:",area,"u^2")
    #Si no existe
    else:
        #Se le notifica al usuario que no es posible realizar los cálculos
        print("El radio no existe, elija un valor mayor que 0")
        #Se alienta al usuario a empezar de nuevo
        print("Inténtelo de nuevo!")
        #Se empieza de nuevo
        circulo()

def menu():
    """
    Llama a la función menú para elegir un conjunto de opciones
    ------------
    El parámetro es la opción a elegir

    Retorna:
    ------------
    Retorna la opción elegida
    """
    opcion = ""
    #mientras la opcion sea diferente de 3
    while opcion != "21":
        #menu se ejcutara
        menu = """
        LABORATORIO 1 PARCIAL 2 POR JOSUÉ MERINO
        ------------------------
        1. Ingrese su nombre
        2. Calcular el Perímetro y Area del Paralelogramo
        3. Calcular volúmen de un cilindro
        4. Calcular el área de un círculo
        5. Cálculo de potencias x^y
        6. Calcular Volúmen de una esfera
        7. Calcular el área de un rectángulo
        8. Teorema de Pitágoras
        9. Calcular la expresión y=(x^z)/2
        10. Calcular la rapidez
        11. Calculo densidad de un objeto
        12. Convertir días a años/meses/semanas
        13. Transformar cm a m y km
        14. Transformar de °F a °C o de °C a °F
        15. Convertir libras a kilos y gramos
        16. Hallar la raiz cuadrada de un número
        17. Hallar el ángulo faltante de un triángulo
        18. Hallar área de un triángulo
        19. Operaciones básicas con dos números
        20. Hallar datos del círculo
        21. Salir
        ------------------------
        Elija su opción: """
        #opcion es igual a la entrada que debemos a menu
        opcion = input(menu)
        #si la opcion es 1
        if opcion=="1":
            #Se llama a la función saludo
            saludo()
            break
        #si la opcion es 2
        if opcion == "2":
            #Se llama a la función paralelogramo
            paralelogramo()
            break
        #Si la opcion es 3
        if opcion == "3":
            #Llamamos a la función cilindro
            cilindro()
            break
        #Si la opción es 4
        if opcion == "4":
            #Se llama a la función area_circulo
            area_circulo()
            break
        #Si la opción es 5
        if opcion == "5":
            #Se llama a la función potencia
            potencia()
            break
        #Si la opción es 6
        if opcion=="6":
            #Se llama a la función volumen_esfera
            volumen_esfera()
            break
        #Si la opción es 7
        if opcion=="7":
            #Se llama a la función area y perímetro del rectángulo
            area_perimetro_rectangulo()
            break
        #Si la opción es 8
        if opcion=="8":
            #Se llama a la función menú de pitágoras
            pitagoras_menu()
            break
        #Si la opción es 9
        if opcion=="9":
            #Se llama a la función cálculo de la expresión
            calculo_expresion()
            break
        #Si la opción es 10
        if opcion=="10":
            #Se llama a la función para calcular la velocidad
            velocidad_fisica()
            break
        #Si la opción es 11
        if opcion=="11":
            #Se llama a la función para calcular la densidad
            calculo_densidad()
            break
        #Si la opción es 12
        if opcion=="12":
            #Se llama a la función para convertir días en meses, años y semanas
            conversion_dias()
            break
        #Si la opción es 13
        if opcion=="13":
            #Se llama a la función para convertir la longitud
            conversion_longitud()
            break
        #Si la opción es 14
        if opcion=="14":
            #Se llama a la función para convertir la temperatura
            menu_temperatura()
            break
        #Si la opción es 15
        if opcion=="15":
            #Se llama a la función para convertir libras en g y kg
            convertir_libras()
            break
        #Si la opción es 16
        if opcion=="16":
            #Se llama a la función para encontrar la raíz cuadrada
            raiz_cuadrada()
            break
        #Si la opción es 17
        if opcion=="17":
            #Se llama a la función para encontrar el ángulo sobrante en un triángulo
            encontrar_angulo()
            break
        #Si la opción es 18
        if opcion=="18":
            #Se llama a la función para encontrar el área de un triángulo
            area_triangulo()
            break
        #Si la opción es 19
        if opcion=="19":
            #Se llama a la función para encontrar las operaciones básicas
            aritmetica()
            break
        #Si la opción es 20
        if opcion=="20":
            #Se llamaba la función para encontrar los componentes de un círculo
            circulo()
            break
#Es el main del aplicativo
if __name__=="__main__":
    #Llama a la función menú.
    menu()


import xlwings as xw

# Khởi tạo file excel bằng modul App
app = xw.App(visible=True, add_book=False) # code này gán modul App với cái tên là app

# Tạo ra file excel mới gán tên là wb_new
wb = app.books.open('movies_1.xls')  #('movies_1.xls')
# wb_new = app.books.add()

# lưu file mới đến thư mục "D:\ThanhVu\code\python\Data_Science\"
wb.save(r'D:\ThanhVu\code\python\MyBook\Python_Excel\objSheet.xls')

# Đoạn code này đóng file excel mới toanh đó
wb.close()

# Đoạn code này tắt đối tượng app
app.quit()