from __future__ import print_function

from models.resnet3d import resnet34
from datasets import VideoDataset, EqualDistSampler
from deploy import train
from utils import load_checkpoint
from torch.utils.data import DataLoader

import os
import torch
import csv
import argparse
import socket
import torch.optim as optim
import torch.nn as nn
import functools

from transforms.image_transforms import video_transform as transform

parser = argparse.ArgumentParser(description='Action Recognition')
parser.add_argument('--params', default='', type=str)
parser.add_argument('--name', default='default', type=str)
parser.add_argument('--dataset', default=['gestures'], nargs='+')
parser.add_argument('--version', default='random', type=str)
parser.add_argument('--version_train', nargs='+')
parser.add_argument('--version_valid', nargs='+')
parser.add_argument('--learning_rate', default=10 ** -4, type=float)
parser.add_argument('--network', default='resnet34', type=str)
parser.add_argument('--sample_size', default=112, type=int)
parser.add_argument('--resize_to', default=128, type=int)
parser.add_argument('--batch_size', default=64, type=int)
parser.add_argument('--sample_length', default=24, type=int)
parser.add_argument('--max_epochs', default=50, type=int)
parser.add_argument('--seed', default=1234, type=int)
parser.add_argument('--test', action='store_true')
parser.set_defaults(test=False)
parser.add_argument('--num_workers', default=12, type=int)
parser.add_argument('--location', default='<default>', type=str)
parser.add_argument('--test_location', default='<default>', type=str)
parser.add_argument('--csv_location', default='<default>', type=str)
args = parser.parse_args()

torch.manual_seed(args.seed)

print(args)

# Get accuracte location based on hostname
if args.location == "<default>":
    args.location = '/media/datadrive/baptist/data/autolabel/'
if args.test_location == "<default>":
    args.test_location = '/media/datadrive/baptist/data/autolabel/'
if args.csv_location == "<default>":
    args.csv_location = '/home/baptist/projects/actions/data/csv/'

labels = []
for d in args.dataset:
    with open(os.path.join(args.csv_location, '%s-labels.csv'%d), 'r') as f:
        reader = csv.reader(f, delimiter=';')
        labels += [v for v, in reader]

transform_train = functools.partial(transform,  apply_resize=True,
                                                size=args.sample_size,
                                                resize_to=args.resize_to,
                                                apply_hflip=True,
                                                apply_randomcrop=True,
                                                apply_brightness=True,
                                                apply_saturation=True,
                                                apply_zoom=False,
                                                apply_wb=False)

transform_valid = functools.partial(transform, apply_resize=True,
                                               apply_centercrop=True,
                                               size=args.sample_size,
                                               resize_to=args.sample_size,
                                               apply_wb=False)

transform_test = functools.partial(transform, apply_resize=True,
                                              apply_centercrop=True,
                                              apply_focus=False,
                                              size=args.sample_size,
                                              resize_to=args.sample_size,
                                              apply_wb=False)

# List all files for train, valid, and test set.
train_files = []
valid_files = []
for d in args.dataset:
    if args.version == '':
        train_files += [os.path.join(args.csv_location, '%s-%s.csv' % (d, version)) for version in args.version_train]
        valid_files += [os.path.join(args.csv_location, '%s-%s.csv' % (d, version)) for version in args.version_valid]
    else:
        train_files += [os.path.join(args.csv_location, '%s-%s-train.csv'% (d, args.version))]
        valid_files += [os.path.join(args.csv_location, '%s-%s-validation.csv'%(d, args.version))]

dataset = dict(train=VideoDataset(train_files, labels, select='random', transform=transform_train, sample_length=args.sample_length, sample_size=args.sample_size, location=args.location),
               valid=VideoDataset(valid_files, labels, select='center', transform=transform_valid, sample_length=args.sample_length, sample_size=args.sample_size, location=args.test_location))

sampler = EqualDistSampler(dataset['train']._targets)
train_loader = DataLoader(dataset["train"], batch_size=args.batch_size, num_workers=args.num_workers, sampler=sampler) #shuffle=True) #

valid_loader = DataLoader(dataset["valid"], batch_size=args.batch_size, num_workers=args.num_workers)

if args.version == 'random':
    test_files = []
    for d in args.dataset:
        test_files += [
            os.path.join(args.csv_location, '%s-%s-test.csv' % (d, args.version))] if args.version != '' else \
            [os.path.join(args.csv_location, '%s-%s.csv' % (d, version)) for version in args.version_test]
    dataset['test'] = VideoDataset(test_files, labels, select='center', transform=transform_valid, sample_length=args.sample_length, sample_size=args.sample_size, location=args.location)
    test_loader = DataLoader(dataset["test"], batch_size=args.batch_size, num_workers=args.num_workers, shuffle=True)

net = eval(args.network)( sample_size=(3, args.sample_length, args.sample_size, args.sample_size), num_classes=len(labels))
net.cuda()

optimizer = optim.Adam(net.parameters(), lr=args.learning_rate)

if args.params != "":
    load_checkpoint(net, None, args.params, remove_module=True)

criterion = nn.CrossEntropyLoss().cuda()

if not args.test:
    print("---------")
    print("%d samples and %d batches in train set." % (len(dataset['train']), len(train_loader)))
    print("%d samples and %d batches in validation set." % (len(dataset['valid']), len(valid_loader)))
    if args.version == 'random':
        print("%d samples and %d batches in test set." % (len(dataset['test']), len(test_loader)))
    print("---------")

    train(net,
          dict(train=train_loader, valid=valid_loader, test=None if args.version != 'random' else test_loader),
          args.name,
          optimizer=optimizer,
          criterion=criterion,
          max_epochs=args.max_epochs,
          phases=['train', 'valid'] if args.version != 'random' else ['train', 'valid', 'test'],
          classlabels=labels,
    )


import plotly.graph_objects as go
import pandas as pd
import sys, datetime

#where is the data source file. Contains 'name' and 'data'
data_source_file=sys.argv[1]
#Getting the start date like 2020-03-28
if len(sys.argv) > 2:
    start_date = sys.argv[2]

