from matplotlib import patches
from PIL import Image
import os
import numpy as np
import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt
from bbox_helper import nms_bbox1, center2corner, loc2bbox
from ssd_net import SSD
import torch.nn.functional as F
from bbox_helper import generate_prior_bboxes
import sys

# def load_model() :
#     #defining the model
#     net = SSD(3)
#     net = torch.load(os.path.join('../', 'ssd_net.pth'))
#
#     return model

def plot_output(image, sel_bboxes) :

    fig, ax = plt.subplots(1)
    # imageB_array = resize(image, (600, 1200), anti_aliasing=True)
    ax.imshow(image, cmap='brg')
    for i in range(0, sel_bboxes.shape[0]):
        rect = patches.Rectangle((sel_bboxes[i, 0] * image.size[0], sel_bboxes[i, 1] * image.size[0]),
                                 (sel_bboxes[i, 2] - sel_bboxes[i, 0]) * image.size[0],
                                 (sel_bboxes[i, 3] - sel_bboxes[i, 1]) * image.size[0], linewidth=1, edgecolor='r',
                                 facecolor='none')  # Create a Rectangle patch
        ax.add_patch(rect)  # Add the patch to the Axes
    plt.show()

def main() :
    torch.set_default_tensor_type('torch.cuda.FloatTensor')

    prior_layer_cfg = [
        {'layer_name': 'Conv5', 'feature_dim_hw': (19, 19), 'bbox_size': (60, 60),
         'aspect_ratio': (1.0, 1 / 2, 1 / 3, 2.0, 3.0, '1t')},
        {'layer_name': 'Conv11', 'feature_dim_hw': (10, 10), 'bbox_size': (105, 105),
         'aspect_ratio': (1.0, 1 / 2, 1 / 3, 2.0, 3.0, '1t')},
        {'layer_name': 'Conv14_2', 'feature_dim_hw': (5, 5), 'bbox_size': (150, 150),
         'aspect_ratio': (1.0, 1 / 2, 1 / 3, 2.0, 3.0, '1t')},
        {'layer_name': 'Conv15_2', 'feature_dim_hw': (3, 3), 'bbox_size': (195, 195),
         'aspect_ratio': (1.0, 1 / 2, 1 / 3, 2.0, 3.0, '1t')},
        {'layer_name': 'Conv16_2', 'feature_dim_hw': (2, 2), 'bbox_size': (240, 240),
         'aspect_ratio': (1.0, 1 / 2, 1 / 3, 2.0, 3.0, '1t')},
        {'layer_name': 'Conv17_2', 'feature_dim_hw': (1, 1), 'bbox_size': (285, 285),
         'aspect_ratio': (1.0, 1 / 2, 1 / 3, 2.0, 3.0, '1t')}
    ]
    prior_bboxes = generate_prior_bboxes(prior_layer_cfg)

    # loading the test image
    img_file_path = sys.argv[1]
    # img_file_path = 'image.png'
    img = Image.open(img_file_path)
    img = img.resize((300, 300))
    plot_img = img.copy()
    img_array = np.asarray(img)[:,:,:3]
    mean = np.asarray((127, 127, 127))
    std = 128.0
    img_array = (img_array - mean) / std
    h, w, c = img_array.shape[0], img_array.shape[1], img_array.shape[2]
    img_tensor = torch.Tensor(img_array)
    test_input = img_tensor.view(1, c, h, w)

    # # loading test input to run test on
    # test_data_loader = torch.utils.data.DataLoader(test_input,
    #                                                 batch_size=1,
    #                                                 shuffle=True,
    #                                                 num_workers=0)
    # idx, (img) = next(enumerate(test_data_loader))
    # # Setting model to evaluate mode
    net = SSD(2)
    test_net_state = torch.load('ssd_net.pth')
    net.load_state_dict(test_net_state)
    # net.eval()
    net.cuda()
    # Forward
    test_input = Variable(test_input.cuda())
    test_cof, test_loc = net.forward(test_input)

    test_loc = test_loc.detach()
    test_loc_clone = test_loc.clone()

    # normalizing the loss to add up to 1 (for probability)
    test_cof_score = F.softmax(test_cof[0], dim = 1)
    # print(test_cof_score.shape)
    # print(test_cof_score)

    # running NMS
    sel_idx = nms_bbox1(test_loc_clone[0], prior_bboxes, test_cof_score.detach(), overlap_threshold=0.5, prob_threshold=0.24)

    test_loc = loc2bbox(test_loc[0], prior_bboxes)
    test_loc = center2corner(test_loc)

    sel_bboxes = test_loc[sel_idx]

    # plotting the output
    plot_output(plot_img, sel_bboxes.cpu().detach().numpy())

if __name__ == '__main__':
    main()


import os.path as osp
import os
import numpy as np
import pickle
import h5py

missing_count = 0
noise_len_thres = 11
noise_spr_thres1 = 0.8
noise_spr_thres2 = 0.69754
noise_mot_thres_lo = 0.089925
noise_mot_thres_hi = 2

def get_raw_bodies_data(ske_path, ske_name):
    """
    Get raw bodies data from a skeleton sequence.

    Each body's data is a dict that contains the following keys:
      - joints: raw 3D joints positions. Shape: (num_frames x 25, 3)
      - interval: a list which stores the frame indices of this body.
      - motion: motion amount (only for the sequence with 2 or more bodyIDs).

    Return:
      a dict for a skeleton sequence with 3 key-value pairs:
        - name: the skeleton filename.
        - data: a dict which stores raw data of each body.
        - num_frames: the number of valid frames.
    """
    ske_file = osp.join(ske_path, ske_name + '.skeleton')
    assert osp.exists(ske_file), 'Error: Skeleton file %s not found' % ske_file
    # Read all data from .skeleton file into a list (in string format)
    print('Reading data from %s' % ske_file[-29:])
    with open(ske_file, 'r') as fr:
        str_data = fr.readlines()

    num_frames = int(str_data[0].strip('\r\n'))
    frames_drop = []
    bodies_data = dict()
    valid_frames = -1  # 0-based index
    current_line = 1

    for f in range(num_frames):
        num_bodies = int(str_data[current_line].strip('\r\n'))
        current_line += 1

        if num_bodies == 0:  # no data in this frame, drop it
            frames_drop.append(f)  # 0-based index
            continue

        valid_frames += 1
        joints = np.zeros((num_bodies, 25, 3), dtype=np.float32)

        for b in range(num_bodies):
            bodyID = str_data[current_line].strip('\r\n').split()[0]
            current_line += 1
            num_joints = int(str_data[current_line].strip('\r\n'))  # 25 joints
            current_line += 1

            for j in range(num_joints):
                temp_str = str_data[current_line].strip('\r\n').split()
                joints[b, j, :] = np.array(temp_str[:3], dtype=np.float32)
                current_line += 1

            if bodyID not in bodies_data:  # Add a new body's data
                body_data = dict()
                body_data['joints'] = joints[b]  # ndarray: (25, 3)
                body_data['interval'] = [valid_frames]  # the index of the first frame
            else:  # Update an already existed body's data
                body_data = bodies_data[bodyID]
                # Stack each body's data of each frame along the frame order
                body_data['joints'] = np.vstack((body_data['joints'], joints[b]))
                pre_frame_idx = body_data['interval'][-1]
                body_data['interval'].append(pre_frame_idx + 1)  # add a new frame index

            bodies_data[bodyID] = body_data  # Update bodies_data

    num_frames_drop = len(frames_drop)
    assert num_frames_drop < num_frames, \
        'Error: All frames data (%d) of %s is missing or lost' % (num_frames, ske_name)

    # Calculate motion (only for the sequence with 2 or more bodyIDs)
    if len(bodies_data) > 1:
        for body_data in bodies_data.values():
            body_data['motion'] = np.sum(np.var(body_data['joints'], axis=0))

    return {'name': ske_name, 'data': bodies_data, 'num_frames': num_frames - num_frames_drop}

def get_raw_skes_data():

    raw_data = get_raw_bodies_data(ske_path, ske_name)
    with open(save_data_pkl, 'wb') as fw:
        pickle.dump(raw_data, fw, pickle.HIGHEST_PROTOCOL)

    print('Saved raw bodies data into %s' % save_data_pkl)
    return raw_data

def denoising_by_length(ske_name, bodies_data):
    """
    Denoising data based on the frame length for each bodyID.
    Filter out the bodyID which length is less or equal than the predefined threshold.

    """
    new_bodies_data = bodies_data.copy()
    for (bodyID, body_data) in new_bodies_data.items():
        length = len(body_data['interval'])
        if length <= noise_len_thres:
            del bodies_data[bodyID]

    return bodies_data

def get_valid_frames_by_spread(points):
    """
    Find the valid (or reasonable) frames (index) based on the spread of X and Y.

    :param points: joints or colors
    """
    num_frames = points.shape[0]
    valid_frames = []
    for i in range(num_frames):
        x = points[i, :, 0]
        y = points[i, :, 1]
        if (x.max() - x.min()) <= noise_spr_thres1 * (y.max() - y.min()):  # 0.8
            valid_frames.append(i)
    return valid_frames

def denoising_by_spread(ske_name, bodies_data):
    """
    Denoising data based on the spread of Y value and X value.
    Filter out the bodyID which the ratio of noisy frames is higher than the predefined
    threshold.

    bodies_data: contains at least 2 bodyIDs
    """
    denoised_by_spr = False  # mark if this sequence has been processed by spread.

    new_bodies_data = bodies_data.copy()
    # for (bodyID, body_data) in bodies_data.items():
    for (bodyID, body_data) in new_bodies_data.items():
        if len(bodies_data) == 1:
            break
        valid_frames = get_valid_frames_by_spread(body_data['joints'].reshape(-1, 25, 3))
        num_frames = len(body_data['interval'])
        num_noise = num_frames - len(valid_frames)
        if num_noise == 0:
            continue

        ratio = num_noise / float(num_frames)
        motion = body_data['motion']
        if ratio >= noise_spr_thres2:  # 0.69754
            del bodies_data[bodyID]
            denoised_by_spr = True
        else:  # Update motion
            joints = body_data['joints'].reshape(-1, 25, 3)[valid_frames]
            body_data['motion'] = min(motion, np.sum(np.var(joints.reshape(-1, 3), axis=0)))
            # TODO: Consider removing noisy frames for each bodyID

    return bodies_data, denoised_by_spr

def denoising_bodies_data(bodies_data):
    """
    Denoising data based on some heuristic methods, not necessarily correct for all samples.

    Return:
      denoised_bodies_data (list): tuple: (bodyID, body_data).
    """
    bodies_data = bodies_data['data']

    # Step 1: Denoising based on frame length.
    bodies_data, noise_info_len = denoising_by_length(ske_name, bodies_data)

    if len(bodies_data) == 1:  # only has one bodyID left after step 1
        return bodies_data.items(), noise_info_len

    # Step 2: Denoising based on spread.
    bodies_data, denoised_by_spr = denoising_by_spread(ske_name, bodies_data)

    if len(bodies_data) == 1:
        return bodies_data.items()

    bodies_motion = dict()  # get body motion
    for (bodyID, body_data) in bodies_data.items():
        bodies_motion[bodyID] = body_data['motion']
    # Sort bodies based on the motion
    # bodies_motion = sorted(bodies_motion.items(), key=lambda x, y: cmp(x[1], y[1]), reverse=True)
    bodies_motion = sorted(bodies_motion.items(), key=lambda x: x[1], reverse=True)
    denoised_bodies_data = list()
    for (bodyID, _) in bodies_motion:
        denoised_bodies_data.append((bodyID, bodies_data[bodyID]))

    return denoised_bodies_data

