from django.shortcuts import render, redirect, HttpResponse
from django.core.urlresolvers import reverse
from django.contrib import messages
from .models import User, Friend, Friendship

# Create your views here.
def index(request):
    # Friendship.objects.all().delete()
    # this = Friend.objects.filter(id=10)
    # this.delete()
    id = request.session['thisUser']
    # print(id)
    context = {
    'users':User.objects.all(),
    'thisUser':User.objects.get(id=id),
    'friends':Friend.objects.all(),
    'friendships':Friendship.objects.all(),
    }
    if ('newUser' in request.session) == True:
        # print ('new registration')
        verify = Friend.friendManager.create(id)
        if verify[0] == True:
            # print ('added to friend table')
            del request.session['newUser']
            request.session.modified = True
            return render(request, 'friends/index.html', context)
    else:
        # print ('already registered')
        return render(request, 'friends/index.html', context)

def add(request):
    if request.method == "POST":
        verify = Friend.friendManager.bridge_connections(request.POST)

        if verify == True:
            # print ('friendship created')
            return redirect(reverse('friends:index'))
        else:
            print ('something went wrong')
            return redirect(reverse('friends:index'))

def show(request, id):
    if request.method == "GET":
        context = {
            'user':User.objects.get(id=id)
        }
        return render(request, 'friends/show.html', context)

def remove(request):
    if request.method == "POST":
        destroy = Friend.friendManager.delete(request.POST)
        if destroy == True:
            return redirect(reverse('friends:index'))


#!/usr/local/bin/python3

from detector import HeroDetect
from detector import Util
from collections import deque, Counter
import cv2
import itertools
# from skvideo.io import VideoCapture
from cv2 import VideoCapture

# if __name__ == '__main__':
heroDetect = HeroDetect(input_shape=Util.shape_hero())
heroDetect.load_model(
    model_path='./model/v5.hero.cnn_vgg_dropout.iter0.model.h5',
    label_path='./model/v5.hero.cnn_vgg_dropout.iter0.label.txt')

# video_path = './data/raw_test/bailishouyue/q0532r8l8bq.p712.1.mp4'
# video_path = './data/raw_test/liubang/k0391sd2c3j.p712.1.mp4'
# video_path = './data/raw_test/huamulan/y0382qw3lsj.p712.1.mp4'
# video_path = './data/raw_test/random/d055299tzgr.p712.1.mp4'
# video_path = './data/raw_test/1.mp4'
video_path = './data/raw_test/zhuangzhou/s0535trpsob.p712.1.mp4'

cap = VideoCapture(video_path)
fps = int(cap.get(cv2.CAP_PROP_FPS))
count = 0
sample_sec = 1
begin_sec = 60
votes = {} # {(i,j):deque([label], maxlen=10)}
color = (0, 255, 0)

key = None
while key != 120: # press x to stop
    ret, frame = cap.read()
    if not ret:
        break
    # display key frame ervery n second
    count += 1
    if count % int(sample_sec * fps) == 0 and count / fps > begin_sec:
        # windows = [(i, -1) for i in range(-3,5)] + [(i, 0) for i in range(-4,5)] + [(i, 1) for i in range(-4,4)]
        # windows = itrtools.product(range(-3,4), range(-1,2))
        windows = [(0, 0)]
        for i, j in windows:
            text_coord = '%d,%d' % (i,j)
            x1, y1, x2, y2 = Util.rect_grid_hero(frame, i, j)
            cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
            cv2.putText(img=frame,
                text=text_coord,
                org=(x1, y1 + 15),
                fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                fontScale=.5,
                color=color,
                thickness=1)

            # prediction
            image = Util.crop_grid_hero(frame, i, j)
            label, prob = heroDetect.predict_image(image)
            votes.setdefault((i,j), deque([], maxlen=1))
            votes[(i, j)].append(label if prob > 0.5 else 'NA')

            most_common = Counter(votes[(i, j)]).most_common(n=1)[0] # ('hero', 0.2)
            text_vote = '%s %d' % (most_common[0], most_common[1])

            cv2.putText(img=frame,
                text=text_vote,
                org=(x1, y1 + 30),
                fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                fontScale=.5,
                color=color,
                thickness=1)

            if i == j == 0:
                print('debug label, prob:', label, prob)

        # print('votes:', votes)

        cv2.imshow(' Press "x" to close window. Press anykey to next frame.', frame)
        key = cv2.waitKey(0)
        cv2.destroyAllWindows()
cap.release()


import cv2
import numpy as np
from PIL import Image
from tqdm import tqdm
import glob, os