#Read the Lon, Lat of each state
df = pd.read_csv('./us_state_lonlat.csv')
#read the data set to be displayed on the map
df_data = pd.read_csv(data_source_file)
df.head()
state_df=df

# Let's combine the data source into the  geolocation source
# https://github.com/nytimes/covid-19-data/blob/master/us-states.csv
df['text'] = df['name'] # + df['data'].astype(str)
df = df.set_index('name')
df_data =df_data.set_index('state')

#Turn this on if bubbles are displayed based on categories and size.
#limits = [(0,2),(3,10),(11,20),(21,50),(50,3000)]
limits = [(0,3000)]
colors = ["royalblue","crimson","lightseagreen","orange","lightgrey"]
cities = []
scale = 10 #the scaled used to resize the bubble

# make figure
fig_dict = {
    "data": [],
    "layout": {},
    "frames": []
}
for i in range(len(limits)):
    lim = limits[i]
    # Breaking the list which is based in the order by population.
    df_sub = df[lim[0]:lim[1]]
fig_dict = dict(
    layout = dict(
        title_text = '',
        showlegend = True,
        geo = dict(
            scope = 'usa',
            landcolor = 'lightblue',#'rgb(217, 217, 217)',
        ),
        updatemenus=[dict(
            type="buttons",
            buttons=[dict(label="Play",
                          method="animate",
                          args=[None])])]
    ),
    data = [
        {'type': 'scattergeo',
        'locationmode' : 'USA-states',
		},
#    'name' : '{0} - {1}'.format(lim[0],lim[1])
    ],

    frames = []
)

state_df = state_df.set_index('name')
data_source = pd.read_csv(data_source_file)

count=0
for i in pd.date_range(start_date, datetime.date.today()-datetime.timedelta(days=1), freq='1D'):
    count=count+1

    df_data2=data_source
    df_data2=df_data2.set_index('state')

    df_data2['date'] = pd.to_datetime(df_data2['date'])
    mask=(df_data2['date'] == i)
    df_data2 = df_data2.loc[mask]
    # Let's combine the data source into the  geolocation source
    # https://github.com/nytimes/covid-19-data/blob/master/us-states.csv
    df['data']=df_data2['cases']
    df=df.fillna(axis=1, value='0')
    convertedDate = i.to_pydatetime()
    appendFrame={'name' : count, 'layout' : {'title_text':convertedDate.strftime('%Y-%m-%d'), 'title_x':0.5},
         'data': [
             {'type': 'scattergeo',
              'locationmode' : 'USA-states',
              'lon' : df['lon']-0.5,
              'lat' : df['lat']-0.5,
              'text' : df['text']+' ' +df['data'].astype(str),
              'marker' : {
                'size' : df['data'].astype(int)/scale, #10,  #df_sub['pop']/scale,
                'color' : 'orange',
                'line_color' : 'rgb(40,40,40)',
                'line_width': 0.5,
                'sizemode' : 'area'
            }
            }
            ]
        }

    fig_dict["frames"].append(appendFrame)

fig = go.Figure(fig_dict)

fig.update_layout(
    title={
        'y':0.9,
        'x':0.5,
        'xanchor': 'center',
        'yanchor': 'top',
        'font':dict(
            family="Courier New, monospace",
            size=40,
            color="#7f7f7f"
    )      }
        )

#Make the date dynamically as the time serious
fig.add_trace(go.Scattergeo(
        lon = df['lon'],
        lat = df['lat'],
        text = df['code'],
        mode = 'text',
        textfont=dict(
           family="sans serif",
           color="white"
        ),
        showlegend = False,
    ))

# fig=go.Figure()
# animals=['giraffes', 'orangutans', 'monkeys']
# fig.add_trace(go.Bar
#                 (x=animals,
#                  y=[20, 14, 23]
#                  )
#                 )

fig.show()


import re
from typing import Tuple, Callable, Iterable, Optional
import sys
fname=sys.argv[1] if len(sys.argv)>1 else "input"
f = open(fname)
ftext=f.read()
f=[line.strip() for line in ftext.split("\n")[:-1]]

from collections import defaultdict

d=defaultdict(int)

#copied from https://github.com/joefarebrother/adventofcode/blob/master/misc_utils.py
def lmap(f: Callable, *xs) -> list:
    """Like map but returns a list"""
    return list(map(f, *xs))

def ints(xs: Iterable) -> list[int]:
    """Casts each element of xs to an int"""
    return lmap(int, xs)

def mint(x, default=None):
    """Maybe int - casts to int and returns default on failure"""
    try:
        return int(x)
    except ValueError:
        return default

def ints_in(x: str) -> list[int]:
    """Finds and parses all integers in the string x"""
    ex = r'(?:(?<!\d)-)?\d+'
    return ints(re.findall(ex, x))

class Pt(tuple):
    def __add__(self,other):
        return Pt([x+y for x,y in zip(self,other)])
    def __sub__(self,other):
        return Pt([x-y for x,y in zip(self,other)])
    def __rsub__(self,other):
        return Pt([y-x for x,y in zip(self,other)])
    def __radd__(self,other):
        return Pt([y+x for x,y in zip(self,other)])
    def __rmul__(self,other):
        return Pt([other*x for x in self])
dirs = {
    "R":Pt((0,1)),
    "U":Pt((1,0)),
    "L":Pt((0,-1)),
    "D":Pt((-1,0))
    }
ods = list(dirs.values())

#odirs = [(0,1),(1,0),(0,-1),(-1,0)]
ddirs = lmap(Pt,[(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1)])

try:
    xs = ints(f)
except Exception:
    pass
try:
    xss = lmap(ints_in,f)
except Exception:
    pass

ps = """####

.#.
###
.#.

..#
..#
###

#
#
#
#

##
##"""
ps = [x.split("\n") for x in ps.split("\n\n")]
pss = []
for k in ps:
    p = set()
    for i,row in enumerate(k):
        for j,c in enumerate(row):
            if c=="#":
                p.add(Pt((j,len(k)-1-i)))
    mxx = max(x[0] for x in p)
    pss.append((p,mxx))

h = 0
for i in range(7):
    d[(i,0)] = 1

from itertools import cycle

