from pyfiglet import Figlet
from tabulate import tabulate

def main():
    # MAIN FUNCTION
    POINT = [0, 0]
    figlet = Figlet()
    fonts = figlet.getFonts()
    figlet.setFont(font="slant")
    print(figlet.renderText("Tic-Tac-Toe"))
    try:
        while True:

            boardgame = [" "]*10

            # ASK THE PLAYERS TO CHOOSE THEIR MARKER
            player1,player2 = player_input()

            # CHOOSE WHO GOES IN FIRST
            turn = choose_first(player1)
            print("\n"+turn+" goes first")

            play_game = input('\nLets play the game? (Y or N): ')

            if play_game.upper() == 'Y':
                game_on = True

            else:
                game_on = False

            while game_on:
                # CHECK WHOSE TURN
                table = [["Player", "Point"]]
                if turn == "Player 1":
                    # MOVES FOR PLAYER ONE
                    display_board(boardgame)

                    # ASK THE PLAYER TO PLACE THEIR MARKER,
                    position = player_choice(boardgame)
                    place_marker(boardgame,player1,position)

                    # CHECK FOR WIN OR TIE
                    if win_check(boardgame, player1):
                        display_board(boardgame)
                        POINT[0] += 1
                        print('Player 1 has WON the game!\n')
                        game_on = False
                    else:
                        if full_board_check(boardgame):
                            display_board(boardgame)
                            print('Well played, ended up as a tie!\n')
                            game_on = False
                        else:
                            turn = 'Player 2'

                else:
                    # MOVES FOR PLAYER TWO
                    display_board(boardgame)

                    # ASK THE PLAYER TO PLACE THEIR MARKER,
                    position = player_choice(boardgame)
                    place_marker(boardgame,player2,position)

                    # CHECK FOR WIN OR TIE
                    if win_check(boardgame, player2):
                        display_board(boardgame)
                        POINT[1] += 1
                        print('Player 2 has WON the game!\n')
                        game_on = False
                    else:
                        if full_board_check(boardgame):
                            display_board(boardgame)
                            print('The game has been TIED!\n')
                            game_on = False
                        else:
                            turn = 'Player 1'
                if game_on == False:
                    table.append(["P1", POINT[0]])
                    table.append(["P2", POINT[1]])
                    print(tabulate(table, headers="firstrow",
tablefmt="grid"))
                print()

            # Check whether the player wants to play again
            if not replay():
                break

    except (EOFError, KeyboardInterrupt):
        print("\n\nAww! We are sad you left earlyðŸ¥².")

    else:
        print("\n", tabulate([["Thank you for playing Tic-Tac-Toe!"]],
tablefmt="grid"), "\n")

def display_board(board):
    # display the game as a board
    print('\n'*100)
    table = [[board[1], board[2], board[3]], [board[4], board[5],
board[6]], [board[7], board[8], board[9]]]
    print(tabulate(table, tablefmt="grid"))
    print("\n\n")

def player_input():
    # take users input on which marker to choose
    pmark = input('Choose your marker (X or O): ').upper()
    table = [["Player", "Marker"]]
    while pmark.upper() not in ('X','O'):
        print('Sorry! Invalid marker chosen.')
        pmark = input('Choose your marker (X or O): ')
    table.append(["P1", pmark])
    if pmark.upper() == 'X':
        table.append(["P2", "O"])
        player1 = 'X'
        player2 = 'O'
    else:
        table.append(["P2", "X"])
        player1 = 'O'
        player2 = 'X'
    print(tabulate(table, headers="firstrow", tablefmt="grid"))
    return player1,player2

def place_marker(board, marker, position):
    # place a marker at a position by the user
    board[position] = marker

def win_check(board, marker):
    # win_check() checks if any user wins during the game
    return ((board[1]==marker and board[2]==marker and board[3]==marker)
or
    (board[4]==marker and board[5]==marker and board[6]==marker) or
    (board[7]==marker and board[4]==marker and board[1]==marker) or
    (board[8]==marker and board[5]==marker and board[2]==marker) or
    (board[9]==marker and board[6]==marker and board[3]==marker) or
    (board[1]==marker and board[5]==marker and board[9]==marker) or
    (board[7]==marker and board[8]==marker and board[9]==marker) or
    (board[7]==marker and board[5]==marker and board[3]==marker))

def choose_first(player1):
    # selects the player that chooses X as the first player
    if player1 == "X":
        pone = 1
    else:
        pone = 2
    return f'Player {pone}'

def space_check(board, position):
    # check if there is a space in the given position
    if board[position] == " ":
        return True
    else:
        return False

def full_board_check(board):
    # checks if the board is full to find if its a draw
    countn=0
    for i in range(1,10):
        if board[i] != " ":
            countn+=1
        else:
            return False
    if countn == 9:
        return True

def marker_position(a):
    while a not in range(1, 10):
        return False
    return True

def player_choice(board):
    # enter the next user choice
    a = int(input('Enter your next position: '))
    while not marker_position(a):
        print("Invalid position entered\n")
        a = int(input('Enter your next position: '))
    while a in range(1, 10):
        if space_check(board,a):
            return a
        else:
            print('Please change the position since it is full')
            a = int(input('Enter your next position: '))

def replay():
    # ask user if they want to play again
    replay=input('Would you like to play again by changing the markers? (Y or N): ')
    if replay.upper() == 'Y':
        return True
    else:
        return False

if __name__ == "__main__":
    main()


import cv2
import numpy as np

# body mpi
protoFile = "pose/mpi/pose_deploy_linevec_faster_4_stages.prototxt"
weightsFile = "pose/mpi/pose_iter_160000.caffemodel"
POSE_PAIRS = [[0,1], [1,2], [2,3], [3,4], [1,5], [5,6], [6,7], [1,14], [14,8], [8,9], [9,10], [14,11], [11,12], [12,13] ]

nPoints = 15
video = cv2.VideoCapture('Khmer-Chol-Chnam-Thmay.webm')

print ("okey chạy nè")

ten = 0

while True:
    ten = ten +1
    ret, frame = video.read()
    if (ten > 130 and ten % 3 == 0):

        net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile)

        frameWidth = frame.shape[1]
        frameHeight = frame.shape[0]
        aspect_ratio = frameWidth/frameHeight

        inHeight = 386
        inWidth = int(((aspect_ratio*inHeight)*8)//8)

        inpBlob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight),
                                (0, 0, 0), swapRB=False, crop=False)
        net.setInput(inpBlob)
        output = net.forward()
        points = []
        frameCopy = np.zeros(frame.shape)
        threshold = 0.2

        for i in range(nPoints):
            # confidence map of corresponding body's part.
            probMap = output[0, i, :, :]
            probMap = cv2.resize(probMap, (frameWidth, frameHeight))

            # Find global maxima of the probMap.
            minVal, prob, minLoc, point = cv2.minMaxLoc(probMap)

            if prob > threshold :
                # Add the point to the list if the probability is greater than the threshold
                points.append((int(point[0]), int(point[1])))
            else :
                points.append(None)

        # # Draw Skeleton
        for pair in POSE_PAIRS:
            partA = pair[0]
            partB = pair[1]

            if points[partA] and points[partB]:
                cv2.line(frameCopy, points[partA], points[partB], (0, 255, 255), 2)
                cv2.circle(frameCopy, points[partA], 1, (0, 0, 255), thickness=1, lineType=cv2.FILLED)

        cv2.imwrite('data/'+str(ten)+'.jpg',frameCopy)

print ("xong het roi")


import math
import pandas as pd
import numpy as np
from datetime import datetime
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import colorsys

def dtw(x, y, distance):
    N = len(x)
    M = len(y)
    cost_matrix = np.zeros((N + 1, M + 1))
    for i in range(1, N + 1):
        cost_matrix[i, 0] = np.inf
    for j in range(1, M + 1):
        cost_matrix[0, j] = np.inf

    traceback_mat = np.zeros((N, M))
    for i in range(N):
        for j in range(M):
            penalty = [
                cost_matrix[i, j],
                cost_matrix[i, j + 1],
                cost_matrix[i + 1, j]
            ]
            i_penalty = np.argmin(penalty)
            cost_matrix[i + 1, j +
                        1] = distance(i, j, N, M, x, y) + penalty[i_penalty]
            traceback_mat[i, j] = i_penalty

    i = N - 1
    j = M - 1
    path = [(i, j)]
    while i > 0 or j > 0:
        p = traceback_mat[i, j]
        if p == 0:
            i = i - 1
            j = j - 1
        elif p == 1:
            i = i - 1
        elif p == 2:
            j = j - 1
        path.append((i, j))

    return path[::-1], (cost_matrix[1:, 1:][N-1, M-1] / (N+M))

def manhattan_distance(i, j, N, M, x, y):
    return abs(x[i] - y[j])

def feature_based_distance(i, j, N, M, x, y):

    def cal_f_local(i, N, x):
        f_local = [x[i], x[i]]
        if i != 0:
            f_local[0] = f_local[0] - x[i - 1]
        if i != N - 1:
            f_local[1] = f_local[1] - x[i + 1]
        return f_local

    def cal_f_global(i, N, x):
        f_global = [x[i], x[i]]

        before_half = sum(x[:i]) / len(x[:i + 1])
        f_global[0] = f_global[0] - before_half

        after_half = sum(x[i + 1:]) / len(x[i:])
        f_global[1] = f_global[1] - after_half
        return f_global

    f_local_x = cal_f_local(i, N, x)
    f_local_y = cal_f_local(j, M, y)

    f_global_x = cal_f_global(i, N, x)
    f_global_y = cal_f_global(j, M, y)

    dist_local = abs(f_local_x[0] - f_local_y[0]) + \
        abs(f_local_x[1] - f_local_y[1])
    dist_global = abs(f_global_x[0] - f_global_y[0]) + \
        abs(f_global_x[1] - f_global_y[1])

    return dist_local + dist_global

def _get_colors(num_colors):

    def rgb_to_hex(rgb):
        return ('#{:02X}{:02X}{:02X}').format(int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))

    colors=[]
    for i in np.arange(0., 360., 360. / num_colors):
        hue = i/360.
        lightness = (50 + np.random.rand() * 10)/100.
        saturation = (90 + np.random.rand() * 10)/100.
        colors.append(rgb_to_hex(colorsys.hls_to_rgb(hue, lightness, saturation)))
    return colors

def standardize_prices(x):
    result = []
    for i, j in zip(x, x[1:]):
        result.append(((i - j) * 100) / j)
    return np.array(result)

def min_max_normalize(x):
    result = []
    mx = max(x)
    mn = min(x)
    for i in x:
        result.append(((i - mn) / (mx - mn)) * 100)
    return np.array(result)

