import pandas as pd
import numpy as np
from itertools import product
from enum import Enum
from scipy.interpolate import interp1d
from matplotlib import pyplot as plt
from datetime import datetime, timedelta
from PyQt5.QtCore import QThread, QRunnable, pyqtSignal
from pySOT import *
from poap.controller import ThreadController, BasicWorkerThread, SerialController
plt.style.use('ggplot')

class ObjectiveFunctionType(Enum):
    LCOE = 0,
    GridUsage = 1,
    GridUsageCost = 2,
    GridUsageCost_times_LCOE = 3

class Demand:

    def __init__(self, profile, nominal_power):
        """

        Args:
            profile: scale profile of the demand (all ones, for no change)
            nominal_power: Nominal power of the demand facility
        """
        self.index = None

        self.nominal_power = nominal_power

        self.normalized_power = profile * -1  # the convention is that loads are negative

    def power(self):
        """
        Returns the demanded power
        Returns: Array
        """
        return self.nominal_power * self.normalized_power

class SolarFarm:

    def __init__(self, profile, solar_power_min=0, solar_power_max=10000, unitary_cost=200):
        """

        Args:
            profile: Solar horizontal irradiation profile (W/m2) [1D array]
            solar_power_max: Maximum power in kW to consider when sizing
            unitary_cost: Cost peer installed kW of the solar facility (€/kW)
        """
        self.index = None

        self.nominal_power = None

        self.irradiation = profile

        self.normalized_power = profile / 1000.0

        self.max_power = solar_power_max

        self.min_power = solar_power_min

        self.unitary_cost = unitary_cost

    def power(self):
        """
        Returns the generated power
        Returns: Array
        """
        return self.nominal_power * self.normalized_power

    def cost(self):
        return self.unitary_cost * self.nominal_power

class WindFarm:

    def __init__(self, profile, wt_curve_df, wind_power_min=0, wind_power_max=10000, unitary_cost=900):
        """

        Args:
            profile: Wind profile in m/s
            wt_curve_df: Wind turbine power curve in a DataFrame (Power [any unit, values] vs. Wind speed [m/s, index])
            wind_power_max: Maximum nominal power of the wind park considered when sizing
            unitary_cost: Unitary cost of the wind park in €/kW

            Example of unitary cost:
            A wind park with 4 turbines of 660 kW cost 2 400 000 €
            2400000 / (4 * 660) = 909 €/kW installed
        """
        self.index = None

        self.nominal_power = None

        self.wt_curve_df = wt_curve_df

        # load the wind turbine power curve and normalize it
        ag_curve = interp1d(wt_curve_df.index, wt_curve_df.values / wt_curve_df.values.max())

        self.wind_speed = profile

        self.normalized_power = ag_curve(profile)

        self.max_power = wind_power_max

        self.min_power = wind_power_min

        self.unitary_cost = unitary_cost

    def power(self):
        """
        Returns the generated power
        Returns: Array
        """
        return self.nominal_power * self.normalized_power

    def cost(self):
        return self.unitary_cost * self.nominal_power

class BatterySystem:
    demanded_power = None
    energy = None
    power = None
    grid_power = None
    soc = None
    time = None

    def __init__(self, charge_efficiency=0.9, discharge_efficiency=0.9, max_soc=0.99, min_soc=0.3,
                 battery_energy_min=0, battery_energy_max=100000, unitary_cost=900):
        """

        Args:
            charge_efficiency: Efficiency when charging
            discharge_efficiency:  Efficiency when discharging
            max_soc: Maximum state of charge
            min_soc: Minimum state of charge
            battery_energy_max: Maximum energy in kWh allowed for sizing the battery
            unitary_cost: Cost per kWh of the battery (€/kWh)
        """

        self.index = None

        self.nominal_energy = None

        self.charge_efficiency = charge_efficiency

        self.discharge_efficiency = discharge_efficiency

        self.max_soc = max_soc

        self.min_soc = min_soc

        self.min_soc_charge = (self.max_soc + self.min_soc) / 2  # SoC state to force the battery charge

        self.charge_per_cycle = 0.1  # charge 10% per cycle

        self.max_energy = battery_energy_max

        self.min_energy = battery_energy_min

        self.unitary_cost = unitary_cost

        self.results = None

    def cost(self):
        return self.unitary_cost * self.nominal_energy

    def simulate_array(self, P, soc_0, time, charge_if_needed=False):
        """
        The storage signs are the following

        supply power: positive
        recharge power: negative

        this means that a negative power will ask the battery to charge and
        a positive power will ask the battery to discharge

        to match these signs to the give profiles, we should invert the
        profiles sign
        Args:
            P: Power array that is sent to the battery [Negative charge, positive discharge]
            soc_0: State of charge at the beginning [0~1]
            time: Array of DataTime values
            charge_if_needed: Allow the battery to take extra power that is not given in P. This limits the growth of
            the battery system in the optimization since the bigger the battery, the more grid power it will take to
            charge when RES cannot cope. Hence, since we're minimizing the grid usage, there is an optimum battery size
        Returns:
            energy: Energy effectively processed by the battery
            power: Power effectively processed by the battery
            grid_power: Power dumped array
            soc: Battery state of charge array
        """

        if self.nominal_energy is None:
            raise Exception('You need to set the battery nominal power!')

        # initialize arrays
        P = np.array(P)
        nt = len(P)
        energy = np.zeros(nt + 1)
        power = np.zeros(nt + 1)
        soc = np.zeros(nt + 1)
        grid_power = np.zeros(nt + 1)
        energy[0] = self.nominal_energy * soc_0
        soc[0] = soc_0

        charge_energy_per_cycle = self.nominal_energy * self.charge_per_cycle

        for t in range(nt-1):

            if np.isnan(P[t]):
                print('NaN found!!!!!!')

            # pick the right efficiency value
            if P[t] >= 0:
                eff = self.discharge_efficiency
            else:
                eff = self.charge_efficiency

            # the time comes in nanoseconds, we need the time step in hours
            dt = (time[t + 1] - time[t]).seconds / 3600

            # compute the proposed energy. Later we check how much is actually possible
            proposed_energy = energy[t] - P[t] * dt * eff

            # charge the battery from the grid if the SoC is too low and we are allowing this behaviour
            if charge_if_needed and soc[t] < self.min_soc_charge:
                proposed_energy -= charge_energy_per_cycle / dt  # negative is for charging

            # Check the proposed energy
            if proposed_energy > self.nominal_energy * self.max_soc:  # Truncated, too high

                energy[t + 1] = self.nominal_energy * self.max_soc
                power[t + 1] = (energy[t]-energy[t + 1]) / (dt * eff)
                grid_power[t + 1] = - power[t + 1] + P[t]

            elif proposed_energy < self.nominal_energy * self.min_soc:  # Truncated, too low

                energy[t + 1] = self.nominal_energy * self.min_soc
                power[t + 1] = (energy[t]-energy[t + 1]) / (dt * eff)
                grid_power[t + 1] = - power[t + 1] + P[t]

            else:  # everything is within boundaries

                energy[t + 1] = proposed_energy
                power[t + 1] = P[t]
                grid_power[t + 1] = 0

            # Update the state of charge
            soc[t + 1] = energy[t + 1] / self.nominal_energy

        # Compose a results DataFrame
        self.demanded_power = np.r_[0, P[:-1]]
        self.energy = energy[:-1]
        self.power = power[:-1]
        self.grid_power = grid_power[:-1]
        self.soc = soc[:-1]
        self.time = time

        # d = np.c_[np.r_[0, P[:-1]], power[:-1], grid_power[:-1], energy[:-1], soc[:-1] * 100]
        # cols = ['P request', 'P', 'grid', 'E', 'SoC']
        # self.results = pd.DataFrame(data=d, columns=cols)

        return energy[:-1], power[:-1], grid_power[:-1], soc[:-1]

    def plot(self, fig=None):

        if fig is None:
            fig = plt.figure(figsize=(12, 8))
        ax1 = fig.add_subplot(111)
        ax1.stackplot(self.time, self.power, self.grid_power)
        ax1.plot(self.time, self.demanded_power, linewidth=4)
        ax1.set_ylabel('kW')

        ax2 = ax1.twinx()
        ax2.plot(self.time, self.soc, color='k')
        ax2.set_ylabel('SoC')

        ax1.legend()
        plt.show()