def grabcut(image, flip=False):
    h, w, b = 192, 192, 32
    if flip:
        image = np.flip(np.flip(image, 1), 0)
    mask = np.zeros(image.shape[:2],np.uint8)

    bgdModel = np.zeros((1,65),np.float64)
    fgdModel = np.zeros((1,65),np.float64)
    rect = (b,b,h+b,w+b)
    cv2.grabCut(image,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
    mask = mask[b:h+b,b:w+b]
    mask = np.where((mask==2)|(mask==0),0,1).astype('uint8')

    if flip:
        mask = np.flip(np.flip(mask, 1), 0)
    return mask

def implement(image_root, mask_root):
    h, w, b = 192, 192, 32

    dirs = ['image_train', 'image_test', 'image_query']
    for d in dirs:
        input_dir = os.path.join(image_root, d)
        output_dir = os.path.join(mask_root, d)
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
        filenames = glob.glob(os.path.join(input_dir, '*.jpg'))

        print('start processing the images in %s (totally %i images)'%(input_dir, len(filenames)))
        print('generated foreground mask would be stored in %s'%output_dir)
        pbar = tqdm(total=len(filenames))
        for filename in filenames:
            image = Image.open(filename)
            image = np.array(image.resize((h,w)))
            image = cv2.copyMakeBorder(image,b,b,b,b,cv2.BORDER_REPLICATE)
            mask1 = grabcut(image)
            mask2 = grabcut(image, flip=True)
            mask = mask1*mask2
            cv2.imwrite(filename.replace(image_root, mask_root), mask*255)

            pbar.update(1)
        pbar.close()
    return


# pragma pylint: disable=too-many-instance-attributes, pointless-string-statement

"""
This module contains the hyperopt logic
"""

import json
import logging
import os
import pickle
import signal
import sys
from argparse import Namespace
from functools import reduce
from math import exp
from operator import itemgetter
from typing import Dict, Any, Callable

import numpy
import talib.abstract as ta
from hyperopt import STATUS_FAIL, STATUS_OK, Trials, fmin, hp, space_eval, tpe
from hyperopt.mongoexp import MongoTrials
from pandas import DataFrame

import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.optimize import load_data
from freqtrade.optimize.backtesting import Backtesting
from user_data.hyperopt_conf import hyperopt_optimize_conf

logger = logging.getLogger(__name__)

class Hyperopt(Backtesting):
    """
    Hyperopt class, this class contains all the logic to run a hyperopt simulation

    To run a backtest:
    hyperopt = Hyperopt(config)
    hyperopt.start()
    """
    def __init__(self, config: Dict[str, Any]) -> None:

        super().__init__(config)
        # set TARGET_TRADES to suit your number concurrent trades so its realistic
        # to the number of days
        self.target_trades = 600
        self.total_tries = config.get('epochs', 0)
        self.current_tries = 0
        self.current_best_loss = 100

        # max average trade duration in minutes
        # if eval ends with higher value, we consider it a failed eval
        self.max_accepted_trade_duration = 300

        # this is expexted avg profit * expected trade count
        # for example 3.5%, 1100 trades, self.expected_max_profit = 3.85
        # check that the reported Σ% values do not exceed this!
        self.expected_max_profit = 3.0

        # Configuration and data used by hyperopt
        self.processed = None

        # Hyperopt Trials
        self.trials_file = os.path.join('user_data', 'hyperopt_trials.pickle')
        self.trials = Trials()

    @staticmethod
    def populate_indicators(dataframe: DataFrame) -> DataFrame:
        """
        Adds several different TA indicators to the given DataFrame
        """
        dataframe['adx'] = ta.ADX(dataframe)
        dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
        dataframe['cci'] = ta.CCI(dataframe)
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        dataframe['mfi'] = ta.MFI(dataframe)
        dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
        dataframe['minus_di'] = ta.MINUS_DI(dataframe)
        dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
        dataframe['plus_di'] = ta.PLUS_DI(dataframe)
        dataframe['roc'] = ta.ROC(dataframe)
        dataframe['rsi'] = ta.RSI(dataframe)
        # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
        rsi = 0.1 * (dataframe['rsi'] - 50)
        dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1)
        # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
        dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
        # Stoch
        stoch = ta.STOCH(dataframe)
        dataframe['slowd'] = stoch['slowd']
        dataframe['slowk'] = stoch['slowk']
        # Stoch fast
        stoch_fast = ta.STOCHF(dataframe)
        dataframe['fastd'] = stoch_fast['fastd']
        dataframe['fastk'] = stoch_fast['fastk']
        # Stoch RSI
        stoch_rsi = ta.STOCHRSI(dataframe)
        dataframe['fastd_rsi'] = stoch_rsi['fastd']
        dataframe['fastk_rsi'] = stoch_rsi['fastk']
        # Bollinger bands
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']
        # EMA - Exponential Moving Average
        dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
        dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
        dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
        dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
        # SAR Parabolic
        dataframe['sar'] = ta.SAR(dataframe)
        # SMA - Simple Moving Average
        dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
        # TEMA - Triple Exponential Moving Average
        dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
        # Hilbert Transform Indicator - SineWave
        hilbert = ta.HT_SINE(dataframe)
        dataframe['htsine'] = hilbert['sine']
        dataframe['htleadsine'] = hilbert['leadsine']

        # Pattern Recognition - Bullish candlestick patterns
        # ------------------------------------
        """
        # Hammer: values [0, 100]
        dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
        # Inverted Hammer: values [0, 100]
        dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
        # Dragonfly Doji: values [0, 100]
        dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
        # Piercing Line: values [0, 100]
        dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
        # Morningstar: values [0, 100]
        dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
        # Three White Soldiers: values [0, 100]
        dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
        """

        # Pattern Recognition - Bearish candlestick patterns
        # ------------------------------------
        """
        # Hanging Man: values [0, 100]
        dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
        # Shooting Star: values [0, 100]
        dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
        # Gravestone Doji: values [0, 100]
        dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
        # Dark Cloud Cover: values [0, 100]
        dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
        # Evening Doji Star: values [0, 100]
        dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
        # Evening Star: values [0, 100]
        dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
        """

        # Pattern Recognition - Bullish/Bearish candlestick patterns
        # ------------------------------------
        """
        # Three Line Strike: values [0, -100, 100]
        dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
        # Spinning Top: values [0, -100, 100]
        dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
        # Engulfing: values [0, -100, 100]
        dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
        # Harami: values [0, -100, 100]
        dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
        # Three Outside Up/Down: values [0, -100, 100]
        dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
        # Three Inside Up/Down: values [0, -100, 100]
        dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
        """

        # Chart type
        # ------------------------------------
        # Heikinashi stategy
        heikinashi = qtpylib.heikinashi(dataframe)
        dataframe['ha_open'] = heikinashi['open']
        dataframe['ha_close'] = heikinashi['close']
        dataframe['ha_high'] = heikinashi['high']
        dataframe['ha_low'] = heikinashi['low']

        return dataframe

    def save_trials(self) -> None:
        """
        Save hyperopt trials to file
        """
        logger.info('Saving Trials to \'%s\'', self.trials_file)
        pickle.dump(self.trials, open(self.trials_file, 'wb'))

    def read_trials(self) -> Trials:
        """
        Read hyperopt trials file
        """
        logger.info('Reading Trials from \'%s\'', self.trials_file)
        trials = pickle.load(open(self.trials_file, 'rb'))
        os.remove(self.trials_file)
        return trials

    def log_trials_result(self) -> None:
        """
        Display Best hyperopt result
        """
        vals = json.dumps(self.trials.best_trial['misc']['vals'], indent=4)
        results = self.trials.best_trial['result']['result']
        logger.info('Best result:\n%s\nwith values:\n%s', results, vals)

    def log_results(self, results) -> None:
        """
        Log results if it is better than any previous evaluation
        """
        if results['loss'] < self.current_best_loss:
            self.current_best_loss = results['loss']
            log_msg = '\n{:5d}/{}: {}. Loss {:.5f}'.format(
                results['current_tries'],
                results['total_tries'],
                results['result'],
                results['loss']
            )
            print(log_msg)
        else:
            print('.', end='')
            sys.stdout.flush()

    def calculate_loss(self, total_profit: float, trade_count: int, trade_duration: float) -> float:
        """
        Objective function, returns smaller number for more optimal results
        """
        trade_loss = 1 - 0.25 * exp(-(trade_count - self.target_trades) ** 2 / 10 ** 5.8)
        profit_loss = max(0, 1 - total_profit / self.expected_max_profit)
        duration_loss = 0.4 * min(trade_duration / self.max_accepted_trade_duration, 1)
        return trade_loss + profit_loss + duration_loss

    @staticmethod
    def generate_roi_table(params: Dict) -> Dict[int, float]:
        """
        Generate the ROI table thqt will be used by Hyperopt
        """
        roi_table = {}
        roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3']
        roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2']
        roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1']
        roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0

        return roi_table

    @staticmethod
    def roi_space() -> Dict[str, Any]:
        """
        Values to search for each ROI steps
        """
        return {
            'roi_t1': hp.quniform('roi_t1', 10, 120, 20),
            'roi_t2': hp.quniform('roi_t2', 10, 60, 15),
            'roi_t3': hp.quniform('roi_t3', 10, 40, 10),
            'roi_p1': hp.quniform('roi_p1', 0.01, 0.04, 0.01),
            'roi_p2': hp.quniform('roi_p2', 0.01, 0.07, 0.01),
            'roi_p3': hp.quniform('roi_p3', 0.01, 0.20, 0.01),
        }

    @staticmethod
    def stoploss_space() -> Dict[str, Any]:
        """
        Stoploss Value to search
        """
        return {
            'stoploss': hp.quniform('stoploss', -0.5, -0.02, 0.02),
        }

    @staticmethod
    def indicator_space() -> Dict[str, Any]:
        """
        Define your Hyperopt space for searching strategy parameters
        """
        return {
            'macd_below_zero': hp.choice('macd_below_zero', [
                {'enabled': False},
                {'enabled': True}
            ]),
            'mfi': hp.choice('mfi', [
                {'enabled': False},
                {'enabled': True, 'value': hp.quniform('mfi-value', 10, 25, 5)}
            ]),
            'fastd': hp.choice('fastd', [
                {'enabled': False},
                {'enabled': True, 'value': hp.quniform('fastd-value', 15, 45, 5)}
            ]),
            'adx': hp.choice('adx', [
                {'enabled': False},
                {'enabled': True, 'value': hp.quniform('adx-value', 20, 50, 5)}
            ]),
            'rsi': hp.choice('rsi', [
                {'enabled': False},
                {'enabled': True, 'value': hp.quniform('rsi-value', 20, 40, 5)}
            ]),
            'uptrend_long_ema': hp.choice('uptrend_long_ema', [
                {'enabled': False},
                {'enabled': True}
            ]),
            'uptrend_short_ema': hp.choice('uptrend_short_ema', [
                {'enabled': False},
                {'enabled': True}
            ]),
            'over_sar': hp.choice('over_sar', [
                {'enabled': False},
                {'enabled': True}
            ]),
            'green_candle': hp.choice('green_candle', [
                {'enabled': False},
                {'enabled': True}
            ]),
            'uptrend_sma': hp.choice('uptrend_sma', [
                {'enabled': False},
                {'enabled': True}
            ]),
            'trigger': hp.choice('trigger', [
                {'type': 'lower_bb'},
                {'type': 'lower_bb_tema'},
                {'type': 'faststoch10'},
                {'type': 'ao_cross_zero'},
                {'type': 'ema3_cross_ema10'},
                {'type': 'macd_cross_signal'},
                {'type': 'sar_reversal'},
                {'type': 'ht_sine'},
                {'type': 'heiken_reversal_bull'},
                {'type': 'di_cross'},
            ]),
        }

    def has_space(self, space: str) -> bool:
        """
        Tell if a space value is contained in the configuration
        """
        if space in self.config['spaces'] or 'all' in self.config['spaces']:
            return True
        return False

    def hyperopt_space(self) -> Dict[str, Any]:
        """
        Return the space to use during Hyperopt
        """
        spaces = {}
        if self.has_space('buy'):
            spaces = {**spaces, **Hyperopt.indicator_space()}
        if self.has_space('roi'):
            spaces = {**spaces, **Hyperopt.roi_space()}
        if self.has_space('stoploss'):
            spaces = {**spaces, **Hyperopt.stoploss_space()}
        return spaces

    @staticmethod
    def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
        """
        Define the buy strategy parameters to be used by hyperopt
        """
        def populate_buy_trend(dataframe: DataFrame) -> DataFrame:
            """
            Buy strategy Hyperopt will build and use
            """
            conditions = []
            # GUARDS AND TRENDS
            if 'uptrend_long_ema' in params and params['uptrend_long_ema']['enabled']:
                conditions.append(dataframe['ema50'] > dataframe['ema100'])
            if 'macd_below_zero' in params and params['macd_below_zero']['enabled']:
                conditions.append(dataframe['macd'] < 0)
            if 'uptrend_short_ema' in params and params['uptrend_short_ema']['enabled']:
                conditions.append(dataframe['ema5'] > dataframe['ema10'])
            if 'mfi' in params and params['mfi']['enabled']:
                conditions.append(dataframe['mfi'] < params['mfi']['value'])
            if 'fastd' in params and params['fastd']['enabled']:
                conditions.append(dataframe['fastd'] < params['fastd']['value'])
            if 'adx' in params and params['adx']['enabled']:
                conditions.append(dataframe['adx'] > params['adx']['value'])
            if 'rsi' in params and params['rsi']['enabled']:
                conditions.append(dataframe['rsi'] < params['rsi']['value'])
            if 'over_sar' in params and params['over_sar']['enabled']:
                conditions.append(dataframe['close'] > dataframe['sar'])
            if 'green_candle' in params and params['green_candle']['enabled']:
                conditions.append(dataframe['close'] > dataframe['open'])
            if 'uptrend_sma' in params and params['uptrend_sma']['enabled']:
                prevsma = dataframe['sma'].shift(1)
                conditions.append(dataframe['sma'] > prevsma)

            # TRIGGERS
            triggers = {
                'lower_bb': (
                    dataframe['close'] < dataframe['bb_lowerband']
                ),
                'lower_bb_tema': (
                    dataframe['tema'] < dataframe['bb_lowerband']
                ),
                'faststoch10': (qtpylib.crossed_above(
                    dataframe['fastd'], 10.0
                )),
                'ao_cross_zero': (qtpylib.crossed_above(
                    dataframe['ao'], 0.0
                )),
                'ema3_cross_ema10': (qtpylib.crossed_above(
                    dataframe['ema3'], dataframe['ema10']
                )),
                'macd_cross_signal': (qtpylib.crossed_above(
                    dataframe['macd'], dataframe['macdsignal']
                )),
                'sar_reversal': (qtpylib.crossed_above(
                    dataframe['close'], dataframe['sar']
                )),
                'ht_sine': (qtpylib.crossed_above(
                    dataframe['htleadsine'], dataframe['htsine']
                )),
                'heiken_reversal_bull': (
                    (qtpylib.crossed_above(dataframe['ha_close'], dataframe['ha_open'])) &
                    (dataframe['ha_low'] == dataframe['ha_open'])
                ),
                'di_cross': (qtpylib.crossed_above(
                    dataframe['plus_di'], dataframe['minus_di']
                )),
            }
            conditions.append(triggers.get(params['trigger']['type']))

            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'buy'] = 1

            return dataframe

        return populate_buy_trend

    def generate_optimizer(self, params: Dict) -> Dict:
        if self.has_space('roi'):
            self.analyze.strategy.minimal_roi = self.generate_roi_table(params)

        if self.has_space('buy'):
            self.populate_buy_trend = self.buy_strategy_generator(params)

        if self.has_space('stoploss'):
            self.analyze.strategy.stoploss = params['stoploss']

        results = self.backtest(
            {
                'stake_amount': self.config['stake_amount'],
                'processed': self.processed,
                'realistic': self.config.get('realistic_simulation', False),
            }
        )
        result_explanation = self.format_results(results)

        total_profit = results.profit_percent.sum()
        trade_count = len(results.index)
        trade_duration = results.duration.mean()

        if trade_count == 0 or trade_duration > self.max_accepted_trade_duration:
            print('.', end='')
            return {
                'status': STATUS_FAIL,
                'loss': float('inf')
            }

        loss = self.calculate_loss(total_profit, trade_count, trade_duration)

        self.current_tries += 1

        self.log_results(
            {
                'loss': loss,
                'current_tries': self.current_tries,
                'total_tries': self.total_tries,
                'result': result_explanation,
            }
        )

        return {
            'loss': loss,
            'status': STATUS_OK,
            'result': result_explanation,
        }

    @staticmethod
    def format_results(results: DataFrame) -> str:
        """
        Return the format result in a string
        """
        return ('{:6d} trades. Avg profit {: 5.2f}%. '
                'Total profit {: 11.8f} BTC ({:.4f}Σ%). Avg duration {:5.1f} mins.').format(
                    len(results.index),
                    results.profit_percent.mean() * 100.0,
                    results.profit_BTC.sum(),
                    results.profit_percent.sum(),
                    results.duration.mean(),
                )

    def start(self) -> None:
        timerange = Arguments.parse_timerange(self.config.get('timerange'))
        data = load_data(
            datadir=self.config.get('datadir'),
            pairs=self.config['exchange']['pair_whitelist'],
            ticker_interval=self.ticker_interval,
            timerange=timerange
        )

        if self.has_space('buy'):
            self.analyze.populate_indicators = Hyperopt.populate_indicators
        self.processed = self.tickerdata_to_dataframe(data)

        if self.config.get('mongodb'):
            logger.info('Using mongodb ...')
            logger.info(
                'Start scripts/start-mongodb.sh and start-hyperopt-worker.sh manually!'
            )

            db_name = 'freqtrade_hyperopt'
            self.trials = MongoTrials(
                arg='mongo://127.0.0.1:1234/{}/jobs'.format(db_name),
                exp_key='exp1'
            )
        else:
            logger.info('Preparing Trials..')
            signal.signal(signal.SIGINT, self.signal_handler)
            # read trials file if we have one
            if os.path.exists(self.trials_file) and os.path.getsize(self.trials_file) > 0:
                self.trials = self.read_trials()

                self.current_tries = len(self.trials.results)
                self.total_tries += self.current_tries
                logger.info(
                    'Continuing with trials. Current: %d, Total: %d',
                    self.current_tries,
                    self.total_tries
                )

        try:
            best_parameters = fmin(
                fn=self.generate_optimizer,
                space=self.hyperopt_space(),
                algo=tpe.suggest,
                max_evals=self.total_tries,
                trials=self.trials
            )

            results = sorted(self.trials.results, key=itemgetter('loss'))
            best_result = results[0]['result']

        except ValueError:
            best_parameters = {}
            best_result = 'Sorry, Hyperopt was not able to find good parameters. Please ' \
                          'try with more epochs (param: -e).'

        # Improve best parameter logging display
        if best_parameters:
            best_parameters = space_eval(
                self.hyperopt_space(),
                best_parameters
            )

        logger.info('Best parameters:\n%s', json.dumps(best_parameters, indent=4))
        if 'roi_t1' in best_parameters:
            logger.info('ROI table:\n%s', self.generate_roi_table(best_parameters))

        logger.info('Best Result:\n%s', best_result)

        # Store trials result to file to resume next time
        self.save_trials()

    def signal_handler(self, sig, frame) -> None:
        """
        Hyperopt SIGINT handler
        """
        logger.info(
            'Hyperopt received %s',
            signal.Signals(sig).name
        )

        self.save_trials()
        self.log_trials_result()
        sys.exit(0)