if __name__ == "__main__":

    pattern = min_max_normalize(np.array([2.5, -2.5, 2.5, -2.5, 2.5]))
    pattern_length = len(pattern)
    desired_length = pattern_length + 5

    df = pd.read_csv('data/APPL.csv')
    for i in ["Close/Last", "Open", "High", "Low"]:
        df[i] = df[i].apply(lambda x: float(x.replace('$', '')))

    close = df["Close/Last"]

    # Columns = Date,Close/Last,Volume,Open,High,Low
    # fig = go.Figure()
    fig = make_subplots(rows=2, cols=1)

    fig.add_trace(go.Candlestick(x=df['Date'],
                                open=df['Open'],
                                high=df['High'],
                                low=df['Low'],
                                close=df['Close/Last'], name="TSLA"), row=1, col=1)

    fig.update_layout(xaxis_rangeslider_visible=False)

    fig.add_trace(go.Scatter(
        x=np.arange(pattern_length), y=pattern, name="Pattern", line={"color" : "black"}), row=2, col=1)

    colors = _get_colors(pattern_length)

    patterns = dict()
    for _ in range(pattern_length):
        patterns[_] = [[], []]

    for offset in range(0, (len(close) // desired_length) + 1):

        target = min_max_normalize(
            close[offset * desired_length: (offset + 1) * desired_length])

        if not len(target):
            continue

        path, cost = dtw(target, pattern, feature_based_distance)

        match_x = dict()
        match_y = dict()

        for _ in range(pattern_length):
            match_x[_] = []
            match_y[_] = []

        for pr, nt in zip(path, path[1:]):
            if pr[1] == nt[1]:
                match_x[pr[1]].append(
                    df['Date'][pr[0] + (desired_length * offset)])
                match_y[pr[1]].append(
                    df['Close/Last'][pr[0] + (desired_length * offset)])
            elif pr[1] != nt[1]:
                match_x[pr[1]].append(
                    df['Date'][pr[0] + (desired_length * offset)])
                match_y[pr[1]].append(
                    df['Close/Last'][pr[0] + (desired_length * offset)])
                match_x[pr[1]].append(
                    df['Date'][nt[0] + (desired_length * offset)])
                match_y[pr[1]].append(
                    df['Close/Last'][nt[0] + (desired_length * offset)])

        match_x[path[-1][1]].append(df['Date']
                                    [path[-1][0] + (desired_length * offset)])
        match_y[path[-1][1]].append(df['Close/Last']
                                    [path[-1][0] + (desired_length * offset)])

        for _ in range(pattern_length):
            fig.add_trace(go.Scatter(
                x=match_x[_], y=match_y[_], name=f"Pat-{_}", line={"color": colors[_]}), row=1, col=1)

    fig.show()


"""Console script for semeval_8_2022_ia_downloader."""
import argparse
import json
import os
import os.path
import pathlib
import sys
import time
from multiprocessing import Pool
from urllib.parse import urlparse, urljoin

import pandas as pd
import requests
import tqdm
from newspaper import Article, Config
from requests import RequestException

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

RESOLVE_FQDN_LIST = ['feedproxy.google.com']

def get_local_path_for_article(article_id, dump_dir, extension='.json'):
    """
    maps the `article_id` to the path where the article should be stored
    :param article_id: the id of the article
    :param dump_dir: the root folder where to save articles
    :param extension: the extension for the file that should contain the article
    :return: the path to the file
    """
    dirname = article_id[-2:]
    filename = article_id + extension
    filepath = os.path.join(dump_dir, dirname, filename)
    return filepath

def parse_input(location):
    """
    parses the csv file containing the articles to download
    :param location: the path to the input file
    :return: generator of triplets `(article_id, article_link, article_lang)` containing respectively the id
             of the article, the link to the article, and the language of the article
    """
    df = pd.read_csv(location, index_col='pair_id', encoding='utf8')

    df.rename(columns={"url1_lang": "lang1", "url2_lang": "lang2"}, inplace=True)  # patch for different release format
    all_links = set(df.link1.unique())
    all_links.update(set(df.link2.unique()))

    # https://stackoverflow.com/a/36806159
    # Query parameters are parsed as additional parameters to the API rather than as part of the url

    for pair_id, row in df.iterrows():
        for article_id, article_link, article_lang in zip(pair_id.split('_'),
                                                          row[['link1', 'link2']].values,
                                                          row[['lang1', 'lang2']].values, ):
            domain = urlparse(article_link).netloc
            if domain in RESOLVE_FQDN_LIST:
                # resolve link if e.g., coming from news aggregators like feedproxy.google.com
                try:
                    r = requests.head(article_link, allow_redirects=True)
                    article_link = r.url
                except (TimeoutError, RequestException) as e:
                    print(e)
                    continue
            yield article_id, article_link, article_lang

def get_remaining_articles(location, dump_dir, min_text_length=0):
    """
    finds the articles that need downloading
    :param location: the path to the input file
    :param dump_dir: the root folder where to save articles
    :param min_text_length: include articles that have been downloaded, but for which the `text` entry in the
           corresponding json file is at most `min_text_length` characters
    :return: generator of triplets `(article_id, article_link, article_lang)` containing respectively the id
             of the article, the link to the article, and the language of the article
    """
    for article_id, article_link, article_lang in parse_input(location):
        filepath = get_local_path_for_article(article_id, dump_dir)
        if not os.path.exists(filepath):
            yield article_id, article_link, article_lang
        else:
            with open(filepath, encoding='utf8') as f:
                article_json = json.loads(f.read())
                if ('text' not in article_json) or (len(article_json['text'].strip()) <= min_text_length):
                    yield article_id, article_link, article_lang

def parse_article(dump_dir, article_id, article_link, article_lang, html=None, article_config=None):
    """
    downloads the article, saves the html, uses newspaper3k to parse the article, saves the output as json
    :param dump_dir: the root folder where to save articles
    :param article_id: the id of the article
    :param article_link: the URL of the article to download
    :param article_lang: the language of the article
    :param html: the html of the article, if already downloaded; if None, will download again
    :param article_config: newspaper3k configuration for downloading the article, see:
            https://newspaper.readthedocs.io/en/latest/user_guide/advanced.html#parameters-and-configurations
    :return: None
    """
    filepath = get_local_path_for_article(article_id=article_id, dump_dir=dump_dir, extension='.html')
    pathlib.Path(os.path.dirname(filepath)).mkdir(parents=True, exist_ok=True)
    article = Article(article_link, language=article_lang, config=article_config)

    if html is None:
        article.download()
        article.html = article.html.encode()
    else:
        # set html manually
        article.html = html
        article.download_state = 2

    with open(filepath, 'wb') as f:
        f.write(article.html)
    article.parse()
    article_dict = dict(source_url=article.source_url,
                        url=article.url,
                        title=article.title,
                        top_image=article.top_image,
                        meta_img=article.meta_img,
                        images=list(article.images),
                        movies=article.movies,
                        text=article.text,
                        keywords=article.keywords,
                        meta_keywords=article.meta_keywords,
                        tags=list(article.tags),
                        authors=article.authors,
                        publish_date=article.publish_date and article.publish_date.ctime() or None,
                        summary=article.summary,
                        article_html=article.article_html,
                        meta_description=article.meta_description,
                        meta_lang=article.meta_lang,
                        meta_favicon=article.meta_favicon,
                        meta_data=dict(article.meta_data),
                        canonical_link=article.canonical_link
                        )

    filepath = get_local_path_for_article(article_id=article_id, dump_dir=dump_dir, extension='.json')
    with open(filepath, 'w') as f:
        f.write(json.dumps(article_dict))

def rescrape_original(args):
    """
    scrapes an article
    :param args: tuple `article_id, article_link, article_lang, article_config, dump_dir, retry_wait` where
           retry_wait specifies how many seconds to wait after each requests, and the other parameters are defined in
           `parse_article'
    :return: None
    """
    article_id, article_link, article_lang, article_config, dump_dir, retry_wait = args
    try:
        parse_article(dump_dir, article_id, article_link, article_lang, html=None,
                      article_config=article_config)
    except Exception as e:
        print(e)
        print('cannot download from', article_link)
    time.sleep(retry_wait)

def rescrape_wayback(args):
    """
    scrapes an article from the waybackmachine, retrieving the latest archived copy
    :param args: tuple `article_id, article_link, article_lang, article_config, dump_dir, retry_wait` where
           retry_wait specifies how many seconds to wait after each requests, and the other parameters are defined in
           `parse_article'
    :return: None
    """
    article_id, article_link, article_lang, article_config, dump_dir, retry_wait = args
    try:
        # resolve the latest archived copy
        # https://en.wikipedia.org/wiki/Help:Using_the_Wayback_Machine#Latest_archive_copy
        wayback_prefix = 'https://web.archive.org/web/'
        wayback_link = wayback_prefix + article_link
        response = requests.head(wayback_link, allow_redirects=True)
        # specify the version of the archived copy as "id_":
        # id_ Identity - perform no alterations of the original resource, return it as it was archived.
        # https://en.wikipedia.org/wiki/Help:Using_the_Wayback_Machine#Specific_archive_copy
        wayback_link = response.url[:42] + 'id_' + response.url[42:]
        rescrape_original((article_id, wayback_link, article_lang, article_config, dump_dir, retry_wait))
    except Exception as e:
        print(e)
        print('cannot download from wayback url', wayback_link)

def main():
    """console script for semeval_8_2022_ia_downloader; run with --help to read about the available runtime arguments.
    """
    parser = argparse.ArgumentParser()
    parser.add_argument('--dump_dir', action="store", default="articles", help='dump folder path', required=False)
    parser.add_argument("--links_file", action="store", default="sample_data.csv", help="File to read",
                        required=True,
                        metavar="INFILE")

    parser.add_argument("--retry", action="store", default="original",
                        help="""when articles are inaccessible from the IA, do:
                        - "original": try downloading from the original source URL
                        - "ignore": do nothing
                        - "log": log inaccessible articles to file
                        """,
                        required=False)
    parser.add_argument("--retry_log", action="store",
                        default="inaccessible_urls_{}.csv".format(time.strftime('%Y%m%d%H%M%S')),
                        help="""path to the file to log inaccessible articles if --retry=log, or if
                        --retry=original and the article is inaccessible also from the original source""",
                        required=False)
    parser.add_argument("--retry_delay", action="store", default=3, type=int,
                        help="how many seconds to wait after each requests if --retry=original",
                        required=False)
    parser.add_argument("--retry_min_chars", action="store", default=50, type=int,
                        help="""include articles that have been downloaded, but for which the `text` entry in the
                         corresponding json file is at most `min_text_length` characters""",
                        required=False)

    parser.add_argument("--log_level", action="store", default="INFO", help="scrapy log verbosity level",
                        required=False)
    parser.add_argument("--concurrent_requests", action="store", default=1, type=int,
                        help="number of parallel requests",
                        required=False)
    parser.add_argument("--download_delay", action="store", default=1, type=int,
                        help="download delay between requests to the IA",
                        required=False)

    parser.add_argument("--user_agent", action="store",
                        default='semeval_8_2022_ia_downloader (+http://www.euagendas.org/semeval2022)',
                        type=str,
                        help="user agent to identify the script with the IA and original source website",
                        required=False)

    args = parser.parse_args()
    retry_strategy = args.retry
    retry_wait = args.retry_delay
    retry_log = args.retry_log
    min_text_length = args.retry_min_chars

    article_config = Config()
    article_config.browser_user_agent = args.user_agent
    article_config.request_timeout = 60

    pathlib.Path(args.dump_dir).mkdir(parents=True, exist_ok=True)

    print('Phase 1: scrape after querying the internet archive\'s CDX server')
    # The path seen from root, ie. from main.py
    settings_file_path = 'semeval_8_2022_ia_downloader.semeval_8_2022_ia_downloader.settings'
    os.environ.setdefault('SCRAPY_SETTINGS_MODULE', settings_file_path)
    scrapy_settings = get_project_settings()
    scrapy_settings.set('LOG_LEVEL', args.log_level)
    scrapy_settings.set('CONCURRENT_REQUESTS', args.concurrent_requests)
    scrapy_settings.set('DOWNLOAD_DELAY', args.download_delay)
    scrapy_settings.set('USER_AGENT', args.user_agent)

    process = CrawlerProcess(scrapy_settings)
    process.crawl('IaArticle', links_file=args.links_file,
                  dump_dir=args.dump_dir,
                  min_text_length=args.retry_min_chars
                  )
    process.start()  # the script will block here until the crawling is finished

    if retry_strategy == 'ignore':
        # terminate here if there is no wish to attempt re-downloading missing articles
        pass
    elif retry_strategy == 'original':
        # otherwise, try logging or downloading articles again
        print('Phase 2: rescrape using the internet archive\'s /web/ endpoint')

        # try scraping from wayback
        remaining_articles = list(get_remaining_articles(args.links_file,
                                                         args.dump_dir,
                                                         min_text_length))
        wayback_pool = Pool(processes=args.concurrent_requests)
        for _ in tqdm.tqdm(wayback_pool.imap_unordered(rescrape_wayback, [(article_id,
                                                                           article_link,
                                                                           article_lang,
                                                                           article_config,
                                                                           args.dump_dir,
                                                                           retry_wait) for (article_id,
                                                                                article_link,
                                                                                article_lang) in
                                                                          remaining_articles]),
                           desc='downloading inaccessible articles from the web archive',
                           total=len(remaining_articles)):
            pass
        wayback_pool.close()
        wayback_pool.join()

        print('Phase 3: rescrape using the internet archive\'s /web/ endpoint, after stripping url query parameters')
        # try scraping from wayback, stripping query parameters
        remaining_articles = list(map(lambda x: (x[0], urljoin(x[1], urlparse(x[1]).path), x[2]),
                                      filter(lambda x: len(urlparse(x[1]).query) > 0,
                                             get_remaining_articles(args.links_file,
                                                                    args.dump_dir,
                                                                    min_text_length))))
        wayback_noquery_pool = Pool(processes=args.concurrent_requests)
        for _ in tqdm.tqdm(wayback_noquery_pool.imap_unordered(rescrape_wayback, [(article_id,
                                                                                article_link,
                                                                                article_lang,
                                                                                article_config,
                                                                                args.dump_dir,
                                                                                retry_wait) for (article_id,
                                                                                article_link,
                                                                                article_lang) in
                                                                                remaining_articles]),
                           desc='downloading articles without query parameters from the web archive',
                           total=len(remaining_articles)):
            pass
        wayback_noquery_pool.close()
        wayback_noquery_pool.join()

        # try scraping from the original source
        print('Phase 4: rescrape from the original source')
        remaining_articles = list(get_remaining_articles(args.links_file,
                                                         args.dump_dir,
                                                         min_text_length))
        original_pool = Pool(processes=args.concurrent_requests)
        for _ in tqdm.tqdm(original_pool.imap_unordered(rescrape_original, [(article_id,
                                                                             article_link,
                                                                             article_lang,
                                                                             article_config,
                                                                             args.dump_dir,
                                                                             retry_wait) for (article_id,
                                                                                article_link,
                                                                                article_lang) in
                                                                            remaining_articles]),
                           desc='downloading inaccessible articles from the original source',
                           total=len(remaining_articles)):
            pass
        original_pool.close()
        original_pool.join()

        # log missing articles
        missing_links = [link for _, link, _ in get_remaining_articles(args.links_file, args.dump_dir, 0)]
        with open(retry_log, 'w+', encoding='utf-8') as f:
            f.write('\n'.join(missing_links))

    elif retry_strategy == 'log':
        print('logging inaccessible articles to', retry_log)
        remaining_links = [article_link
                           for _, article_link, _ in get_remaining_articles(args.links_file, args.dump_dir)]
        with open(retry_log, 'a+', encoding='utf-8') as f:
            f.write('\n'.join(remaining_links))
    return 0

if __name__ == "__main__":
    sys.exit(main())  # pragma: no cover


from typing import List

def deserialize(string):
    if string == '{}':
        return None
    nodes = [None if val == 'null' else TreeNode(int(val))
             for val in string.strip('[]{}').split(',')]
    kids = nodes[::-1]
    root = kids.pop()
    for node in nodes:
        if node:
            if kids:
                node.left = kids.pop()
            if kids:
                node.right = kids.pop()
    return root

# Definition for a binary tree node.

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
        ret, level = [], [root]
        while root and level:
            tmp = []
            for _ in range(len(level)):
                node = level.pop(0)
                tmp.append(node.val)
                if node.left:
                    level.append(node.left)
                if node.right:
                    level.append(node.right)
            ret.insert(0, tmp)
        return ret

if __name__ == '__main__':
    root = deserialize('[3,9,20,null,null,15,7]')

    sol = Solution()
    print(sol.levelOrderBottom(root))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author            : Aaron Niskin <aaron@niskin.org>
# Date              : 2019-01-16
# Last Modified Date: 2019-01-20
# Last Modified By  : Aaron Niskin <aaron@niskin.org>
# pylint: disable=fixme
"""
Build the sudoku board object, along with all appropriate methods.

The board IDs will be arranged thusly (for simplicity, dim=3 in this example):

-------------------------------
| column || 0  | 1  | 2  | 3  |
|==============================
| row  0 || 0  | 1  | 2  | 3  |
-------------------------------
|      1 || 4  | 5  | 6  | 7  |
-------------------------------
|      2 || 8  | 9  | 10 | 11 |
-------------------------------
|      3 || 12 | 13 | 14 | 15 |
-------------------------------

The basic structure is:
    cells keep track of everything pertaining to that individual cell. e.g.
        * It's own value -- if it hsa one
        * set of possible values
        * color -- for printing reasons
        * failed guesses
"""

import logging
import random
from copy import copy, deepcopy
from itertools import combinations

LOGGER = logging.getLogger(__file__)
SH = logging.StreamHandler()
SH.setLevel(logging.DEBUG)
LOGGER.addHandler(SH)

def _rowcol_to_id(row, column, dim):
    """Convert row,column to ID"""
    return row*(dim**2) + column

def _id_to_rowcol(id_, dim):
    """Convert id to row, col"""
    col = id_ % (dim ** 2)
    row = (id_ - col) // (dim ** 2)
    return row, col

def _row_to_ids(row, dim):
    """Convert a row number to the IDs of the cells in the same row"""
    return [_rowcol_to_id(row, i, dim) for i in range(dim**2)]

def _id_to_row(id_, dim):
    """Convert id to the IDs of the cells in the same row"""
    col = id_ % (dim ** 2)
    row = (id_ - col) // (dim ** 2)
    return _row_to_ids(row, dim)

def _col_to_ids(col, dim):
    """Convert a col number to the IDs of the cells in the same col"""
    return [_rowcol_to_id(i, col, dim) for i in range(dim**2)]

def _id_to_col(id_, dim):
    """Convert id to the IDs of the cells in the same col"""
    col = id_ % (dim ** 2)
    return _col_to_ids(col, dim)

def _id_to_box(id_, dim):
    """Convert id to box ID"""
    row = id_ // (dim ** 3)
    col = (id_ % (dim ** 2)) // dim
    return row * dim + col

def _box_to_ids(box, dim):
    row0 = box - (box % dim)
    col0 = (box % dim) * dim
    retval = []
    for row in range(row0, row0+dim):
        vals = [_rowcol_to_id(row, col, dim) for col in range(col0, col0+dim)]
        retval.extend(vals)
    return retval

def _id_to_box_id(id_, dim):
    """Convert id to box ID"""
    return _box_to_ids(_id_to_box(id_, dim), dim)

def _id_to_related(id_, dim):
    retset = set()
    retset.update(_id_to_row(id_, dim))
    retset.update(_id_to_col(id_, dim))
    retset.update(_id_to_box_id(id_, dim))
    return retset

def test_functions(func, dim=3, indx=0):
    """docstring"""
    brd = SudokuBoard(dim)
    for id_ in func(indx, dim):
        brd[id_].val = 'H'
    brd[indx].val = 'O'
    return brd

class SudokuCell():
    """The sudoku board cell class

    cells keep track of everything pertaining to that individual cell. e.g.
        * It's own value -- if it hsa one
        * set of possible values

    args:
        id_ : int
            The cell ID
    """

    def __init__(self, val=None, color='34', good_color='34',
                 bad_color='35'):
        # pylint: disable=too-many-arguments
        self.val = val
        self.color = color
        self.good_color = good_color
        self.bad_color = bad_color  # for identifying erroneous moves easily

    def set_value(self, val):
        """Sets the value of the cell

        IDEMPOTENT

        args:
            val : int
                The value to remove.
        """
        self.val = val
        return True

    def get_value(self):
        """getter"""
        return self.val

    def bad_cell(self):
        """designate this cell as bad"""
        self.color = self.bad_color

    def good_cell(self):
        """Designate this cell as 'good'"""
        self.color = self.good_color

    def __str__(self):
        """return a string representation of the cell (just the value)"""
        return '  \x1b[%sm% 3s\x1b[0m  ' % (self.color,
                                            str(self.val) if self.val is not None else '')

class SudokuBoard():
    """The sudoku board class

    args:
        dim : int
            "dimension of the board". For instance, a traditional sudoku board
            would have dim == 3. Note: the actual number of squares in a sudoku
            board are dim^4 not dim^2.
        kwargs : keys = int, vals = int
            the values are the values to initialize the board with, and the
            keys are the IDs of the cells in question.
    """
    CELL_TYPE = SudokuCell
    def __init__(self, dim=3, board=None, good_color='34', bad_color='35'):
        # pylint: disable=redefined-outer-name
        self.dim = dim
        if board is None:
            self.board = [self.CELL_TYPE(good_color=good_color,
                                         bad_color=bad_color) for _ in range(dim**4)]
        else:
            self.board = [self.CELL_TYPE(**deepcopy(x.__dict__)) for x in board]
            assert len(board) == self.dim**4, 'Invalid board size'
            self.board = board

    def _set_value(self, id_, val):
        """set the value of a cell explicitly"""
        return self[id_].set_value(val)

    def make_move(self, id_, val):
        """set the value of a cell -- can be replaced with more complicated
        logic later and things should still work
        """
        return self._set_value(id_, val)

    def copy_board(self):
        """make a copy of the board"""
        return [deepcopy(x) for x in self]

    def __str__(self):
        """Get the sudoku board vector"""
        line_len = 8*(self.dim**2)+self.dim+2
        outstr = '|'
        for id_, cell in enumerate(self.board):
            outstr += '|'+cell.__str__()
            if (id_ % self.dim) == (-1 % self.dim):
                outstr += '|'
                if (id_ % (self.dim**2)) == (-1 % (self.dim**2)):
                    outstr += '|\n'
                    if (id_ % (self.dim**3)) == (-1 % (self.dim**3)):
                        outstr += '='*line_len + '\n'
                    else:
                        outstr += '-'*line_len + '\n'
                    outstr += '|'
        return " \n" + ('='*line_len) + '\n' + outstr + '|'*(line_len - 1)

    def __itercols__(self):
        for i in range(self.dim**2):
            yield _col_to_ids(i, self.dim)

    def __iterrows__(self):
        for i in range(self.dim**2):
            yield _row_to_ids(i, self.dim)

    def __iterboxes__(self):
        for i in range(self.dim**2):
            yield _box_to_ids(i, self.dim)

    def __iterneighbors__(self):
        for func in [self.__iterboxes__, self.__iterrows__, self.__itercols__]:
            for items in func():
                yield items

    def get_relatives(self, id_, relationship='all'):
        """get all ids in the same {box,row,column}

        args:
            id_ : int
                the cell ID you want the relatives of
            relationship : str
                {box,row,col,all}
        """
        if relationship == 'all':
            return _id_to_related(id_, self.dim)
        if relationship == 'box':
            return _id_to_box_id(id_, self.dim)
        if relationship == 'row':
            return _id_to_row(id_, self.dim)
        if relationship == 'col':
            return _id_to_col(id_, self.dim)
        return False

    def good_cell(self, id_):
        """designate a cell as bad"""
        return self[id_].good_cell()

    def bad_cell(self, id_):
        """designate a cell as bad"""
        return self[id_].bad_cell()

    @classmethod
    def from_json(cls, js_dict):
        """convert json to board"""
        # pylint: disable=redefined-outer-name,invalid-name
        dim = js_dict.pop('dim')
        board = cls(dim=dim)
        for k, v in js_dict.items():
            id_ = int(k.strip('_'))
            board.make_move(id_, v)
        return board

    def is_valid(self):
        """docstring"""
        # pylint: disable=invalid-name
        for items in self.__iterneighbors__():
            item_vals = [self[x].val for x in items if self[x].val is not None]
            if len(set(item_vals)) != len(item_vals):
                dups = [(x, y) for x, y in combinations(items, 2) if self[x].val == self[y].val]
                for x, y in dups:
                    self.bad_cell(x)
                    self.bad_cell(y)
                return False
        return True

    def complete(self):
        """Check if the board is completed without errors"""
        for cell in self:
            if cell.get_value() is None:
                return False
        return self.is_valid()

    def __getitem__(self, i):
        return self.board[i]

    def __iter__(self):
        return iter(self.board)

    @classmethod
    def from_board(cls, sudokuboard_obj):
        """Copy a board into another
        This is particularly useful for solver classes that inherit from this
        one... They can take a SudokuBoard object, and import it into their own
        class and do whatever to it. The thing calls "make_move" on each cell
        which a value that is not None

        args:
            board : SudokuBoard object
        returns:
            Whatever class this is initialized with the values from this
            SudokuBoard object
        """
        in_dict = deepcopy(sudokuboard_obj.__dict__)
        inboard = in_dict.pop('board')
        outboard = cls(**in_dict)
        for id_, cell in enumerate(inboard):
            value = cell.get_value()
            if value is not None:
                outboard.make_move(id_, value)
        return outboard

    @classmethod
    def get_random_board(cls, dim, pct=0.05, seed=1234):
        """Generate a random sudoku board"""
        random.seed(seed)
        outboard = cls(dim=dim)
        num_cells = len(outboard.board)
        sample = random.sample(range(num_cells), int(num_cells*pct))
        if len(sample) != len(set(sample)):
            print('ruh roh')
        for cell in sample:
            outboard.make_move(cell, random.randint(1, dim**2))
        return outboard

if __name__ == '__main__':
    import sys
    import os
    LIBPATH = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                            os.pardir, os.pardir))
    sys.path.append(LIBPATH)
    from sudso.example_boards import BOARDS
    for b in BOARDS:
        print('================================')
        board = copy(SudokuBoard.from_json(b))
        print(board)
        print(board.is_valid(), board.complete())