class MicroGrid(QThread):

    progress_signal = pyqtSignal(float)
    progress_text_signal = pyqtSignal(str)
    done_signal = pyqtSignal()

    dim = 3  # 3 variables to optimize

    def __init__(self, solar_farm: SolarFarm, wind_farm: WindFarm, demand_system: Demand, battery_system: BatterySystem,
                 time_arr, LCOE_years=20, investment_rate=0.03, spot_price=None, band_price=None, maxeval=1000,
                 obj_fun_type: ObjectiveFunctionType=ObjectiveFunctionType.GridUsage):
        """

        :param solar_farm:
        :param wind_farm:
        :param demand_system:
        :param battery_system:
        :param time_arr:
        :param LCOE_years:
        :param investment_rate:
        :param spot_price:
        :param band_price:
        :param maxeval:
        :param obj_fun_type:
        """

        QThread.__init__(self)

        # variables for the optimization
        self.xlow = np.zeros(self.dim)  # lower bounds
        self.xup = np.array([solar_farm.max_power, wind_farm.max_power, battery_system.max_energy])
        self.info = "Microgrid with Wind turbines, Photovoltaic panels and storage coupled to a demand"  # info
        self.integer = np.array([0])  # integer variables
        self.continuous = np.arange(1, self.dim)  # continuous variables

        # assign the device list
        self.solar_farm = solar_farm

        self.wind_farm = wind_farm

        self.demand_system = demand_system

        self.battery_system = battery_system

        self.spot_price = spot_price

        self.band_price = band_price

        self.obj_fun_type = obj_fun_type

        # create a time index matching the length
        self.time = time_arr

        # economic variables
        self.lcoe_years = LCOE_years

        self.investment_rate = investment_rate

        # Results

        self.aggregated_demand_profile = None

        self.solar_power_profile = None

        self.wind_power_profile = None

        self.grid_power = None

        self.Energy = None
        self.battery_output_power = None
        self.battery_output_current = None
        self.battery_voltage = None
        self.battery_losses = None
        self.battery_state_of_charge = None
        self.iteration = None
        self.max_eval = maxeval

        self.optimization_values = None
        self.raw_results = None
        self.solution = None

        self.grid_energy = None
        self.energy_cost = None
        self.investment_cost = None
        self.lcoe_val = None

        self.x_fx = list()

    def __call__(self, x, verbose=False):
        """
        Call for this object, performs the dispatch given a vector x of facility sizes
        Args:
            x: vector [solar nominal power, wind nominal power, storage nominal power]

        Returns: Value of the objective function for the given x vector

        """
        return self.objfunction(x, verbose)

    def objfunction(self, x, verbose=False):
        """

        Args:
            x: optimsation vector [solar nominal power, wind nominal power, storage nominal power]

        Returns:

        """
        ################################################################################################################
        # Set the devices nominal power
        ################################################################################################################
        self.solar_farm.nominal_power = x[0]

        self.wind_farm.nominal_power = x[1]

        self.battery_system.nominal_energy = x[2]

        '''
        The profiles sign as given are:

            demand: negative
            generation: positive
        '''

        ################################################################################################################
        # Compute the battery desired profile
        ################################################################################################################

        self.aggregated_demand_profile = self.demand_system.power() + self.wind_farm.power() + self.solar_farm.power()

        ################################################################################################################
        # Compute the battery real profile: processing the desired profile
        ################################################################################################################

        '''
        The storage signs are the following

        supply power: positive
        recharge power: negative

        this means that a negative power will ask the battery to charge and
        a positive power will ask the battery to discharge

        to match these signs to the give profiles, we should invert the
        profiles sign
        '''

        # reverse the power profile sign (the surplus, should go in the battery)
        demanded_power = - self.aggregated_demand_profile
        # initial state of charge
        SoC0 = 0.5

        # calculate the battery values: process the desired power
        # energy, power, grid_power, soc
        self.Energy, \
        self.battery_output_power, \
        self.grid_power, \
        self.battery_state_of_charge = self.battery_system.simulate_array(P=demanded_power, soc_0=SoC0,
                                                                          time=self.time, charge_if_needed=True)

        # the processed values are 1 value shorter since we have worked with time increments

        # calculate the grid power as the difference of the battery power
        # and the profile required for perfect auto-consumption
        # self.grid_power = demanded_power - self.battery_output_power

        # compute the investment cost
        self.investment_cost = self.solar_farm.cost() + self.wind_farm.cost() + self.battery_system.cost()

        # compute the LCOE Levelized Cost Of Electricity
        res = self.lcoe(generated_power_profile=self.grid_power, investment_cost=self.investment_cost,
                        discount_rate=self.investment_rate, years=self.lcoe_years, verbose=verbose)
        self.grid_energy = res[0]
        self.energy_cost = res[1]
        self.investment_cost = res[2]
        self.lcoe_val = res[3]

        # select the objective function
        if self.obj_fun_type is ObjectiveFunctionType.LCOE:
            fx = abs(self.lcoe_val)
        elif self.obj_fun_type is ObjectiveFunctionType.GridUsage:
            fx = sum(abs(self.grid_power))
        elif self.obj_fun_type is ObjectiveFunctionType.GridUsageCost:
            fx = sum(abs(self.grid_power * self.spot_price))
        elif self.obj_fun_type is ObjectiveFunctionType.GridUsageCost_times_LCOE:
            fx = sum(abs(self.grid_power * self.spot_price)) * (1-abs(self.lcoe_val))
        else:
            fx = 0

        # store the values
        self.raw_results.append([fx] + list(x) + [self.lcoe_val, sum(abs(self.grid_power)), self.investment_cost])

        self.iteration += 1
        prog = self.iteration / self.max_eval
        # print('progress:', prog)
        self.progress_signal.emit(prog * 100)

        return fx

    def lcoe(self, generated_power_profile, investment_cost, discount_rate, years, verbose=False):
        """

        :param generated_power_profile:
        :param investment_cost:
        :param discount_rate:
        :param verbose:
        :return:
        """
        grid_energy = generated_power_profile.sum()
        energy_cost = (generated_power_profile * self.spot_price).sum()

        # build the arrays for the n years
        I = np.zeros(years)  # investment
        I[0] = investment_cost
        E = np.ones(years) * grid_energy  # gains/cost of electricity
        M = np.ones(years) * investment_cost * 0.1  # cost of maintenance

        dr = np.array([(1 + discount_rate)**(i+1) for i in range(years)])
        A = (I + M / dr).sum()
        B = (E / dr).sum()

        if verbose:
            print('Grid energy', grid_energy, 'kWh')
            print('Energy cost', energy_cost, '€')
            print('investment_cost', investment_cost, '€')
            print('dr', dr)
            print('A:', A, 'B:', B)
            print('lcoe_val', A/B)

        # self.grid_energy = grid_energy
        # self.energy_cost = energy_cost
        # self.investment_cost = investment_cost
        lcoe_val = A / B

        return grid_energy, energy_cost, investment_cost, lcoe_val

    def economic_sensitivity(self, years_arr, inv_rate_arr):
        """

        :param years_arr:
        :param inv_rate_arr:
        :return:
        """
        ny = len(years_arr)
        ni = len(inv_rate_arr)
        values = np.zeros((4, ni, ny))

        for i, years in enumerate(years_arr):
            for j, inv_rate in enumerate(inv_rate_arr):

                grid_energy, \
                energy_cost, \
                investment_cost, \
                lcoe_val = self.lcoe(generated_power_profile=self.grid_power,
                                     investment_cost=self.investment_cost,
                                     discount_rate=inv_rate,
                                     years=years,
                                     verbose=False)

                values[:, j, i] = np.array([grid_energy, energy_cost, investment_cost, lcoe_val])

        df_lcoe = pd.DataFrame(data=values[3, :, :], index=inv_rate_arr, columns=years_arr)

        return df_lcoe

    def optimize(self):
        self.run()

    def run(self):
        """
        Function that optimizes a MicroGrid Object
        Args:

        Returns:

        """
        self.iteration = 0
        self.raw_results = list()

        self.progress_signal.emit(0)
        self.progress_text_signal.emit('Optimizing facility sizes by surrogate optimization...')

        # (1) Optimization problem
        # print(data.info)

        # (2) Experimental design
        # Use a symmetric Latin hypercube with 2d + 1 samples
        exp_des = SymmetricLatinHypercube(dim=self.dim, npts=2 * self.dim + 1)

        # (3) Surrogate model
        # Use a cubic RBF interpolant with a linear tail
        surrogate = RBFInterpolant(kernel=CubicKernel, tail=LinearTail, maxp=self.max_eval)

        # (4) Adaptive sampling
        # Use DYCORS with 100d candidate points
        adapt_samp = CandidateDYCORS(data=self, numcand=100 * self.dim)

        # Use the serial controller (uses only one thread)
        controller = SerialController(self.objfunction)

        # (5) Use the synchronous strategy without non-bound constraints
        strategy = SyncStrategyNoConstraints(
            worker_id=0, data=self, maxeval=self.max_eval, nsamples=1,
            exp_design=exp_des, response_surface=surrogate,
            sampling_method=adapt_samp)
        controller.strategy = strategy

        # Run the optimization strategy
        result = controller.run()

        # Print the final result
        print('Best value found: {0}'.format(result.value))
        print('Best solution found: {0}'.format(
            np.array_str(result.params[0], max_line_width=np.inf,
                         precision=5, suppress_small=True)))
        self.solution = result.params[0]

        # Extract function values from the controller
        self.optimization_values = np.array([o.value for o in controller.fevals])

        # turn the results into a DataFrame
        data = np.array(self.raw_results)
        self.x_fx = pd.DataFrame(data=data[:, 1:], index=data[:, 0], columns=['Solar', 'Wind', 'Battery', 'LCOE', 'Grid energy', 'Investment'])
        self.x_fx.sort_index(inplace=True)

        self.progress_text_signal.emit('Done!')
        self.done_signal.emit()

    def plot(self):
        """
        Plot the dispatch values
        Returns:

        """
        # plot results
        plot_cols = 3
        plot_rows = 2

        steps_number = len(self.demand_system.normalized_power)

        plt.figure(figsize=(16, 10))
        plt.subplot(plot_rows, plot_cols, 1)
        plt.plot(self.demand_system.power(), label="Demand Power")
        plt.plot(self.solar_farm.power(), label="Photovoltaic Power")
        plt.plot(self.wind_farm.power(), label="Wind Power")
        plt.ylabel('kW')
        plt.legend()

        plt.subplot(plot_rows, plot_cols, 2)
        plt.plot(self.aggregated_demand_profile, label="Aggregated power profile")
        plt.plot(np.zeros(steps_number), 'k')
        plt.ylabel('kW')
        plt.legend()

        plt.subplot(plot_rows, plot_cols, 3)
        plt.plot(- self.aggregated_demand_profile, label="Power demanded to the battery")
        plt.plot(np.zeros(steps_number), 'k')
        plt.ylabel('kW')
        plt.legend()

        plt.subplot(plot_rows, plot_cols, 4)
        plt.plot(self.grid_power, label="Power demanded to the grid")
        plt.plot(np.zeros(steps_number), 'k')
        plt.ylabel('kW')
        plt.legend()

        plt.subplot(plot_rows, plot_cols, 5)
        plt.plot(self.battery_output_power, label="Battery power")
        plt.plot(np.zeros(steps_number), 'k')
        plt.ylabel('kW')
        plt.legend()

        plt.subplot(plot_rows, plot_cols, 6)
        plt.plot(self.battery_state_of_charge, label="Battery SoC")
        plt.plot(np.zeros(steps_number), 'k')
        plt.ylabel('Per unit')
        plt.legend()

    def plot_optimization(self, ax=None):
        """
        Plot the optimization convergence
        Returns:

        """
        if self.optimization_values is not None:
            max_eval = len(self.optimization_values)

            if ax is None:
                f, ax = plt.subplots()
            # Points
            ax.plot(np.arange(0, max_eval), self.optimization_values, 'bo')
            # Best value found
            ax.plot(np.arange(0, max_eval), np.minimum.accumulate(self.optimization_values), 'r-', linewidth=3.0)
            ax.set_xlabel('Evaluations')
            ax.set_ylabel('Function Value')
            ax.set_title('Optimization convergence')

    def export(self, file_name):
        """
        Export definition and results to excel
        :param file_name:
        :return:
        """

        writer = pd.ExcelWriter(file_name)

        # Solar irradiation
        pd.DataFrame(data=self.solar_farm.irradiation,
                     index=self.time,
                     columns=['irradiation (MW/m2)']).to_excel(writer, 'irradiation')

        # wind speed
        pd.DataFrame(data=self.wind_farm.wind_speed,
                     index=self.time,
                     columns=['VEL(m/s):60']).to_excel(writer, 'wind')

        # AG curve
        self.wind_farm.wt_curve_df.to_excel(writer, 'AG_CAT')

        # demand
        pd.DataFrame(data=self.demand_system.normalized_power * -1,
                     index=self.time,
                     columns=['normalized_demand']).to_excel(writer, 'demand')

        # prices
        pd.DataFrame(data=np.c_[self.spot_price, self.band_price],
                     index=self.time,
                     columns=['Secondary_reg_price', 'Spot_price']).to_excel(writer, 'prices')

        # Results
        self.x_fx.to_excel(writer, 'results')

        writer.save()