def start(args: Namespace) -> None:
    """
    Start Backtesting script
    :param args: Cli args from Arguments()
    :return: None
    """

    # Remove noisy log messages
    logging.getLogger('hyperopt.mongoexp').setLevel(logging.WARNING)
    logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING)

    # Initialize configuration
    # Monkey patch the configuration with hyperopt_conf.py
    configuration = Configuration(args)
    logger.info('Starting freqtrade in Hyperopt mode')

    optimize_config = hyperopt_optimize_conf()
    config = configuration._load_common_config(optimize_config)
    config = configuration._load_backtesting_config(config)
    config = configuration._load_hyperopt_config(config)
    config['exchange']['key'] = ''
    config['exchange']['secret'] = ''

    # Initialize backtesting object
    hyperopt = Hyperopt(config)
    hyperopt.start()


import requests
from bs4 import BeautifulSoup as bs

lists = ["피카츄", "파이리", "꼬부기", "오쪼라고"]
for list in lists:
    result = requests.get("https://ko.wikipedia.org/wiki/" + list)
    soup = bs(result.text, 'html.parser')
    ps = soup.select(".mw-parser-output p")
    print(list)

    try:
        print(ps[0].text)
    except:
        print("정보없음")

    print("\n\n")


# DEPRECATED FILE DONT USE

