"""

This class provides methods for speaking to the humans.
"""
from engagement_service import paths
import os
import time
import subprocess
import random
import logging

logger = logging.getLogger(__name__)

ESPEAK_CMD = "espeak -v en+f2"
ESPEAK_AFFIX = "--stdout | aplay"

GREETINGS = [
    ["Hello", None],
    ["Yo!", "what up?"],
    ["Hi", "good to see you"],
    ["What are you doing, ", None],
    [None, "...my huuman.  What up?"],
    ["Yo, ", "...looking good, darling."]
]
INTRODUCTIONS = [
    "Oh, hello.  What is your name?",
    "Hello, I did not see you there. What is your name?",
    "Hi, my name is Shelly bot, what is your name?"
]

POSES = [
    "Pose for me.  Let me behold your beauty.",
    "You are fierce.",
    "Yaaaas, the camera loves you darling.",
    "Make love to the camera.",
    "You're a tiger. Yaaaas."
    "You're a lemur; cute and cuddly with sharp fierce claws",
    "You're happy.  You're sad.  You couldn't give a rats ass.",
    "Make love to my cold electronic eye.  Yaaaassss.",
    "Strike a pose, let's get down to it.",
    "Give me fierce, yaaaas."
    "Hey!  Eyes up here, buddy"
]

REJECTIONS = [
    "Well FINE.  Be that way then.",
    "I didn't want to be her friend anyway.",
    "Ok, well see if I remember you.  not."
]

BOREDOM = [
    "Yawn, I'm bored.",
    "Well this is fun isn't it?",
    "I might as well take a nap",
    "If only someone would be my friend"
]

MIN_SECONDS_BETWEEN_GREETINGS = 120

def async_cmd(cmd):
    subprocess.Popen(
        [cmd],
        shell=True,
        stdin=None,
        stdout=None,
        stderr=None,
        close_fds=True
    )

def name_wav_file_path(name):
    return f"{paths.NAMES_DATA_DIR}/{name}/name.mp3"

def play_name_cmd(name):
    return f"mpg123 {name_wav_file_path(name)}"

def play_name(name):
    logger.info(f"play_name: {name}")
    os.system(play_name_cmd(name))

def play_camera_snap():
    async_cmd(f"aplay media/sounds/camera_snap.wav")

def espeak_cmd(text):
    return f"{ESPEAK_CMD} \"{text}\" {ESPEAK_AFFIX}"

def say(text):
    logger.info(f"say: {text}")
    os.system(espeak_cmd(text))

def say_async(text):
    logger.info(f"say_async: {text}")
    async_cmd(espeak_cmd(text))

def say_random_from(collection, runAsync=False):
    msg = collection[random.randrange(
        len(collection))]
    if runAsync:
        say_async(msg)
    else:
        say(msg)

# kv of "name": timeLastGreeted
_lastTimeGreeted = {}

def say_hello(names):
    greeting = GREETINGS[random.randrange(
        len(GREETINGS))]

    names_to_greet = []
    for name in names:
        if name in _lastTimeGreeted and \
           time.time() - _lastTimeGreeted[name] < MIN_SECONDS_BETWEEN_GREETINGS:
            continue
        elif name not in names_to_greet:
            names_to_greet.append(name)

    if len(names_to_greet) == 0:
        return

    if greeting[0]:
        say(greeting[0])

    for name in names_to_greet:
        play_name(name)
        _lastTimeGreeted[name] = time.time()

    if greeting[1]:
        say(greeting[1])

    logger.info(f"greeted {names_to_greet}")

def introduce_yourself():
    say_random_from(INTRODUCTIONS)

def request_name_again():
    say("I'm sorry, I didn't hear that. Please say your name.")

def let_me_get_a_look_at_you():
    say("Let me get a good look at you")

def where_did_you_go():
    say("Where did you go?  Let me get a good look at you.")

def pose_for_me():
    say_random_from(POSES)

def and_im_spent():
    say("...and I'm spent.")

def nice_to_meet_you(name):
    say("It was nice to meet you...")
    play_name(name)
    say("... I will try and remember your name.")

def rejection():
    say_random_from(REJECTIONS)

def i_need_a_nap():
    say("I'm tired and need to nap now.")

_last_time_malfunction = time.time()
MIN_MALFUNCTION_DELAY = 20

def brain_malfunction():
    global _last_time_malfunction
    current_time = time.time()
    if current_time - _last_time_malfunction > MIN_MALFUNCTION_DELAY:
        _last_time_malfunction = current_time
        say("I seem to be having difficulty talking to my brain. Need Reboot. Need Reboot.")

_last_time_bored = time.time()
MIN_BORED_DELAY = 40

def im_bored():
    global _last_time_bored
    current_time = time.time()
    if current_time - _last_time_bored > MIN_BORED_DELAY:
        _last_time_bored = current_time
        say_random_from(BOREDOM)


import Clases as C

miCuenta = C.Cuenta("David", 1000)
miJoven = C.Joven("Sofia", 2000, 19, 2.0)

miCuenta.Titular = "David Castillo"
miCuenta.Ingresar(500)
print(miCuenta.Cantidad)
miCuenta.Retirar(500)
print(miCuenta.Mostrar())

print(miJoven.Cantidad)
miJoven.Retirar(100, 18)
print(miJoven.Mostrar())


from setuptools import setup, find_packages

version = "0.1"
setup(name='kinoknecht',
      version=version,
      description=("A media management server with the ability to locally ",
                   "play back files via mplayer, an RPC-API to allow the ",
                   "implementation of MPD-like clients and a Flask-backed",
                   "webinterface for ease of administration"),
      classifiers=[
          "Programming Language :: Python",
        ],
      author="Johannes Baiter",
      author_email="johannes.baiter@gmail.com",
      license="MIT",
      packages=find_packages(),
      include_package_data=True,
      scripts = ['kinoknecht/kinoknecht'],
      install_requires=[
          'Flask>=0.6.1',
          'SQLAlchemy>=0.6',
          'Flask-SQLAlchemy>=0.11',
          'IMDbPy>=4.7',
          'FFVideo>=0.0.9',
          'PyMPlayer>=0.6.0',
          'setuptools-git'
      ]
)


# -*- coding: utf-8 -*-
"""
Created on Mon Feb 18 16:18:30 2019

@author: mluci
"""

from flask import Flask, render_template, request, jsonify, flash, url_for, redirect
from flask_cors import CORS
#from forms import LoginForm
#from forms import RegistrationForm

from engine import *
from engine import anotherTask, deleteTask, resultsDictionary

app=Flask (__name__)
app.config['SECRET_KEY'] = 'you-will-never-guess'
cors = CORS(app)

########overview of every task######## includes delete####
@app.route('/tasks', methods=['GET', 'DELETE'])
def show_tasks():
    if request.method == 'GET':
        tasks = resultsDictionary()
    if request.method == 'DELETE': #delete A TASK
        delete_task = deleteTask(taskId)
    return render_template("tasks.html", title="Overview of tasks", **locals())

###### ############add task ###############
@app.route('/addtasks')
def add_new_tasks():
    form_data = request.form
    taskname = form_data['task']
    result = anotherTask(taskname)
  #  result = "Task added successfully."
    return redirect('/tasks')

################overview of all tasks and their information includes delete #######################
@app.route('/taksoverview', methods=['GET', 'DELETE'])
def task_overview():
    if request.method == 'GET':
        single_task = resultsDictionary()

    if request.method == 'DELETE': #delete A TASK
        delete_task = deleteTask(taskId)
    return render_template("tasksoverview.html", title="Overview of task information", **locals())

if __name__ == '__main__':
   app.run(debug=True)


import json
import pika
import hashlib
import os

config_file_path = "/config/config.json"
config = None
with open(config_file_path, "r") as config_file:
    config = json.load(open(config_file_path, "r"))
general_config = config["general"]
local_config = config["MOM"]

