# Ricart & Agrawala
# Author: Magnus Karlson

from threading import Thread
import time
import socket
import sys
import random

if len(sys.argv) < 2 or int(sys.argv[1]) < 1:
    exit("Number of processes must be atleast 1!")

max_p_timeout = 5
max_cs_timeout = 10
total = int(sys.argv[1])
processes = []

HOST = '127.0.0.1'
PORT = 17000

class State:
    WANTED = 'WANTED'
    HELD = 'HELD'
    DONOTWANT = 'DO-NOT-WANT'

class Process(Thread):
    def __init__(self, index, total):
        Thread.__init__(self)
        self.index = index
        self.total = total
        self.timestamp = 0
        self.queue = []
        self.waitsResponse = []
        self.start_p_countdown()
        Thread(target=self.launch_server).start()

    def start_p_countdown(self):
        self.state = State.DONOTWANT
        self.countdown = random.randint(5, max_p_timeout)

    def start_cs_countdown(self):
        self.state = State.HELD
        self.countdown = random.randint(10, max_cs_timeout)

    # Process server listening requests
    def launch_server(self):
        s = socket.socket()
        s.bind((HOST, PORT + self.index))
        s.listen(self.total)
        while True:
            con, addr = s.accept()
            action, index, timestamp = con.recv(1024).decode().split('_')
            index = int(index)
            timestamp = int(timestamp)

            if action == 'CSASK':
                # Process doesn't want to access cs
                if self.state == State.DONOTWANT:
                    con.send('ok'.encode())
                # Process itself wants to access critical section
                elif self.state == State.WANTED:
                    # Req process has lower timestamp
                    if timestamp < self.timestamp:
                        con.send('ok'.encode())
                    # Queue current process
                    else:
                        con.send('notok'.encode())
                        self.queue.append(index)
                # Process is accessing cs, queue req
                elif self.state == State.HELD:
                    con.send('notok'.encode())
                    self.queue.append(index)

            elif action == 'CSEND':
                self.waitsResponse.remove(index)
                if len(self.waitsResponse) == 0:
                    self.start_cs_countdown()

    # RPC requests
    def send_access_req(self):
        for i in range(total):
            if i == self.index: continue
            # creates connection
            s = socket.socket()
            s.connect((HOST, PORT + i))
            s.send(f'CSASK_{self.index}_{self.timestamp}'.encode())

            # gets response
            res = s.recv(1024).decode()

            # stores processes, what response it waits
            if res != 'ok':
                self.waitsResponse.append(i)

            # closes connection
            s.close()

        if len(self.waitsResponse) == 0:
            self.start_cs_countdown()

    def send_cs_finished(self):
        for i in self.queue:
            s = socket.socket()
            s.connect((HOST, PORT + i))
            s.send(f'CSEND_{self.index}_{self.timestamp}'.encode())
            s.close()

        self.queue = []

    def run(self):
        while True:
            time.sleep(1)
            self.timestamp += 1

            if self.countdown == 0 or self.state not in [State.DONOTWANT, State.HELD]:
                continue

            self.countdown -= 1
            if self.countdown == 0:
                if self.state == State.DONOTWANT:
                    self.state = State.WANTED
                    self.send_access_req()
                elif self.state == State.HELD:
                    self.start_p_countdown()
                    self.send_cs_finished()

    def __str__(self):
        return f'P{self.index + 1}, {self.state}'

for i in range(total):
    time.sleep(0.05) # small delay between threads, so they don't do everything at the exact same time
    p = Process(i, total)
    processes.append(p)
    p.start()

while True:
    msg = input("Enter command (list | info | time-cs | time-p | exit): ")
    parts = msg.split(' ')
    cmd = parts[0].lower()
    param = parts[1] if len(parts) > 1 else ''

    if cmd == 'list':
        for p in processes:
            print(p)

    elif cmd == 'info':
        print(f'p-max-timeout {max_p_timeout}, cs-max-timeout {max_cs_timeout}, processes {len(processes)}')

    elif cmd == 'time-cs':
        if not param.isdigit() or int(param) < 10:
            print('Invalid CS timeout!')
            continue
        max_cs_timeout = int(param)

    elif cmd == 'time-p':
        if not param.isdigit() or int(param) < 5:
            print('Invalid p timeout!')
            continue
        max_p_timeout = int(param)

    elif cmd == 'exit':
        for p in processes:
            p.join()
        break


from django.http import JsonResponse, HttpResponse, QueryDict
from django.core.paginator import Paginator
from . import models
from . import forms
from django.views import View
from django.db.models import Q
from utils.tools import model_to_dict

class CaseView(View):
    def post(self,request):
        form = forms.CaseForm(request.POST)
        if form.is_valid():
            models.Case.objects.create(**form.cleaned_data)
            data = {'code': 0, 'msg': '添加成功'}
        else:
            data = {'code':-1,'msg':form.error_msg}
        return JsonResponse(data)

    def get_paginator(self,data_list):
        limit = self.request.GET.get('limit',20)
        page = self.request.GET.get('page',1)
        paginator = Paginator(data_list, limit)
        page_data = paginator.page(page)
        return page_data,paginator

    def get_search_data(self):
        data = []
        search = self.request.GET.get('search')
        if search:
            data = models.Case.objects.filter(Q(title__contains=search) |
                                       Q(desc__contains=search) |
                                       Q(url__contains=search) |
                                       Q(params__contains=search)
                                       )
        return data

    def get_filter_data(self):
        data = []
        filter_field = ['id','title','method']#支持通过哪些字段来过滤
        filter_dict = {} #{'id':1,'title':abc}
        for field in filter_field: #
            value = self.request.GET.get(field)
            if value:
                filter_dict[field] = value
        if filter_dict:
            data = models.Case.objects.filter(**filter_dict)
        return data

    def get(self,request):
        if self.get_filter_data():
            case_sets = self.get_filter_data()
        elif self.get_search_data():
            case_sets = self.get_search_data()
        else:
            case_sets = models.Case.objects.filter(is_delete=False) #查询所有的
        page_data,paginator = self.get_paginator(case_sets)
        data = []
        for c in page_data:
            d = model_to_dict(c)
            data.append(d)
        response = {'code': 0, 'msg': '成功', 'data': data,'count':paginator.count}
        return JsonResponse(response, json_dumps_params={'ensure_ascii': False})

    def delete(self,request):
        id = request.GET.get('id')
        models.Case.objects.filter(id=id).update(is_delete=True)
        response = {'code': 0, 'msg': '成功'}
        return JsonResponse(response, json_dumps_params={'ensure_ascii': False})

    def put(self,request):
        pass

class CaseSet(View):

    def post(self,request):
        form = forms.CaseSetForm(request.POST)  #name=xiaohei,desc=xxx
        if form.is_valid():
            form.save() #
            # models.CaseSet.objects.create(**form.cleaned_data)
            data = {'code': 0, 'msg': '添加成功'}
        else:
            data = {'code': -1, 'msg': form.error_msg }
        return JsonResponse(data)

    def get(self,request):

        page = request.GET.get('page',1)
        limit = request.GET.get('limit',20)
        #1、模糊查询，哪写字段支持模糊查询 name、desc
        search = request.GET.get('search')

        #2、某些字段来过滤，id、name http://127.0.0.1:8000/case?id=1&name=abc&search=xxx

        filter_field = ['id','name','is_delete'] #  #{id:1,name:2}
        filter_dic = {} #过滤的条件
        for field in filter_field:
            value = request.GET.get(field)
            if value:
                filter_dic[field] = value

        search_field = ['name','desc']
        q = Q()
        if search:
            for field in search_field:
                #Q(name__contains=search)
                d = {'%s__contains'%field : search }
                q = Q(**d)|q # {'desc__contains':小黑}

        #/case_set_new 1、全部 2、只传search  3、只查询条件 4、 查询条件和模糊查询都传了
        #
        case_sets = models.CaseSet.objects.filter(**filter_dic).filter(q)

        paginator = Paginator(case_sets,limit)

        data = []
        for c in paginator.page(page):
            d = model_to_dict(c) #把查出来的每条数据转成字典
            data.append(d)

        response = {'code': 0, 'msg': '成功', 'data': data,'count':paginator.count}
        return JsonResponse(response, json_dumps_params={'ensure_ascii': False})

    def delete(self,request):
        id = request.GET.get('id')
        models.Case.objects.delete(id=id)
        response = {'code': 0, 'msg': '成功'}
        return JsonResponse(response, json_dumps_params={'ensure_ascii': False})

# name qq id
#name password qq phone email  id
    def put(self,request):
        #request.GET ？url里面传的数据 /xxx?xxx=xx
        #request.POST ? body k-v，但是只能是post请求的时候取到
        #request.POST.get()
        # print('body...',request.body) #请求的正文信息

        case_set = models.CaseSet.objects.get(id=request.PUT.get('id'))

        form = forms.CaseSetForm(request.PUT,instance=case_set)  #name=xiaohei,desc=xxx
        if form.is_valid():
            form.save()
            data = {'code': 0, 'msg': '添加成功'}
        #如果你要实现只传某个字段就修改某个字段的话，就拿着请求参数里面的key和出错的key做比较，如果请求的key没有出现
        #在出错的key里面就说明这个参数通过了校验
        else:
            data ={'code':-1,'msg':form.error_msg}

        return JsonResponse(data)


#!/usr/bin/env python3
from itertools import combinations
from math import prod

with open("input.txt") as puzzle_input:
    INPUT = [int(line) for line in puzzle_input]

def find_combo(values, n, target=2020):
    for vs in combinations(values, n):
        if sum(vs) == target:
            return prod(vs)

print(f"Part One: {find_combo(INPUT, 2)}")
print(f"Part Two: {find_combo(INPUT, 3)}")