# V2.3

# fixed Lynette Dash fix
# original script https://github.com/leotorrez/LeoTools/blob/main/FixLynetteDash.py
# fixed by z3phyn

# original fix https://discord.com/channels/971945032552697897/995556578675671121/1149641642505142312
# fixed fixed by LeoTorreZ <3 Thanks Zlevir for playtesting

import os
import re
import argparse

folder_path = "."
def process_folder(folder_path):
    parser = argparse.ArgumentParser()
    parser.add_argument("-r", "--recursive", action="store_true",  default=False)
    args = parser.parse_args()
    for filename in [filename for filename in os.listdir(folder_path) if "disabled" not in filename.lower()]:
        file_path = os.path.join(folder_path, filename)
        disabled_path = os.path.join(folder_path, "DISABLED_brokendash" + filename)

        if args.recursive and os.path.isdir(file_path):
            process_folder(file_path)
        elif filename.endswith('.ini'):
            with open(file_path, "r") as file:
                lines = file.readlines()
            os.rename(file_path, disabled_path)
            with open(file_path, "w") as file:
                bodyFound = False
                extraFound = False
                firstShaderReplace = True
                for line in lines:
                    if re.search("LynetteExtra\]", line):
                        bodyFound = False
                        extraFound = True
                    elif re.search("LynetteBody\]", line):
                        bodyFound = True
                        extraFound = False
                    elif re.search("\[", line):
                        bodyFound = False
                        extraFound = False
                    if re.search("(^\t?)ps-t1 = ", line):
                        value = line.strip().split(" = ")[1]
                        file.write("if ps-t1 == 943068897\n")
                        if extraFound:
                            file.write("    ps-t1 = ResourceLynetteDash\n")
                        else:
                            file.write(";    ps-t1 = ResourceLynetteDash\n")
                        file.write("else if ps-t1 != 857068213\n")
                        file.write(f"    ps-t1 = {value}\n")
                        if not (bodyFound or extraFound):
                            file.write("endif\n")
                    elif re.search("ps-t2 = ", line):
                        if bodyFound or extraFound:
                            value = line.strip().split(" = ")[1]
                            file.write(f"    ps-t2 = {value}\n")
                            file.write("endif\n")
                    elif re.search("ps-t3 = ", line):
                        file.write("")
                    elif re.search(" = reference", line):
                        if "Head" in line or "Dress" in line:
                            line = line.replace("ps-t1", "ps-t0")
                            line = line.replace("ps-t2", "ps-t1")
                        file.write(line)
                    elif re.search("\[ShaderRegexCharReflection\]", line) and firstShaderReplace == True:
                        firstShaderReplace=False
                        file.write(
r"""[ShaderOverrideSprint]
hash = fd8af14fb718d258
run = CommandListSprint
allow_duplicate_hash=overrule

[CommandListSprint]
if $CharacterIB != 0
    if $CharacterIB == 1
        ps-t0 = copy ResourceRefHeadLightMap
    else if $CharacterIB == 2
        ps-t0 = copy ResourceRefBodyDiffuse
    else if $CharacterIB == 3
        ps-t0 = copy ResourceRefDressLightMap
    else if $CharacterIB == 4
        ps-t0 = copy ResourceRefExtraDiffuse
    endif
drawindexed=auto
$CharacterIB = 0
endif

[TextureOverrideLynetteDash]
hash = b3aaa524
match_priority = 65465
filter_index = 943068897
ResourceLynetteDash = ps-t1

[ResourceLynetteDash]

[ShaderRegexCharReflection]
""")
                    else:
                        file.write(line)
            with open(file_path, 'r') as f:
                s = f.read()
            s = s.replace(r"""[ShaderRegexCharOutline]
shader_model = ps_5_0
run = CommandListOutline
[ShaderRegexCharOutline.pattern]
mov o\d+\.\w+, l\(\d+\)\n
mov o\d+\.\w+, r\d+\.\w+\n
mov o\d+\.\w+, l\(\d+\.\d+\)
""", r"""[ShaderOverrideOutline]
hash=6ce92f3bcc9c03d0
run = CommandListOutline
allow_duplicate_hash=overrule""")
            with open(file_path, 'w') as f:
                f.write(s)

if __name__ == "__main__":
    prompt = input("This script will fix Lynette Dash for you. YOU MUST run AGMG3ShaderFix 37.py before hand. Do you want to continue? (y/n) ")
    if prompt.lower() == "y":
        process_folder(folder_path)
        print("Done! Make sure to install Yelan Dash fix in ShaderFixes")
    else:
        print("Canceled. Exiting...")

deprecated - Do not use in the latest versions/FixLynetteDash.py


from core.modules import *

class PropertyFinder(object):

    def __init__(self):
        self.capabilities_location = os.path.join(os.getcwd(), 'core', 'capabilities.ini')
        self.android_capabilities = {}
        self.mobile_driver = None
        self.driver_uri = None
        self.set_capabilities()
        self.get_app_location()
        self.start_driver()

    def set_capabilities(self):
        capabilities = ConfigParser()
        capabilities.optionxform = str
        capabilities.read(self.capabilities_location)
        for section in capabilities.sections():
            for option in capabilities.options(section):
                if section == "Android":
                    self.android_capabilities[option] = capabilities.get(section, option)
                if option == 'android_server':
                    self.driver_uri = capabilities.get(section, option)

    def start_driver(self):
        self.mobile_driver = app.Remote(self.driver_uri, self.android_capabilities)

    def get_app_location(self):
        self.android_capabilities['app'] = os.path.join(os.getcwd(), 'core', 'apps', self.android_capabilities['app'])