#!/usr/bin/env python
# coding: utf-8

# In[1]:

import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
import plotly.express as px
from ipywidgets import widgets
from IPython.display import display

# In[97]:

studentName = pd.Series(['Adeyemi','Jude','Lukemon','Tunde'])
studentGrade = pd.Series([2.34,3.45,4.00,3.24])
studentMatric = pd.Series(['HCSF/20/0024','HCSF/20/0034','HCSF/20/0045','HCSF/20/0067'])

print(studentName)
print(studentGrade)
print(studentMatric)

# In[98]:

studentData = pd.DataFrame({'studentName': studentName, 'studentGrade':studentGrade, 'studentMatric':studentMatric})

studentData

# In[99]:

studentData = pd.read_csv('Studentdata.csv')
studentData

# In[100]:

studentData.head(30)

# In[101]:

luke = studentData.tail(30)
luke

# In[102]:

luked = luke.plot(kind="hist")

# In[103]:

breast_cancer = load_breast_cancer()

# In[104]:

print("Feature Names", breast_cancer.feature_names)
print("Target: ", breast_cancer.target_names)
print("Dataset Size", breast_cancer.data.shape)

breast_cancer_df = pd.DataFrame(breast_cancer.data, columns=breast_cancer.feature_names)
breast_cancer_df['Target'] = breast_cancer.target
breast_cancer_df['Target'] = ['Malignant' if typ==0 else 'Benign' for typ in breast_cancer_df['Target']]
breast_cancer_df.head()

