import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from matplotlib.lines import Line2D
import math

NUM_FREQS = 9
NUM_CORES = 15
NUM_SAMPLES = 5 #length of thread_idx_set

freq_series = np.zeros([NUM_FREQS, NUM_CORES],dtype=[('F',int),('C',int),('perf', float),('E', float),('S', float)])
freq_series_sample = np.zeros([NUM_FREQS, NUM_SAMPLES],dtype=[('F',int),('C',int),('perf', float),('E', float),('S', float)])

filename="de_phi_profile_upto60_v2.csv"
fx_ex = np.genfromtxt(filename, delimiter=' ', names=True) #Read in csv data from the file
freq_set=[619, 667, 714, 762, 857, 952, 1048, 1143, 1238]
thread_idx_set = [0,1,3,7,14] #to pick out profile entries at 4,8,16,32,60
markers = Line2D.filled_markers

# Create a figure, specific size & dots per inch
fig1 = plt.figure(figsize=(8, 8), dpi=128)
scatter_pp = fig1.add_subplot(111)
scatter_pp.set_xlabel('Average Power per Frame (W)', fontsize=16)
scatter_pp.set_ylabel('Average Performance (FPS)', fontsize=16)
#scatter_pp.set_title('Performance & Power Points', fontsize=16)
scatter_pp.tick_params(axis='y', labelsize=16)
scatter_pp.tick_params(axis='x', labelsize=16)
scatter_pp.grid(True)

max_threads = 60
#Data table creation
j=0
for freq_idx in range (0,NUM_FREQS,1):
    i=0
    for thread_idx in range (0,NUM_CORES,1):
        
        p1 = fx_ex[j]['perf']
        e1 = fx_ex[j]['energy']
        j+=1
        p2 = fx_ex[j]['perf']
        e2 = fx_ex[j]['energy']
        j+=1
        p3 = fx_ex[j]['perf']
        e3 = fx_ex[j]['energy']
        j+=1
        p4 = fx_ex[j]['perf']
        e4 = fx_ex[j]['energy']
        j+=1
        
        perf_av = (p1 + p2 + p3 + p4) / 4
        e_av    = (e1 + e2 + e3 + e4) / 4
        
        freq_series[NUM_FREQS-1-freq_idx][NUM_CORES-1-i] = (freq_set[NUM_FREQS-1-freq_idx], 
                                                max_threads-(4*thread_idx), perf_av, e_av, 0.0)
        i+=1

for freq_idx in range (0,NUM_FREQS):
    freq_series_sample[freq_idx] = freq_series[freq_idx][thread_idx_set]
        
#Plot Graph  
#scatter_pp
for f in range(0,NUM_FREQS):
    scatter_pp.plot(freq_series[f]['E']*freq_series[f]['perf'],freq_series[f]['perf'], label=str(freq_set[f]), 
                   marker=markers[f], linestyle='None',markersize=10, color=plt.cm.jet(f/10.))

#Drawing optimisation arrow on figure
#Start point
f1=8
k1=1
px1=freq_series[f1]['E'][k1]*freq_series[f1]['perf'][k1]
py1=freq_series[f1]['perf'][k1]
p1text = "    P1:\n" + str(freq_series[f1]['C'][k1]) + " cores \n" + str(freq_series[f1]['F'][k1]) + " MHz"
scatter_pp.plot(px1,py1, marker="o", markersize=28, mec='k', mfc="None", mew=3)
scatter_pp.annotate(p1text, xy=(px1,py1), xytext=(25,-20), textcoords='offset points', fontsize=16)
    
#End point
f2=0
k2=5
px2=freq_series[f2]['E'][k2]*freq_series[f2]['perf'][k2]
py2=freq_series[f2]['perf'][k2]
p2text = "    P2:\n" + str(freq_series[f2]['C'][k2]) + " cores \n" + str(freq_series[f2]['F'][k2]) + " MHz"
scatter_pp.plot(px2,py2, marker="o", markersize=28, mec='k', mfc="None", mew=3)
scatter_pp.annotate(p2text, xy=(px2,py2), xytext=(-60,20), textcoords='offset points', fontsize=16)
#Arrow
scatter_pp.annotate("",xy=(px2,py2), xytext=(px1, py1), 
    textcoords='data', arrowprops=dict(color='r', width=12, headlength=20, headwidth=25))