# Various attempts at improving the WAE
import tensorflow as tf
import numpy as np
import wae
import os
import logging
from models import encoder, decoder
from datahandler import datashapes
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

def improved_sampling(opts):
    NUM_ROWS = 10
    NUM_COLS = 10
    NUM_GD_STEPS = 100000

    num_z = NUM_ROWS * NUM_COLS
    checkpoint = opts['checkpoint']
    with tf.Session() as sess:
        with sess.graph.as_default():

            z = tf.get_variable(
                "latent_codes", [num_z, opts['zdim']],
                tf.float32, tf.random_normal_initializer(stddev=1.))
            is_training_ph = tf.placeholder(tf.bool, name='is_training_ph')
            gen, _ = decoder(opts, z, is_training=is_training_ph)
            data_shape = datashapes[opts['dataset']]
            gen.set_shape([num_z] + data_shape)
            e_gen, _ = encoder(opts, gen, is_training=is_training_ph)
            if opts['e_noise'] == 'gaussian':
                e_gen = e_gen[0]
            ae_gen = decoder(opts, e_gen, reuse=True, is_training=is_training_ph)
            loss = wae.WAE.reconstruction_loss(opts, gen, ae_gen)
            # optim = tf.train.AdamOptimizer(0.001, 0.9)
            optim = tf.train.AdamOptimizer(0.01, 0.9)
            optim = optim.minimize(loss, var_list=[z])

            # Now restoring weights from the checkpoint
            # We need to restore all variables except for newly created ones
            all_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
            enc_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                                         scope='encoder')
            dec_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                                         scope='generator')
            new_vars = [v for v in all_vars if \
                        v not in enc_vars and v not in dec_vars]
            vars_to_restore = enc_vars + dec_vars
            saver = tf.train.Saver(vars_to_restore)
            saver.restore(sess, checkpoint)
            logging.error('Restored.')

            init = tf.variables_initializer(new_vars)
            for iteration in range(1):
                pic_id = 0
                loss_prev = 1e10
                init.run()
                for step in range(NUM_GD_STEPS):
                    if (step < 100) or (step >= 100 and step % 100 == 0):
                        # will save all 100 first steps and then every 100 steps
                        pics = gen.eval(feed_dict={is_training_ph: False})
                        codes = z.eval()
                        pic_path = os.path.join(opts['work_dir'],
                                                'pic%03d' % pic_id)
                        code_path = os.path.join(opts['work_dir'],
                                                 'code%03d' % pic_id)
                        np.save(pic_path, pics)
                        np.save(code_path, codes)
                        pic_id += 1
                    # Make a gradient step
                    sess.run(optim, feed_dict={is_training_ph: False})
                    if step % 10 == 0:
                        loss_cur = loss.eval(feed_dict={is_training_ph: False})
                        rel_imp = abs(loss_cur - loss_prev) / abs(loss_prev)
                        logging.error('step %d, loss=%f, rel_imp=%f' % (step, loss_cur, rel_imp))
                        # if rel_imp < 1e-2:
                        #     break
                        loss_prev = loss_cur

def add_aefixedpoint_cost(opts, wae_model):

    w_aefixedpoint = tf.placeholder(tf.float32, name='w_aefixedpoint')
    wae_model.w_aefixedpoint = w_aefixedpoint

    gen_images = wae_model.decoded
    gen_images.set_shape([opts['batch_size']] + wae_model.data_shape)
    tmp = encoder(opts, reuse=True, inputs=gen_images,
                  is_training=wae_model.is_training)
    tmp_sg = encoder(opts, reuse=True,
                     inputs=tf.stop_gradient(gen_images),
                     is_training=wae_model.is_training)
    encoded_gen_images = tmp[0]
    encoded_gen_images_sg = tmp_sg[0]
    if opts['e_noise'] == 'gaussian':
        # Encoder outputs means and variances of Gaussian
        # Encoding into means
        encoded_gen_images = encoded_gen_images[0]
        encoded_gen_images_sg = encoded_gen_images_sg[0]
    autoencoded_gen_images, _ = decoder(
        opts, reuse=True, noise=encoded_gen_images,
        is_training=wae_model.is_training)
    autoencoded_gen_images_sg, _ = decoder(
        opts, reuse=True, noise=encoded_gen_images_sg,
        is_training=wae_model.is_training)
    a = wae.WAE.reconstruction_loss(opts, gen_images, autoencoded_gen_images)
    b = tf.stop_gradient(a)
    c = wae_model.reconstruction_loss(
            tf.stop_gradient(gen_images),
            autoencoded_gen_images_sg)
    extra_cost = b + a - c
    # Check gradients
    # encoder_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope='encoder')
    # wae_model.grad_extra = tf.gradients(ys=extra_cost, xs=encoder_vars)
    # for idx, el in enumerate(wae_model.grad_extra):
    #    print encoder_vars[idx].name, el

    wae_model.wae_objective += wae_model.w_aefixedpoint * extra_cost

def examples(opts, wae_model):
    # Example code showing how to load the checkpoint with a modified graph
    checkpoint = opts['checkpoint']
    with wae_model.sess.as_default(), wae_model.sess.graph.as_default():
        # Imagine the current graph coincides with the one stored in the
        # checkpoint up to 2 placeholders replaced with variables.
        all_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
        inputs_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='inputs')
        vars_to_restore = [v for v in all_vars if v not in inputs_vars]
        saver = tf.train.Saver(vars_to_restore)
        saver.restore(wae_model.sess, checkpoint)
        init = tf.variables_initializer(inputs_vars)
        init.run()


import asyncio

from aiogram import Bot, Dispatcher, executor, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from decouple import config

from logger import write_to_botlog, write_bug_report
from templates_handler import get_templates, fetch_formatter
from manage_users import manage_user, user_permissions
from manage_dbs import manage_db, execute_query
from generator import random_table

TOKEN = config('TOKEN')
USER_MESSAGES = get_templates('user')

storage = MemoryStorage()
bot = Bot(token=TOKEN)
dp = Dispatcher(bot=bot, storage=storage)

class Form(StatesGroup):
    bugreport = State()

@dp.message_handler(commands=["start"])
async def start_handler(message: types.Message):
    # Handles the '/start' command.
    tg_id, uid, lang, user_name = unpack_message(message)
    await message.reply(USER_MESSAGES[lang][message.text].format(user_name))
    await asyncio.sleep(1)
    await bot.send_message(
        tg_id, USER_MESSAGES[lang]["TOUR"], parse_mode='MarkdownV2')
    manage_user(uid, 'CREATE')
    user_permissions(uid, 'ALTER')
    manage_db(uid, 'CREATE')
    await bot.send_message(
        tg_id, USER_MESSAGES[lang]["LETSGO"], parse_mode='MarkdownV2')

@dp.message_handler(commands=["reload"])
async def reload_handler(message: types.Message):
    # Handles the '/reload' command.
    tg_id, uid, lang, user_name = unpack_message(message)
    manage_db(uid, 'DROP')
    manage_db(uid, 'CREATE')
    await bot.send_message(
        tg_id, USER_MESSAGES[lang]["RELOADED"], parse_mode='MarkdownV2')

@dp.message_handler(commands=["help"])
async def help_handler(message: types.Message):
    # Handles the '/help' command.
    tg_id, uid, lang, user_name = unpack_message(message)
    await bot.send_message(
        tg_id, USER_MESSAGES[lang]["HELP"], parse_mode='MarkdownV2')