from flask import Flask
import userStorage
from flask import  request
app = Flask(__name__)

@app.route("/api/add_record", methods=['POST'])
def addUserController():
    response = user_repository.addUser(request.json)
    return response

@app.route("/api/last_10", methods=['GET'])
def getLast10UsersController():
    users = user_repository.getLast10()
    response = {
        "last 10 users": users
    }
    return response

@app.route("/api/delete/<int:id>", methods=["DELETE"])
def DeleteUserController(id: int):
    users = user_repository.userStorage
    user = list(filter(lambda x: x['id'] == id, users))
    if(len(user) == 0):
        return  "Not found"
    users.remove(user[0])
    return f"Removed record {user[0]}"

if __name__ == "__main__":
    user_repository = userStorage.UserRepository()
    app.run(host='0.0.0.0', port=8080 , debug=True)


import os
import pickle
from scipy.io.wavfile import read
from sklearn.mixture import GaussianMixture
from sklearn import preprocessing
import numpy as np
import python_speech_features as mfcc

#Calculate and returns the delta of given feature vector matrix
def calculate_delta(array):
    rows,cols = array.shape
    deltas = np.zeros((rows,20))
    N = 2
    for i in range(rows):
        index = []
        j = 1
        while j <= N:
            if i-j < 0:
                first = 0
            else:
                first = i-j
            if i+j > rows -1:
                second = rows -1
            else:
                second = i+j
            index.append((second,first))
            j+=1
        deltas[i] = ( array[index[0][0]]-array[index[0][1]] + (2 * (array[index[1][0]]-array[index[1][1]])) ) / 10
    return deltas

#convert audio to mfcc features
def extract_features(audio,rate):
    mfcc_feat = mfcc.mfcc(audio, rate, 0.025, 0.01,20,appendEnergy = True, nfft=1200)
    mfcc_feat = preprocessing.scale(mfcc_feat) #Standarization Gaussian with zero mean and unit variance.
    delta = calculate_delta(mfcc_feat)

    #combining both mfcc features and delta
    combined = np.hstack((mfcc_feat,delta))
    return combined

def add_user(user_id):

    name = user_id

    source = "./API/app/voice_database/" + name

    dest =  "./API/app/gmm_models/"
    count = 1

    for path in os.listdir(source):
        path = os.path.join(source, path)

        features = np.asfarray(())

        # reading audio files of speaker
        (sr, audio) = read(path)

        # extract 40 dimensional MFCC & delta MFCC features
        vector   = extract_features(audio,sr)

        if features.size == 0:
            features = vector
        else:
            features = np.vstack((features, vector))

        # when features of 3 files of speaker are exist and concatenated, then do model training
        if count == 3:
            gmm = GaussianMixture(n_components = 16, max_iter = 500, covariance_type='diag',n_init = 3)
            gmm.fit(features)

            # saving the trained gaussian model
            pickle.dump(gmm, open(dest + name + '.gmm', 'wb'))
            print(name + ' added successfully')

            features = np.asfarray(())
            count = 0
        count = count + 1

#The model will be created for every 3 voices in the folder.

def recognize(user_id, voice):
    audio = voice

    modelpath = "./API/app/gmm_models"

    gmm_files = [os.path.join(modelpath, fname) for fname in os.listdir(modelpath) if fname.endswith('.gmm')]
    print (gmm_files)

    models    = [pickle.load(open(fname,'rb')) for fname in gmm_files]

    speakers   = [fname.split("/")[-1].split(".gmm")[0] for fname in gmm_files]

    #read test file
    sr,audio = read(voice)

    # extract mfcc features
    vector = extract_features(audio,sr)
    log_likelihood = np.zeros(len(models))

    #checking with each model one by one
    for i in range(len(models)):
        gmm = models[i]
        scores = np.array(gmm.score(vector))
        log_likelihood[i] = scores.sum()
        print (scores)
        print (models[i])

    pred = np.argmax(log_likelihood)
    identity = speakers[pred]
    print(pred)
    print(identity)

    # if voice not recognized than terminate the process
    if identity == 'unknown':
        return ("Not Recognized! Try again...")

    # if voice is not the same then return unknown
    if identity != ("gmm_models\\" + user_id):
        return ("Unknown voice!")

    return (True)


# -*- coding: utf-8 -*-
import os.path
import sys

pharse_dict_loader = '''
from pypinyin import load_phrases_dict

def load():
    load_phrases_dict(phrases_dict)
'''

def remove_dup_items(lst):
    new_lst = []
    for item in lst:
        if item not in new_lst:
            new_lst.append(item)
    return new_lst

def parse(in_fp):
    phrases_dict = {}
    for line in in_fp.readlines():
        line = line.strip()
        if line.startswith('#') or not line:
            continue

        # 中国: zhōng guó
        data = line.split('#')[0]
        hanzi, pinyin = data.strip().split(':')
        hanzi = hanzi.strip()
        # [[zhōng], [guó]]
        pinyin_list = [[s] for s in pinyin.split()]

        if hanzi not in phrases_dict:
            phrases_dict[hanzi] = pinyin_list
        else:
            for index, value in enumerate(phrases_dict[hanzi]):
                value.extend(pinyin_list[index])
                phrases_dict[hanzi][index] = remove_dup_items(value)

    return phrases_dict

def generate_file(hanzi_pairs, out_fp):

    out_fp.write('''# -*- coding: utf-8 -*-
from __future__ import unicode_literals

# Warning: Auto-generated file, don't edit.
phrases_dict = {
''')

    for hanzi, pinyin_list in hanzi_pairs:
        #     中国: [[zhōng], [guó]]
        new_line = "    '{hanzi}': {pinyin_list},\n".format(
            hanzi=hanzi.strip(), pinyin_list=pinyin_list
        )
        out_fp.write(new_line)
    out_fp.write('}\n\n')

    out_fp.write(pharse_dict_loader)

def generate_multi_file(hanzi_pairs, out_name, num):
    base_name, ext = os.path.splitext(out_name)
    length = len(hanzi_pairs)
    file_names = []
    size = int(length / num)
    start = 0
    end = start + size
    for i in range(num + 1):
        file_name = '{}_{}{}'.format(base_name, i, ext)
        paris = hanzi_pairs[start:end]
        if not paris:
            continue

        with open(file_name, 'w') as fp:
            generate_file(paris, fp)

        file_names.append(file_name)
        start = end
        end = start + size

    with open(out_name, 'w') as fp:
        fp.write('''# -*- coding: utf-8 -*-
from __future__ import unicode_literals

# Warning: Auto-generated file, don't edit.
phrases_dict = {}

''')
        for i, name in enumerate(file_names):
            name = os.path.basename(name)
            base_name, _ = os.path.splitext(name)
            fp.write('''
from pypinyin_dict.phrase_pinyin_data import {0}
phrases_dict.update({0}.phrases_dict)
'''.format(base_name))

        fp.write(pharse_dict_loader)

def main(in_name, out_name, num):
    with open(in_name) as in_fp:
        hanzi_pairs = sorted(parse(in_fp).items(), key=lambda x: x[0])

    if num < 2:
        with open(out_name, 'w') as out_fp:
            generate_file(hanzi_pairs, out_fp)
    else:
        generate_multi_file(hanzi_pairs, out_name, num)

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print('python gen_phrases_dict.py INPUT OUTPUT [OUTPUT_FILE_NUM]')
        sys.exit(1)

    in_f = sys.argv[1]
    out_f = sys.argv[2]
    file_num = 1
    if len(sys.argv) > 3:
        file_num = int(sys.argv[3])

    main(in_f, out_f, file_num)


#!/usr/bin/env python
import os
import sys

import http.client
from urllib.parse import urlencode
import xml.etree.ElementTree as ET

from argparse import ArgumentParser, RawTextHelpFormatter

DESCRIPTION = """

© Prifysgol Bangor University

"""

marytts_p2ipa_mapping = {'A':'ä','{':'æ','@':'ɐ','O':'ɔ','aU':'aʊ','AI':'aɪ','b':'b','tS':'tʃ',
'd':'d','D':'ð','E':'ɛ','r=':'ɝ','EI':'eɪ','f':'f','g':'ɡ','h':'h','I':'ɪ','i':'i','dZ':'dʒ',
'k':'k','l':'l','m':'m','n':'n','N':'ŋ','@U':'oʊ','OI':'ɔɪ','p':'p','r':'ɹ','s':'s','S':'ʃ','t':'t',
'T':'θ','U':'ʊ','u':'u','V':'ɐ','v':'v','w':'w','j':'j','z':'z','Z':'ʒ'}

class MaryTTSAPI(object):

    def __init__(self):

        self.marytts_host = "127.0.0.1"
        self.marytts_port = 59125

    def get_allophones_xml(self, lang, text):
        """Given a message in message,
           return a response in the appropriate
           format."""

        raw_params = {"INPUT_TEXT": text,
                "INPUT_TYPE": "TEXT",
                "OUTPUT_TYPE": "ALLOPHONES",
                "LOCALE": lang,
                }

        params = urlencode(raw_params)
        headers = {}

        # Open connection to self.host, self.port.
        conn = http.client.HTTPConnection(self.marytts_host, self.marytts_port)

        conn.request("POST", "/process", params, headers)
        response = conn.getresponse()
        if response.status != 200:
            #print response.getheaders()
            raise RuntimeError("{0}: {1}".format(response.status, response.reason))
        return response.read()

    def g2a(self, lang, text):
        t = ''
        ns = {'m':'http://mary.dfki.de/2002/MaryXML'}

        allophones_xml = self.get_allophones_xml(lang, text)
        root = ET.fromstring(allophones_xml)

        for syllable in root.findall('./m:p/m:s/m:phrase/m:t/m:syllable', ns):
            for phoneme in syllable.findall('./m:ph', ns):
                mp = phoneme.attrib['p']
                t = t + marytts_p2ipa_mapping[mp]

            if 'stress' in syllable.attrib:
                stress = syllable.attrib['stress']
                if (stress == "1"):
                    t = t + "'"

            t = t + "."

        t = t[:-1]
        return t