#Red text
scatter_pp.text(12, 0.95, "Same Performance\nLower Power", color='r', ha="left", va="top", size=22, 
               bbox=dict(fc="None",ec="k",lw=1))

legend = scatter_pp.legend(loc= 'lower right', title='Freq (MHz)', numpoints=1, fontsize=16)
plt.setp(legend.get_title(),fontsize=16)
fig1.savefig("pp_power_opt.pdf",bbox_inches='tight')

#Point power and perf
f = f1
k = k1
pow1 = freq_series[f1]['E'][k1]*freq_series[f1]['perf'][k1]
perf1 = freq_series[f1]['perf'][k1]
print("Point 1:")
print("power = " + str(pow1))
print("perf = " + str(perf1))
f = f2
k = k2
pow2 = freq_series[f]['E'][k]*freq_series[f]['perf'][k]
perf2 = freq_series[f]['perf'][k]
print("Point 2:")
print("power = " + str(pow2))
print("power diff = " + str((pow1-pow2)/pow1*100) + "%")
print("perf = " + str(perf2) + "\n")

################################################
# Create a figure, specific size & dots per inch
fig1 = plt.figure(figsize=(8, 8), dpi=128)
scatter_pp = fig1.add_subplot(111)
scatter_pp.set_xlabel('Average Power per Frame (W)', fontsize=16)
scatter_pp.set_ylabel('Average Performance (FPS)', fontsize=16)
#scatter_pp.set_title('Performance & Power Points', fontsize=16)
scatter_pp.tick_params(axis='y', labelsize=16)
scatter_pp.tick_params(axis='x', labelsize=16)
scatter_pp.grid(True)

#Plot Graph  
#scatter_pp
for f in range(0,NUM_FREQS):
    scatter_pp.plot(freq_series[f]['E']*freq_series[f]['perf'],freq_series[f]['perf'], label=str(freq_set[f]), 
                   marker=markers[f], linestyle='None',markersize=10, color=plt.cm.jet(f/10.))

#Drawing optimisation arrow on figure
#Start point
f1=8
k1=0
px1=freq_series[f1]['E'][k1]*freq_series[f1]['perf'][k1]
py1=freq_series[f1]['perf'][k1]
p1text = "    P3:\n" + str(freq_series[f1]['C'][k1]) + " cores \n" + str(freq_series[f1]['F'][k1]) + " MHz"
scatter_pp.plot(px1,py1, marker="o", markersize=28, mec='k', mfc="None", mew=3)
scatter_pp.annotate(p1text, xy=(px1,py1), xytext=(25,-20), textcoords='offset points', fontsize=16)
    
#End point
f2=1
k2=7
px2=freq_series[f2]['E'][k2]*freq_series[f2]['perf'][k2]
py2=freq_series[f2]['perf'][k2]
p2text = "    P4:\n" + str(freq_series[f2]['C'][k2]) + " cores \n" + str(freq_series[f2]['F'][k2]) + " MHz"
scatter_pp.plot(px2,py2, marker="o", markersize=28, mec='k', mfc="None", mew=3)
scatter_pp.annotate(p2text, xy=(px2,py2), xytext=(-80,20), textcoords='offset points', fontsize=16)
#Arrow
scatter_pp.annotate("",xy=(px2,py2), xytext=(px1, py1), 
    textcoords='data', arrowprops=dict(color='r', width=12, headlength=20, headwidth=25))
#Red text
scatter_pp.text(12, 0.95, "Same Power\nHigher Performance", color='r', ha="left", va="top", size=22,
                bbox=dict(fc="None",ec="k",lw=1))

legend = scatter_pp.legend(loc= 'lower right', title='Freq (MHz)', numpoints=1, fontsize=16)
plt.setp(legend.get_title(),fontsize=16)
fig1.savefig("pp_perf_opt.pdf",bbox_inches='tight')