@dp.message_handler(commands=["random"])
async def random_handler(message: types.Message):
    # Handles the '/random' command.
    tg_id, uid, lang, user_name = unpack_message(message)
    response_from_db = random_table(uid)
    if response_from_db == 'CONNECTION_FAILED':
        await bot.send_message(tg_id, USER_MESSAGES[lang][response_from_db])
    elif response_from_db:
        await bot.send_message(
            tg_id, USER_MESSAGES[lang]["RANDOM"].format(response_from_db),
            parse_mode='MarkdownV2')

@dp.message_handler(commands=["bugreport"])
async def bugreport_handler(message: types.Message):
    # Handles the '/bugreport' command.
    tg_id, uid, lang, user_name = unpack_message(message)
    await Form.bugreport.set()
    await message.reply(
        USER_MESSAGES[lang][message.text].format(user_name),
        parse_mode='MarkdownV2')

@dp.message_handler(state='*', commands=['cancel'])
async def cancel_handler(message: types.Message, state: FSMContext):
    tg_id, uid, lang, user_name = unpack_message(message)
    current_state = await state.get_state()
    if current_state is None:
        return
    await state.finish()
    await message.reply(USER_MESSAGES[lang][message.text])

@dp.message_handler(state=Form.bugreport)
async def process_name(message: types.Message, state: FSMContext):
    tg_id, uid, lang, user_name = unpack_message(message)
    await state.finish()
    write_bug_report(message.text, uid, user_name)
    await message.reply(USER_MESSAGES[lang]["BUG_REPORTED"].format(user_name))

@dp.message_handler()
async def message_handler(message: types.Message):
    # Handles messages which not contain any command.
    tg_id, uid, lang, user_name = unpack_message(message)
    query = message.text
    response_from_db = execute_query(uid, query)
    if response_from_db == 'CONNECTION_FAILED':
        await bot.send_message(tg_id, USER_MESSAGES[lang][response_from_db])
    elif response_from_db:
        await bot.send_message(
            tg_id, fetch_formatter(response_from_db), parse_mode='MarkdownV2')

def unpack_message(message):
    # Extractng data from message.
    tg_id = message.from_user.id
    uid = 'id' + str(tg_id)
    lang = message.from_user.language_code
    lang = lang if lang == 'ru' else 'en'
    user_name = message.from_user.first_name
    write_to_botlog(uid, lang, message.text)
    return tg_id, uid, lang, user_name

if __name__ == "__main__":
    executor.start_polling(dp)


#!/usr/bin/env python

"""
Interval Data Class

Implements the Interval class, a data class that defines lower and upper
bounding values for an interval. Are the building blocks for representing
multi-dimensional regions and computing for overlap between those regions.
Provides methods for determining if there is an overlap between two intervals,
what the intersection interval between the two intervals is, what the union
interval between the two intervals is, randomly generate intervals, and
randomly choose values within an interval.

Classes:
- Interval
"""

from collections import abc
from dataclasses import asdict, astuple, dataclass
from functools import reduce
from numbers import Number, Real
from typing import Any, Callable, Dict, List, Tuple, Union

from numpy import floor

from sources.abstract import IOable
from sources.helpers import NDArray, RandomFn, Randoms