def get_one_actor_points(body_data, num_frames):
    """
    Get joints for only one actor.
    For joints, each frame contains 75 X-Y-Z coordinates.
    """
    joints = np.zeros((num_frames, 75), dtype=np.float32)
    start, end = body_data['interval'][0], body_data['interval'][-1]
    joints[start:end + 1] = body_data['joints'].reshape(-1, 75)

    return joints

def remove_missing_frames(ske_name, joints):
    """
    Cut off missing frames which all joints positions are 0s

    For the sequence with 2 actors' data, also record the number of missing frames for
    actor1 and actor2, respectively (for debug).
    """

    # Find valid frame indices that the data is not missing or lost
    # For two-subjects action, this means both data of actor1 and actor2 is missing.
    valid_indices = np.where(joints.sum(axis=1) != 0)[0]  # 0-based index
    missing_indices = np.where(joints.sum(axis=1) == 0)[0]
    num_missing = len(missing_indices)

    if num_missing > 0:  # Update joints
        joints = joints[valid_indices]

    return joints

def get_two_actors_points(bodies_data):
    """
    Get the first and second actor's joints positions locations.

    # Arguments:
        bodies_data (dict): 3 key-value pairs: 'name', 'data', 'num_frames'.
        bodies_data['data'] is also a dict, while the key is bodyID, the value is
        the corresponding body_data which is also a dict with 4 keys:
          - joints: raw 3D joints positions. Shape: (num_frames x 25, 3)
          - interval: a list which records the frame indices.
          - motion: motion amount

    # Return:
        joints.
    """
    num_frames = bodies_data['num_frames']

    bodies_data = denoising_bodies_data(bodies_data)  # Denoising data

    bodies_data = list(bodies_data)
    if len(bodies_data) == 1:  # Only left one actor after denoising

        bodyID, body_data = bodies_data[0]
        joints = get_one_actor_points(body_data, num_frames)
    else:

        joints = np.zeros((num_frames, 150), dtype=np.float32)

        bodyID, actor1 = bodies_data[0]  # the 1st actor with largest motion
        start1, end1 = actor1['interval'][0], actor1['interval'][-1]
        joints[start1:end1 + 1, :75] = actor1['joints'].reshape(-1, 75)
        del bodies_data[0]

        start2, end2 = [0, 0]  # initial interval for actor2 (virtual)

        while len(bodies_data) > 0:
            bodyID, actor = bodies_data[0]
            start, end = actor['interval'][0], actor['interval'][-1]
            if min(end1, end) - max(start1, start) <= 0:  # no overlap with actor1
                joints[start:end + 1, :75] = actor['joints'].reshape(-1, 75)
                # Update the interval of actor1
                start1 = min(start, start1)
                end1 = max(end, end1)
            elif min(end2, end) - max(start2, start) <= 0:  # no overlap with actor2
                joints[start:end + 1, 75:] = actor['joints'].reshape(-1, 75)
                # Update the interval of actor2
                start2 = min(start, start2)
                end2 = max(end, end2)
            del bodies_data[0]

    return joints

def get_raw_denoised_data(raw_data):
    num_bodies = len(raw_data['data'])
    if num_bodies == 1:  # only 1 actor
        num_frames = raw_data['num_frames']
        body_data = list(raw_data['data'].values())[0]
        joints= get_one_actor_points(body_data, num_frames)
    else:  # more than 1 actor, select two main actors
        joints= get_two_actors_points(raw_data)
        # Remove missing frames
        joints= remove_missing_frames(ske_name, joints)
    return joints

def seq_translation(ske_joints):
    num_frames = ske_joints.shape[0]
    num_bodies = 1 if ske_joints.shape[1] == 75 else 2
    if num_bodies == 2:
        missing_frames_1 = np.where(ske_joints[:, :75].sum(axis=1) == 0)[0]
        missing_frames_2 = np.where(ske_joints[:, 75:].sum(axis=1) == 0)[0]
        cnt1 = len(missing_frames_1)
        cnt2 = len(missing_frames_2)

    i = 0  # get the "real" first frame of actor1
    while i < num_frames:
        if np.any(ske_joints[i, :75] != 0):
            break
        i += 1

    origin = np.copy(ske_joints[i, 3:6])  # new origin: joint-2

    for f in range(num_frames):
        if num_bodies == 1:
            ske_joints[f] -= np.tile(origin, 25)
        else:  # for 2 actors
            ske_joints[f] -= np.tile(origin, 50)

    if (num_bodies == 2) and (cnt1 > 0):
        ske_joints[missing_frames_1, :75] = np.zeros((cnt1, 75), dtype=np.float32)

    if (num_bodies == 2) and (cnt2 > 0):
        ske_joints[missing_frames_2, 75:] = np.zeros((cnt2, 75), dtype=np.float32)

    return ske_joints

def one_hot_vector(label):
    label_vector = np.zeros((1, 60))
    label_vector[0, int(label)-1] = 1
    print(label_vector)

    return label_vector

if __name__ == '__main__':
    save_path = './'
    ske_path = '../../../Raw_Skeleton_data/NTU-RGB-D60'
    # ske_name = 'S013C003P017R002A038'
    # ske_name = 'S017C001P008R001A010'
    ske_name = 'S013C003P028R001A024'

    visual_label = ske_name[-3:]

    if not osp.exists('./visual_data'):
        os.makedirs('./visual_data')

    save_data_pkl = osp.join(save_path, 'visual_data', 'visual_skes_data.pkl')

    raw_data = get_raw_skes_data()
    joints = get_raw_denoised_data(raw_data)
    ske_joints = seq_translation(joints)
    ske=[]
    ske.append(ske_joints)
    h5file = h5py.File(osp.join(save_path, 'NTU_visual_24.h5'), 'w')
    # Visual set
    h5file.create_dataset('visual_x', data=ske)
    visual_one_hot_labels = one_hot_vector(visual_label)
    h5file.create_dataset('visual_y', data=visual_one_hot_labels)

    h5file.close()


def filter_on_case_size(df, case_id_glue="case:concept:name", min_case_size=2, max_case_size=None):
    """
    Filter a dataframe keeping only traces with at least the specified number of events

    Parameters
    -----------
    df
        Dataframe
    case_id_glue
        Case ID column in the CSV
    min_case_size
        Minimum size of a case
    max_case_size
        Maximum case size

    Returns
    -----------
    df
        Filtered dataframe
    """
    element_group_size = df[case_id_glue].groupby(df[case_id_glue]).transform('size')
    df = df[element_group_size >= min_case_size]
    if max_case_size:
        element_group_size = df[case_id_glue].groupby(df[case_id_glue]).transform('size')
        df = df[element_group_size <= max_case_size]
    return df


import datetime
from datetime import date
import calendar
import pytz

class MyDateTools:
    def get_current_year(self):
        """
        Returns the current year.
        """

        return int(date.today().year)

    def get_current_month(self):
        """
        Returns the current month in string format. If the month val's length is
        one, a zero is concatenated to the front month val.
        """

        return datetime.date.today().month

    def get_current_day(self):

        return datetime.date.today().day

    def get_current_date(self):
        """
        Returns the current date formatted as year-month-day.
        """

        return datetime.datetime.today().strftime('%Y-%m-%d')

    def get_currentpretty_date(self):
        month = self.get_current_month()
        day = self.get_current_day()
        year = self.get_current_year()
        month_str = calendar.month_name[month]

        return month_str + ' ' + str(day) + ', ' + str(year)

    def get_endof_week(self):
        """
        Returns the full date of the last day of the current week
        """

        current_date = datetime.date.today()
        current_day = current_date.day
        day_val = current_date.weekday()
        end_day = 0
        increment_vals = {0: 6, 1: 5, 2: 4, 3: 3, 4: 2, 5: 1, 6: 1}

        if day_val == 7:
            end_day = current_day
        else:
            for i in range(7):
                if day_val == i:
                    end_day = current_day + increment_vals[i]
                    break

        date_str = str(current_date.year) + '-' + str(current_date.month) + '-' + str(end_day)
        date_obj = datetime.datetime.strptime(date_str, '%Y-%m-%d').date()

        return date_obj

    def getcurrent_beginend_ofmonth(self):
        """
        Returns the beginning and end dates of the current month
        """

        year = self.get_current_year()
        month = self.get_current_month()
        num_of_days = calendar.monthrange(year, month)[1]
        start_date = datetime.date(year, month, 1)
        end_date = datetime.date(year, month, num_of_days)

        return start_date, end_date

    def get_begin_endof_month(self, month, year):
        """
        Return the beginning and end dates of desired month and year
        :param month: The desired month
        :param year: The desired year
        """

        num_of_days = calendar.monthrange(year, month)[1]
        start_date = datetime.date(year, month, 1)
        end_date = datetime.date(year, month, num_of_days)

        return str(start_date), str(end_date)

    def get_posyears_set(self):
        """
        Returns a list of future years, starting with the current year.
        """

        current_year = self.get_current_year()
        year_list = []
        year = 0

        for i in range(6):
            year = current_year + i
            year_list.append(year)

        return year_list

    def get_current_time(self):
        """
        Returns the current Utah time in 12hr format.
        """

        our_zone = pytz.timezone('US/Arizona')
        current_time = datetime.datetime.now(our_zone)

        return str(current_time.strftime('%I:%M %p'))


"""Update User table

Revision ID: cb50363a3d09
Revises: 2450c90e4636
Create Date: 2022-09-21 03:08:50.421699

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = 'cb50363a3d09'
down_revision = '2450c90e4636'
branch_labels = None
depends_on = None

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('TelegramUser',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('first_start_timestamp', sa.DateTime(), nullable=True),
    sa.Column('is_stopped', sa.Integer(), nullable=True),
    sa.Column('first_name', sa.String(), nullable=True),
    sa.Column('last_name', sa.String(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_TelegramUser_first_start_timestamp'), 'TelegramUser', ['first_start_timestamp'], unique=False)
    op.drop_index('ix_user_first_start_timestamp', table_name='user')
    op.drop_table('user')
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('user',
    sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
    sa.Column('first_start_timestamp', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
    sa.Column('is_stopped', sa.INTEGER(), autoincrement=False, nullable=True),
    sa.Column('first_name', sa.VARCHAR(), autoincrement=False, nullable=True),
    sa.Column('last_name', sa.VARCHAR(), autoincrement=False, nullable=True),
    sa.PrimaryKeyConstraint('id', name='user_pkey')
    )
    op.create_index('ix_user_first_start_timestamp', 'user', ['first_start_timestamp'], unique=False)
    op.drop_index(op.f('ix_TelegramUser_first_start_timestamp'), table_name='TelegramUser')
    op.drop_table('TelegramUser')
    # ### end Alembic commands ###


   # -*- coding: utf-8 -*-
# @File    : InsertIntoABinarySearchTree.py
# @Date    : 2020-02-26
# @Author  : tc
"""
题号 701. 二叉搜索树中的插入操作
给定二叉搜索树（BST）的根节点和要插入树中的值，将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 保证原始二叉搜索树中不存在新值。

注意，可能存在多种有效的插入方式，只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。

例如,

给定二叉搜索树:

        4
       / \
      2   7
     / \
    1   3

和 插入的值: 5
你可以返回这个二叉搜索树:

         4
       /   \
      2     7
     / \   /
    1   3 5
或者这个树也是有效的:

         5
       /   \
      2     7
     / \
    1   3
         \
          4

参考：https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/solution/er-cha-sou-suo-shu-zhong-de-cha-ru-cao-zuo-by-le-2/