class MicroGridBrute(MicroGrid):

    def __init__(self, solar_farm: SolarFarm, wind_farm: WindFarm, demand_system: Demand, battery_system: BatterySystem,
                 time_arr, LCOE_years=20, investment_rate=0.03, spot_price=None, band_price=None, maxeval=1000,
                 obj_fun_type: ObjectiveFunctionType=ObjectiveFunctionType.GridUsage):
        """

        :param solar_farm:
        :param wind_farm:
        :param demand_system:
        :param battery_system:
        :param time_arr:
        :param LCOE_years:
        :param investment_rate:
        :param spot_price:
        :param band_price:
        :param maxeval:
        :param obj_fun_type:
        """

        MicroGrid.__init__(self, solar_farm, wind_farm, demand_system, battery_system,
                           time_arr, LCOE_years, investment_rate, spot_price, band_price,
                           maxeval, obj_fun_type)

    def run(self):
        """

        :return:
        """

        self.progress_signal.emit(0)
        self.progress_text_signal.emit('Optimizing facility sizes by brute force...')

        step = self.max_eval
        solar_range = np.arange(self.solar_farm.min_power, self.solar_farm.max_power + step, step)
        wind_range = np.arange(self.wind_farm.min_power, self.wind_farm.max_power + step, step)
        storage_range = np.arange(self.battery_system.min_energy, self.battery_system.max_energy + step, step)
        self.max_eval = len(solar_range) * len(wind_range) * len(storage_range)
        self.iteration = 0
        self.raw_results = list()

        # Run all the combinations
        for solar_x, wind_x, storage_x in product(solar_range, wind_range, storage_range):
            x = np.array([solar_x, wind_x, storage_x])
            res = self.objfunction(x)
            # results.append(np.r_[x, res])

        data = np.array(self.raw_results)
        self.x_fx = pd.DataFrame(data=data[:, 1:], index=data[:, 0], columns=['Solar', 'Wind', 'Battery', 'LCOE', 'Grid energy', 'Investment'])

        # Extract function values from the controller
        self.optimization_values = data[:, 0].copy()

        # Sort to pick the best
        self.x_fx.sort_index(inplace=True)

        # the best solution, after sorting, is the first one
        self.solution = self.x_fx.values[0, :]

        self.progress_text_signal.emit('Done!')
        self.done_signal.emit()

if __name__ == '__main__':

    # Create devices
    fname = 'data.xls'

    prices = pd.read_excel(fname, sheetname='prices')[['Secondary_reg_price', 'Spot_price']].values

    # load the solar irradiation in W/M2 and convert it to kW
    solar_radiation_profile = pd.read_excel(fname, sheetname='irradiation')['irradiation (MW/m2)'].values

    #  create the solar farm object
    solar_farm = SolarFarm(solar_radiation_profile)

    # Load the wind speed in m/s
    wind_speed_profile = pd.read_excel(fname, sheetname='wind')['VEL(m/s):60'].values

    # load the wind turbine power curve and normalize it
    ag_curve_df = pd.read_excel(fname, sheetname='AG_CAT')['P (kW)']

    # create the wind farm object
    wind_farm = WindFarm(wind_speed_profile, ag_curve_df)

    # load the demand values and set it negative for the sign convention
    demand_profile = pd.read_excel(fname, sheetname='demand')['normalized_demand'].values

    # Create the demand facility
    desalination_plant = Demand(demand_profile, nominal_power=1000)

    # Create a Battery system
    battery = BatterySystem()

    nt = len(wind_speed_profile)
    time = [datetime(2016, 1, 1) + timedelta(hours=h) for h in range(nt)]

    # Create a MicroGrid with the given devices
    # Divide the prices by thousand because they represent €/MWh and we need €/kWh
    micro_grid = MicroGrid(solar_farm=solar_farm,
                           wind_farm=wind_farm,
                           battery_system=battery,
                           demand_system=desalination_plant,
                           time_arr=time,
                           LCOE_years=20,
                           spot_price=prices[:, 0] / 1000,
                           band_price=prices[:, 1] / 1000,
                           maxeval=100)
    micro_grid.optimize()
    res = micro_grid(micro_grid.solution, verbose=True)
    micro_grid.x_fx.to_excel('results.xlsx')
    # print(micro_grid.x_fx)

    micro_grid.plot()
    micro_grid.plot_optimization()

    battery.plot()

    plt.show()

    # print(res)


#!/usr/env/python3

import sys
import os
import json
from pathlib import Path
from monty.os import cd
from subprocess import getoutput
from time import sleep

from ase.calculators.abacus.create_input import AbacusInput
from math import ceil, pi
import numpy as np
from ase.io import read
from ase.io.abacus import write_input_stru

HELPER = r"/WORK/nscc-gz_material_1/ICSD_vasp/abacus_calc/scripts/abacus_helper/"

def yield_stru(path):
   for i in path.rglob("*"):
       if len(i.parts) == len(path.parts) + 1:
           yield i

def _write_input(path):
    with cd(path):
        os.system("cp SCF_KPT KPT")
        ntype = getoutput("ls *.orb |wc -l")
        input = AbacusInput()
        input.set(atom_file=path.name,
                  ntype=int(ntype),
                  kpoint_file='KPT',
                  pseudo_dir='./',
                  calculation='nscf',
                  nspin=2,
                  ecutwfc=60,
                  niter=50,
                  dr2=1.0e-9,
                  ethr=1.0e-7,
                  start_charge="file",
                  out_dos=1,
                  smearing='gaussian',
                  sigma=0.02,
                  ks_solver='genelpa',
                  basis_type='lcao',
                  mixing_type='pulay',
                  mixing_beta=0.4,
                  gamma_only=0)
        input.write_input_input()
        os.system('yhbatch -N 1 abacus.sh')
        sleep(1)

def make_band_inputs(scf):
    with cd(scf):
        os.system("mv *.out ./OUT.ABACUS/")
        os.system("mv OUT.ABACUS BAND_OUT.ABACUS")
        os.system("mv INPUT BAND_INPUT")
        os.system("mv KPT BAND_KPT")
    _write_input(scf)

if __name__ == "__main__":
    args = sys.argv

    scf = Path(args[1])
    for s in yield_stru(scf):
        s = s.absolute()
        #try:
        make_band_inputs(s)
        #except:
        #    os.system(f"echo {s} >> err_dos.log")


#https://www.cnblogs.com/liulinghua90/p/9935642.html

#https://blog.csdn.net/liuyq859/article/details/78387600
import pandas as pd
import math
import matplotlib.pyplot as plt
from scipy import stats
import numpy as np
from sklearn.cluster import KMeans
df = pd.read_excel('BIM3010_Fall_2019_Homework4th.xls')
data = np.array(df.values)
#data=format(data)
nonSaverage = [] # record average number of gene expression level for non-smoker
Saverage = [] # average gene expression level for smoker
FC = [] #record fold change of each gene
Pvalue=[]
logadjPvalue = [] #record statistical significance of gene expression difference between smoker and non-smoker
DEG = [] #record differencial expression genes
for i in range(len(data)):
    nonSaverage.append(sum(data[i][3:15])/12)
    Saverage.append(sum(data[i][15:25])/10)
    fc = math.log2((sum(data[i][15:25])/10)/(sum(data[i][3:15])/12))
    FC.append(fc) #calculate fold change of each gene
    #P = stats.ttest_ind(data[i][3:15],data[i][15:25],equal_var=False).pvalue
    P = stats.ttest_ind(data[i][3:15],data[i][15:25],equal_var=False).pvalue
    Pvalue.append(P)
rankp= sorted(Pvalue)

f = 'DEG.txt'
fil=open(f,'w')
f2 = 'DEGLabel.txt'
fil2=open(f2,'w')
f3 = 'detail.txt'
fil3 = open(f3,'w+')
fil3.write(str(np.matrix(['ID','log2FC','Pvalue', '-log10adjPvalue'])))
f4 = 'DEGinfor.txt'
fil4 = open(f4,'w+')
DEGdata = []
for i in range(len(data)):
    adjP = Pvalue[i]*len(data)/(rankp.index(Pvalue[i])+1)
    logadjPvalue.append(-math.log10(adjP))

    if abs(FC[i]) >= 1 and adjP < 0.05:
        DEGdata.append(data[i][3:25])
        DEG.append(data[i][0])
        fil.write(data[i][0])
        fil.write('\n')
        fil2.write(data[i][2])
        fil2.write('\n')
    detail = np.matrix([data[i][0], FC[i], Pvalue[i], logadjPvalue[i]])
    fil3.write(str(detail))
    fil3.write('\n')
    fil4.write(data[i][0]+''+str(data[i][3:25]))
    fil4.write('\n')
print(DEGdata)

# clustering
DEG = np.array(DEGdata)
kmeans = KMeans(n_clusters = 3, random_state=0).fit(DEG)
print(kmeans.labels_)

fil.close()
fil2.close()
fil3.close()
fil4.close()
#print(FC)
#print(logadjPvalue)
#len(data)*
x1=[-1,-1]
y1=[0,3]
x2=[1,1]
y2=[0,3]
x3=[-3,6]
y3=[-math.log10(0.05),-math.log10(0.05)]
plt.plot(x1,y1,x2,y2,x3,y3,color='black',linestyle='--')
print(len(FC))
plt.scatter(FC,logadjPvalue,s=2)
plt.show()