@dataclass(order = True)
class Interval(IOable, abc.Container, abc.Hashable):
  """
  The lower and upper bounding values for an interval.

  Building block for representing multi-dimensional regions and computing
  for overlap between those regions. Provides methods for determining if there
  is an overlap between two intervals, what the intersection length between the
  two intervals is, what the union interval between the two intervals is,
  and randomly generate intervals.

  Extends:
    IOable
    abc.Container
    abc.Hashable

  Attributes:
    lower, upper:
      The lower and upper bounding values.
  """
  lower: float
  upper: float

  def __init__(self, lower: Number, upper: Number):
    """
    Initialize a new Interval, with the lower and upper bounding values.
    Converts input values to floating point numbers, and assigns
    the float value to the lower and upper fields. If lower is greater
    than upper, swaps the lower and upper values.

    Args:
      lower, upper:
        the lower and upper bounding values.
    """
    self.assign(float(lower), float(upper))

  ### Properties: Getters

  @property
  def _instance_invariant(self) -> bool:
    """
    Invariant:
    - lower, upper must Real or float values
    - lower <= upper

    Returns:
      True: If instance invariant holds
      False: Otherwise.
    """
    return all([isinstance(self.lower, Real),
                isinstance(self.upper, Real),
                self.lower <= self.upper])

  @property
  def length(self) -> float:
    """
    Compute the length of this Interval.

    Returns:
      The distance between the lower and
      upper bounding values.
    """
    return abs(self.upper - self.lower)

  @property
  def midpoint(self) -> float:
    """
    Compute the midpoint between the lower and upper bounds
    of this Interval.

    Returns:
      The value equal distance between the lower and
      upper bounding values.
    """
    return (self.lower + self.upper) / 2

  ### Methods: Assignment

  def __setattr__(self, name, value):
    """
    Called when an attribute assignment is attempted. This is called instead
    of the normal mechanism. name is the attribute name, value is the value
    to be assigned to it.

    Ensures that the lower and upper bounding values satisfy the object
    invariant: lower <= upper, the lower and upper bounding values cannot be
    modified directly after this Interval is initialized. If the lower or upper
    bounding values are attempted to be set, raises an exception. This
    effectively prevents this Interval from being mutated into an invalid state.
    To mutate this Interval, call the assign() method with both lower and
    upper bounding values instead.

    Args:
      name:   The attribute name
      value:  The value to be assigned to it.

    Raises:
      Exception: Attempt to be set lower or upper
    """
    if name == 'lower' or name == 'upper':
      raise Exception(f'Cannot set immutable "{name}" attribute')

    object.__setattr__(self, name, value)

  def assign(self, lower: Real, upper: Real):
    """
    Assign the lower and upper bounding values of this Interval.
    Converts input values to floating point numbers, and assigns
    the float value to the lower and upper fields. If lower is greater
    than upper, swaps the lower and upper values.

    Args:
      lower, upper:
        the lower and upper bounding values.
    """
    assert isinstance(lower, Real)
    assert isinstance(upper, Real)

    if lower > upper:
      object.__setattr__(self, 'lower', float(upper))
      object.__setattr__(self, 'upper', float(lower))
    else:
      object.__setattr__(self, 'lower', float(lower))
      object.__setattr__(self, 'upper', float(upper))

  ### Methods: Hash

  def __hash__(self) -> str:
    """
    Return the hash value for this object.
    Two objects that compare equal must also have the same hash value,
    but the reverse is not necessarily true.

    Returns:
      The hash value for this object.
    """
    return hash(astuple(self))

  ### Methods: Clone

  def __copy__(self) -> 'Interval':
    """
    Create a shallow copy of this Interval and return it.

    Returns:
      The newly created Interval copy.
    """
    return Interval(*astuple(self))

  def copy(self) -> 'Interval':
    """
    Create a shallow copy of this Interval and return it.

    Alias for:
      self.__copy__()

    Returns:
      The newly created Interval copy.
    """
    return self.__copy__()

  ### Methods: Queries

  def contains(self, value: float, inc_lower = True, inc_upper = True) -> bool:
    """
    Determine if the value lies between the lower and upper bounding values.

    Args:
      value:
        The value to test if it lies within
        this Interval's bounds.
      inc_lower, inc_upper:
        Boolean flag for whether or not to include or
        to exclude the lower or upper bounding values
        of this Interval. If inc_lower is True, includes
        the lower bounding value, otherwise excludes it.
        Likewise, if inc_upper is True, includes the
        upper bounding value, otherwise excludes it.

    Returns:
      True:   If values lies between the lower and
              upper bounding values.
      False:  Otherwise.
    """
    assert self._instance_invariant

    gte_lower = self.lower <= value if inc_lower else self.lower < value
    lte_upper = self.upper >= value if inc_upper else self.upper > value

    return gte_lower and lte_upper

  def encloses(self, that: 'Interval', inc_lower = True, inc_upper = True) -> bool:
    """
    Determine if the Interval lies entirely within this Interval.

    Args:
      that:
        The other Interval to test if it lies
        entirely within this Interval's bounds.
      inc_lower, inc_upper:
        Boolean flag for whether or not to include or
        to exclude the lower or upper bounding values
        of this Interval. If inc_lower is True, includes
        the lower bounding value, otherwise excludes it.
        Likewise, if inc_upper is True, includes the
        upper bounding value, otherwise excludes it.

    Returns:
      True:   If other Interval lies entirely within
              this Interval's bounds.
      False:  Otherwise.
    """
    assert isinstance(that, Interval)

    return all([self.length >= that.length,
                self.contains(that.lower, inc_lower, inc_upper),
                self.contains(that.upper, inc_lower, inc_upper)])

  def __contains__(self, value: Union[float, 'Interval']) -> bool:
    """
    Determine if the value or Interval lies entirely between this Interval's
    lower and upper bounding values, inclusively. Return True if value or
    Interval is entirely within this Interval, otherwise False.

    Is syntactic sugar for:
      value in self

    Overload Method that wraps:
      self.contains when value is a float value, and
      self.encloses when value is an Interval

    Args:
      value:
        The value to test if it lies within this Interval's
        bounds, or the other Interval to test if it lies
        entirely within this Interval's bounds.

    Returns:
      True:   If the value or other Interval lies entirely
              within this Interval's bounds.
      False:  Otherwise.
    """
    if isinstance(value, Interval):
      return self.encloses(value)
    else:
      return self.contains(value)

  def overlaps(self, that: 'Interval') -> bool:
    """
    Determine if the given Interval overlaps with this Interval.
    If the Intervals are equal, exact same lower and upper bounding values,
    then overlaps. If the Intervals are adjacent, then not overlapping.
    To be overlapping, one Interval must contains the other's lower or upper
    bounding value. Return True if given Interval overlaps with this Interval,
    otherwise False.

    Args:
      that:
        The other Interval to test if it overlaps
        with this Interval.

    Returns:
      True:   If that Interval overlaps with this Interval
      False:  Otherwise.

    Examples:

      Overlapping:
      - |<---- Interval A ---->|
        |<---- Interval B ---->|
      - |<---- Interval A ---->|
            |<- Interval B ->|
      -    |<- Interval A ->|
        |<---- Interval B ---->|
      - |<- Interval A ->|
              |<- Interval B ->|
      -       |<- Interval A ->|
        |<- Interval B ->|

      Not overlapping:
      - |<- Interval A ->||<- Interval B ->|
      - |<- Interval B ->||<- Interval A ->|
      - |<- Interval A ->|   |<- Interval B ->|
      - |<- Interval B ->|   |<- Interval A ->|
    """
    assert isinstance(that, Interval)
    assert self._instance_invariant
    assert that._instance_invariant

    if self == that:
      return True
    if self.upper <= that.lower or that.upper <= self.lower:
      return False

    # return any([that.lower in self, that.upper in self, self.lower in that, self.upper in that])
    return max(self.lower, that.lower) <= min(self.upper, that.upper)

  ### Methods: Generators

  def intersect(self, that: 'Interval') -> Union['Interval', None]:
    """
    Compute the overlapping Interval between this Interval and that Interval.
    Return the overlapping Interval or None if the Intervals do not overlap.

    Args:
      that:
        The other Interval which this Interval is to compute
        the overlapping Interval with.

    Returns:
      The overlapping Interval: If the Intervals overlap.
      None: If the Intervals do not overlap.

    Examples:

      Intersects:
      - |<---- Interval A ---->|
        |<---- Interval B ---->|
        |<---- ########## ---->|
      - |<---- Interval A ---->|
            |<- Interval B ->|
            |<- ########## ->|
      -    |<- Interval A ->|
        |<---- Interval B ---->|
            |<- ######### ->|
      - |<- Interval A ->|
              |<- Interval B ->|
              |<- #### ->|
      -       |<- Interval A ->|
        |<- Interval B ->|
              |<- #### ->|
    """
    assert isinstance(that, Interval)
    assert self._instance_invariant
    assert that._instance_invariant

    if not self.overlaps(that):
      return None

    return Interval(max(self.lower, that.lower),
                    min(self.upper, that.upper))

  def union(self, that: 'Interval') -> 'Interval':
    """
    Compute the Interval that encloses both this Interval and that Interval.
    Return the enclosing Interval.

    Args:
      that:
        The other Interval which this Interval is to compute
        the encloseing Interval with.

    Returns:
      The enclosing Interval.

    Examples:

      Unions:
      - |<- Interval A ->|        |<- A ->||<- B ->|
        |<- Interval B ->|        |<- B ->||<- A ->|
        |<- ########## ->|        |<- ########## ->|
      - |<--- Interval A --->|        |<- Interval A ->|
            |<- Interval B ->|    |<--- Interval B --->|
        |<- ############## ->|    |<- ############## ->|
      - |<- Interval A ->|        |<- Interval A ->|
            |<- Interval B ->|    |<- Interval B ->|
        |<- ############## ->|    |<- ############## ->|
      - |<- A ->|    |<- B ->|    |<- B ->|    |<- A ->|
        |<- ############## ->|    |<- ############## ->|
    """
    assert isinstance(that, Interval)
    assert self._instance_invariant
    assert that._instance_invariant

    return Interval(min(self.lower, that.lower),
                    max(self.upper, that.upper))

  def random_values(self, nvalues: int = 1, randomng: RandomFn = Randoms.uniform()) -> NDArray:
    """
    Randomly draw N samples from a given distribution or random number
    generation function. Samples are drawn over the interval [lower, upper)
    specified by this Interval. The given size N specifies the number of
    values to output. The default nvalues is 1; a single value is returned.
    Otherwise, a list with the specified number of samples are drawn.

    The default behavior is that samples are uniformly distributed. In other
    words, any value within the given interval is equally likely to be drawn
    by uniform. Other distribution or random number generation functions
    can be substituted via the `randomng` parameter.

    Args:
      nvalues:  The number of values to generate
      randomng: The random number generator that dictates
                the distribution of the values generated.

    Returns:
      List of randomly, sampled values drawn
      over this Interval.
    """
    assert isinstance(nvalues, int) and nvalues > 0
    assert isinstance(randomng, Callable)

    return randomng(nvalues, self.lower, self.upper)

  def random_intervals(self, nintervals: int = 1, sizepc: 'Interval' = None,
                             posnrng: RandomFn = Randoms.uniform(),
                             sizerng: RandomFn = Randoms.uniform(),
                             precision: int = None) -> List['Interval']:
    """
    Randomly generate N Intervals within this Interval, each with a random size
    as a percentage of the total Interval length, bounded by the given size
    percentage Interval (enclosed by Interval(0, 1)). The default distributions
    for choosing the position of the Interval and its size percentage are
    uniform distributions, but can be substituted for other distribution or
    random number generation functions via the `posnrng` and `sizerng`
    parameter. If precision is given, return the randomly generated Intervals
    where the lower and upper bounding values are rounded/truncated to the
    specified precision (number of digits after the decimal point).
    If precision is None, the lower and upper bounding values are of
    arbitrary precision.

    Args:
      nintervals: The number of Intervals to be generated.
      sizepc:     The size range as a percentage of the
                  total Interval length.
      posnrng:    The random number generator for choosing
                  the position of the Interval.
      sizerng:    The random number generator for choosing
                  the size of the Interval.
      precision:  The number of digits after the decimal
                  point for the lower and upper bounding
                  values, or None for arbitrary precision.

    Returns:
      List of randonly generated Intervals
      within this Interval.
    """
    if sizepc == None:
      sizepc = Interval(0, 1)
    if precision != None:
      assert isinstance(precision, int)

    assert isinstance(sizepc, Interval) and Interval(0, 1).encloses(sizepc)
    assert isinstance(posnrng, Callable) and isinstance(sizerng, Callable)

    intervals = []
    positions = self.random_values(nintervals, posnrng)
    lengths   = [s * self.length for s in sizepc.random_values(nintervals, sizerng)]

    for i in range(nintervals):
      length = lengths[i]
      position = positions[i]
      lower = position if position <= self.midpoint else max(position - length, self.lower)
      upper = min(lower + length, self.upper)
      if precision != None:
        lower = round(lower, precision)
        upper = round(upper, precision)
      intervals.append(Interval(lower, upper))

    return intervals

  ### Class Methods: Generators

  @classmethod
  def from_intersect(cls, intervals: List['Interval']) -> Union['Interval', None]:
    """
    Construct a new Interval from the intersection of the given list of
    Intervals. If not all the Intervals intersect, return None, otherwise
    return the Interval that intersects with all of the given Intervals.

    Args:
      intervals:
        List of Intervals to compute the intersecting
        Interval amongst.

    Returns:
      Interval that intersects with all given Intervals.
      None: If not all the Intervals intersect.
    """
    assert isinstance(intervals, List) and len(intervals) > 1
    assert all([isinstance(interval, Interval) for interval in intervals])

    def intersect(a: Interval, b: Interval) -> Interval:
      assert a != None and b != None
      assert a.overlaps(b)
      return a.intersect(b)

    try:
      return reduce(intersect, intervals)
    except AssertionError:
      return None

  @classmethod
  def from_union(cls, intervals: List['Interval']) -> 'Interval':
    """
    Construct a new Interval from the union of the given list of Intervals.
    Return the Interval that encloses all of the given Intervals.

    Args:
      intervals:
        List of Intervals to compute the union
        Interval amongst.

    Returns:
      Interval that encloses all of the given Intervals.
    """
    assert isinstance(intervals, List) and len(intervals) > 1
    assert all([isinstance(interval, Interval) for interval in intervals])

    return reduce(lambda a, b: a.union(b), intervals)

  ### Class Methods: (De)serialization

  @classmethod
  def to_object(cls, object: 'Interval', format: str = 'json', **kwargs) -> Any:
    """
    Generates an object (dict, list, or tuple) from the given Interval object
    that can be converted or serialized as the specified data format: 'json'.
    Additional arguments passed via kwargs are used to customize and tweak the
    object generation process.

    Args:
      object: The Interval convert to an object (dict, list, tuple).
      format: The targetted output format type.
      kwargs: Additional arguments or options to customize and
              tweak the object generation process.

    kwargs:
      compact:
        Boolean flag for whether or not the data representation
        of the output JSON is a compact, abbreviated representation or
        the full data representation with all fields.

    Returns:
      The generated object.
    """
    assert isinstance(object, Interval)

    if 'compact' in kwargs and kwargs['compact']:
      return astuple(object)
    else:
      return asdict(object)

  @classmethod
  def from_object(cls, object: Any) -> 'Interval':
    """
    Construct a new Interval from the conversion of the given object.
    The object may be a Dict, List or Tuple. If it is a Dict contains fields:
    lower and upper bounding values. If it is a List or Tuple contains two
    values, first for the lower bound and second for the upper bound. Returns
    the new Interval.

    Args:
      object:
        The object (dict, tuple, list) to be converted
        into a new Interval instance.

    Returns:
      The newly constructed Interval.
    """
    if isinstance(object, Dict):
      assert 'lower' in object and isinstance(object['lower'], (Real, str))
      assert 'upper' in object and isinstance(object['upper'], (Real, str))
      return Interval(**object)
    else:
      assert isinstance(object, (List, Tuple)) and len(object) == 2
      assert all([isinstance(item, (Real, str)) for item in object])
      return Interval(*object)