def main(wordlist_file_path, locale, output_file_path, **args):
    mtts = MaryTTSAPI()
    with open(output_file_path, "w", encoding='utf-8') as out:
        with open(wordlist_file_path, "r", encoding='utf-8') as wordlist:
            for word in wordlist:
               word = word.rstrip()
               a = mtts.g2a(locale, word)
               print (a)
               out.write("%s\t%s\n" % (word, a))

if __name__ == "__main__":
    parser = ArgumentParser(description=DESCRIPTION, formatter_class=RawTextHelpFormatter)
    parser.add_argument("-w", dest="wordlist_file_path", required=True, help="lleoliad ffeiliau geiriau / location of wordlist file")
    parser.add_argument("-l", dest="locale", required=True, help="iaith ffeil rhestr geiriau / locale of word list")
    parser.add_argument("-o", dest="output_file_path", default="g2a.dict")
    parser.set_defaults(func=main)
    args = parser.parse_args()
    args.func(**vars(args))


from flask import Flask, request

app = Flask(__name__)
@app.route("/")
@app.route("/<path:path>")
def echo_server(path):
    url = request.full_path
    method = request.method
    header = request.headers
    data = request.data.decode()

    return f'''======
URL: {method} {url}
--
header:
{header}
--
data:
{data}
'''

if __name__ == "__main__":
    app.run('127.0.0.1', 8080)
    # app.run('0.0.0.0', 8080)


from pandas_datareader import data as pdr
import pandas as pd
import datetime as dt
from dateutil.parser import parse

import fix_yahoo_finance as fyf

def today_date():
    '''
    utils:
    get the datetime of today
    '''
    date=dt.datetime.now().date()
    date=pd.to_datetime(date)
    return date

def get_data(source, ticker, start_date='2014-01-01', end_date=today_date()):

    _available_sources = ['yahoo', 'google']

    _available_tickers = ['MSFT', 'AAPL', '^GSPC']

    if source not in _available_sources:
        raise ValueError('Source: [' + source + '] is not available try: ' + ' , '.join(available_sources))

    if ticker not in _available_tickers:
        raise ValueError('Ticker: [' + ticker + '] is not yet supported: try: ' + ' , '.join(available_tickers))

    try:
        if type(start_date) is str:
            start_date = parse(start_date)
        if type(end_date) is str and type(end_date) is not pd.Timestamp:
            end_date = parse(end_date)
    except ValueError as ve:
        print(ve)
        raise

    _source = source.lower()
    if _source == 'google':
        _panel_data = pdr.DataReader(ticker, _source, start_date, end_date)
    elif _source == 'yahoo':
        _panel_data = pdr.get_data_yahoo(ticker, start_date, end_date)
        # This is some additional column which is available from yahoo.
        _panel_data.drop('Adj Close', inplace=True, axis=1)
    else:
        raise ValueError("Data source is not handled at the moment")

    return _panel_data

def save_pckl(data, file):
    data.to_pickle(file)

def load_pckl(file):
    _data = pd.read_pickle(file)
    return _data


from .db import db, environment, SCHEMA, add_prefix_for_prod
from datetime import datetime

playlist_song = db.Table(
    'playlist_song',
    db.Model.metadata,
    db.Column('id', db.Integer, primary_key=True ),
    db.Column('song_id', db.Integer, db.ForeignKey(add_prefix_for_prod('songs.id'))),
    db.Column('playlist_id', db.Integer, db.ForeignKey(add_prefix_for_prod('playlists.id'))),
    db.Column('created_at', db.DateTime, default=datetime.now)
)

if environment == "production":
    playlist_song.schema = SCHEMA


#!/usr/bin/python

import argparse, codecs, datetime, pandas, sys, os, time
import suds, httplib, pprint
from sklearn.feature_extraction.text import CountVectorizer
from googleads import adwords
from progress.bar import Bar

reload(sys)
sys.setdefaultencoding("UTF-8")

COUNTRYCODES = {'MY': '2458', 'TH': '2764', 'ID': '2360', 'HK': '2344', 'SG': '2702', 'PH': '2608', 'VN': '2704'}

argparser = argparse.ArgumentParser(add_help=False)
argparser.add_argument('cc', type=str, help=('Country code(s).'))
argparser.add_argument('--stats', dest="stats", action='store_const', const=1, help=('Return keyword search volume.'))
argparser.add_argument('--ideas', dest="ideas", action='store_const', const=1, help=('Return keyword ideas.'))
argparser.add_argument('--tokenize', type=bool, help=('Tokenize each line of the input files into n-grams.'))
argparser.add_argument('--shingles', type=int, default=0, help=('Number of shingles.'))
argparser.add_argument('file', type=str, help=('File with one keyword per line.'))

PAGE_SIZE = 700

def query_adwords(service, ccs, keywords, rtype):
    data = {}

    retries = 3
    while retries > 0:

        selector = {
            'searchParameters': [
                {
                    'xsi_type': 'RelatedToQuerySearchParameter',
                    'queries': keywords
                },
                {
                    'xsi_type': 'LocationSearchParameter',
                    'locations': [{'id': COUNTRYCODES[cc.upper()]} for cc in ccs]
                },
                {
                    'xsi_type': 'NetworkSearchParameter',
                    'networkSetting': {
                        'targetGoogleSearch': True,
                        'targetSearchNetwork': False,
                        'targetContentNetwork': False,
                        'targetPartnerSearchNetwork': False
                    }
                }
            ],
            'ideaType': 'KEYWORD',
            'requestType': rtype,
            'requestedAttributeTypes': ['KEYWORD_TEXT', 'SEARCH_VOLUME', 'COMPETITION', 'AVERAGE_CPC',
                                        'TARGETED_MONTHLY_SEARCHES', 'CATEGORY_PRODUCTS_AND_SERVICES'],
            'paging': {
                'startIndex': str(0),
                'numberResults': str(PAGE_SIZE)
            },
            'currencyCode': 'USD'
        }

        try:
            data = service.get(selector)
        except suds.WebFault as e:
            if e.fault.faultstring == "[RateExceededError <rateName=RATE_LIMIT, rateKey=null, rateScope=ACCOUNT, retryAfterSeconds=30>]":
                retries = retries - 1
                pprint.pprint("WARNING: RateExceededError, sleeping 30 seconds, retries left #" + str(retries),
                              sys.stderr)
                time.sleep(30)
                continue
        except httplib.BadStatusLine as e:
            pprint.pprint(e, sys.stderr)

        retries = 0

    return data

def initialize_service():
    yaml_path = os.path.join(os.path.dirname(__file__), os.pardir, 'keyword-planner', 'googleads.yaml')
    client = adwords.AdWordsClient.LoadFromStorage(yaml_path)
    service = client.GetService('TargetingIdeaService')
    return service

def parse(cc, data):
    entries = []
    if 'entries' in data:
        for result in data['entries']:
            entry = {}
            entry['cc'] = cc
            entry['cat'] = 'Unknown'

            for attribute in result['data']:
                if attribute['key'] == 'KEYWORD_TEXT':
                    if 'value' in attribute['value']:
                        entry['keyword'] = attribute['value']['value']
                elif attribute['key'] == 'TARGETED_MONTHLY_SEARCHES':
                    if 'value' in attribute['value']:
                        for v in attribute['value']['value']:
                            if len(v) > 2:
                                entry[str(v[0]) + "-" + str(v[1])] = v[2]
                            else:
                                entry[str(v[0]) + "-" + str(v[1])] = 0
                elif attribute['key'] == 'COMPETITION':
                    if 'value' in attribute['value']:
                        entry['competition'] = attribute['value']['value']
                    else:
                        entry['competition'] = 0
                elif attribute['key'] == 'AVERAGE_CPC':
                    if 'value' in attribute['value']:
                        entry['cpc'] = int(attribute['value']['value']['microAmount']) / 1000000.0
                    else:
                        entry['cpc'] = 0
                elif attribute['key'] == 'CATEGORY_PRODUCTS_AND_SERVICES':
                    if 'value' in attribute['value']:
                        cats = []
                        for value in attribute['value']['value']:
                            try:
                                cats.append(TAX[value])
                            except KeyError:
                                pass
                        if len(cats) > 0:
                            # detect main category
                            for t in TAX_ORDER:
                                if t in cats:
                                    entry['cat'] = t
                                    break
            entries.append(entry)
    return entries

def read_file(filename, tokenize=False, shingles=0):
    with codecs.open(filename, 'r', 'utf-8') as f:
        lines = [x.strip('\n').strip(" ") for x in f.readlines()]

    if tokenize:
        newlines = []
        for line in lines:
            tmp = line
            while " " in tmp:
                tmp = line[0:tmp.rfind(" ")]
                newlines.append(tmp)
        lines = lines + newlines

    if shingles > 0:
        shingled_text = []
        for line in lines:
            vectorizer = CountVectorizer(ngram_range=(shingles, len(line)))
            analyzer = vectorizer.build_analyzer()
            shingled_text += analyzer(line)
        lines = shingled_text

    unique = set(lines)
    return list(unique)

TAX = dict()
TAX_ORDER = [
    'Vehicles', 'Computers & Consumer Electronics', 'Internet & Telecom',
    'Apparel', 'Sports & Fitness', 'Beauty & Personal Care', 'Health',
    'Business & Industrial', 'Food & Groceries', 'Home & Garden',
    'Family & Community', 'Arts & Entertainment', 'Hobbies & Leisure',
    'Occasions & Gifts', 'Finance', 'Travel & Tourism',
    'Retailers & General Merchandise', 'Real Estate', 'Jobs & Education',
    'Dining & Nightlife', 'Law & Government', 'News']