# ## CREATING INDIVIDUAL PLOT

# In[105]:

with plt.style.context(("seaborn","ggplot")):
    color = {"Malignant": "tab:red", "Benign":"tab:green"}
    plt.figure(figsize=(10,5))
    for tumor_typ in breast_cancer_df["Target"].unique():
        plt.scatter(breast_cancer_df[breast_cancer_df["Target"]==tumor_typ]["mean radius"],
                   breast_cancer_df[breast_cancer_df["Target"]==tumor_typ]["mean texture"],
                    c = color[tumor_typ],
                    s=200,
                    alpha = 0.6,
                    label = tumor_typ)
        plt.xlabel("mean radius")
        plt.ylabel("mean texture")
        plt.title("mean radius vs mean texture Scatter Plot")
        plt.legend(title = "Tumor Type", loc="best")

# In[106]:

with plt.style.context(("seaborn","ggplot")):
    avg_radius_per_tumor_typ = breast_cancer_df.groupby(by ="Target").mean()[["mean radius"]]

    plt.figure(figsize=(5,5))
    plt.bar(avg_radius_per_tumor_typ.index,
           avg_radius_per_tumor_typ["mean radius"],
           color="tab:blue",
           width=0.6)
    plt.ylabel("mean radius")
    plt.title("Average mean radius per tumor type")

# In[2]:

df = px.data.tips()
fig = px.pie(df, values='tip', names='day', color='day',
             color_discrete_map={'Thur':'lightcyan',
                                 'Fri':'cyan',
                                 'Sat':'royalblue',
                                 'Sun':'darkblue'})
fig.show()

# In[3]:

df = px.data.tips()
fig = px.pie(df, values='tip', names='day',color='day',
            color_discrete_map={'Thur':'lightgreen','Fri':'cyan'}
            )
fig.show()

# In[109]:

text = widgets.Text()
display(text)

def handle_submit(sender):
    print(text.value)

text.on_submit(handle_submit)

# In[110]:

btn = widgets.Button(description = "Click ME")

text = widgets.Text()
display(text)
display(btn)
def on_btn_click(b):
    print(''+text.value)
    #print(" Button Clicked")
# call back the function using .on_click method
text.on_submit(handle_submit)
btn.on_click(on_btn_click)

# In[114]:

from ipywidgets import *
from IPython.html.widgets import *

# In[4]:

t = arange(0.0, 1.0, 0.01)

def pltsin(f):
    plt.plot(x,sin(2*pi*t*f))
    plt.show()

interact(pltsin, f=(1,10,0.1))

# In[6]:

import numpy as np
import os
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns

# ## LINE GRAPH

# In[11]:

a = np.array([1,2,3,4,5])

plt.plot(a)
plt.show

# ### ploting two lines in a graph

# In[13]:

a = np.array([1,2,3,4,5])
b = np.array([2,2,1,4,10])

plt.plot(a)
plt.plot(b)
plt.show()

# #### Increase the size of the graph

# In[20]:

plt.figure(figsize = (10,5))
a = np.array([1,2,3,4,5])
b = np.array([2,2,1,4,10])

plt.plot(a)
plt.plot(b)
plt.show()

# ##### changing color of line

# In[22]:

plt.figure(figsize = (10,5))
a = np.array([1,2,3,4,5])
b = np.array([2,2,1,4,10])

plt.plot(a, color='green')
plt.plot(b, color='red')
plt.show()

# In[40]:

plt.figure(figsize = (10,5))
a = np.array([1,2,3,4,5])
b = np.array([2,2,1,4,10])
c = np.array([3,2,4,6,5])

plt.plot(a, color='green', linestyle = '--')
plt.plot(b, color='red')
plt.plot(c,  linewidth=3)

#endevour to put plt.show
plt.show()

# In[55]:

plt.title("Rent Per Month Somehow", fontsize=20, fontweight="bold")
plt.figure(figsize= (10,10))
a = np.array([1,2,3,4,5])
b = np.array([2,2,1,4,10])
c = np.array([3,2,4,6,5])

plt.plot(a, color='green', linestyle = '--', label = "Rent for all")
plt.plot(b, color='red', label = "Rent for Fy")
plt.plot(c,  linewidth=3, label = "Rent for FZ")

#set location of legend
plt.legend(loc = "upper left")
plt.xticks(range(0, len(a)),("", "","","",""))
#endevour to put plt.show
plt.show()

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:


from typing import Any, MutableMapping

import structlog

from starlette_context import context

def setup_logging():
    import logging.config

    def add_app_context(
        logger: logging.Logger,
        method_name: str,
        event_dict: MutableMapping[str, Any],
    ) -> MutableMapping[str, Any]:
        if context.exists():
            event_dict.update(context.data)
        return event_dict

    structlog.configure(
        processors=[
            structlog.processors.TimeStamper(fmt="iso"),
            add_app_context,
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.AsyncBoundLogger,
        cache_logger_on_first_use=True,
    )

    logging_config = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "json": {
                "()": structlog.stdlib.ProcessorFormatter,
                "processor": structlog.processors.JSONRenderer(),
            },
        },
        "handlers": {
            "json": {
                "level": "DEBUG",
                "class": "logging.StreamHandler",
                "formatter": "json",
            },
        },
        "loggers": {
            "starlette_context_example": {
                "handlers": ["json"],
                "level": "INFO",
            },
            "uvicorn": {"handlers": ["json"], "level": "INFO"},
            "uvicorn.error": {"handlers": ["json"], "level": "INFO"},
            "uvicorn.access": {
                "handlers": ["json"],
                "level": "INFO",
                "propagate": False,
            },
        },
    }
    logging.config.dictConfig(logging_config)


