import requests, time, random, webbrowser, datetime, platform, sys, pygame, shutil
import ftplib, getpass, os, json, pyaudio, wave, smtplib, random, socket
import speech_recognition as sr_audio
from bs4 import BeautifulSoup
import pyttsx3 as pyttsx
import os, re, matplotlib
from reportlab.pdfgen import canvas
from reportlab.lib import utils
from reportlab.lib.pagesizes import letter
from reportlab.lib.pagesizes import landscape, portrait
from reportlab.platypus import Image
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
from matplotlib.patches import Shadow
import numpy as np
from textblob import TextBlob
from PyPDF2 import PdfFileMerger, PdfFileReader
import numpy as np
from os.path import basename
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.utils import COMMASPACE, formatdate
from email import encoders
import pandas as pd

##############################################################################
##                            HELPER FUNCTIONS                              ##
##############################################################################

def speaktext(text):
    # speak to a user from a text sample
    engine=pyttsx.init()
    engine.setProperty('voice','com.apple.speech.synthesis.voice.fiona')
    engine.say(text)
    engine.runAndWait()

def get_date():
    return str(datetime.datetime.now())

def playbackaudio(filename):
    # takes in a question and a filename to open and plays back the audio
    # file and prints on the screen the question for the user
    pygame.mixer.init()
    pygame.mixer.music.load(filename)
    pygame.mixer.music.play()

    return "playback completed"

def transcribe_audio(filename,hostdir,transcript_type):
    # transcribe the audio according to transcript type
    # google or sphinx (custom model)
    try:
        if transcript_type == 'sphinx':
            transcript=pst.transcribe(hostdir,filename)
            print('pocket: '+transcript)

        elif transcript_type == 'google':
            try:
                # try google if you can, otherwise use sphinx
                r=sr_audio.Recognizer()
                with sr_audio.AudioFile(filename) as source:
                    audio = r.record(source)
                transcript=r.recognize_google_cloud(audio)
                print('google: '+transcript)
            except:
                print('error using google transcription, need to put API key in environment vars')
                print('defaulting to pocketsphinx...')
                r=sr_audio.Recognizer()
                with sr_audio.AudioFile(filename) as source:
                    audio = r.record(source)
                transcript=r.recognize_sphinx(audio)
                print('sphinx (failed google): '+transcript)

        else:
            # default to sphinx if not sphinx or google inputs
            transcript=pst.transcribe(hostdir,filename)
            print('pocket: '+transcript)

    except:
        transcript=''

    return transcript

def record_to_file(path,filename,recordtime):

    # record 3 second voice file
    CHUNK = 1024
    FORMAT = pyaudio.paInt16 #paInt8
    CHANNELS = 1
    RATE = 16000 #sample rate
    RECORD_SECONDS = recordtime
    WAVE_OUTPUT_FILENAME = filename

    p = pyaudio.PyAudio()

    stream = p.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    frames_per_buffer=CHUNK) #buffer

    print("* recording")

    frames = []

    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data) # 2 bytes(16 bits) per channel

    print("* done recording")

    stream.stop_stream()
    stream.close()
    p.terminate()

    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()

def clean_answer(answer):
    answer=answer.replace(' ','').replace('$','')

    if answer == 'y':
        answer='yes'
    elif answer == 'n':
        answer='no'
    # this is for intent querying
    return answer

def ask(question, hostdir):
    speaktext(question)
    playbackaudio(hostdir+'/data/tone.wav')
    time.sleep(0.25)
    unique_sample='carbon_sample.wav'
    transcript_type = 'google'
    query_time=2
    sufficient=False
    record_to_file(os.getcwd(),unique_sample, query_time)
    transcript=transcribe_audio(unique_sample, hostdir, transcript_type)
    transcript=clean_answer(transcript)

    while sufficient==False:
        # just need yes/no answers and whether a number
        # answers=['2', '50', '5', 'no', '1', 'yes', 'yes', '5', 'no', 'yes', '50']
        try:
            if transcript in ['yes','no']:
                sufficient=True
            elif int(transcript) == int(transcript):
                sufficient=True
        except:
            sufficient=False
            speaktext('Sorry, I did not get that. %s'%(question))
            playbackaudio(hostdir+'/data/tone.wav')
            time.sleep(0.25)
            record_to_file(os.getcwd(),unique_sample, query_time)
            transcript=transcribe_audio(unique_sample, hostdir, transcript_type)
            transcript=clean_answer(transcript)

    os.remove(unique_sample)
    print(transcript)
    return transcript

def make_report(footprint, footprintdelta):
    # load the data here
    g=open('report.txt').read()

    if footprintdelta < 0:
        footprintdelta = str(footprintdelta) + ' less than'
    else:
        footprintdelta = str(footprintdelta) + ' greater than'

    footprint=str(footprint) + ' tons of CO2/year'

    g=g.replace('[INSERT_FOOTPRINT_HERE]', footprint)
    g=g.replace('[INSERT_FOOTPRINTDELTA_HERE]', footprintdelta)

    return report

# create a pdf
def calculate_footprint(answers):
    # create carbon calculator

    # initialize answers
    answer_1 = answers[0]
    answer_2 = answers[1]
    answer_3 = answers[2]
    answer_4 = answers[3]
    answer_5 = answers[4]
    answer_6 = answers[5]
    answer_7 = answers[6]
    answer_8 = answers[7]
    answer_9 = answers[8]
    answer_10 = answers[9]
    answer_11 = answers[10]

    # Electric bill = 7,252.76 kg CO2/year
    # $0.1327/kwh/0.62 kg CO2/kwh = $0.214/kg CO2  - all we need to do is divide montly bill by this.
    # electric bill = (electric bill / people in household) / ($0.214/kgCo2)

    try:
        answer_1=answer_1.replace('$','')
        electric_=(int(answer_2)/int(answer_1))*12/0.214
    except:
        print('--> error on electric CO2 calculation')

    # Flights = 602.448 kg CO2/year (if yes)
    # 286.88 kg CO2/flight
    try:
        flight_= float(answer_3)*286.88
    except:
        print('--> error on flight CO2 calculation')
        flight_=602.448

    # Transportation = 0.
    # 6,525.0 kg CO2/year (if drive only), 4,470.0 kg CO2/year (if mixed), 2,415.0 kg/year (if public)
    # 0.435 kg CO2/mile driving, 0.298 kg CO2/mile 50%/50% public transport and driving, and 0.161 kg CO2/mile (if public)
    # assume 220 working days/year (w/ vacation)
    try:
        if answer_4 == 'yes' and answer_6 == 'no':
            transportation_=float(answer_5)*1.61* 0.435*2*220

        elif answer_4 == 'yes' and answer_6 == 'yes':
            transportation_=float(answer_5)*1.61*0.298*2*220

        elif answer_4 == 'no' and answer_6 == 'yes':
            transportation_=float(answer_5)*1.61*0.161*2*220

        # Uber trips
        # 45.27 kg CO2/year (average)
        # 6 miles * 0.435 kg Co2/ mile = 2.61 kg CO2/trip
        transportation_=transportation_+float(answer_8)*2.61*12

    except:
        print('--> error on transportation CO2 caclulation')
        transportation=4515.27

    # Vegetarian - assume footprint from food
    try:
        if answer_9 == 'yes':
            food_=1542.21406
        # meat lover
        elif answer_10 == 'yes':
            food_=2993.70964
        else:
            food_=2267.96185
    except:
        print('--> error on food CO2 calculation')
        food_=2267.96185

    # do you use amazon? --> retail, etc.
    answer_11=answer_11.replace('$','').replace(' ','')
    retail_=0.1289*float(answer_11)

    footprint=electric_+flight_+transportation_+food_+retail_
    footprintbytype=[electric_, flight_, transportation_, food_, retail_]

    # compared to averages (kg Co2/year)
    footprint_avg = 14660.85
    footprintbytype_avg = [7252.76, 602.45, 4515.27, 2267.96, 22.41]

    footprint_delta=footprint-footprint_avg
    footprintbytype_delta=list(np.array(footprintbytype)-np.array(footprintbytype_avg))

    labels_footprint=['electric (kg Co2/year)', 'flight (kg Co2/year)', 'transportation (kg Co2/year)', 'food (kg Co2/year)', 'retail (kg Co2/year)']
    labels_footprintbytype = 'total kg Co2/year'

    return footprint, footprintbytype, footprint_delta, footprintbytype_delta, labels_footprint, labels_footprintbytype

# helper function to sort names
def sortnames(filelist):
    filelist.sort(key=lambda var:[int(x) if x.isdigit() else x for x in re.findall(r'[^0-9]|[0-9]+', var)])
    return filelist

def delete_pdfs():
    listdir=os.listdir()
    for i in range(len(listdir)):
        if listdir[i][-4:]=='.pdf':
            os.remove(listdir[i])
def cover_page(pdfname, surveyname, company, date, sampleid):
    c=canvas.Canvas(pdfname, pagesize=portrait(letter))

    logo='logo.png'
    c.drawImage(logo, 200, 500, width=200,height=100, preserveAspectRatio=True)

    c.setFont('Helvetica-Bold', 16, leading=None)
    c.drawCentredString(300,460,"Carbon footprint report")
    c.setFont('Helvetica', 16, leading=None)
    c.drawCentredString(300,430,"%s"%(surveyname))
    c.drawCentredString(300,400,"%s"%(date[0:10]))

    # get .PNG from GDRIVE for text
    c.drawImage('cover_text.png', 50,200,width=510,height=200,preserveAspectRatio=True)
    #wave looking thing on front page
    c.drawImage('footer.png', -100,-35,width=800,height=100,preserveAspectRatio=True)
    c.save()

    return pdfname

def combine_pdfs():
    return ''

def make_graphs(individual_means, individual_means_2):

    # bar graph compared to average in each category (2 phase bar graph)
    labels = ['Electricity consumption (kwh * 1000)', '# of flights per year', '# commute miles per year (thousands)', '# of uber trips per year', 'food choice (tons of CO2 emissions/year)']
    population_means = [11.698, 2.1, 15, 7.86, 2.5]
    population_means=list(map(int,population_means))

    print(labels)
    print(individual_means)
    print(population_means)

    x = np.arange(len(labels))  # the label locations
    width = 0.35  # the width of the bars

    fig, ax = plt.subplots()
    rects1 = ax.bar(x - width/2, individual_means, width, label='Your score', color='#5dcf60')
    rects2 = ax.bar(x + width/2, population_means, width, label='Average score', color='#595959')

    # Add some text for labels, title and custom x-axis tick labels, etc.
    ax.set_ylabel('Scores')
    ax.set_title('Scores by label')
    ax.set_xticks(x)
    ax.set_xticklabels(labels, rotation='vertical') #rotation='vertical', fontsize='x-small',
    ax.legend()

    def autolabel(rects):
        """Attach a text label above each bar in *rects*, displaying its height."""
        for rect in rects:
            height = rect.get_height()
            ax.annotate('{}'.format(height),
                        xy=(rect.get_x() + rect.get_width() / 2, height),
                        xytext=(0, 3),  # 3 points vertical offset
                        textcoords="offset points",
                        ha='center', va='bottom')

    autolabel(rects1)
    autolabel(rects2)

    fig.tight_layout()
    # plt.show()
    plt.savefig('bar.png', format="png")

    # bar 2
    labels = ['electricity', 'flights', 'transportation', 'food', 'retail']
    population_means = [7252.76, 602.45, 4515.27, 2267.96, 22.41]
    population_means=list(map(int,population_means))
    individual_means_2=list(map(int, individual_means_2))

    print(labels)
    print(individual_means_2)
    print(population_means)

    x = np.arange(len(labels))  # the label locations
    width = 0.35  # the width of the bars

    fig, ax = plt.subplots()
    rects1 = ax.bar(x - width/2, individual_means_2, width, label='Your score', color='#5dcf60')
    rects2 = ax.bar(x + width/2, population_means, width, label='Average score', color='#595959')

    # Add some text for labels, title and custom x-axis tick labels, etc.
    ax.set_ylabel('kg Co2/year')
    ax.set_title('Scores by label')
    ax.set_xticks(x)
    ax.set_xticklabels(labels, rotation='vertical') #rotation='vertical', fontsize='x-small',
    ax.legend()

    def autolabel(rects):
        """Attach a text label above each bar in *rects*, displaying its height."""
        for rect in rects:
            height = rect.get_height()
            ax.annotate('{}'.format(height),
                        xy=(rect.get_x() + rect.get_width() / 2, height),
                        xytext=(0, 3),  # 3 points vertical offset
                        textcoords="offset points",
                        ha='center', va='bottom')

    autolabel(rects1)
    autolabel(rects2)

    fig.tight_layout()
    # plt.show()
    plt.savefig('bar_2.png', format="png")

    # % of contributions to your carbon footpint
    fig = plt.figure(figsize=(6, 6))
    ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])

    labels = ['electricitiy', 'flights', 'transport', 'food', 'retail']
    fracs = [individual_means_2[0], individual_means_2[1], individual_means_2[2], individual_means_2[3], individual_means_2[4]]
    colors = ['#5dcf60', '#999999', '#A4efa4', '#595959', '#c3dbc3', '#70b170']
    explode = (0, 0, 0, 0, 0)

    # We want to draw the shadow for each pie but we will not use "shadow"
    # option as it does'n save the references to the shadow patches.
    pies = ax.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%', colors=colors)

    for w in pies[0]:
        # set the id with the label.
        w.set_gid(w.get_label())

        # we don't want to draw the edge of the pie
        w.set_edgecolor("none")

    for w in pies[0]:
        # create shadow patch
        s = Shadow(w, -0.01, -0.01)
        s.set_gid(w.get_gid() + "_shadow")
        s.set_zorder(w.get_zorder() - 0.1)
        ax.add_patch(s)

    # save
    plt.savefig('pi.png', format="png")