"""
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
        if not root:
            return TreeNode(val)  # 这是最关键的地方
        if val > root.val:
            root.right = self.insertIntoBST(root.right, val)
        elif val < root.val:
            root.left = self.insertIntoBST(root.left, val)
        return root


from tensorflow.python.keras.models import Sequential,Model
from tensorflow.python.keras.layers import Dense,Conv2D,MaxPool2D,Flatten,Conv1D,Reshape, LSTM,Input
from tensorflow.keras.datasets import fashion_mnist
import numpy as np

#1 데이터
(x_train,y_train), (x_test,y_test) = fashion_mnist.load_data()
print(x_train.shape,y_train.shape) # (60000, 28, 28) (60000,)
print(x_test.shape, y_test.shape)
print(np.unique(y_train))
x_train = x_train.reshape(60000, 28, 28, 1)/255.
x_test = x_test.reshape(10000, 28, 28, 1)/255.

print(x_train.shape,y_train.shape) # (60000, 28, 28, 1) (60000,)
print(x_test.shape, y_test.shape) # (10000, 28, 28, 1) (10000,)

#2 모델
input1 = Input(shape=(28, 28, 1))
Conv2D1 = Conv2D(64,(3,3),padding='same')(input1)
Maxpool1 = MaxPool2D()(Conv2D1)
Conv2D2 = Conv2D(50,(3,3))(Maxpool1)
Conv2D3 = Conv2D(10,3)(Conv2D2)
Maxpool2 = MaxPool2D()(Conv2D3)
flatten1 = Flatten()(Maxpool2)
output1 = Dense(10,activation='softmax')(flatten1)
model = Model(inputs = input1, outputs = output1)
model.summary()
Reshape1 = Reshape(target_shape=(250,10))(flatten1)
Conv2D4 = Conv2D(10,3,padding='same')(Reshape1)
lstm = LSTM(784)(Conv2D4)
Reshape2 = Reshape(target_shape=(28,28,1))(lstm)
Conv2D5 = Conv2D(32,(4,4))(Reshape2)
flatten2 = Flatten()(Conv2D5)
output1 = Dense(10,activation='softmax')(flatten2)
model = Model(inputs = input1, outputs = output1)
model.summary()

#3 컴파일, 훈련
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['acc'])
model.fit(x_train,y_train,
          epochs=5,
          batch_size=200)

#4 평가, 예측

results = model.evaluate(x_test,y_test)
print("loss : ", results[0])
print("acc : ", results[1])

plt.imshow(x_train[0], 'gray')
plt.show()


# coding=utf-8
# Copyright 2018 The Dopamine Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module defining classes and helper methods for general agents."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import glob
import shutil

import sys
import time
import datetime
import csv, json, pickle

import random
from dopamine.agents.dqn import dqn_agent
from dopamine.agents.implicit_quantile import implicit_quantile_agent
from dopamine.agents.rainbow import rainbow_agent
from dopamine.agents.fqf import fqf_agent
from dopamine.discrete_domains import atari_lib
from dopamine.discrete_domains import checkpointer
from dopamine.discrete_domains import iteration_statistics
from dopamine.discrete_domains import logger
from dopamine.discrete_domains.tictactoe.examples.td_agent import TDAgent
import numpy as np
import tensorflow as tf

import gin.tf

from copy import deepcopy

def load_gin_configs(gin_files, gin_bindings):
  """Loads gin configuration files.
  Args:
    gin_files: list, of paths to the gin configuration files for this
      experiment.
    gin_bindings: list, of gin parameter bindings to override the values in
      the config files.
  """
  gin.parse_config_files_and_bindings(gin_files,
                                      bindings=gin_bindings,
                                      skip_unknown=False)

@gin.configurable
def create_agent(sess, environment, agent_name=None, summary_writer=None,
                 debug_mode=False):
  """Creates an agent.
  Args:
    sess: A `tf.Session` object for running associated ops.
    environment: A gym environment (e.g. Atari 2600).
    agent_name: str, name of the agent to create.
    summary_writer: A Tensorflow summary writer to pass to the agent
      for in-agent training statistics in Tensorboard.
    debug_mode: bool, whether to output Tensorboard summaries. If set to true,
      the agent will output in-episode statistics to Tensorboard. Disabled by
      default as this results in slower training.
  Returns:
    agent: An RL agent.
  Raises:
    ValueError: If `agent_name` is not in supported list.
  """

  #return dqn_agent.DQNAgent(graph=tf.Graph(),  num_actions=environment.action_space.n, environment=environment,
  #                            summary_writer=summary_writer)

  assert agent_name is not None
  if not debug_mode:
    summary_writer = None
  if agent_name == 'dqn':
    return dqn_agent.DQNAgent(num_actions=environment.action_space.n,
                              summary_writer=summary_writer)
  elif agent_name == 'rainbow':
    return rainbow_agent.RainbowAgent(
         num_actions=environment.action_space.n,
        summary_writer=summary_writer)
  elif agent_name == 'implicit_quantile':
    return implicit_quantile_agent.ImplicitQuantileAgent(
         num_actions=environment.action_space.n, environment=environment, #CHANGE
        summary_writer=summary_writer)
  elif agent_name == 'fqf':
    return fqf_agent.FQFAgent(
        sess, num_actions=environment.action_space.n,
        summary_writer=summary_writer)
  else:
    raise ValueError('Unknown agent: {}'.format(agent_name))

@gin.configurable
def create_runner(base_dir, schedule='continuous_train_and_eval'):
  """Creates an experiment Runner.
  Args:
    base_dir: str, base directory for hosting all subdirectories.
    schedule: string, which type of Runner to use.
  Returns:
    runner: A `Runner` like object.
  Raises:
    ValueError: When an unknown schedule is encountered.
  """
  assert base_dir is not None
  # Continuously runs training and evaluation until max num_iterations is hit.
  if schedule == 'continuous_train_and_eval':
    return Runner(base_dir, create_agent)
  # Continuously runs training until max num_iterations is hit.
  elif schedule == 'continuous_train':
    return TrainRunner(base_dir, create_agent)
  else:
    raise ValueError('Unknown schedule: {}'.format(schedule))

@gin.configurable
class Runner(object):
  """Object that handles running Dopamine experiments.
  Here we use the term 'experiment' to mean simulating interactions between the
  agent and the environment and reporting some statistics pertaining to these
  interactions.
  A simple scenario to train a DQN agent is as follows:
  ```python
  import dopamine.discrete_domains.atari_lib
  base_dir = '/tmp/simple_example'
  def create_agent(sess, environment):
    return dqn_agent.DQNAgent(sess, num_actions=environment.action_space.n)
  runner = Runner(base_dir, create_agent, atari_lib.create_atari_environment)
  runner.run()
  ```
  """

  def __init__(self,
               base_dir,
               create_agent_fn,
               create_environment_fn=atari_lib.create_atari_environment,
               checkpoint_file_prefix='ckpt',
               logging_file_prefix='log',
               log_every_n=1,
               num_iterations=200,
               runtype='run',
               game='Pong',
               training_steps=250000,
               evaluation_steps=125000,
               max_steps_per_episode=27000):
    """Initialize the Runner object in charge of running a full experiment.
    Args:
      base_dir: str, the base directory to host all required sub-directories.
      create_agent_fn: A function that takes as args a Tensorflow session and an
        environment, and returns an agent.
      create_environment_fn: A function which receives a problem name and
        creates a Gym environment for that problem (e.g. an Atari 2600 game).
      checkpoint_file_prefix: str, the prefix to use for checkpoint files.
      logging_file_prefix: str, prefix to use for the log files.
      log_every_n: int, the frequency for writing logs.
      num_iterations: int, the iteration number threshold (must be greater than
        start_iteration).
      training_steps: int, the number of training steps to perform.
      evaluation_steps: int, the number of evaluation steps to perform.
      max_steps_per_episode: int, maximum number of steps after which an episode
        terminates.
    This constructor will take the following actions:
    - Initialize an environment.
    - Initialize a `tf.Session`.
    - Initialize a logger.
    - Initialize an agent.
    - Reload from the latest checkpoint, if available, and initialize the
      Checkpointer object.
    """
    assert base_dir is not None
    self._logging_file_prefix = logging_file_prefix
    self._log_every_n = log_every_n
    self._num_iterations = num_iterations
    self._runtype = runtype
    self._game = game
    self._training_steps = training_steps
    self._evaluation_steps = evaluation_steps
    self._max_steps_per_episode = max_steps_per_episode
    self._base_dir = base_dir
    self._create_directories()
    #self._summary_writer = tf.contrib.summary.FileWriter(self._base_dir) CHANGE
    self.testing = False
    self.opponent=TDAgent('X',0,0)
    self.PATH='/home/neriait/tic_tac_toe_IQN_2/dopamine/discrete_domains/'
    self.opponent.load_model(self.PATH + 'tictactoe/examples/mirai')
    if 'sticky' in runtype:
        self.sticky_actions = True
    else:
        self.sticky_actions = False
    self._environment = create_environment_fn(sticky_actions=self.sticky_actions)
    # Set up a session and initialize variables.

    self._agent = create_agent_fn(None, self._environment,
                                  summary_writer=None)

    self._agent2 = create_agent_fn(None, self._environment,
                                  summary_writer=None)

    self._agent11 = create_agent_fn(None, self._environment,
                                  summary_writer=None)

    self._agent22 = create_agent_fn(None, self._environment,
                                  summary_writer=None)

    self._agent.testing = self.testing
    self._agent2.testing = self.testing

    self._agent11.testing = self.testing
    self._agent22.testing = self.testing

    self.create_agent_fn=create_agent_fn

    #self._summary_writer.add_graph(graph=tf.get_default_graph()) CHANGE
    self._initialize_checkpointer_and_maybe_resume(checkpoint_file_prefix)

    self.step_count_total = 0
    #game = self._environment.name
    print (">>>>>>>>>", self._game)
    self.date = date = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
    self.job_name = self._game + '-' + self._runtype + '-' + date
    self.filename = './fout-results/results-%s_%s-%s.txt' % (self._game, self._runtype, date)
    self.filename_test = './fout-results/testresults-%s_%s-%s.txt' % (self._game, self._runtype, date)
    self.filename_test_raw = 'testresults-%s_%s-%s.txt' % (self._game, self._runtype, date)
    if not self.testing:
        self.fout = open(self.filename, 'w+')
        self.fout_test = open(self.filename_test, 'w+')

  def _create_directories(self):
    """Create necessary sub-directories."""
    self._checkpoint_dir = os.path.join(self._base_dir, 'checkpoints')
    print("CHECKPOINTDIR:", self._checkpoint_dir)
    self._logger = logger.Logger(os.path.join(self._base_dir, 'logs'))

  def _initialize_checkpointer_and_maybe_resume(self, checkpoint_file_prefix):
    """Reloads the latest checkpoint if it exists.
    This method will first create a `Checkpointer` object and then call
    `checkpointer.get_latest_checkpoint_number` to determine if there is a valid
    checkpoiRnt in self._checkpoint_dir, and what the largest file number is.
    If a valid checkpoint file is found, it will load the bundled data from this
    file and will pass it to the agent for it to reload its data.
    If the agent is able to successfully unbundle, this method will verify that
    the unbundled data contains the keys,'logs' and 'current_iteration'. It will
    then load the `Logger`'s data from the bundle, and will return the iteration
    number keyed by 'current_iteration' as one of the return values (along with
    the `Checkpointer` object).
    Args:
      checkpoint_file_prefix: str, the checkpoint file prefix.
    Returns:
      start_iteration: int, the iteration number to start the experiment from.
      experiment_checkpointer: `Checkpointer` object for the experiment.
    """
    self._checkpointer = checkpointer.Checkpointer(self.PATH + "checkpoints",
                                                   checkpoint_file_prefix)

    self._checkpointer2 = checkpointer.Checkpointer(self.PATH + "player2", checkpoint_file_prefix)
    self._checkpointerlatest1 = checkpointer.Checkpointer(self.PATH + "latest3", checkpoint_file_prefix)
    self._checkpointerlatest2 = checkpointer.Checkpointer(self.PATH + "latest4", checkpoint_file_prefix)
    self._checkpointertest = checkpointer.Checkpointer(self.PATH + "test", checkpoint_file_prefix)

    self._start_iteration = 0
    # Check if checkpoint exists. Note that the existence of checkpoint 0 means
    # that we have finished iteration 0 (so we will start from iteration 1).
    latest_checkpoint_version = checkpointer.get_latest_checkpoint_number(
        self.PATH + "checkpoints")
    print("Latest version:",latest_checkpoint_version)
    return 5
    if latest_checkpoint_version >= 0:
      experiment_data = self._checkpointer.load_checkpoint(
          latest_checkpoint_version)
      print("Read to apple")
      if self._agent.unbundle(
          self.PATH + "checkpoints", latest_checkpoint_version, experiment_data):

        assert 'logs' in experiment_data
        assert 'current_iteration' in experiment_data
        self._logger.data = experiment_data['logs']
        self._start_iteration = experiment_data['current_iteration'] + 1
        tf.logging.info('Reloaded checkpoint and will start from iteration %d',
                        self._start_iteration)
        #self.testing = True
        #self._agent.testing = True
        print ('TESTING:', self.testing)
        print("BANANA")

  def _my_initialize_resume(self,  checkpoint_file_prefix, folder_type, iteration, player):

    self._start_iteration = 0
    # Check if checkpoint exists. Note that the existence of checkpoint 0 means
    # that we have finished iteration 0 (so we will start from iteration 1).
    if player==11 or player==1:
      if folder_type=='latest':
        self._delete_everything_in_folder(self.PATH + ""+ folder_type +str(1+2))
        self._move_everything_in_folder(self.PATH + ""+ folder_type +str(1), self.PATH + ""+ folder_type +str(1+2))

      latest_checkpoint_version = iteration
      if True:
        if folder_type=='latest':
          experiment_data = self._checkpointerlatest1.load_checkpoint(iteration)
        else:
          experiment_data = self._checkpointer1.load_checkpoint(iteration)
        x=False
        if player==11:
          x=self._agent11.unbundle(self.PATH + ""+folder_type+str(1+2), iteration, experiment_data)
        else:
          x=self._agent.unbundle(self.PATH + ""+folder_type+str(1+2), iteration, experiment_data)
        if x:
          assert 'logs' in experiment_data
          assert 'current_iteration' in experiment_data
          self._logger.data = experiment_data['logs']
          self._start_iteration = experiment_data['current_iteration'] + 1
          #self.testing = True
          #self._agent.testing = True
    else:
      if folder_type=='latest':
        self._delete_everything_in_folder(self.PATH + ""+ folder_type +str(2+2))
        self._move_everything_in_folder(self.PATH + ""+ folder_type +str(2), self.PATH + ""+ folder_type +str(2+2))

      latest_checkpoint_version = iteration
      if True:
        if folder_type=='latest':
          experiment_data = self._checkpointerlatest2.load_checkpoint(iteration)
        else:
          moop=poopo
        if player==22:
          x=self._agent22.unbundle(self.PATH + ""+folder_type+str(2+2), iteration, experiment_data)
        else:
          x=self._agent2.unbundle(self.PATH + ""+folder_type+str(2+2), iteration, experiment_data)
        if x:
          assert 'logs' in experiment_data
          assert 'current_iteration' in experiment_data
          self._logger.data = experiment_data['logs']
          self._start_iteration = experiment_data['current_iteration'] + 1
          #self.testing = True
          #self._agent.testing = True

  def _initialize_episode(self, run_mode_str):
    """Initialization for a new episode.
    Returns:
      action: int, the initial action chosen by the agent.
    """
    print("Start episode initaliaztion")
    initial_observation = self._environment.reset()
    initial_observation=initial_observation[0]

    print("Finish episode initaliaztion")
    return self._agent.get_action_from_observation(initial_observation)

  def _initialize_former_agents(self):

    self._my_initialize_resume('crpt', 1)
    self._my_initialize_resume('crpt', 2)

  def _run_one_step(self, action):
    """Executes a single step in the environment.
    Args:
      action: int, the action to perform in the environment.

    Returns:
      The observation, reward, and is_terminal values returned from the
        environment.
    """
    observation, reward, is_terminal, _ = self._environment.step(action)
    '''
    if self.testing:
        image = self._environment.render('rgb_array')
        self._agent.vis['s'].append(image)
        self._agent.vis['state_input'].append(image)
        #print (image.shape)
        self._agent.vis['r'].append(reward)
        quantiles, values = self._sess.run([
            self._agent._net_outputs.quantiles,
            self._agent._net_outputs.quantile_values],
            {self._agent.state_ph: self._agent.state}
        )
        self._agent.vis['fraction'].append(quantiles)
        self._agent.vis['values'].append(values)
        #print (quantiles.shape)
    '''
    return observation, reward, is_terminal

  def _end_episode(self, reward):
    """Finalizes an episode run.
    Args:
      reward: float, the last reward from the environment.
    """
    self._agent.end_episode(reward)

  def observation_to_tensorlike(self, observation):
    x=0
    if observation[1]=='O':
      x=1
    else:
      x=-1
    return observation[0]+(50*x,)

  def _run_one_episode(self, run_mode_str):
    """Executes a full trajectory of the agent interacting with the environment.
    Returns:
      The number of steps taken and the total reward.
    """
    step_number = 0
    total_reward = 0.

    action=None
    switchedobservation2=None
    gameplayer=[]
    gameopponent=[]
    pi_data=[]
    count=0

    initial_observation = self._environment.reset()

    is_terminal = False
    observation2=initial_observation

    first_time=True
    while True:
    #Player1
      originalobservation2=observation2
      observation2=self.observation_to_tensorlike(observation2)
      if run_mode_str=='oldvsnew':
        pass
      #    observation, reward, is_terminal = self._run_one_step(self._agent11.get_action_from_observation(observation2))
      #    action=2
      #elif run_mode_str=='oldvsboss':
      #    observation, reward, is_terminal = self._run_one_step(self._agent11.get_action_from_observation(observation2))
      #    action=2
      #elif run_mode_str=='oldvsold':
      #    observation, reward, is_terminal = self._run_one_step(self._agent11.get_action_from_observation(observation2))
      #    action=2
      #elif run_mode_str=='newvsold':
      #    observation, reward, is_terminal = self._run_one_step(self._agent.get_action_from_observation(observation2))
      #    action=2

      elif run_mode_str=='evalrandom':
          observation, reward, is_terminal = self._run_one_step(self._agent.get_action_from_observation(observation2))
          action=2
      elif run_mode_str=='eval':
        action=self._agent.get_action_from_observation(observation2)
        observation, reward, is_terminal = self._run_one_step(action)
      elif run_mode_str=='train1':
        self._agent.eval_mode=False

        if count < CFG.temp_thresh:
                best_child = self.mcts1.search(game, node, CFG.temp_init)
        else:
                best_child = self.mcts1.search(game, node, CFG.temp_final)

        action = best_child.action
        observation, reward, is_terminal = self._run_one_step(action)
      elif run_mode_str=='train2':
        self._agent.eval_mode=True
        if count < CFG.temp_thresh:
                best_child = self.mcts.search(game, node, CFG.temp_init)
        else:
                best_child = self.mcts.search(game, node, CFG.temp_final)
        action = best_child.action
        observation, reward, is_terminal = self._run_one_step(action)
      else:
        print("Marco polo")
        action = self._agent.get_action_from_observation(observation2)
        observation, reward, is_terminal = self._run_one_step(action)

      observationbeforefirstplayeraction=observation2
      originalobservation=observation
      observation=self.observation_to_tensorlike(observation)
      if not is_terminal:
      #Player2
        if run_mode_str=='oldvsnew':
          pass
        #  observation2, reward2, is_terminal = self._run_one_step(self._agent2.get_action_from_observation(observation))
        #  action2=2
        #elif run_mode_str=='oldvsold':
        #  observation2, reward2, is_terminal = self._run_one_step(self._agent22.get_action_from_observation(observation))
        #  action2=2
        #elif run_mode_str=='newvsold':
        #  observation2, reward2, is_terminal = self._run_one_step(self._agent22.get_action_from_observation(observation))
        #  action2=2
        #elif run_mode_str=='evalrandom':
        #  observation2, reward2, is_terminal = self._run_one_step(random.choice(self._environment.available_actions()))
        #  action2=2
        #elif run_mode_str=='eval':
        #  observation2, reward2, is_terminal = self._run_one_step(self.opponent.act(originalobservation, self._environment.available_actions()))
        #  action2=2
        #elif run_mode_str=='oldvsboss':
        #  observation2, reward2, is_terminal = self._run_one_step(self.opponent.act(originalobservation, self._environment.available_actions()))
        #  action2=2
        elif run_mode_str=='train1':
          self._agent.eval_mode=True
          if count < CFG.temp_thresh:
                best_child = self.mcts.search(game, node, CFG.temp_init)
          else:
                best_child = self.mcts.search(game, node, CFG.temp_final)
          action = best_child.action
          observation, reward, is_terminal = self._run_one_step(action)

        elif run_mode_str=='train2':
          self._agent.eval_mode=False
          if count < CFG.temp_thresh:
                best_child = self.mcts.search(game, node, CFG.temp_init)
          else:
                best_child = self.mcts.search(game, node, CFG.temp_final)

          action = best_child.action
          observation, reward, is_terminal = self._run_one_step(action)

        else:
          print("Marco polo2")
          action2 = self._agent2.get_action_from_observation(observation)
          observation2, reward2, is_terminal = self._run_one_step(action2)

        reward=reward+reward2
        gameopponent.append([observation,-reward, is_terminal,action2])
        gameplayer.append([observationbeforefirstplayeraction,reward,is_terminal,action])

      else:
        gameplayer.append([observationbeforefirstplayeraction,reward,is_terminal,action])
        gameopponent[len(gameopponent)-1][1]=-reward
        gameopponent[len(gameopponent)-1][2]=True

      total_reward += reward
      step_number += 1
      first_time=False
      # Perform reward clipping.
      reward = np.clip(reward, -1, 1)
      if (is_terminal or
          step_number == self._max_steps_per_episode):
        # Stop the run loop once we reach the true end of episode.
        break

    if run_mode_str=='train1':
      for item in gameplayer:
          self._agent._store_transition([item[0]], item[3], item[1], item[2])
          if not item[2]:
            self._agent._train_step()

    if run_mode_str=='train2':
      for item in gameopponent:

          self._agent2._store_transition([item[0]], item[3], item[1], item[2])
          if not item[2]:
            self._agent2._train_step()

    if total_reward==-2:
      total_reward=-1

    return step_number, total_reward, gameplayer, gameopponent

  def _run_one_phase(self, min_steps, statistics, run_mode_str, is_silent):
    """Runs the agent/environment loop until a desired number of steps.
    We follow the Machado et al., 2017 convention of running full episodes,
    and terminating once we've run a minimum number of steps.
    Args:
      min_steps: int, minimum number of steps to generate in this phase.
      statistics: `IterationStatistics` object which records the experimental
        results.
      run_mode_str: str, describes the run mode for this agent.
    Returns:
      Tuple containing the number of steps taken in this phase (int), the sum of
        returns (float), and the number of episodes performed (int).
    """
    step_count = 0
    num_episodes = 0
    sum_returns = 0.

    while step_count < min_steps:
      start_time = time.time()
      episode_length, episode_return, self.gameplayer, self.gameopponent = self._run_one_episode(run_mode_str)
      time_delta = time.time() - start_time
      statistics.append({
          '{}_episode_lengths'.format(run_mode_str): episode_length,
          '{}_episode_returns'.format(run_mode_str): episode_return
      })
      step_count += episode_length
      self.step_count_total += episode_length
      sum_returns += episode_return
      num_episodes += 1
      # We use sys.stdout.write instead of tf.logging so as to flush frequently
      # without generating a line break.
      if not self.testing:
          self.fout.write('%d %f %d\n' % (self.step_count_total, episode_return, episode_length))
          self.fout.flush()
      if not is_silent:
        sys.stdout.write('Steps executed: {} '.format(step_count) +
                       'Episode length: {} '.format(episode_length) +
                       'Return: {} '.format(episode_return) +
                       'Average time one episode: {}\r'.format(episode_length/time_delta))
      sys.stdout.flush()
    if run_mode_str!='evalrandom' or run_mode_str!='train':
      for item in self.gameplayer:
        print(item)
    return step_count, sum_returns, num_episodes

  def _run_train_phase(self, statistics):
    """Run training phase.
    Args:
      statistics: `IterationStatistics` object which records the experimental
        results. Note - This object is modified by this method.
    Returns:
      num_episodes: int, The number of episodes run in this phase.
      average_reward: The average reward generated in this phase.
    """
    # Perform the training phase, during which the agent learns.
    self._agent.eval_mode = False
    self._agent2.eval_mode = False

    start_time = time.time()
    number_steps, sum_returns, num_episodes = self._run_one_phase(
        2000, statistics, 'train',False)
    average_return = sum_returns / num_episodes if num_episodes > 0 else 0.0
    statistics.append({'train_average_return': average_return})
    time_delta = time.time() - start_time
    tf.logging.info('Average undiscounted return per training episode: %.2f',
                    average_return)
    tf.logging.info('Average training steps per second: %.2f',
                    number_steps / time_delta)
    return num_episodes, average_return

  def _run_eval_phase(self, statistics):
    """Run evaluation phase.
    Args:
      statistics: `IterationStatistics` object which records the experimental
        results. Note - This object is modified by this method.
    Returns:
      num_episodes: int, The number of episodes run in this phase.
      average_reward: float, The average reward generated in this phase.
    """
    # Perform the evaluation phase -- no learning.
    self._agent.eval_mode = True
    self._agent2.eval_mode = True

    _, sum_returns, num_episodes = self._run_one_phase(
        80, statistics, 'eval',False)
    average_return = sum_returns / num_episodes if num_episodes > 0 else 0.0
    tf.logging.info('Average undiscounted return per evaluation episode: %.2f',
                    average_return)
    statistics.append({'eval_average_return': average_return})
    return num_episodes, average_return

  def _run_generic_phase(self, statistics, name):
    """Run evaluation phase.
    Args:
      statistics: `IterationStatistics` object which records the experimental
        results. Note - This object is modified by this method.
    Returns:
      num_episodes: int, The number of episodes run in this phase.
      average_reward: float, The average reward generated in this phase.
    """
    # Perform the evaluation phase -- no learning.
    self._agent.eval_mode = True
    self._agent2.eval_mode = True
    self._agent11.eval_mode = True
    self._agent22.eval_mode = True
    _, sum_returns, num_episodes = self._run_one_phase(
        1000, statistics, name,True)
    average_return = sum_returns / num_episodes if num_episodes > 0 else 0.0
    tf.logging.info('Average undiscounted return per evaluation episode: %.2f',
                    average_return)
    statistics.append({'eval_average_return': average_return})
    return num_episodes, average_return

  def _move_everything_in_folder(self, src, dst):
    src_files = os.listdir(src)
    for file_name in src_files:
      full_file_name = os.path.join(src, file_name)
      if os.path.isfile(full_file_name):
        shutil.copy(full_file_name, dst)

  def _delete_everything_in_folder(self, path):

    files = glob.glob(path + '/*')
    for f in files:
      os.remove(f)

    files = glob.glob(path + '/.*')
    for f in files:
      os.remove(f)

  def _run_one_iteration(self, iteration, firstiteration):
    """Runs one iteration of agent/environment interaction.
    An iteration involves running several episodes until a certain number of
    steps are obtained. The interleaving of train/eval phases implemented here
    are to match the implementation of (Mnih et al., 2015).
    Args:
      iteration: int, current iteration number, used as a global_step for saving
        Tensorboard summaries.
    Returns:
      A dict containing summary statistics for this iteration.
    """

    #if firstiteration:
    #  experiment_data = self._agent2.bundle_and_checkpoint(self.PATH + "test",
    #                                                    iteration)
    #  if experiment_data:
    #    experiment_data['current_iteration'] = iteration
    #    experiment_data['logs'] = self._logger.data
    #    self._checkpointertest.save_checkpoint(iteration, experiment_data)
    #if iteration==25:
    #    experiment_data = self._checkpointertest.load_checkpoint(0)
    #    x=False
    #   x=self._agent2.unbundle(self.PATH + "test", 0, experiment_data)
    #

    #self.to_graph = tf.Graph() # graph where everything above will be copied to

    #self._q_argmax = tf.contrib.copy_graph.copy_op_to_graph(self._agent._q_argmax, self.to_graph,[])
    #self._q_argmax = tf.contrib.copy_graph.copy_op_to_graph(self._agent.state_ph, self.to_graph,[])
    #self._q_argmax = tf.contrib.copy_graph.copy_op_to_graph(self._agent.validmoves_ph, self.to_graph,[])

    if firstiteration:
      self._my_checkpoint_experiment(iteration, 'latest', 1)
      self._my_checkpoint_experiment(iteration, 'latest', 2)
    self._my_checkpoint_experiment(iteration, 'player', 1)

    q_argmax2 = self._agent._sess.run(self._agent._q_values, {self._agent.state_ph: [[[[0],[0],[0],[0],[0],[0],[0],[0],[0],[50]]]], self._agent.validmoves_ph:

         [1,3,5,7,8]})

    #print("Q Before loading", q_argmax2)

    self._my_initialize_resume('crpt', 'latest',  self.latest1, 11)
    self._my_initialize_resume('crpt', 'latest',  self.latest2, 22)

    statistics = iteration_statistics.IterationStatistics()
    tf.logging.info('Starting iteration %d', iteration)
    print("SELF.COUNTER:", self.counter)
    if iteration>50 and self.counter>=1:
      self.counter=0
      self.player1_turn_training=not self.player1_turn_training

    if iteration<50000:
      num_episodes_train, average_reward_train = self._run_generic_phase(
        statistics,'train1')

      num_episodes_train, average_reward_train = self._run_generic_phase(
        statistics,'train2')
      print("TRAIN TRAIN1")
    else:
      if self.player1_turn_training:
        num_episodes_train, average_reward_train = self._run_generic_phase(
        statistics,'train1')
        print("TRAIN TRAIN2")
      else:
        num_episodes_train, average_reward_train = self._run_generic_phase(
        statistics,'train2')
        print("TRAIN TRAIN3")

    num_episodes_eval, average_reward_eval = self._run_eval_phase(
        statistics)

    print("PLAYER1 TURN TRAINING:", self.player1_turn_training)
    print("EVAL EVAL")

    #numep, evalaveragereward =  self._run_generic_phase(
    #    statistics,'preveval')

    print("LATEST1:", self.latest1)
    print("LATEST2:", self.latest2)

    numep, oldvsnew = self._run_generic_phase(
        statistics,'oldvsnew')
    numep, newvsold = self._run_generic_phase(
        statistics,'newvsold')
    numep, oldvsold = self._run_generic_phase(
        statistics,'oldvsold')
    print("OLD VS NEW:",oldvsnew)
    print("NEW VS OLD:",newvsold)
    print("OLD VS OLD:", oldvsold)
    numep, oldvsboss = self._run_generic_phase(
        statistics,'oldvsboss')
    print("OLD VS BOSS", oldvsboss)
    numep, evalrandom = self._run_generic_phase(
        statistics,'evalrandom')
    print("EVALRANDOM:", evalrandom)
    print("PLAYER1 TURN TRAINING2:", self.player1_turn_training)
    if self.player1_turn_training:
      self.compare_result_against=newvsold
      self.who_to_change=1
      self.who_to_change_latest=self.latest1
      if newvsold>0.05:
        self.counter+=1
      else:
        self.counter=0
    else:
      self.compare_result_against=oldvsnew
      self.who_to_change=2
      self.who_to_change_latest=self.latest2
      if oldvsnew<0.05:
        self.counter+=1
      else:
        self.counter=0

    if iteration<50000:
      self._my_checkpoint_experiment(iteration, 'latest', 1)
      self.latest1=iteration
      self._my_checkpoint_experiment(iteration, 'latest', 2)
      self.latest2=iteration
      self.counter=0
    else:
      if self.player1_turn_training:
        if oldvsold + 0.05 <  self.compare_result_against:
          self._my_checkpoint_experiment(iteration, 'latest', self.who_to_change)
          if self.who_to_change==1:
            self.latest1=iteration
          else:
            self.latest2=iteration
          print("CROOK: Changing player "+ str(self.who_to_change)  +" version")
        else:
          self._my_initialize_resume('crpt','latest',self.who_to_change_latest, self.who_to_change)
          print("CROOK: Staying with old player "+str(self.who_to_change)  )
      else:
        if oldvsold - 0.05 >  self.compare_result_against:
          self._my_checkpoint_experiment(iteration, 'latest', self.who_to_change)
          if self.who_to_change==1:
            self.latest1=iteration
          else:
            self.latest2=iteration
          print("CROOK: Changing player "+ str(self.who_to_change)  +" version")
        else:
          self._my_initialize_resume('crpt','latest',self.who_to_change_latest, self.who_to_change)
          print("CROOK: Staying with old player "+str(self.who_to_change)  )

    #variables = tf.trainable_variables()
    #print("Weight matrix: {0}".format(self._agent._sess.run(variables[0])))

    print("EPSILON:")
    print(self._agent.epsilon_fn(
          self._agent.epsilon_decay_period,
          self._agent.training_steps,
          self._agent.min_replay_history,
          self._agent.epsilon_train))

    #self._save_tensorboard_summaries(iteration, num_episodes_train,
    #                                 average_reward_train, num_episodes_eval,
    #                                 average_reward_eval)
    if not self.testing:
        self.fout_test.write('%d %f %d\n' % (iteration, average_reward_eval, num_episodes_eval))
        self.fout_test.flush()
    return statistics.data_lists

  def _save_tensorboard_summaries(self, iteration,
                                  num_episodes_train,
                                  average_reward_train,
                                  num_episodes_eval,
                                  average_reward_eval):
    """Save statistics as tensorboard summaries.
    Args:
      iteration: int, The current iteration number.
      num_episodes_train: int, number of training episodes run.
      average_reward_train: float, The average training reward.
      num_episodes_eval: int, number of evaluation episodes run.
      average_reward_eval: float, The average evaluation reward.
    """
    summary = tf.Summary(value=[
        tf.Summary.Value(tag='Train/NumEpisodes',
                         simple_value=num_episodes_train),
        tf.Summary.Value(tag='Train/AverageReturns',
                         simple_value=average_reward_train),
        tf.Summary.Value(tag='Eval/NumEpisodes',
                         simple_value=num_episodes_eval),
        tf.Summary.Value(tag='Eval/AverageReturns',
                         simple_value=average_reward_eval)
    ])
    self._summary_writer.add_summary(summary, iteration)
  def _log_experiment(self, iteration, statistics):
    """Records the results of the current iteration.
    Args:
      iteration: int, iteration number.
      statistics: `IterationStatistics` object containing statistics to log.
    """
    self._logger['iteration_{:d}'.format(iteration)] = statistics
    if iteration % self._log_every_n == 0:
      self._logger.log_to_file(self._logging_file_prefix, iteration)

  def _my_checkpoint_experiment(self,  iteration, folder_type, player):
    """Checkpoint experiment data.
    Args:
      iteration: int, iteration number for checkpointing.
    """

    if player==1:
      experiment_data = self._agent.bundle_and_checkpoint(self.PATH + ""+ folder_type +"3",  iteration)
      if experiment_data:
        experiment_data['current_iteration'] = iteration
        experiment_data['logs'] = self._logger.data
        if folder_type=='latest':
          self._checkpointerlatest1.save_checkpoint(iteration, experiment_data)
        else:
          self._checkpointer2.save_checkpoint(iteration, experiment_data)
      if folder_type=='latest':
        self._delete_everything_in_folder(self.PATH + ""+ folder_type +"1")
        self._move_everything_in_folder(self.PATH + ""+ folder_type +"3", self.PATH + ""+ folder_type +"1")

    else:
      experiment_data = self._agent2.bundle_and_checkpoint(self.PATH + ""+folder_type+"4", iteration)
      if experiment_data:
        experiment_data['current_iteration'] = iteration
        experiment_data['logs'] = self._logger.data
        if folder_type=='latest':
          self._checkpointerlatest2.save_checkpoint(iteration, experiment_data)
        else:
          self._checkpointer2.save_checkpoint(iteration, experiment_data)
      if folder_type=='latest':
        self._delete_everything_in_folder(self.PATH + ""+ folder_type +"2")
        self._move_everything_in_folder(self.PATH + ""+ folder_type +"4", self.PATH + ""+ folder_type +"2")

  def _checkpoint_experiment(self, iteration):
    """Checkpoint experiment data.
    Args:
      iteration: int, iteration number for checkpointing.
    """
    experiment_data = self._agent.bundle_and_checkpoint(self.PATH + "checkpoints",
                                                        iteration)
    if experiment_data:
      experiment_data['current_iteration'] = iteration
      experiment_data['logs'] = self._logger.data
      self._checkpointer.save_checkpoint(iteration, experiment_data)

  def run_experiment(self):
    """Runs a full experiment, spread over multiple iterations."""
    i=3
    j=i
    i=2
    print("MAKEUP:", j)
    print(tf.get_default_graph())
    tf.logging.info('Beginning training...')
    if self._num_iterations <= self._start_iteration:
      tf.logging.warning('num_iterations (%d) < start_iteration(%d)',
                         self._num_iterations, self._start_iteration)
      return

    self._delete_everything_in_folder(self.PATH + "player2")
    self._delete_everything_in_folder(self.PATH + "latest1")
    self._delete_everything_in_folder(self.PATH + "latest2")
    self._delete_everything_in_folder(self.PATH + "latest3")
    self._delete_everything_in_folder(self.PATH + "latest4")
    self.counter=0
    self.evalwhoamiplaying=1
    self.latest1=0
    self.latest2=0
    self.player1_turn_training=False
    first=True
    for iteration in range(self._start_iteration, 2000):
      statistics = self._run_one_iteration(iteration, first)
      first=False
      self._log_experiment(iteration, statistics)
      self._checkpoint_experiment(iteration)

@gin.configurable
class TrainRunner(Runner):
  """Object that handles running experiments.
  The `TrainRunner` differs from the base `Runner` class in that it does not
  the evaluation phase. Checkpointing and logging for the train phase are
  preserved as before.
  """

  def __init__(self, base_dir, create_agent_fn,
               create_environment_fn=atari_lib.create_atari_environment):
    """Initialize the TrainRunner object in charge of running a full experiment.
    Args:
      base_dir: str, the base directory to host all required sub-directories.
      create_agent_fn: A function that takes as args a Tensorflow session and an
        environment, and returns an agent.
      create_environment_fn: A function which receives a problem name and
        creates a Gym environment for that problem (e.g. an Atari 2600 game).
    """
    tf.logging.info('Creating TrainRunner ...')
    super(TrainRunner, self).__init__(base_dir, create_agent_fn,
                                      create_environment_fn)
    self._agent.eval_mode = False

  def _run_one_iteration(self, iteration):
    """Runs one iteration of agent/environment interaction.
    An iteration involves running several episodes until a certain number of
    steps are obtained. This method differs from the `_run_one_iteration` method
    in the base `Runner` class in that it only runs the train phase.
    Args:
      iteration: int, current iteration number, used as a global_step for saving
        Tensorboard summaries.
    Returns:
      A dict containing summary statistics for this iteration.
    """
    statistics = iteration_statistics.IterationStatistics()
    num_episodes_train, average_reward_train = self._run_train_phase(
        statistics)

    self._save_tensorboard_summaries(iteration, num_episodes_train,
                                     average_reward_train)
    return statistics.data_lists

  def _save_tensorboard_summaries(self, iteration, num_episodes,
                                  average_reward):
    """Save statistics as tensorboard summaries."""
    summary = tf.Summary(value=[
        tf.Summary.Value(tag='Train/NumEpisodes', simple_value=num_episodes),
        tf.Summary.Value(
            tag='Train/AverageReturns', simple_value=average_reward),
    ])
    self._summary_writer.add_summary(summary, iteration)


def non_divisible_subset(k: int, s: list) -> int:
    """
    Non-Divisible Subset problem: https://www.hackerrank.com/challenges/non-divisible-subset/problem

    :param k: The divisor.
    :param s: An array of integers.
    :return: The length of the longest subset of 's' meeting the criteria.
    """
    d = {x: [] for x in range(0, k)}
    for i in range(0, len(s)):
        d[s[i] % k].append(s[i])
    count = 0
    if len(d[0]) > 0:
        count = 1
    listRange = range(1, (k // 2) + 1)
    S = set((x, k - x) for x in listRange)
    for i, j in S:
        if i != j:
            if len(d[i]) > len(d[j]):
                count += len(d[i])
            else:
                count += len(d[j])
        else:
            if len(d[i]) > 0:
                count += 1
    return count


"""Interface to GitHub."""
import requests

def _get_github_api_url(repo: str) -> str:
    """Get GitHub API URL for a repository, accepting a flexible range of inputs.

    Args:
        repo (str): Repository in the form "user/repo" or full repository URL.

    Returns:
        str: GitHub issues API URL for the repository.
    """
    if repo.startswith("https://api.github.com/repos/"):
        # Assume it's already a GitHub API URL
        if repo.endswith("/"):
            repo = repo[:-1]
        return repo

    # Assume it's a GitHub repository URL and accept a flexible range of inputs
    # Normalize the URL
    if repo.startswith("https://github.com/"):
        repo = repo.replace("https://github.com/", "")
    if repo.endswith(".git"):
        repo = repo[:-4]
    if repo.endswith("/"):
        repo = repo[:-1]

    # Create the GitHub API URL from the normalized URL
    if "/" in repo:
        return f"https://api.github.com/repos/{repo}"

    raise ValueError("Invalid repository format. Must be in the form 'user/repo' or full repository URL.")

def _invoke_github_api(repo: str, endpoint: str) -> dict:
    """Invoke the GitHub API for a repository.

    Args:
        repo (str): Repository in the form "user/repo" or "https://..."
        endpoint (str): API endpoint and its parameters, e.g. "issues/1234".
    """
    url = _get_github_api_url(repo)
    if endpoint:
        url = f"{url}/{endpoint}"

    response = requests.get(url, timeout=10)
    response.raise_for_status()
    return response.json()

def get_issue(repo: str, issue_id: str = "") -> dict:
    """Get a specific issue from GitHub.

    Args:
        repo (str): Repository in the form "user/repo" or the full URL to the issue, e.g.
        "https://github.com/qjebbs/vscode-plantuml/issues/255".
        issue_id (int): Issue number. Optional if the URL already contains the issue number.

    Returns:
        dict: Issue data.
    """
    if "/issues/" in repo:
        # Assume it's already a fully-formed GitHub issue API URL
        return _invoke_github_api(repo, "")
    return _invoke_github_api(repo, f"issues/{issue_id}")

def get_issue_comments(issue: dict) -> dict:
    """Get comments for a specific issue.

    Args:
        issue (dict): Issue data, as returned by GitHub (the JSON response).

    Returns:
        dict: Comments data.
    """
    return _invoke_github_api(issue["comments_url"], "")

def parse_issue(issue: dict) -> str:
    """Parse issue data returned by GitHub into a text format.

    The goal is to translate the JSON into a text format that has only the information we need to pass to an LLM. The
    shorter format helps the LLM understand the data better and saves tokens (and thus cost).

    The text includes some annotations to help guide the LLM. For example, using delimiters to indicate the start and
    end of the body text, and using a prefix to indicate the start of each field. This is based on previous experiments
    with LLMs. It may need to be adjusted for different LLMs. Other LLMs may not need as much guidance.

    Args:
        issue (dict): Issue data, as returned by GitHub (the JSON response).

    Returns:
        str: Parsed issue data.
    """
    parsed = f"Title: {issue['title']}\n"
    parsed += f"Body (between '''):\n'''\n{issue['body']}\n'''\n"
    parsed += f"Submitted by: {issue['user']['login']}\n"
    parsed += f"Submitted on: {issue['created_at']}\n"
    parsed += f"Submitter association: {issue['author_association']}\n"
    parsed += f"State: {issue['state']}\n"
    parsed += f"Labels: {', '.join([label['name'] for label in issue['labels']])}\n"
    return parsed

def parse_comments(comments: dict) -> str:
    """Parse comments data returned by GitHub into a text format.

    See comments for parse_issue() for more details.

    Args:
        comments (dict): Comments data, as returned by GitHub (the JSON response).

    Returns:
        str: Parsed comments data.
    """
    parsed = ""
    for comment in comments:
        parsed += f"Comment by: {comment['user']['login']}\n"
        parsed += f"Comment on: {comment['created_at']}\n"
        parsed += f"Body (between '''):\n'''\n{comment['body']}\n'''\n"
    return parsed


# Section: Files
f = open('data.txt', 'w')  # create a data.txt file with write privileges
f.write('Hello\n')
print(f.write('world!\n'))  # returns the number of characters printed
f.close()

f = open('data.txt')  # reading is the default operation
text = f.read()  # files are always read as strings
print(text)
f.close()


'''Clients place orders to a stockbroker as strings. The order can be simple or multiple.

Type of a simple order: Quote /space/ Quantity /space/ Price /space/ Status

where Quote is formed of non-whitespace character, Quantity is an int, Price a double (with mandatory decimal point "." ), Status is represented by the letter B (buy) or the letter S (sell).

Example:

"GOOG 300 542.0 B"

A multiple order is the concatenation of simple orders with a comma between each.

Example:

"ZNGA 1300 2.66 B, CLH15.NYM 50 56.32 B, OWW 1000 11.623 B, OGG 20 580.1 B"

To ease the stockbroker your task is to produce a string of type

"Buy: b Sell: s" where b and s are 'double' formatted with no decimal (rounded to integers), b representing the total price of bought stocks and s the total price of sold stocks.

Example:

"Buy: 294990 Sell: 0"

Unfortunately sometimes clients make mistakes. When you find mistakes in orders, you must pinpoint these badly formed orders and produce a string of type:

"Buy: b Sell: s; Badly formed nb: badly-formed 1st simple order ;badly-formed nth simple order ;"

where nb is the number of badly formed simple orders, b representing the total price of bought stocks with correct simple order and s the total price of sold stocks with correct simple order.

Examples:

"Buy: 263 Sell: 11802; Badly formed 2: CLH16.NYM 50 56 S ;OWW 1000 11 S ;"

"Buy: 100 Sell: 56041; Badly formed 1: ZNGA 1300 2.66 ;"

Note: Due to Codewars whitespace differences will not always show up in test results.'''

def balance_statement(lst):
    if(len(lst) == 0):
        return "Buy: 0 Sell: 0"
    str1 = lst.split(',')
    buy = 0
    sell = 0
    bad = []
    for s in str1:
        stock = s.strip().split(' ')
        if len(stock) != 4:
            bad.append(stock)
        elif(stock[2].isdigit() == True):
            bad.append(stock)
        elif(stock[1].isdigit() == False):
            bad.append(stock)
        elif(stock[3] == 'B' or stock[3] =='S'):
            if stock[3] == 'B':
                buy += float(stock[1]) * float(stock[2])
            elif stock[3] == 'S':
                sell += float(stock[1]) * float(stock[2])
        else:
            bad.append(stock)

    bad_s = ''
    length = len(bad)
    l = str(length)
    if(length > 0):
        for b in bad:
            bad_s += ' '.join(b) + ' ;'
    if bad_s == '':
        return "Buy: " + str(int(round(buy))) + " Sell: " + str(int(round(sell)))
    else:
        return "Buy: " + str(int(round(buy))) + " Sell: " + str(int(round(sell))) + "; Badly formed " + l + ": " + bad_s;

'''def balance_statement(lst):
    bad_formed, prices = [], {'B':0, 'S':0}
    for order in filter(None, lst.split(', ')):
        if not re.match('\S+ \d+ \d*\.\d+ (B|S)', order):
            bad_formed.append(order + ' ;')
        else:
            _, quantity, price, status = order.split()
            prices[status] += int(quantity) * float(price)
    ret = "Buy: %.0f Sell: %.0f" % (prices['B'], prices['S'])
    if bad_formed:
        ret += "; Badly formed %d: %s" % (len(bad_formed), ''.join(bad_formed))
    return ret'''


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    # Time = O(N)
    # Space = O(1)
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # Edge case: empty list
        if not head: return None

        hare, tortoise = head, head

        # While the hare can move 2 steps forward
        while hare.next and hare.next.next:
            hare = hare.next.next
            tortoise = tortoise.next
            # Cycle found
            if hare == tortoise:
                # Start third pointer from the head and move it forward until it meets with
                #.  the tortoise
                third = head
                while third != tortoise:
                    third = third.next
                    tortoise = tortoise.next
                return third

        # No cycle found
        return None


import datetime
import os
import sys
from argparse import ArgumentParser
from time import time

import math
import numpy as np
import tensorflow as tf

from lang import Vocabulary, Corpus
from model import LSTM

parser = ArgumentParser()
parser.add_argument("-l", "--lines", type=int,
                    help="Number of lines to read. If not provided, reads the whole file")

parser.add_argument("-bs", "--batch-size", type=int, default=64,
                    help="Train batch size")
parser.add_argument("-e", "--epochs", type=int, default=1,
                    help="Training epochs")

parser.add_argument("-pe", "--print-every", type=int, default=100,
                    help="Number of steps between info printing")
parser.add_argument("-ve", "--val-every", type=int, default=1000,
                    help="Number of steps between evaluation metric computation")
parser.add_argument("-se", "--save-every", type=int, default=1000,
                    help="Number of steps between model checkpoint saving")

parser.add_argument('-t', "--task", choices=["1a", "1b", "1c", "2"], required=True,
                    help="The task to run")

parser.add_argument("-rs", "--random-seed", type=int, default=9,
                    help="Random seed")

args = parser.parse_args()

tf.set_random_seed(args.random_seed)
np.random.seed(args.random_seed)

# --------------------------------------------------------CONSTANTS---------------------------------------------------------------------
# ------values in comment for cluster deployement-------
batch_size = args.batch_size
num_epochs = args.epochs
eval_every = args.val_every
print_every = args.print_every  # limit the number of prints during training
save_every = args.save_every
n_lines = args.lines
print("Variables: batch_size: {}, num_epochs: {}, eval_every: {}, print_every: {}, save_every: {}, n_lines: {}".format(
    batch_size, num_epochs, eval_every, print_every, save_every, n_lines))
max_length = 20  # maximum number of words per sentence during completion
# ------------------------------------------------------
task = args.task

train_path = os.path.join(os.getcwd(), "data", "sentences.train")
eval_path = os.path.join(os.getcwd(), "data", "sentences.eval")
test_path = os.path.join(os.getcwd(), "data", "sentences_test.txt")
embedding_path = os.path.join(os.getcwd(), "data", "wordembeddings-dim100.word2vec")
prompt_path = os.path.join(os.getcwd(), "data", "sentences.continuation")

sentence_len = 30  # padded sentence length including EOS and BOS
vocab_size = 20000  # vocabulary size
clip_grad_norm = 5
model_task2 = "modelC.ckpt"

# ------------------------------------------------------FUNCTIONS---------------------------------------------------------------------

def get_data(corpus, shuffle=False, batch_size=None):
    data = corpus.data
    data_size = data.shape[0]
    if shuffle:
        shuffle_indices = np.random.permutation(np.arange(data_size))
        data = data[shuffle_indices]
    x = data[:, :-1]
    y = data[:, 1:]
    num_batches = 1  # default value
    if batch_size is not None:
        num_batches = int(math.ceil(data_size / batch_size))
    x = np.array_split(x, num_batches)  # split into num_batches batches (last batch size may differ from previous ones)
    y = np.array_split(y, num_batches)  # split into num_batches batches (last batch size may differ from previous ones)
    return np.array(x), np.array(y)

# Trains the model and returns perplexity values on the eval sentences
def train_model(model, sess, num_epochs, train_x_batched, train_y_batched, eval_x_batched, eval_y_batched,
                model_ckpt_name="model.ckpt", eval_every=-1):
    # Training loop
    models_dir = os.path.join(os.getcwd(), "models")
    model_path = os.path.join(models_dir, model_ckpt_name)  # path of file to save model
    num_batches_train = train_x_batched.shape[0]  # nb of batches in the training set
    num_batches_eval = eval_x_batched.shape[0]  # nb of batches in the eval set

    # train_summary_writer = tf.summary.FileWriter(train_summary_dir, sess.graph)
    if os.path.exists(models_dir) and model.load_model(sess, model_path):  # if successfully loaded model
        step, step_loss = eval_model(model, sess, eval_x_batched, eval_y_batched, num_batches_eval)
        perps = get_perplexity(model, sess, eval_x_batched, eval_y_batched,
                               V_train)  # compute perplexities over eval dataset
        mean, median = np.mean(perps), np.median(perps)
        print("\nEvaluating restored model on eval dataset:"
              "\n   batches: {}, step {}, loss {}, perp_mean: {}, perp_median {}\n".format(num_batches_eval, step,
                                                                                step_loss, mean, median))
        return

    # otherwise, train model and save
    print("Training model ...")
    # Initialize all variables
    sess.run(tf.global_variables_initializer())
    for e in range(num_epochs):
        for b in range(num_batches_train):
            _, step, step_loss = model.train_step(sess, train_x_batched[b], train_y_batched[b])
            if step == 1 or step % print_every == 0:
                time_str = datetime.datetime.now().isoformat()
                print("epoch {}, batch {}:\n{}: step {}, loss {}".format(e + 1, b + 1, time_str, step, step_loss))
            if step % save_every == 0:
                model.save_model(sess, model_path)  # save trained model
            if eval_every > 0 and step % eval_every == 0:  # do not evaluate if eval_every <= 0
                perps = get_perplexity(model, sess, eval_x_batched, eval_y_batched,
                                       V_train)  # compute perplexities over eval dataset
                mean, median = np.mean(perps), np.median(perps)
                print("\nEvaluation:\n    batches: {}, step: {}, perp_mean: {}, perp_median: {}\n".format(
                    num_batches_eval, step, mean, median))

    model.save_model(sess, model_path)  # save trained model

def eval_model(model, sess, eval_x_batched, eval_y_batched, num_batches):
    sum = 0
    counter = 0
    for i in range(num_batches):  # compute the loss over each batch
        step, step_loss = model.eval_step(sess, eval_x_batched[i], eval_y_batched[i])
        if not np.isnan(step_loss):
            sum += step_loss
            counter += 1
    step_loss = sum / counter  # compute the mean loss over all batches
    return step, step_loss

def get_perplexity(model, sess, test_x_batched, test_y_batched, V_train):
    num_batches = test_x_batched.shape[0]
    perp = None
    for i in range(num_batches):
        batch_perp = model.perplexity(sess, test_x_batched[i], test_y_batched[i], V_train)
        if perp is None:
            perp = batch_perp
        else:
            perp = np.concatenate((perp, batch_perp))
    return perp

def write_out(array, file_name):
    """
    Writes the given array in a file with the given name inside the output folder.
    If 'filename' already exists, it will be overwritten.

    :param array: data to write
    :param file_name: name of the file
    """
    n = array.shape[0]
    with open(os.path.join('output', file_name), 'w') as output:
        for row in range(n):  # write each row
            output.write(str(array[row]) + '\n')

# --------------------------------------------------------START---------------------------------------------------------------------
# Data IO
print("\nData IO ...")
t = time()
C_train = Corpus(train_path, sentence_len, read_from_file="corpus_train.pkl",
                 n_sentences=n_lines)  # create training corpus from training file
V_train = Vocabulary(vocab_size, read_from_file="vocab.pkl")  # object to represent training vocabulary
V_train.build_from_corpus(C_train, write_to_file="vocab.pkl")  # build vocabulary from training corpus
C_train.build_data_from_vocabulary(V_train,
                                   write_to_file="corpus_train.pkl")  # build corpus data matrix from vocabulary, write object to disk

C_eval = Corpus(eval_path, sentence_len, read_from_file="corpus_eval.pkl",
                n_sentences=n_lines)  # create evaluation corpus from evaluation file
C_eval.build_data_from_vocabulary(V_train,
                                  write_to_file="corpus_eval.pkl")  # build corpus data matrix from vocabulary, write object to disk

C_test = Corpus(test_path, sentence_len, read_from_file="corpus_test.pkl",
                n_sentences=n_lines)  # create test corpus from test file
C_test.build_data_from_vocabulary(V_train,
                                  write_to_file="corpus_test.pkl")  # build corpus data matrix from vocabulary, write object to disk

print("Total time (s): ", time() - t, "\n")

print("Vocab size: ", V_train.vocab_size)
print("Train data matrix shape: ", C_train.data.shape)
print("Eval data matrix shape: ", C_eval.data.shape)

# Train and Eval data
train_x_batched, train_y_batched = get_data(C_train, shuffle=True,
                                            batch_size=batch_size)  # training data, shuffled, batched
# eval and test data batched but not shuffled
eval_x_batched, eval_y_batched = get_data(C_eval, batch_size=batch_size)
test_x_batched, test_y_batched = get_data(C_test, batch_size=batch_size)
# Constants
vocab_size = V_train.vocab_size  # get true vocab size
time_steps = sentence_len - 1

# Tensorboard constants
timestamp = str(int(time()))
out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", timestamp))
train_summary_dir = os.path.join(out_dir, "summaries", "train")

# Models
if task == "1a":
    with tf.Graph().as_default():  # create graph for Experiment A
        with tf.Session() as sess:
            print("\nRunning Experiment A ...")
            # input("Press Enter to continue...")
            modelA = LSTM(V_train, embedding_size=100, hidden_size=512, time_steps=time_steps, clip_norm=clip_grad_norm)
            train_model(modelA, sess, num_epochs, train_x_batched, train_y_batched, eval_x_batched, eval_y_batched,
                        model_ckpt_name="modelA.ckpt", eval_every=eval_every)
            perp = get_perplexity(modelA, sess, test_x_batched, test_y_batched,
                                  V_train)  # compute perplexities on test set
            write_out(perp, "group17.perplexityA")
elif task == "1b":
    with tf.Graph().as_default():  # create graph for Experiment B
        with tf.Session() as sess:
            print("\nRunning Experiment B ...")
            # input("Press Enter to continue...")
            modelB = LSTM(V_train, embedding_size=100, hidden_size=512, time_steps=time_steps, clip_norm=clip_grad_norm,
                          load_external_embedding=True, embedding_path=embedding_path)
            train_model(modelB, sess, num_epochs, train_x_batched, train_y_batched, eval_x_batched, eval_y_batched,
                        model_ckpt_name="modelB.ckpt", eval_every=eval_every)
            perp = get_perplexity(modelB, sess, test_x_batched, test_y_batched,
                                  V_train)  # compute perplexities on test set
            write_out(perp, "group17.perplexityB")
elif task == "1c":
    with tf.Graph().as_default():  # create graph for Experiment C
        with tf.Session() as sess:
            print("\nRunning Experiment C ...")
            # input("Press Enter to continue...")
            modelC = LSTM(V_train, embedding_size=100, hidden_size=1024, time_steps=time_steps,
                          clip_norm=clip_grad_norm, down_project=True, down_projection_size=512)
            train_model(modelC, sess, num_epochs, train_x_batched, train_y_batched, eval_x_batched, eval_y_batched,
                        model_ckpt_name="modelC.ckpt", eval_every=eval_every)
            perp = get_perplexity(modelC, sess, test_x_batched, test_y_batched,
                                  V_train)  # compute perplexities on test set
            write_out(perp, "group17.perplexityC")
elif task == "2":
    with tf.Graph().as_default():  # create graph for task 2
        print("\nRunning Task 2 ...")
        # Read sentences to complete
        sentences = []
        with open(prompt_path) as sentences_file:
            for sentence in sentences_file:
                tokens = sentence.strip().split(" ")
                sentences.append(tokens)

        # Pick the model to use
        models_dir = os.path.join(os.getcwd(), "models")
        model_path = os.path.join(models_dir, model_task2)
        if model_task2 == "modelA.ckpt":
            model2 = LSTM(V_train, embedding_size=100, hidden_size=512, time_steps=time_steps, clip_norm=clip_grad_norm)
        elif model_task2 == "modelB.ckpt":
            model2 = LSTM(V_train, embedding_size=100, hidden_size=512, time_steps=time_steps, clip_norm=clip_grad_norm,
                          load_external_embedding=True, embedding_path=embedding_path)
        elif model_task2 == "modelC.ckpt":
            model2 = LSTM(V_train, embedding_size=100, hidden_size=1024, time_steps=time_steps,
                          clip_norm=clip_grad_norm, down_project=True, down_projection_size=512)

        with tf.Session() as sess:
            # Load model
            if not os.path.exists(models_dir) or not model2.load_model(sess, model_path):  # if couldn't load model
                print("Please train the model first.")
                sys.exit(-1)

            # Complete sentences
            model2.build_sentence_completion_graph()
            print("\nSentence Completion Graph Built")
            completed_sentences = []
            nb_completed = 0
            for sentence in sentences:  # for each tokenized sentence
                prompt = V_train.get_tokens_ids(sentence)
                continuation = model2.sentence_continuation(sess, prompt, V_train, max_length)
                nb_completed += 1
                if nb_completed == 1 or nb_completed % print_every == 0:
                    print("Completed {} sentence".format(nb_completed))
                completed_sentences.append(sentence + continuation)

        # Write out completed sentences
        write_path = os.path.join(os.getcwd(), "output", "group17.continuation")
        print("\nWriting Completed Sentences")
        with open(write_path, "w") as file:
            for sentence in completed_sentences:
                for word in sentence:
                    if word == V_train.PAD_token or word == V_train.BOS_token:
                        continue
                    file.write(word + ' ')
                file.write('\n')
else:
    raise ValueError("Wut unrecognized task {}".format(task))


class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
#         START, END = 0, 1
#         # 여기도 interval 순서는 뒤죽박죽...
#         intervals.sort(key=lambda interval: interval[START])

#         last_end = float("-inf")
#         answer = 0
#         for interval in intervals:
#             # last_end가 갱신되어야 하는 경우
#             if last_end > interval[START]:
#                 # last_end가 갱신되는 경우.
#                 answer += 1
#                 # END가 더 큰거를 없애야 함. 뒤에 영향이 안 가게..
#                 last_end = min(last_end, interval[END])
#             else:
#                 # 겹치지 않는 경우엔 아니면 last_end를 업데이트해준다.
#                 last_end = interval[END]

#         return answer

        # end time으로 정렬하면 더 간단한 greedy가 됨. end가 큰 것만 greedy하게 없애는 것..
        START, END = 0, 1
        # 여기도 interval 순서는 뒤죽박죽...
        intervals.sort(key=lambda interval: interval[END])

        last_end = float("-inf")
        answer = 0
        for interval in intervals:
            if last_end > interval[START]:
                # last_end가 갱신되는 경우. 여기선 min을 볼 필요가 없음.
                answer += 1
            else:
                # 겹치지 않는 경우엔 아니면 last_end를 업데이트해준다.
                last_end = interval[END]

        return answer


# -*- coding: utf-8 -*-
import copy
import numpy as np
import os
import sys
from struct import unpack, pack
import torch
import torch.nn as nn

from utils import hparams as hp

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

def log_config():
    print(f'PID = {os.getpid()}')
    print(f'PyTorch version = {torch.__version__}')
    if "CUDA_VISIBLE_DEVICES" in os.environ:
        print('cuda device = {}'.format(os.environ['CUDA_VISIBLE_DEVICES']))
    for key in hp.__dict__.keys():
        if not '__' in key:
            print('{} = {}'.format(key, eval('hp.'+key)))

def frame_stacking(x, x_lengths, stack):
    batch_size = x.shape[0]
    newlen = x.shape[1] // stack
    x_lengths = x_lengths // stack
    stacked_x = x[:, 0:newlen*stack].reshape(batch_size, newlen, hp.lmfb_dim * stack)
    return stacked_x, x_lengths

def load_model(model_file):
    """
    To load PyTorch models either of single-gpu and multi-gpu based model
    """
    model_state = torch.load(model_file)
    is_multi_loading = True if torch.cuda.device_count() > 1 else False
    # This line may include bugs!!
    is_multi_loaded = True if 'module' in list(model_state.keys())[0] else False

    if is_multi_loaded is is_multi_loading:
        return model_state

    # the model to load is multi-gpu and the model to use is single-gpu
    elif is_multi_loaded is False and is_multi_loading is True:
        new_model_state = {}
        for key in model_state.keys():
            new_model_state['module.'+key] = model_state[key]

        return new_model_state
    elif is_multi_loaded is True and is_multi_loading is False:
        new_model_state = {}
        for key in model_state.keys():
            new_model_state[key[7:]] = model_state[key]
        return new_model_state
    else:
        print('ERROR in load model')
        sys.exit(1)

def overwrite_hparams(args):
    for key, value in args._get_kwargs():
        if value is not None and value != 'load_name':
            setattr(hp, key, value)

def fill_variables():
    if hasattr(hp, 'num_hidden_nodes'):
        num_hidden_nodes_encoder = hp.num_hidden_nodes
        num_hidden_nodes_decoder = hp.num_hidden_nodes
    else:
        num_hidden_nodes_encoder = 512
        num_hidden_nodes_decoder = 512

    default_var = {'spm_model':None, 'T_norm':True, 'B_norm':False, 'save_per_epoch':1, 'lr_adjust_epoch': 20,
                   'reset_optimizer_epoch': 40, 'num_hidden_nodes_encoder':num_hidden_nodes_encoder, 'num_hidden_nodes_decoder':num_hidden_nodes_decoder,
                    'comment':'', 'load_name_lm':None, 'shuffle': False, 'num_mask_F':1, 'num_mask_T':1,
                    'max_width_F':23, 'max_width_T':100}
    for key, value in default_var.items():
        if not hasattr(hp, key):
            # print('{} is not found in hparams. defalut {} is used.'.format(key, value))
            setattr(hp, key, value)


import time
from picamera import PiCamera
import subprocess
import sys
from gpiozero import LED
from gpiozero import Button
from signal import pause

IMAGE_FILE = "/home/pi/Pictures/camera.jpg"
OUTPUT_TEXT = "/home/pi/Pictures/output"
TESS_PATH = "/usr/local/bin/"
COMMAND = [TESS_PATH+"tesseract", IMAGE_FILE, OUTPUT_TEXT]
SECS = 1

global lights_on

def main():

    global lights_on
    lights_on = False

    def capture():
        global lights_on
        if not lights_on:
            led.on()
            camera.start_preview()
            lights_on = True
        else:
            camera.capture(IMAGE_FILE, use_video_port=True)
            camera.stop_preview()
            led.blink()
            subprocess.run(COMMAND)
            led.off()

            with open(OUTPUT_TEXT + '.txt') as f:
                read_data = f.read()
            print(read_data)
            doing_ocr = False
            lights_on = False

    led = LED(17)
    #pause()
    button = Button(2)
    button.when_pressed = capture
    camera = PiCamera()
    #camera.resolution = (1920, 1080)
    camera.video_stabilization = True
    while True:
        time.sleep(SECS)

if __name__ == "__main__":
    main()
    sys.exit()


import pygame
import os
import generation
import tiles

def wfc(r, c, pixels):
    # Visuals setup
    os.environ["SDL_VIDEO_CENTERED"] = '1'

    black = (0, 0, 0)
    images = tiles.load_images(pixels)

    #  pygame setup
    size = (c * pixels, r * pixels)
    pygame.init()
    pygame.display.set_caption("Tile Map Generation")
    screen = pygame.display.set_mode(size)
    clock = pygame.time.Clock()
    fps = 5

    #  Data structure setup
    tile_grid = generation.TileGrid(c, r, pixels)

    # Running
    run = True
    keep_generating = True
    while run:
        clock.tick(fps)
        # Update the game
        if keep_generating: keep_generating = tile_grid.iterate()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    # tile_grid.test()
                    # run = tile_grid.iterate()
                    keep_generating = not keep_generating
                elif event.key == pygame.K_BACKSPACE:
                    tile_grid.reset()
                    keep_generating = True
        screen.fill(black)
        tile_grid.update(surface=screen, images=images)
        pygame.display.update()
    pygame.quit()


"""

A girl lost the key to her locker (note: The key is only numeric). She remembers the number of digits N as well as the sum S of all the digits of her password. She also knows that her password is the largest number of N digits that can be possible with a given sum S.

Implement a function that would help her retrieve her key.

Input#
The number of digits and the sum of those digits

Output#
The largest number that can be created (or the key) as a list

Sample input#
sum_of_digits = 20
number_of_digits = 3
Sample output#
result = [9, 9, 2]

The time complexity is O(n)
 because we can simply find the solution in one iteration.
"""

def find_largest_number(digits, sum):
    if sum == 0:
        if digits == 1:
            return [0]
        else:
            return [-1]
    if sum > 9 * digits:
        return [-1]
    result = [0] * digits
    for i in range(digits):
        if sum >= 9:
            result[i] = 9
            sum -= 9
        else:
            result[i] = sum
            sum = 0
    return result

# Driver code to test above function
if __name__ == '__main__':

    sum_of_digits = 20
    number_of_digits = 3

    print(find_largest_number(number_of_digits, sum_of_digits))

    sum_of_digits = 100
    number_of_digits = 2

    print(find_largest_number(number_of_digits, sum_of_digits))