def read_taxonomy():
    taxonomy_file = os.path.join(os.path.dirname(__file__), os.pardir, 'keyword-planner', 'productsservices.csv')
    for line in open(taxonomy_file):
        l = line.split(",")
        try:
            TAX[int(l[0].strip('"'))] = (l[1].strip("\n").strip('"') + "/").split("/")[1]
        except ValueError:
            pass

def main(argv):
    args = argparser.parse_args()

    print >> sys.stderr, '# Start: Adwords Data: %s, %s' % (args.cc, datetime.datetime.now().time().isoformat())

    read_taxonomy()
    service = initialize_service()
    keywords = read_file(args.file, args.tokenize, args.shingles)

    bar = Bar('Processing', max=len(keywords), suffix='%(percent).1f%% - %(eta)ds')

    data = []
    if args.stats:
        # pagination of 800 items
        kws = keywords
        while len(kws) > 0:
            page = kws[0:PAGE_SIZE]
            kws = kws[PAGE_SIZE:]

            data = data + parse(args.cc, query_adwords(service, args.cc.split(","), page, "STATS"))

            bar.next(len(page))

    elif args.ideas:
        # pagination of 1 item
        for kw in keywords:
            data = data + parse(args.cc, query_adwords(service, args.cc.split(","), kw, "IDEAS"))

            bar.next()

    bar.finish()

    headers = sorted(data[0].keys(), reverse=True) if len(data) > 0 else [
        'keyword',
        'cpc',
        'competition',
        'cc',
        'cat',
        'search_volume',
        'year',
        'month',
        'product'
    ]
    output = pandas.DataFrame(data, columns=headers)
    if len(data) > 0:
        output = transpose_columns(output, product_name(args.file))
    output.to_csv(sys.stdout, header=True, index=False, encoding='utf-8-sig')

    print >> sys.stderr, '# End: Adwords Data: %s, %s' % (args.cc, datetime.datetime.now().time().isoformat())

def transpose_columns(df, product):
    id_vars = ['keyword', 'cpc', 'competition', 'cc', 'cat']

    if df.columns[4] != 'cat':
        del id_vars[4]

    df = df.melt(id_vars)
    df['year'], df['month'] = df['variable'].str.split('-', 1).str
    df.rename(columns={'value': 'search_volume'}, inplace=True)
    df.drop('variable', axis=1, inplace=True)
    df['product'] = product

    return df

def product_name(path):
    head = os.path.splitext(path)[0]
    return os.path.basename(head)

if __name__ == '__main__':
    main(sys.argv)


#!/usr/bin/python3
# coding=utf-8
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

pc_ds   =np.genfromtxt('ds.csv',delimiter=',')
ax=plt.figure().gca(projection='3d')
ax.plot(pc_ds[:,0],pc_ds[:,1],pc_ds[:,2],'.r',markersize=0.5)
ax.set_title('ds')
plt.show()

pc_scene=np.genfromtxt('scene.csv',delimiter=',')
ax=plt.figure().gca(projection='3d')
ax.plot(pc_scene[:,0],pc_scene[:,1],pc_scene[:,2],'.b',markersize=0.02)
ax.set_title('scene')
plt.show()

pc_out  =np.genfromtxt('out.csv',delimiter=',')
ax=plt.figure().gca(projection='3d')
ax.plot(pc_scene[:,0],pc_scene[:,1],pc_scene[:,2],'.b',markersize=0.5)
ax.plot(pc_out  [:,0],pc_out  [:,1],pc_out  [:,2],'.r',markersize=0.5)
ax.set_title('scene.csv with out.csv')
plt.show()


import asyncio

async def work(x):  # 通过async关键字定义一个协程
    for _ in range(3):
        print('Work {} is running ..'.format(x))

coroutine_1 = work(1)  # 协程是一个对象，不能直接运行

loop = asyncio.get_event_loop()
# task = loop.create_task(coroutine_1)  # 创建一个task方式一
task = asyncio.ensure_future(coroutine_1)  # 创建一个task方式二
print(task)
# run_until_complete接受的参数是一个future对象，当传入一个协程时，其内部自动封装成task对象。
# 所谓的task对象就是Future类的子类，它保存了协程运行后的状态，用于未来获取协程的结果
loop.run_until_complete(task)
print(task)

# 创建task后，task在加入事件循环之前是pending状态
# 因为上例中没有耗时操作，task很快会完成，后面打印finished状态

print(isinstance(task, asyncio.Future))  # 验证task是Future的子类
# isinstance() 与 type() 区别：
# type() 不会认为子类是一种父类类型，不考虑继承关系。
# isinstance() 会认为子类是一种父类类型，考虑继承关系。


import requests

API_ENDPOINT = "https://opentdb.com/api.php"
PARAMETERS = {
    "amount": 10,
    "type": "boolean",
    "category": 18,
}

response = requests.get(url=API_ENDPOINT, params=PARAMETERS)
response.raise_for_status()

question_data = response.json()["results"]


import sys

def read_matrix():
    (rows_count, columns_count) = [int(x) for x in input().split()]
    matrix = []
    for _ in range(rows_count):
        line = [int(x) for x in input().split()]
        matrix.append(line)
    return matrix

def get_submatrix_sum(matrix, row, col, size):
    the_sum = 0
    for r in range(row, row + size):
        for c in range(col, col + size):
            the_sum += matrix[r][c]
    return the_sum

def print_summatrix(matrix, row, col, size):
    for r in range(row, row + size):
        for c in range(col, col + size):
            print(matrix[r][c], end=' ')
        print()

size = 3
matrix = read_matrix()
best_position = None
best_value = -sys.maxsize - 1

for row in range(len(matrix) - size + 1):
    for col in range(len(matrix[row]) - size + 1):
        current_value = get_submatrix_sum(matrix, row, col, size)
        if best_value < current_value:
            best_value = current_value
            best_position = (row, col)

print(f'Sum = {best_value}')
(best_row, best_col) = best_position
print_summatrix(matrix, best_row, best_col, size)

labs and excercise documents/excercise_tasks/ex_3_multidimensional_lists/max_sum_2.py


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
    Python pointcloud viewer application for MKE sensors