def make_bar_pdf(pdfname, logo):

    c=canvas.Canvas(pdfname, pagesize=portrait(letter))
    c.setFont('Helvetica-Bold', 16, leading=None)
    c.drawCentredString(300,600,"Your carbon consumption relative to the average American (units)")
    c.drawImage(logo, 0, 200, width=600,height=300, preserveAspectRatio=True)
    #powered by.. for branding
    c.setFont('Helvetica-Bold', 12, leading=None)
    c.drawCentredString(300,100,"Powered by:")
    c.drawImage('logo.png', 250, 30, width=100,height=100, preserveAspectRatio=True)
    c.setFont('Helvetica', 12, leading=None)
    c.drawCentredString(300,50,"http://www.protea.earth")

    #wave looking thing on front page
    c.drawImage('footer.png', -100,-35,width=800,height=100,preserveAspectRatio=True)
    c.save()

def make_bar_2_pdf(pdfname, logo, footprint_delta):

    c=canvas.Canvas(pdfname, pagesize=portrait(letter))
    c.setFont('Helvetica-Bold', 16, leading=None)
    c.drawCentredString(300,600,"Your carbon consumption relative to the average American (kg CO2)")
    c.drawImage(logo, 0, 250, width=600,height=300, preserveAspectRatio=True)

    # now draw the delta
    c.setFont('Helvetica', 11, leading=None)
    if float(footprint_delta) <= 0:
        c.drawCentredString(300, 200, "Your carbon consumption is %s kg CO2 less than the average American."%(footprint_delta))
    else:
        c.drawCentredString(300, 200, "Your carbon consumption is %s kg CO2 over the average American."%(footprint_delta))

    #powered by.. for branding
    c.setFont('Helvetica-Bold', 12, leading=None)
    c.drawCentredString(300,100,"Powered by:")
    c.drawImage('logo.png', 250, 30, width=100,height=100, preserveAspectRatio=True)
    c.setFont('Helvetica', 12, leading=None)
    c.drawCentredString(300,50,"http://www.protea.earth")

    #wave looking thing on front page
    c.drawImage('footer.png', -100,-35,width=800,height=100,preserveAspectRatio=True)
    c.save()

def make_pie_pdf(pdfname):
    c=canvas.Canvas(pdfname, pagesize=portrait(letter))
    logo='pi.png'
    c.setFont('Helvetica-Bold', 16, leading=None)
    c.drawCentredString(300,600,"Your carbon consumption: by category")
    c.drawImage(logo, 0, 200, width=600,height=300, preserveAspectRatio=True)
    #powered by.. for branding
    c.setFont('Helvetica-Bold', 12, leading=None)
    c.drawCentredString(300,100,"Powered by:")
    c.drawImage('logo.png', 250, 30, width=100,height=100, preserveAspectRatio=True)
    c.setFont('Helvetica', 12, leading=None)
    c.drawCentredString(300,50,"http://www.protea.earth")

    #wave looking thing on front page
    c.drawImage('footer.png', -100,-35,width=800,height=100,preserveAspectRatio=True)
    c.save()

def make_getinvolved(pdfname):
    c=canvas.Canvas(pdfname, pagesize=portrait(letter))
    c.setFont('Helvetica-Bold', 16, leading=None)
    c.drawCentredString(300,600,"Get involved")
    c.setFont('Helvetica', 12, leading=None)
    c.drawCentredString(300,500,"● Global Warming of 1.5 degrees C || https://www.ipcc.ch/sr15/")
    c.drawCentredString(300,480,"● Empowering everyday people to become activists || https://www.climaterealityproject.org/")
    c.drawCentredString(300,460,"● EU Climate Knowledge and Innovation Community --> https://www.climate-kic.org/")
    c.drawCentredString(300,440,"● Measure Ecological Footprint || https://www.footprintnetwork.org/")
    c.drawCentredString(300,420,"● Technical climate solutions || https://www.drawdown.org/")
    c.drawCentredString(300,400,"● Sustainable textile supply chain || https://textileexchange.org/")
    c.drawCentredString(300,380,"● Student strike on Fridays || https://globalclimatestrike.net/")
    c.drawCentredString(300,360,"● Breakthrough Energy Coalition and Ventures || http://www.b-t.energy/")
    c.drawCentredString(300,340,"● Machine Learning for Climate Change || https://www.climatechange.ai/")
    c.drawCentredString(300,320,"● NASA: Facts around Climate Change || https://climate.nasa.gov/evidence/")

    #powered by.. for branding
    c.setFont('Helvetica-Bold', 12, leading=None)
    c.drawCentredString(300,100,"Powered by:")
    c.drawImage('logo.png', 250, 30, width=100,height=100, preserveAspectRatio=True)
    c.setFont('Helvetica', 12, leading=None)
    c.drawCentredString(300,50,"http://www.protea.earth")

    #wave looking thing on front page
    c.drawImage('footer.png', -100,-35,width=800,height=100,preserveAspectRatio=True)
    c.save()

def make_lastpage(pdfname):
    c=canvas.Canvas(pdfname, pagesize=portrait(letter))
    # Protea signup
    c.drawImage('protea_signup.png', 100, 200, width=400,height=400, preserveAspectRatio=True)

    #wave looking thing on front page
    c.drawImage('footer.png', -100,-35,width=800,height=100,preserveAspectRatio=True)
    c.save()

def merge_pdfs():
    # get file list
    listdir=os.listdir()
    # get only pdfs
    pdflist=list()
    for i in range(len(listdir)):
        if listdir[i][-4:]=='.pdf':
            pdflist.append(listdir[i])
    # sort pdfs by number
    pdflist=sortnames(pdflist)
    print(pdflist)

    merger = PdfFileMerger()
    for pdf in pdflist:
       merger.append(PdfFileReader(open(pdf, 'rb')))
    merger.write('merged.pdf')

def improvement_pdf(pdfname, truthlist):
    # areas of improvement (recommend areas of improvement)
    # how to be more involved

    c=canvas.Canvas(pdfname, pagesize=portrait(letter))
    c.setFont('Helvetica-Bold', 16, leading=None)
    c.drawCentredString(300,600,"Here are some recommendations to become a better climate citizen:")
    recommendation_list=['ask questions to learn more about climate issues',
                       'carpool with friends to/from work',
                       'collect climate data in the scientific community',
                       'take efforts to clean nature (e.g. like recycling off the street)',
                       'reflect upon being a better climate citizen',
                       'collect and use clean energy (e.g. wind or solar power)',
                       'eat less meat when you can going out',
                       'buy local produce from farmers markets on weekends',
                       'exercise instead of uber to work',
                       'make eco-friendly purchases (e.g. Allbirds shoes instead of Nike shoes)',
                       'participate in climate-related events',
                       'post articles about climate change on social media platforms',
                       'plant trees or take care of plants in your house',
                       'try to recycle and reduce waste when you can',
                       'take fewer flights and/or reduce your own transportation',
                       'turn off your air conditioning unit and use less electricity when you can',
                       'write scientific papers and/or blog posts on climate change issues',
                       'take showers for 5 minutes or less']

    recommendations=list()
    for i in range(len(truthlist)):
        if truthlist[i] == False:
            recommendations.append(recommendation_list[i])

    c.setFont('Helvetica', 11, leading=None)
    height=550
    for i in range(len(recommendations)):
        c.drawCentredString(300,height,recommendations[i])
        height=height-20

    #powered by.. for branding
    c.setFont('Helvetica-Bold', 12, leading=None)
    c.drawCentredString(300,100,"Powered by:")
    c.drawImage('logo.png', 250, 30, width=100,height=100, preserveAspectRatio=True)
    c.setFont('Helvetica', 12, leading=None)
    c.drawCentredString(300,50,"http://www.protea.earth")

    #wave looking thing on front page
    c.drawImage('footer.png', -100,-35,width=800,height=100,preserveAspectRatio=True)

    c.save()

##############################################################################
##                            MAIN SCRIPT                                   ##
##############################################################################

print('please type answers to the following questions below')
email=input('what is your email? \n')
# example = 2
answer_1 = input('How many people are in your household? (e.g. 2) \n')
answer_1=clean_answer(answer_1)
# example = 50
answer_2 = input('What is your electric bill (in dollars) monthly?  (e.g. 50) \n')
answer_2=clean_answer(answer_2)
# example = 5
answer_3 = input('How many flights do you take per year? (e.g. 10) \n')
answer_3=clean_answer(answer_3)
# example = no
answer_4 = input('Do you own a car? (e.g. n | y) \n')
answer_4=clean_answer(answer_4)
# example = 1
answer_5 = input('What is your average distance to commute to/from work in miles - for example 21? (e.g. 10) \n')
answer_5=clean_answer(answer_5)
# example = yes
answer_6= input('Do you use public transportation? (e.g. y)\n')
answer_6=clean_answer(answer_6)
# example = yes
answer_7 = input('Do you use uber or another ride sharing platform like Lyft? (e.g. y) \n')
answer_7=clean_answer(answer_7)

if answer_7 == 'yes':
    # example = 5
    answer_8 = input("How many ride-sharing trips do you complete per month? (e.g. 10) \n")
    answer_8=clean_answer(answer_8)
else:
    answer_8 = '0'

# example = yes
answer_9 = input('Are you a vegetarian? (e.g. n) \n')
answer_9=clean_answer(answer_9)
# example = no
answer_10= input('Do you eat meat more than 3 times each week? (e.g. y) \n')
answer_10=clean_answer(answer_10)
# example = 50
answer_11 = input('How much money do you spend on Amazon per month in US dollars - for example, fifty dollars? (e.g. 150) \n')
answer_11=clean_answer(answer_11)

answers=[answer_1, answer_2, answer_3, answer_4, answer_5,
         answer_6, answer_7, answer_8, answer_9, answer_10, answer_11]

# -----------------
# FOR TESTING ONLY
# email='js@neurolex.co'
# answers=['2', '50', '5', 'no', '1', 'yes', 'yes', '5', 'no', 'yes', '50']
# answer_1='2'
# answer_2='50'
# answer_3='5'
# answer_4='no'
# answer_5='1'
# answer_6='yes'
# answer_7='yes'
# answer_8='5'
# answer_9='no'
# answer_10='yes'
# answer_11='50'
# -----------------

## report on recommendations pop up + saved in directory
footprint, footprintbytype, footprint_delta, footprintbytype_delta, labels_footprint, labels_footprintbytype =calculate_footprint(answers)

data = {'email': email,
        #'questions': questions,
        'answers': answers,
        'footprint': footprint,
        'footprintbytype': footprintbytype,
        'footprint_delta': footprint_delta,
        'footprintbytype_delta': footprintbytype_delta,
        'labels_footprint': labels_footprint,
        'labels_footprintbytype': labels_footprintbytype}