def canmove(p,dr):
    for c in p:
        if d[c+dr] or not 0<=(c+dr)[0]<7:
            break
    else:
        return True
    return False

ps = cycle(pss)
dirs = cycle(f[0])
ds = defaultdict(list)
for i in range(1585+1705*10):
    hs = tuple([d[x,y+h] for x in range(7) for y in range(0,-10,-1)])
    ds[hs].append((i,h))
    p,mx = next(ps)
    ll = Pt((2,h+4))
    while True:
        c = next(dirs)
        if c=="<":
            if canmove(p,ll+(-1,0)):
                ll+=(-1,0)
        elif c==">":
            if canmove(p,ll+(1,0)):
                ll+=(1,0)
        if canmove(p,ll+(0,-1)):
            ll+=(0,-1)
        else:
            for c in p:
                d[c+ll]=1
                h=max(h,(c+ll)[1])
            break

ks = list(ds.keys())
a = defaultdict(int)
for k in ds:
    a[len(ds)]+=1

s=0
for (line,ints) in zip(f,xss):
    pass
print(h + 586510253*2618)


# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import logging
from .. import readers
from .. import writers
logger = logging.getLogger(__name__)

# Module API

def process_publication(conn, table, extractors):
    """Translate publication records from warehouse to database.

    Args:
        conn (dict): connection dict
        table (str): table name
        extractors (dict): extractors dict

    """

    # Extract and write source
    source = extractors['extract_source'](None)
    source_id = writers.write_source(conn, source)

    errors = 0
    success = 0
    for record in readers.read_records(conn, table):

        conn['database'].begin()

        try:

            # Extract and write publication/relationships
            publications = extractors['extract_publications'](record)
            for publication in publications:

                # Write publication
                publication_id = writers.write_publication(conn, publication, source_id)
                if publication_id is None:
                    continue

                # Following code could be moved to separete linking processor:
                # https://github.com/opentrials/opentrials/issues/115

                # Delete existent relationships
                conn['database']['trials_publications'].delete(
                    publication_id=publication_id)

                # Write relationships
                for id in publication['trial_identifiers']:
                    trial_objects = readers.read_objects(conn, 'trials', facts=[id])
                    for trial_object in trial_objects:
                        trial_id = trial_object['id']
                        writers.write_trial_relationship(
                            conn, 'publication', publication, publication_id, trial_id)

        except Exception as exception:
            errors += 1
            conn['database'].rollback()
            logger.exception('Processing error: %s [%s]',
                repr(exception), errors)

        else:
            success += 1
            conn['database'].commit()
            if not success % 100:
                logger.info('Processed %s publications from %s',
                    success, table)


# -*- coding: utf-8 -*-
from django.urls import path
from . import views

urlpatterns = [
        path('',views.home,name='home'),
        path('login',views.login,name='login'),
        path('signup',views.signup,name='signup'),
        path('slogin',views.slogin,name='slogin'),
        path('ssignup',views.ssignup,name='ssignup'),
        path('predict',views.predict,name="predict"),
        path('consult',views.consult,name="consult")
        ]


from bs4 import BeautifulSoup
import requests
import json
import random
url = "http://tj.xiaozhu.com/fangzi/28657302903.html"
wb_data = requests.get(url)
print(wb_data.text)
soup = BeautifulSoup(wb_data.text,"lxml")
titles = soup.select("body > div > div > div > h4 > em")
names = soup.select("body > div.wrap.clearfix.con_bg > div.con_l > div.pho_info > p")
print(names)
for title,name in zip(titles,names):
    data ={
        "title" : title.get_text(),
        "name" : title.get_text()
    }

resp = requests.get("http://tor1024.com/static/proxy_pool.txt")
ips_txt = resp.text.strip().split("\n")
ips = []
for i in ips_txt:
    try:
        k = json.loads(i)
        ips.append(k)
    except Exception as e:
        print(e)
r = requests.get("http://bj.ganji.com/",proxies=random.choice(ips),timeout=6)
print(r)


import math as math
from math import pi, sqrt
import fractions as frac
from cmath import phase

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
import numpy as np

from qiskit import Aer, IBMQ, execute

from qiskit.tools.monitor import job_monitor
from itertools import groupby
import qiskit.quantum_info.synthesis.two_qubit_decompose as twoq

imag = complex(0, 1)

ket_0 = np.array([[1], [0]])
ket_1 = np.array([[0], [1]])

ket_plus = np.array([[1 / sqrt(2)], [1 / sqrt(2)]])
ket_minus = np.array([[1 / sqrt(2)], [- 1 / sqrt(2)]])

ket_l = np.array([[1 / sqrt(2)], [imag / sqrt(2)]])
ket_r = np.array([[1 / sqrt(2)], [-imag / sqrt(2)]])

pauliX = np.array([[0, 1], [1, 0]], dtype=complex)
pauliY = np.array([[0, 0. - 1.j], [0. + 1.j, 0]], dtype=complex)
pauliZ = np.array([[1, 0], [0, -1]], dtype=complex)

hadamard = (1 / sqrt(2)) * np.array([[1 + 0.j, 1 + 0.j],
                                     [1 + 0.j, -1 + 0.j]], dtype=complex)

cnot01 = np.array([[1, 0, 0, 0],
                   [0, 1, 0, 0],
                   [0, 0, 0, 1],
                   [0, 0, 1, 0]], dtype=complex)

cnot10 = np.array([[1, 0, 0, 0],
                   [0, 0, 0, 1],
                   [0, 0, 1, 0],
                   [0, 1, 0, 0]], dtype=complex)

# line feed in latex
llf = r'\begin{equation} \\ \end{equation}'

default_bracket_type = 'p'

def ket(str_bits):
    ret = get_basis(str_bits[0])
    for n in range(1, len(str_bits)):
        ret = np.kron(ret, get_basis(str_bits[n]))
    return ret

def bra(str_bits):
    return ket(str_bits).T.conj()

def get_basis(char_bit):
    if char_bit == '0' or char_bit == 'H':
        return ket_0
    if char_bit == '1' or char_bit == 'V':
        return ket_1
    if char_bit == '+':
        return ket_plus
    if char_bit == '-':
        return ket_minus
    if char_bit == 'L':
        return ket_l
    if char_bit == 'R':
        return ket_r

    raise ValueError('Invalid character passed to get_basis')