import pygame

class Tourelle_1:

    def __init__(self):
        self.image = pygame.image.load('image/tour_de_get.jpg')
        self.image = pygame.transform.scale(self.image, (50, 50))
        self.rect = self.image.get_rect()
        self.rect.x = -10
        self.rect.y = -10
        self.vitesse = 5
        self.degat = 10

    def MouvementDroite(self):
        if self.rect.x <= 1030:
            self.rect.x += self.vitesse

    def MouvementGauche(self):
        if self.rect.x >= 0:
            self.rect.x -= self.vitesse

    def MouvementHaut(self):
        if self.rect.y >= 0:
            self.rect.y -= self.vitesse

    def MouvementBas(self):
        if self.rect.y <=  650:
            self.rect.y += self.vitesse

    def attaquer_les_monstres(self, jeu):
        distance = 1000000
        for monstre in jeu.tout_monstre:
            if monstre.distance_joueur < distance:
                distance = monstre.distance_joueur
        for monstre in jeu.tout_monstre:
            if monstre.distance_joueur == distance:
                monstre.vie -= self.degat


# %% [markdown]
# (imperative_workflow)=
#
# # Imperative Workflows
#
# ```{eval-rst}
# .. tags:: Basic
# ```
#
# Workflows are commonly created by applying the `@workflow` decorator to Python functions.
# During compilation, this involves processing the function's body and utilizing subsequent calls to
# underlying tasks to establish and record the workflow structure. This approach is known as declarative
# and is suitable when manually drafting the workflow.
#
# However, in cases where workflows are constructed programmatically, an imperative style is more appropriate.
# For instance, if tasks have been defined already, their sequence and dependencies might have been specified
# in textual form (perhaps during a transition from a legacy system).
# In such scenarios, you want to orchestrate these tasks.
# This is where Flyte's imperative workflows come into play, allowing you to programmatically construct workflows.
#
# To begin, import the necessary dependencies.
# %%
from flytekit import Workflow

# %% [markdown]
# We import the `slope` and `intercept` tasks from the `workflow.py` file.
# %%
from .workflow import intercept, slope

# %% [markdown]
# Create an imperative workflow.
# %%
imperative_wf = Workflow(name="imperative_workflow")

# %% [markdown]
# Add the workflow inputs to the imperative workflow.
# %%
imperative_wf.add_workflow_input("x", list[int])
imperative_wf.add_workflow_input("y", list[int])

# %% [markdown]
# ::: {note}
# If you want to assign default values to the workflow inputs,
# you can create a {ref}`launch plan <launch_plan>`.
# :::
#
# Add the tasks that need to be triggered from within the workflow.
# %%
node_t1 = imperative_wf.add_entity(slope, x=imperative_wf.inputs["x"], y=imperative_wf.inputs["y"])
node_t2 = imperative_wf.add_entity(
    intercept, x=imperative_wf.inputs["x"], y=imperative_wf.inputs["y"], slope=node_t1.outputs["o0"]
)

# %% [markdown]
# Lastly, add the workflow output.
# %%
imperative_wf.add_workflow_output("wf_output", node_t2.outputs["o0"])

# %% [markdown]
# You can execute the workflow locally as follows:
# %%
if __name__ == "__main__":
    print(f"Running imperative_wf() {imperative_wf(x=[-3, 0, 3], y=[7, 4, -2])}")

# %% [markdown]
# :::{note}
# You also have the option to provide a list of inputs and
# retrieve a list of outputs from the workflow.
#
# ```python
# wf_input_y = imperative_wf.add_workflow_input("y", list[str])
# node_t3 = wf.add_entity(some_task, a=[wf.inputs["x"], wf_input_y])
# ```
#
# ```python
# wf.add_workflow_output(
#     "list_of_outputs",
#     [node_t1.outputs["o0"], node_t2.outputs["o0"]],
#     python_type=list[str],
# )
# ```
# :::


import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('toblur.jpg')
assert img is not None, "file could not be read, check with os.path.exists()"
blur = cv.GaussianBlur(img,(5,5), 0)
cv.imwrite('blur.png', blur)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()


from optlang import Variable, Constraint, Objective, Model

# Define problem parameters
# Note this can be done using any of Python's data types. Here we have chosen dictionaries
supply = {"Seattle": 350, "San_Diego": 600}
demand = {"New_York": 325, "Chicago": 300, "Topeka": 275}

distances = {  # Distances between locations in thousands of miles
    "Seattle": {"New_York": 2.5, "Chicago": 1.7, "Topeka": 1.8},
    "San_Diego": {"New_York": 2.5, "Chicago": 1.8, "Topeka": 1.4}
}

freight_cost = 9  # Cost per case per thousand miles

# Define variables
variables = {}
for origin in supply:
    variables[origin] = {}
    for destination in demand:
        # Construct a variable with a name, bounds and type
        var = Variable(name="{}_to_{}".format(origin, destination), lb=0, type="integer")
        variables[origin][destination] = var

# Define constraints
constraints = []
for origin in supply:
    const = Constraint(
        sum(variables[origin].values()),
        ub=supply[origin],
        name="{}_supply".format(origin)
    )
    constraints.append(const)
for destination in demand:
    const = Constraint(
        sum(row[destination] for row in variables.values()),
        lb=demand[destination],
        name="{}_demand".format(destination)
    )
    constraints.append(const)

# Define the objective
obj = Objective(
    sum(freight_cost * distances[ori][dest] * variables[ori][dest] for ori in supply for dest in demand),
    direction="min"
)
# We can print the objective and constraints
print(obj)
print("")
for const in constraints:
    print(const)

print("")

# Put everything together in a Model
model = Model()
model.add(constraints)  # Variables are added implicitly
model.objective = obj

# Optimize and print the solution
status = model.optimize()
print("Status:", status)
print("Objective value:", model.objective.value)
print("")
for var in model.variables:
    print(var.name, ":", var.primal)


import sqlite3
from flask import Blueprint, jsonify, request
from todo.models import db
from todo.models.todo import Todo
import datetime

api = Blueprint("api", __name__, url_prefix="/api/v1")

@api.route("/health")
def health():
    return jsonify({"status": "ok"})

@api.route("/todos", methods=["GET"])
def get_todos():
    todos = Todo.query.filter(
        Todo.completed.in_(
            (True,) if request.args.get("completed", None) else (True, False)
        ),
        Todo.deadline_at
        <= datetime.datetime.now()
        + datetime.timedelta(days=int(request.args.get("window", None)))
        if request.args.get("window", None)
        else Todo.deadline_at >= datetime.datetime.min,
    )

    result = []
    for todo in todos:
        result.append(todo.to_dict())
    return jsonify(result)

@api.route("/todos/<int:todo_id>", methods=["GET"])
def get_todo(todo_id):
    todo = Todo.query.get(todo_id)
    if todo is None:
        return jsonify({"error": "Todo not found"}), 404
    return jsonify(todo.to_dict())

@api.route("/todos", methods=["POST"])
def create_todo():
    try:
        todo = Todo(**request.json)
        if "deadline_at" in request.json:
            todo.deadline_at = datetime.datetime.fromisoformat(request.json.get("deadline_at"))
        # Adds a new record to the database or will update an existing record
        db.session.add(todo)
        # Commits the changes to the database, this must be called for the changes to be saved
        db.session.commit()
        return jsonify(todo.to_dict()), 201
    except:
        return jsonify({f"error": "Invalid ToDo content: {error}"}), 400

@api.route("/todos/<int:todo_id>", methods=["PUT"])
def update_todo(todo_id):
    todo = Todo.query.get(todo_id)
    if todo is None:
        return jsonify({"error": "Todo not found"}), 404
    if request.json.get("id", None) and request.json.get("id", None) != todo_id:
        return jsonify({"error": "Cannot change ID of Todo"}), 400
    for field in request.json.keys():
        if field not in Todo.attribute_names(Todo):
            return jsonify({"error": "Invalid field name"}), 400
    todo.title = request.json.get("title", todo.title)
    todo.description = request.json.get("description", todo.description)
    todo.completed = request.json.get("completed", todo.completed)
    todo.deadline_at = request.json.get("deadline_at", todo.deadline_at)
    db.session.commit()

    return jsonify(todo.to_dict())

@api.route("/todos/<int:todo_id>", methods=["DELETE"])
def delete_todo(todo_id):
    todo = Todo.query.get(todo_id)
    if todo is None:
        return jsonify({}), 200
    db.session.delete(todo)
    db.session.commit()
    return jsonify(todo.to_dict()), 200


"""
MQTT/HTTP based messaging module for client.

All requests go through the HTTP interface for authentication,
MQTT is used to receive events from running gimels.
"""
import sys
import atexit
import queue
import threading

import paho.mqtt.client as mqtt
import requests
import json

SETUP_CMD      = "https://menshnet.online/api/setup"
DISCONNECT_CMD = "https://menshnet.online/api/disconnect"
START_CMD      = "https://menshnet.online/api/start"
STOP_CMD       = "https://menshnet.online/api/stop"
HEARTBEAT_CMD  = "https://menshnet.online/api/heartbeat"