print(data)

# compared to averages
# footprint_avg = 14642.40 kg Co2/year
# footprintbytype_avg = [7252.76, 602.45, 4,4515.27, 2267.96]

########################################################
##              Now create the PDF                    ##
########################################################

curdir=os.getcwd()
os.chdir('assets')
html_message=open('msg.html').read()
description=open('description.txt').read()
os.chdir(curdir)

tempdir='tempdir'
try:
    os.mkdir(tempdir)
    os.chdir(tempdir)
except:
    shutil.rmtree(tempdir)
    os.mkdir(tempdir)
    os.chdir(tempdir)

# copy required assets
shutil.copy(curdir+'/assets/protea_signup.png', os.getcwd()+'/protea_signup.png')
shutil.copy(curdir+'/assets/cover_text.png', os.getcwd()+'/cover_text.png')
shutil.copy(curdir+'/assets/footer.png', os.getcwd()+'/footer.png')
shutil.copy(curdir+'/assets/logo.png', os.getcwd()+'/logo.png')
shutil.copy(curdir+'/assets/main_contributors.png', os.getcwd()+'/main_contributors.png')

# Now make all the .PDF pages
print(footprintbytype[3])
# individual_means = ['Electricity consumption (kwh * 1000)', '# of flights per year', '# of driven miles/year (thousands)', '# of uber trips/year', 'food choice (tons of CO2 emissions/year)']
if answer_4 == 'yes' and answer_6 == 'no':
    individual_means = [(int(answer_2)/0.1327)*12/1000, int(answer_3), int(answer_5)*220*2/1000, int(answer_8)*12, footprintbytype[3]/1000]
elif answer_4 == 'yes' and answer_6 == 'yes':
    individual_means = [(int(answer_2)/0.1327)*12/1000, int(answer_3), int(answer_5)*220*2/1000, int(answer_8)*12,  footprintbytype[3]/1000]
elif answer_4 == 'no' and answer_6 == 'yes':
    individual_means = [(int(answer_2)/0.1327)*12/1000, int(answer_3), int(answer_5)*220*2/1000, int(answer_8)*12, footprintbytype[3]/1000]
else:
    individual_means = [(int(answer_2)/0.1327)*12/1000, int(answer_3), 0, int(answer_8)*12, footprintbytype[3]/1000]

individual_means=list(map(int,individual_means))