def reverse_results(results):
    new_results = {}
    for k, v in results.items():
        k = reverse_string(k)
        new_results[k] = v
    return new_results.items()

def print_reverse_results(results, label=None):
    lbl = 'Reversed:'
    if not label is None:
        lbl = lbl + label + ':'
    print(lbl, sorted(reverse_results(results)))

def swap_entries(qiskit_array):
    half_size = int(qiskit_array.shape[0] / 2)  # always square and powers of 2.

    for n in range(1, half_size, 2):
        qiskit_array[:, [n, half_size + n - 1]] = qiskit_array[:, [half_size + n - 1, n]]

    for n in range(1, half_size, 2):
        qiskit_array[[n, half_size + n - 1], :] = qiskit_array[[half_size + n - 1, n], :]
    return qiskit_array

def print_matrix(QC):
    print('Adjusted Matrix:')
    with np.printoptions(linewidth=1024):
        print(what_is_the_matrix(QC))

def show_eigens(QC, bracket_type=None):
    unitary = what_is_the_matrix(QC)
    w, v = np.linalg.eig(unitary)

    bracket_type = get_bracket_type(bracket_type)
    output = r'\begin{equation*}'
    for n in range(w.shape[0]):
        output += r'\lambda_' + str(n) + '=' + format_complex_as_latex(w[n])
        output += r',\; '
        output += np_array_to_latex(v[n].reshape(v[n].shape[0], 1),
                                    bracket_type=bracket_type, factor_out=False, begin_equation=False,
                                    label='v_' + str(n))
        output += r'\quad'
    output += r'\end{equation*}'
    return output

def what_is_the_matrix(QC):
    qiskit_array = execute_unitary(QC)
    return swap_entries(qiskit_array)

def show_me_the_matrix(qc, bracket_type=None, factor_out=True, label=None):
    unitary = what_is_the_matrix(qc)
    return np_array_to_latex(unitary,
                             bracket_type=get_bracket_type(bracket_type),
                             factor_out=factor_out,
                             label=label)

def show_density_matrix(qc, bracket_type=None, factor_out=False, label=None):
    state_vector = execute_state_vector(qc)
    sv = state_vector.reshape(1, state_vector.shape[0])
    density_matrix = sv.T.conj()@sv
    if not np.isclose(np.trace(density_matrix@density_matrix), 1):
        return 'Not a pure state -- not implemented for mixed'
    return np_array_to_latex(density_matrix,
                             bracket_type=get_bracket_type(bracket_type),
                             factor_out=factor_out,
                             label=label)

def get_bracket_type(bracket_type=None):
    if bracket_type is None:
        return default_bracket_type
    return bracket_type

def np_array_to_latex(np_array, bracket_type=None, factor_out=True, label=None, begin_equation=True):
    rows, cols = np_array.shape
    bracket_type = get_bracket_type(bracket_type)
    if factor_out:
        factor = factor_array(np_array)
        if factor == 0:
            factor_out = False
    output = ''
    if begin_equation:
        output = r'\begin{equation*}'
    if label is not None:
        output += label + ' = '
    if factor_out:
        output += format_float_as_latex(factor)
    output += r'\begin{' + bracket_type + r'matrix}'
    for i in range(rows):
        for j in range(cols):
            current = np_array[i, j]
            if factor_out:
                current = current / factor
            output += format_complex_as_latex(current)
            if j < cols - 1:
                output += ' & '
        output += r' \\ ' + '\n'
    output += r'\end{' + bracket_type + r'matrix}'
    if begin_equation:
        output += r'\end{equation*}'
    return output

def factor_array(np_array):
    factor = 0

    rows, cols = np_array.shape
    for i in range(rows):
        for j in range(cols):
            potential = abs(round(np_array[i, j].real, 6))
            if potential != 0 and factor != 0 and potential != factor:
                return 0
            else:
                if factor == 0 and potential != 0:
                    factor = potential

            potential = abs(round(np_array[i, j].imag, 6))
            if potential != 0 and factor != 0 and potential != factor:
                return 0
            else:
                if factor == 0 and potential != 0:
                    factor = potential
    if factor == 1:
        return 0
    return factor

def format_complex_as_latex(complex):
    latex = ''
    if np.isclose(complex.real, 0):
        if np.isclose(complex.imag, 0):
            return ' 0 '
        else:
            if complex.imag < 0:
                latex += '-'
            if np.isclose(np.abs(complex.imag), 1):
                latex += 'i'
            else:
                latex += format_float_as_latex(np.abs(complex.imag)) + 'i'
    else:
        latex += format_float_as_latex(complex.real)
        if np.isclose(complex.imag, 0):
            return latex
        if complex.imag > 0:
            latex += '+'
        else:
            latex += '-'
        if np.isclose(np.abs(complex.imag), 1):
            latex += 'i'
        else:
            latex += format_float_as_latex(np.abs(complex.imag)) + 'i'
    return latex

def format_float_as_latex(raw):
    if raw < 0:
        sign = '-'
    else:
        sign = ''

    positive = np.abs(raw)

    f = frac.Fraction(positive).limit_denominator(8)
    if f.denominator == 1:
        return format_raw(raw)

    if np.isclose(f.numerator / f.denominator, positive):
        return sign + r'\frac{' + str(f.numerator) + '}{' + str(f.denominator) + '}'

    square = positive ** 2
    f = frac.Fraction(square).limit_denominator(16)
    if np.isclose(f.numerator / f.denominator, square):
        return sign + r'\frac{' + latex_sqrt(reduce_int_sqrt(f.numerator)) + '}{' + latex_sqrt(
            reduce_int_sqrt(f.denominator)) + '}'

    return format_raw(raw)

def latex_sqrt(reduce):
    factor = reduce[0]
    radical = reduce[1]
    if radical == 1:
        return str(factor)
    if factor == 1:
        return r'\sqrt{' + str(radical) + '}'
    return str(factor) + r'\sqrt{' + str(radical) + '}'