class Messenger:

    def _on_shutdown(self):
        try:
            r = requests.post(DISCONNECT_CMD, json={ 'apiKey': self.apiKey })
            if r.status_code != 200:
                logging.error("disconnect %s" % r.content.decode())
        except:
            # send and forget
            pass

    def __init__(self, apiKey, logger):
        self.mqttc = None
        self.mt = None
        self.apiKey = apiKey
        self.subscribe_inflight = {}
        self.logger = logger

        # handle inbound events and route to user supplied
        # endpoint
        self.topic_handlers = {}

        atexit.register(self._on_shutdown)

    def __del__(self):
        self._on_shutdown()
        atexit.unregister(self._on_shutdown)

    def _on_message(self, client, userdata, msg):
        #self.logger.debug("Inbound message %s %s" % (msg.topic,msg.payload))
        handler = self.topic_handlers.get(msg.topic)
        if callable(handler):
            jobj = json.loads(msg.payload.decode())
            handler(jobj)
        else:
            self.logger.warning("Unhandled topic %s" % msg.topic)

    def _on_subscribe(self, client, userdata, mid, granted_qos):
        self.logger.debug("on subscribe mid=%s" % str(mid))
        if mid in self.subscribe_inflight:
             # set event that we have subscribed to topic
             self.subscribe_inflight[mid].set()

    def _on_connect(self, client, userdata, flags, rc):
        self.logger.debug("on connect rc=%d" % rc)

    ## public interface

    def heartbeat(self):
        r = requests.post(HEARTBEAT_CMD, json={
                "apiKey": self.apiKey
            })
        if r.status_code != 200:
            print(r.content)
            self.logger.error("Unable to send heartbeat to remote server")

    def register(self, topic, handler):
        "bind a callback to a mqtt topic"
        # subscribe, wait for confirmation
        mid = self.mqttc.subscribe(topic)[1]
        self.subscribe_inflight[mid] = threading.Event()
        self.subscribe_inflight[mid].wait()
        del self.subscribe_inflight[mid]

        # assign topic handler
        self.topic_handlers[topic] = handler

    def unregister(self, topic):
        self.mqttc.unsubscribe(topic)
        if topic in self.topic_handlers:
            del self.topic_handlers[topic]

    def start(self, name, resId, event_topic, config):
        """
        post message to gatway to kick off pipeline
        """
        r = requests.post(START_CMD, json={
            "apiKey": self.apiKey,
            "name": name,
            "resId": resId,
            "event_topic": event_topic,
            "config": config
        })
        if r.status_code == 400:
            raise ValueError(r.content.decode())
        elif r.status_code != 200:
            raise RuntimeError(r.content.decode())

    def stop(self, resId):
        """
        post message to gatway to kick off pipeline
        """
        r = requests.post(STOP_CMD, json={
            "apiKey": self.apiKey,
            "resId": resId
        })
        if r.status_code == 400:
            raise ValueError(r.content.decode())
        elif r.status_code != 200:
            raise RuntimeError(r.content.decode())

    def setup(self, timeout=15):
        """
        Setup communication for receinging events and sending
        commands.

        apiKey: apiKey which is generated by menshnet and is available on the
                user dashboard page.

        on_ready: if supplied then setup will not block rather it will
                  call on_ready when messenger is ready to process
                  commands. Otherwise this call shall block.

        Return:
            A list of pipeline names that teh end user dfined in thier git repo.
        """
        pipeline_names = []
        # validate apikey
        r = requests.post(SETUP_CMD, json={
            "apiKey": self.apiKey
        })
        if r.status_code != 200:
            raise ConnectionRefusedError(r.content)
        data = json.loads(r.content)
        pipeline_names = data['result']['names']

        if len(pipeline_names) == 0:
            self.logger.warning("There are no configured pipeline names for you to use!")

        f = "apikey validated connecting to mqtt broker pipeline_names=%s"
        self.logger.debug(f % str(pipeline_names))

        self.mqttc = mqtt.Client(transport="websockets")
        self.mqttc.on_connect = self._on_connect
        self.mqttc.on_message = self._on_message
        self.mqttc.on_subscribe = self._on_subscribe

        self.mqttc.connect('menshnet.online',9001,60)

        #NOTE: do not use the loop_start, very wierd problems
        # using that. Instead we will run our own network
        # thread.
        self.mt = threading.Thread(target=self.mqttc.loop_forever)
        self.mt.daemon = True
        self.mt.start()

        return pipeline_names

if __name__ == '__main__':
    import sys
    import logging

    apiKey = sys.argv[1]
    logging.basicConfig(stream=sys.stdout,level=logging.DEBUG,format="%(message)s")

    m = Messenger(apiKey, logging)
    m.setup()
    print("connected")
    def dummy(*args):
        pass
    m.register("dummy/event", dummy)
    m.heartbeat()


import json, sys, string

def main():
    if len(sys.argv) != 3:
        return

    inname = sys.argv[1]
    outname = sys.argv[2]

    with open(inname, mode='r') as inFile:
        tweets = json.load(inFile)
        exclude = set(string.punctuation)

        for tweet in tweets:
            text = ''.join(ch for ch in tweet['text'] if ch not in exclude)
            tweet['words'] = len(text.split())

        with open(outname, 'w') as outfile:
            json.dump(tweets, outfile)

if __name__ == '__main__':
    main()


# CONVEX HULL IMPLEMENTATION USING GRAHAM SCAN ALGORITHM.
# APPLICATION :
# SELF DRIVING CARS(COLLISION AVOIDANCE),OBJECT TRACKING , EPIDEMIC SPREAD EXTENT , CALCULATING THE AREA OF CONTAINMENT ZONE TO MITIGATE PANDEMICS
# , IMAGE PROCESSING , PACKAGING ETC.
#

import numpy as np
import matplotlib.pyplot as plt
import math
import matplotlib
class Hull:
    def __init__(self):
        self.points = [] #GRAPH POINTS
        self.stack = [] #MAINTING A STACK OF EXPLORED POINTS SATISFYING THE CCW CONDITION

    def insert(self,point):
        #INSERTING THE POINTS
        if not point is None:
            self.points.append(point)

    def sort(self):
        #THIS FUNCTION SORTS THE POINTS FIRST , BASED ON THE Y COORDINATE OF POINTS AND THEN BASED ON THE POLAR ANGLE MADE BY EVERY OTHER POINT
        #WITH THE POINT WITH LOWEST Y COORDINATE .
        def fun(x):
            return x[1]
        ar = sorted(self.points,key=fun)
        start = ar[0] #POINT WITH LOWEST Y COORDINATE POINT
        def fun2(x):
            return self.slope(start,x) #CALCULATING THE POLAR ANGLE OF REST OF POINT WITH RESPECT TO THE POINT WITH LOWEST Y COORDINATE
        return sorted(ar[1:],key=fun2),start

    def slope(self,point1,point2):
        return math.atan2((point2[1] - point1[1]),(point2[0] - point1[0])) #POLAR SLOPE OF THE POINTS
    def calc_slope_btw_last_three_points(self):

        #CALCULATING THE RELATIVE CCW ROTATION EXTENT
        length = len(self.stack)-1
        p = self.stack[length-2]
        q = self.stack[length-1]
        r = self.stack[length]
        return (q[1] - p[1])*(r[0] - q[0]) - (r[1] - q[1])*(q[0] - p[0])
        # return self.slope(self.stack[length-2],self.stack[length-1]) - self.slope(self.stack[length-1],self.stack[length])

    def hull(self):
        #MAIN FUNCTION
        sorted_array,start =self.sort()
        self.stack.append(start) #APPENDING THE START POINT INTO STACK
        for i in range(0,len(sorted_array)): #ITERATING OVER THE POINTS
            self.stack.append(sorted_array[i])
            # print(self.stack)
            while len(self.stack)>2 and not self.calc_slope_btw_last_three_points()<0: #CONTINUOUSLY ASSESING THE STACK POINTS FOR THE CCW ROTATION IF NOT DROPPING THE POINTS
                del self.stack[-2]
            yield self.stack #USED FOR ANIMATING DISPLAY USING MATPLOTLIB

    def animate(self,i):
        #FUNCTION FOR PLOTTING THE AS ALGORITHM INPUTS POINTS INTO STACK.

        plt.cla() #CLEARING THE PLOT EACH TIME SO AS TO CREATE A ANIMATING APPEAL

        plt.plot(np.array(self.points)[:,0],np.array(self.points)[:,1],'o') #PLOTTING THE GRAPH POINTS

        plt.plot(np.array(self.stack)[:,0],np.array(self.stack)[:,1],'k-') #PLOTTING THE LINE THROUGH THE POINTS IN THE STACK

        plt.title("Grapham Scan Algorithm , For Convex Hull") #TITLE

    def __str__(self):
        string = ""
        for i in self.stack:
            string+=str('('+str(i[0])+' '+str(i[1])+')')
        return string

hull = Hull()
points = np.random.rand(50, 2) #GENERATING SOME RANDOM DATA POINTS TO SURROUND THEM WITH CONVEX HULL

#INITIAL DATA USED FOR TESTING THE ALGORITHM PERFORMANCE

# points = [[0, 3], [1, 1], [2, 2], [4, 4], [0, 0], [1, 2], [3, 1], [3, 3]]

for i in points:
    hull.insert(i)#INSERTING THE DATA INTO CLASS'S POINTS LIST

ani = matplotlib.animation.FuncAnimation(plt.gcf(),hull.animate,hull.hull) #MATPLOTLIB FUNCTION USED FOR ANIMATING THE PLOTS.

plt.show()


from langchain.tools import StructuredTool
from pydantic.v1 import BaseModel

def write_report(filename, html):
    with open(filename, "w") as f:
        f.write(html)

class WriteReportArgsSchema(BaseModel):
    filename: str
    html: str

"""
    In 'sql.py' we used 'Tool' class, here we will use 'StructuredTool', as it supports multiple arguments, like
    defined in above class
"""
write_report_tool = StructuredTool.from_function(
    name="write_report",
    description="Write an HTML file to disk. Use this tool whenever someone asks for a report",
    func=write_report,
    args_schema=WriteReportArgsSchema,
)


import pygame
import time
from ccm.display.pygame import render

class PygameDisplay:
    def __init__(self,obj,width=640,height=480,full=False,title='CCMSuite',background='#CCCCCC'):
        self.obj=obj
        self.title=title
        self.paused=False
        self.skipped_frame=False
        self.rate=1.0

        pygame.init()
        self.screen=pygame.display.set_mode((width,height),pygame.RESIZABLE|pygame.DOUBLEBUF,32)
        self.background=pygame.Color(background)

        obj._get_scheduler().add(self.render_loop)

    def update_title(self):
        rateinfo=''
        if self.rate!=1.0: rateinfo='[%1.3fx]'%self.rate
        pygame.display.set_caption('%s: time=%1.3f%s'%(self.title,self.obj.now(),rateinfo))

    def on_escape(self):
        self.obj.stop()
    def on_pause(self):
        self.paused=not self.paused
    def on_pgup(self):
        self.rate*=1.1
    def on_pgdown(self):
        self.rate/=1.1

    def on_key(self,key):
        if hasattr(self.obj,'key_pressed'):
            self.obj.key_pressed(key)

    def render_loop(self):
        obj=self.obj

        dt=0.01

        while True:
            next_time=time.clock()+dt
            yield dt*self.rate
            self.screen.fill(self.background)
            render(obj,self.screen)
            self.handle_events()
            pygame.display.flip()
            if time.clock()>next_time and obj.now()>dt:
                #print 'frame skipped at t=%1.3fs'%obj.now()
                self.skipped_frame=True
                if dt<0.3:
                    dt+=0.01
            while self.paused or time.clock()<next_time:
                self.handle_events()
            self.update_title()

    def handle_events(self):
        for event in pygame.event.get():
            if event.type==pygame.KEYDOWN:
                if event.key==pygame.K_ESCAPE:
                     self.obj.stop()
                elif event.key==pygame.K_PAGEDOWN:
                     self.on_pgdown()
                elif event.key==pygame.K_PAGEUP:
                     self.on_pgup()
                elif event.key==pygame.K_PAUSE:
                     self.on_pause()

                elif event.key<128:
                     self.on_key(chr(event.key))
            elif event.type==pygame.QUIT:
                self.obj.stop()