#['electric (kg Co2/year)', 'flight (kg Co2/year)', 'transportation (kg Co2/year)', 'food (kg Co2/year)', 'retail (kg Co2/year)']
truthlist=[False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
delete_pdfs()
cover_page('1.pdf', email, 'User', str(datetime.datetime.now()), '100')
make_graphs(individual_means, footprintbytype)
make_bar_pdf('2.pdf', 'bar.png')
make_bar_2_pdf('3.pdf', 'bar_2.png', str(footprint_delta))
make_pie_pdf('4.pdf')
improvement_pdf('5.pdf', truthlist)
make_getinvolved('6.pdf')
shutil.copy(curdir+'/assets/7.pdf', os.getcwd()+'/7.pdf')
make_lastpage('8.pdf')
merge_pdfs()
filename='footprint_report.pdf'
os.rename('merged.pdf', filename)
os.system('open %s'%(filename))


import pygame as pg
from settings import *
import net
from worrior import *

def make_text(text, font, color, surface, x, y):
    textobj = font.render(text, 1, color)
    textrect = textobj.get_rect()
    textrect.topleft = (x, y)
    surface.blit(textobj, textrect)

#def is_in_DB(nick):
#    return False

screen = pg.display.set_mode((1280, 720))
font = pg.font.Font(None, 32)
font2 = pg.font.Font(None, 64)

pg.display.set_caption('Main Menu')
clock = pg.time.Clock()
pg.init()
click = False
done = False
hero = Worrior()

def main_menu(player_list):

    input_box = pg.Rect(520, 300, 140, 32)
    color_inactive = pg.Color('lightskyblue3')
    color_active = pg.Color('dodgerblue2')
    color = color_inactive
    active = False
    text = ''
    done = False
    nick = ''

    print(f'player list: {player_list}')

    while not done:

        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            if event.type == pg.MOUSEBUTTONDOWN:

                if input_box.collidepoint(event.pos):

                    active = not active

                else:
                    active = False

                color = color_active if active else color_inactive
            if event.type == pg.KEYDOWN:
                if active:
                    if event.key == pg.K_RETURN:
                        nick = text
                        print(text)
                        text = ''
                        print(net.is_in_DB(nick))
                        playerData = next((x for x in player_list if x['name'] == nick), None)

                        if playerData == None:

                            print('wchodzi do pentli i ')
                            print(nick)
                            create_champ(nick)
                            done = True
                        else:
                            print(playerData)
                            global hero
                            hero = Worrior(ad=playerData['ad'], arm=playerData['arm'], curr_quests=playerData['curr_quests'], dir=playerData['dir'], exp=playerData['exp'], gold=playerData['gold'], hp=playerData['hp'], map=playerData['map'], moving=playerData['moving'], name=playerData['name'], pa=playerData['pa'], position_x=playerData['position_x'], position_y=playerData['position_y'], prev_map=playerData['prev_map'], prev_pos=playerData['prev_pos'], quests_completed=playerData['quests_completed'], sprite=playerData['sprite'], velocity=playerData['velocity'])
                            return hero

                    elif event.key == pg.K_BACKSPACE:
                        text = text[:-1]
                    else:
                        text += event.unicode

        screen.fill((30, 30, 30))

        txt_surface = font.render(text, True, color)

        width = max(200, txt_surface.get_width()+10)
        input_box.w = width

        screen.blit(txt_surface, (input_box.x+5, input_box.y+5))

        pg.draw.rect(screen, color, input_box, 2)

        make_text('Welcome in our game!', font2, color_inactive, screen, 375, 50)
        make_text('Insert your nickname to start the game', font, color_inactive, screen, 400, 200)
        make_text('Your Nick:', font, color_inactive, screen, 560, 260)
        pg.display.flip()

def create_champ(nick):
    global click
    running = True
    available_points = 20
    strength = 0
    health = 0
    armor = 0
    action = 0

    while running:
        screen.fill(darkgray)
        make_text('Creating your champion', font, black, screen, 20, 20)
        mx, my = pygame.mouse.get_pos()
        make_text(f'Remainings available points: {available_points}', font, black, screen, 900, 20)

        #strength --------------------
        button_strength_add = pygame.Rect(350, 100, 76, 76)
        button_strength_reduce = pygame.Rect(450, 100, 76, 76)

        make_text(f'Strength points: {strength}', font, black, screen, 20, 125)

        if button_strength_add.collidepoint((mx, my)):
            if click:
                if available_points > 0:
                    available_points -= 1
                    strength += 1

        if button_strength_reduce.collidepoint((mx, my)):
            if click:
                if strength > 0:
                    strength -= 1
                    available_points += 1

        #health-----------------------
        button_health_add = pygame.Rect(350, 200, 76, 76)
        button_health_reduce = pygame.Rect(450, 200, 76, 76)

        make_text(f'Health points: {health}', font, black, screen, 20, 225)

        if button_health_add.collidepoint((mx, my)):
            if click:
                if available_points > 0:
                    available_points -= 1
                    health += 1

        if button_health_reduce.collidepoint((mx, my)):
            if click:
                if health > 0:
                    health -= 1
                    available_points += 1

        #armor ----------------------
        button_armor_add = pygame.Rect(350, 300, 76, 76)
        button_armor_reduce = pygame.Rect(450, 300, 76, 76)

        make_text(f'Armor points: {armor}', font, black, screen, 20, 325)

        if button_armor_add.collidepoint((mx, my)):
            if click:
                if available_points > 0:
                    available_points -= 1
                    armor += 1

        if button_armor_reduce.collidepoint((mx, my)):
            if click:
                if armor > 0:
                    armor -= 1
                    available_points += 1

        #action ----------------------
        button_action_add = pygame.Rect(350, 400, 76, 76)
        button_action_reduce = pygame.Rect(450, 400, 76, 76)

        make_text(f'Action points: {action}', font, black, screen, 20, 425)

        if button_action_add.collidepoint((mx, my)):
            if click:
                if available_points > 0:
                    available_points -= 1
                    action += 1

        if button_action_reduce.collidepoint((mx, my)):
            if click:
                if action > 0:
                    action -= 1
                    available_points += 1

        # submit
        button_submit = pygame.Rect(800, 270, 256, 256)

        if button_submit.collidepoint((mx, my)):
            global hero, done
            if click:
                if available_points == 0:
                    action += 10
                    print("zmieniam hirka")
                    hero = Worrior(name=nick, sprite='./BULBA64.png', position_x = 0, position_y = 0, hp = (health * 50), ad = (strength * 25), arm = (armor * 10), pa = action)
                    print(f'moje statystyki {hero.name} \n {hero.sprite}   \n {hero.hp}  \n {hero.arm}')
                    done = True
                    running = False
                    return hero

        #generating buttons ------------
        pygame.draw.rect(screen, lightskyblue3, button_strength_add)
        pygame.draw.rect(screen, lightskyblue3, button_strength_reduce)
        screen.blit(plus2, button_strength_add)
        screen.blit(minus2, button_strength_reduce)

        pygame.draw.rect(screen, lightskyblue3, button_health_add)
        pygame.draw.rect(screen, lightskyblue3, button_health_reduce)
        screen.blit(plus2, button_health_add)
        screen.blit(minus2, button_health_reduce)

        pygame.draw.rect(screen, lightskyblue3, button_armor_add)
        pygame.draw.rect(screen, lightskyblue3, button_armor_reduce)
        screen.blit(plus2, button_armor_add)
        screen.blit(minus2, button_armor_reduce)

        pygame.draw.rect(screen, lightskyblue3, button_action_add)
        pygame.draw.rect(screen, lightskyblue3, button_action_reduce)
        screen.blit(plus2, button_action_add)
        screen.blit(minus2, button_action_reduce)

        pygame.draw.rect(screen, darkgray, button_submit)
        screen.blit(start3, button_submit)

        click = False

        for event in pg.event.get():
            if event.type == pg.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_ESCAPE:
                    running = False
            if event.type == pg.MOUSEBUTTONDOWN:
                if event.button == 1:
                    click = True

        pygame.display.update()
        clock.tick(60)

def get_hero():
    global hero
    return hero


"""
Tic Tac Toe Player
"""

import math
import random
import copy

X = "X"
O = "O"
EMPTY = None

#added_helping_functions
def get_horizontal_winner(board):
    winner_val = None
    len_board = len(board)

    for i in range(len_board):
        winner_val = board[i][0]
        for j in range(len_board):
            if board[i][j] != winner_val:
                winner_val = None
        if winner_val:
            return winner_val
    return winner_val

def get_vertical_winner(board):
    winner_val = None
    len_board = len(board)

    for i in range(len_board):
        winner_val = board[0][i]
        for j in range(len_board):
            if board[j][i] != winner_val:
                winner_val = None
        if winner_val:
            return winner_val
    return winner_val

def get_diagonal_winner(board):
    winner_val = None
    len_board = len(board)
    winner_val = board[0][0]
    for i in range(len_board):
            if board[i][i] != winner_val:
                winner_val = None
    if winner_val:
        return winner_val

    winner_val = board[0][len_board -1]
    for i in range(len_board):
        j= len_board -1 -i
        if board[i][j] != winner_val:
            winner_val = None
    return winner_val

def min_max_value(board,player,alpha,beta):
    #fto get minimum/maximum value
    if terminal(board):
        return utility(board)
    if player== X:
        v= -math.inf
        for action in actions(board):
            v=max(v,min_max_value(result(board,action),O,alpha,beta))
            alpha=max(v,alpha)
            if alpha >= beta:
                break
        return v
    else:

        v= math.inf
        for action in actions(board):
            v=min(v,min_max_value(result(board,action),X, alpha, beta))
            beta=min(v,beta)
            if alpha >= beta:
                break
        return v

#existing tictactoe functions
def initial_state():
    """
    Returns starting state of the board.
    """
    return [[EMPTY, EMPTY, EMPTY],
            [EMPTY, EMPTY, EMPTY],
            [EMPTY, EMPTY, EMPTY]]

def player(board):
    """
    Returns player who has the next turn on a board.
    """
    count = 0
    for row in board:
        for cell in row:
            if cell:
                count += 1
    if count % 2 != 0:
        return O
    return X

def actions(board):
    """
    Returns set of all possible actions (i, j) available on the board.
    """
    result = set()

    for i, row in enumerate(board):
        if EMPTY in row:
            for j, space in enumerate(row):
                if space is EMPTY:
                    result.add((i,j))
    return result

def result(board, action):
    """
    Returns the board that results from making move (i, j) on the board.
    """
    if terminal(board):
        raise ValueError("GAME OVER")
    else:
        result_board = copy.deepcopy(board)
        current_player = player(result_board)
        (i, j) = action
        result_board[i][j]=current_player

    return result_board

def winner(board):
    """
    Returns the winner of the game, if there is one.
    """
    winner_val = get_horizontal_winner(board) or get_vertical_winner(board) or get_diagonal_winner(board)
    return winner_val

def terminal(board):
    """
    Returns True if game is over, False otherwise.
    """
    if winner(board) != None:
        return True

    for row in board:
        for cell  in row:
            if cell == EMPTY:
                return False
    return True

def utility(board):
    """
    Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
    """
    winner_player = winner(board)
    if winner_player == X:
        return 1
    elif winner_player == O:
        return -1
    return 0

def minimax(board):
    """
    Returns the optimal action for the current player on the board.
    """
    if board == initial_state():
       return(random.randint(0,2), random.randint(0,2))
    current_player = player(board)
    alpha = -math.inf
    beta  = math.inf
    action_to_return = None
    if current_player == X:
        val = -(math.inf)

        for action in actions(board):
            result_minvalue= min_max_value(result(board,action),O,alpha,beta)
            alpha= max(result_minvalue,val)
            if val < result_minvalue:
                val = result_minvalue
                action_to_return = action
    elif current_player == O:
        val = math.inf
        action_to_return = None
        for action in actions(board):
            result_maxvalue= min_max_value(result(board,action),X,alpha,beta)
            beta= min(val,result_maxvalue)
            if val > result_maxvalue:
                val = result_maxvalue
                action_to_return = action

    return action_to_return


from unittest import TestCase
from unittest.mock import patch, MagicMock

from deliveryAPI.cache.LocationCache import UELocationCache

MODULE_PATH = "deliveryAPI.cache.LocationCache."

@patch(MODULE_PATH + "dbm")
@patch(MODULE_PATH + "time")
class Test_UELocationCache_getCacheLocation(TestCase):

    def test_valid_cache(self, time_, dbm):
        time_.time.return_value = 604800
        dbCache = MagicMock()
        dbCache.get.return_value = '{"example": "data", "create_date": 0}'
        dbm.open.return_value.__enter__.return_value = dbCache

        location = UELocationCache.getCacheLocation("AB12ABC")

        self.assertEqual(
            {
                "example": "data"
            },
            location
        )

    def test_no_postcode_cache(self, _time, dbm):
        dbCache = MagicMock()
        dbCache.get.return_value = None
        dbm.open.return_value.__enter__.return_value = dbCache

        location = UELocationCache.getCacheLocation("AB12ABC")

        self.assertIsNone(location)

    def test_expired(self, time_, dbm):
        time_.time.return_value = 604801
        dbCache = MagicMock()
        dbCache.get.return_value = '{"example": "data", "create_date": 0}'
        dbm.open.return_value.__enter__.return_value = dbCache

        location = UELocationCache.getCacheLocation("AB12ABC")

        self.assertIsNone(location)
        dbCache.__delitem__.assert_called_once_with("AB12ABC")

@patch(MODULE_PATH + "dbm")
@patch(MODULE_PATH + "time")
class Test_UELocationCache_setCacheLocation(TestCase):

    def test_ok(self, time_, dbm):
        time_.time.return_value = 1234
        dbm.open.return_value.__enter__.return_value = {}

        UELocationCache.setCacheLocation(
            "AB12ABC", {"example": "data"}
        )

        self.assertEqual(
            '{"example": "data", "create_date": 1234}',
            dbm.open.return_value.__enter__.return_value["AB12ABC"]
        )


import sys

T = int(sys.stdin.readline())

net = [False, False] + [True]*9999

for i in range(2, 10000):
    if net[i]:
        tmp = 2 * i
        while tmp < 10000:
            net[tmp] = False
            tmp += i

for _ in range(T):
    a, b = map(int, sys.stdin.readline().split())

    chk = [False for _ in range(10001)]
    dis = [100000 for _ in range(10001)]
    que = [a]
    chk[a] = True
    dis[a] = 0

    while que:
        tmp = que.pop(0)

        if tmp == b:
            break

        for i in range(4):  # 천, 백, 십, 일자릿수
            stmp = str(tmp)

            for j in range(10):
                stmp = stmp[:i] + str(j) + stmp[i + 1:]  # 해당 자릿수를 바꿈
                tmp2 = int(stmp)

                if 1000 < tmp2 < 10000 and net[tmp2] and not chk[tmp2] and dis[tmp2] > dis[tmp] + 1:  # 바꾼 수가 소수일 경우
                    dis[tmp2] = dis[tmp] + 1
                    que.append(tmp2)
                    chk[tmp2] = True  # 이 수는 더이상 큐에 안 넣는다.

    print(dis[b])


from __future__ import annotations

import cmath
import concurrent.futures
import copy
import itertools
import logging
import math
import random

import iotbx.phil
import libtbx
from libtbx.test_utils import approx_equal
from libtbx.utils import plural_s
from rstbx.dps_core import Direction, Directional_FFT
from rstbx.indexing_api import dps_extended
from rstbx.indexing_api.lattice import DPS_primitive_lattice
from rstbx.phil.phil_preferences import indexing_api_defs
from scitbx import matrix
from scitbx.array_family import flex
from scitbx.simplex import simplex_opt

import dials.util
from dials.algorithms.indexing.indexer import find_max_cell
from dials.util import Sorry, log
from dials.util.mp import available_cores
from dials.util.options import ArgumentParser, reflections_and_experiments_from_files
from dials.util.slice import slice_reflections

logger = logging.getLogger("dials.command_line.search_beam_position")

help_message = """

Search for a better beam centre using the results of spot finding. Based on
method of Sauter et al., J. Appl. Cryst. 37, 399-409 (2004).

Examples::

  dials.search_beam_position imported.expt strong.refl
"""

phil_scope = iotbx.phil.parse(
    """
nproc = Auto
  .type = int(value_min=1)
plot_search_scope = False
  .type = bool
max_cell = None
  .type = float
  .help = "Known max cell (otherwise will compute from spot positions)"
image_range = None
  .help = "The range of images to use in indexing. Number of arguments"
    "must be a factor of two. Specifying \"0 0\" will use all images"
    "by default. The given range follows C conventions"
    "(e.g. j0 <= j < j1)."
  .type = ints(size=2)
  .multiple = True
max_reflections = 10000
  .type = int(value_min=1)
  .help = "Maximum number of reflections to use in the search for better"
          "experimental model. If the number of input reflections is greater"
          "then a random subset of reflections will be used."
mm_search_scope = 4.0
  .help = "Global radius of origin offset search."
  .type = float(value_min=0)
wide_search_binning = 2
  .help = "Modify the coarseness of the wide grid search for the beam centre."
  .type = float(value_min=0)
n_macro_cycles = 1
  .type = int
  .help = "Number of macro cycles for an iterative beam centre search."
d_min = None
  .type = float(value_min=0)

seed = 42
  .type = int(value_min=0)

output {
  experiments = optimised.expt
    .type = path
  log = "dials.search_beam_position.log"
    .type = str
}
"""
)

def optimize_origin_offset_local_scope(
    experiments,
    reflection_lists,
    solution_lists,
    amax_lists,
    mm_search_scope=4,
    wide_search_binning=1,
    plot_search_scope=False,
):
    """Local scope: find the optimal origin-offset closest to the current overall detector position
    (local minimum, simple minimization)"""

    beam = experiments[0].beam
    s0 = matrix.col(beam.get_s0())
    # construct two vectors that are perpendicular to the beam.  Gives a basis for refining beam
    axis = matrix.col((1, 0, 0))
    beamr0 = s0.cross(axis).normalize()
    beamr1 = beamr0.cross(s0).normalize()
    beamr2 = beamr1.cross(s0).normalize()

    assert approx_equal(s0.dot(beamr1), 0.0)
    assert approx_equal(s0.dot(beamr2), 0.0)
    assert approx_equal(beamr2.dot(beamr1), 0.0)
    # so the orthonormal vectors are s0, beamr1 and beamr2

    if mm_search_scope:
        plot_px_sz = experiments[0].detector[0].get_pixel_size()[0]
        plot_px_sz *= wide_search_binning
        grid = max(1, int(mm_search_scope / plot_px_sz))
        widegrid = 2 * grid + 1

        def get_experiment_score_for_coord(x, y):
            new_origin_offset = x * plot_px_sz * beamr1 + y * plot_px_sz * beamr2
            return sum(
                _get_origin_offset_score(
                    new_origin_offset,
                    solution_lists[i],
                    amax_lists[i],
                    reflection_lists[i],
                    experiment,
                )
                for i, experiment in enumerate(experiments)
            )

        scores = flex.double(
            get_experiment_score_for_coord(x, y)
            for y in range(-grid, grid + 1)
            for x in range(-grid, grid + 1)
        )

        def igrid(x):
            return x - (widegrid // 2)

        idxs = [igrid(i) * plot_px_sz for i in range(widegrid)]

        # if there are several similarly high scores, then choose the closest
        # one to the current beam centre
        potential_offsets = flex.vec3_double()
        if scores.all_eq(0):
            raise Sorry("No valid scores")
        sel = scores > (0.9 * flex.max(scores))
        for i in sel.iselection():
            offset = (idxs[i % widegrid]) * beamr1 + (idxs[i // widegrid]) * beamr2
            potential_offsets.append(offset.elems)
            # print offset.length(), scores[i]
        wide_search_offset = matrix.col(
            potential_offsets[flex.min_index(potential_offsets.norms())]
        )

    else:
        wide_search_offset = None

    # Do a simplex minimization
    class simplex_minimizer:
        def __init__(self, wide_search_offset):
            self.n = 2
            self.wide_search_offset = wide_search_offset
            self.optimizer = simplex_opt(
                dimension=self.n,
                matrix=[flex.random_double(self.n) for _ in range(self.n + 1)],
                evaluator=self,
                tolerance=1e-7,
            )
            self.x = self.optimizer.get_solution()
            self.offset = self.x[0] * 0.2 * beamr1 + self.x[1] * 0.2 * beamr2
            if self.wide_search_offset is not None:
                self.offset += self.wide_search_offset

        def target(self, vector):
            trial_origin_offset = vector[0] * 0.2 * beamr1 + vector[1] * 0.2 * beamr2
            if self.wide_search_offset is not None:
                trial_origin_offset += self.wide_search_offset
            target = 0
            for i, experiment in enumerate(experiments):
                target -= _get_origin_offset_score(
                    trial_origin_offset,
                    solution_lists[i],
                    amax_lists[i],
                    reflection_lists[i],
                    experiment,
                )
            return target

    new_offset = simplex_minimizer(wide_search_offset).offset

    if plot_search_scope:
        plot_px_sz = experiments[0].get_detector()[0].get_pixel_size()[0]
        grid = max(1, int(mm_search_scope / plot_px_sz))
        scores = flex.double()
        for y in range(-grid, grid + 1):
            for x in range(-grid, grid + 1):
                new_origin_offset = x * plot_px_sz * beamr1 + y * plot_px_sz * beamr2
                score = 0
                for i, experiment in enumerate(experiments):
                    score += _get_origin_offset_score(
                        new_origin_offset,
                        solution_lists[i],
                        amax_lists[i],
                        reflection_lists[i],
                        experiment,
                    )
                scores.append(score)

        def show_plot(widegrid, excursi):
            excursi.reshape(flex.grid(widegrid, widegrid))
            idx_max = flex.max_index(excursi)

            def igrid(x):
                return x - (widegrid // 2)

            idxs = [igrid(i) * plot_px_sz for i in range(widegrid)]

            from matplotlib import pyplot as plt

            plt.figure()
            CS = plt.contour(
                [igrid(i) * plot_px_sz for i in range(widegrid)],
                [igrid(i) * plot_px_sz for i in range(widegrid)],
                excursi.as_numpy_array(),
            )
            plt.clabel(CS, inline=1, fontsize=10, fmt="%6.3f")
            plt.title("Wide scope search for detector origin offset")
            plt.scatter([0.0], [0.0], color="g", marker="o")
            plt.scatter([new_offset[0]], [new_offset[1]], color="r", marker="*")
            plt.scatter(
                [idxs[idx_max % widegrid]],
                [idxs[idx_max // widegrid]],
                color="k",
                marker="s",
            )
            plt.axes().set_aspect("equal")
            plt.xlabel("offset (mm) along beamr1 vector")
            plt.ylabel("offset (mm) along beamr2 vector")
            plt.savefig("search_scope.png")

            # changing value
            trial_origin_offset = (idxs[idx_max % widegrid]) * beamr1 + (
                idxs[idx_max // widegrid]
            ) * beamr2
            return trial_origin_offset

        show_plot(widegrid=2 * grid + 1, excursi=scores)

    new_experiments = copy.deepcopy(experiments)
    for expt in new_experiments:
        expt.detector = dps_extended.get_new_detector(expt.detector, new_offset)
    return new_experiments

def _get_origin_offset_score(
    trial_origin_offset, solutions, amax, spots_mm, experiment
):
    trial_detector = dps_extended.get_new_detector(
        experiment.detector, trial_origin_offset
    )
    experiment = copy.copy(experiment)
    experiment.detector = trial_detector

    # Key point for this is that the spots must correspond to detector
    # positions not to the correct RS position => reset any fixed rotation
    # to identity

    experiment.goniometer.set_fixed_rotation((1, 0, 0, 0, 1, 0, 0, 0, 1))
    spots_mm.map_centroids_to_reciprocal_space([experiment])
    return _sum_score_detail(spots_mm["rlp"], solutions, amax=amax)

def _sum_score_detail(reciprocal_space_vectors, solutions, granularity=None, amax=None):
    """Evaluates the probability that the trial value of (S0_vector | origin_offset) is correct,
    given the current estimate and the observations.  The trial value comes through the
    reciprocal space vectors, and the current estimate comes through the short list of
    DPS solutions. Actual return value is a sum of NH terms, one for each DPS solution, each ranging
    from -1.0 to 1.0"""

    nh = min(solutions.size(), 20)  # extended API
    sum_score = 0.0
    for t in range(nh):
        dfft = Directional_FFT(
            angle=Direction(solutions[t]),
            xyzdata=reciprocal_space_vectors,
            granularity=5.0,
            amax=amax,  # extended API XXX These values have to come from somewhere!
            F0_cutoff=11,
        )
        kval = dfft.kval()
        kmax = dfft.kmax()
        kval_cutoff = reciprocal_space_vectors.size() / 4.0
        if kval > kval_cutoff:
            ff = dfft.fft_result
            kbeam = ((-dfft.pmin) / dfft.delta_p) + 0.5
            Tkmax = cmath.phase(ff[kmax])
            backmax = math.cos(
                Tkmax + (2 * math.pi * kmax * kbeam / (2 * ff.size() - 1))
            )
            ### Here it should be possible to calculate a gradient.
            ### Then minimize with respect to two coordinates.  Use lbfgs?  Have second derivatives?
            ### can I do something local to model the cosine wave?
            ### direction of wave travel.  Period. phase.
            sum_score += backmax
    return sum_score

def run_dps(experiment, spots_mm, max_cell):
    # max_cell: max possible cell in Angstroms; set to None, determine from data
    # recommended_grid_sampling_rad: grid sampling in radians; guess for now

    horizon_phil = iotbx.phil.parse(input_string=indexing_api_defs).extract()
    DPS = DPS_primitive_lattice(
        max_cell=max_cell, recommended_grid_sampling_rad=None, horizon_phil=horizon_phil
    )

    DPS.S0_vector = matrix.col(experiment.beam.get_s0())
    DPS.inv_wave = 1.0 / experiment.beam.get_wavelength()
    if experiment.goniometer is None:
        DPS.axis = matrix.col((1, 0, 0))
    else:
        DPS.axis = matrix.col(experiment.goniometer.get_rotation_axis())
    DPS.set_detector(experiment.detector)

    # transform input into what DPS needs
    # i.e., construct a flex.vec3 double consisting of mm spots, phi in degrees
    data = flex.vec3_double()
    for spot in spots_mm.rows():
        data.append(
            (
                spot["xyzobs.mm.value"][0],
                spot["xyzobs.mm.value"][1],
                spot["xyzobs.mm.value"][2] * 180.0 / math.pi,
            )
        )

    logger.info("Running DPS using %i reflections", len(data))

    DPS.index(
        raw_spot_input=data,
        panel_addresses=flex.int(s["panel"] for s in spots_mm.rows()),
    )
    solutions = DPS.getSolutions()

    logger.info(
        "Found %i solution%s with max unit cell %.2f Angstroms.",
        len(solutions),
        plural_s(len(solutions))[1],
        DPS.amax,
    )

    # There must be at least 3 solutions to make a set, otherwise return empty result
    if len(solutions) < 3:
        return {}
    return {"solutions": flex.vec3_double(s.dvec for s in solutions), "amax": DPS.amax}

def discover_better_experimental_model(
    experiments,
    reflections,
    params,
    nproc=1,
    d_min=None,
    mm_search_scope=4.0,
    wide_search_binning=1,
    plot_search_scope=False,
):
    assert len(experiments) == len(reflections)
    assert len(experiments) > 0

    refl_lists = []
    max_cell_list = []

    # The detector/beam of the first experiment is used to define the basis for the
    # optimisation, so assert that the beam intersects with the detector
    detector = experiments[0].detector
    beam = experiments[0].beam
    beam_panel = detector.get_panel_intersection(beam.get_s0())
    if beam_panel == -1:
        raise Sorry("input beam does not intersect detector")

    for expt, refl in zip(experiments, reflections):
        refl = copy.deepcopy(refl)
        refl["imageset_id"] = flex.int(len(refl), 0)
        refl.centroid_px_to_mm([expt])
        refl.map_centroids_to_reciprocal_space([expt])

        if d_min is not None:
            d_spacings = 1 / refl["rlp"].norms()
            sel = d_spacings > d_min
            refl = refl.select(sel)

        # derive a max_cell from mm spots
        if params.max_cell is None:
            max_cell = find_max_cell(
                refl, max_cell_multiplier=1.3, step_size=45
            ).max_cell
            max_cell_list.append(max_cell)

        if params.max_reflections is not None and refl.size() > params.max_reflections:
            logger.info(
                "Selecting subset of %i reflections for analysis",
                params.max_reflections,
            )
            perm = flex.random_permutation(refl.size())
            sel = perm[: params.max_reflections]
            refl = refl.select(sel)

        refl_lists.append(refl)

    if params.max_cell is None:
        max_cell = flex.median(flex.double(max_cell_list))
    else:
        max_cell = params.max_cell

    with concurrent.futures.ProcessPoolExecutor(max_workers=nproc) as pool:
        solution_lists = []
        amax_list = []
        for result in pool.map(
            run_dps, experiments, refl_lists, itertools.repeat(max_cell)
        ):
            if result.get("solutions"):
                solution_lists.append(result["solutions"])
                amax_list.append(result["amax"])

    if not solution_lists:
        raise Sorry("No solutions found")

    new_experiments = optimize_origin_offset_local_scope(
        experiments,
        refl_lists,
        solution_lists,
        amax_list,
        mm_search_scope=mm_search_scope,
        wide_search_binning=wide_search_binning,
        plot_search_scope=plot_search_scope,
    )
    new_detector = new_experiments[0].detector
    old_panel, old_beam_centre = detector.get_ray_intersection(beam.get_s0())
    new_panel, new_beam_centre = new_detector.get_ray_intersection(beam.get_s0())

    old_beam_centre_px = detector[old_panel].millimeter_to_pixel(old_beam_centre)
    new_beam_centre_px = new_detector[new_panel].millimeter_to_pixel(new_beam_centre)

    logger.info(
        "Old beam centre: %.2f, %.2f mm" % old_beam_centre
        + " (%.1f, %.1f px)" % old_beam_centre_px
    )
    logger.info(
        "New beam centre: %.2f, %.2f mm" % new_beam_centre
        + " (%.1f, %.1f px)" % new_beam_centre_px
    )
    logger.info(
        "Shift: %.2f, %.2f mm"
        % (matrix.col(old_beam_centre) - matrix.col(new_beam_centre)).elems
        + " (%.1f, %.1f px)"
        % (matrix.col(old_beam_centre_px) - matrix.col(new_beam_centre_px)).elems
    )
    return new_experiments

@dials.util.show_mail_handle_errors()
def run(args=None):
    usage = "dials.search_beam_position [options] imported.expt strong.refl"

    parser = ArgumentParser(
        usage=usage,
        phil=phil_scope,
        read_experiments=True,
        read_reflections=True,
        check_format=False,
        epilog=help_message,
    )

    params, options = parser.parse_args(args, show_diff_phil=False)
    reflections, experiments = reflections_and_experiments_from_files(
        params.input.reflections, params.input.experiments
    )

    if len(experiments) == 0 or len(reflections) == 0:
        parser.print_help()
        exit(0)

    # Configure the logging
    log.config(logfile=params.output.log)

    # Log the diff phil
    diff_phil = parser.diff_phil.as_str()
    if diff_phil != "":
        logger.info("The following parameters have been modified:\n")
        logger.info(diff_phil)

    if params.seed is not None:
        flex.set_random_seed(params.seed)
        random.seed(params.seed)

    if params.nproc is libtbx.Auto:
        params.nproc = available_cores()

    imagesets = experiments.imagesets()
    # Split all the refln tables by ID, corresponding to the respective imagesets
    reflections = [
        refl_unique_id
        for refl in reflections
        for refl_unique_id in refl.split_by_experiment_id()
    ]

    assert len(imagesets) > 0
    assert len(reflections) == len(imagesets)

    if params.image_range is not None and len(params.image_range) > 0:
        reflections = [
            slice_reflections(refl, params.image_range) for refl in reflections
        ]

    for i in range(params.n_macro_cycles):
        if params.n_macro_cycles > 1:
            logger.info("Starting macro cycle %i", i + 1)
        experiments = discover_better_experimental_model(
            experiments,
            reflections,
            params,
            nproc=params.nproc,
            d_min=params.d_min,
            mm_search_scope=params.mm_search_scope,
            wide_search_binning=params.wide_search_binning,
            plot_search_scope=params.plot_search_scope,
        )
        logger.info("")

    logger.info("Saving optimised experiments to %s", params.output.experiments)
    experiments.as_file(params.output.experiments)

if __name__ == "__main__":
    run()


"""Module that computes indexes for shapely (2D) and polydata (3D) shapes"""

import math
from shapely.geometry import Point, MultiPoint, Polygon
from helpers.geometry import surface_normal
try:
    from helpers.mesh import to_pymesh, to_pyvista, intersect
    pymesh_exists = True
except:
    print("WARNING: pymesh not found! Exchange index calculation will be omitted...")
    pymesh_exists = False
import miniball
import numpy as np
import pyvista as pv
# import pymesh

def circularity(shape):
    """Returns circularity 2D for a given polygon"""

    return 4 * math.pi * shape.area / math.pow(shape.length, 2)

def hemisphericality(mesh):
    """Returns hemisphericality for a given volume.

    Hemisphericality can be perceived as a similar metric
    to circularity in 2D. But in 3D no building is expected
    to be spherical, but can be relatively hemispherical
    (i.e. starting with a big footpring and narrowing towards
    the roof).
    """

    return 3 * math.sqrt(2) * math.sqrt(math.pi) * mesh.volume / math.pow(mesh.area, 3/2)

def convexity_2d(shape):
    """Returns the convexity in 2D"""

    return shape.area / shape.convex_hull.area

def fractality_2d(shape):
    """Returns the fractality in 2D for a given polygon"""

    return 1 - math.log(shape.area) / (2 * math.log(shape.length))

def fractality_3d(mesh):
    """Returns the fractality in 3D for a given volume"""

    # TODO: Check this formula
    return 1 - math.log(mesh.volume) / (3/2 * math.log(mesh.area))

def squareness(shape):
    """Returns the squareness in 2D for a given polygon"""

    return 4 * math.sqrt(shape.area) / shape.length

def cubeness(mesh):
    """Returns the cubeness in 3D for a given volume"""

    return 6 * math.pow(mesh.volume, 2/3) / mesh.area

def get_box_dimensions(box):
    """Given a box (as shapely polygon) returns its dimensions as a tuple
    (small, large)
    """

    obb_pts = list(box.boundary.coords)

    S = Point(obb_pts[1]).distance(Point(obb_pts[0]))
    L = Point(obb_pts[2]).distance(Point(obb_pts[1]))

    if S > L:
        L, S = S, L

    return S, L

def elongation(S, L):
    """Returns the elongation for the given dimensions"""

    if S > L:
        return 1 - L / S

    return 1 - S / L

def equivalent_rectangular_index(shape, obb_2d=None):
    """Returns the equivalent rectangular index"""

    if obb_2d is None:
        obb_2d = shape.minimum_rotated_rectangle

    k = math.sqrt(shape.area / obb_2d.area)

    return k * obb_2d.length / shape.length

def equivalent_prism_index(mesh, obb):
    """Returns the equivalent rectangular prism index"""

    k = math.pow(mesh.volume / obb.volume, 2/3)

    # evrp: equal volume rectangular prism
    A_evrp = k * obb.area

    return A_evrp / mesh.area

def create_grid_2d(shape, density):
    """Return the grid for a given polygon"""

    x_min, y_min, x_max, y_max = shape.bounds
    x = np.arange(x_min, x_max, density)
    y = np.arange(y_min, y_max, density)
    x, y = np.meshgrid(x, y)

    x = np.hstack(x)
    y = np.hstack(y)

    return [(x[i], y[i]) for i in range(len(x))]

def create_grid_3d(mesh, density, check_surface=False):
    """Returns the grid for a given mesh"""
    voxel = pv.voxelize(mesh, density=density, check_surface=check_surface)

    return voxel.cell_centers().points

def to_3d(points, normal, origin):
    """Translate local 2D coordinates to 3D"""

    x_axis, y_axis = axes_of_normal(normal)

    return (np.repeat([origin], len(points), axis=0)
        + np.matmul(points, [x_axis, y_axis]))

def axes_of_normal(normal):
    """Returns an x-axis and y-axis on a plane of the given normal"""
    if normal[2] > 0.001 or normal[2] < -0.001:
        x_axis = [1, 0, -normal[0]/normal[2]];
    elif normal[1] > 0.001 or normal[1] < -0.001:
        x_axis = [1, -normal[0]/normal[1], 0];
    else:
        x_axis = [-normal[1] / normal[0], 1, 0];

    x_axis = x_axis / np.linalg.norm(x_axis)
    y_axis = np.cross(normal, x_axis)

    return x_axis, y_axis

def project_2d(points, normal):
    origin = points[0]

    x_axis, y_axis = axes_of_normal(normal)

    return [[np.dot(p - origin, x_axis), np.dot(p - origin, y_axis)] for p in points]

def create_surface_grid(mesh, density=1):
    """Create a 2-dimensional grid along the surface of a 3D mesh"""

    result = []

    sized = mesh.compute_cell_sizes()

    for i in range(mesh.n_cells):
        if not mesh.cell_type(i) in [5, 6, 7, 9, 10]:
            continue

        pts = mesh.cell_points(i)

        try:
            normal = surface_normal(pts)
        except:
            continue

        pts_2d = project_2d(pts, normal)
        poly_2d = Polygon(pts_2d)

        if not poly_2d.is_valid:
            continue

        grid = create_grid_2d(poly_2d, density)
        grid = MultiPoint(grid).intersection(poly_2d)

        if grid.is_empty:
            continue
        elif grid.geom_type == "Point":
            grid = np.array(grid.coords)
        else:
            grid = np.array([list(p.coords[0]) for p in grid.geoms])

        # TODO: Randomise the origin
        result.extend(list(to_3d(grid, normal, pts[0])))

    return result

def distance(x, y):
    """Returns the euclidean distance between two points"""

    return math.sqrt(sum([math.pow(x[c] - y[c], 2) for c in range(len(x))]))

def cohesion_2d(shape, grid=None, density=1):
    """Returns the cohesion index in 2D for a given polygon"""

    if grid is None:
        grid = create_grid_2d(shape, density)

    if isinstance(grid, list):
        grid = MultiPoint(grid).intersection(shape)

    d = 0
    for pi in grid.geoms:
        for pj in grid.geoms:
            if pi == pj:
                continue

            d += pi.distance(pj)

    n = len(grid.geoms)
    return 0.9054 * math.sqrt(shape.area / math.pi) / (1 / (n * (n - 1)) * d)

def cohesion_3d(mesh, grid=None, density=1, check_surface=False):
    """Returns the cohesion index in 3D for a given mesh"""

    if grid is None:
        grid = create_grid_3d(density=density, check_surface=check_surface)

    d = 0
    for pi in grid:
        for pj in grid:
            d += distance(pi, pj)

    n = len(grid)
    return 36 / 35 * math.pow(3 * mesh.volume / (4 * math.pi), 1/3) / (1 / (n * (n - 1)) * d)

def proximity_2d(shape, density=1, grid=None):
    """Returns the proximity index in 2D for a given polygon"""

    if grid is None:
        grid = create_grid_2d(shape, density)

    if isinstance(grid, list):
        grid = MultiPoint(grid).intersection(shape)

        if grid.is_empty:
            return -1

        if grid.geom_type == "Point":
            grid = MultiPoint([grid])

    centroid = shape.centroid

    return 2 / 3 * math.sqrt(shape.area / math.pi) / np.mean([centroid.distance(p) for p in grid.geoms])

def proximity_3d(mesh, grid=None, density=1, check_surface=False):
    """Returns the cohesion index in 3D for a given mesh"""

    if grid is None:
        grid = create_grid_3d(mesh, density=density, check_surface=check_surface)

    centroid = np.mean(grid, axis=0)

    # TODO: Verify the formula here
    r = math.pow(3 * mesh.volume / (4 * math.pi), 1/3)

    return (3 * r / 4) / np.mean([distance(centroid, p) for p in grid])

def equal_volume_radius(volume):
    """Returns the radius of the equal volume sphere"""

    return math.pow(3 * volume / (4 * math.pi), 1/3)

def equal_volume_sphere(mesh, position=(0, 0, 0)):
    """Returns the sphere that has the same volume as the given mesh"""

    r = math.pow(3 * mesh.volume / (4 * math.pi), 1/3)

    return pv.Sphere(radius=r, center=position)

def exchange_2d(shape):
    """Returns the exchange index in 2D for a given polygon"""

    r = math.sqrt(shape.area / math.pi)

    eac = shape.centroid.buffer(r)

    return shape.intersection(eac).area / shape.area

def exchange_3d(mesh, evs=None, density=0.25, engine="igl"):
    """Returns the exhange index in 3D for a given mesh

    mesh: The pyvista mesh to evaluate
    evs: The equal volume sphere (if provided speeds up the calculation)
    density: If no evs is provided, it is used to create a grid to compute the center of mass
    enginge: The engine for the boolean operations
    """

    if not pymesh_exists:
        return -1

    if evs is None:
        voxel = pv.voxelize(mesh, density=density, check_surface=False)
        grid = voxel.cell_centers().points

        if len(grid) == 0:
            centroid = mesh.center
        else:
            centroid = np.mean(grid, axis=0)

        evs = equal_volume_sphere(mesh, centroid)

    if mesh.n_open_edges > 0:
        return -1

    pm_mesh = to_pymesh(mesh)
    pm_evs = to_pymesh(evs)

    try:
        inter = intersect(pm_mesh, pm_evs, engine)
    except:
        return -1

    return inter.volume / mesh.volume

def spin_2d(shape, grid=None, density=1):
    if grid is None:
        grid = create_grid_2d(shape, density)

    if isinstance(grid, list):
        grid = MultiPoint(grid).intersection(shape)

        if grid.is_empty:
            return -1

        if grid.geom_type == "Point":
            grid = MultiPoint([grid])

    centroid = shape.centroid

    return 0.5 * (shape.area / math.pi) / np.mean([math.pow(centroid.distance(p), 2) for p in grid])

def spin_3d(mesh, grid=None, density=1, check_surface=False):
    """Returns the cohesion index in 3D for a given mesh"""

    if grid is None:
        voxel = pv.voxelize(mesh, density=density, check_surface=check_surface)
        grid = voxel.cell_centers().points

    centroid = np.mean(grid, axis=0)

    r = math.pow(3 * mesh.volume / (4 * math.pi), 1/3)
    # TODO: Calculate the actual formula here
    return 3 / 5 * math.pow(r, 2) / np.mean([math.pow(distance(centroid, p), 2) for p in grid])

def perimeter_index(shape):
    return 2 * math.sqrt(math.pi * shape.area) / shape.length

def circumference_index_3d(mesh):
    return 4 * math.pi * math.pow(3 * mesh.volume / (4 * math.pi), 2 / 3) / mesh.area

def depth_2d(shape, grid=None, density=1):
    if grid is None:
        grid = create_grid_2d(shape, density)

    if isinstance(grid, list):
        grid = MultiPoint(grid).intersection(shape)

        if grid.is_empty:
            return -1

        if grid.geom_type == "Point":
            grid = MultiPoint([grid])

    return 3 * np.mean([p.distance(shape.boundary) for p in grid]) / math.sqrt(shape.area / math.pi)

def depth_3d(mesh, grid=None, density=1, check_surface=False):
    """Returns the depth index in 3D for a given mesh"""

    if grid is None:
        voxel = pv.voxelize(mesh, density=density, check_surface=check_surface)
        grid = voxel.cell_centers()

    dist = grid.compute_implicit_distance(mesh)

    r = math.pow(3 * mesh.volume / (4 * math.pi), 1/3)
    return 4 * np.mean(np.absolute(dist["implicit_distance"])) / r

from polylabel import polylabel

def largest_inscribed_circle(shape):
    """Returns the largest inscribed circle of a polygon in 2D"""

    centre, r = polylabel([list([list(c)[:2] for c in shape.boundary.coords])], with_distance=True)  # ([0.5, 0.5], 0.5)

    lic = Point(centre).buffer(r)

    return lic

def largest_inscribed_sphere(mesh, grid=None, density=1, check_surface=False):
    """Returns the largest inscribed sphere of a mesh in 3D"""

    if grid is None:
        voxel = pv.voxelize(mesh, density=density, check_surface=check_surface)
        grid = voxel.cell_centers()

    if not isinstance(grid, pv.PolyData):
        grid = pv.PolyData(grid)

    dist = grid.compute_implicit_distance(mesh)

    if grid.n_points == 0:
        return pv.Sphere(center=(0, 0, 0), radius=(mesh.bounds[2] - mesh.bounds[0]) / 2)

    # The largest inscribed circle's radius is the largest (internal) distance,
    # hence the lowest value (as internal distance is negative)
    lis_radius = np.min(dist["implicit_distance"])
    lis_center = dist.points[np.where(dist["implicit_distance"] == lis_radius)][0]

    return pv.Sphere(center=lis_center, radius=abs(lis_radius))

def girth_2d(shape):
    """Return the girth index in 2D for a given polygon"""

    lic = largest_inscribed_circle(shape)

    if lic.is_empty:
        return -1

    # Compute the radius as half the bounding box width
    r = (lic.bounds[2] - lic.bounds[0]) / 2

    return r / math.sqrt(shape.area / math.pi)

def girth_3d(mesh, grid=None, density=1, check_surface=False):
    """Return the girth index in 3D for a given mesh"""

    lis = largest_inscribed_sphere(mesh,
                                   grid=grid,
                                   density=density,
                                   check_surface=check_surface)

    r = (lis.bounds[1] - lis.bounds[0]) / 2
    r_evs = math.pow(3 * mesh.volume / (4 * math.pi), 1/3)

    return r / r_evs

def range_2d(shape):
    """Returns the range index in 2D for a given polygon"""

    from helpers.smallestenclosingcircle import make_circle

    x, y, r = make_circle([c[:2] for c in  shape.boundary.coords])

    return math.sqrt(shape.area / math.pi) / r

def get_bounding_ball_radius(points):
    """Returns the bounding ball for a set of points"""

    try:
        _, r2 = miniball.get_bounding_ball(points)
    except:
        return -1

    return r2

def range_3d(mesh):
    """Returns the range index in 3D for a given mesh"""

    r2 = -1

    pts = mesh.clean().points
    t = np.mean(pts, axis=0)
    pts = pts - t

    count = 0

    while r2 < 0:
        r2 = get_bounding_ball_radius(pts)
        count += 1

        if count > 10:
            return -1

    r_scc = math.sqrt(r2)

    return math.pow(3 * mesh.volume / (4 * math.pi), 1/3) / r_scc

def dispersion_2d(shape, density=0.2):
    """Returns the dispersion index in 2d for a given polygon"""

    c = shape.centroid
    b = shape.boundary

    r = math.sqrt(shape.area / math.pi)

    r_dev = 0
    r_ibp = 0
    for l in np.arange(0, b.length, density):
        p = b.interpolate(l)

        r_dev += abs(p.distance(c) - r)
        r_ibp += p.distance(c)

    return 1 - (r_dev / r_ibp)

def dispersion_3d(mesh, grid, density=0.5):
    """Returns the dispersion index in 3d for a given mesh"""

    centroid = np.mean(grid, axis=0)

    s_grid = create_surface_grid(mesh, density)

    r = equal_volume_radius(mesh.volume)

    r_dev = 0
    r_ibp = 0
    for p in s_grid:
        d_i = distance(centroid, p)
        r_dev += abs(d_i - r)
        r_ibp += d_i

    return 1 - (r_dev / r_ibp)

def roughness_index_2d(shape, density=0.2):
    c = shape.centroid
    b = shape.boundary

    if b.length < 1:
        return -1

    r_ibp = 0
    for l in np.arange(0, b.length, density):
        p = b.interpolate(l)

        r_ibp += p.distance(c)

    m_r = r_ibp / math.floor(b.length / density)

    return 42.62 * math.pow(m_r, 2) / (shape.area + math.pow(shape.length, 2))

def roughness_index_3d(mesh, grid, density=0.5):
    centroid = np.mean(grid, axis=0)

    s_grid = create_surface_grid(mesh, density)

    if len(s_grid) == 0:
        return -1

    r_ibp = 0
    for p in s_grid:
        d_i = distance(centroid, p)
        r_ibp += d_i

    m_r = r_ibp / len(s_grid)

    return 48.735 * math.pow(m_r, 3) / (mesh.volume + math.pow(mesh.area, 3/2))


# MATH 3670 Article Clustering
# Authors: Akash Patel, Nabeel Mahmood, Taylor Fields

import statistics as stat
from nltk.corpus import reuters
import os
import numpy as np
np.set_printoptions(threshold=np.inf)

from scipy.cluster.vq import kmeans, vq

### Get complete word list

os.listdir('/home/apatel435/nltk_data/corpora/reuters/training/')

wordlist = [];
num_articles = 0
artnum = 1000
j = 0
for filename in os.listdir('/home/apatel435/nltk_data/corpora/reuters/training/'):
    if j < artnum:
        words = reuters.words('training/' + str(filename))
        wordlist.append(words)
        num_articles += 1
    j += 1

# Flatten
wordlist = [item for sublist in wordlist for item in sublist]
# Lowercase
wordlist = [x.lower() for x in wordlist]
# Filter repeats
wordlist = list(set(wordlist))
# Sort
wordlist.sort()

### Filter common words and special characters
# take out numbers
wordlist = [ x for x in wordlist if x.isalpha() ]

# Filter common words
common = ["a", "about", "above", "above", "across", "after", "afterwards",
"again", "against", "all", "almost", "alone", "along", "already",
"also","although","always","am","among", "amongst", "amongst", "amount",  "an",
"and", "another", "any","anyhow","anyone","anything","anyway", "anywhere",
"are", "around", "as",  "at", "back","be","became",
"because","become","becomes", "becoming", "been", "before", "beforehand",
"behind", "being", "below", "beside", "besides", "between", "beyond", "bill",
"both", "bottom","but", "by", "call", "can", "cannot", "cant", "co", "con",
"could", "couldnt", "do", "done", "down",
"due", "during", "each", "e.g.", "eight", "either", "eleven","else",
"elsewhere", "empty", "enough", "etc", "even", "ever", "every", "everyone",
"everything", "everywhere", "except", "few", "fifteen", "fifty", "fill", "find",
"fire", "first", "five", "for", "former", "formerly", "forty", "found", "four",
"from", "front", "full", "further", "get", "give", "go", "had", "has", "hasnt",
"have", "he", "hence", "her", "here", "hereafter", "hereby", "herein",
"hereupon", "hers", "herself", "him", "himself", "his", "how", "however",
"hundred", "ie", "if", "in", "inc", "indeed", "interest", "into", "is", "it",
"its", "itself", "keep", "last", "latter", "latterly", "least", "less", "ltd",
"made", "many", "may", "me", "meanwhile", "might", "mill", "mine", "more",
"moreover", "most", "mostly", "move", "much", "must", "my", "myself", "name",
"namely", "neither", "never", "nevertheless", "next", "nine", "no", "nobody",
"none", "noone", "nor", "not", "nothing", "now", "nowhere", "of", "off",
"often", "on", "once", "one", "only", "onto", "or", "other", "others",
"otherwise", "our", "ours", "ourselves", "out", "over", "own","part", "per",
"perhaps", "please", "put", "rather", "re", "same", "see", "seem", "seemed",
"seeming", "seems", "serious", "several", "she", "should", "show", "side",
"since", "sincere", "six", "sixty", "so", "some", "somehow", "someone",
"something", "sometime", "sometimes", "somewhere", "still", "such",
"take", "ten", "than", "that", "the", "their", "them", "themselves", "then",
"thence", "there", "thereafter", "thereby", "therefore", "therein", "thereupon",
"these", "they", "thick", "thin", "third", "this", "those", "though", "three",
"through", "throughout", "thru", "thus", "to", "together", "too", "top",
"toward", "towards", "twelve", "twenty", "two", "un", "under", "until", "up",
"upon", "us", "very", "via", "was", "we", "well", "were", "what", "whatever",
"when", "whence", "whenever", "where", "whereafter", "whereas", "whereby",
"wherein", "whereupon", "wherever", "whether", "which", "while", "whither",
"who", "whoever", "whole", "whom", "whose", "why", "will", "with", "within",
"without", "would", "yet", "you", "your", "yours", "yourself", "yourselves",
"the"]

wordlist = [x for x in wordlist if x not in common]

num_wordlist = len(wordlist)

### Continue with matrix building
# Add a 1 if the article contains the word in wordlist
arr = np.zeros((num_wordlist,num_articles))

i = 0

for filename in os.listdir('/home/apatel435/nltk_data/corpora/reuters/training/'):

    if i < artnum:
        words = reuters.words('training/' + str(filename))
        # Lowercase
        words = [x.lower() for x in words]
        # Filter repeats
        words = list(set(words))

        # Add the one if word is in the wordlist
        for x in range(0, num_wordlist):
            if wordlist[x] in words:
                arr[x,i] = 1

    i += 1

stackarr = arr[:,0]

for x in range(1,artnum):
    stackarr = np.vstack((stackarr,arr[:,x]))

# Covariance matrix
cov_arr = np.cov(stackarr)

### Perform k - means algo clustering
# Cluster size chosen to be 5 due to relatively large dataset (1000x1000)
clusters = 5

centroids,_= kmeans(cov_arr,clusters)
idx,_ = vq(cov_arr,centroids)

# Sort matrix by clusters
# ie vectors that are in cluster one are adjacent to one another and so on
# for each cluster

index = idx.argsort();
cov_arrsort = cov_arr[index];

# Transpose matrix to sort the rows
# Matrix is symmetric so transposing it produces the same result
cov_arrtrans = cov_arrsort.transpose();
cluster_cov_arr = cov_arrtrans[index];
print(cluster_cov_arr)


from sklearn.gaussian_process import GaussianProcessRegressor, kernels
from sklearn.datasets import make_regression
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as ss

'''
def log_marginal_likelihood(gp, theta):
    return gp.log_marginal_likelihood(theta)

def log_prior_parameters(theta):
    return np.sum(np.log([ss.norm(0, 1).pdf(theta_k) for theta_k in theta]))

def log_joint_unnorm(gp, theta):
    return log_marginal_likelihood(gp, theta) + log_prior_parameters(theta)
'''

class Gaussian_Process():

    def __init__(self, kernel, mode):
        self.kernel = kernel
        if mode == 'OPT':
            self.gp = GaussianProcessRegressor(kernel=kernel,
                                               n_restarts_optimizer=10,
                                               normalize_y=True)
        elif mode == 'MAP':
            self.gp = GaussianProcessRegressor(kernel=kernel,
                                               optimizer = None,
                                               n_restarts_optimizer=10,
                                               normalize_y=True)
        else:
            raise Exception('Wrong GP initial mode!!!')
    '''
    def optimise__with_splice_sampling(self, initial_theta, num_iters, sigma, burn_in,  X, y):
        self.fit(X, y)
        slice_sampler = Slice_sampler(num_iters = num_iters,
                                      sigma = sigma,
                                      burn_in = burn_in,
                                      gp = self)
        samples = slice_sampler.sample(init = initial_theta)
        theta_opt = [np.mean(samples[0]),np.mean(samples[1])]

        self.gp.set_params(**{"kernel__k1__length_scale": np.mean(samples[0]),
                            "kernel__k2__noise_level": np.power(np.mean(samples[0]),2)})
    '''

    def set_params(self, theta_set):
        self.gp.set_params(**{"kernel__k1__noise_level": np.abs(theta_set[0]),
                        "kernel__k2__k1__constant_value": np.abs(theta_set[1]),
                        "kernel__k2__k2__length_scale": theta_set[2:]})

    def log_marginal_likelihood(self,theta):
        return self.gp.log_marginal_likelihood(theta)

    def log_prior_parameters(self, theta):
        return np.sum(np.log([ss.norm(0, 1).pdf(theta_k) for theta_k in theta]))

    def log_joint_unnorm(self, theta):
        return self.log_marginal_likelihood(theta) + self.log_prior_parameters(theta)

    def fit(self, X, y):
        self.gp.fit(X, y)

    def predict(self, X):
        mu, sigma = self.gp.predict(X, return_std =True)
        return mu, sigma

class Slice_sampler():

    def __init__(self, num_iters, sigma, burn_in):
        self.num_iters = num_iters # specify number of samples we want to draw
        self.sigma = sigma
        self.burn_in = burn_in

    def sample(self, init, gp, step_out=True):

        D = len(init)
        samples = np.zeros((D, self.num_iters))

        xx = init.copy()

        for i in range(self.num_iters + self.burn_in):
            perm = list(range(D))
            np.random.shuffle(perm)
            last_llh = gp.log_joint_unnorm(xx)

            for d in perm:
                llh0 = last_llh + np.log(np.random.rand())
                rr = np.random.rand(1)
                x_l = xx.copy()
                x_l[d] = x_l[d] - rr * self.sigma[d]
                x_r = xx.copy()
                x_r[d] = x_r[d] + (1 - rr) * self.sigma[d]

                if step_out:
                    llh_l = gp.log_joint_unnorm(x_l)
                    while llh_l > llh0:
                        x_l[d] = x_l[d] - self.sigma[d]
                        llh_l = gp.log_joint_unnorm(x_l)
                    llh_r = gp.log_joint_unnorm(x_r)
                    while llh_r > llh0:
                        x_r[d] = x_r[d] + self.sigma[d]
                        llh_r = gp.log_joint_unnorm(x_r)

                x_cur = xx.copy()
                while True:
                    xd = np.array(np.random.rand() * (x_r[d] - x_l[d]) + x_l[d])
                    x_cur[d] = xd.copy()
                    last_llh = gp.log_joint_unnorm(x_cur)
                    if last_llh > llh0:
                        xx[d] = xd.copy()
                        break
                    elif xd > xx[d]:
                        x_r[d] = xd
                    elif xd < xx[d]:
                        x_l[d] = xd
                    else:
                        raise RuntimeError('Slice sampler shrank too far.')

            if i == 0:
                pass
                #print ("burn-in")
            elif i > self.burn_in and i % 100 == 0:
                pass
                #print ('iteration', i - self.burn_in)

            if i >= self.burn_in:
                samples[:, i-self.burn_in] = xx.copy().ravel() # index starting from 0

        #plt.hist(samples[0])

        return samples


from flask import Flask
import pika

app = Flask(__name__)

@app.route("/sendMsg/<string:msg>")
def send(msg):
    parameters = pika.URLParameters('amqp://imaya:imaya@rabbitmq:5672/%2F')
    connection = pika.BlockingConnection(parameters)
    channel = connection.channel()
    channel.queue_declare(queue="request", durable=True)
    channel.basic_publish(exchange='', routing_key='request', body=msg, properties=pika.BasicProperties(delivery_mode = 2))
    connection.close()

    html = "
Hello !
" \
       "Message Sent
" \
       "Again"
    return html.format()

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)


from coinapi_service import coinapi_get_exchange_rates_extended
import json
from pathlib import Path
from datetime import datetime, date

def get_json_rates(rates_data):
    return json.dumps([{ "date":d['time_period_start'][0:10], "Value":int(d['rate_close']) } for d in rates_data])

def save_data_to_json_file(json_data, filename):
    with open(filename, 'w') as file:
        file.write(json_data)
        file.close()

def load_json_data_from_file(data_filename_to_load):
    with open(data_filename_to_load, 'r') as file:
        try:
            return json.load(file)
        except json.JSONDecodeError:
            print("Le fichier est vide ou contient des données JSON non valides.")
            return []

def get_and_manage_rates_data(assets,date_start_str,date_end_str):
    pass

assets = "BTC/EUR"
data_filename = assets.replace("/","_")+".json"
date_start_str = date(2023, 4, 10)
date_end_str = date.today()
rates= []
if Path(data_filename).is_file():
    #Json File exist
    rates= load_json_data_from_file(data_filename)
    if rates:
        save_data_date_start_str = datetime.strptime(rates[0]["date"], "%Y-%m-%d").date()
        save_data_date_end_str = datetime.strptime(rates[-1]["date"], "%Y-%m-%d").date()
        print(save_data_date_end_str, save_data_date_start_str)
        print("save_data_date_start_str<date_start_str: ",save_data_date_start_str<date_start_str)
        if save_data_date_start_str<date_start_str:
            rates = coinapi_get_exchange_rates_extended(assets,save_data_date_start_str,date_start_str) + rates
        if save_data_date_end_str > date_end_str :
            rates+=coinapi_get_exchange_rates_extended(assets,date_end_str,save_data_date_end_str)
        print("save_data_date_end_str > date_end_str :",save_data_date_end_str > date_end_str)
        rates = get_json_rates(rates)
        save_data_to_json_file(rates, data_filename)
else:
    #Json File don't exist
    rates = coinapi_get_exchange_rates_extended(assets, date_start_str,date_end_str)
    print()
    if rates:
        json_data= get_json_rates(rates)
        print(assets, "nombres de cours : ", len(rates))
        save_data_to_json_file(json_data, data_filename)


#!/usr/bin/python3

# references:
# - http://ccg.doc.gold.ac.uk/ccg_old/teaching/ludic_computing/ludic16.pdf
# - https://int8.io/monte-carlo-tree-search-beginners-guide/

# To be verified: "eval" recalculation in backpropagation

balance_constant = 50 # exploration vs exploitation balance
move_timeframe = 20  # number of seconds for evaluation
modeleval = None # model will be loaded in protover call

import Const
from queue import *
import chess.uci
import sys
from math import (sqrt, log)
from FeaturesExtraction import extract_features
import numpy as np
from copy import deepcopy

class MctsNode(object):

    def __init__(self, board, parent=None):
        #h#global evaltable, hitsevaltable
        self.board = board
        self.parent = parent
        self.children = []
        self.movetochild = []
        self.visitcount = 1
        #h#try:
        #h#    self.anneval, self.movestoexpand = evaltable[" ".join(self.board.fen().split()[0:4])]
        #h#    print("# evaltable ", self.board.fen(), " = ", self.anneval)
        #h#    hitsevaltable += 1
        #h#except KeyError:
        if True: #h#
            self.movestoexpand = Queue()
            if self.board.is_checkmate():
                self.anneval = -999999
            else:
                if board.turn == chess.WHITE:
                    b = self.board
                else:
                    b = self.board.mirror()
                y = modeleval.predict(np.array([extract_features(b)]), batch_size=1, verbose=0)
                self.anneval = y[0][0] #* (1 if self.board.turn==chess.WHITE else -1)
                print("# evaluated ", self.board.fen(), " = ", self.anneval)
                l = []
                self.terminalnode = True
                for m in self.board.generate_legal_moves():
                    a = str(m).upper()
                    l.append((m, y[1][0][ord(a[0])-65, ord(a[1])-49, ord(a[2])-65, ord(a[3])-49]))
                    self.terminalnode = False
                print("# sorted moves ", end="")
                for m in sorted(l, key=lambda e: e[1], reverse=True):
                    self.movestoexpand.put(m[0]) # moves ordered via policy values
                    print(str(m[0]), " ", end="")
                print("")
        #h#        evaltable[" ".join(self.board.fen().split()[0:4])] = (self.anneval, deepcopy(self.movestoexpand))
        self.value = self.anneval
        self.isroot = False # root will be moved during game progress

    def boardcopy(self):
        return self.board.copy()

    def setroot(self):
        self.isroot = True

def traverse(parent, mainline=""):

    parent.visitcount += 1
    print("# visit ", parent.visitcount, " of ", mainline if mainline!="" else "root")

    if parent.terminalnode:
        print("# terminal node")
        return parent

    if not parent.movestoexpand.empty():
        # pick_univisted_children and create node for it
        move = parent.movestoexpand.get()
        parent.movetochild.append(move)
        board = parent.boardcopy()
        board.push(move)
        node = MctsNode(board, parent=parent)
        parent.children.append(node)
        print("# picked unvisited ", move)
        return node

    # traverse child with best upper confidence bound
    max = -float('inf')
    bestchild = None
    for i in range(len(parent.children)):
        exploit = -parent.children[i].value
        explore = balance_constant * sqrt( log( parent.visitcount ) / parent.children[i].visitcount )
        e = exploit + explore
        print("# deciding on move ", parent.movetochild[i], \
              " exploit ", exploit, " explore ", explore)
        if e > max:
            bestchild = i
            max = e
    print("# picked move ", parent.movetochild[bestchild], \
          " visited already ", parent.children[bestchild].visitcount, " times")

    return traverse(parent.children[bestchild], mainline+str(parent.movetochild[bestchild])+",")

def backpropagate(node, eval):

    # recalculate max (to be optimized)
    if len(node.children)>1:
        print("# backpropagated value ", eval)
        mx = eval
        for n in node.children:
            mx = max( mx, -n.value) # minus because is adversary
        eval = mx

    if node.movestoexpand.empty() or not node.value:
        node.value = eval
        print("# backpropagated assignment: old node value ", node.value, " new node value ", eval)
        if not node.isroot:
            print("# continue backpropagation")
            backpropagate(node.parent, -eval)
    else:
        print("# backpropagation stops here")

    return

#=========================
root = MctsNode(chess.Board())
root.setroot() # stop backpropagation here
while true:
	leaf = traverse(root) # to pick unvisited/best node
	eval = leaf.value # simulation
	backpropagate(leaf.parent, -eval)


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Wed May 23 20:07:30 2018

@author: shuhongyan
"""
#因为是按顺序排列的，所以从i=0开始，nums[i]==nums[i+1]，如果不等于，nums[i]就是那个只出现过一次的数
#唯一的特例是那个只出现过一次的数是数列最后一位，所以while loop函数只要判断到数列的倒数第三位
#如果还没找到，就必然是最后一个是单着的，直接return就行

class Solution:
    def singleNonDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        i=0
        if nums ==[]:
            return None #首先先判断是否为空集
        while i<len(nums)-2: #然后用while loop 判断到倒数第三个数
            if nums[i]!=nums[i+1]:
                return nums[i]
            i +=2
        return nums[i]#前面的都找不到就return最后一个元素

#用递归二分法
class Solution:
    def singleNonDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if nums == []:
            return None
        if len(nums)==1:
            return nums[0]
        mid = int(len(nums)/2)
        if mid%2==0:
            if nums[mid] == nums[mid-1]:
                return self.singleNonDuplicate(nums[:mid-1])
            if nums[mid] == nums[mid+1]:
                return self.singleNonDuplicate(nums[mid+2:])
        if mid%2!=0:

            if nums[mid] == nums[mid+1]:
                return self.singleNonDuplicate(nums[:mid])
            if nums[mid] == nums[mid-1]:
                return self.singleNonDuplicate(nums[mid+1:])
        else:
            return nums[mid]

#迭代二分法
class Solution:
    def singleNonDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if nums == []:
            return None
        while len(nums) > 1:
            mid = int(len(nums)/2)
            if nums[mid] == nums[mid-1] or nums[mid] == nums[mid+1]:
                if mid%2==0:
                    if nums[mid] == nums[mid-1]:
                        nums = nums[:mid-1]
                    else:
                        nums = nums[mid+2:]
                if mid%2!=0:
                    if nums[mid] == nums[mid+1]:
                        nums = nums[:mid]
                    else:
                        nums = nums[mid+1:]
            else:
                return nums[mid]
        return nums[0]

540. Single Element in a Sorted Array.py


#!/usr/bin/env python
import numpy as np
result_mu = [0]*10
result_pi = [0]*10
result_cov = [0]*10
def multinorm(x, mu, cov):
	COE = -float(len(mu)) * np.log(2* np.pi) / 2 - np.log(np.linalg.det(cov)) / 2
	EXP = (-0.5) * (np.dot(np.dot((x-mu),np.linalg.inv(cov)),(x-mu)))
	return COE + EXP

if __name__ == '__main__':
	with open("./paras_result.txt","r") as paras:
		for para in paras:
			para = para.strip()
			class_no , para = para.split('\t')
			if "__" not in class_no:
				class_no = int(class_no)
				pi, cov , mu = para.split(":")
				mu =mu.split(",")
				cov = cov.split(",")
				result_pi[class_no] = float(pi)
				result_mu[class_no] = [float(x) for x in mu]
				result_cov[class_no] = np.asarray([float(x) for x in cov]).reshape(25,25)
	result_mu = np.asarray(result_mu)
	gamma = np.zeros(10)
	class_cnt=[[] for x in range(10)]
	with open("../data/image_test.txt","r") as images:
		idx = 0
		for image in images:
			image = image.strip()
			coords = image.split(",")
			coords = [float(x) for x in coords]
			coords = np.asarray(coords)
			for i in range(0,10):
				gamma[i] = np.log(result_pi[i]) + multinorm(coords,result_mu[i],result_cov[i])
			class_no = gamma.argmax()
			class_cnt[class_no].append(idx)
			idx += 1

	true_label = []
	with open("../data/label_test.txt", "r") as f:
		for label in f:
			label = label.strip()
			label = int(label)
			true_label.append(label)

	major_labels = [0]*10
	correct_images = [0]*10
	Num_images = [0]*10

	for i in range(10):
		Num_images[i] = len(class_cnt[i])
		count = np.zeros(10)
		for j in range(len(class_cnt[i])):
			count[true_label[class_cnt[i][j]]]+=1
		print(str(i)+": "+str(count))
		major_labels[i] = count.argmax()
		correct_images[i] = count[major_labels[i]]
	Accuracy = [float(correct_images[i])/Num_images[i] for i in range(10)]
	for i in range(0,10):
		print(str(i)+'\t'+str(Num_images[i])+'\t'+str(major_labels[i])+'\t'+str(correct_images[i])+'\t'+"{0:0.2f}".format(Accuracy[i]*100))
	print("Total"+'\t'+str(sum(Num_images))+'\t'+'\t'+str(sum(correct_images))+'\t'+"{0:0.2f}".format(float(sum(correct_images))*100/sum(Num_images)))
	print("\n")