"""Plots for the graphtester package."""
import graphtester as gt

from matplotlib import pyplot as plt

dataset_names_gr = [
    "ogbg-molesol",
    "ogbg-molfreesolv",
    "ogbg-mollipo",
]

results_list_gr = []
for dataset_name in dataset_names_gr:
    dataset = gt.load(dataset_name)
    metrics = ["lower_bound_mse"]
    evaluation = gt.evaluate(dataset, metrics=metrics)
    print(evaluation)
    results = list(evaluation.results["lower_bound_mse"].values())
    results_list_gr.append(results)

fig = plt.figure(figsize=(8, 4))
for dataset_name, results in zip(dataset_names_gr, results_list_gr):
    plt.plot(
        results,
        label=dataset_name,
    )
plt.xlabel("Layer count")
plt.ylabel("Lower bound MSE")
plt.legend()
plt.xticks(range(len(results)))
plt.yscale("log")
plt.grid()
plt.tight_layout()
plt.title("Lower bound MSE vs layer count for graph regression datasets")
plt.savefig("lower_bound_mse_vs_layer_count_for_graph_regression_datasets.png", bbox_inches='tight')
plt.show()

# graph classification
dataset_names_gc = [
    "Mutagenicity",
    "MUTAG",
    "NCI1",
    "NCI109",
    "PTC_FM",
]

results_list_gc = []
for dataset_name in dataset_names_gc:
    dataset = gt.load(dataset_name)
    metrics = ["upper_bound_accuracy"]
    evaluation = gt.evaluate(dataset, metrics=metrics)
    print(evaluation)
    results = list(evaluation.results["upper_bound_accuracy"].values())
    results_list_gc.append(results)

fig = plt.figure(figsize=(8, 4))
for dataset_name, results in zip(dataset_names_gc, results_list_gc):
    plt.plot(
        results,
        label=dataset_name,
    )
plt.xlabel("Layer count")
plt.ylabel("Upper bound accuracy")
plt.legend()
plt.xticks(range(len(results)))
plt.grid()
plt.tight_layout()
plt.title("Upper bound accuracy vs layer count for graph classification datasets")
plt.savefig("upper_bound_accuracy_vs_layer_count_for_graph_classification_datasets.png", bbox_inches='tight')
plt.show()

# mse change per recommended feature at zero iteration
dataset_names = [
    "ogbg-molfreesolv",
]
recommendation_results_list_gc = {}
for idx, dataset_name in enumerate(dataset_names):
    dataset = gt.load(dataset_name)
    metrics = ["lower_bound_mse"]
    recommendation = gt.recommend(dataset, metrics=metrics, max_feature_count=1, fast=False)
    print(recommendation)
    for (state, features), results in recommendation.results.items():
        if state == "With node and edge features":
            recommendation_results_list_gc[features[0] if features else "Original"] = (
                results.results["lower_bound_mse"][0]
            )

# sort by mse change
recommendation_results_list_gc = {
    k: v
    for k, v in sorted(
        recommendation_results_list_gc.items(),
        key=lambda item: item[1],
    )
}

fig = plt.figure(figsize=(12, 6))
plt.bar(
    recommendation_results_list_gc.keys(),
    recommendation_results_list_gc.values(),
    # color origial differetly, the rest the same
    color=["C0" if key == "Original" else "C1" for key in recommendation_results_list_gc.keys()],
)
plt.xlabel("Recommended feature")
plt.ylabel("Lower bound MSE")
plt.xticks(rotation=90)
plt.grid()
plt.tight_layout()
plt.title("Lower bound MSE per recommended feature at zero iteration for graph regression datasets")
plt.savefig("lower_bound_mse_per_recommended_feature_at_zero_iteration_for_graph_regression_datasets.png", bbox_inches='tight')
plt.show()