import numpy as np
from astropy import units as u
import astropy.coordinates as coord
from astropy.coordinates import SkyCoord
import pandas as pd

df = pd.read_csv('Binaries_Catalog.csv')   #A catalog
f = pd.read_csv('Astrosat.csv')
f = f.drop_duplicates(subset=['SIMBAD_Name'], keep='first').reset_index(drop=True) #B catalog

ra_A = df['ra']
dec_A = df['dec']
ra_B = f['ra']
dec_B = f['dec']

sourcecat = SkyCoord(ra = ra_A*u.degree, dec=dec_A*u.degree, frame='icrs')
astrocat = SkyCoord(ra = ra_B*u.degree, dec=dec_B*u.degree, frame='icrs')

idx,d2d,d3d = (sourcecat).match_to_catalog_sky(astrocat)  #crossmatch
max_radius = 120./3600 #120 arcsec

df['Astrosat_Flag'] = np.zeros(len(df))
df['Date_Observed'] = np.zeros(len(df))
df['Time_Observed'] = np.zeros(len(df))
df['Proposal_ID'] = np.zeros(len(df))
df['Target_ID'] = np.zeros(len(df))
df['Observation_ID'] = np.zeros(len(df))
df['Instrument'] = np.zeros(len(df))

Required_Types = list(['LMXB','HMXB','LPV*','GlCl','EB*','XB','Be*','Symbiotic*']) #only sources of this type will be crossmatched to avoid errors

for id1, (closest_id2, dist) in enumerate(zip(idx, d2d)):
    closest_dist = dist.value

    if closest_dist < max_radius:
        if f['Source_Type'][closest_id2] in Required_Types:
            df['Astrosat_Flag'][id1] = 1 #Set Flag = 1 if there is a match
            df['Date_Observed'][id1] = f['Date_Observed'][closest_id2]
            df['Time_Observed'][id1] = f['Time_Observed'][closest_id2]
            df['Proposal_ID'][id1] = f['Proposal_ID'][closest_id2]
            df['Target_ID'][id1] = f['Target_ID'][closest_id2]
            df['Observation_ID'][id1] = f['Observation_ID'][closest_id2]
            df['Instrument'][id1] = f['Instrument'][closest_id2]

    else:
        pass

print(df.head())
df.to_csv('Crossmatch_AB.csv', index=False)


"""

This module contains GranularityLevel and Program class.

"""
import os
import shutil
import json
from enum import Enum
from distutils.dir_util import copy_tree
from .logger import Logger

class GranularityLevel(Enum):
    """

    GranularityLevel represents the granularity levels of program.

    """
    LINE = 'line'
    AST = 'AST'

    @classmethod
    def is_valid(cls, value):
        """
        :param value: The value of enum to check

        :return: Whether there is an enum that has a value equal to the `value`
        :rtype: bool

        .. hint::
            There are some examples,
            ::
                GranularityLevel.is_valid('line')
                >> True
                GranularityLevel.is_valid('random_text')
                >> False
        """
        return any(value == item.value for item in cls)

class Program(object):
    """

    Program encapsulates the original source code.
    Currently, PYGGI stores the source code as a list of code lines,
    as lines are the only supported unit of modifications.
    For modifications at other granularity levels,
    this class needs to process and store the source code accordingly
    (for example, by parsing and storing the AST).

    """
    CONFIG_FILE_NAME = 'PYGGI_CONFIG'
    TMP_DIR = "./pyggi_tmp/"

    def __init__(self, path, granularity_level=GranularityLevel.LINE,
                 config_file_name=CONFIG_FILE_NAME):
        assert isinstance(granularity_level, GranularityLevel)
        self.path = path.strip()
        if self.path.endswith('/'):
            self.path = self.path[:-1]
        self.name = os.path.basename(self.path)
        self.logger = Logger(self.name)
        self.granularity_level = granularity_level
        with open(os.path.join(self.path, config_file_name)) as config_file:
            config = json.load(config_file)
            self.test_command = config['test_command']
            self.target_files = config['target_files']
        Program.clean_tmp_dir(self.tmp_path)
        copy_tree(self.path, self.tmp_path)
        self.contents = Program.parse(self.granularity_level, self.path, self.target_files)
        self.modification_weights = dict()
        self._modification_points = None

    def __str__(self):
        if self.granularity_level == GranularityLevel.LINE:
            code = ''
            for k in sorted(self.contents.keys()):
                idx = 0
                for line in self.contents[k]:
                    code += "{}\t: {}\t: {}\n".format(k, idx, line)
                    idx += 1
            return code
        return self.target_files

    def reset_tmp_dir(self):
        Program.clean_tmp_dir(self.tmp_path)
        copy_tree(self.path, self.tmp_path)

    @property
    def tmp_path(self):
        """
        :return: The path of the temporary dirctory
        :rtype: str
        """
        return os.path.join(Program.TMP_DIR, self.name)

    @property
    def modification_points(self):
        """
        :return: The list of position of modification points for each target program
        :rtype: dict(str, ?)
        """
        assert isinstance(self.granularity_level, GranularityLevel)

        if self._modification_points:
            return self._modification_points

        self._modification_points = dict()
        if self.granularity_level == GranularityLevel.LINE:
            for target_file in self.target_files:
                self._modification_points[target_file] = list(range(len(self.contents[target_file])))
        elif self.granularity_level == GranularityLevel.AST:
            for target_file in self.target_files:
                if Program.is_python_code(target_file):
                    from .helper import stmt_python
                    self._modification_points[target_file] = stmt_python.get_modification_points(
                        self.contents[target_file])
        return self._modification_points

    def select_modification_point(self, target_file, method="random"):
        """
        :param str target_file: The modification point is chosen within target_file
        :param str method: The way how to choose a modification point, *'random'* or *'weighted'*
        :return: The **index** of modification point
        :rtype: int
        """
        import random
        assert target_file in self.target_files
        assert method in ['random', 'weighted']
        candidates = self.modification_points[target_file]
        if method == 'random' or target_file not in self.modification_weights:
            return random.randrange(len(candidates))
        elif method == 'weighted':
            cumulated_weights = sum(self.modification_weights[target_file])
            list_of_prob = list(map(lambda w: float(w)/cumulated_weights, self.modification_weights[target_file]))
            return random.choices(list(range(len(candidates))), weights=list_of_prob, k=1)[0]

    def set_modification_weights(self, target_file, weights):
        """
        :param str target_file: The path to file
        :param weights: The modification weight([0,1]) of each modification points
        :type weights: list(float)
        :return: None
        :rtype: None
        """
        from copy import deepcopy
        assert target_file in self.target_files
        assert len(self.modification_points[target_file]) == len(weights)
        assert not list(filter(lambda w: w < 0 or w > 1, weights))
        self.modification_weights[target_file] = deepcopy(weights)

    def write_to_tmp_dir(self, new_contents):
        """
        Write new contents to the temporary directory of program

        :param new_contents: The new contents of the program.
          Refer to *apply* method of :py:class:`.patch.Patch`
        :type new_contents: dict(str, ?)
        :rtype: None
        """
        for target_file in new_contents:
            with open(os.path.join(self.tmp_path, target_file), 'w') as tmp_file:
                tmp_file.write(Program.to_source(self.granularity_level, new_contents[target_file]))

    def print_modification_points(self, target_file, indices=None):
        """
        Print the source of each modification points

        :param target_file: The path to target file
        :type target_file: str
        :return: None
        :rtype: None
        """
        title_format = "=" * 25 + " {} {} " + "=" * 25
        if not indices:
            indices = range(len(self.modification_points[target_file]))
        if self.granularity_level == GranularityLevel.LINE:
            def print_modification_point(contents, modification_points, i):
                print(title_format.format('line', i))
                print(contents[modification_points[i]])
        elif self.granularity_level == GranularityLevel.AST:
            if Program.is_python_code(target_file):
                def print_modification_point(contents, modification_points, i):
                    import astor
                    from .helper import stmt_python
                    print(title_format.format('node', i))
                    blk, idx = stmt_python.pos_2_block_n_index(contents, modification_points[i])
                    print(astor.to_source(blk[idx]))
        for i in indices:
            print_modification_point(self.contents[target_file], self.modification_points[target_file], i)

    @classmethod
    def to_source(cls, granularity_level, contents_of_file):
        """
        Change contents of file to the source code

        :param granularity_level: The parsing level of the program
        :type granularity_level: :py:class:`GranularityLevel`
        :param contents_of_file: The contents of the file which is the parsed form of source code
        :type contents_of_file: ?
        :return: The source code
        :rtype: str
        """
        if granularity_level == GranularityLevel.LINE:
            return '\n'.join(contents_of_file) + '\n'
        elif granularity_level == GranularityLevel.AST:
            import astor
            return astor.to_source(contents_of_file)
        return ''

    @classmethod
    def clean_tmp_dir(cls, tmp_path):
        """
        Clean the temporary project directory if it exists.

        :param str tmp_path: The path of directory to clean.
        :return: None
        """
        if os.path.exists(tmp_path):
            shutil.rmtree(tmp_path)
        if not os.path.exists(Program.TMP_DIR):
            os.mkdir(Program.TMP_DIR)
        os.mkdir(tmp_path)

    @classmethod
    def parse(cls, granularity_level, path, target_files):
        """
        :param granularity_level: The granularity level of a program
        :type granularity_level: :py:class:`.program.GranularityLevel`
        :param str path: The project root path
        :param target_files: The paths to target files from the project root
        :type target_files: list(str)

        :return: The contents of the files, see `Hint`
        :rtype: dict(str, list(str))

        .. hint::
            - key: the file name
            - value: the contents of the file
        """
        assert isinstance(granularity_level, GranularityLevel)
        if granularity_level == GranularityLevel.LINE:
            contents = {}
            for target in target_files:
                with open(os.path.join(path, target), 'r') as target_file:
                    contents[target] = list(
                        map(str.rstrip, target_file.readlines()))
            return contents
        elif granularity_level == GranularityLevel.AST:
            import ast
            import astor
            contents = {}
            for target in target_files:
                if cls.is_python_code(target):
                    root = astor.parse_file(os.path.join(path, target))
                    contents[target] = root
                else:
                    raise Exception('Program', '{} file is not supported'.format(cls.get_file_extension(target)))
            return contents
        return None

    @staticmethod
    def is_python_code(source_path):
        """
        :param source_path: The path of the source file
        :type source_path: str
        :return: whether the file's extention is *.py* or not
        :rtype: bool
        """
        _, file_extension = os.path.splitext(source_path)
        return file_extension == '.py'

    @staticmethod
    def is_java_code(source_path):
        """
        :param source_path: The path of the source file
        :type source_path: str
        :return: whether the file's extention is *.java* or not
        :rtype: bool
        """
        _, file_extension = os.path.splitext(source_path)
        return file_extension == '.java'

    @staticmethod
    def get_file_extension(file_path):
        """
        :param file_path: The path of file
        :type file_path: str
        :return: file extension
        :rtype: str
        """
        _, file_extension = os.path.splitext(file_path)
        return file_extension

    @staticmethod
    def have_the_same_file_extension(file_path_1, file_path_2):
        """
        :param file_path_1: The path of file 1
        :type file_path_1: str
        :param file_path_2: The path of file 2
        :type file_path_2: str
        :return: same or not
        :rtype: bool
        """
        return Program.get_file_extension(file_path_1) == Program.get_file_extension(file_path_2)