"""play.py

Play a bin file.
"""

import sys
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
from constants import FRAME_NCOLS, FRAME_NROWS
from visualize import to_24bit

def main() -> None:
    try:
        bin_path = Path(sys.argv[1])
    except IndexError:
        sys.stderr.write(f"Expected a file.\n")
        sys.exit(22)

    bin_bytes = np.fromfile(bin_path, dtype=np.uint8)

    num_frames, remainder = divmod(bin_bytes.size, FRAME_NROWS * FRAME_NCOLS)
    assert remainder == 0

    frames = np.reshape(bin_bytes, (num_frames, FRAME_NROWS, FRAME_NCOLS))

    for frame_num, frame in enumerate(frames):
        plt.figure()
        plt.title(f"{bin_path}: Frame {frame_num}")
        plt.imshow(to_24bit(frame))

    plt.show()

if __name__ == "__main__":
    main()


import asyncio

from telethon import events
from telethon.errors.rpcerrorlist import MessageDeleteForbiddenError
from telethon.tl.types import ChannelParticipantsAdmins

from sbb_b import sbb_b
from ..core.session import tgbot

OWNER_ID = sbb_b.uid
# حتى يتعرف من ان المستخدم مشرف
async def is_administrator(user_id: int, message):
    admin = False
    async for user in tgbot.iter_participants(
        message.chat_id, filter=ChannelParticipantsAdmins
    ):
        if user_id == user.id or OWNER_ID:
            admin = True
            break
    return admin

@tgbot.on(events.NewMessage(pattern="^تنظيف$"))
async def purge(event):
    chat = event.chat_id
    msgs = []

    if not await is_administrator(user_id=event.sender_id, message=event): # حتى يتاكد من كود الاشراف بسطر 12
        await event.reply("- يجب ان تكون مشرف اولا")
        return

    msg = await event.get_reply_message()
    if not msg: #حتى يتاكد انك سويت رد لرسالة
        await event.reply("يجب عليك الرد على الرسالة لحذف الرسائل التي تحتها")
        return

    try:
        msg_id = msg.id
        count = 0
        to_delete = event.message.id - 1
        await tgbot.delete_messages(chat, event.message.id)
        msgs.append(event.reply_to_msg_id)
        for m_id in range(to_delete, msg_id - 1, -1):
            msgs.append(m_id)
            count += 1
            if len(msgs) == 100:
                await tgbot.delete_messages(chat, msgs)
                msgs = []

        await tgbot.delete_messages(chat, msgs)
        del_res = await tgbot.send_message(
            event.chat_id, f"تم مسح {count} رسالة بنجاح ."
        )

        await asyncio.sleep(4)
        await del_res.delete()

    except MessageDeleteForbiddenError:
        text = (
            "فشل في حذف الرسائل\n"
            + "يبدو ان الرسائل قديمة جدا او انا لست مشرف"
        )
        del_res = await respond(text, parse_mode="md")
        await asyncio.sleep(5)
        await del_res.delete()

@tgbot.on(events.NewMessage(pattern="^مسح$"))
async def delete_msg(event):
    if not await is_administrator(user_id=event.sender_id, message=event):
        await event.reply("انت لست مشرف لاستخدام هذا الامر")
        return
    chat = event.chat_id
    msg = await event.get_reply_message()
    if not msg:
        await event.reply("يجب عليك الرد على الرسالة اولا")
        return
    to_delete = event.message
    chat = await event.get_input_chat()
    rm = [msg, to_delete]
    await tgbot.delete_messages(chat, rm)


#!/usr/bin/env python

#Utility Imports
import importlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import pickle
import sys

#Other Module Imports
from faser_math import tm
from faser_plotting.Draw.Draw import DrawRectangle, DrawAxes, drawMesh
from faser_robot_kinematics import loadArmFromURDF
from faser_utils.disp.disp import disp, progressBar

#This Module Imports
from robot_link import RobotLink
from alpha_shape import AlphaShape
from workspace_analyzer import WorkspaceAnalyzer
from workspace_helper_functions import score_point, post_flag
from workspace_helper_functions import load_point_cloud_from_file, sort_cloud
from workspace_viewer import view_workspace

#The ALPHA_VALUE is the alpha parameter which determines which points are included or
# excluded to create an alpha shape. This is technically an optional parameter,
# however when the alpha shape optimizes this itself, for large clouds of points
# it can take many hours to reach a solution. 1.2 is a convenient constant tested on a variety
# of robot sizes.
ALPHA_VALUE = 1.2

#The TRANSPARENCY_CONSTANT determines the degree of transparency at which 3d objects are shown
# when plotted within matplotlib.
TRANSPARENCY_CONSTANT = .5

done = False

cmd_str_help = 'help'
cmd_str_exit = 'exit'
cmd_str_load_robot = 'loadRobot'
cmd_str_analyze_task_manipulability = 'analyzeTaskSpaceManipulability'
cmd_str_analyze_total_workspace_exhaustive = 'exhaustiveMethodTotalWorkspace'
cmd_str_analyze_total_workspace_alpha = 'alphaMethodTotalWorkspace'
cmd_str_analyze_unit_shell_manipulability = 'unitShellManipulability'
cmd_str_analyze_manipulability_object_surface = 'objectSurfaceManipulability'
cmd_str_manipulability_within_volume = 'manipulabilityWithinVolume'
cmd_str_manipulability_over_trajectory = 'manipulabilityOverTrajectory'
cmd_str_manipulability_over_trajectory_cloud = 'manipulabilityFromCloudTrajectory'
cmd_str_view_manipulability_space = 'viewManipulabilitySpace'
cmd_str_brute_manipulability = 'analyzeBruteManipulability'
cmd_str_analyze_force_norm = 'analyzeForceNorm'
cmd_str_analyze_velocity_norm = 'analyzeVelocityNorm'
cmd_str_analyze_wrench_torques = 'analyzeWrenchTorques'
cmd_str_schedule_sequence = 'newSequence'
cmd_str_start_sequence = 'startSequence'
cmd_str_view_sequence = 'viewSequence'
cmd_str_load_sequence = 'loadSequence'
cmd_str_save_sequence = 'saveSequence'

valid_commands = [
    cmd_str_help,
    cmd_str_exit,
    cmd_str_load_robot,
    cmd_str_analyze_task_manipulability,
    cmd_str_analyze_total_workspace_exhaustive,
    cmd_str_analyze_total_workspace_alpha,
    cmd_str_analyze_unit_shell_manipulability,
    cmd_str_analyze_manipulability_object_surface,
    cmd_str_manipulability_over_trajectory_cloud,
    cmd_str_brute_manipulability,
    cmd_str_manipulability_within_volume,
    cmd_str_manipulability_over_trajectory,
    cmd_str_view_manipulability_space,
    cmd_str_schedule_sequence,
    cmd_str_analyze_force_norm,
    cmd_str_analyze_velocity_norm,
    cmd_str_analyze_wrench_torques,
    cmd_str_start_sequence,
    cmd_str_view_sequence,
    cmd_str_load_sequence,
    cmd_str_save_sequence
]

# General_Flags
cmd_flag_save_results = '-o'
cmd_flag_input_file = '-f'
cmd_flag_num_iters = '-numIterations'
cmd_flag_plot_results = '-plot'
cmd_flag_parallel = '-parallel'
cmd_flag_manip_res = '-manipulationResolution'
cmd_flag_jacobian_manip = '-useJacobian'
cmd_flag_collision_detect = '-checkCollisions'

# Robot Loading Flags
cmd_flag_from_urdf = '-fromURDF'
cmd_flag_from_file = '-fromPyFile'
cmd_flag_from_directory = '-dir'

# 6DOF Manipulability Flags
cmd_flag_shells_range = '-range'
cmd_flag_shells_num_shells = '-numShells'
cmd_flag_shells_points_per_shell = '-numShellPoints'

# Object Surface Flags
cmd_flag_object_scale = '-scale'
cmd_flag_object_pose = '-pose'
cmd_flag_object_exempt_ee = '-exemptEE'
cmd_flag_object_bound_volume = '-boundVolume'
cmd_flag_object_min_dist = '-minDist'
cmd_flag_object_approx_collide = '-approxCollisions'

# Within Volume Flags
cmd_flag_volume_grid_res = '-gridResolution'

# Over Trajectory Flags
cmd_flag_trajectory_unit_manip = '-unitSphereManipulability'
cmd_flag_trajectory_interpolation = '-interpolationDist'
cmd_flag_trajectory_arc_interp = '-arcInterpolation'

#Workspace Viewer Flags
cmd_flag_viewer_draw_alpha = '-alphaFile'
cmd_flag_viewer_draw_slices = '-draw3DSlices'

#Vels and Torques Flags
cmd_flag_jac_norm = '-targetNorm'
cmd_flag_jac_grav = '-grav'
cmd_flag_jac_origin = '-massCG'
cmd_flag_jac_mass = '-mass'
cmd_flag_jac_angular = '-angular'

#Brute Manipulability Joints Flags
cmd_flag_brute_excluded = '-exclude'

#Trajectory point cloud flags
cmd_flag_point_traj_cloud = '-pointCloud'
cmd_flag_point_traj_trajectory = '-trajectory'