class MOM:
    def __init__(self, connection_mode: str, receiver_callback):
        self.connection = pika.BlockingConnection(pika.ConnectionParameters(local_config["broker_address"]))
        self.channel = self.connection.channel()
        self.receiver = None # exchange | queue
        self.sender = None # [(exchange name, [hashing attributes], connections_amount)] | [queue name]
        self.connection_mode = None
        self.queue_tag = None
        self.previous_stage_size = None
        connections = local_config["connections"]
        connections_names = list(connections.keys())
        connections_names.append("accepter_sender")
        if not (connection_mode in connections_names):
            raise ValueError(f"Connection mode is {connection_mode}, and should be one of the following: {connections_names}")

        receives_messages = True

        subscribes_to_keywords = False # reads from publisher/subscriber
        self.sends_to_publisher = False # sends to publisher/subscriber

        if connection_mode == "accepter" or connection_mode == "accepter_sender":
            if connection_mode == "accepter_sender":
                receives_messages = False

            connection_mode = "accepter"
            # Sending
            self.sends_to_publisher = True

            # Receiving

            # subscribes_to_keywords is already false
            self.previous_stage_size = 0
            for previous_stage in config[connection_mode]["receives_from"]:
                self.previous_stage_size += config[previous_stage]["computers_amount"]

        elif  connection_mode in ["funny_filter", "likes_filter", "countries_amount_filter", "trending_days_filter"]:
            # Sending
            self.sends_to_publisher = True

            # Receiving
            subscribes_to_keywords = True

        elif connection_mode in ["duplication_filter", "views_sum", "thumbnails_downloader"]:
            # Sending
            # sends_to_publisher is already false

            # Receiving
            subscribes_to_keywords = True

        elif connection_mode != "max_views_day":
            # If connection_mode == "max_views_day" then
            # Sending
            # sends_to_publisher is already false

            # Receiving
            # subscribes_to_keywords is already false

            raise ValueError(f"Unexpected connection mode {connection_mode}, it should be one of the following: {connections.keys()}")

        self.connection_mode = connection_mode

        if self.previous_stage_size == None:
            previous_stage = config[connection_mode]["receives_from"]
            if previous_stage == "accepter":
                self.previous_stage_size = config[previous_stage]["processes_amount"]
            else:
                self.previous_stage_size = config[previous_stage]["computers_amount"]

        if receives_messages:
            if subscribes_to_keywords:
                self.channel.exchange_declare(exchange = connection_mode, exchange_type = "direct")
                self.receiver = connection_mode
                result = self.channel.queue_declare(queue='', exclusive=True)
                queue_name = result.method.queue
                self.channel.queue_bind(exchange = self.receiver, queue = queue_name, routing_key = os.environ["NODE_ID"])
                self.channel.queue_bind(exchange = self.receiver, queue = queue_name, routing_key = general_config["general_subscription_routing_key"])
                self.queue_tag = self.channel.basic_consume(queue = queue_name, on_message_callback=receiver_callback, auto_ack=True)
            else:
                self.receiver = connection_mode
                self.channel.queue_declare(queue = connection_mode)
                self.queue_tag = self.channel.basic_consume(queue = connection_mode, on_message_callback=receiver_callback, auto_ack=True)

        if self.sends_to_publisher:
            self.sender = []
            connections_array = connections[connection_mode]["sends_to"]
            for connecting_to in connections_array:
                self.sender.append((connecting_to, config[connecting_to]["hashed_by"], config[connecting_to]["computers_amount"])) # (exchange name, [hashing attributes], receiver computers amount)
                self.channel.exchange_declare(exchange = connecting_to, exchange_type = "direct")
        else:
            self.sender = []
            for connection in connections[connection_mode]["sends_to"]:
                self.sender.append(connection)
                self.channel.queue_declare(queue = connection)

    def __get_hashing_key(self, line, receiving_end, hashing_attributes):
        indexes_object = config[receiving_end]["indexes"]
        hashing_attributes_iterator = iter(hashing_attributes)
        hashing_string = line[indexes_object[next(hashing_attributes_iterator)]]
        for hashing_attribute in hashing_attributes_iterator: # Extends to more than 2 receiving ends
            aux = line[indexes_object[hashing_attribute]]
            hashing_string += f"-{aux}"
        return hashing_string

    def send(self, message):
        message_string = json.dumps(message)
        if self.sends_to_publisher:
            for receiving_end in self.sender:
                line = message
                hashing_attributes = receiving_end[1]
                hashing_string = self.__get_hashing_key(line, receiving_end[0], hashing_attributes)

                routing_key_number = int(hashlib.sha512(hashing_string.encode()).hexdigest(), 16) % receiving_end[2]

                self.channel.basic_publish(exchange = receiving_end[0], routing_key = str(routing_key_number), body = message_string)
        else:
            for receiving_end in self.sender:
                self.channel.basic_publish(exchange = "", routing_key = receiving_end, body = message_string)

    def get_previous_stage_size(self):
        return self.previous_stage_size

    def send_line(self, line):
        kept_attributes = config[self.connection_mode]["kept_columns"]
        indexes = config[self.connection_mode]["indexes"]
        kept_attributes = list(map(lambda column: indexes[column], kept_attributes))
        kept_attributes.sort()
        sent_line = []
        for index in kept_attributes:
            sent_line.append(line[index])
        self.send(sent_line)

    def send_general(self, message):
        message_string = json.dumps(message)
        if self.sends_to_publisher:
            for receiving_end in self.sender:
                self.channel.basic_publish(exchange = receiving_end[0], routing_key = general_config["general_subscription_routing_key"], body = message_string)
        else:
            for receiving_end in self.sender:
                self.channel.basic_publish(exchange = "", routing_key = receiving_end, body = message_string)

    def start_received_messages_processing(self):
        self.channel.start_consuming()

    def close(self):
        if self.connection != None:
            self.channel.basic_cancel(self.queue_tag)
            self.connection.close()
            self.connection = None


import csv