def format_raw(raw):
    output = np.format_float_positional(raw, precision=4, trim='-')
    ## doesn't seem to trim properly
    if output[-1] == '.':
        output = output[:-1]
    return output

def prime_factors(n):
    i = 2
    factors = []
    while i ** 2 <= n:
        if n % i:
            i += 1
        else:
            n = n / i
            factors.append(i)
    if n > 1:
        factors.append(n)
    return factors

def reduce_int_sqrt(n):
    factor = 1
    radical = 1
    for prime, prime_group in groupby(prime_factors(n)):
        prime_exponent = len(list(prime_group))
        factor = factor * prime ** (prime_exponent // 2)
        radical = radical * prime ** (prime_exponent % 2)
    return factor, radical

def reverse_string(str):
    return str[::-1]

def print_state_vector(QC):
    state_vector = execute_state_vector(QC)
    print(format_state_vector(state_vector))

def show_state_vector(QC, show_zeros=False, label='\psi'):
    str_state_vector = r'\begin{equation*} \vert ' + label + r'\rangle='
    ket_format = format_state_vector(execute_state_vector(QC), show_zeros)
    is_first = True
    for k, v in sorted(ket_format.items()):
        if not is_first:
            if v.real >= 0:
                str_state_vector += '+'
            else:
                if v.real == 0 and v.imag >= 0:
                    str_state_vector += '+'
        is_first = False
        str_state_vector += format_complex_as_latex(v) + r' \vert' + k + r'\rangle'
    str_state_vector += r'\end{equation*}'
    return str_state_vector

def format_state_vector(state_vector, show_zeros=False):
    binary_vector = {}

    bits = int(math.log(len(state_vector), 2))
    for n in range(len(state_vector)):
        if show_zeros or state_vector[n] != 0:
            ket = f"{n:b}"  ## get the binary for the cell
            ket = ket.rjust(bits, '0')
            ket = reverse_string(ket)
            binary_vector[ket] = state_vector[n]
    return binary_vector

def print_short_state_vector(QC):
    ket_format = format_state_vector(execute_state_vector(QC))
    for k, v in ket_format.items():
        if v.imag != 0:
            print('{0}+{1}I |{2}> '.format(v.real, v.imag, k))
        else:
            print('{0}|{1}> '.format(v.real, k))

def decompose_single(unitary_matrix):
    (theta, phi, lamb) = twoq.euler_angles_1q(unitary_matrix)
    qr = QuantumRegister(1)
    qc = QuantumCircuit(qr)
    qc.append(rrz_gate(lamb), [qr[0]])
    qc.ry(theta, qr[0])
    qc.append(rrz_gate(phi), [qr[0]])
    new = what_is_the_matrix(qc)
    alpha = get_global_phase(unitary_matrix, new)
    print('alpha= {}, beta= {}, gamma= {}, delta={}'
          .format(format_rotation(alpha),
                  format_rotation(phi),
                  format_rotation(theta),
                  format_rotation(lamb)))

def decompose_single_qiskit(unitary_matrix):
    (theta, phi, lamb) = twoq.euler_angles_1q(unitary_matrix)
    qc = QuantumCircuit(1)
    qc.u3(theta, phi, lamb, 0)
    new = what_is_the_matrix(qc)
    alpha = get_global_phase(unitary_matrix, new)
    print('theta= {}, phi= {}, lambda= {}, phase={}'
          .format(format_rotation(theta),
                  format_rotation(phi),
                  format_rotation(lamb),
                  format_rotation(alpha)))

def get_global_phase(original, new):
    if np.allclose(original, new):
        alpha = 0
    else:
        m_factor = original @ np.linalg.inv(new)
        if not np.isclose(m_factor[0, 0], 0):
            factor = phase(m_factor[0, 0])
        else:
            factor = phase(m_factor[0, 1])

        if np.allclose(original,
                       (np.exp(imag * factor)) * new):
            alpha = factor
        else:
            raise ValueError('New Matrix not equal to old ')
    return alpha

def decompose_single_all(decompose, fraction=8):
    found = False
    i = complex(0, 1)
    for a in range(1, 2 * fraction):
        for b in range(0, 2 * fraction):
            for c in range(0, 2 * fraction):
                for d in range(0, 2 * fraction):

                    alpha = pi - (pi / fraction) * a
                    beta = pi - (pi / fraction) * b
                    gamma = pi - (pi / fraction) * c
                    delta = pi - (pi / fraction) * d

                    ar = np.array([[np.cos(alpha) + i * np.sin(alpha), 0],
                                   [0, np.cos(alpha) + i * np.sin(alpha)]])
                    br = np.array([[np.cos(beta / 2) - i * np.sin(beta / 2), 0],
                                   [0, np.cos(beta / 2) + i * np.sin(beta / 2)]])
                    cr = np.array([[np.cos(gamma / 2), -np.sin(gamma / 2)],
                                   [np.sin(gamma / 2), np.cos(gamma / 2)]])
                    dr = np.array([[np.cos(delta / 2) - i * np.sin(delta / 2), 0],
                                   [0, np.cos(delta / 2) + i * np.sin(delta / 2)]])

                    if np.allclose(dr @ cr @ br @ ar, decompose):
                        print('alpha= {}, beta= {} gamma= {} delta= {}'
                              .format(format_rotation(alpha),
                                      format_rotation(beta),
                                      format_rotation(gamma),
                                      format_rotation(delta)))
                        found = True
    if not found:
        print('Didnt find it')

def decompose_single_u3_all(decompose, fraction=8):
    found = False
    i = complex(0, 1)
    for t in range(1, 2 * fraction):
        for l in range(0, 2 * fraction):
            for p in range(0, 2 * fraction):

                theta = pi - (pi / fraction) * t
                lam = pi - (pi / fraction) * l
                phi = pi - (pi / fraction) * p

                u = np.array([[np.cos(theta / 2), -np.exp(i * lam) * np.sin(theta / 2)],
                              [np.exp(i * phi) * np.sin(theta / 2), np.exp(i * lam + i * phi) * np.cos(theta / 2)]])

                if np.allclose(u, decompose):
                    print('theta= {}, phi= {}, lambda= {}'
                          .format(format_rotation(theta),
                                  format_rotation(phi),
                                  format_rotation(lam)))
                    found = True
    if not found:
        print('Didnt find it')

def decompose_single_qiskit_raw(unitary_matrix):
    alpha = phase(np.linalg.det(unitary_matrix) ** (-1.0 / 2.0))

    (theta, lamb, phi) = twoq.euler_angles_1q(unitary_matrix)
    return alpha, theta, lamb, phi

def execute_state_vector(QC):
    backend = Aer.get_backend('statevector_simulator')
    results = execute(QC, backend=backend).result()
    state_vector = results.get_statevector(QC)
    return state_vector

def execute_unitary(QC):
    backend = Aer.get_backend('unitary_simulator')
    results = execute(QC, backend=backend).result()
    unitary = results.get_unitary(QC)
    return unitary

def execute_real(QC, strBackend, shots):
    backend = IBMQ.get_backend(strBackend)
    job = execute(QC, backend=backend, shots=shots)
    job_monitor(job)

    results = job.result()
    answer = results.get_counts()
    return answer

def execute_seeded(QC, shots):
    return execute_simulated(QC, shots, 12345)  ## just a number that will always be the same

def execute_simulated(QC, shots, seed_simulator=None):
    backend = Aer.get_backend("qasm_simulator")
    results = execute(QC, backend=backend, shots=shots, seed_simulator=seed_simulator).result()
    answer = results.get_counts()
    return answer

def simulate(QC, shots, seed_simulator=None):
    results = execute_simulated(QC, shots, seed_simulator)
    print_reverse_results(results)

# Custom Gates

def global_gate(alpha):
    name = 'G \n(' + format_rotation(alpha) + ')'
    sub_global = QuantumCircuit(1, name=name)
    sub_global.rz(alpha, 0)
    sub_global.y(0)
    sub_global.rz(alpha, 0)
    sub_global.y(0)
    return sub_global.to_instruction()

def rrz_gate(beta):
    name = 'RRz \n(' + format_rotation(beta)+ ')'
    sub_rrz = QuantumCircuit(1, name=name)
    sub_rrz.rz(beta / 2, 0)
    sub_rrz.x(0)
    sub_rrz.rz(-beta / 2, 0)
    sub_rrz.x(0)
    return sub_rrz.to_instruction()

def format_rotation(rot):
    fraction = frac.Fraction(round(math.degrees(rot)), 180).limit_denominator(8)
    if np.isclose(fraction * pi, rot):
        if fraction < 0:
            sign = '-'
        else:
            sign = ''
        ret = str(abs(fraction))
        ret = ret.replace('1/', 'pi/')
        if ret == '1':
            return sign + 'pi'
        if ret == '2':
            return sign + '2pi'
        return sign + ret
    else:
        return str(rot)


from prometheus_client import start_http_server, Info, Counter, Summary # noqa

total = Counter('pup_advisor_total', 'The total amount of uploads')
valid = Counter('pup_advisor_valid', 'The total amount of valid uploads')
invalid = Counter('pup_advisor_invalid', 'The total amount of successfully validated uploads')
inventory_post_success = Counter('pup_advisor_inventory_post_success', 'The total amount of successful inventory posts')
inventory_post_failure = Counter('pup_advsior_inventory_post_failure', 'The total amount of failed inventory posts')

validation_time = Summary('validation_processing_seconds', 'Time spent validating archive')
inventory_post_time = Summary('inventory_post_processing_seconds', 'Time spent posting to inventory')

upload_service_version = Info('upload_service_version', 'Build commit and date')


# -*- coding: utf-8 -*-

# This file is responible for all learning related functions, the neural network
# The evolutionary algorith and all its necesarry functions, as well as the
# PWA (positive weight augmentation which boosts weights along a gradient)

import numpy as np
import random

# this gives the resposnse of a preceptron to the sum of inputs (maps -inf,inf to -1,1)
def sigmund(value):
    return ((1/(1 + np.exp(-value)))*2)-1

# This takes the input vector and weights list (weights list contains a list of 2d matrexies that describe how its conected)
def NN(weights, inputs): # each row represents the weight to each of the preceptrons
    biases = weights[-1][0]
    bias_idx = 0
    for idx,w in enumerate(weights[:-1]):
        bias_end_idx = bias_idx + w.shape[0]
        inputs = sigmund(np.dot(w,inputs)-biases[bias_idx:bias_end_idx])
        bias_idx = bias_end_idx
    return inputs

# from this point on its the evulutionary network

# Takes all the weights and with a certain probability it will add or substract a random amount that is between -mut_amm,mut_amm
def rand_mutation(flat_weights, mutation_rate, muation_ammount):
    for fw in flat_weights:
        done = False
        mutation_index = 0
        while not done:
            mutation_index += int(np.random.exponential(mutation_rate))
            if mutation_index >= len(fw):
                done = True
                break
            fw[mutation_index] += (random.random()*muation_ammount*2)-muation_ammount
    return flat_weights

# Basically cross mutation
# Takes the scores and "mates" more sucesfull "agents" more frequently than ones with low scores
# it swaps random parts of their weights between the "mom" and "dad" to creat two offsprings
def death_roulette(weights, sim_scores):
    min_score = min(sim_scores)
    max_score = max(sim_scores)
    accum_scores = [0]
    for score in sim_scores:
        accum_scores.append(score+accum_scores[-1]-(0.3*min_score))

    tot_score = accum_scores[-1]
    new_weights = []
    while len(new_weights)+2 < len(weights):
        dad_score = random.random()*tot_score
        mom_score = random.random()*tot_score

        for idx, s in enumerate(accum_scores):
            if s >= dad_score:
                dad = weights[idx-1]
                break

        for idx, s in enumerate(accum_scores):
            if s >= mom_score:
                mom = weights[idx-1]
                break

        len_weight = len(weights[0])
        cut1 = random.randint(0, len_weight-2)
        cut2 = random.randint(cut1, len_weight-1)

        dad_cut = dad[cut1:cut2]
        mom_cut = mom[cut1:cut2]

        dad = np.concatenate([dad[0:cut1],mom_cut,dad[cut2:]])
        mom = np.concatenate([mom[0:cut1],dad_cut,mom[cut2:]])

        new_weights.append(dad)
        new_weights.append(mom)

    new_weights.append(weights[sim_scores.index(max_score)])
    new_weights.append(weights[sim_scores.index(max_score)])

    if len(new_weights) > len(weights):
        new_weights.pop(-1)

    return new_weights

# takes the weights and flattens them down to a 1d array so that I can do the random mutation and the cross "breeding" easily
def flatten_concat(weights):
    flat_weights = []
    for w in weights:
        lst = [x.flatten() for x in w]
        flat = np.concatenate(lst)
        flat_weights.append(flat)
    return flat_weights

# takes the flat weights and turns it back into a list of numpy arrays
def unflatten(weights,shape):
    out = []
    for w in weights:
        weight = []
        idx = 0
        for s in shape:
            lenght = s[0]*s[1] + idx
            weight.append(w[idx:lenght].reshape(s))
            idx += s[0]*s[1]
        out.append(weight)
    return out

# main loop of the evulutionary learning
def darwin_was_right(weights, sim_scores, mutation_rate):

    matrix = weights[0]
    shape = []
    for m in matrix:
        shape.append(m.shape)

    flat_weights = flatten_concat(weights)

    new_weights = death_roulette(flat_weights, sim_scores)

    final_weights = rand_mutation(new_weights,mutation_rate,0.3)

    return unflatten(final_weights, shape)

def return_best(weights,scores):
    best = (0,0)
    for idx,s in enumerate(scores):
        if s >= best[0]:
            best = (s,idx)

    return weights[best[1]],best[0]

def gen_weights(pop,shape):
    weights = []
    for i in range(pop):
        weight = []
        num_biases = 0
        for s in shape:
            w = np.random.rand(s[0],s[1])
            weight.append((w*2)-1)
            num_biases += s[1]
        weight.append((np.random.rand(1,num_biases)*2)-1)
        weights.append(weight)
    return weights

def weightBooster(scoresOld, scoresCurrent, weightsCurrent, weightsOld, improveThreshold,boost):
    matrixCurrent = weightsCurrent[0]
    shapeCurrent = []
    for m in matrixCurrent:
        shapeCurrent.append(m.shape)

    weightsCurrent  = flatten_concat(weightsCurrent)
    weightsOld      = flatten_concat(weightsOld)

    #replace all zero entries in scores with 0.1 (to avoid devision by zero)
    scoresOld = [0.1 if x == 0 else x for x in scoresOld]

    idxChange       = [True if (current_i-old_i)/old_i > improveThreshold else False for current_i, old_i in zip(scoresCurrent, scoresOld)]
    weightsDiff     = [boost*(current_i - old_i) for current_i,old_i in zip(weightsCurrent, weightsOld)]
    weightsBoost    = [current_i*boost for current_i, change_i in zip(weightsDiff,idxChange) if change_i == True]

    #also make where the boosted weights are popped. Thist list will go into the evolutionary algorithm
    weightsNoBoost  = [current_i for current_i, change_i in zip(weightsCurrent,idxChange) if change_i == False]
    scoresNoBoost   = [current_i for current_i, change_i in zip(scoresCurrent,idxChange) if change_i == False]

    weightsNoBoost  = unflatten(weightsNoBoost,shapeCurrent)
    weightsBoost    = unflatten(weightsBoost,shapeCurrent)

    return idxChange, weightsBoost, weightsNoBoost, scoresNoBoost, shapeCurrent

def merging(idxChange, weightsNoBoost,weightsBoost,shapeCurrent):

    for idx,value in enumerate(idxChange):
        if value:
            weightsNoBoost.insert(idx, weightsBoost[0])
            weightsBoost.pop(0)

    return weightsNoBoost

def weightBoosterv2(scoresOld, scoresCurrent, weightsCurrent, weightsOld, threshold,boost,percentageBoost):
    matrixCurrent = weightsCurrent[0]
    shapeCurrent = []
    scoresTrue = []

    maxBoostCount = int(percentageBoost * len(weightsCurrent))
    if maxBoostCount == 0 :
        maxBoostCount = 1

    for m in matrixCurrent:
        shapeCurrent.append(m.shape)

    weightsCurrent  = flatten_concat(weightsCurrent)
    weightsOld      = flatten_concat(weightsOld)

    idxTrue       = []
    weightsBoost    = []
    for idx_c, (weight_c, scores_c) in enumerate(zip(weightsCurrent, scoresCurrent)):
        for idx_o, (weight_o, scores_o) in enumerate(zip(weightsOld, scoresOld)):

            meanDiff = np.mean(weight_c - weight_o)
            sumDiff = np.sum(abs(weight_c - weight_o))
            scoreDiff = scores_c - scores_o

            if 0 < abs(meanDiff) < threshold[0] and abs(sumDiff) < threshold[1] and scoreDiff > threshold[2]:
                scoresTrue.append(scoreDiff)
                idxTrue.append(idx_c)
                weightsBoost.append(weight_c + ((weight_c - weight_o) * boost))

    #select the indices of the maxBoostCount highest scores.
    if len(scoresTrue) >= maxBoostCount:
        ind             = np.argpartition(scoresTrue, -maxBoostCount)[-maxBoostCount:]
    else:
        ind = []

    weightsBoost    = [i for j, i in enumerate(weightsBoost) if j in ind]
    idxTrue         = [i for j, i in enumerate(idxTrue) if j in ind]

    weightsNoBoost  = [i for j, i in enumerate(weightsCurrent) if j not in idxTrue]
    scoresNoBoost   =  [i for j, i in enumerate(scoresCurrent) if j not in idxTrue]
    idxChange       = [True if j in idxTrue else False for j,i in enumerate(weightsCurrent)]

    weightsNoBoost  = unflatten(weightsNoBoost,shapeCurrent)
    weightsBoost    = unflatten(weightsBoost,shapeCurrent)

    return idxChange, weightsBoost, weightsNoBoost, scoresNoBoost, shapeCurrent


import numpy as np
import array
import os, sys
import re

import smpy.smpy as S
from scipy.interpolate import CubicSpline

from astropy.table import Table
from astropy import units as u

def f99_extinction(wave):
    """
    Return Fitzpatrick 99 galactic extinction curve as a function of wavelength
    """
    anchors_x = [0., 0.377, 0.820, 1.667, 1.828, 2.141, 2.433, 3.704, 3.846]
    anchors_y = [0., 0.265, 0.829, 2.688, 3.055, 3.806, 4.315, 6.265, 6.591]

    f99 = CubicSpline(anchors_x, anchors_y)

    output_x = (1 / wave.to(u.micron)).value

    return f99(output_x)

def process(catalog_in, translate_file, filter_file,
            cat_format = 'ascii.commented_header',
            id_col = 'id', flux_col = 'flux',
            fluxerr_col = 'fluxerr', exclude_columns=None,
            correct_extinction=True,
            overwrite=True, verbose=False):

    input_data = Table.read(catalog_in, format=cat_format)
    if exclude_columns != None:
        input_data.remove_columns(exclude_columns)
    column_names = input_data.columns.keys()

    ID = input_data[id_col]

    flux_col_end = flux_col
    fluxerr_col_end = fluxerr_col

    try:
        translate_init = Table.read(translate_file, format='ascii.no_header')
        fnames = translate_init['col1']
        fcodes = translate_init['col2']
        flux_cols = np.array([a.startswith('F') for a in fcodes])
        fluxerr_cols = np.array([a.startswith('E') for a in fcodes])

    except:
        raise

        # Parse filter file with smpy - get central wavelengths back
    filt_obj = S.LoadEAZYFilters(filter_file)

    lambda_cs = np.zeros(flux_cols.sum())
    f99_means = np.zeros(flux_cols.sum())

    for il, line in enumerate(translate_init[flux_cols]):
        filtnum = int(line['col2'][1:])-1
        lambda_cs[il] = filt_obj.filters[filtnum].lambda_c.value

        wave = filt_obj.filters[filtnum].wave
        resp = filt_obj.filters[filtnum].response

        f99_ext = f99_extinction(wave)
        f99_means[il] = np.trapz(resp*f99_ext, wave.value) / np.trapz(resp, wave.value)

    wl_order = np.argsort(lambda_cs)

    flux_colnames_ordered = fnames[flux_cols][wl_order]
    fluxerr_colnames_ordered = fnames[fluxerr_cols][wl_order]

    f99_means_ordered = f99_means[wl_order]

    fluxes = np.zeros((len(input_data), len(flux_colnames_ordered)))
    fluxerrs = np.zeros((len(input_data), len(flux_colnames_ordered)))

    for i in range(len(flux_colnames_ordered)):
        if correct_extinction:
            try:
                extinctions = f99_means_ordered[i]*input_data['EBV']
                flux_correction = 10**(extinctions/2.5)

                isgood = (input_data[fluxerr_colnames_ordered[i]] > 0.)

                input_data[flux_colnames_ordered[i]][isgood] *= flux_correction[isgood]
            except:
                raise

        fluxes[:,i] = input_data[flux_colnames_ordered[i]]
        fluxerrs[:,i] = input_data[fluxerr_colnames_ordered[i]]

    fluxes = fluxes[:,1:-1]
    fluxerrs = fluxerrs[:,1:-1]
    fnames_full = flux_colnames_ordered[1:-1]
    efnames_full = fluxerr_colnames_ordered[1:-1]
    fnames = [filt.split('_')[0] for filt in fnames_full]

    color_a = np.zeros(fluxes.shape)
    color_b = np.zeros(fluxes.shape)

    upper_lims = np.where(np.logical_and(fluxes/fluxerrs < 2., fluxerrs > 0.))
    fluxes[upper_lims] = -99.
    #np.maximum(fluxes[upper_lims], 0) + 2.*fluxerrs[upper_lims]

    color_a[:,1:] = -2.5*np.log10(fluxes[:,:-1]/fluxes[:,1:])
    color_b[:,:-1] = -2.5*np.log10(fluxes[:,:-1]/fluxes[:,1:])

    # Identify anomalous datapoints by extreme red/blue colours
    # Or strong jumps in colour

    drop = np.logical_and(color_a < -1., color_b > 1.)
    spike = np.logical_and(color_a > 2.5, color_b < -2.5)

    criteria1 = np.logical_or(drop, spike)
    criteria2 = np.logical_or(color_a < -5, color_b > 5)

    anomalous = np.logical_or(criteria1,criteria2) #* (fluxes / fluxerrs > 2.)
    #anomalous_bands = I[anomalous]

    good = (fluxerrs > 0)
    fraction_bad = (good*anomalous).sum(0).astype('float') / good.sum(0)
    gg = zip(fnames, fraction_bad*100)

    for i, name in enumerate(fnames_full):
        if verbose:
            print(name)
        input_data[name][anomalous[:,i]] = -90.
        input_data[efnames_full[i]][anomalous[:,i]] = -90.

    outname = catalog_in+'.mod'

    if (overwrite == True) and os.path.isfile(outname):
        os.remove(outname)

    input_data.write(outname, format=cat_format)

    return outname, gg

if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("-p","--params", type=str,
                        help = "Parameter file")
    parser.add_argument("-q", "--quiet", help = "Suppress extra outputs",
                        action = "store_true")
    args = parser.parse_args()
    quiet = args.quiet

    params_root = re.split(".py", args.params)[0]
    if os.path.isfile(params_root+".pyc"):
        os.remove(params_root+".pyc")

    import importlib
    try:
        pipe_params = importlib.import_module(params_root)
        print('Successfully loaded "{0}" as params'.format(args.params))
        reload(pipe_params)
    except:
        print('Failed to load "{0}" as params'.format(args.params))
        raise

    catalog_in = '/data2/ken/photoz/ezpipe-bootes/Bootes_merged_Icorr_2014a_all_ap4_mags.zs.fits'

    outname, bf, f, fe = process(catalog_in, '{0}/{1}'.format(pipe_params.working_folder, pipe_params.translate_file),
                                '{0}/{1}'.format(pipe_params.working_folder, pipe_params.filter_file),
                                cat_format='fits')