#Point power and perf
f = f1
k = k1
pow1 = freq_series[f1]['E'][k1]*freq_series[f1]['perf'][k1]
perf1 = freq_series[f1]['perf'][k1]
print("Point 1:")
print("power = " + str(pow1))
print("perf = " + str(perf1))
f = f2
k = k2
pow2 = freq_series[f]['E'][k]*freq_series[f]['perf'][k]
perf2 = freq_series[f]['perf'][k]
print("Point 2:")
print("power = " + str(pow2))
print("perf = " + str(perf2))
print("perf diff = " + str((perf1-perf2)/perf1*100) + "%\n")

#####################################################
# Create a figure, specific size & dots per inch
fig1 = plt.figure(figsize=(8, 8), dpi=128)
scatter_pp = fig1.add_subplot(111)
scatter_pp.set_xlabel('Average Power per Frame (W)', fontsize=16)
scatter_pp.set_ylabel('Average Performance (FPS)', fontsize=16)
#scatter_pp.set_title('Performance & Power Points', fontsize=16)
scatter_pp.tick_params(axis='y', labelsize=16)
scatter_pp.tick_params(axis='x', labelsize=16)
scatter_pp.grid(True)

#Plot Graph  
#scatter_pp
for f in range(0,NUM_FREQS):
    scatter_pp.plot(freq_series[f]['E']*freq_series[f]['perf'],freq_series[f]['perf'], label=str(freq_set[f]), 
                   marker=markers[f], linestyle='None',markersize=10, color=plt.cm.jet(f/10.))

#Drawing optimisation arrow on figure
#Start point
f1=5
k1=7
px1=freq_series[f1]['E'][k1]*freq_series[f1]['perf'][k1]
py1=freq_series[f1]['perf'][k1]
p1text = "    P5:\n" + str(freq_series[f1]['C'][k1]) + " cores \n" + str(freq_series[f1]['F'][k1]) + " MHz"
scatter_pp.plot(px1,py1, marker="o", markersize=28, mec='k', mfc="None", mew=3)
scatter_pp.annotate(p1text, xy=(px1,py1), xytext=(-50,30), textcoords='offset points', fontsize=16)
    
#End point
f2=1
k2=3
px2=freq_series[f2]['E'][k2]*freq_series[f2]['perf'][k2]
py2=freq_series[f2]['perf'][k2]
p2text = "    P6:\n" + str(freq_series[f2]['C'][k2]) + " cores \n" + str(freq_series[f2]['F'][k2]) + " MHz"
scatter_pp.plot(px2,py2, marker="o", markersize=28, mec='k', mfc="None", mew=3)
scatter_pp.annotate(p2text, xy=(px2,py2), xytext=(-60,40), textcoords='offset points', fontsize=16)
#Arrow
scatter_pp.annotate("",xy=(px2,py2), xytext=(px1, py1), 
    textcoords='data', arrowprops=dict(color='r', width=12, headlength=20, headwidth=25))
#Red text
scatter_pp.text(12, 0.95, "Power and \nPerformance \nScaling", color='r', ha="left", va="top", size=22,
               bbox=dict(fc="None",ec="k",lw=1))

legend = scatter_pp.legend(loc= 'lower right', title='Freq (MHz)', numpoints=1, fontsize=16)
plt.setp(legend.get_title(),fontsize=16)
fig1.savefig("pp_scaling.pdf",bbox_inches='tight')

#Point power and perf
f = f1
k = k1
pow1 = freq_series[f1]['E'][k1]*freq_series[f1]['perf'][k1]
perf1 = freq_series[f1]['perf'][k1]
print("Point 1:")
print("power = " + str(pow1))
print("perf = " + str(perf1))
f = f2
k = k2
pow2 = freq_series[f]['E'][k]*freq_series[f]['perf'][k]
perf2 = freq_series[f]['perf'][k]
print("Point 2:")
print("power = " + str(pow2))
print("power diff = " + str((pow1-pow2)/pow1*100) + "%")
print("perf = " + str(perf2))
print("perf diff = " + str((perf1-perf2)/perf1*100) + "%\n")