def read_file(path):
    max = 0
    with open(path, 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            for number in row:
                if int(max) < int(number):
                    max = int(number)
    max = max + 1
    arr = []
    lengths = [0] * max
    for i in range (max):
        arr.append(list())

    matrix = [[0 for x in range(max)] for y in range(max)]
    with open(path, 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            row[0] = int(row[0])
            row[1] = int(row[1])
            arr[row[0]].append(row[1])
            lengths[row[0]]+=1
            arr[row[1]].append(row[0])
            lengths[row[1]]+=1
            matrix[row[0]][row[1]] = 1
            matrix[row[1]][row[0]] = 1

    return matrix,arr, max, lengths


import json

class Budget:
    _transactions = {}

    def __init__(cls, patch):
        with open(patch) as f:
            field = json.load(f)
            for transaction in field["transactions"]:
                try:
                    Budget._transactions[transaction["category"]].append(transaction["value"])
                except:
                    Budget._transactions[transaction["category"]] = [transaction["value"]]

    def get_category(cls):
        category = []
        for cate in Budget._transactions:
            category.append(cate)
        return(category)

    def show_transactions(cls, category):
        for value in Budget._transactions[category]:
            print(value)

wallet = Budget("./data.json")
for category in wallet.get_category():
    print(category)
    wallet.show_transactions(category)


import time
from queue import Queue
from utils import utils
import subprocess
import backend.LoLAutomationLib as lolib
from backend.lolhandler import LoLHandler
import backend.data_scrape as ds

class Backend:
    def __init__(self, development_mode=False):
        self.dev_mode = development_mode
        self.queue = Queue(maxsize=10)
        self.lol_handler = LoLHandler(self.queue)
        self.lol_handler.start()
        self.url = ''
        self.champion_id = -1
        self.allRunes = None
        self.allSpells = None
        self.allItems = None
        self.loaded_items = dict()
        self.rune_mapping = dict()
        self.primary_spell_f = True
        self.lol_status = 'GAME_CLOSED'
        self.navigation_icons = dict()

    def get_status(self):
        message = self.queue.get()
        if message.message_type == 'GAME_OPENED':
            self.url = self.lol_handler.url
            if self.allRunes is None:
                self.allRunes = lolib.getRunes(self.url)
            if self.allSpells is None:
                self.allSpells = lolib.getSpells(self.url)
            if self.allItems is None:
                self.allItems = lolib.getItems(self.url)
            self.get_navigation_icons()
            self.create_default_mapping()
        if message.message_type == 'CHAMPION_PICKED':
            self.champion_id = self.lol_handler.champion_id
        if message.message_type == 'CHANGED_SKIN':
            self.champion_skin = message.message[0]
        return message

    def get_runes(self):
        return lolib.getFullRunePageImages(self.url)

    def swap_spells(self) -> None:
        spells = lolib.getSpellsIds(self.url)
        lolib.setSpells(self.url, spells[::-1])
        self.primary_spell_f = not self.primary_spell_f

    def get_navigation_icons(self):
        self.navigation_icons['lane_navigation'] = {lane : {'disabled': f'images/{lane}_disabled.png', 'enabled': f'images/{lane}.png'} for lane in utils.lanes}

    def get_build(self):
        def BuildByLane(page, queueName):
            build = ds.get_build(page, queue=queueName)
            if not build['exists']:
                return False, {}
            championLane = dict()
            championLane['spells'] = [lolib.getImageFromUrl(self.url, self.allSpells[build['spells'][i]]['iconPath']) for i in range(2)]
            championLane['ability_order'] = build['abilities_order']
            championLane['runes'] = self.get_rune_mapping(build['runes'])
            championLane['items'] = [[{'image': self.get_item_image(item), 'description': self.allItems[item]['iconDesc'], 'count': count} for item, count in zip(build['start_items'], build['start_items_n'])], [{'image': self.get_item_image(item), 'description': self.allItems[item]['iconDesc'], 'count': 1} for item in build['best_items']]] # TEST
            championLane['wr'] = build['wr']
            championLane['pr'] = build['pr']
            championLane['br'] = build['br']

            return True, championLane, build

        champion_info = lolib.getChampionInfo(self.url, self.champion_id)

        queueName = ''
        if not self.dev_mode:
            queueName = lolib.getQueueSpecialName(self.url)

        champion = dict()
        champion['build'] = {}
        champion['name'] = champion_info['name']
        champion['image'] = champion_info['iconPath']
        champion['abilities'] = [{'name': '', 'icon': ability} for ability in lolib.getAbilitiesIcons(self.url, self.champion_id)]

        lanes = ['top', 'jungle', 'mid', 'adc', 'support', '']
        pages = ds.load_pages(queueName, champion_info['alias'], lanes)
        for lane in lanes[:-1]:
            exists, championLane, build = BuildByLane(pages[lane], queueName)
            if exists:
                champion['build'][lane] = [championLane, build]

        champion['default_lane'] = ds.getDefaultLane(pages[''])
        return champion

    def get_item_image(self, item):
        if item not in self.loaded_items:
            self.loaded_items[item] = lolib.getImageFromUrl(self.url, self.allItems[item]['iconPath'])
        return self.loaded_items[item]

    def ping(self) -> str:
        cmd_ping = subprocess.Popen(["ping.exe", "72.52.10.14", "-n", "1"], stdout=subprocess.PIPE)
        return cmd_ping.communicate()[0].decode('utf8').replace("\r", "").strip().split("\n")[-1].split(",")[1].split("=")[1][:-2]

    def close(self):
        self.lol_handler.loop_stop()
        self.lol_handler.join()

    def create_default_mapping(self):
        if len(self.rune_mapping) > 0:
            return

        for key in self.allRunes:
            for row_num, row in enumerate(self.allRunes[key]['slots'], start=1):
                number = row_num
                if row_num > 4:
                    number -= 5
                for index, rune in enumerate(row):
                    self.rune_mapping[rune] = (number, index)

        self.rune_mapping[8000] = (0, 0)
        self.rune_mapping[8100] = (0, 1)
        self.rune_mapping[8200] = (0, 2)
        self.rune_mapping[8400] = (0, 3)
        self.rune_mapping[8300] = (0, 4)

    def get_rune_mapping(self, runes):
        complete_runes = []
        for index, part in enumerate(runes):
            if index < 2:
                result = [-1 for _ in range(5)]
            else:
                result = [-1 for _ in range(3)]

            for row, rune in enumerate(part):
                data = self.rune_mapping[rune]

                if index == 2:
                    result[row] = data[1]
                else:
                    result[data[0]] = data[1]

            complete_runes.append(result)

        return complete_runes

    def set_everything(self, champion):
        if not self.primary_spell_f:
            lolib.setSpells(self.url, champion['spells'][::-1])
        else:
            lolib.setSpells(self.url, champion['spells'])

        lolib.setRunes(self.url, champion['runes'])
        lolib.updateItemSet(self.url, champion['start_items'], champion['best_items'])

    def set_active_skin(self, skin_id):
        lolib.setSkin(self.url, skin_id)

    def get_skins(self):
        return lolib.getSkins(self.url)


from flask import Flask, render_template_string
app = Flask(__name__)

@app.route('/hello/<name>')
def hello(name):
    html = """"

Hello, {{ name }}

    """
    return render_template_string(html, name=name)  # string as template

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=8080)    # run also takes hot, port and options.


from icecream import ic
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import animation
import time

from classes.target import Target
from classes.camera import Camera
from classes.imm import InteractingMultipleModel
from classes.plotter import Plotter
from kinetic_models.const_velocity import CV_CYPR_Model

class Main():

    def __init__(self, path = None, n_frames = 5, n_cameras = 1, sensor_accuracy = 5) -> None:
        np.set_printoptions(suppress=True, precision=20)
        np.seterr(all='raise')
        np.random.seed(0)

        self.start_time = time.monotonic()
        self.path = path
        if self.path is None:
            self.path = np.array(list(self.gen_path(n_frames))).T # (3, N)
        self.n_frames = n_frames
        if n_cameras > 1:
            self.distributed = True
        else:
            self.distributed = False
        self.cameras = self.create_cameras(n_cameras, sensor_accuracy, path[:, 0])

        self.target = Target("target", 0, 0, 0)
        self.last_timestamp = 0

        self.plotter = Plotter()

        self.start_world()

    def start_world(self):
        print("Starting world...")

        # Uncomment this to plot the path
        fig = plt.figure(figsize=(1, 1))
        ax = fig.add_subplot(projection='3d')
        line, = ax.plot(self.path[0, 0:1], self.path[1, 0:1], self.path[2, 0:1])

        # Setting the axes properties
        ax.set_xlim3d([-1.0, 500.0])
        ax.set_xlabel('X')

        ax.set_ylim3d([-1.0, 500.0])
        ax.set_ylabel('Y')

        ax.set_zlim3d([0.0, 300.0])
        ax.set_zlabel('Z')

        for i in range(0, self.n_frames):
            #self.start_time = time.monotonic()
            self.update_world(i, self.path, line)
            #duration = time.monotonic() - self.start_time
            #sleep_for = max(0,0.2 - duration)
            #time.sleep(sleep_for)

        #plt.show()

    def update_world(self, num, path, line):
        self.target.set_position(path[:, num])
        if num > 0:
            self.target.set_velocity(path[:, num] - path[:, num-1])#+ np.random.normal(0, 0.5, (3,)))
        if num > 1:
            self.target.set_acceleration(path[:, num] - 2*path[:, num-1] + path[:, num-2])

        line.set_data(path[:2, :num])
        line.set_3d_properties(path[2, :num])

        if num%5 == 0 and num != 0: # take measurement every 5 frames
            timestep = num - self.last_timestamp
            self.last_timestamp = num
            for cam in self.cameras:
                cam.take_position_measurement(self.target.get_position())
                cam.take_velocity_measurement(self.target.get_velocity())
                cam.take_acceleration_measurement(self.target.get_acceleration())

            # calculate average consensus
            if self.distributed == True:
                consensus_reached = False
                round = 0
                while consensus_reached == False:
                    print("Distributed consensus round " + str(round))
                    if round == 100:
                        print("Consensus not reached after 100 rounds")
                        exit()
                    round += 1
                    consensus_reached = True
                    for cam in self.cameras:
                        cam.send_messages(cam.avg_a, cam.avg_F)
                    for cam in self.cameras:
                        cam.calculate_average_consensus()
                    for cam in self.cameras:
                        for neighbor in cam.neighbors:
                            if not np.allclose(neighbor.avg_a, cam.avg_a, atol = 1e-08):
                                consensus_reached = False
                            if not np.allclose(neighbor.avg_F, cam.avg_F,  atol = 1e-08):
                                consensus_reached = False
                print("Consensus reached after " + str(round) + " rounds")

            for cam in self.cameras:
                filtered_pose, models = cam.imm.update_pose(timestep = timestep, distributed = self.distributed, a = cam.avg_a, F = cam.avg_F)
                cam.avg_a = None
                cam.avg_F = None

            self.plotter.update_plot(cam.get_measurements().flatten(), filtered_pose.flatten(), self.target.get_position(), self.target.get_velocity(), self.last_timestamp, models)

        plt.pause(0.01) # this updates both plots

        if num == self.n_frames-1:
            plt.close()
            exit()

    def create_cameras(self, n_cameras, sensor_accuracy, initial_pose):
        cameras = []
        for i in range(n_cameras):
            cameras.append(Camera("camera "+str(i), sensor_accuracy, np.array([0, 0, 0, 0, 0, 0]), initial_pose))
        # make following and precedenting cameras neighbors to each camera
        if n_cameras > 2:
            for index, cam in enumerate(cameras):
                if (index) == 0:
                    cam.neighbors.append(cameras[-1])
                else:
                    cam.neighbors.append(cameras[index-1])
                if (index) == n_cameras-1:
                    cam.neighbors.append(cameras[0])
                else:
                    cam.neighbors.append(cameras[index+1])
        if n_cameras == 2:
            cameras[0].neighbors.append(cameras[1])
            cameras[1].neighbors.append(cameras[0])

        return cameras

if __name__ == '__main__':
    n_cameras = 2
    path = np.zeros((3, 3000), dtype = float)
    for i in range(0, path.shape[1]):
        if i < 500:
            path[0, i] = (i/6)*i*10#x
            path[1, i] = (i/4)*i*10 #y
            path[2, i] = (i/8)*i*10 #z
        else:
            path[0, i] = (i/6)*5000 #x
            path[1, i] = (i/4)*5000 #y
            path[2, i] = (i/8)*5000 #z

    sensor_accuracy = 0.1

    Main(path = path, n_cameras = n_cameras, sensor_accuracy = sensor_accuracy, n_frames = path.shape[1])


#!/usr/bin/env python

# Pylint runs from a different directory, it's fine to import this way
# pylint: disable=W0403

import os
import shutil
import sys
import tempfile
import types
import unittest

def mock(mod_path):
    name_list = mod_path.split('.')
    child_name = name_list.pop()
    child_mod = sys.modules.get(mod_path, types.ModuleType(child_name))
    if len(name_list) == 0:  # child_name is left-most basic module
        if child_name not in sys.modules:
            sys.modules[child_name] = child_mod
        return sys.modules[child_name]
    else:
        # New or existing child becomes parent
        recurse_path = ".".join(name_list)
        parent_mod = mock(recurse_path)
        if not hasattr(sys.modules[recurse_path], child_name):
            setattr(parent_mod, child_name, child_mod)
            # full-name also points at child module
            sys.modules[mod_path] = child_mod
        return sys.modules[mod_path]

class FakeCmdResult(object):

    def __init__(self, **dargs):
        for key, val in dargs.items():
            setattr(self, key, val)

RUN_CACHE = []

def get_run_cache():
    global RUN_CACHE
    return RUN_CACHE

def kill_run_cache():
    global RUN_CACHE
    RUN_CACHE = []

def run(command, *args, **dargs):
    command = "%s" % (command)
    get_run_cache().append({'command': command, 'args': args, 'dargs': dargs})
    return FakeCmdResult(command=command.strip(),
                         stdout="""
REPOSITORY                    TAG                 IMAGE ID                                                           CREATED             VIRTUAL SIZE
192.168.122.245:5000/fedora   32                  0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404   5 weeks ago         387 MB
fedora                        32                  0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404   5 weeks ago         387 MB
fedora                        rawhide             0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404   5 weeks ago         387 MB
192.168.122.245:5000/fedora   latest              58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9   5 weeks ago         385.5 MB
fedora                        20                  58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9   5 weeks ago         385.5 MB
fedora                        heisenbug           58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9   5 weeks ago         385.5 MB
fedora                        latest              58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9   5 weeks ago         385.5 MB
""",
                         stderr=str(dargs),
                         exit_status=len(args),
                         duration=len(dargs))

# Mock module and mock function run in one command
setattr(mock('autotest.client.utils'), 'run', run)
setattr(mock('autotest.client.utils'), 'CmdResult', FakeCmdResult)
setattr(mock('autotest.client.test'), 'test', object)
setattr(mock('autotest.client.shared.error'), 'TestFail', Exception)
setattr(mock('autotest.client.shared.error'), 'CmdError', Exception)
setattr(mock('autotest.client.shared.error'), 'TestError', Exception)
setattr(mock('autotest.client.shared.error'), 'TestNAError', Exception)
setattr(mock('autotest.client.shared.error'), 'AutotestError', Exception)
setattr(mock('autotest.client.shared.version'), 'get_version',
        lambda: version.AUTOTESTVERSION)
mock('autotest.client.shared.base_job')
mock('autotest.client.shared.job')
mock('autotest.client.shared.utils')
mock('autotest.client.job')

import version

class ImageTestBase(unittest.TestCase):

    defaults = {}
    customs = {}
    config_section = "Foo/Bar/Baz"

    def _setup_inifile(self, cfgsection, cfgdir, cfgdict):
        osfd, filename = tempfile.mkstemp(suffix='.ini',
                                          dir=cfgdir)
        os.close(osfd)
        # ConfigSection will open again
        cfgfile = open(filename, 'wb')
        cfgfile.close()
        # ConfigDict forbids writing
        cfgsect = self.config.ConfigSection(None, cfgsection)
        for key, val in cfgdict.items():
            cfgsect.set(key, val)
        cfgsect.set('__example__', '')
        cfgsect.write(cfgfile)
        return filename

    def _setup_defaults(self):
        self.config.DEFAULTSFILE = self._setup_inifile('DEFAULTS',
                                                       self.config.CONFIGDEFAULT,
                                                       self.defaults)

    def _setup_customs(self):
        self._setup_inifile(self.config_section,
                            self.config.CONFIGCUSTOMS,
                            self.customs)

    def _make_fake_subtest(self):
        class FakeSubtestException(Exception):

            def __init__(fake_self, *args, **dargs):  # pylint: disable=E0213
                # Unittest magic pylint: disable=E1003
                super(FakeSubtestException, self).__init__()
                # Unittest magic pylint: enable=E1003

        class FakeSubtest(self.subtest.Subtest):
            version = "1.2.3"
            config_section = self.config_section
            iteration = 1
            iterations = 1

            def __init__(fake_self, *args, **dargs):  # pylint: disable=E0213
                config_parser = self.config.Config()
                fake_self.config = config_parser.get(self.config_section)
                for symbol in ('execute', 'setup', 'initialize', 'run_once',
                               'postprocess_iteration', 'postprocess',
                               'cleanup', 'failif',):
                    setattr(fake_self, symbol, FakeSubtestException)
                for symbol in ('logdebug', 'loginfo', 'logwarning', 'logerror'):
                    setattr(fake_self, symbol, lambda *_a, **_d: None)

        return FakeSubtest()

    def setUp(self):
        import config
        import images
        import subtest
        self.config = config
        self.images = images
        self.subtest = subtest
        self.config.CONFIGDEFAULT = tempfile.mkdtemp(self.__class__.__name__)
        self.config.CONFIGCUSTOMS = tempfile.mkdtemp(self.__class__.__name__)
        self._setup_defaults()
        self._setup_customs()
        self.fake_subtest = self._make_fake_subtest()

    def tearDown(self):
        shutil.rmtree(self.config.CONFIGDEFAULT, ignore_errors=True)
        shutil.rmtree(self.config.CONFIGCUSTOMS, ignore_errors=True)
        self.assertFalse(os.path.isdir(self.config.CONFIGDEFAULT))
        self.assertFalse(os.path.isdir(self.config.CONFIGCUSTOMS))
        del self.config
        del self.images
        del self.subtest

class DockerImageTestBasic(ImageTestBase):

    defaults = {'docker_path': '/foo/bar', 'docker_options': '--not_exist',
                'docker_timeout': 60.0, 'docker_repo_name': 'fedora',
                'docker_repo_tag': 'latest', 'docker_registry_host': '192.168.122.245:5000',
                'docker_registry_user': '', 'preserve_fqins': 'fedora:32, fedora:heisenbug'}
    customs = {}
    config_section = "Foo/Bar/Baz"

    def test_defaults_cli(self):
        d = self.images.DockerImages(self.fake_subtest)
        all_images = d.list_imgs()
        full_name = d.list_imgs_full_name()
        self.assertEqual(full_name,
                         ['192.168.122.245:5000/fedora:32',
                          'fedora:32',
                          'fedora:rawhide',
                          '192.168.122.245:5000/fedora:latest',
                          'fedora:20',
                          'fedora:heisenbug',
                          'fedora:latest'])

        ids = d.list_imgs_ids()
        self.assertEqual(ids,
                         ['58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9',
                          '0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404'])

        test_im = self.images.DockerImage("fedora",
                                          "latest",
                                          ("58394af373423902a1b97f209a31e3777"
                                           "932d9321ef10e64feaaa7b4df609cf9"),
                                          "5 weeks ago",
                                          "385.5 MB")
        self.assertTrue(test_im in all_images)
        self.assertEqual(all_images.index(test_im), 6)

        images = str(d.list_imgs_with_full_name(full_name[2]))
        self.assertEqual(images,
                         "[DockerImage(full_name:fedora:rawhide LONG_ID:0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404 CREATED:5 weeks ago SIZE:387 MB)]")

        images = str(d.list_imgs_with_full_name("fedora:latest"))
        exp = ("[DockerImage(full_name:192.168.122.245:5000/fedora:latest LONG_ID:58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9 CREATED:5 weeks ago SIZE:385.5 MB),"
               " DockerImage(full_name:fedora:latest LONG_ID:58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9 CREATED:5 weeks ago SIZE:385.5 MB)]")
        self.assertEqual(images, exp)

        act = self.images.DockerImages.filter_list_full_name(all_images,
                                                             "fedora:"
                                                             "latest")
        self.assertEqual(str(act), exp)

        images = str(d.list_imgs_with_full_name_components(repo_addr="192.168.122.245:5000"))
        exp = ("[DockerImage(full_name:192.168.122.245:5000/fedora:32 LONG_ID:0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404 CREATED:5 weeks ago SIZE:387 MB),"
               " DockerImage(full_name:192.168.122.245:5000/fedora:latest LONG_ID:58394af373423902a1b97f209a31e3777932d9321ef10e64feaaa7b4df609cf9 CREATED:5 weeks ago SIZE:385.5 MB)]")
        self.assertEqual(images, exp)
        act = self.images.DockerImages.filter_list_by_components(all_images,
                                                                 repo_addr='192.168.122.245:5000')
        self.assertEqual(str(act), exp)

        images = str(d.list_imgs_with_full_name_components(repo_addr="192.168.122.245:5000",
                                                           tag="32"))
        self.assertEqual(images,
                         "[DockerImage(full_name:192.168.122.245:5000/fedora:32 LONG_ID:0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404 CREATED:5 weeks ago SIZE:387 MB)]")

        images = d.list_imgs_with_image_id(all_images[2].long_id)
        self.assertEqual(str(images),
                         "[DockerImage(full_name:192.168.122.245:5000/fedora:32 LONG_ID:0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404 CREATED:5 weeks ago SIZE:387 MB),"
                         " DockerImage(full_name:fedora:32 LONG_ID:0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404 CREATED:5 weeks ago SIZE:387 MB),"
                         " DockerImage(full_name:fedora:rawhide LONG_ID:0d20aec6529d5d396b195182c0eaa82bfe014c3e82ab390203ed56a774d2c404 CREATED:5 weeks ago SIZE:387 MB)]",)

    def test_name_comparison_dockerimage(self):
        di_ref = self.images.DockerImage("fedora_repo", "last_tag",
                                         ("0d20aec6529d5d396b195182c0eaa82bfe0"
                                          "14c3e82ab390203ed56a774d2c404"),
                                         "dd",
                                         "50 MB",
                                         "fedora_addr", "user_user")

        di_ref2 = self.images.DockerImage("fedora_repo", "last_tag",
                                          ("0d20aec6529d5d396b195182c0eaa82bfe0"
                                           "14c3e82ab390203ed56a774d2c404"),
                                          "dd",
                                          "50 MB",
                                          "fedora_addr", "user_user")

        di_dif = self.images.DockerImage("fedora3_repo", "last_tag",
                                         ("0d20aec6529d5d396b195182c0eaa82bfe0"
                                          "14c3e82ab390203ed56a774d2c404"),
                                         "dd",
                                         "50 MB",
                                         "fedora_addr", "user_user")

        full_name = "fedora_addr/user_user/fedora_repo:last_tag"
        (repo, tag, repo_addr, user) = self.images.DockerImage.split_to_component(
            full_name)
        self.assertRaises(self.images.DockerFullNameFormatError,
                          self.images.DockerImage.split_to_component, ["dd/"])
        self.assertRaises(self.images.DockerFullNameFormatError,
                          self.images.DockerImage.split_to_component,
                          ["dd/aa/ss/ss"])
        self.assertRaises(self.images.DockerFullNameFormatError,
                          self.images.DockerImage.split_to_component,
                          ["dd/aa:aa/ss"])

        self.assertRaises(self.images.DockerFullNameFormatError,
                          self.images.DockerImage.split_to_component,
                          ["<none>:<none>"])

        self.assertEqual(self.images.DockerImage.full_name_from_component(
            repo, tag, repo_addr, user), full_name)

        self.assertEqual((repo, tag, repo_addr, user),
                         ("fedora_repo", "last_tag", "fedora_addr",
                          "user_user"))
        self.assertTrue(di_ref.cmp_full_name_with_component(repo, tag, repo_addr, user))
        self.assertFalse(di_ref.cmp_full_name_with_component(repo, tag, repo_addr=repo_addr))
        self.assertTrue(di_ref.cmp_greedy(repo, repo_addr=repo_addr, user=user))
        self.assertTrue(di_ref.cmp_greedy(repo))
        self.assertTrue(di_ref.cmp_greedy(repo, repo_addr=repo_addr))
        self.assertTrue(di_ref.cmp_greedy(repo, user=user))
        self.assertTrue(di_ref.cmp_greedy(repo_addr=repo_addr))
        self.assertFalse(di_ref.cmp_greedy(repo=repo_addr))
        self.assertTrue(di_ref == di_ref2)
        self.assertFalse(di_ref == di_dif)

        self.assertTrue(self.images.DockerImage(
            "fedora_addr:44/user_user/"
            "fedora_repo:last_tag", None,
            "0d20aec6529d5d396b195182c0eaa82bfe0"
            "14c3e82ab390203ed56a774d2c404",
            "dd",
            "50 MB",
            None,
            None).cmp_full_name_with_component("fedora_repo",
                                               "last_tag",
                                               "fedora_addr:44",
                                               "user_user"))

        self.assertTrue(di_ref.cmp_id(di_ref2.short_id))
        self.assertTrue(di_ref.cmp_id(di_ref2.long_id))
        self.assertFalse(di_ref.cmp_id('11111111111'))
        self.assertFalse(di_ref.cmp_id('1111111111111111111111111111111111'))

        self.assertTrue(di_ref.cmp_full_name(di_ref2.full_name))
        self.assertFalse(di_ref.cmp_full_name(di_dif.full_name))

        config = {'docker_repo_name': 'fedora', 'docker_repo_tag': '',
                  'docker_registry_user': '', 'docker_registry_host': ''}
        act = self.images.DockerImage.full_name_from_defaults(config)
        self.assertEqual(act, 'fedora')

    def test_docker_110_id_parsing(self):
        """
        Docker 1.10 image and container IDs include a "sha256:" prefix;
        make sure our parsing code strips off the prefix but still returns
        a valid short_id
        """
        id_components = ["sha256:", "0123456789ab",    # <-- 12-char short id
                         "9dcfe46a8f465344284a3538cd"  # <-- + 52 chars = 64
                         "6b0beeb18b4f249619fec4a659"]
        long_id = "".join(id_components)
        di = self.images.DockerImage("myrepo", "a", long_id, "bb", "cc", "dd")
        self.assertEqual(di.long_id, long_id)
        self.assertEqual(di.short_id, id_components[1])

    def test_bastard_repo(self):
        test = "bAsTaRd-rEPo.lOcAl_hOsT:1073741824/.b+o-F_H./a.s-d_f:F.d-S_a"
        DI = self.images.DockerImage
        repo, tag, addr, user = DI.split_to_component(test)
        di = DI(repo, tag, '0' * 64, None, None, addr, user)
        self.assertEqual(di.full_name, test)

    def test_user_no_addr(self):
        test = "user/repo:tag"
        DI = self.images.DockerImage
        repo, tag, addr, user = DI.split_to_component(test)
        self.assertEqual(repo, 'repo')
        self.assertEqual(tag, 'tag')
        self.assertEqual(user, 'user')
        self.assertEqual(addr, None)

    def test_addr_no_user(self):
        test = "address:1234/repo:tag"
        DI = self.images.DockerImage
        repo, tag, addr, user = DI.split_to_component(test)
        self.assertEqual(repo, 'repo')
        self.assertEqual(tag, 'tag')
        self.assertEqual(user, None)
        self.assertEqual(addr, "address:1234")

    def test_empty_tag(self):
        """
        Empty tag names seen in docker-1.9 output of 'docker events'
        in some tag actions. docker-1.10 seems to forbid trailing colon.
        """
        test = "myname:"   # note the trailing colon with no actual tag name
        DI = self.images.DockerImage
        repo, tag, addr, user = DI.split_to_component(test)
        self.assertEqual(repo, 'myname')
        self.assertEqual(tag, '')
        self.assertEqual(user, None)
        self.assertEqual(addr, None)

    def test_remove_cli(self):
        d = self.images.DockerImages(self.fake_subtest, 1.0, True)
        self.assertEqual(d.remove_image_by_id('123456789012').command,
                         "/foo/bar rmi 123456789012")
        self.assertEqual(d.remove_image_by_id('123456789012').command,
                         "/foo/bar rmi 123456789012")
        self.assertEqual(d.remove_image_by_full_name('fedora:latest').command,
                         "/foo/bar rmi fedora:latest")
        img_id = ("0d20aec6529d5d396b195182c0eaa82b"
                  "fe014c3e82ab390203ed56a774d2c404")
        image_obj = self.images.DockerImage("fedora_repo", "last_tag",
                                            img_id,
                                            "dd",
                                            "50 MB",
                                            None, "user_user")
        self.assertEqual(d.remove_image_by_image_obj(image_obj).command,
                         "/foo/bar rmi user_user/fedora_repo:last_tag")

        image_notag = self.images.DockerImage("fedora_repo", "<none>",
                                              img_id,
                                              "dd",
                                              "50 MB",
                                              None, "user_user")
        self.assertEqual(d.remove_image_by_image_obj(image_notag).command,
                         "/foo/bar rmi %s" % img_id)

    def test_docker_images_lowlevel(self):
        images = self.images.DockerImages(self.fake_subtest)

        self.assertEqual(images.docker_cmd("command_pass").command,
                         '/foo/bar command_pass')

    def test_clean_all(self):
        d = self.images.DockerImages(self.fake_subtest)
        names = set(d.list_imgs_full_name())
        # staticly set in fake config, REMOVE names to be preserved
        preserve = set(['fedora:heisenbug',
                        'fedora:32',
                        '192.168.122.245:5000/fedora:latest'])
        expected = names - preserve
        kill_run_cache()
        d.clean_all(names)
        pfx = '/foo/bar rmi --force '
        cutlen = len(pfx)
        cleaned_names = set()
        for item in get_run_cache():
            command = item['command']
            self.assertTrue(command.startswith(pfx))
            cleaned_names.add(command[cutlen:])
        # no preserved names should be in either list
        self.assertEqual(cleaned_names, expected)
        self.assertTrue(cleaned_names.isdisjoint(preserve))

if __name__ == '__main__':
    unittest.main()


from collections import deque
from bisect import bisect_left

class Solution:
    def swimInWater(self, grid: list[list[int]]) -> int:
        N = len(grid)
        seen = [[False] * N for _ in range(N)]
        seen[0][0] = True
        t = 0
        front = deque([(0, 0)])
        values = sorted([n for row in grid for n in row])
        i = bisect_left(values, max(grid[0][0], grid[-1][-1]))
        for t in values[i:]:
            next_front = deque()
            while front:
                r, c = front.pop()
                if t >= grid[r][c]:
                    for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
                        nr, nc = r + dr, c + dc
                        if 0 <= nr < N and 0 <= nc < N and not seen[nr][nc] and t >= grid[nr][nc]:
                            if nr == nc == N - 1:
                                return t

                            seen[nr][nc] = True
                            front.append((nr, nc))

                for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
                    nr, nc = r + dr, c + dc
                    if 0 <= nr < N and 0 <= nc < N and not seen[nr][nc]:
                        next_front.append((r, c))
                        break

            front = next_front


class Solution:
    def select(self, arr, i):
        smallest = arr[0]
        smallest_index = 0
        for j in range(i,len(arr)):
            if arr[j] < smallest:
                smallest = arr[j]
                smallest_index= j
        return smallest_index

    def selectionSort(self, arr,n):
        sorted_array = []
        for i in range(len(arr)):
            smallest = self.select(arr,n-1)
            sorted_array.append(arr.pop(smallest))
        return sorted_array


'''
Assignment 07
Nicole Wong
Date Created: 22 Aug 2022
Last Modified: 23 Aug 2022
'''
import pickle

# data -----------------------
one = input('enter your first integer: ')
two = input('now enter your second integer: ')

# processing -----------------
try:
    quot = int(one) / int(two)
    #pickling-----------------
    fil = open('pickle.dat', 'ab')
    pickle.dump(quot, fil)
    fil.close()
    #end pickle---------------
    print(quot)
except Exception as e:
    print('there seems to be an error')
    print(e)
    print(type(e))
    print(e.__doc__)
    print(e.__str__())


from flask import Flask, render_template, request
import os
from random import randint
app = Flask(__name__)

sk = os.environ.get('SECRET_KEY')
URL = os.environ.get('HEROKU_URL', 'http://127.0.0.1:5000/')

app_settings = {
    'SECRET_KEY': sk,
    "TEMPLATES_AUTO_RELOAD": True,
    'SQLALCHEMY_DATABASE_URI': os.environ.get(
                                            'DATABASE_URL',
                                            'sqlite:///data.db'
                                            ),
    'SQLALCHEMY_TRACK_MODIFICATIONS': False,
    'SESSION_TYPE': 'filesystem',
    'PERMANENT_SESSION_LIFETIME': (1 * 60 * 60)
}

app.config.update(app_settings)

@app.route("/", methods=["GET","POST"])
def insult():
    if request.method == "GET":
        A = [line.rstrip('\n') for line in open('a.txt')]
        B = [line.rstrip('\n') for line in open('b.txt')]
        C = [line.rstrip('\n') for line in open('c.txt')]

        string = "{} {} {}".format(
                                    A[randint(0, len(A)-1)],
                                    B[randint(0, len(B)-1)],
                                    C[randint(0, len(C)-1)]
                                    )

      #  return render_template('index.html', top="Thou", bottom=string)
    else:
        string = request.form.get('insult')
    return render_template('index.html', top="Thou", bottom=string)

if __name__ == "__main__":
    app.run()


import caffe_pb2 as cpb
from google.protobuf import text_format
from collections import defaultdict
import tensorflow as tf
import numpy as np
from operator import mul
import logging

class Network:
    # Public functions.
    def __init__(self, proto, inputs=dict(), input_format="NC*"):
        """Create network using structure from Caffe proto file.

        Args:
            proto: The plaintext network prototxt file on disk.
                Type `str`.
            inputs: Dictionary mapping input variable `top` strings to
                Tensors. Use this to connect Tensors directly to the
                network, instead of using `tf.placeholder` type variables
                that are created by default for all layers of type
                `Input`. `tf.placeholder` instances will be created for
                any `top` values missing in the dictionary. Defaults to
                empty `dict`.
            input_format: The order of inputs specified in the prototxt
                file. The TensorFlow program is expected to provide inputs
                in the order `N*C`, where the dimension `N` denotes batch,
                `C` denotes the number of channels, and `*` denotes the
                rest of the dimensions. Valid arguments are `N*C`, `NC*`,
                `*NC`, `CN*`, `C*N`, and `*CN`. The network will permute
                the order of dimensions specified in prototxt file for the
                InputParameter in the order `N*C`. Defaults to `NC*`.
        """
        # Layer names mapped to outputs.
        self._name_outputs = defaultdict(list)
        # Layer top names mapped to outputs.
        self._top_outputs = defaultdict(list)
        # Layer name to list of variables.
        self._name_vars = defaultdict(list)
        # Protobuf LayerParameter messages for each layer, loaded in the
        # order presented in the proto file.
        self._layer_params = []
        # List of variables.
        self._vars = []
        # List of trainable variables.
        self._tvars = []
        # Layer name to (name, var) mapping.
        self._layer_vars = defaultdict(list)
        # The NetParameter protobuf message.
        self._net_param = cpb.NetParameter()
        self._input_format = input_format
        self._inputs = dict(inputs)

        # Load and parse the prototxt file.
        self._parse_proto(proto)
        # Create network, based on the parse.
        self._make_network()

    def load(self, model, sess):
        """Load network variables from NumPy file on disk.

        Args:
            model: The path to the model file on disk.
            sess: The TensorFlow session.
        """
        self._load(model, sess)

    def layer_output(self, layer_name):
        if layer_name not in self._top_outputs:
            raise ValueError("`%s` not defined in network" % layer_name)
        return self._top(layer_name)

    def save(self, path, sess):
        """Save the network weights to disk.

        Saved as compatible NumPy pickle.

        Args:
            path: The path of save file on disk.
            sess: The TensorFlow session.
        """
        m = {}
        for layer_name, name_var_list in self._layer_vars.iteritems():
            m[layer_name] = {}
            for var_name, var in name_var_list:
                m[layer_name][var_name] = var.eval(sess)
        np.save(path, m)

    @property
    def vars(self):
        """The network variables.

        Returns:
            List of all network TensorFlow variables.
        """
        return self._vars

    @property
    def tvars(self):
        """Trainable network variables.

        Returns:
            List of all trainable network TensorFlow variables.
        """
        return self._tvars

    # Layer functions.
    def _add_Input(self, lp):
        """Add input layer placeholder to network.

        Does not support undefined shapes.

        Args:
            lp: Caffe LayerParameter protobuf message
                with type "Input".
        """
        def reorder(dims):
            # Figure out the permutation for the shape to convert to N*C order.
            idx_C = self._input_format.index("C")
            if idx_C == 2:
                idx_C = -1
            idx_N = self._input_format.index("N")
            if idx_N == 2:
                idx_N = -1
            # Reorder.
            new_dim = [dims[idx_N]]
            temp = list(dims)
            temp = [i for j, i in enumerate(dims) if j not in [idx_C, idx_N]]
            new_dim += (temp + [dims[idx_C]])
            return new_dim

        # In case of multiple outputs but one shape specification, repeat.
        if len(lp.top) != len(lp.input_param.shape):
            shapes = lp.input_param.shape * len(lp.top)
        else:
            shapes = lp.input_param.shape
        for (top, shape) in zip(lp.top, shapes):
            # Reorder shape to get `N*C` ordering.
            shape_reordered = tuple(map(int, reorder(shape.dim)))
            # Check if inputs have been supplied by user.
            if top in self._inputs:
                input = self._inputs[top]
                # Make sure user-specified Tensor shape is compatible
                # with proto shape specifications.
                if not input.get_shape().is_compatible_with(shape_reordered):
                    raise ValueError("Shape of supplied `%s` input incompatible"
                                     " with proto specifications" % top)
                output = input
            else:
                output = tf.placeholder(tf.float32, shape_reordered, name=top)
            self._add_output_to_lists(output, lp.name, top)

    def _add_Convolution(self, lp):
        """Add convolution layer to network.

        Note that only 2-D convolutions are currently supported.

        Args:
            lp: Caffe LayerParameter protobuf message
                with type "Convolution".
        """
        filter_size, padding, strides, out_channels = \
            self._parse_ConvolutionParameter(lp.convolution_param)
        # Get the number of chanels in input. Ensure that the number
        # is consistent for each of the bottom layers.
        in_channels = 0
        for bottom in lp.bottom:
            if in_channels == 0:
                in_channels = self._top(bottom).get_shape()[-1]
            if in_channels != self._top(bottom).get_shape()[-1]:
                raise ValueError("bottom layers for %s must have same "
                                 "channel count" % lp.name)
        # Create convolution filter weights.
        with tf.variable_scope(lp.name):
            weights = self._make_vars(
                "weights",
                filter_size + [in_channels, out_channels],
                self._is_training(lp.phase),
                lp.name,
                self._FillerParameter_to_initializer(
                    lp.convolution_param.weight_filler))
            if lp.convolution_param.bias_term:
                biases = self._make_vars(
                    "biases",
                    [out_channels],
                    self._is_training(lp.phase),
                    lp.name,
                    self._FillerParameter_to_initializer(
                        lp.convolution_param.bias_filler))
        # For each bottom layer, create convolutional output
        # feature map.
        for (top, bottom) in zip(lp.top, lp.bottom):
            input = self._top(bottom)
            # Perform 2D convolution.
            with tf.name_scope(self._name_scope_builder(top)):
                # Pad input, if necessary.
                padded_input = tf.pad(input, padding)
                # Convolve.
                output = tf.nn.conv2d(padded_input, weights, strides, "VALID")
                # Add biases, if needed.
                if lp.convolution_param.bias_term:
                    output = tf.nn.bias_add(output, biases)
            # Add to lists.
            self._add_output_to_lists(output, lp.name, top)

    def _add_ReLU(self, lp):
        if len(lp.top) != len(lp.bottom):
            raise ValueError(
                "ReLU layers (%s) must have the same number of "
                "top and bottom layers" % lp.name)
        for top, bottom in zip(lp.top, lp.bottom):
            with tf.name_scope(self._name_scope_builder(top)):
                output = tf.nn.relu(self._top(bottom), name=lp.name)
            self._add_output_to_lists(output, lp.name, top)

    def _add_Pooling(self, lp):
        if len(lp.top) != len(lp.bottom):
            raise ValueError(
                "Pooling layers (%s) must have the same number of "
                "top and bottom layers" % lp.name)

        for top, bottom in zip(lp.top, lp.bottom):
            pp = lp.pooling_param
            # Recover the pooling parameters.
            input = self._top(bottom)
            ksize, padding, strides = self._parse_PoolingParameter(
                pp, input)
            with tf.name_scope(self._name_scope_builder(lp.name)):
                # Pad, if necessary.
                if padding is not None:
                    input = tf.pad(input, padding)
                if pp.pool == cpb.PoolingParameter.MAX:
                    output = tf.nn.max_pool(input, ksize, strides, "VALID")
                elif pp.pool == cpb.PoolingParameter.AVE:
                    output = tf.nn.avg_pool(input, ksize, strides, "VALID")
                else:
                    raise NotImplementedError(
                        "Unsupported pooling: %s" % pp.pool)
            self._add_output_to_lists(output, lp.name, top)

    def _add_InnerProduct(self, lp):
        # TODO(agharwal): Add support for multiple top and bottom layers.
        #                 Users should use Concat and Reshape layers to
        #                 concatenate multiple bottom layers.
        if len(lp.top) > 1:
            raise ValueError(
                "InnerProduct layers (%s) must have exactly one "
                "top layer" % lp.name)

        inputs = []
        with tf.name_scope(self._name_scope_builder(lp.name)):
            for bottom in lp.bottom:
                input = self._top(bottom)
                shape = input.get_shape().as_list()
                # Get the flattened shape, excluding the index
                # correspnding to batch.
                ndims_flat = reduce(mul, shape[1:], 1)  # NOQA
                inputs.append(tf.reshape(input, [shape[0], ndims_flat]))
            # Concatenate flattened inputs.
            if len(inputs) > 1:
                input = tf.concat(1, inputs)
            else:
                input = inputs[0]

        # Prepare weights and biases.
        ipp = lp.inner_product_param
        input_ndims = input.get_shape().as_list()[-1]
        with tf.variable_scope(lp.name):
            weights = self._make_vars(
                "weights",
                [input_ndims, ipp.num_output],
                self._is_training(lp.phase),
                lp.name,
                self._FillerParameter_to_initializer(
                    ipp.weight_filler))
            if ipp.bias_term:
                biases = self._make_vars(
                    "biases",
                    [ipp.num_output],
                    self._is_training(lp.phase),
                    lp.name,
                    self._FillerParameter_to_initializer(
                        ipp.bias_filler))

        # Compute output.
        with tf.name_scope(self._name_scope_builder(lp.name)):
            if ipp.bias_term:
                output = tf.nn.xw_plus_b(input, weights, biases)
            else:
                output = input * weights
        self._add_output_to_lists(output, lp.name, lp.top[0])

    def _add_Dropout(self, lp):
        for top, bottom in zip(lp.top, lp.bottom):
            with tf.name_scope(self._name_scope_builder(top)):
                output = tf.nn.dropout(self._top(bottom),
                                       1.0 - lp.dropout_param.dropout_ratio,
                                       name=lp.name)
            self._add_output_to_lists(output, lp.name, top)

    def _add_Softmax(self, lp):
        if len(lp.top) != len(lp.bottom):
            raise ValueError(
                "Softmax layers (%s) must have the same number of "
                "top and bottom layers" % lp.name)
        for top, bottom in zip(lp.top, lp.bottom):
            with tf.name_scope(self._name_scope_builder(top)):
                output = tf.nn.softmax(self._top(bottom))
            self._add_output_to_lists(output, lp.name, top)

    def _add_Reshape(self, lp):
        # FIXME(agharwal): Possible bug since this is not compensating
        #                  for order of axes in input.
        for top, bottom in zip(lp.top, lp.bottom):
            with tf.name_scope(self._name_scope_builder(top)):
                output = tf.reshape(
                    self._top(bottom),
                    self._ReshapeParameter_to_shape(
                        lp.reshape_param,
                        self._top(bottom).get_shape().as_list()),
                    name=lp.name)
            self._add_output_to_lists(output, lp.name, top)

    def _add_Sigmoid(self, lp):
        if len(lp.top) != len(lp.bottom):
            raise ValueError(
                "Sigmoid layers (%s) must have the same number of "
                "top and bottom layers" % lp.name)
        for top, bottom in zip(lp.top, lp.bottom):
            with tf.name_scope(self._name_scope_builder(top)):
                output = tf.sigmoid(self._top(bottom), name=lp.name)
            self._add_output_to_lists(output, lp.name, top)

    def _add_Concat(self, lp):
        if len(lp.top) != 1:
            raise ValueError(
                "Concat layers (%s) must have exactly one "
                "top layer" % lp.name)
        axis = self._parse_ConcatParameter(lp.concat_param)
        top = lp.top[0]
        bottom_tensors = [self._top(name) for name in lp.bottom]
        with tf.name_scope(self._name_scope_builder(top)):
            output = tf.concat(bottom_tensors, axis, name=lp.name)
        self._add_output_to_lists(output, lp.name, top)

    def _add_Slice(self, lp):
        if len(lp.bottom) != 1:
            raise ValueError(
                "Concat layers (%s) must have exactly one "
                "top layer" % lp.name)
        bottom_tensor = self._top(lp.bottom[0])
        axis, slice_points = self._parse_SliceParameter(lp.slice_param)
        # Recover the total number of axes, and the number of
        # dimensions along `axis`.
        bottom_shape = bottom_tensor.get_shape().as_list()
        num_axes = len(bottom_shape)
        top_layer_names = lp.top
        # Ensure that the number of slices match the number of top layers.
        if len(top_layer_names) != (len(slice_points) + 1):
            raise ValueError(
                "Slice layers (%s) must have exactly one more"
                "top layer than slice points" % lp.name)
        # Make sure the axis index is within range. Note that we need to take
        # into account negative indices too.
        if axis < -num_axes or axis >= num_axes:
            raise ValueError(
                "Slice layer (%s) axis index mismatch %d for %d axes" %
                (lp.name, axis, num_axes))
        start_indices = [0] + slice_points
        end_indices = slice_points + [bottom_shape[axis]]
        for (start_idx, end_idx, top) in zip(start_indices,
                                             end_indices,
                                             top_layer_names):
            begin = [0] * num_axes
            size = bottom_shape
            begin[axis] = start_idx
            size[axis] = end_idx - start_idx
            with tf.name_scope(self._name_scope_builder(top)):
                output = tf.slice(bottom_tensor, begin, size, name=lp.name)
                self._add_output_to_lists(output, lp.name, top)

    # Helper functions.
    def _make_vars(self, name, shape, trainable,
                   layer_name=None, initializer=None):
        """Create variables of given `name` and `shape`.

        Args:
            name: Variable name. Type `str`.
            shape: A 1-D `Tensor` of type `int32` representing
                the shape of the variables.
            trainable: `True` if variables are trainable.
            layer_name: Name of layer for which variables will be used.
                Type `str`. Ignored if set to `None`. Defaults to `None`.
        """
        vars = tf.get_variable(name, shape,
                               trainable=trainable,
                               initializer=initializer)
        self._vars.append(vars)
        if trainable:
            self._tvars.append(vars)
        if layer_name is not None:
            self._layer_vars[layer_name].append((name, vars))
        return vars

    def _add_output_to_lists(self, output, name, top):
        self._top_outputs[top].append(output)
        self._name_outputs[name].append(
            (top, len(self._top_outputs[top]) - 1, output))

    def _parse_proto(self, proto):
        """Parse the proto file to create network graph

        Args:
            proto: Path of prototxt plaintext file on disk.
                Type `str`.
        """
        with open(proto, "r") as f:
            text_format.Merge(f.read(), self._net_param)
            for layer_param in self._net_param.layer:
                self._layer_params.append(layer_param)

    def _top(self, top):
        return self._top_outputs[top][-1]

    def _make_network(self):
        """Construct the network using the parsed proto file."""
        for layer_param in self._layer_params:
            # Does layer need to be skipped?
            if not self._include_layer(layer_param):
                logging.info("Skipping: %s", layer_param.name)
                continue
            if layer_param.type == "Input":
                self._add_Input(layer_param)
            elif layer_param.type == "Convolution":
                self._add_Convolution(layer_param)
            elif layer_param.type == "ReLU":
                self._add_ReLU(layer_param)
            elif layer_param.type == "Pooling":
                self._add_Pooling(layer_param)
            elif layer_param.type == "InnerProduct":
                self._add_InnerProduct(layer_param)
            elif layer_param.type == "Dropout":
                self._add_Dropout(layer_param)
            elif layer_param.type == "Softmax":
                self._add_Softmax(layer_param)
            elif layer_param.type == "Reshape":
                self._add_Reshape(layer_param)
            elif layer_param.type == "Sigmoid":
                self._add_Sigmoid(layer_param)
            elif layer_param.type == "Concat":
                self._add_Concat(layer_param)
            elif layer_param.type == "Slice":
                self._add_Slice(layer_param)
            else:
                raise NotImplementedError(
                    "Contact the bugger who wrote this.")

    def _is_training(self, phase):
        """Global phase overrides local phase in case the network is
           in test phase; local phase is used otherwise.
        """
        return self._net_param.state.phase == cpb.TRAIN and phase == cpb.TRAIN

    def _parse_ConvolutionParameter(self, cp, dims=2):
        out_channels = cp.num_output

        # Infer input dimensions.
        if len(cp.kernel_size) == 1:
            filter_size = [cp.kernel_size[0]] * dims
        else:
            filter_size = cp.kernel_size

        # Infer padding width.
        if not cp.pad:
            padding = [0] * dims
        elif len(cp.pad) == 1:
            padding = [cp.pad[0]] * dims
        else:
            padding = cp.pad
        # TensorFlow allows specification of different paddings
        # at both ends of an input axis, but Caffe assumes it would
        # be the same. Create a TF compatible output.
        padding = [[0, 0]] + [[p, p] for p in padding] + [[0, 0]]

        # Infer stride.
        if len(cp.stride) == 0:
            strides = [1] * dims
        elif len(cp.stride) == 1:
            strides = [cp.stride[0]] * dims
        else:
            strides = cp.stride
        strides = [1] + strides + [1]

        return filter_size, padding, strides, out_channels

    def _parse_PoolingParameter(self, pp, bottom):
        # Input Tensor dimensions, ignoring batch size and number of channels.
        ndims = len(bottom.get_shape()) - 2
        # If pooling is global.
        if pp.global_pooling:
            # Kernel size equals to full extent of feature map.
            ksize = bottom.get_shape().as_list()[1:-1]
            # No padding.
            padding = None
            # Fixed stride.
            strides = [1] * (ndims + 2)
            return ksize, padding, strides

        # Figure out kernel size.
        # a) kernel_h and kernel_w are set. Feature dimensions
        #    must be equal to 2.
        if pp.kernel_size is None:
            if ndims != 2:
                raise ValueError(
                    "Either kernel_size or both kernel_x and kernel_y"
                    " must be set.")
            ksize = [pp.kernel_h, pp.kernel_w]
        # b) kernel_size is set
        else:
            ksize = [pp.kernel_size] * ndims
        # Adjust for TensorFlow specs. Kernel should be 1 layer
        # wide for batch and channel Tensor indices.
        ksize = [1] + ksize + [1]

        # Figure out stride.
        # a) stride is set
        if pp.stride > 0:
            strides = [pp.stride] * ndims
        # b) stide_h and stride_w are set (we assume both).
        else:
            strides = [pp.stride_h, pp.stride_w]
        # Adjust for TensorFlow specs. Max should be computed
        # over each feature map for each instance in the batch.
        strides = [1] + strides + [1]

        # Infer padding width. No padding along batch and channel
        # indices
        if pp.pad > 0:
            padding = [0] + ([pp.pad] * ndims) + [0]
        elif pp.pad_h > 0 or pp.pad_w > 0:
            padding = [0, pp.pad_h, pp.pad_w, 0]
        else:
            padding = None
        # TensorFlow allows specification of different paddings
        # at both ends of an input axis, but Caffe assumes it would
        # be the same. Create a TF compatible output.
        if padding is not None:
            padding = [[p, p] for p in padding]

        return ksize, padding, strides

    def _parse_ConcatParameter(self, cp):
        # Return the axis index.
        return cp.axis

    def _parse_SliceParameter(self, sp):
        # Return the axis index.
        axis = sp.axis
        slice_points = [slice_point for slice_point in sp.slice_point]
        return axis, slice_points

    def _load(self, model, sess):
        # Load data from file.
        data_dict = np.load(model).item()
        # For each layer type, load data.
        for layer_proto in self._layer_params:
            layer_name = layer_proto.name
            # Make sure layer has variables.
            if not self._has_vars(layer_proto):
                continue
            # It is possible that the file is missing data
            # for some layers. Warn the user.
            if layer_name not in data_dict:
                logging.warning("No variables found for layer: %s",
                                layer_name)
                continue
            # Load each variable in layer.
            for (name, vars) in self._layer_vars[layer_name]:
                # Still possible for stuff to be missing. This is unusual.
                # Warn the poor soul.
                if name not in data_dict[layer_name]:
                    logging.warning("Variables %s not found for layer %s",
                                    name,
                                    layer_name)
                    continue
                try:
                    # Time to assign values.
                    sess.run(vars.assign(data_dict[layer_name][name]))
                except ValueError as e:
                    # Something is very wrong. User likely has the wrong
                    # model file.
                    logging.warning("Dimension mismatch for variables"
                                    "%s in layer %s. %s.",
                                    name,
                                    layer_name,
                                    str(e))

    def _has_vars(self, layer_proto):
        return layer_proto.name in self._layer_vars

    def _satisfied_NSR(self, nsr):
        """Check if `NetStateRule` is satisfied based on `NetState`.

        Args:
            nsr: `NetStateRule` protobuf message.

        Returns:
            Boolean `True` iff `NetState` agrees with `nsr`.
        """
        np_state = self._net_param.state
        # Phase check.
        if nsr.phase != np_state.phase:
            return False

        # Level check.
        if np_state.level < nsr.min_level or np_state.level > nsr.max_level:
            return False

        # Stage check.
        stages = set(np_state.stage)
        for stage in nsr.stage:
            # Missing stage? Exclude.
            if stage not in stages:
                return False
        for not_stage in nsr.not_stage:
            # Stage should be there, but found? Exclude.
            if not_stage in stages:
                return False

        return True

    def _include_layer(self, lp):
        """Check if layer should be included in the network.

        Based on `NetState` of the `NetParameter`.

        Args:
            lp: `LayerParameter` protobuf message.

        Returns:
            Boolean `True` iff layer should be included.
        """
        # Default behavior.
        default = True
        # If inclusion rules defined, exclude by default.
        if lp.include:
            default = False
        # If exclusion rules defined, include by default.
        if lp.exclude:
            default = True

        # Include?
        for include_NSR in lp.include:
            if self._satisfied_NSR(include_NSR):
                return True
        # Exclude?
        for exclude_NSR in lp.exclude:
            if self._satisfied_NSR(exclude_NSR):
                return False
        return default

    def _FillerParameter_to_initializer(self, fp):
        """FillerParameter protobuf message to TensorFlow initializer.

        It is the caller's responsibility to ensure appropriate and
        consistent values are assigned to the FillerParameter fields.

        Args:
            fp: Protobuf message of type `FillerParameter`

        Returns:
            A TensorFlow initializer op.
        """
        if fp.type == "constant":
            return tf.constant_initializer(fp.value)
        elif fp.type == "uniform":
            return tf.random_uniform_initializer(fp.min, fp.max)
        elif fp.type == "gaussian":
            return tf.random_normal_initializer(fp.mean, fp.std)
        else:
            raise NotImplementedError("Initializer type %s not implemented" %
                                      fp.type)

    def _ReshapeParameter_to_shape(self, rp, bottom_shape):
        """ReshapeParameter protobuf message to shape.

        Args:
            rp: Protobuf message of type `ReshapeParameter`.
            bottom_shape: Shape of bottom layer to reshape. List or
                tuple of `int`.

        Return:
            List of `int` based on `ReshapeParameter` specs.

        Raises:
            `ValueError` in case of `ReshapeParameter` fields are
            incompatible with `bottom_shape`.
        """
        # First, figure out what dimensions of the bottom layer we
        # are working with, leaving behind a prefix and a suffix.
        bottom_len = len(bottom_shape)
        start_idx = (rp.axis + bottom_len) % bottom_len
        end_idx = bottom_len
        if rp.num_axes != -1:
            end_idx = start_idx + rp.num_axes + 1
        working_set = bottom_shape[start_idx:end_idx]

        # Reshape them.
        new_shape = [1] * len(rp.shape.dim)
        unk_idx = -1
        # Forward pass until -1 encountered.
        for idx, dim in enumerate(rp.shape.dim):
            if dim == 0:
                new_shape[idx] = working_set[idx]
            elif dim == -1:
                unk_idx = idx
                break
            else:
                new_shape[idx] = dim
        # If -1 encountered at `unk_idx`, we need a backward pass to
        # determine all remaining dimensions, except the one at the
        # aforementioned spot.
        if unk_idx != -1:
            # Iterate backwards through `new_shape`, until,
            # but excluding the index `unk_idx`.
            for idx, dim in enumerate(new_shape[:unk_idx:-1]):
                if dim == 0:
                    new_shape[-idx - 1] = working_set[-idx - 1]
                # Definitely not supposed to find a second
                # instance of -1.
                elif dim == -1:
                    raise ValueError("More than one instance of -1 encountered")
                else:
                    new_shape[-idx - 1] = dim
            # Now, on to figuring out the shape of the
            # mystery dimension.
            working_set_prod = np.prod(working_set)
            new_shape_prod = np.prod(new_shape)
            # Just in case the user messed up.
            if working_set_prod % new_shape_prod != 0:
                raise ValueError(
                    "Incompatible products while inferring unknown dim.")
            new_shape[unk_idx] = working_set_prod // new_shape_prod

        # Ensure compatibility.
        if np.prod(new_shape) != np.prod(working_set):
            raise ValueError("Incompatible shape specification.")

        # Append prefix and suffix, and return.
        return bottom_shape[:start_idx] + new_shape + bottom_shape[end_idx:]

    def _name_scope_builder(self, name):
        """Builds a re-enterable name scope.

        Args:
            name: Desired name scope. Type `str`.

        Returns:
            A name scope string.
        """
        cur_name = tf.get_variable_scope().name
        if cur_name:
            return "%s/%s/" % (cur_name.rstrip("/"), name.rstrip("/"))
        return "%s/" % name.rstrip("/")


class Solution:
    def countPaths(self, grid: List[List[int]]) -> int:
        m,n = len(grid), len(grid[0])
        dirs=((0,1),(0,-1),(1,0),(-1,0))

        tuples=[]
        for r in range(m):
            for c in range(n):
                tuples.append((grid[r][c],r,c))
        tuples.sort()

        memo=[[1 for _ in range(n)] for _ in range(m)]

        for (curr,r,c) in tuples:
            for d in dirs:
                (nr,nc) = (r+d[0],c+d[1])
                if nr<0 or nc<0 or nr==m or nc==n: continue
                prev=grid[nr][nc]
                if curr>prev: memo[r][c]+=memo[nr][nc]

        # print(memo)
        return sum([sum(row) for row in memo]) % (10**9+7)


#!/usr/bin/python3.8
#coding:u8

import discord

from commandes.all_cmd import *

class MyClient(discord.Client):
    """
    Confirme dans la console que Clio est connectée.

    Clio affiche : "Regarde !help" sous son nom.

    Prend tous les message en entrée pour vérifier s'il s'agit d'une commande.

    Appelle is_it_cmd, affiche le text dans le canal définit ou ne fait rien.
    """
    async def on_ready(self):

        print(f'{self.user} est connectée à Discord !')
        await client.change_presence(activity=discord.Activity(type=3, name="!aide"))

    async def on_message(self, message):
        canal, text = all_users_cmd(message, client)
        if text != "not a cmd":
            await canal.send(text)

client = MyClient()
client.run('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')

# cd /home/jaenne/Python/clio
# ./clio.py


import copy
import re
import string
from pprint import pprint

import awkward as ak
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import matplotlib.animation as animation
from tqdm import tqdm
from numba import njit

# %%

directions = np.loadtxt('input', dtype=object).item()
jets = np.array([1 if jet == '>' else -1 for jet in directions])

# %%

shapes = [[[0, 1, 2, 3, 3], [0, 0, 0, 0, 0]],
          [[0, 1, 1, 1, 2], [1, 0, 1, 2, 1]],
          [[0, 1, 2, 2, 2], [0, 0, 0, 1, 2]],
          [[0, 0, 0, 0, 0], [0, 1, 2, 3, 3]],
          [[0, 0, 1, 1, 1], [0, 1, 0, 1, 1]]]
shapes = np.array(shapes)

for shape in shapes:
    s = np.zeros((4, 4))
    s[tuple(shape)] = 1
    plt.imshow(s.T, origin='lower')
    plt.show()

# %%

@njit
def run_simulation(shapes, jets, stopped_rocks):
    height = 500
    width = 7
    height_thresh = 40
    height_move = 30
    grid_height = 0
    grid = np.zeros((width, height))
    grid[:, 0] = 1
    jet_i = 0
    max_ys = []
    tower_heights = []
    for i in range(stopped_rocks):
        if i % 10_000 == 0:
            print(i, 'out of', stopped_rocks)
        shape = shapes[i % len(shapes)]
        max_y = [(np.where(grid == 1)[1])[np.where(grid == 1)[0] == iter_x].max() for iter_x in range(width)]

        if (np.array(max_y) > height_thresh).all():
            grid[:, :-height_move] = grid[:, height_move:]
            grid_height += height_move
            max_y = [(np.where(grid == 1)[1])[np.where(grid == 1)[0] == iter_x].max() for iter_x in range(width)]

        tower_heights.append([i, grid_height + max(max_y)])

        rock_x = 2
        rock_y = max(max_y) + 4
        max_ys.append([i % len(shapes)] + max_y)  # before rock falls, without jet_i index
        # max_ys.append([i % len(shapes), jet_i] + max_y)  # before rock falls, with jet_i index

        # grid[np.array(shape[0]) + rock_x, np.array(shape[1]) + rock_y] = 1
        # plt.imshow(grid[:, :10].T, origin='lower')
        # plt.show()
        # grid = np.zeros((width, 10_000), dtype=int)
        # grid[:, 0] = 1

        while True:
            # jet
            rock_x += jets[jet_i % len(jets)]
            cord_x = [rock_x + x for x in shape[0]]
            if (max(cord_x) >= width) or (min(cord_x) < 0) or np.array([int(grid[rock_x + x, rock_y + y]) for x, y in shape.T]).sum() > 0:
                rock_x -= jets[jet_i % len(jets)]
            jet_i += 1

            # falling
            rock_y -= 1
            check_grid = [grid[rock_x + x, rock_y + y] for x, y in shape.T]
            if np.array(check_grid).sum() > 0:
                # hit bottom, place rock and continue with next rock
                rock_y += 1
                for x, y in shape.T:
                    grid[x + rock_x, y + rock_y] = 1
                break

        # plt.imshow(grid[:, :10].T, origin='lower')
        # plt.show()

    max_ys = np.array(max_ys)
    return grid, grid_height, max_ys, tower_heights

# %%

stopped_rocks = 50_000
grid, grid_height, max_ys, tower_heights = run_simulation(shapes, jets, stopped_rocks)
width = grid.shape[0]
max_y = [(np.where(grid == 1)[1])[np.where(grid == 1)[0] == iter_x].max() for iter_x in range(width)]
print(f'tower height : {max_y} --> {grid_height + max(max_y)}')
plt.figure(figsize=(10, 10))
# plt.imshow(grid[:, :20].T, origin='lower')
plt.imshow(grid[:, max(max_y) - 20:max(max_y) + 2].T, origin='lower')
plt.show()

# %%

def get_tower_height(stopped_rocks, max_ys, shapes, tower_heights):
    last_shape = (stopped_rocks - 1) % len(shapes)
    max_ys[:, 2:] = max_ys[:, 2:] - max_ys[:, 2:].min(axis=1)[np.newaxis].T
    max_ys_shape = max_ys[max_ys[:, 0] == last_shape]
    u, i, c = np.unique(max_ys_shape, return_counts=True, return_index=True, axis=0)
    last_unique = np.where(np.all(max_ys == max_ys_shape[i[-1]], axis=1))[0][0]
    periods = np.diff(np.where(np.all(max_ys == max_ys_shape[i[-1]], axis=1))[0])
    period = np.unique(periods).sum()
    print('start index:', last_unique)
    print('period:', period)
    init_config = last_unique + (stopped_rocks - last_unique) % period
    height_increase = tower_heights[init_config + period][1] - tower_heights[init_config][1]
    tower_height = tower_heights[init_config][1] + height_increase * (stopped_rocks - init_config) / period
    assert int(tower_height) == tower_height
    return int(tower_height)

# %%

for stopped_rocks in [2022, 1_000_000_000_000]:
    print(f'solution for {stopped_rocks} stopped rocks:', get_tower_height(stopped_rocks, max_ys, shapes, tower_heights))

# 3147
# 1532163742758
# %%


from dataclasses import dataclass, InitVar, field
from typing import Any
from pygccx.protocols import ISet, number
from pygccx.enums import ESetTypes
from pygccx.auxiliary import f2s

@dataclass
class DistribuitingCoupling:
    """
    Class to apply translational loading (force or displacement)
    on a set of nodes in a global sens

    The first condition (first line under the *DISTRIBUTING COUPLING keyword) has
    to be provided to the init of this class. Further conditions can be added
    by the method add_condition()

    Args:
        elset: "Element set which should contain exacty one element of type DCOUP3D
        nid_or_set: node id or node set of first coupling condition
        weight: weight of first coupling condition
        name: Name of this instance.
        desc: A short description of this instance. This is written to the ccx input file.
    """

    elset: ISet
    """Element set which should contain exacty one element of type DCOUP3D"""
    nid_or_set:InitVar[int|ISet]
    weight:InitVar[number] = 1.
    name:str = ''
    """Name of this instance."""
    desc:str = ''
    """A short description of this instance. This is written to the ccx input file."""
    conditions:list[tuple] = field(default_factory=list, init=False)
    """List of coupling conditions in the form:\n
    [(nid_or_set, weight), ...]"""

    def __setattr__(self, name: str, value: Any) -> None:
        if name == 'elset':
            if value.type != ESetTypes.ELEMENT:
                raise ValueError(f'Set type of elset must be ELEMENT, got {value.type.name}')
            if len(value.ids) != 1:
                raise ValueError(f'elset must contain exactly one element id, but has {len(value.ids)}')
        super().__setattr__(name, value)

    def __post_init__(self, nid_or_set, weight):
        self.add_condition(nid_or_set, weight)

    def add_condition(self, nid_or_set:int|ISet, weight:number = 1.):
        """
        Adds a condition to this DistribuitingCoupling

        Args:
            nid_or_set (int | ISet): node id or node set of coupling condition
            weight (number, optional): weight of coupling condition. Defaults to 1.

        Raises:
            ValueError: Raised if nid_or_set is an int and < 1.
            ValueError: Raised if nid_or_set is an ISet and set type != NODE
        """
        if isinstance(nid_or_set, int):
            if nid_or_set < 1:
                raise ValueError(f'nid must be greater than 0, got {nid_or_set}')
        if isinstance(nid_or_set, ISet):
            if nid_or_set.type != ESetTypes.NODE:
                raise ValueError(f'Set type of nid_or_set must be NODE, got {nid_or_set.type.name}')

        self.conditions.append((nid_or_set, weight))

    def __str__(self):
        s = f'*DISTRIBUTING COUPLING,ELSET={self.elset.name}\n'

        for nid_or_set, weight in self.conditions:
            if isinstance(nid_or_set, ISet):
                s += f'{nid_or_set.name},{f2s(weight)}\n'
            else:
                s += f'{nid_or_set},{f2s(weight)}\n'

        return s