"""
__author__ = 'Sujay Jayashankar', 'Kiran NV', 'Karthik Raja S'
__copyright__ = 'Copyright (c) 2021, Magik-Eye Inc.'

# =============================================================================

from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *

from .viewer import Ui_MainWindow
from vispy import app, visuals, scene, color

from queue import Queue
import time
from datetime import datetime as dt
import re
import threading
import numpy as np
import copy
import sys
import os

import pymkeapi

os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
# =============================================================================
# =============================================================================

class SimpleViewer(QtWidgets.QMainWindow):
    handle_connection_sig = QtCore.Signal(str)
    stop_viewer_sig = QtCore.Signal()
    start_viewer_sig = QtCore.Signal()

    # =========================================================================

    def __init__(self):
        super(SimpleViewer, self).__init__()
        current_dir = os.path.dirname(os.path.realpath(__file__))
        self.setWindowIcon(QIcon(os.path.join(current_dir,'icon/mke_logo.png')))
        self.settings = QSettings('MagikEye', 'PyMkeViewer')
        self.geometry = self.settings.value('geometry', bytes('', 'utf-8'))
        self.save_window = self.settings.value('savestate', bytes('', 'utf-8'))
        self._ui = Ui_MainWindow()
        self._ui.setupUi(self)
        self.windowWidth = self.width()
        self.windowHeight = self.height()
        self._ui.widget = QWidget(self._ui.openGLWidget)
        self._ui.widget.setGeometry(QRect(
            0, 0, self.windowWidth, self.windowHeight))
        self._ui.widget.setLayout(QVBoxLayout())
        self._ui.pushButtonStart.clicked.connect(
            lambda: self.render_widget_action(
                self._ui.pushButtonStart.text()))
        self._ui.checkBoxAxis.stateChanged.connect(
            lambda: self._thread.check_axis(
                self._ui.checkBoxAxis.isChecked()))
        self._ui.dockWidget_2.toggleViewAction().toggled.connect(self.update_dock_widget)
        self._ui.dockWidgetContents_2.dock_widget_sig.connect(self.update_dock_widget)
        self._ui.pushButtonRefresh.clicked.connect(self.refresh_device_manager)
        self._ui.pushButtonConnect.clicked.connect(
            lambda: self.handle_connection(self._ui.pushButtonConnect.text()))
        self._ui.actionExit.triggered.connect(self.close_application)

        self._thread = Worker(self._ui.widget)
        self._ui.horizontalSlider.valueChanged.connect(
            self._thread.update_point)
        self._ui.comboBox.activated.connect(self._thread.update_color)
        self._ui.comboBox_1.currentTextChanged.connect(self.handle_policy)
        self._thread.fps_text.connect(self._ui.fps.setText)
        self._thread.sensor_fps_text.connect(self._ui.sensor_fps.setText)
        self._thread.dfps_text.connect(self._ui.missed_frames.setText)
        self._thread.render_btn_text.connect(self._ui.pushButtonStart.setText)
        self._thread.disconnect_sig.connect(self.remote_connection)
        self._thread.opengl_loaded_sig.connect(self._ui.openglLoaded)

        self._remote_disconnection = False
        self.__disconnect = False
        self._radio_buttons = []
        self._ips = []
        self._selected_ip = None
        self._device_ids = []
        self._selected_device_id = None
        self.__connection_established = False
        self.handle_connection_sig.connect(self.handle_connection)
        self.start_viewer_sig.connect(self._thread.start_viewer)
        self.stop_viewer_sig.connect(self._thread.stop_viewer)

        self._msg = QMessageBox()
        self._msg.setWindowTitle("Connection Status")
        self._msg.setStyleSheet(
            u"color: rgb(255, 255, 255);\nbackground-color: rgb(46, 52, 54);")

        self.enable_color = u"color: rgb(255, 255, 255);\nbackground-color: rgb(46, 52, 54);"
        self.disable_color = u"color: rgb(0, 0, 0);\nbackground-color: rgb(186, 189, 182);"

        self._ui.pushButtonConnect.setDisabled(True)
        self._ui.pushButtonConnect.setStyleSheet(self.enable_color)
        self._ui.pushButtonStart.setDisabled(True)
        self._ui.checkBoxAxis.setDisabled(True)
        self._ui.horizontalSlider.setDisabled(True)
        self._ui.spinBox.setDisabled(True)
        self._ui.comboBox.setDisabled(True)
        self._ui.comboBox_1.setDisabled(True)

        self.save_point_size = self.settings.value("point_size",
                                        self._ui.horizontalSlider.value(),  type=int)
        self.save_point_color = self.settings.value("point_color",
                                        self._ui.comboBox.currentIndex(),  type=int)
        self.save_check_axis = self.settings.value("check_axis",
                                        self._ui.checkBoxAxis.isChecked(),  type=bool)
        self.read_config()

    # =========================================================================

    def read_config(self):
        """
        :Description: Function to restore previous settings.
        :Params: None
        :return: None
        """
        try:
            self.restoreGeometry(self.geometry)
            self.restoreState(self.save_window)
            self.update_dock_widget()
            if self.save_point_size >= 0 and self.save_point_size <= 100:
                self._ui.horizontalSlider.setValue(self.save_point_size)
                self._ui.spinBox.setValue(self.save_point_size)
                self._thread.update_point(self.save_point_size)
            else:
                self._ui.horizontalSlider.setValue(5)
                self._ui.spinBox.setValue(5)
                self._thread.update_point(5)
            if self.save_point_color >= 0 and self.save_point_color <= 26:
                self._ui.comboBox.setCurrentIndex(self.save_point_color)
                self._thread.update_color(self.save_point_color)
            else:
                self._ui.comboBox.setCurrentIndex(0)
                self._thread.update_color(0)
            if self.save_check_axis:
                self._ui.checkBoxAxis.setChecked(True)
                self._thread.check_axis(True)
            else:
                self._ui.checkBoxAxis.setChecked(False)
                self._thread.check_axis(False)

        except Exception as msg:
            print(
                f"Exception occurred in read_config() due to :: {str(msg)}")
            print(f"Resetting to Default Configurations.")

    # =========================================================================

    def render_widget_action(self, state):
        """
        :Description: Function to handle/route the start and stop rendering action.
        :Params: state [str]: state can be either 'Start Rendering' or 'Stop Rendering'
        :return: None
        """
        try:
            if state == 'Start Rendering':
                self.start_viewer_sig.emit()
                self._ui.comboBox_1.setDisabled(True)
                self._ui.comboBox_1.setStyleSheet(self.enable_color)
            elif state == 'Stop Rendering':
                self.stop_viewer_sig.emit()
                self._ui.comboBox_1.setDisabled(False)
                self._ui.comboBox_1.setStyleSheet(self.disable_color)

        except Exception as msg:
            print(
                f"Exception occurred in render_widget_action() due to :: {str(msg)}")

    # =========================================================================
    @QtCore.Slot()
    def update_dock_widget(self):
        """
        :Description: Function to update height and width based on the dockable widget state.
        :Params: None
        :return: None
        """
        try:
            self._ui.horizontalSlider.setGeometry(QRect
                (30, 420, self._ui.dockWidget_2.width()-111, 20))
            self._ui.spinBox.setGeometry(QRect
                (self._ui.dockWidget_2.width()-70, 420, 48, 21))
            self._ui.pushButtonRefresh.setGeometry(QRect
                (self._ui.dockWidget_2.width()-110, 220, 100, 25))
            self._ui.pushButtonStart.setGeometry(QRect
                (30, 310, self._ui.dockWidget_2.width()-60, 25))
            self._ui.listView.setGeometry(QRect
                (10, 290, self._ui.dockWidget_2.width()-20, 210))
            self._ui.verticalLayoutWidget.setGeometry(QRect
                (10, 40, self._ui.dockWidget_2.width()-20, 170))
            self._ui.groupBox.setGeometry(QRect
                (0, 0, self._ui.dockWidget_2.width()-20, 170))
            self._ui.comboBox.setGeometry(QRect
                (120, 460, self._ui.dockWidget_2.width()-150, 25))
            self._ui.comboBox_1.setGeometry(QRect
                (120, 560, self._ui.dockWidget_2.width()-150, 25))

            if self._ui.dockWidget_2.isVisible() and not self._ui.dockWidget_2.isFloating():
                self.windowWidth = self.width() - self._ui.dockWidget_2.width()
                self.update_tool_box()
            else:
                self.windowWidth = self.width()
                self.update_tool_box()
            self.windowHeight = self.height()
            self._ui.widget.setGeometry(QRect(
                0, 0, self.windowWidth, self.windowHeight))

        except Exception as msg:
            print(
                f"Exception occurred in update_dock_widget() due to :: {str(msg)}")

    # =========================================================================

    def update_tool_box(self):
        """
        :Description: Function to update the Checkbox of the Toolbox button
        :Params: None
        :return: None
        """
        try:
            if self._ui.dockWidget_2.isVisible():
                self._ui.actionToolbox.setChecked(True)
            else:
                self._ui.actionToolbox.setChecked(False)

        except Exception as msg:
            print(
                f"Exception occurred in update_tool_box() due to :: {str(msg)}")

    # =========================================================================

    def update(self):
        """
        :Description: Function to update the width and height of window
        :Params: None
        :return: None
        """
        try:
            if self._ui.dockWidget_2.isFloating() or not self._ui.dockWidget_2.isVisible():
                self.windowWidth = self.width()
            else:
                self.windowWidth = self.width() - self._ui.dockWidget_2.width()
            self.windowHeight = self.height()
        except Exception as msg:
            print(f"Exception occurred in update() due to :: {str(msg)}")

    # =========================================================================

    def resizeEvent(self, event):
        """
        :Description: Function to adjust geometry on resizing the Application.
        :Params: event
        :return: None
        """
        try:
            self.update()
            self._ui.widget.setGeometry(QRect(
                0, 0, self.windowWidth, self.windowHeight))
        except Exception as msg:
            print(f"Exception occurred in resizeEvent() due to :: {str(msg)}")

    # =========================================================================

    def closeEvent(self, event):
        """
        :Description: Function to stop the rendering and disconnect the device on closing the Application from Control Button.
        :Params: event
        :return: None
        """
        self.close_application()

    # =========================================================================

    def close_application(self):
        """
        :Description: Function to stop the rendering and disconnect the device on closing the Application from Exit Button.
        :Params: event
        :return: None
        """
        try:
            if self._thread.isRunning():
                if verbose:
                    print("closeEvent -> stop thread")
                self._thread.stop()
            if self.__disconnect:
                if verbose:
                    print("closeEvent -> disconnect signal")
                self.handle_connection_sig.emit("Disconnect")
            self.geometry = self.saveGeometry()
            self.save_window = self.saveState()
            self.settings.setValue('geometry', self.geometry)
            self.settings.setValue("point_size", self._ui.horizontalSlider.value())
            self.settings.setValue("point_color", self._ui.comboBox.currentIndex())
            self.settings.setValue("check_axis", self._ui.checkBoxAxis.isChecked())
            self.settings.setValue("savestate", self.save_window)
            sys.exit()

        except Exception as msg:
            print(f"Exception occurred in closeEvent() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot()
    def refresh_device_manager(self):
        """
        :Description: Function to update the available devices in the DeviceManager GUI
        :Params: None
        :return: None
        """
        try:
            # Set connect button disabled. Will be enabled once the device selected.
            if not self.__connection_established:
                self._ui.pushButtonConnect.setDisabled(True)
                self._ui.pushButtonConnect.setStyleSheet(self.enable_color)

            if self._ui.pushButtonStart.text() == "Start Rendering":
                QApplication.setOverrideCursor(Qt.WaitCursor)
                # Disable the connect Button
                self._ui.pushButtonConnect.setEnabled(False)
                self._ui.pushButtonConnect.repaint()

                # Locate Devices
                devices = locate_devices()

                # Clear the existing devices from DeviceManager
                existing_rows = self._ui.formLayout.rowCount()
                for row in range(int(existing_rows)):
                    self._ui.formLayout.removeRow(0)

                # Add new devices
                self._radio_buttons.clear()
                self._ips.clear()
                radio_button = QRadioButton()
                if self._ui.pushButtonConnect.text() == "Disconnect":
                    if self._selected_ip:
                        self._radio_buttons.append(radio_button)
                        self._ips.append(self._selected_ip)
                        self._device_ids.append(self._selected_device_id)
                        label = QLabel(
                            self._selected_ip + " " + self._selected_device_id)
                        self._ui.formLayout.addRow(radio_button, label)
                        if self.__connection_established:
                            radio_button.setChecked(True)
                            radio_button.setDisabled(True)

                if len(devices) > 0:
                    for index, device in enumerate(devices):
                        radio_button = QRadioButton()
                        radio_button.clicked.connect(self.allow_connect)
                        ip, _id = device['ip'], device['id']
                        # Retain the state of selected ip if connection established
                        if ip != self._selected_ip:
                            self._radio_buttons.append(radio_button)
                            self._ips.append(ip)
                            self._device_ids.append(_id)
                            label = QLabel(ip + " " + _id)
                            self._ui.formLayout.addRow(radio_button, label)
                            self._ui.scrollArea.setWidget(self._ui.groupBox)
                            self._ui.verticalLayout_2.addWidget(
                                self._ui.scrollArea)
                        if self._ui.pushButtonConnect.text() == "Disconnect":
                            radio_button.setDisabled(True)

                else:
                    if len(self._ips) == 0:
                        QApplication.restoreOverrideCursor()
                        self._msg.move(self._ui.widget.rect().center())
                        self._msg.setIcon(QMessageBox.Information)
                        self._msg.setText("No Magik Eye device found on the local network.\n"
                                        "Please connect a Magik Eye device and try again.")
                        self._msg.exec_()

                # Enable the Connections
                if len(self._ips) == 0:
                    self._ui.pushButtonConnect.setEnabled(False)
                else:
                    self._ui.pushButtonConnect.setEnabled(True)

            else:
                self._msg.move(self._ui.widget.rect().center())
                self._msg.setIcon(QMessageBox.Warning)
                self._msg.setText("Stop rendering before refreshing")
                self._msg.exec_()

            QApplication.restoreOverrideCursor()
        except Exception as msg:
            QApplication.restoreOverrideCursor()
            print(
                f"Exception occurred in refresh_device_manager() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot(str)
    def remote_connection(self, action):
        """
        :Description: Function to handle disconnection action when MKE sensor Connection is Interrupted
        :Params: action [str]: action can be either Connect or Disconnect
        :return: None
        """
        self._remote_disconnection = True
        self.handle_connection(action)

    # =========================================================================

    @QtCore.Slot(str)
    def handle_connection(self, action):
        """
        :Description: Function to handle connection or disconnection actions
        :Params: action [str]: action can be either Connect or Disconnect
        :return: None
        """
        try:
            if action == "Connect":
                self.connect_to_device()
                if self._ui.pushButtonConnect.text() == "Disconnect":
                    self._ui.pushButtonStart.setDisabled(False)
                    self._ui.pushButtonStart.setStyleSheet(self.disable_color)
                    self._ui.horizontalSlider.setDisabled(False)
                    self._ui.spinBox.setDisabled(False)
                    self._ui.comboBox.setDisabled(False)
                    self._ui.comboBox.setStyleSheet(self.disable_color)
                    self._ui.checkBoxAxis.setDisabled(False)
                    self._ui.comboBox_1.setDisabled(False)
                    self._ui.comboBox_1.setStyleSheet(self.disable_color)

            elif action == "Disconnect":
                self.disconnect_the_device()
                if self._ui.pushButtonConnect.text() == 'Connect':
                    self._ui.pushButtonStart.setDisabled(True)
                    self._ui.pushButtonStart.setStyleSheet(self.enable_color)
                    self._ui.horizontalSlider.setDisabled(True)
                    self._ui.spinBox.setDisabled(True)
                    self._ui.comboBox.setDisabled(True)
                    self._ui.comboBox.setStyleSheet(self.enable_color)
                    self._ui.checkBoxAxis.setDisabled(True)
                    self._ui.comboBox_1.setDisabled(True)
                    self._ui.comboBox_1.setStyleSheet(self.enable_color)

        except Exception as msg:
            print(
                f"Exception occurred in handle_connection() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot()
    def allow_connect(self):
        """
        :Description: Function to enable the connect button only when a device selected in GUI
        :Params: None
        :return: None
        """
        self._ui.pushButtonConnect.setDisabled(False)
        self._ui.pushButtonConnect.setStyleSheet(self.disable_color)

    # =========================================================================

    @QtCore.Slot()
    def connect_to_device(self):
        """
        :Description: Function to handle the Device connection feature.
        :Params: None
        :return: None
        """
        # Establishing actual connection with the Sensor
        for index, radio in enumerate(self._radio_buttons):
            if radio.isChecked():
                self._selected_ip = self._ips[index]
                self._selected_device_id = self._device_ids[index]
                break

        info_msg = ""
        self._msg.move(self._ui.widget.rect().center())
        if self._selected_ip:
            try:
                connection_status = self._thread.connect_sensor(
                    self._selected_ip)

                # List all available policies
                current_policy = self._thread.get_policy()
                self.list_policies = self._thread.list_policy()
                for index, policies in enumerate(self.list_policies):
                    if index == 0:
                        self._ui.comboBox_1.setItemText(index, policies)
                    else:
                        self._ui.comboBox_1.addItem(policies)
                self._ui.comboBox_1.setCurrentText(current_policy)
                if verbose:
                    print("IPs and Device IDs :", self._ips, self._device_ids)
                    print(f"Connecting to the ip = {self._selected_ip} ...")
                    print("CONNECTION RESULT: ", connection_status)
                    print("Current Policy : ", current_policy)
                    print("Available Policies :",self.list_policies)
                if connection_status:
                    self.setWindowTitle(
                        "Connected to MKE Sensor " + str(self._selected_device_id)
                        + "@" + str(self._selected_ip))
                    self.__disconnect = True
                    self.__connection_established = True
                    self._ui.pushButtonConnect.setText("Disconnect")
                    for index, radio in enumerate(self._radio_buttons):
                        if self._ips[index] == self._selected_ip:
                            radio.setChecked(True)
                            radio.setDisabled(True)
                        else:
                            radio.setDisabled(True)

                else:
                    self._msg.setIcon(QMessageBox.Warning)
                    info_msg = "Connection Failed"
                    self._msg.setText(info_msg)
                    self._selected_ip = None
                    self._msg.exec_()

            except Exception as msg:
                print(
                    f"Exception occurred in connect_to_device() due to :: {str(msg)}")

        else:
            info_msg = "Please select any device to connect"
            self._msg.setIcon(QMessageBox.Warning)
            self._msg.setText(info_msg)
            if verbose:
                print(info_msg)
            self._msg.exec_()

    # =========================================================================

    @QtCore.Slot()
    def disconnect_the_device(self):
        """
        :Description: Function to handle the Device disconnection feature.
        :Params: None
        :return: None
        """
        try:
            info_msg = ""
            if self._ui.pushButtonConnect.text() == "Disconnect":
                # Disconnect device
                self._msg.move(self._ui.widget.rect().center())
                if self._ui.pushButtonStart.text() == "Start Rendering":
                    self.setWindowTitle("PyMkEViewer")
                    self._ui.comboBox_1.clear()
                    self._ui.comboBox_1.addItem("None")
                    self._thread.disconnect_sensor()
                    self.__connection_established = False
                    self._selected_ip = None
                    self._msg.setIcon(QMessageBox.Critical)
                    if self._remote_disconnection is True:
                        info_msg = "MKE sensor connection interrupted:" \
                                   "\ndevice has been disconnected"
                        self._remote_disconnection = False
                        self._msg.setText(info_msg)
                        self._msg.exec_()
                        self.refresh_device_manager()
                    self.__disconnect = False
                    self._ui.pushButtonConnect.setText("Connect")
                    for index, radio in enumerate(self._radio_buttons):
                        radio.setEnabled(True)
                        radio.setCheckable(True)
                else:
                    self._msg.setIcon(QMessageBox.Warning)
                    info_msg = "Stop rendering before disconnecting"
                    self._msg.setText(info_msg)
                    self._msg.exec_()
                if verbose:
                    print("Disconnecting device")
                    print(info_msg)

        except Exception as err_msg:
            print(
                f"Exception occurred in disconnect_the_device() due to :: {str(err_msg)} ")
            info_msg = "Unable to disconnect the device.\n"\
                "Please try again.\nMore Info:{" + str(err_msg) + "}"
            self._msg.setIcon(QMessageBox.Warning)
            self._msg.setText(info_msg)
            self._msg.exec_()

    # =========================================================================

    def handle_policy(self,value):
        """
        :Description: Function to handle set policy
        :Params: None
        :return: None
        """
        try:
            if value in self.list_policies:
                self._thread.set_policy(value)
        except Exception as msg:
            print(f"Exception occured in handle_policy() due to :: {str(msg)}")

# =============================================================================

def locate_devices():
    """
    :Description: Function to locate the available Mke Sensors
    :Params: None
    :return: None
    """
    try:
        mke_sensors = []
        mke_discovery = pymkeapi.discover_devices()
        for key,value in mke_discovery.items():
            mke_sensors.append({'ip':value,'id':key})

        if verbose:
            print('MKE sensors: ')
            for mke_sensor in mke_sensors:
                print(mke_sensor['ip'], mke_sensor['id'])
        return mke_sensors

    except Exception as msg:
        print(f"Exception occured in locate_devices() due to :: {str(msg)}")
        QApplication.restoreOverrideCursor()
        msg_box = QMessageBox()
        msg_box.setStyleSheet(u"color: rgb(255, 255, 255);\n"
                          "background-color: rgb(46, 52, 54);")
        msg_box.setIcon(QMessageBox.Warning)
        msg_box.setText(f"Unable to locate the devices!\nmore Info:{str(msg)}")
        msg_box.exec_()

# =============================================================================
# =============================================================================

class Worker(QtCore.QThread):
    end_sig = QtCore.Signal()
    model_updated = QtCore.Signal()
    fps_update = QtCore.Signal()
    fps_text = QtCore.Signal(str)
    dfps_text = QtCore.Signal(str)
    sensor_fps_text = QtCore.Signal(str)
    render_btn_text = QtCore.Signal(str)
    disconnect_sig = QtCore.Signal(str)
    opengl_loaded_sig = QtCore.Signal()

    # =========================================================================

    def __init__(self, widget, parent=None):
        super().__init__(parent)
        if verbose:
            print("INIT")
        self.sensor_port = 8888  # Port at which mkert exec is running
        self.__color_options = [
            'autumn', 'blues', 'cool', 'greens', 'reds', 'spring', 'summer',
            'fire', 'grays', 'hot', 'ice', 'RdYeBuCy', 'winter', 'light_blues',
            'orange', 'viridis', 'coolwarm', 'PuGr', 'GrBu', 'GrBu_d', 'RdBu',
            'cubehelix', 'single_hue', 'hsl', 'husl', 'diverging']
        self._col = color.get_colormap('autumn')

        self._widget = widget

        self._stream = None
        self.__bus = None
        self.__client = None
        self._frame = None
        self.__process = -1
        self._FPS = 0
        self._FDPS = 0
        self._fps_window_size = 5
        self._queue_size = 10
        self._sensor_fps = 0
        self.__seq_timer_list = list()
        self._missed_frames = 0
        self.__viewer_start_time = 0
        self._point_size = 5
        self._FRAME_TYPE = 1

        self.__axis_created = False
        self.__axis_visibility = True

        self.__lock = threading.Lock()
        self._queue_buffer = Queue(self._queue_size)
        self._render_buffer = Queue(self._queue_size)
        self._fps_buffer = Queue(self._queue_size)
        self.__model = pymkeapi.Frame()

        self.__init_view_pyvis()

        scatter3d = scene.visuals.create_visual_node(visuals.MarkersVisual)
        self.__p1 = scatter3d(parent=self.__view.scene)
        self.__p1.set_gl_state('translucent', blend=True, depth_test=True)
        self._widget.layout().addWidget(self.__canvas.native)

        self.model_updated.connect(self.render_view)
        self.fps_update.connect(self.update_fps)
        self.end_sig.connect(self.stop_viewer)

    # =========================================================================

    def __init_view_pyvis(self):
        try:
            self.__canvas = scene.SceneCanvas(show=True)
            self.__view = self.__canvas.central_widget.add_view()
            self.__view.camera = 'turntable'
            self.__view.camera.fov = 45
            self.__view.camera.distance = 1
            self.__view.camera.up = 'z'
            if verbose:
                print("Center ", self.__view.camera.center)

            # Trivial XYZ axes lines that are initialized much faster:
            # self.__xyz = scene.XYZAxis(
            #     parent=self.__view.scene)

            QTimer.singleShot(200, self.__create_full_axis_once)  # run after 0.2s

        except Exception as msg:
            print(
                f"Exception occurred in __init_view_pyvis() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot(str)
    def __create_full_axis_once(self):

        if self.__axis_created:
            return
        self.__axis_created = True

        try:
            self.__xax = scene.Axis(
                pos=[[0, 0],
                     [1, 0]],
                tick_direction=(0, 1),
                domain=(0, 2000),
                axis_color='r', tick_color='r', text_color='r', font_size=0,
                tick_width=1, axis_width=1, axis_label="x",
                parent=self.__view.scene)
            self.__x_text = scene.Text(
                text=["X", "200", "400", "600", "800", "1000", "1200",
                      "1400", "1600", "1800", "2000"],
                pos=[(0.5, -0.02), (0.1, 0.02), (0.2, 0.02), (0.3, 0.02),
                     (0.4, 0.02), (0.5, 0.02), (0.6, 0.02), (0.7, 0.02),
                     (0.8, 0.02), (0.9, 0.02), (1, 0.02)],
                color='r', font_size=10,
                parent=self.__view.scene)
            self.__zero_text = scene.Text(
                text=["0"],
                pos=[(-0.01, -0.01)],
                color='r', font_size=10,
                parent=self.__view.scene)
            self.__yax = scene.Axis(
                pos=[[0, 0],
                     [0, 1]],
                tick_direction=(1, 0),
                domain=(0, 2000),
                axis_color='g', tick_color='g', text_color='g', font_size=0,
                tick_width=1, axis_width=1, axis_label="y",
                parent=self.__view.scene)
            self.__y_text = scene.Text(
                text=["Y", "200", "400", "600", "800", "1000", "1200",
                      "1400", "1600", "1800", "2000"],
                pos=[(-0.02, 0.5), (0.02, 0.1), (0.02, 0.2), (0.02, 0.3),
                     (0.02, 0.4), (0.02, 0.5), (0.02, 0.6), (0.02, 0.7),
                     (0.02, 0.8), (0.02, 0.9), (0.02, 1)],
                color='g', font_size=10,
                parent=self.__view.scene)
            self.__zax = scene.Axis(
                pos=[[0, 0],
                     [-1, 0]],
                tick_direction=(0, 1),
                domain=(0, 2000),
                axis_color='b', tick_color='b', text_color='b', font_size=0,
                tick_width=1, axis_width=1, axis_label="z",
                parent=self.__view.scene)
            self.__z_text = scene.Text(
                text=["Z", "200", "400", "600", "800", "1000", "1200",
                      "1400", "1600", "1800", "2000"],
                pos=[(-0.5, -0.02), (-0.1, 0.02), (-0.2, 0.02), (-0.3, 0.02),
                     (-0.4, 0.02), (-0.5, 0.02), (-0.6, 0.02), (-0.7, 0.02),
                     (-0.8, 0.02), (-0.9, 0.02), (-1, 0.02)],
                color='b', font_size=10,
                parent=self.__view.scene)
            # its actually an inverted xaxis
            self.__zax.transform = scene.transforms.MatrixTransform()
            self.__zax.transform.rotate(90, (0, 1, 0))  # rotate cw around yaxis
            # tick direction towards (-1,-1)
            self.__zax.transform.rotate(-45, (0, 0, 1))
            self.__z_text.transform = scene.transforms.MatrixTransform()
            self.__z_text.transform.rotate(90, (0, 1, 0))  # rotate cw around yaxis
            # tick direction towards (-1,-1)
            self.__z_text.transform.rotate(-45, (0, 0, 1))

            self.check_axis(self.__axis_visibility)

            self.opengl_loaded_sig.emit()

        except Exception as msg:
            print(
                f"Exception occurred in __create_full_axis_once() due to :: {str(msg)}")

    # =========================================================================

    def check_axis(self, state: bool):
        """
        :Description: Function to enable or disable the 3d axis
        :Params: state [boolean]
        :return: None
        """
        try:
            self.__axis_visibility = state
            if self.__axis_created:
                self.__xax.visible = state
                self.__yax.visible = state
                self.__zax.visible = state
                self.__x_text.visible = state
                self.__y_text.visible = state
                self.__z_text.visible = state
                self.__zero_text.visible = state

            if verbose:
                print("AXIS STATUS: ", state)

        except Exception as msg:
            print(f"Exception occurred in check_axis() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot(str)
    def connect_sensor(self, selected_ip):
        """
        :Description: Function to Establish a connection with the MKe Sensor
        :Params:  selected IP to get connected with
        :return: Boolean
        """
        try:
            # Go to state depth sensing
            self.__bus = pymkeapi.TcpBus(selected_ip, self.sensor_port)
            self.__client = pymkeapi.SyncClient(self.__bus)
            return True

        except Exception as msg:
            print(
                f"Exception occurred in connect_sensor() due to :: {str(msg)}")
            return False

    # =========================================================================

    @QtCore.Slot()
    def disconnect_sensor(self, force_stop=False):
        """
        :Description: Function to disconnect the connection between python viewer and Mke Sensor
        (Deletes the objects bus and client which holds the connection).
        :Params: None
        :return: None
        """
        try:
            del self.__bus
            del self.__client
        except Exception as msg:
            print(
                f"Exception occurred in disconnect_sensor() due to :: {str(msg)}")

    # =========================================================================

    def list_policy(self):
        """
        :Description: Function to return the avaliable list of Policies
        :Params: None
        :return: list
        """
        try:
            return self.__client.list_policies()
        except Exception as msg:
             print(
                f"Exception occurred in list_policy() due to :: {str(msg)}")

    # =========================================================================

    def get_policy(self):
        """
        :Description: Function to return the current policy
        :Params: None
        :return: str
        """
        try:
            return self.__client.get_policy()
        except Exception as msg:
            print(
                f"Exception occurred in get_policy() due to :: {str(msg)}")

    # =========================================================================

    def set_policy(self, value):
        """
        :Description: Function to set the Policy
        :Params:  value [str] : Policy
        :return: None
        """
        try:
            self.__client.set_policy(value)
        except Exception as msg:
            print(
                f"Exception occurred in set_policy() due to :: {str(msg)}")

    # =========================================================================

    def run(self):
        """
        :Description: Run function of the Qthread, handles the frame movement between the different queues
        :Params: None
        :return: None
        """
        try:
            if verbose:
                print("QTHREAD RUNNING")
            force_stop = False
            while self.__process == 0:
                if not self._queue_buffer.empty():
                    # Display current frame details on Frame Details widget
                    self._frame = copy.deepcopy(self._queue_buffer.get())
                    if not self._frame:
                        print(
                            "-" * 10,
                            "\nConnection Issue, Received Signal to Stop the Viewer\n",
                            "-" * 10)
                        force_stop = True
                        break
                    if not self._render_buffer.full():
                        self._render_buffer.put(self._frame)
                    if not self._fps_buffer.full():
                        self._fps_buffer.put(self._frame)
                    self.fps_update.emit()
                    self.model_updated.emit()
                    time.sleep(0.01)
                    self._queue_buffer.task_done()
            if force_stop:
                self.stop_viewer(force_stop=True)
                self.disconnect_sig.emit("Disconnect")

            if verbose:
                print("QTHREAD TERMINATING")

        except Exception as msg:
            print(f"Exception occurred in run() due to :: {str(msg)}")

    # =========================================================================

    def stop(self):
        """
        :Description: Function to manage closeEvent() by safely terminating the thread and Qthread
        :Params: None
        :return: None
        """
        try:
            if verbose:
                print("Is thread alive? ", self._stream.is_alive())
            if self._stream.is_alive():
                self.end_sig.emit()
            self.wait()
            if verbose:
                print("THREAD TERMINATION SUCCESSFUL")
        except Exception as msg:
            print(f"Exception occurred in stop() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot()
    def start_viewer(self):
        """
        :Description: Function to start the rendering process
        :Params: None
        :return: None
        """
        try:
            if verbose:
                print("START RENDERING")
            self.__viewer_start_time = dt.now()
            if self.__seq_timer_list:
                self.__seq_timer_list.clear()
            if self.__client.get_state() != pymkeapi.MKE_STATE_DEPTH_SENSOR:
                if verbose:
                    print("STATE CHANGE FROM ", self.__client.get_state())
                self.__client.set_state(pymkeapi.MKE_STATE_DEPTH_SENSOR)
            if verbose:
                print("CURRENT STATE ", self.__client.get_state())
            self.__process = 0
            self._stream = threading.Thread(
                target=stream_acquire,
                args=(self._queue_buffer, self.__client, self.__lock))
            self._stream.setDaemon(True)
            self._stream.start()
            self.start()
            self.render_btn_text.emit("Stop Rendering")

        except Exception as msg:
            print(f"Exception occurred in start_viewer() due to :: {str(msg)}")
            self.disconnect_sig.emit("Disconnect")

    # =========================================================================

    @QtCore.Slot()
    def stop_viewer(self, force_stop=False):
        """
        :Description: Function to stop the rendering process
        :Params: force_stop (boolean)   [optional]
        :return: None
        """
        try:
            if verbose:
                print("STOP RENDERING")
            self.__lock.acquire()
            self.__process = 1
            self._stream.join()
            time.sleep(0.01)
            if not force_stop:
                frame = self.__client.get_frame(self._FRAME_TYPE)
                self.__client.set_state(pymkeapi.MKE_STATE_IDLE)
            self.__lock.release()
            if verbose:
                print("IS THREAD ALIVE? ", self._stream.is_alive())
            self.render_btn_text.emit("Start Rendering")
        except Exception as msg:
            print(f"Exception occurred in stop_viewer() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot(int)
    def update_point(self, value):
        """
        :Description: Function to update the points in viewer
        :Params: value
        :return: None
        """
        try:
            self._point_size = value
            if verbose:
                print("POINT SIZE CHANGED TO ", self._point_size)
            if self.__process == 1:
                self.model_updated.emit()

        except Exception as msg:
            print(f"Exception occurred in update_point() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot(str)
    def update_color(self, value):
        """
        :Description: Function to update the point color in viewer
        :Params: value [str] : value of color
        :return: None
        """
        try:
            if verbose:
                print("COLOR CHANGED TO ", self.__color_options[value])
            self._col = color.get_colormap(self.__color_options[value])
            if self.__process == 1:
                self.model_updated.emit()

        except Exception as msg:
            print(f"Exception occurred in update_color() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot()
    def update_fps(self):
        """
        :Description: Function to calculate Render FPS, Frame DPS,
        Sensor FPS and emit signal to display them in the Python viewer
        :Params: None
        :return: None
        """
        try:
            frames_missed = 0
            if self._frame:
                timer = self._frame.timer
                seqn = self._frame.seqn
                if len(self.__seq_timer_list) < self._fps_window_size:
                    self.__seq_timer_list.append((seqn, timer))
                else:
                    self.__seq_timer_list.remove(self.__seq_timer_list[0])
                    self.__seq_timer_list.append((seqn, timer))

                if len(self.__seq_timer_list) > 1:
                    seq_diff = int(
                        self.__seq_timer_list[-1][0]) - int(self.__seq_timer_list[0][0])
                    time_diff = int(
                        self.__seq_timer_list[-1][1]) - int(self.__seq_timer_list[0][1])
                    time_in_secs = time_diff / 1000

                    diff_bw_end_frames = int(
                        self.__seq_timer_list[-1][0]) - int(self.__seq_timer_list[-2][0])
                    if diff_bw_end_frames > 1:
                        frames_missed = diff_bw_end_frames - 1
                        self._missed_frames += frames_missed

                    # Determine FDPS
                    current_time = dt.now()
                    if current_time.second != self.__viewer_start_time.second:
                        if self._missed_frames:
                            self._FDPS = self._missed_frames / (
                                current_time.second - self.__viewer_start_time.second)
                        else:
                            self._FDPS = 0
                        if seq_diff:
                            self._FPS = seq_diff / time_in_secs
                        self.fps_text.emit("Frames rendered/s: " + str(int(self._FPS)))
                        self.dfps_text.emit(
                            "Frames dropped/s: " + str(int(self._FDPS)))
                        self.__viewer_start_time = current_time
                        self._missed_frames = 0
                        self._sensor_fps = self._FPS + float(self._FDPS)
                        self.sensor_fps_text.emit(
                            "Frames sensed/s: " + str(int(self._sensor_fps)))
        except Exception as msg:
            print(f"Exception occurred in update_fps() due to :: {str(msg)}")

    # =========================================================================

    @QtCore.Slot()
    def render_view(self):
        """
        :Description: Function to view the rendered data in 3d
        :Params: None
        :return: None
        """
        try:
            if self.__process == 0:
                self.__model = copy.deepcopy(self._render_buffer.get())

            points_length = self.__model.pts3d.shape[0]
            pts3d = self.__model.pts3d
            pts3d = pts3d[pts3d[:, 2].argsort()]
            pts3d = pts3d / 2000
            colormap = self._col[np.linspace(0., 1., points_length)]
            colors = colormap.rgba
            if points_length > 0:
                self.__p1.set_data(
                    pts3d, face_color=colors, symbol='o', size=self._point_size,
                    edge_width=0.5, edge_color='black')
        except Exception as msg:
            print(f"Exception occurred in render_view() due to :: {str(msg)}")

# =============================================================================

def stream_acquire(q, client, lock):
    """
    :Description: Function to get frame from the client object and add the frame in the queue buffer.
    :Params: client [object], lock
    :return: None
    """
    try:
        while True:
            if not lock.locked():
                frame = client.get_frame(1)
                q.put(frame)
            else:
                if verbose:
                    print("Lock set, entering termination mode for thread")
                break
            time.sleep(0.0001)

    except Exception as msg:
        print(f"Exception occurred in stream_acquire() due to :: {str(msg)}")
        # Disconnect the Device
        q.put(False)

# =============================================================================
# =============================================================================

def main_func():
    App = QApplication([])
    viewer = SimpleViewer()
    viewer.show()
    app.run()

# =============================================================================

sys_args = sys.argv[1:]
verbose = "-v" in sys_args

if __name__ == '__main__':
    main_func()


import numpy as np
from buckyballgenerator import generator
import time
import matplotlib.pyplot as plt

def energy_per_config(structure, vector):
    energy = 0
    for edge in edges:
        i = edge[0]
        j = edge[1]
        energy += J * vector[i] * vector[j]
    return energy

def gapp(structure, statevector, J, flipnode):
        oldEnergy = 0
        newEnergy = 0
        flip = -statevector[flipnode]
        neigh = structure[flipnode]
        for i in range(3):
            oldEnergy += J * statevector[flipnode] * statevector[neigh[i]]
            newEnergy += J * flip * statevector[neigh[i]]
        gap = oldEnergy - newEnergy
        return gap, flip

structure, edges = generator()
E_min = -66
E_max = 90
J = 1
beta = 1
logG = {}
Hist = {}
f = 2.718281828

for E in range(E_min, E_max+1):
    logG[E] = 1
    Hist[E] = 0
statevector = np.random.rand(60)
# statevector = np.where(statevector > 0.5, 1, -1)

for i in range(60):
    if statevector[i] > 0.5:
        statevector[i] = 1
    else:
        statevector[i] = -1
start = time.time()

statevector = np.ones(60)
E = energy_per_config(structure, statevector)

while(f > 1+10e-8):

    for u in range(E_min, E_max+1):
        Hist[u] = 0
    for iter in range(2000):
        for j in range(60):
        # j = np.random.randint(0, 60)
            gap, flip = gapp(structure, statevector, J, j)
            E_new = E - gap
            if np.exp(logG[E] - logG[E_new]) > 1:
                E = E_new
                logG[E] += np.log(f)
                Hist[E] += 1
                statevector[j] = flip
            else:
                p = np.random.rand()
                if p < np.exp(logG[E] - logG[E_new]):
                    E = E_new
                    logG[E] += np.log(f)
                    Hist[E] += 1
                    statevector[j] = flip
                else:
                    logG[E] += np.log(f)
                    Hist[E] += 1

        Hist_list = [Hist[i] for i in np.arange(E_min, E_max+1, 2)]
        Hist_list = Hist_list[0:len(Hist_list)-3]
        error = (np.max(Hist_list) - np.mean(Hist_list)) / np.mean(Hist_list)
        # if iter % 1000 == 0:
        #     print(error)

            # plt.ion()
            # X = range(E_min, E_max)
            # Y = np.array([logG[i] for i in X])

            # index = np.nonzero(Y==1)
            # # print(index)

            # Y = np.delete(Y, index)
            # X = np.delete(X, index)
            # plt.plot(X, Y)
            # plt.show()
            # plt.pause(0.01)
            # plt.clf()
    f = f ** 0.5

X = list(range(E_min, E_max + 1))
Y = np.zeros(E_max - E_min + 1)
for i in range(Y.shape[0]):
    Y[i] = logG[X[i]]

Y_remove = [Y[i] for i in np.arange(0, E_max - E_min, 2)]
Y = [Y[i] for i in np.arange(0, E_max - E_min, 2)]

while 1 in Y_remove:
    Y_remove.remove(1)

Y = np.exp(Y - np.min(Y_remove))
print(np.min(Y_remove))
print(Y[0] / np.sum(Y) * 2**60)
E = (np.arange(E_min, E_max, 2))
Z = np.dot(Y, np.exp(-E))/sum(Y)*2**60
stop = time.time()

print(np.log(Z)/60, stop-start)