{'stmt_python': 'helper.stmt_python', 'random': 'random', 'deepcopy': 'copy.deepcopy'}.to_source

{'stmt_python': 'helper.stmt_python', 'random': 'random', 'deepcopy': 'copy.deepcopy'}.is_python_code

{'stmt_python': 'helper.stmt_python', 'random': 'random', 'deepcopy': 'copy.deepcopy', 'astor': 'astor'}.TMP_DIR

{'stmt_python': 'helper.stmt_python', 'random': 'random', 'deepcopy': 'copy.deepcopy', 'astor': 'astor', 'ast': 'ast'}.get_file_extension


#!/usr/bin/env python3
import rospy
from std_msgs.msg import Float32MultiArray
from geometry_msgs.msg import TwistStamped, PoseStamped
import numpy as np
from scipy.spatial.transform import Rotation

class RobotState():
    def __init__(self):
        self.state = np.zeros(12)
        rospy.init_node('robot_state_listener', anonymous=True)
        rospy.Subscriber("/mavros/local_position/velocity_body",
                         TwistStamped, self.body_velocity_info)
        rospy.Subscriber("/mavros/local_position/pose",
                         PoseStamped, self.local_pose_info)
        # rospy.Subscriber("/mavros/vision_pose/pose", PoseStamped, self.local_pose_info)
        self.pub = rospy.Publisher(
            '/robot_state', Float32MultiArray, queue_size=10)
        self.robot_state_pub()

    def quaternion_to_euler(self, q):
        # Extract components of the quaternion
        w, x, y, z = q

        # Compute Euler angles using the ZYX convention
        sinr_cosp = 2.0 * (w*x + y*z)
        cosr_cosp = 1.0 - 2.0 * (x*x + y*y)
        roll = np.arctan2(sinr_cosp, cosr_cosp)

        sinp = 2.0 * (w*y - z*x)
        if np.abs(sinp) >= 1:
            pitch = np.copysign(np.pi/2, sinp)
        else:
            pitch = np.arcsin(sinp)

        siny_cosp = 2.0 * (w*z + x*y)
        cosy_cosp = 1.0 - 2.0 * (y*y + z*z)
        yaw = np.arctan2(siny_cosp, cosy_cosp)

        # Return Euler angles in radians
        return np.array([yaw, pitch, roll])

    def body_velocity_info(self, data):
        u, v, w = data.twist.linear.x, data.twist.linear.y, data.twist.linear.z
        lx, ly, lz = data.twist.angular.x, data.twist.angular.y, data.twist.angular.z
        self.state[3:6] = u, v, w
        self.state[9:12] = lx, ly, lz
        data = Float32MultiArray()
        data.data = self.state
        self.pub.publish(data)

    def local_pose_info(self, data):
        q = data.pose.orientation.x, data.pose.orientation.y, data.pose.orientation.z, data.pose.orientation.w
        position = data.pose.position.x, data.pose.position.y, data.pose.position.z
        r = Rotation.from_quat(q)
        self.state[:3] = position
        self.state[6:9] = r.as_euler('XYZ')
        data = Float32MultiArray()
        data.data = self.state
        self.pub.publish(data)

    def robot_state_pub(self):
        rate = rospy.Rate(1)
        while not rospy.is_shutdown():
            rate.sleep()

if __name__ == '__main__':
    RS = RobotState()


import os
import cv2
import numpy as np
import requests
import pyfakewebcam
import traceback
import time

def get_mask(frame, bodypix_url=os.environ.get("BODYPIX_URL","http://bodypix:9000")):
    _, data = cv2.imencode(".jpg", frame)
    r = requests.post(
        url=bodypix_url,
        data=data.tobytes(),
        headers={'Content-Type': 'application/octet-stream'})
    mask = np.frombuffer(r.content, dtype=np.uint8)
    mask = mask.reshape((frame.shape[0], frame.shape[1]))
    return mask

def post_process_mask(mask):
    mask = cv2.dilate(mask, np.ones((10,10), np.uint8) , iterations=1)
    mask = cv2.blur(mask.astype(float), (10,10))
    return mask

def shift_image(img, dx, dy):
    img = np.roll(img, dy, axis=0)
    img = np.roll(img, dx, axis=1)
    if dy>0:
        img[:dy, :] = 0
    elif dy<0:
        img[dy:, :] = 0
    if dx>0:
        img[:, :dx] = 0
    elif dx<0:
        img[:, dx:] = 0
    return img

def hologram_effect(img):
    # add a blue tint
    holo = cv2.applyColorMap(img, cv2.COLORMAP_WINTER)
    # add a halftone effect
    bandLength, bandGap = 2, 3
    for y in range(holo.shape[0]):
        if y % (bandLength+bandGap) < bandLength:
            holo[y,:,:] = holo[y,:,:] * np.random.uniform(0.1, 0.3)
    # add some ghosting
    holo_blur = cv2.addWeighted(holo, 0.2, shift_image(holo.copy(), 5, 5), 0.8, 0)
    holo_blur = cv2.addWeighted(holo_blur, 0.4, shift_image(holo.copy(), -5, -5), 0.6, 0)
    # combine with the original color, oversaturated
    out = cv2.addWeighted(img, 0.5, holo_blur, 0.6, 0)
    return out