class CommandExecutor:
    """Superclass for workspace command line, to enable use in other applications more easily"""

    def __init(self):
        """Initialize Command Executor"""
        self.analyzer = None
        self.ready = False
        self.done = False
        self.exhaustive_pose_cloud = None
        self.functional_pose_cloud = None
        self.pose_results_6dof_shells = None
        self.pose_results_object_surface = None
        self.pose_results_manipulability_volume = None
        self.pose_results_manipulability_trajectory = None
        self.sequence = []

    def load_args(self, args):
        if '-sequence' in args:
            sequence_file_name = post_flag('-sequence', args)
            with open(sequence_file_name) as input_file:
                lines = input_file.readlines()
                for line in lines:
                    self.sequence.append(line.strip())
            self.cmd_start_sequence()
            return
    def set_sequence_at_launch(self, args):
        """
        Sets a launch sequence so that it can be executed straight from the command line
        Args:
            args: Argument sequence to set up
        """
        self.load_args(args)
        args = sys.argv[1:]
        self.load_args(args)

    def load_robot_from_urdf(self, fname):
        """
        Load a robot from URDF using FASER robotics conventions.
        Args:
            fname: file name of target file
        Returns:
            arm link instance
        """
        arm = loadArmFromURDF(fname)
        link = RobotLink(arm)

        def get_ee():
            return link.robot.getEEPos()

        def do_fk(x):
            return (link.robot.FK(x), True)

        def do_ik(x):
            return link.robot.IK(x, protect=True)

        def get_jt():
            return link.robot.getJointTransforms()

        link.bind_ee(get_ee)
        link.bind_fk(do_fk)
        link.bind_ik(do_ik)
        link.bind_jt(get_jt)
        link.joint_mins = link.robot.joint_mins
        link.joint_maxs = link.robot.joint_maxs

        #Collision Support
        link.link_names = arm.link_names
        link.vis_props = arm.vis_props
        link.col_props = arm.col_props
        return link

    def load_robot_from_module(self, dir_name, file_name):
        """
        Load a robot from a module file
        Args:
            dir_name: directory path .
            file_name: module file name
        Returns:
            RobotLink: Resulting RobotLink Object
        """
        sys.path.append(dir_name)
        file_name_sanitized = file_name.replace('.py', '')
        temp_module = importlib.import_module(file_name_sanitized)
        return temp_module.load_arm()

    def cmd_load_robot(self, cmds):
        """
        loadRobot -from___ file_name.
        Loads a robot for use in the workspace analyzer
        -fromURDF loads from a URDF file using modern_robotics/faser_robotics
        -fromPyFile loads from a user-defined python file specifying the robot
            see the example_robot_module.py file for additional details
        """
        if cmd_flag_from_urdf in cmds:
            file_name = post_flag(cmd_flag_from_urdf, cmds)
            link = self.load_robot_from_urdf(file_name)
        if cmd_flag_from_directory in cmds and cmd_flag_from_file in cmds:
            dir_name = post_flag(cmd_flag_from_directory, cmds)
            file_name = post_flag(cmd_flag_from_file, cmds)
            link = self.load_robot_from_module(dir_name, file_name)
        self.analyzer = WorkspaceAnalyzer(link)
        self.ready = True
        return link

    def load_point_cloud_in_cmds(self, cmds):
        """
        Load pose cloud from a file:
        Args:
            cmds: command list
        Returns:
            [tm] tm list
        """
        if cmd_flag_input_file not in cmds:
            disp('A trajectory file is required')
            return None
        else:
            point_list = load_point_cloud_from_file(post_flag(cmd_flag_input_file, cmds))
        return point_list

    def save_results_flag(self, cmds):
        """
        Determine whether or not to save results
        Args:
            cmds: command list
        Returns:
            output boolean, output file name
        """
        out_file_name = ''
        save_output = False
        if cmd_flag_save_results in cmds:
            out_file_name = post_flag(cmd_flag_save_results, cmds)
            save_output = True
        return save_output, out_file_name

    def save_to_file(self, results, out_file_name):
        """
        Saves results to a file
        Args:
            results: results to save
            out_file_name: output file name
        """
        with open(out_file_name, 'wb') as fp:
            pickle.dump(results, fp)

    def plot_joints_to_ee(self, results, grid_rez=0.2):
        """
        Plots to matplotlib instance
        Args:
            results: results super-list
            grid_rez: size of cubes to plot
        """
        plt.figure()
        ax = plt.axes(projection='3d')
        for r in results:
            min = np.inf
            for j in r:
                if min > max(j[1]):
                    min = max(j[1])
            col = score_point(1 - min)
            DrawRectangle(
                tm([r[0][0], r[0][1], r[0][2], 0, 0, 0]),
                [grid_rez]*3, ax, c=col, a=TRANSPARENCY_CONSTANT)
        plt.show()

    def plot_reachability_space(self, pose_cloud):
        """
        Plots reachability space givin a pose cloud
        Args:
            pose_cloud: pose cloud to plot for
        """
        plt.figure()
        ax = plt.axes(projection='3d')
        alpha = AlphaShape(sort_cloud(pose_cloud), ALPHA_VALUE)
        my_cmap = plt.get_cmap('jet')
        ax.plot_trisurf(*zip(*alpha.verts),
            triangles=alpha.triangle_inds, cmap=my_cmap,alpha =.2, edgecolor='black')
        plt.show()

    def plot_manipulability_grid(self, results, grid_rez):
        """
        Plots a manipulability grid given results and a grid resolution

        Args:
            results: Results list
            grid_rez: grid resolution, in meters
        """
        plt.figure()
        ax = plt.axes(projection='3d')
        ax.set_xlim3d(-4,4)
        ax.set_ylim3d(-4,4)
        ax.set_zlim3d(-4,4)
        for r in results:
            score = r[1]
            col = score_point(score)
            DrawRectangle(
                tm([r[0][0], r[0][1], r[0][2], 0, 0, 0]),
                [grid_rez]*3, ax, c=col, a=TRANSPARENCY_CONSTANT)
        plt.show()

    def cmd_analyze_task_manipulability(self, cmds):
        """
        Analyze manipulability space
        -o : save output to file specified
        -plot: displays a basic plot of the results
        -manipResolution: desired manipulation resolution (number of unit sphere points) Default 25
        -parallel: Calculate in parallel for increased efficiency. Default false
        -useJacobian: use jacobian optimization instead of unit sphere.
            Faster, but may give less info. Default false
        -checkCollisions: enable collision checking
        """
        point_list = []
        manip_resolution = 25
        parallel = False
        use_jacobian = False
        collision_detect = False
        plot = False

        point_list = load_point_cloud_from_file(post_flag(cmd_flag_input_file, cmds))
        if point_list is None:
            return

        if cmd_flag_manip_res in cmds:
            manip_resolution = int(post_flag(cmd_flag_manip_res, cmds))
        parallel = cmd_flag_parallel in cmds
        use_jacobian = cmd_flag_jacobian_manip in cmds
        collision_detect = cmd_flag_collision_detect in cmds

        save_output, out_file_name = self.save_results_flag(cmds)
        plot = cmd_flag_plot_results in cmds

        results = self.analyzer.analyze_task_space_manipulability(point_list,
                manip_resolution, parallel, use_jacobian, collision_detect)

        self.pose_results_manipulability_volume = results
        if plot:
            self.plot_manipulability_grid(results, 0.1)
        if save_output:
            self.save_to_file(results, out_file_name)
        return results

    def cmd_analyze_total_workspace_exhaustive(self, cmds):
        """
        Analyze a robot's total reachability using joint variation (exponential time).
        -numIterations: number of interpolated positions per joint
        -o : save output to file specified
        -plot: displays a basic plot of the results
        """
        num_iterations = 12
        plot = False
        if cmd_flag_num_iters in cmds:
            num_iters_str = post_flag(cmd_flag_num_iters, cmds)
            num_iterations = int(num_iters_str)
        save_output, out_file_name = self.save_results_flag(cmds)
        plot = cmd_flag_plot_results in cmds
        pose_cloud = self.analyzer.analyze_total_workspace_exhaustive_point_cloud(num_iterations)
        if plot:
            self.plot_reachability_space(pose_cloud)
        self.exhaustive_pose_cloud = pose_cloud
        if save_output:
            self.save_to_file(pose_cloud, out_file_name)
        return pose_cloud

    def cmd_analyze_total_workspace_functional(self, cmds):
        """
        Analyze a robot's total reachability using single joint variation alpha shape projection.
        Generally runs in linear time
        -numIterations: number of interpolated positions per joint
        -o: save output to file specified
        -plot: displays a basic plot of the results
        """
        num_iterations = 25
        plot = False
        if cmd_flag_num_iters in cmds:
            num_iters_str = post_flag(cmd_flag_num_iters, cmds)
            num_iterations = int(num_iters_str)
        save_output, out_file_name = self.save_results_flag(cmds)
        plot = cmd_flag_plot_results in cmds
        pose_cloud = self.analyzer.analyze_total_workspace_functional(num_iterations)
        if plot:
            self.plot_reachability_space(pose_cloud)
        self.functional_pose_cloud = pose_cloud

        if save_output:
            self.save_to_file(pose_cloud, out_file_name)
        return pose_cloud

    def cmd_analyze_manipulability_unit_shells(self, cmds):
        """
        Analyzes workspace manipulability through IK and unit spheres.
        -range: maximum range (meters) of the largest unit sphere from robot origin. Default 4
        -numShells: number of concentric shells from the robot base to range. Default 30
        -numShellPoints: number of points to analyze per shell. Default 1000
        -checkCollisions: enable collision checking
        -plot: displays a basic plot of the results
        """
        shells_range = 4
        num_shells = 30
        points_per_shell = 1000
        plot = False
        if cmd_flag_shells_range in cmds:
            num_iters_str = post_flag(cmd_flag_shells_range, cmds)
            shells_range = int(num_iters_str)
        if cmd_flag_shells_range in cmds:
            num_iters_str = post_flag(cmd_flag_shells_range, cmds)
            num_shells = int(num_iters_str)
        if cmd_flag_shells_points_per_shell  in cmds:
            num_iters_str = post_flag(cmd_flag_shells_points_per_shell, cmds)
            points_per_shell = int(num_iters_str)
        collision_detect = cmd_flag_collision_detect in cmds
        save_output, out_file_name = self.save_results_flag(cmds)
        plot = cmd_flag_plot_results in cmds

        results = self.analyzer.analyze_6dof_manipulability(
            shells_range, num_shells, points_per_shell, collision_detect)
        self.pose_results_6dof_shells = results
        if plot:
            self.plot_manipulability_grid(results, 0.25)
        if save_output:
            self.save_to_file(results, out_file_name)
        return results

    def cmd_analyze_manipulability_object_surface(self, cmds):
        """
        Analyzes manipulability on the surface of an object
        -f: filename of the stl object to analyze
        -o: Save output to file. Default to unused
        -scale: scale of the object Default 1
        -pose: pose of the object. TAA such as [x,y,z,xr,yr,zr]. M/Rad. No spaces. Default identity
        -manipulationResolution: number of points in manipulation unit sphere. Default 25
        -checkCollisions: make sure no part of the arm intersects the object. Default True
        -exemptEE: exempt the end effector of the robot from collision checking. Default True
        -parallel: run in parallel for increased efficiency. Default False.
        -useJacobian: uses jacobian instead of unit sphere manipulability.
            Incompatible with pose checking
        -plot: displays a basic plot of the results
        -boundVolume: Optional bounds volume
        -minDist: Minimum distance between vertices to consider analyzing
        """
        object_file_name = ''
        object_scale = 1
        object_pose = tm()
        manip_resolution = 25
        collision_detect = True
        exempt_ee = True
        parallel = False

        plot = False
        use_jacobian = False
        shape = None
        min_dist = -1.0

        if cmd_flag_input_file in cmds:
            object_file_name = post_flag(cmd_flag_input_file, cmds)
        if cmd_flag_object_scale in cmds:
            obj_scale_str = post_flag(cmd_flag_object_scale, cmds)
            object_scale = float(obj_scale_str)
        if cmd_flag_object_pose in cmds:
            pose_str = post_flag(cmd_flag_object_pose, cmds)
            object_pose = tm([float(item) for item in pose_str[1:-1].split(',')])
        if cmd_flag_manip_res in cmds:
            manip_resolution = int(post_flag(cmd_flag_manip_res, cmds))
        collision_detect = cmd_flag_collision_detect in cmds
        exempt_ee = cmd_flag_object_exempt_ee in cmds
        parallel = cmd_flag_parallel in cmds
        save_output, out_file_name = self.save_results_flag(cmds)
        plot = cmd_flag_plot_results in cmds
        if cmd_flag_jacobian_manip in cmds:
            use_jacobian = True
        if cmd_flag_object_bound_volume in cmds:
            shape_fname = post_flag(cmd_flag_object_bound_volume, cmds)
            with open(shape_fname, 'rb') as fp:
                shape = AlphaShape(sort_cloud(pickle.load(fp)), ALPHA_VALUE)
        if cmd_flag_object_min_dist in cmds:
            min_dist = float(post_flag(cmd_flag_object_min_dist, cmds))

        results, mesh = self.analyzer.analyze_manipulability_on_object_surface(
            object_file_name, object_scale,
            object_pose, manip_resolution,
            collision_detect, exempt_ee,
            parallel, use_jacobian,
            min_dist, shape)

        if plot:
            plt.figure()
            ax = plt.axes(projection='3d')
            for r in results:
                score = r[1]
                if score < .1:
                    continue
                else:
                    col = score_point(score)
                    DrawRectangle(tm([r[0][0], r[0][1], r[0][2], 0, 0, 0]),
                        [.25] * 3, ax, c=col, a=TRANSPARENCY_CONSTANT)
            drawMesh(mesh, ax)
            plt.show()
        self.pose_results_object_surface = results
        if save_output:
            self.save_to_file(results, out_file_name)
        return results

    def cmd_analyze_manipulability_within_volume(self, cmds):
        """
        Analyze manipulability within the bounds of a volume.
        -f: filename of shape. Defaults to exhaustive or alpha total workspace if run prior.
            Required if not (.npy)
        -o: output file, if desired
        -gridResolution: desired grid resolution in meters. defaults to .25m
        -manipResolution: desired manipulation resolution (number of unit sphere points) Default 25
        -parallel: Calculate in parallel for increased efficiency. Default false
        -useJacobian: use jacobian optimization instead of unit sphere.
            Faster, but may give less info. Default false
        -plot: displays a basic plot of the results
        """
        shape_fname = ''
        shape = None
        if self.exhaustive_pose_cloud is not None:
            res = input('Would you like to use prior calculated exhaustive pose cloud? Y/N >')
            if res.lower() == 'y':
                shape = AlphaShape(sort_cloud(self.exhaustive_pose_cloud), ALPHA_VALUE)
        if self.functional_pose_cloud is not None:
            res = input('Would you like to use prior calculated functional pose cloud? Y/N >')
            if res.lower() == 'y':
                shape = AlphaShape(sort_cloud(self.functional_pose_cloud), ALPHA_VALUE)

        if shape is None:
            if cmd_flag_input_file in cmds:
                shape_fname = post_flag(cmd_flag_input_file, cmds)
            with open(shape_fname, 'rb') as fp:
                arr = sort_cloud(pickle.load(fp))
            shape = AlphaShape(arr, ALPHA_VALUE)
        if shape is None:
            disp('This function requires an alpha shape cloud as an input\n' +
                'Please either specify a .npy file name or run a pose cloud calculator')
            return

        manip_resolution = 25
        grid_resolution = .25

        if cmd_flag_manip_res in cmds:
            manip_resolution = int(post_flag(cmd_flag_manip_res, cmds))
        if cmd_flag_volume_grid_res in cmds:
            grid_resolution = float(post_flag(cmd_flag_volume_grid_res, cmds))
        parallel = cmd_flag_parallel in cmds
        save_output, out_file_name = self.save_results_flag(cmds)
        collision_detect = cmd_flag_collision_detect in cmds
        use_jacobian = cmd_flag_jacobian_manip in cmds
        plot = cmd_flag_plot_results in cmds
        results = self.analyzer.analyze_manipulability_within_volume(
            shape, grid_resolution, manip_resolution, parallel, use_jacobian, collision_detect)
        if plot:
            self.plot_manipulability_grid(results, grid_resolution)
        if save_output:
            self.save_to_file(results, out_file_name)
        return results

    def cmd_analyze_manipulability_over_trajectory(self, cmds):
        """
        Analyzes manipulability over a given trajectory.
        -f: required file name for trajectory in TAA format ([x,y,z,xr,yr,zr]) one per line
        -o: optional output file for saving results
        -unitSphereManipulability: use unit sphere manipulability instead of jacobian manipulability
        -manipResolution: desired manipulation resolution (number of unit sphere points) Default 25
        -interpolationDist: interpolate between points in unit trajectory by a given amount (meters)
        -arcInterpolation: use arc interpolation instead of linear interpolation
        -plot: displays a basic plot of the results
        """
        point_list = []
        manipulability_mode = 1
        manip_resolution = 25
        point_interpolation_dist = -1
        point_interpolation_mode = 1

        plot = False

        if cmd_flag_input_file not in cmds:
            disp('A trajectory file is required')
            return
        else:
            point_list = load_point_cloud_from_file(post_flag(cmd_flag_input_file, cmds))
        if cmd_flag_manip_res in cmds:
            manip_resolution = int(post_flag(cmd_flag_manip_res, cmds))
        if cmd_flag_trajectory_unit_manip in cmds:
            manipulability_mode = 2
        if cmd_flag_trajectory_interpolation in cmds:
            point_interpolation_dist = float(post_flag(cmd_flag_trajectory_interpolation, cmds))
        if cmd_flag_trajectory_arc_interp in cmds:
            point_interpolation_mode = 2
        collision_detect = cmd_flag_collision_detect in cmds
        save_output, out_file_name = self.save_results_flag(cmds)
        plot = cmd_flag_plot_results in cmds

        results = self.analyzer.analyze_manipulability_over_trajectory(
            point_list, manipulability_mode, manip_resolution,
            point_interpolation_dist, point_interpolation_mode, collision_detect)

        self.pose_results_manipulability_trajectory = results

        if plot:
            plt.figure()
            ax = plt.axes(projection='3d')
            for r in results:
                DrawAxes(r[0], r[1] / 2, ax)
                ax.scatter3D(r[0][0], r[0][1], r[0][2], c=score_point(r[1]), s=25)
            plt.show()
        if save_output:
            self.save_to_file(results, out_file_name)
        return results

    def cmd_view_manipulability_space(self, cmds):
        """
        Views a previously saved workspace
        -f: Results file to load (.dat)
        -alphafile: alpha shape file to load in (Optional)
        -draw3DSlices: Draw an interactive 3d representation of the alpha slices (Optional)
        """
        object_file_name = ''
        draw_alpha_shape = False
        alpha_shape_file = ''
        draw_slices = False
        if cmd_flag_input_file in cmds:
            object_file_name = post_flag(cmd_flag_input_file, cmds)
        if cmd_flag_viewer_draw_alpha in cmds:
            alpha_shape_file = post_flag(cmd_flag_viewer_draw_alpha, cmds)
            draw_alpha_shape = True
        if cmd_flag_viewer_draw_slices in cmds:
            draw_slices = True

        view_workspace(object_file_name, draw_alpha_shape, alpha_shape_file, draw_slices)

    def _relate_joints_to_ee(self, cmds):
        """
        Helper function for cmd_analyze_velocity_norm and cmd_analyze_torque_norms

        Args:
            cmds: command strings
        Returns:
            list: target values related to givens
        """
        target_norm = 0
        if cmd_flag_input_file in cmds:
            object_file_name = post_flag(cmd_flag_input_file, cmds)
        if cmd_flag_jac_norm in cmds:
            target_norm = float(post_flag(cmd_flag_jac_norm))
        angular = cmd_flag_jac_angular in cmds
        with open(object_file_name, 'rb') as fp:
            data = pickle.load(fp)
        results = self.analyzer.analyze_joint_related_to_end_effector_vals(
                data, target_norm, angular)
        save_output, out_file_name = self.save_results_flag(cmds)
        if save_output:
            self.save_to_file(results, out_file_name)
        if cmd_flag_plot_results in cmds:
            self.plot_points_to_ee(self, results)
        return results

    def cmd_analyze_velocity_norm(self, cmds):
        """
        Determine minimum joint velocities for a given end effector velocity norm
        -f: filename of previously created manipulability file
        -targetNorm: float target norm for EE velocity (linear or angular)
        -angular: use angular calculations instead of linear
        -plot: plot results
        -o: output file name
        """
        return self._relate_joints_to_ee(cmds)

    def cmd_analyze_force_norm(self, cmds):
        """
        Determine minimum joint torques for a given end effector force norm
        -f: filename of previously created manipulability file
        -targetNorm: float target norm for EE Torques (linear or angular)
        -angular: use angular calculations instead of linear
        -plot: plot results
        -o: output file name
        """
        return self._relate_joints_to_ee(cmds)

    def cmd_analyze_wrench_torques(self, cmds):
        """
        Given a mass, gravity (or force vector) and mass origin,
        calculate the joint torques of a robot at each point in manipulability
        -f: filename of previously created manipulability file
        -targetNorm: float target norm for EE Torques (linear or angular)
        -grav: gravity vector of the three-form [0,0,-9.81]
        -massCG: mass cg transform of the TAA form [0,1,2,np.pi/4,np.pi/7,np.pi/8]
        -mass: mass amount to apply (Kg) or N if using a force vector instead of gravity
        -plot: plot results
        -o: output file name
        """
        data = None
        mass = 10
        origin = tm()
        grav_vector = np.array([0, 0, -9.81])
        if cmd_flag_input_file in cmds:
            object_file_name = post_flag(cmd_flag_input_file, cmds)
        if cmd_flag_jac_grav in cmds:
            pose_str = post_flag(cmd_flag_jac_grav, cmds)
            grav_vector = np.array([float(item) for item in pose_str[1:-1].split(',')])
        if cmd_flag_jac_origin in cmds:
            pose_str = post_flag(cmd_flag_jac_origin, cmds)
            origin = tm([float(item) for item in pose_str[1:-1].split(',')])
        if cmd_flag_jac_mass in cmds:
            mass = float(post_flag(cmd_flag_jac_mass, cmds))
        with open(object_file_name, 'rb') as fp:
            data = pickle.load(fp)
        results = self.analyzer.analyze_matching_joint_torques(data, origin, mass, grav_vector)
        save_output, out_file_name = self.save_results_flag(cmds)
        if cmd_flag_plot_results in cmds:
            self.plot_points_to_ee(results, 0.1)
        if save_output:
            self.save_to_file(results, out_file_name)
        return results

    def cmd_analyze_brute_manipulability(self, cmds):
        """
        Analyze Manipulability using a brute-force FK based approach
        -numIterations: number of iterations per joint to use
        -exclude: list of joints [x,y,z...] to lock at 0 if desired.
        -checkCollisions: check collisions
        -parallel: Calculate in parallel for increased efficiency. Default false
        -plot: plot results
        -o: output file name
        """
        excluded = []
        num_iters = 15
        parallel = cmd_flag_parallel in cmds
        collision_detect = cmd_flag_collision_detect in cmds
        if cmd_flag_brute_excluded in cmds:
            exclude_str = post_flag(cmd_flag_brute_excluded, cmds)
            excluded = [int(item) for item in exclude_str[1:-1].split(',')]
        if cmd_flag_num_iters in cmds:
            num_iters = int(post_flag(cmd_flag_num_iters, cmds))
        results = self.analyzer.analyze_brute_manipulability_on_joints(
                num_iters, excluded, parallel, collision_detect)
        save_output, out_file_name = self.save_results_flag(cmds)
        if save_output:
            disp('saving results')
            self.save_to_file(results, out_file_name)
        if cmd_flag_plot_results in cmds:
            disp('plotting')
            plt.figure()
            ax = plt.axes(projection='3d')
            ax.set_xlim3d(-4,4)
            ax.set_ylim3d(-4,4)
            ax.set_zlim3d(-4,4)
            result_len = len(results)
            for i in range(result_len):
                r = results[i]
                progressBar(i, result_len-1, 'plotting')
                score = r[1]
                col = score_point(score)
                ax.scatter3D(r[0][0], r[0][1], r[0][2], s=1, color=col)
            plt.show()

    def cmd_analyze_point_cloud_with_trajectory(self, cmds):
        """
        Analyze the effects of a trajectory on a set of points, useful for
        determining whether or not objects can be removed from a surface or something similar

        -pointCloud: the point cloud used as the origin of trajectories
        -trajectory: the local trajectory applied to each point
        -o: optional output file for saving results
        -unitSphereManipulability: use unit sphere manipulability instead of jacobian manipulability
        -manipResolution: desired manipulation resolution (number of unit sphere points) Default 25
        -interpolationDist: interpolate between points in unit trajectory by a given amount (meters)
        -arcInterpolation: use arc interpolation instead of linear interpolation
        -plot: displays a basic plot of the results
        """
        point_list = []
        point_interpolation_dist = -1
        point_interpolation_mode = 1
        manipulability_mode = 1
        manip_resolution = 25
        plot = False
        parallel = False

        if cmd_flag_point_traj_cloud not in cmds:
            disp('A point cloud file is required')
            return
        else:
            point_list = load_point_cloud_from_file(post_flag(cmd_flag_point_traj_cloud, cmds))
        if cmd_flag_point_traj_trajectory not in cmds:
            disp('A trajectory cloud file is required')
            return
        else:
            trajectory = load_point_cloud_from_file(post_flag(cmd_flag_point_traj_trajectory, cmds))
        if cmd_flag_trajectory_interpolation in cmds:
            point_interpolation_dist = float(post_flag(cmd_flag_trajectory_interpolation, cmds))
        if cmd_flag_manip_res in cmds:
            manip_resolution = int(post_flag(cmd_flag_manip_res, cmds))
        if cmd_flag_trajectory_arc_interp in cmds:
            point_interpolation_mode = 2
        if cmd_flag_trajectory_unit_manip in cmds:
            manipulability_mode = 2
        parallel = cmd_flag_parallel in cmds
        collision_detect = cmd_flag_collision_detect in cmds

        results = self.analyzer.analyze_manipulability_point_cloud_with_trajectory(
            point_list, trajectory,
            manip_resolution, point_interpolation_dist, manipulability_mode,
            point_interpolation_mode, collision_detect, parallel)

        save_output, out_file_name = self.save_results_flag(cmds)
        plot = cmd_flag_plot_results in cmds
        if plot:
            plt.figure()
            ax = plt.axes(projection='3d')
            for traj in results:
                for r in traj[2]:
                    DrawAxes(r[0], r[1] / 2, ax)
                    ax.scatter3D(r[0][0], r[0][1], r[0][2], c=score_point(r[1]), s=25)
            plt.show()
        if save_output:
            self.save_to_file(results, out_file_name)

    def cmd_start_sequence(self):
        """
        Starts or repeats a sequence of commands
        previously defined by the user
        """
        disp('Starting Sequence')
        for cmd in self.sequence:
            self.cmd_parser(cmd)
        disp('Sequence Complete')

    def cmd_prepare(self, cmds):
        cmd_no_eq = cmds.replace('=', ' ')
        cmds_parsed = cmd_no_eq.split(' ')
        return cmds_parsed

    def cmd_parser(self, cmd):
        """
        Parse a command string.

        Args:
            cmd: command string passed in
        """
        cmds_parsed = self.cmd_prepare(cmd)
        if not cmds_parsed[0] in valid_commands:
            disp('Unrecognized command. Please try again. You can use \'help\' for help')
            return
        if cmds_parsed[0] == cmd_str_help:
            self.cmd_help(cmds_parsed)
            return
        if cmds_parsed[0] == cmd_str_exit:
            self.done = True
            return False
        elif cmds_parsed[0] == cmd_str_schedule_sequence:
            self.cmd_schedule_sequence()
            return
        elif cmds_parsed[0] == cmd_str_start_sequence:
            self.cmd_start_sequence()
            return
        elif cmds_parsed[0] == cmd_str_view_sequence:
            self.cmd_view_sequence()
            return
        elif cmds_parsed[0] == cmd_str_save_sequence:
            self.cmd_save_sequence(cmds_parsed)
            return
        elif cmds_parsed[0] == cmd_str_load_sequence:
            self.cmd_load_sequence(cmds_parsed)
            return
        elif cmds_parsed[0] == cmd_str_load_robot:
            self.cmd_load_robot(cmds_parsed)
            return
        if not self.ready:
            disp('Please load a robot first')
            return
        if cmds_parsed[0] == cmd_str_analyze_total_workspace_exhaustive:
            return self.cmd_analyze_total_workspace_exhaustive(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_analyze_task_manipulability:
            return self.cmd_analyze_task_manipulability(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_analyze_total_workspace_alpha:
            return self.cmd_analyze_total_workspace_functional(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_analyze_unit_shell_manipulability:
            return self.cmd_analyze_manipulability_unit_shells(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_analyze_manipulability_object_surface:
            return self.cmd_analyze_manipulability_object_surface(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_manipulability_within_volume:
            return self.cmd_analyze_manipulability_within_volume(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_manipulability_over_trajectory:
            return self.cmd_analyze_manipulability_over_trajectory(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_view_manipulability_space:
            return self.cmd_view_manipulability_space(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_brute_manipulability:
            return self.cmd_analyze_brute_manipulability(cmds_parsed)
        elif cmds_parsed[0] == cmd_str_manipulability_over_trajectory_cloud:
            return self.cmd_analyze_point_cloud_with_trajectory(cmds_parsed)

class WorkspaceCommandLine(CommandExecutor):
    """
    Provide command line access to workspace_analyzer .
    """

    def __init__(self, args = []):
        super().__init__()
        self.analyzer = None
        self.ready = False
        self.done = False
        self.exhaustive_pose_cloud = None
        self.functional_pose_cloud = None
        self.pose_results_6dof_shells = None
        self.pose_results_object_surface = None
        self.pose_results_manipulability_volume = None
        self.pose_results_manipulability_trajectory = None
        self.sequence = []
        self.set_sequence_at_launch(args)
        self.main_loop()

    def cmd_schedule_sequence(self):
        """
        Allows for scheduling a sequence of commands
        Presents a prompt to the user
        """
        self.sequence = []
        sequence_done = False
        disp('Welcome to sequence scheduler. To exit, type \'done\'')
        while not sequence_done:
            cmd = input('Sequence Command>')
            if cmd.strip().lower() == 'done':
                disp('Exiting. Use \'startSequence\' to start sequence')
                sequence_done = True
                continue
            confirm = input('Confirm Command?(Y/N)>')
            if confirm.strip().lower() != 'y':
                continue
            self.sequence.append(cmd)

    def cmd_load_sequence(self, cmds):
        """
        Loads a command sequence from file
        -f: file to load from
        """
        if cmd_flag_input_file in cmds:
            object_file_name = post_flag(cmd_flag_input_file, cmds)
            with open(object_file_name) as input_file:
                lines = input_file.readlines()
                for line in lines:
                    self.sequence.append(line.strip())
        elif len(cmds) > 1:
            object_file_name = cmds[1]
            with open(object_file_name) as input_file:
                lines = input_file.readlines()
                for line in lines:
                    self.sequence.append(line.strip())
        else:
            disp('Please specify a file')

    def cmd_save_sequence(self, cmds):
        """
        Saves a command sequence to file
        -f: file to save to
        """
        if cmd_flag_input_file in cmds:
            object_file_name = post_flag(cmd_flag_input_file, cmds)
            with open(object_file_name, 'w+') as output_file:
                for line in self.sequence:
                    output_file.write(line + '\n')
        else:
            disp('Please specify a file')

    def cmd_view_sequence(self):
        """
        If a user has previously defined a sequence of commands,
        this function will display them for the user to review
        """
        if len(self.sequence) == 0:
            disp('Nothing to show')
        else:
            for i in range(len(self.sequence)):
                disp(str(i+1) + ') ' + self.sequence[i])

    def main_loop(self):
        """Start Main Loop for Command Line Program."""
        self.done = False
        while not self.done:
            cmd = input('Command> ')
            self.cmd_parser(cmd)
            continue
            try:
                cmd = input('Command> ')
                self.cmd_parser(cmd)
            except Exception as e:
                disp('Command failure. Details: ' + str(e))

    def cmd_help(self, cmds):
        """
        Assist the user in function understanding.

        Args:
            cmds: command passed through, for specific help if needed
        """
        if len(cmds) > 1:
            if cmds[1] == cmd_str_exit:
                disp('Exits the program')
            elif cmds[1] == cmd_str_load_robot:
                disp(self.cmd_load_robot.__doc__)
            elif cmds[1] == cmd_str_analyze_task_manipulability:
                disp(self.cmd_analyze_task_manipulability.__doc__)
            elif cmds[1] == cmd_str_analyze_total_workspace_exhaustive:
                disp(self.cmd_analyze_total_workspace_exhaustive.__doc__)
            elif cmds[1] == cmd_str_analyze_total_workspace_alpha:
                disp(self.cmd_analyze_total_workspace_functional.__doc__)
            elif cmds[1] == cmd_str_analyze_unit_shell_manipulability:
                disp(self.cmd_analyze_manipulability_unit_shells.__doc__)
            elif cmds[1] == cmd_str_analyze_manipulability_object_surface:
                disp(self.cmd_analyze_manipulability_object_surface.__doc__)
            elif cmds[1] == cmd_str_manipulability_within_volume:
                disp(self.cmd_analyze_manipulability_within_volume.__doc__)
            elif cmds[1] == cmd_str_manipulability_over_trajectory:
                disp(self.cmd_analyze_manipulability_over_trajectory.__doc__)
            elif cmds[1] == cmd_str_manipulability_over_trajectory_cloud:
                disp(self.cmd_analyze_point_cloud_with_trajectory.__doc__)
            elif cmds[1] == cmd_str_schedule_sequence:
                disp(self.cmd_schedule_sequence.__doc__)
            elif cmds[1] == cmd_str_start_sequence:
                disp(self.cmd_start_sequence.__doc__)
            elif cmds[1] == cmd_str_view_sequence:
                disp(self.cmd_view_sequence.__doc__)
            elif cmds[1] == cmd_str_load_sequence:
                disp(self.cmd_load_sequence.__doc__)
            elif cmds[1] == cmd_str_save_sequence:
                disp(self.cmd_save_sequence.__doc__)
            elif cmds[1] == cmd_str_view_manipulability_space:
                disp(self.cmd_view_manipulability_space.__doc__)
            elif cmds[1] == cmd_str_brute_manipulability:
                disp(self.cmd_analyze_brute_manipulability.__doc__)
        else:
            disp('\nhelp [cmd] for details\nValid Commands:')
            for command in valid_commands:
                disp('\t'+command)
            disp('\n')

if __name__ == '__main__':
    command_line = WorkspaceCommandLine()