def get_frame(cap, background_scaled, speed=True):
    _, frame = cap.read()
    # fetch the mask with retries (the app needs to warmup and we're lazy)
    # e v e n t u a l l y c o n s i s t e n t
    mask = None
    while mask is None:
        try:

            if speed:
                shrinked_frame = cv2.resize(frame, (width//2, height//2))
                shrinked_mask = get_mask(shrinked_frame)
                mask = cv2.resize(shrinked_mask, (width, height))
            else:
                mask = get_mask(frame)

        except requests.RequestException:
            print("mask request failed, retrying")
            traceback.print_exc()
            time.sleep(5)

    # post-process mask and frame
    mask = post_process_mask(mask)
    #frame = hologram_effect(frame)

    # composite the foreground and background
    inv_mask = 1-mask
    for c in range(frame.shape[2]):
       frame[:,:,c] = frame[:,:,c]*mask + background_scaled[:,:,c]*inv_mask

    return frame

if __name__ == '__main__':

    actual_device = os.environ.get('ACTUAL_CAMERA','/dev/video0')
    fake_device = os.environ.get('FAKE_CAMERA','/dev/video20')
    width = int(os.environ.get('CAMERA_WIDTH',640))
    height = int(os.environ.get('CAMERA_HEIGHT',360))
    cam_fps = int(os.environ.get('CAMERA_FPS',24))

    # setup access to the *real* webcam
    cap = cv2.VideoCapture(actual_device)

    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
    cap.set(cv2.CAP_PROP_FPS, cam_fps)

    # setup the fake camera
    fake = pyfakewebcam.FakeWebcam(fake_device, width, height)

    # load the virtual background
    background_file_path = "/data/background.jpg"
    background = cv2.imread(background_file_path)
    background_scaled = cv2.resize(background, (width, height))

    org_background_file_size = os.stat(background_file_path).st_size

    # frames forever
    while True:

        frame = get_frame(cap, background_scaled)
        # fake webcam expects RGB
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        fake.schedule_frame(frame)

        if int(time.time()) % 3 == 0:
            updated_background_file_size = os.stat(background_file_path).st_size
            if updated_background_file_size != org_background_file_size:
                background = cv2.imread(background_file_path)
                background_scaled = cv2.resize(background,(width, height))
                org_background_file_size = updated_background_file_size


from __future__ import print_function, division
from builtins import range, input
# Note: you may need to update your version of future
# sudo pip install -U future
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import copy
from sklearn.decomposition import PCA

def pca_sklearn(Xtrain, Ytrain=None, plot_scatter=False, floydhub=False):
    # pca expects shape of (num_samples, num_features)
    print("Pre-PCA shape:", Xtrain.shape)
    nsamples, nx, ny, channels = Xtrain.shape

    pca_train_dataset = Xtrain.reshape((nsamples, nx*ny*channels))
    print("pca_train_dataset", pca_train_dataset.shape)

    ## Performing PCA
    num_red_dims=0.95 # TODO, make dependent to the  x % acccuracy needed

    print("\nStarting PCA on dataset...")
    pca = PCA(num_red_dims)
    print("pca inited")
    reduced = pca.fit_transform(pca_train_dataset)
    print("Done with PCA on dataset!\n")
    if plot_scatter:
        plt.scatter(reduced[:,0], reduced[:,1], s=100, c=Ytrain, alpha=0.5)
        plt.show()
    # When talking about PCA, the sum of the sample variances of all individual
    # variables is called the total variance. If we divide individual variances
    # by the total variance, we’ll see how much variance each variable explainsself.
    # This is plotted below, and should be held by the leftmost part of the graph,
    # seeing it is sorted in decreased order with regards to variance.
    plt.plot(pca.explained_variance_ratio_)
    if floydhub:
        plt.savefig('/output/PCA_explained_variance_ratio' , bbox_inches='tight')
        plt.clf()
    else:
        plt.show()
    # cumulative variance, vizualising how many principal components is needed to obtain 100% of the variance
    # choose k = number of dimensions that gives us 95-99% variance
    cumulative = []
    last = 0
    for v in pca.explained_variance_ratio_:
        cumulative.append(last + v)
        last = cumulative[-1]
    plt.plot(cumulative)
    if floydhub:
        plt.savefig('/output/PCA_cumulative' , bbox_inches='tight')
        plt.clf()
    else:
        plt.show()

    # Plot the explained variances
    features = range(pca.n_components_)
    plt.bar(features, pca.explained_variance_ratio_, color='black')
    plt.xlabel('PCA features')
    plt.ylabel('variance %')
    plt.xticks(features)
    plt.show()

     # Save components to a DataFrame
    PCA_components = pd.DataFrame(reduced)
    ## we plot the first two dimensjons. We do this to notice if there is any clear clusters
    plt.scatter(PCA_components[0], PCA_components[1], alpha=.1, color='black')
    plt.xlabel('PCA 1')
    plt.ylabel('PCA 2')
    plt.show()

    ### calculate PCA and inverse transform PCA output
    reduced_img = pca.inverse_transform(reduced)
    reduced_reshaped_img = reduced_img.reshape(Xtrain.shape)
    print("reduced.shape", reduced.shape)
    print("reduced_img.shape", reduced_img.shape)
    print("reduced_img.reshape(Xtrain.shape)", reduced_img.reshape(Xtrain.shape).shape)

    plot_PCA=True
    if plot_PCA:

        ### PLot inline before and after
        # plots original random iline
        plt.title("Original image")
        plt.imshow(np.squeeze(Xtrain[0]))
        plt.colorbar()
        plt.show()
        plt.show()
        # plots PCA reduced random inline
        plt.title("PCA transformed image")
        plt.imshow(np.squeeze(reduced_reshaped_img[0]))
        plt.colorbar()

        if floydhub:
            plt.savefig('/output/PCA_reduced_img' , bbox_inches='tight')
            plt.clf()
        else:
            plt.show()

    return reduced_reshaped_img

if __name__ == '__main__':
    #get data to use
    data_cube = np.load("/Users/anderskampenes/Documents/Dokumenter/NTNU/MASTER/code/data/raw/f3-benchmark/test_once/test1_seismic.npy")
    # make sure it is fomrated correctly
    img = data_cube[0:10,:,:].T
    img = np.transpose(img,(2,0,1))
    print(img.shape)
    data = img.reshape(img.shape[0], img.shape[1], img.shape[2],1)
    pca_sklearn(data)


"""Interface for measuring distance between histograms."""

# If we are running the unit tests.
if __name__ == '__main__':
    import sys
    sys.path.insert(1, '..')

import wx

from state.Histogram import histogramDistance

class Selection(wx.Panel):
    def __init__(self, parent, state):
        wx.Panel.__init__(self, parent, -1)
        self.state = state
        self.outputKeys = []

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.output = wx.Choice(self, choices=[])
        self.Bind(wx.EVT_CHOICE, self.onOutput, self.output)
        sizer.Add(self.output, 1, wx.EXPAND | wx.ALL, 5)
        self.species = wx.Choice(self, choices=[])
        sizer.Add(self.species, 1, wx.EXPAND | wx.ALL, 5)
        self.frame = wx.Choice(self, choices=[])
        sizer.Add(self.frame, 1, wx.EXPAND | wx.ALL, 5)
        self.SetSizer(sizer)

        self.refresh()

    def onOutput(self, event):
        self.update()
        event.Skip()

    def update(self):
        index = self.output.GetSelection()
        if index == wx.NOT_FOUND:
            return
        # Check that the simulation output has not disappeared.
        if not self.outputKeys[index] in self.state.output:
            self.refresh()
            return
        output = self.state.output[self.outputKeys[index]]
        modelId = self.outputKeys[index][0]
        model = self.state.models[modelId]

        # The species choice.
        selection = self.species.GetSelection()
        self.species.Clear()
        self.species.Append('All species')
        for i in output.recordedSpecies:
            self.species.Append(model.speciesIdentifiers[i])
        if selection != wx.NOT_FOUND and selection < self.species.GetCount():
            self.species.SetSelection(selection)
        else:
            self.species.SetSelection(0)

        # The frame choice.
        selection = self.frame.GetSelection()
        self.frame.Clear()
        if output.__class__.__name__ == 'HistogramFrames':
            self.frame.Append('All frames')
            for time in output.frameTimes:
                self.frame.Append(str(time))
            self.frame.Enable()
            if selection != wx.NOT_FOUND and selection < self.frame.GetCount():
                self.frame.SetSelection(selection)
            else:
                self.frame.SetSelection(0)
        else:
            self.frame.Disable()

    def histograms(self):
        """Return a list of the selected histograms."""
        index = self.output.GetSelection()
        if index == wx.NOT_FOUND:
            return None
        # Check that the simulation output has not disappeared.
        if not self.outputKeys[index] in self.state.output:
            self.refresh()
            return None
        output = self.state.output[self.outputKeys[index]]

        s = self.species.GetSelection()
        if s == wx.NOT_FOUND:
            return None

        if output.__class__.__name__ == 'HistogramFrames':
            f = self.frame.GetSelection()
            if f == wx.NOT_FOUND:
                return None
            if f == 0:
                if s == 0:
                    result = []
                    for frame in output.histograms:
                        result.extend(frame)
                    return result
                else:
                    result = []
                    for frame in output.histograms:
                        result.append(frame[s-1])
                    return result
            else:
                if s == 0:
                    return output.histograms[f-1]
                else:
                    return [output.histograms[f-1][s-1]]
        elif output.__class__.__name__ == 'HistogramAverage':
            if s == 0:
                return output.histograms
            else:
                return [output.histograms[s-1]]
        else:
            assert(False)

    def refresh(self):
        # Get the histogram outputs.
        self.outputKeys = []
        for key in self.state.output:
            if self.state.output[key].__class__.__name__ in\
                    ('HistogramFrames', 'HistogramAverage'):
                self.outputKeys.append(key)
        outputChoices = [x[0] + ', ' + x[1] for x in self.outputKeys]
        selection = self.output.GetSelection()
        self.output.Clear()
        for choice in outputChoices:
            self.output.Append(choice)
        # Set the selection.
        if selection != wx.NOT_FOUND and selection < self.output.GetCount():
            self.output.SetSelection(selection)
        else:
            self.output.SetSelection(0)
        # Updated the species and frame for this output.
        self.update()

class HistogramDistance(wx.Frame):
    def __init__(self, state, title, parent=None):
        wx.Frame.__init__(self, parent, -1, title)
        self.state = state

        # Selections.
        selectionsSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.selections = [Selection(self, state), Selection(self, state)]
        for s in self.selections:
            selectionsSizer.Add(s, 1, wx.EXPAND | wx.ALL, 5)
            self.Bind(wx.EVT_CHOICE, self.onModification, s.output)
            self.Bind(wx.EVT_CHOICE, self.onModification, s.species)
            self.Bind(wx.EVT_CHOICE, self.onModification, s.frame)
        sizer = wx.BoxSizer(wx.VERTICAL)
        # Don't expand in the vertical direction.
        sizer.Add(selectionsSizer, 0, wx.EXPAND | wx.ALIGN_TOP, 5)

        # Distance.
        self.distance = wx.StaticText(self, -1, '')
        sizer.Add(self.distance, 0, wx.ALIGN_TOP, 5)

        self.SetSizer(sizer)
        self.Fit()
        self.SetSize(self.GetBestSize())
        self.computeDistance()
        # Intercept the close event.
        self.Bind(wx.EVT_CLOSE, self.onClose)

    def onClose(self, event):
        # If there is a parent, it stores a dictionary of these frames.
        if self.GetParent():
            del self.GetParent().children[self.GetId()]
        self.Destroy()

    def refresh(self):
        # CONTINUE: Store the current selections.
        for s in self.selections:
            s.refresh()
        self.computeDistance()

    def onModification(self, event):
        self.computeDistance()

    def computeDistance(self):
        self.distance.SetLabel('')
        a = self.selections[0].histograms()
        b = self.selections[1].histograms()
        if not (a and b):
            return
        if len(a) != len(b):
            return
        distance = 0.
        for i in range(len(a)):
            distance += histogramDistance(a[i], b[i])
        distance /= len(a)
        if (len(a) == 1):
            self.distance.SetLabel(' Distance = ' + str(distance))
        else:
            self.distance.SetLabel(' Average distance = ' + str(distance))

def main():
    import sys
    sys.path.insert(1, '..')
    from random import uniform

    from state.Model import Model
    from state.Histogram import Histogram
    from state.HistogramFrames import HistogramFrames

    # A histogram.
    numberOfBins = 4
    multiplicity = 2

    # Simulation output.
    frameTimes = [0, 1]
    recordedSpecies = [0, 1, 2]
    hf = HistogramFrames(numberOfBins, multiplicity, recordedSpecies)
    hf.setFrameTimes(frameTimes)
    for i in range(len(frameTimes)):
        for j in range(len(recordedSpecies)):
            h = Histogram(numberOfBins, multiplicity)
            h.setCurrentToMinimum()
            for b in range(numberOfBins):
                h.accumulate(b, uniform(0., 1.))
            hf.histograms[i][j].merge(h)

    # The model.
    model = Model()
    model.speciesIdentifiers = ['s1', 's2', 's3']

    # The state.
    class TestState:
        pass
    state = TestState()
    state.models = {}
    state.models['model'] = model
    state.output = {}
    state.output[('model', 'method')] = hf

    app = wx.PySimpleApp()
    HistogramDistance(state, 'Histogram distace').Show()
    app.MainLoop()

if __name__ == '__main__':
    main()