import glob, os
import numpy as np
import random
import librosa.display
import librosa
from xml.dom import minidom
from scipy import signal
from random import randint
import pickle
from matplotlib import pyplot as plt
import pandas as pd
import math
import datetime
from AnnotationReader import *

class Preprocessing:

    def __init__(self, species_folder, lowpass_cutoff,
                 downsample_rate, nyquist_rate, segment_duration,
                 positive_class, background_class,
                 augmentation_amount_positive_class,
                 augmentation_amount_background_class,
                 n_fft, hop_length, n_mels, f_min, f_max, file_type, audio_extension):
        self.species_folder = species_folder
        self.lowpass_cutoff = lowpass_cutoff
        self.downsample_rate = downsample_rate
        self.nyquist_rate = nyquist_rate
        self.segment_duration = segment_duration
        self.augmentation_amount_positive_class = augmentation_amount_positive_class
        self.augmentation_amount_background_class = augmentation_amount_background_class
        self.positive_class = positive_class
        self.background_class = background_class
        self.audio_path = self.species_folder + '/Audio/'
        self.annotations_path = self.species_folder + '/Annotations/'
        self.saved_data_path = self.species_folder + '/Saved_Data/'
        self.training_files = self.species_folder + '/DataFiles/TestingFiles.txt'
        self.n_ftt = n_fft
        self.hop_length = hop_length
        self.n_mels = n_mels
        self.f_min = f_min
        self.f_max = f_max
        self.file_type = file_type
        self.audio_extension = audio_extension

    def update_audio_path(self, audio_path):
        self.audio_path = self.species_folder + '/'+ audio_path

    def read_audio_file(self, file_name):
        '''
        file_name: string, name of file including extension, e.g. "audio1.wav"

        '''
        # Get the path to the file
        audio_folder = os.path.join(file_name)

        # Read the amplitudes and sample rate
        audio_amps, audio_sample_rate = librosa.load(audio_folder, sr=None)

        return audio_amps, audio_sample_rate

    def butter_lowpass(self, cutoff, nyq_freq, order=4):
        normal_cutoff = float(cutoff) / nyq_freq
        b, a = signal.butter(order, normal_cutoff, btype='lowpass')
        return b, a

    def butter_lowpass_filter(self, data, cutoff_freq, nyq_freq, order=4):
        # Source: https://github.com/guillaume-chevalier/filtering-stft-and-laplace-transform
        b, a = self.butter_lowpass(cutoff_freq, nyq_freq, order=order)
        y = signal.filtfilt(b, a, data)
        return y

    def downsample_file(self, amplitudes, original_sr, new_sample_rate):
        '''
        Downsample an audio file to a given new sample rate.
        amplitudes:
        original_sr:
        new_sample_rate:

        '''
        return librosa.resample(amplitudes,
                                original_sr,
                                new_sample_rate,
                                res_type='kaiser_fast'), new_sample_rate

    def convert_single_to_image(self, audio):
        '''
        Convert amplitude values into a mel-spectrogram.
        '''
        S = librosa.feature.melspectrogram(audio, n_fft=self.n_ftt,hop_length=self.hop_length,
                                           n_mels=self.n_mels, fmin=self.f_min, fmax=self.f_max)

        image = librosa.core.power_to_db(S)
        image_np = np.asmatrix(image)
        image_np_scaled_temp = (image_np - np.min(image_np))
        image_np_scaled = image_np_scaled_temp / np.max(image_np_scaled_temp)
        mean = image.flatten().mean()
        std = image.flatten().std()
        eps=1e-8
        spec_norm = (image - mean) / (std + eps)
        spec_min, spec_max = spec_norm.min(), spec_norm.max()
        spec_scaled = (spec_norm - spec_min) / (spec_max - spec_min)
        S1 = spec_scaled

        # 3 different input
        return S1

    def convert_all_to_image(self, segments):
        '''
        Convert a number of segments into their corresponding spectrograms.
        '''
        spectrograms = []
        for segment in segments:
            spectrograms.append(self.convert_single_to_image(segment))

        return np.array(spectrograms)

    def add_extra_dim(self, spectrograms):
        '''
        Add an extra dimension to the data so that it matches
        the input requirement of Tensorflow.
        '''
        spectrograms = np.reshape(spectrograms,
                                  (spectrograms.shape[0],
                                   spectrograms.shape[1],
                                   spectrograms.shape[2],1))
        return spectrograms

    def time_shift(self, audio, shift):
        '''
        Shift ampltitude values (to the right) by a random value.
        Values are wrapped back to the left.
        '''

        augmented = np.zeros(len(audio))
        augmented [0:shift] = audio[-shift:]
        augmented [shift:] = audio[:-shift]
        return augmented

    def augment_single_segment(self, segment, label, meta_time, verbose):
        '''
        Augment a segment of amplitude values a number of times (pre-defined).
        Augmenting is done by applying a time shift.
        '''

        augmented_segments = []
        augmented_metadata = []
        augmented_labels = []

        if label in self.positive_class:
            augmentation_amount = self.augmentation_amount_positive_class
        else:
            augmentation_amount = self.augmentation_amount_background_class

        for i in range (0, augmentation_amount):

            if verbose:
                print ('augmentation iteration', i)
                print ('----------------------------')

            # Randomly select amount to shift by
            random_time_point_segment = randint(1, len(segment)-1)

            # Time shift
            segment = self.time_shift(segment, random_time_point_segment)

            # Append the augmented segments
            augmented_segments.append(segment)

            # Duplicate the metadata for each augmentation example
            # as the metadata should not change
            augmented_metadata.append(meta_time)

            # Append the label (a multiclass problem can be converted into
            # one-hot encoded vectors at a later stage)
            augmented_labels.append(label)

        return augmented_segments, augmented_metadata, augmented_labels

    def getXY(self, audio_amplitudes, start_sec, annotation_duration_seconds,
        label, file_name_no_extension, verbose):
        '''
        Extract a number of segments based on the user-annotations.
        If possible, a number of segments are extracted provided
        that the duration of the annotation is long enough. The segments
        are extracted by shifting by 1 second in time to the right.
        Each segment is then augmented a number of times based on a pre-defined
        user value.
        '''

        if verbose == True:
            print ('start_sec', start_sec)
            print ('annotation_duration_seconds', annotation_duration_seconds)
            print ('self.segment_duration ', self.segment_duration )

        X_augmented_segments = []
        X_meta_augmented_segments = []
        Y_augmented_labels = []

        # Calculate how many segments can be extracted based on the duration of
        # the annotated duration. If the annotated duration is too short then
        # simply extract one segment. If the annotated duration is long enough
        # then multiple segments can be extracted.
        if annotation_duration_seconds-self.segment_duration < 0:
            segments_to_extract = 1
        else:
            segments_to_extract = annotation_duration_seconds-self.segment_duration+1

        if verbose:
            print ("segments_to_extract", segments_to_extract)

        if label in self.background_class:
            if segments_to_extract > 15:
                segments_to_extract = 15

        for i in range (0, segments_to_extract):
            if verbose:
                print ('Semgnet {} of {}'.format(i, segments_to_extract-1))
                print ('*******************')

            # Set the correct location to start with.
            # The correct start is with respect to the location in time
            # in the audio file start+i*sample_rate
            start_data_observation = start_sec*self.downsample_rate+i*(self.downsample_rate)
            # The end location is based off the start
            end_data_observation = start_data_observation + (self.downsample_rate*self.segment_duration)

            # This case occurs when something is annotated towards the end of a file
            # and can result in a segment which is too short.
            if end_data_observation > len(audio_amplitudes):
                continue

            # Extract the segment of audio
            X_audio = audio_amplitudes[start_data_observation:end_data_observation]

            # Determine the actual time for the event
            start_time_seconds = start_sec + i

            # Extract some meta data (here, the hour at which the vocalisation event occured)
            meta_time = self.get_metadata(file_name_no_extension, start_time_seconds)

            if verbose == True:
                print ('start frame', start_data_observation)
                print ('end frame', end_data_observation)

            # Augment the segment a number of times
            X_augmented, X_meta_augmented, Y_augmented = self.augment_single_segment(X_audio, label, meta_time, verbose)

            # Extend the augmented segments and labels (and the metadata)
            X_augmented_segments.extend(X_augmented)
            X_meta_augmented_segments.extend(X_meta_augmented)
            Y_augmented_labels.extend(Y_augmented)

        return X_augmented_segments, X_meta_augmented_segments, Y_augmented_labels

    def save_data_to_pickle(self, X, Y):
        '''
        Save all of the spectrograms to a pickle file.

        '''
        outfile = open(os.path.join(self.saved_data_path, 'X-pow.pkl'),'wb')
        pickle.dump(X, outfile, protocol=4)
        outfile.close()

        outfile = open(os.path.join(self.saved_data_path, 'Y-pow.pkl'),'wb')
        pickle.dump(Y, outfile, protocol=4)
        outfile.close()

    def load_data_from_pickle(self):
        '''
        Load all of the spectrograms from a pickle file

        '''
        infile = open(os.path.join(self.saved_data_path, 'X.pkl'),'rb')
        X = pickle.load(infile)
        infile.close()

        infile = open(os.path.join(self.saved_data_path, 'Y.pkl'),'rb')
        Y = pickle.load(infile)
        infile.close()

        return X, Y

    def get_metadata(self, filename, time_to_add):

        '''
            Get some metadata from the file name.

            In this case, get the hour at which the particular vocalisation
            event occured. This is computed using the timestamp in the .wav
            file name and by adding on the time at which the event occured
            within the file. All vocalisation events are included, i.e.
            presence and absence events.

            filename: string, file name
            time_to_add: integer, seconds for which event occured

            Assumption: the filename has the following format:
            HGSM3XX_0+1_YYYYMMDD_HHMMSS
            Where XX = location (A, B, C, AB, ...)

            Return: Integer (0, 1, ..., 23)

        '''

        print('meta')
        print('*********')
        print ('filename',filename)

        # Find the position of 'SM3' to determine the location name
        index_SM3 = filename.find('SM3')+3

        # Find the position of the first underscore
        index_first_underscore = filename.find('_')

        # Extract location name
        location = filename[index_SM3:index_first_underscore]

        # Find the position of the second underscore
        index_second_underscore = filename.find('0+1')+4

        # Find the position of the last underscore
        index_last_underscore = filename.rfind('_')

        # Get the date component from the string
        date_found = filename[index_second_underscore:index_last_underscore]

        # Get the time component from the string
        time = filename[index_last_underscore+1:]

        print('location',location)
        print('date found',date_found)
        print ('time found',time)

        year = int(date_found[0:4])
        month = int(date_found[4:6])
        date = int(date_found[6:])
        hour = int(time[0:2])
        minutes = int(time[2:4])
        seconds = int(time[4:])

        date_and_time = datetime.datetime(year, month, date, hour, minutes, seconds)
        print ('original time',date_and_time)

        # Compute the new time by taking the starting time from the file name
        # and adding on the time of the vocalisation event.
        new_time = date_and_time + datetime.timedelta(seconds=time_to_add)
        print ('new time', new_time)
        print ('encoded time', new_time.hour)

        # Add any other logical operations here on how to encode the time component
        # example 1: if hour < 12 then return 'am', else return 'pm'
        # example 2: if date_and_time.hour >= 0 and date_and_time.hour < 4 return 'early'
        # Other things can also be returned, e.g. location, or any other metadata

        return new_time.hour

    def create_dataset(self, verbose):
        '''
        Create X and Y values which are inputs to a ML algorithm.
        Annotated files (.svl) are read and the corresponding audio file (.wav)
        is read. A low pass filter is applied, followed by downsampling. A
        number of segments are extracted and augmented to create the final dataset.
        Annotated files (.svl) are created using SonicVisualiser and it is assumed
        that the "boxes area" layer was used to annotate the audio files.
        '''

        # Keep track of how many calls were found in the annotation files
        total_calls = 0

        # Initialise lists to store the X and Y values
        X_calls = []
        X_meta = []
        Y_calls = []

        if verbose == True:
            print ('Annotations path:',self.annotations_path+"*.svl")
            print ('Audio path',self.audio_path+"*.wav")

        # Read all names of the training files
        training_files = pd.read_csv(self.training_files, header=None)

        # Iterate over each annotation file
        for training_file in training_files.values:

            file = training_file[0]

            if self.file_type == 'svl':
                # Get the file name without paths and extensions
                file_name_no_extension = file
                #print ('file_name_no_extension', file_name_no_extension)
            if self.file_type == 'raven_caovitgibbons':
                file_name_no_extension = file[file.rfind('-')+1:file.find('.')]

            print ('Processing:',file_name_no_extension)

            reader = AnnotationReader(file, self.species_folder, self.file_type, self.audio_extension)

            # Check if the .wav file exists before processing
            if self.audio_path+file_name_no_extension+self.audio_extension  in glob.glob(self.audio_path+"*"+self.audio_extension):
                print('Found file')

                # Read audio file
                audio_amps, original_sample_rate = self.read_audio_file(self.audio_path+file_name_no_extension+self.audio_extension)
                print('Filtering...')
                # Low pass filter
                filtered = self.butter_lowpass_filter(audio_amps, self.lowpass_cutoff, self.nyquist_rate)

                print('Downsampling...')
                # Downsample
                amplitudes, sample_rate = self.downsample_file(filtered, original_sample_rate, self.downsample_rate)

                df, audio_file_name = reader.get_annotation_information()

                print('Reading annotations...')
                for index, row in df.iterrows():

                    start_seconds = int(round(row['Start']))
                    end_seconds = int(round(row['End']))
                    label = row['Label']
                    annotation_duration_seconds = end_seconds - start_seconds

                    # Extract augmented audio segments and corresponding binary labels
                    X_data, X_meta_data, y_data = self.getXY(amplitudes, start_seconds,
                                                annotation_duration_seconds, label,
                                                file_name_no_extension, verbose)

                    # Convert audio amplitudes to spectrograms
                    X_data = self.convert_all_to_image(X_data)

                    # Append the segments and labels
                    X_calls.extend(X_data)
                    X_meta.extend(X_meta_data)
                    Y_calls.extend(y_data)

        # Convert to numpy arrays
        X_calls, X_meta, Y_calls = np.asarray(X_calls), np.asarray(X_meta), np.asarray(Y_calls)

        # Add extra dimension
        X_calls = self.add_extra_dim(X_calls)

        return X_calls, X_meta, Y_calls


from app import mail
from flask import current_app, render_template
from flask_mail import Message

class Helper:
    @staticmethod
    def enviar_email(cls, subject, to, template, **kwargs) -> None:
        msg = Message(
            subject=subject,
            sender=current_app.config["MAIL_SENDER"],
            recipients=[to],
        )

        msg.body = render_template(f"mails/{template}.txt", **kwargs)
        msg.html = render_template(f"mails/{template}.html", **kwargs)
        mail.send(msg)


# The ZeroDivisionError exception (being a concrete case of the ArithmeticError exception class) is raised INSIDE the bad_fun() function,
#   and it doesn't leave the function – the function itself takes care of it.

def bad_fun(n):
    try:
        return 1 / n
    except ArithmeticError:
        print("Arithmetic Problem!")
    return None

bad_fun(0)

print("THE END.")


"""
Algorythm ex {
    Debut
        saisir(n)
        src <- "source.txt"
        remplir (src, n)
        genererM(src, n, m)
        triN(m,n)
        src1 <- "source1.txt"
        generer(src , m, n)

    Fin
}

"""

import random as r
import numpy as np
from numpy.lib.index_tricks import mgrid

def saisir():
    global n
    quit = False
    while not quit:
        n = int(input("Enter n "))
        quit = n in range(3, 16)

def remplirS(n, src):
    f = open(src, "w")
    for i in range(n):
        quit = False
        while not quit:
            lg = input("Enter Number structure ")
            quit = lg.isnumeric()
        f.write(lg+"\n")

def remplireMat(src, n, m):
    myFile = open(src, "r")
    lines = myFile.readlines()
    for i in range(n):
        mf = lines[i][0:len(lines[i])-1]
        print(mf)
        while len(mf) < n:
            random = str(r.randint(0, 10))
            print(random)
            mf = random + mf
            print("\n")
            print(mf)
        for j in range(n):
            m[i][j] = mf[j]

        # PP
n = 0

saisir()

m = np.array([[""]*n]*n, dtype=str)
src0 = "source0.txt"

remplirS(n, src0)

remplireMat(src0, n, m)


import pdfplumber

class PdfExtractor:
    @staticmethod
    def getTextFromPdf(self, pdf):
        pdf_obj = pdfplumber.open(pdf)
        list = []
        for page in pdf_obj.pages:
            list.append(page.extract_text())
        pdf_obj.close()
        return list

    @staticmethod
    def getTextFromPage(self, pdf, page_num):
        pdf_obj = pdfplumber.open(pdf)
        page = pdf_obj.pages[page_num - 1]
        text = page.extract_text()
        pdf_obj.close()
        return text

    @staticmethod
    def getPages(self, pdf):
        pdf_obj = pdfplumber.open(pdf)
        pages = pdf_obj.pages
        pdf_obj.close()
        return pages


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time, datetime, random

from flask import request, redirect, render_template, url_for, abort, flash, g, current_app
from flask.views import MethodView
from flask.ext.login import current_user, login_required

from . import models, forms, signals
from accounts.models import User
from accounts.permissions import admin_permission, editor_permission, writer_permission, reader_permission
from OctBlog.config import OctBlogSettings

# POST_TYPES = ('post', 'page')
POST_TYPES = OctBlogSettings['post_types']
PER_PAGE = OctBlogSettings['pagination'].get('admin_per_page', 10)

article_models = {
    'post': models.Post,
    'draft': models.Draft
}

def get_current_user():
    user = User.objects.get(username=current_user.get_id())
    return user

class AdminIndex(MethodView):
    decorators = [login_required]
    template_name = 'blog_admin/index.html'

    def get(self):
        blog_meta = OctBlogSettings['blog_meta']
        user = get_current_user()
        return render_template(self.template_name, blog_meta=blog_meta, user=user)

class PostsList(MethodView):
    decorators = [login_required]
    template_name = 'blog_admin/posts.html'
    is_draft = False
    article_model = models.Post

    def get(self, post_type='post'):
        posts = self.article_model.objects.filter(post_type=post_type).order_by('-update_time')

        if not g.identity.can(editor_permission):
            posts = posts.filter(author=get_current_user())

        try:
            cur_page = int(request.args.get('page', 1))
        except:
            cur_page = 1

        posts = posts.paginate(page=cur_page, per_page=PER_PAGE)

        return render_template(self.template_name, posts=posts, post_type=post_type, is_draft=self.is_draft)

class DraftList(PostsList):
    is_draft = True
    article_model = models.Draft

class PostStatisticList(MethodView):
    decorators = [login_required, editor_permission.require(401)]
    template_name = 'blog_admin/post_statistics.html'

    def get(self):
        posts = models.PostStatistics.objects.all()

        cur_page = request.args.get('page', 1)
        if not cur_page:
            abort(404)
        posts = posts.paginate(page=int(cur_page), per_page=PER_PAGE)

        return render_template(self.template_name, posts=posts)

class Post(MethodView):
    decorators = [login_required, writer_permission.require(401)]
    template_name = 'blog_admin/post.html'

    def get(self, slug=None, form=None, post_type='post', is_draft=False):
        edit_flag = slug is not None or False
        post = None

        if edit_flag:
            try:
                post = models.Draft.objects.get(slug=slug)
            except models.Draft.DoesNotExist:
                post = models.Post.objects.get_or_404(slug=slug)

            if not g.identity.can(editor_permission) and post.author.username != current_user.username:
                abort(401)

        display_slug = slug if slug else 'slug-value'

        if not form:
            if post:
                # post = models.Post.objects.get_or_404(slug=slug)
                post.post_id = str(post.id)
                post.tags_str = ', '.join(post.tags)
                form = forms.PostForm(obj=post)
            else:
                form = forms.PostForm(post_type=post_type)

        categories = models.Post.objects.distinct('category')
        tags = models.Post.objects.distinct('tags')

        context = {'edit_flag':edit_flag, 'form':form, 'display_slug':display_slug,
            'categories':categories, 'tags':tags
        }

        # return context
        return render_template(self.template_name, **context)

    def post(self, slug=None, post_type='post', is_draft=False):
        article_model = article_models['post'] if request.form.get('publish') else article_models['draft']

        form = forms.PostForm(obj=request.form)
        if not form.validate():
            return self.get(slug, form)

        # if slug:
        #     post = article_model.objects.get_or_404(slug=slug)
        # else:
        #     post = article_model()
        #     post.author = get_current_user()

        try:
            post = article_model.objects.get_or_404(slug=slug)
        except:
            post = article_model()
            post.author = get_current_user()

        post.title = form.title.data.strip()
        post.slug = form.slug.data.strip()
        post.raw = form.raw.data.strip()
        abstract = form.abstract.data.strip()
        post.abstract = abstract if abstract else post.raw[:140]
        post.category = form.category.data.strip() if form.category.data.strip() else None
        post.tags = [tag.strip() for tag in form.tags_str.data.split(',')] if form.tags_str.data else None
        post.post_type = form.post_type.data if form.post_type.data else None

        if request.form.get('publish'):
            post.is_draft = False
            msg = 'Succeed to publish the {0}'.format(post_type)
            redirect_url = url_for('blog_admin.pages') if form.post_type.data == 'page' else url_for('blog_admin.posts')
            post.save()

            signals.post_pubished.send(current_app._get_current_object(), post=post)

            try:
                draft = models.Draft.objects.get(slug=slug)
                draft.delete()
            except:
                pass

            try:
                post_statistic = models.PostStatistics.objects.get(post=post)
            except models.PostStatistics.DoesNotExist:
                post_statistic = models.PostStatistics()
                post_statistic.post = post
                post_statistic.verbose_count_base = random.randint(500, 5000)
                post_statistic.save()

        elif request.form.get('draft'):
            post.is_draft = True
            msg = 'Succeed to save the draft'
            redirect_url = url_for('blog_admin.page_drafts') if form.post_type.data == 'page' else url_for('blog_admin.drafts')
            post.save()
        else:
            return self.get(slug, form, is_draft)

        flash(msg, 'success')
        return redirect(redirect_url)

    def delete(self, slug):
        if request.args.get('is_draft') and request.args.get('is_draft').lower()=='true':
            article_model = article_models['draft']
        else:
            article_model = article_models['post']
        post = article_model.objects.get_or_404(slug=slug)
        post_type = post.post_type
        # is_draft = post.is_draft

        try:
            post_statistic = models.PostStatistics.objects.get(post=post)
            post_statistic.delete()
        except:
            pass

        post.delete()

        redirect_url = url_for('blog_admin.pages') if post_type == 'page' else url_for('blog_admin.posts')
        # if is_draft:
        #     redirect_url = redirect_url + '?draft=true'

        flash('Succeed to delete the {0}'.format(post_type), 'success')

        if request.args.get('ajax'):
            return 'success'

        return redirect(redirect_url)

class SuPostsList(MethodView):
    decorators = [login_required, admin_permission.require(401)]
    template_name = 'blog_admin/su_posts.html'

    def get(self):
        posts = models.Post.objects.all().order_by('-update_time')
        cur_type = request.args.get('type')
        # post_types = posts.distinct('post_type')
        if cur_type:
            posts = posts.filter(post_type=cur_type)

        cur_page = request.args.get('page', 1)
        if not cur_page:
            abort(404)
        posts = posts.paginate(page=int(cur_page), per_page=PER_PAGE)

        data = {
            'posts':posts,
            'post_types': POST_TYPES,
            'cur_type': cur_type
        }

        return render_template(self.template_name, **data)

class SuPost(MethodView):
    decorators = [login_required, admin_permission.require(401)]
    template_name = 'blog_admin/su_post.html'

    def get_context(self, slug, form=None):
        post = models.Post.objects.get_or_404(slug=slug)

        if not form:
            # post.post_id = str(post.id)
            # post.tags_str = ', '.join(post.tags)
            form = forms.SuPostForm(obj=post)

        categories = models.Post.objects.distinct('category')
        tags = models.Post.objects.distinct('tags')

        context = {'form':form, 'display_slug':slug, 'post': post,
            'categories':categories, 'tags':tags, 'post_types': POST_TYPES,
        }

        return context

    def get(self, slug, form=None):
        context = self.get_context(slug, form)
        return render_template(self.template_name, **context)

    def post(self, slug):
        form = forms.SuPostForm(request.form)
        if not form.validate():
            return self.get(slug, form)

        # return form.author.data.username

        # if slug:
        #     post = models.Post.objects.get_or_404(slug=slug)
        # else:
        #     post = models.Post()
        #     post.author = get_current_user()

        post = models.Post.objects.get_or_404(slug=slug)

        post.title = form.title.data.strip()
        post.slug = form.slug.data.strip()
        post.fix_slug = form.fix_slug.data.strip()
        post.raw = form.raw.data.strip()
        abstract = form.abstract.data.strip()
        post.abstract = abstract if abstract else post.raw[:140]
        post.is_draft = form.is_draft.data
        post.author = form.author.data

        # post.post_type = form.post_type.data.strip() if form.post_type.data else None

        post.post_type = request.form.get('post_type') or post.post_type
        pub_time = request.form.get('publish_time')
        update_time = request.form.get('update_time')

        if pub_time:
            post.pub_time = datetime.datetime.strptime(pub_time, "%Y-%m-%d %H:%M:%S")

        if update_time:
            post.update_time = datetime.datetime.strptime(update_time, "%Y-%m-%d %H:%M:%S")

        redirect_url = url_for('blog_admin.su_posts')

        post.save(allow_set_time=True)

        flash('Succeed to update post', 'success')
        return redirect(redirect_url)


#!/usr/bin/env python
# coding: utf-8

# ## **Load Libraries**

# In[ ]:

get_ipython().system(u'pip install zipfile38')

import os
import tqdm
import math
import random
import pickle
import shutil
import numpy as np
import pandas as pd
import zipfile38 as zipfile

from scipy import io
from urllib import request
from tensorflow.keras.utils import to_categorical

# ## **Mount Drive**

# In[ ]:

from google.colab import drive
drive.mount('/content/drive') # Mounts Drive In Content Directory (Under Files Section)

# In[ ]:

Path = r'/content/drive/MyDrive/ECG ML'

# ## **Download Dataset**

# In[ ]:

# Progress Bar For Downloading
def my_hook(t):
    '''Wraps tqdm instance.
    Don't forget to close() or __exit__()
    the tqdm instance once you're done with it (easiest using `with` syntax).
    Example
    -------
    >>> with tqdm(...) as t:
    ...     reporthook = my_hook(t)
    ...     urllib.urlretrieve(..., reporthook = reporthook)
    '''
    last_b = [0]

    def update_to(b = 1, bsize = 1, tsize = None):
        '''
        b  : int, optional
            Number of blocks transferred so far [default: 1].
        bsize  : int, optional
            Size of each block (in tqdm units) [default: 1].
        tsize  : int, optional
            Total size (in tqdm units). If [default: None] or -1,
            remains unchanged.
        '''
        if tsize not in (None, -1):
            t.total = tsize
        displayed = t.update((b - last_b[0]) * bsize)
        last_b[0] = b
        return displayed

    return update_to

# In[ ]:

if os.path.isdir(os.path.join(Path, 'Dataset')): # Checks If Directory Exist
  shutil.rmtree(os.path.join(Path, 'Dataset')) # Removes Directory
os.mkdir(os.path.join(Path, 'Dataset')) # Creates Directory

# Downloads Dataset
for index in range(3):
  with tqdm.tqdm(unit = 'B', unit_scale = True, unit_divisor = 1024, miniters = 1, desc = f'Downloading TrainingSet{index + 1}.zip ') as t: # Progress Bar
    request.urlretrieve(f'http://hhbucket.oss-cn-hongkong.aliyuncs.com/TrainingSet{index + 1}.zip', os.path.join(Path, 'Dataset', f'TrainingSet{index + 1}.zip'), my_hook(t)) # Connects And Downloads Data

# ## **Unpack Dataset**

# In[ ]:

# Unzip Archive Files
for Archive in ['TrainingSet1.zip', 'TrainingSet2.zip', 'TrainingSet3.zip']:
  with zipfile.ZipFile(os.path.join(Path, 'Dataset', Archive)) as zf:
    for member in tqdm.tqdm(zf.infolist(), unit_scale = True, miniters = 1, desc = f'Unpacking {Archive} '): # Progress Bar
      zf.extract(member, os.path.join(Path, 'Dataset'))

# ## **Convert and Merge Dataset**

# In[ ]:

os.mkdir(os.path.join(Path, 'Dataset', 'Converted')) # Create Directory

for TrainingSet in ['TrainingSet1', 'TrainingSet2', 'TrainingSet3']:
  for File_Name in tqdm.tqdm(os.listdir(os.path.join(Path, 'Dataset', TrainingSet)), unit_scale = True, miniters = 1, desc = f'Converting {TrainingSet} '):
    if File_Name[0] == 'A':
      pd.DataFrame(io.loadmat(os.path.join(Path, 'Dataset', TrainingSet, File_Name))['ECG'][0][0][2].T, columns = ['DI', 'DII', 'DIII', 'AVR', 'AVL', 'AVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6']).to_csv(os.path.join(Path, 'Dataset', 'Converted', f'{os.path.splitext(File_Name)[0]}.csv'), index = False) # Reads MATLAB Files And Converts Them To CSV File
shutil.copyfile(os.path.join(Path, 'Dataset', 'TrainingSet3', 'REFERENCE.csv'), os.path.join(Path, 'Dataset', 'Converted', 'Reference.csv')) # Copy File (Reference.csv)

# ## **Select Required Features and Split Dataset**

# In[ ]:

# Features To Include In Model
Feature_List = [1, 4, 5, 7]
Feature_List.sort()

Dict = {}
for index, Feature in enumerate(Feature_List):
  Dict[Feature] = index

# In[ ]:

# Reads Reference.csv File
df = pd.read_csv(os.path.join(Path, 'Dataset', 'Converted', 'Reference.csv'), index_col = 'Recording').sort_index()

# Selects Required Features
First_label = df[df['First_label'].isin(Feature_List)].drop(columns = ['Second_label', 'Third_label'])
First_label.columns = ['Label']

Second_label = df[df['Second_label'].isin(Feature_List)].drop(columns = ['First_label', 'Third_label'])
Second_label = Second_label[np.logical_not(Second_label.index.isin(First_label.index))]
Second_label.columns = ['Label']

Third_label = df[df['Third_label'].isin(Feature_List)].drop(columns = ['First_label', 'Second_label'])
Third_label = Third_label[np.logical_not(Third_label.index.isin(pd.Series(np.concatenate([First_label.index, Second_label.index]))))]
Third_label.columns = ['Label']

# Sort Dataset According To Number Of Samples
df = pd.concat([First_label, Second_label, Third_label], axis = 0).convert_dtypes().sort_index()
Length_Dict = {File_Name: np.genfromtxt(os.path.join(Path, 'Dataset', 'Converted', f'{File_Name}.csv'), dtype = np.float64, delimiter = ',', skip_header = 1).shape[0] for File_Name in tqdm.tqdm(df.index, unit_scale = True, miniters = 1, desc = 'Calculating Lengths ')}

df = df.reindex(sorted(df.index, key = lambda File_Name: Length_Dict[File_Name]))
Max = pd.read_csv(os.path.join(Path, 'Dataset', 'Converted', f'{df.index[-1]}.csv')).shape[0] # Gets The Maximum Number Of Samples Available In Any Single File

# In[ ]:

# Separate Validation Files
Count = [0] * len(Feature_List)
Validation_Files = [[] for _ in range(len(Feature_List))]

index = 0
while sum(Count) != 100:
  File_Name = df.index[index]
  Label = df.loc[File_Name, 'Label']

  if Count[Dict[Label]] < 25:
    Validation_Files[Dict[Label]].append(File_Name)
    Count[Dict[Label]] += 1

  index += 1

Validation_Files = [File_Name for Feature in Validation_Files for File_Name in Feature]

# In[ ]:

# Separate Dataframe
train_df = df[np.logical_not(df.index.isin(Validation_Files))]
validation_df = df[df.index.isin(Validation_Files)]

# ## **Augment Dataset**

# In[ ]:

# Augment Dataset Using Fixed Overlapping Window

Max = int(Max / 1400) - 1
Augmented_Data = [[] for _ in range(len(Feature_List))]
Count = [[] for _ in range(len(Feature_List))]

for File_Name in tqdm.tqdm(train_df.index, unit_scale = True, miniters = 1, desc = 'Loading Training Data '): # Progress Bar
  File = np.genfromtxt(os.path.join(Path, 'Dataset', 'Converted', f'{File_Name}.csv'), dtype = np.float64, delimiter = ',', skip_header = 1) # Read Files
  Label = train_df.loc[File_Name, 'Label']
  Length = int(File.shape[0] / 1400) - 1

  # Augment
  File_Data = [File[index * 1400:(index + 2) * 1400] for index in range(0, Length, 2)]
  File_Data.extend([np.empty([0]) for _ in range(int(Max / 2) - math.ceil(Length / 2))])
  File_Data.extend([File[index * 1400:(index + 2) * 1400] for index in range(1, Length, 2)])
  File_Data.extend([np.empty([0]) for _ in range(int(Max / 2) - math.floor(Length / 2))])

  Count[Dict[Label]].append(math.ceil(Length / 2) + math.floor(Length / 2))
  Augmented_Data[Dict[Label]].append(File_Data)

# In[ ]:

# Load Validation Data

Validation_Data = [[] for _ in range(len(Feature_List))]

for File_Name in tqdm.tqdm(validation_df.index, unit_scale = True, miniters = 1, desc = 'Loading Validation Data '): # Progress Bar
  File = np.genfromtxt(os.path.join(Path, 'Dataset', 'Converted', f'{File_Name}.csv'), dtype = np.float64, delimiter = ',', skip_header = 1)
  Label = validation_df.loc[File_Name, 'Label']

  # Append Directly
  Validation_Data[Dict[Label]].append(File[0:2800])

# ## **Balance Dataset**

# In[ ]:

# This Balances Dataset (Equal Number Of Samples Of Each Class)

Feature_List = [index for index in range(len(Augmented_Data))]
Max = min(sum(Feature) for Feature in Count) # Min Number Of Samples Present In Any Class

Count = [0] * len(Feature_List)
LENGTHS = [len(Feature) for Feature in Augmented_Data]
Training_Data = [[] for _ in range(len(Feature_List))]

# Main Loop
index = 0
Length = len(Feature_List)
while Length:
  Feature = Augmented_Data[Feature_List[index]]

  INDEX = 0
  LENGTH = LENGTHS[Feature_List[index]]
  while INDEX < LENGTH:
    File = Feature[INDEX]

    if File[0].shape[0] != 0:
      Training_Data[Feature_List[index]].append(File[0])
      Count[Feature_List[index]] += 1
      if Count[Feature_List[index]] == Max:
        break

    del File[0]
    if not len(File):
      del Feature[INDEX]
      INDEX -= 1
      LENGTH -= 1

    INDEX += 1

  LENGTHS[Feature_List[index]] = LENGTH

  if Count[Feature_List[index]] == Max:
    del Feature_List[index]
    index -= 1
    Length -= 1

  index += 1

  if index == Length:
    index = 0

# ## **Save Processed Dataset**

# In[ ]:

# Save Data List In Pickle Format
pickle_out = open(os.path.join(Path, 'Dataset', 'Training_Data.pickle'), 'wb')
pickle.dump(Training_Data, pickle_out)
pickle_out.close()

pickle_out = open(os.path.join(Path, 'Dataset', 'Validation_Data.pickle'), 'wb')
pickle.dump(Validation_Data, pickle_out)
pickle_out.close()

# ## **Shuffle Dataset**

# In[ ]:

# Restructure Data List
Training_Data = [[Sample, index] for index, Feature in enumerate(Training_Data) for Sample in Feature]

# Shuffle Data
random.shuffle(Training_Data)

# In[ ]:

# Restructure Data List
Validation_Data = [[Sample, index] for index, Feature in enumerate(Validation_Data) for Sample in Feature]

# Shuffle Data
random.shuffle(Validation_Data)

# ## **Save Shuffled Dataset**

# In[ ]:

# Separate Target Variable
x_train, y_train = np.array([Sample[0] for Sample in Training_Data], dtype = np.float64), to_categorical(np.array([Sample[1] for Sample in Training_Data], dtype = np.int64))
x_validation, y_validation = np.array([Sample[0] for Sample in Validation_Data], dtype = np.float64), to_categorical(np.array([Sample[1] for Sample in Validation_Data], dtype = np.int64))

# In[ ]:

# Save Data List In Pickle Format
pickle_out = open(os.path.join(Path, 'Dataset', 'x_train.pickle'), 'wb')
pickle.dump(x_train, pickle_out)
pickle_out.close()

pickle_out = open(os.path.join(Path, 'Dataset', 'y_train.pickle'), 'wb')
pickle.dump(y_train, pickle_out)
pickle_out.close()

# In[ ]:

# Save Data List In Pickle Format
pickle_out = open(os.path.join(Path, 'Dataset', 'x_validation.pickle'), 'wb')
pickle.dump(x_validation, pickle_out)
pickle_out.close()

pickle_out = open(os.path.join(Path, 'Dataset', 'y_validation.pickle'), 'wb')
pickle.dump(y_validation, pickle_out)
pickle_out.close()


def look_and_say(digits: str) -> str:
    length = len(digits)
    current = 0
    result = ""

    while current < length:
        digit = digits[current]

        count = 1
        offset = current + 1
        while offset < length:
            if digit == digits[offset]:
                count += 1
                offset += 1
            else:
                break

        result += f"{count}{digit}"

        current = offset

    return result

def main():
    from sys import argv

    result = argv[1]
    steps = int(argv[2])

    for _ in range(steps):
        result = look_and_say(result)

    print(len(result))

if __name__ == '__main__':
    main()


import time
import multiprocessing
import numpy as np
from igraph import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

joints = np.load("joints.npy", allow_pickle=True).tolist()

segMetric = np.load("segMetric.npy", allow_pickle=True)
segMetricNormal = np.load("segMetricNormal.npy", allow_pickle=True)
roadProperties = np.load("roadProperties.npy", allow_pickle=True)
properties = np.load("properties.npy", allow_pickle=True)

### GRAPH ###
g = Graph(directed=True)

try:
    graphFile = np.load("graph.npy", allow_pickle=True)
except:
    graphFile = [0,0,0,"#"]
if properties[5] == graphFile[3]:
    edgesToAdd, vertexToRoad, vertexCount = graphFile[0], graphFile[1], graphFile[2]
    for i in range(vertexCount + 1):
        g.add_vertices(1)
else:
    np.save("cacheWeight.npy", [[], [], []])
    np.save("cacheRoute.npy", [[], [], [], []])

    edgesToAdd = []

    log = []
    vertexLog = []
    segmentVertices = [[False, False] for i in range(len(splitRoadsWithHeights))]
    vertexCount = -1
    length = len(splitRoadsWithHeights)
    vertexToRoad = []
    for segment in range(length):
        if segment % np.floor(length / 20) == 0:
            print((segment / length)*100)
        for startOrEnd in range(0,2):
            connections = joints[segment][startOrEnd][0]
            startOrEnds = joints[segment][startOrEnd][1]
            for c in range(len(connections)):
                if (connections[c], startOrEnds[c]) not in log:
                    #print(connections[c], startOrEnds[c])
                    log.append((connections[c], startOrEnds[c]))
                    vertexCount += 1
                    g.add_vertices(1)
                    vertexToRoad.append([connections[c], startOrEnds[c]])
                    segmentVertices[connections[c]][startOrEnds[c]] = vertexCount

    for segment in range(length):
        if segment % np.floor(length / 20) == 0:
            print((segment / length)*100)
        for startOrEnd in range(-1, 1):
            connections = joints[segment][startOrEnd][0]
            startOrEnds = joints[segment][startOrEnd][1]
            for c in range(len(connections)):
                #if (connections[c], startOrEnds[c]) in log:
                edgesToAdd.append((segmentVertices[segment][startOrEnd], segmentVertices[connections[c]][startOrEnds[c]]))
    tally = 0
    for i in range(len(segmentVertices)):
        if segmentVertices[i][0] != False and segmentVertices[i][1] != False:
            if int(roadProperties[i][0]) == 0:
                edgesToAdd.append((segmentVertices[i][1], segmentVertices[i][0]))
            edgesToAdd.append((segmentVertices[i][0], segmentVertices[i][1]))
        else:
            tally += 1
    edgesToAdd = [i for i in edgesToAdd if i[0] != False and i[1] != False]
    np.save("graph.npy", np.array([edgesToAdd, vertexToRoad, vertexCount, properties[5]]))

g.add_edges(edgesToAdd)

def multiprocessing_func(id, Global, stuff, waypoints, goals):
    Global.startedIDs.append(id)
    ranges = properties[4]
    rang = stuff[0]
    steps = stuff[1]
    waypointsPoss = stuff[2]
    howManyVariable = stuff[3]
    length = stuff[4]

    min = False
    for a in range(waypointsPoss):
        vertexes = []
        notStaticCount = -1
        for i in range(length):
            if waypoints[i] == None:
                notStaticCount += 1
                vertexes.append(rang[int((a // (steps ** notStaticCount)) % steps)])
            else:
                vertexes.append(waypoints[i])
        possibleRoute = True
        route = []
        for i in range(length - 1):
            directions = g.get_shortest_paths(vertexes[i], to=vertexes[i+1], weights=[1 for i in range(len(edgesToAdd))], mode=OUT)[0]
            if len(directions) > 1:
                route.extend(directions)
            else:
                possibleRoute = False
                break
        if possibleRoute:
            arr = np.take(np.transpose(vertexToRoad)[0], route)
            arrDir = np.take(np.transpose(vertexToRoad)[1], route)
            # u = [x for x in set(arr) if arr.count(x) > 0]

            ###finding the route metrics...
            routeMetric = [0, 0, 0]  # distance, elevation up, elevation down
            for seg in range(len(arr)):
                if seg > 0 and arr[seg] != arr[seg - 1]:
                    routeMetric[0] += segMetric[arr[seg]][0][arrDir[seg]]
                    routeMetric[1] += segMetric[arr[seg]][1][arrDir[seg]]
                    routeMetric[2] += segMetric[arr[seg]][1][-1 - arrDir[seg]]
            score = []
            for o in range(len(goals)):
                score.append((ranges[o] + np.abs(goals[o] - routeMetric[o])) / ranges[o])
            score = np.mean(score)
            if min == False or score < min:
                min = score
                minRouteMetric = routeMetric
                minDirections = directions
                # print(i / length, score)
                optBatch = Global.optBatch
                optBatch[0][id] = (min)
                optBatch[1][id] = (minRouteMetric)
                optBatch[2][id] = (minDirections)
                Global.optBatch = optBatch
        optPerc = Global.optPerc
        optPerc[id] = (a + 1) / waypointsPoss
        Global.optPerc = optPerc

class OptProgThread(QThread):
    def run(self):
        global lastUpdateProg
        global Global
        while True:
            #print(Global.optBatch[0])
            if Global.optInterrupt == False and time.clock() - lastUpdateProg >= 0.25:
                lastUpdateProg = time.clock()
                percs = np.take(Global.optBatch[0], Global.startedIDs)
                if len(Global.startedIDs) > 0:
                    print(np.mean(percs))

if __name__ == '__main__':
    coreCount = multiprocessing.cpu_count() - 2

    global Global
    manager = multiprocessing.Manager()
    Global = manager.Namespace()
    Global.optBatch = [[None for i in range(coreCount)], [None for i in range(coreCount)], [None for i in range(coreCount)]] #scores, routeMetrics, directions
    Global.optPerc = [0 for i in range(coreCount)]
    Global.optInterrupt = False
    Global.startedIDs = []

    lastUpdateProg = time.clock()

    waypoints = [None, None, 1376, 1567]
    howManyVariable = sum([1 for i in waypoints if i == None])
    length = len(waypoints)

    optProgThread = OptProgThread()
    optProgThread.start()
    quality = ((vertexCount * 0.025) ** (1 / howManyVariable) * coreCount) / vertexCount
    goals = [30000, 0, 0]

    steps = int((vertexCount * quality) / coreCount)
    waypointsPoss = int(steps ** howManyVariable)
    distribution = np.linspace(0, vertexCount-1, num=steps, endpoint=True)
    spread = distribution[1] - distribution[0]
    spreadPerCore = spread / (coreCount)
    rangs = []
    for core in range(coreCount):
        rangs.append((distribution + (spreadPerCore * (core))).astype(int).tolist())

    processes = []

    for core in range(coreCount):
        stuff = [rangs[core], steps, waypointsPoss, howManyVariable, length]
        p = multiprocessing.Process(target=multiprocessing_func, args=(core, Global, stuff, waypoints, goals))
        p.daemon = True
        processes.append(p)
        processes[-1].start()

    shallIContinue = input()


#imagination better than knowledge
#Aranyak 093
#SSTF

def sstf(requests,head):
    diff_list=[];
    visited=[];
    head_count=0;
    movements=0;
    sz=len(requests);
    for i in range(sz):
        diff_list.append(0);
        visited.append(0);

    while True:
        for i in range(sz):
            if(requests[i]!=head and not visited[i]):
                diff_list[i]=abs(head-requests[i]);
            else:
                diff_list[i]=1000000;

        min_diff=min(diff_list);
        indx=diff_list.index(min_diff)
        diff=abs(requests[indx]-head)
        movements+=diff;
        head=requests[indx];
        head_count+=1;
        visited[indx]=1;
        #print(diff_list)
        print("")
        print(head);
        if(head_count>sz):
            break;

    return movements;

myList=[176, 79, 34, 60,92, 11, 41, 114];
head=50;
print("Movements: ",sstf(myList,head));


# ðŸš¨ Don't change the code below ðŸ‘‡
number = int(input("Which number do you want to check? "))
# ðŸš¨ Don't change the code above ðŸ‘†

#Write your code below this line ðŸ‘‡
# if the number variable is divisible by 2 it should have a remainder of 0 thus the == to check precisely.
if number % 2 == 0:
  print(f" The number {number} is even!")
  # if the remainder is not equal to 0 than the number is odd and the else statement is executed.
else:
  print(f"The number {number} is odd!")


import pygame
import sys

if __name__ == '__main__':
    try:
        a, n = map(int, input().split())
    except ValueError:
        print('Неверный формат ввода.')
        sys.exit()

    def draw():
        screen.fill((255, 255, 255))
        for i in range(n):
            for j in range(n):
                if (i + 1) % 2:
                    if (j + 1) % 2:
                        pygame.draw.rect(screen, (0, 0, 0), (j * x, i * x, a, a), 0)
                        pygame.display.flip()
                    else:
                        pygame.draw.rect(screen, (255, 255, 255), (j * x, i * x, a, a), 0)
                        pygame.display.flip()
                else:
                    if (j + 1) % 2:
                        pygame.draw.rect(screen, (255, 255, 255), (j * x, i * x, a, a), 0)
                        pygame.display.flip()
                    else:
                        pygame.draw.rect(screen, (0, 0, 0), (j * x, i * x, a, a), 0)
                        pygame.display.flip()

    size = (a, a)
    x = a // n
    pygame.init()
    screen = pygame.display.set_mode(size)
    pygame.display.set_caption('Шахматная клетка')
    draw()
    while pygame.event.wait().type != pygame.QUIT:
        pass
    pygame.quit()


# coding: utf-8
import math

from Mordicus.Core.IO.SolutionReaderBase import SolutionReaderBase
import medcoupling as ml
import MEDLoader as ML
import numpy as np

from Mordicus.Modules.EDF.IO.MEDMesh import MEDMesh
from Mordicus.Modules.EDF.Containers.FieldHandlers.MEDFieldHandler import safe_clone
from Mordicus.Modules.EDF.Containers.SolutionStructures.MEDAsterSolutionStructure import MEDAsterSolutionStructure

class MEDSolutionReader(SolutionReaderBase):
    """
    Class to convert MEDSolution into ProblemData
    """
    Mordicus2MEDAster = {"U"    : "DEPL",
                         "sigma": "SIEF_ELGA",
                         "Fext" : "FORC_NODA",
                         "r"    : "REAC_NODA"}

    Disc2MLType = {"nodes": ML.ON_NODES,
                   "gauss": ML.ON_GAUSS_PT}

    def __init__(self, fileName, timeIt):
        '''
        Constructor

        Arguments
        ---------
        fileName : str
            File name to read solutions from
        timeIt : float
            time for loading a reference field
        '''
        super(MEDSolutionReader, self).__init__()
        self.fileName = fileName
        self.timeIt = timeIt

    def ReadTimeSequenceFromSolutionFile(self, fieldName):
        """
        Overriden from SolutionReaderBase
        """
        # TODO: demander en comité technique à ajouter fieldName à ReadTimeSequenceFromSolutionFile
        all_iterations, MED_field_name = self._extract_name_and_sequence(fieldName)
        return np.array([it[2] for it in all_iterations])

    def _extract_name_and_sequence(self, fieldName):
        """
        Returns the MED name of the field as well as all time iterations for this field

        fieldName : str
            name of the solution from which the snapshot is read (e.g. "U", "sigma")
        """
        # Recuperer le nom MED du champ
        all_field_names = ML.GetAllFieldNames(self.fileName)
        try:
            MED_field_name = next(n for n in all_field_names if n.endswith(self.Mordicus2MEDAster[fieldName]))
        except StopIteration:
            raise IOError("File {0} does not contain a field whose name ends with {1}".format(self.fileName,
                    self.Mordicus2MEDAster[fieldName]))
        # Trouver (iteration, order) qui correspond au pas de temps
        all_iterations = ML.GetAllFieldIterations(self.fileName, MED_field_name)
        return all_iterations, MED_field_name

    def ReadSnapshotComponent(self, fieldName, time, primality, solutionStructure):
        """
        Reads a snapshots from the solutions of name "fieldName", at time "time" and of primality "primality", from the HF computation

        Parameters
        ----------
        fieldName : str
            name of the solution from which the snapshot is read
        time : float
            time at which the snapshot is read
        primality : bool
            primality of the solution from which the snapshot is read
        solutionStructure : SolutionStructure
            solution structure to guide putting the result into array

        Returns
        -------
        np.ndarray
            of size (numberOfDofs,)
        """
        all_iterations, MED_field_name = self._extract_name_and_sequence(fieldName)
        iteration, order = next((a, b) for (a, b, c) in all_iterations if math.isclose(c, time))

        # read the field under the form of a MEDFileField1TS
        f1ts = ML.MEDFileField1TS(self.fileName, MED_field_name, iteration, order)
        mfMeshName = f1ts.getMeshName()
        mf_mesh = ML.MEDFileUMesh.New(self.fileName, mfMeshName)

        fieldInstances = solutionStructure.GetInternalStorage()

        start = True
        for relLevel, (fieldInstance, idsInRelLevel) in fieldInstances.items():
            disc = fieldInstance.getTypeOfField()
            f = f1ts.getFieldOnMeshAtLevel(disc, relLevel, mf_mesh)

            # Select groups if necessary
            if idsInRelLevel is not None:
                if solutionStructure.discr in ("gauss", "cell"):
                    f = f.buildSubPart(idsInRelLevel)
                if solutionStructure.discr in ("node", ):
                    f.setArray(f.getArray()[idsInRelLevel].deepCopy())

            compos_num_to_select = [f.getArray().getInfoOnComponents().index(c) for c in fieldInstance.getArray().getInfoOnComponents()]
            f = f.keepSelectedComponents(compos_num_to_select)
            if not start:
                res = np.concatenate((res, f.getArray().toNumPyArray().flatten()))
            if start:
                res = f.getArray().toNumPyArray().flatten()
                start = False

        return res

    def WriteReducedOrderBasis(self, fileName, solutionStructure, reducedOrderBasis, fieldName):
        """
        Converts Mordicus reduced order basis into the MED format

        Parameters
        ----------
        fileName : str
            MED file to write reduced basis to
        fieldStructure : MEDAsterSolutionStructure
            field structure giving the context to interpret the vector in terms of field values on the mesh
        reducedOrderBasis : nparray(numberOfModes, numberOfDofs)
            numpy array of the modes
        fieldName : str
            name of field associated with the basis (e.g. "U", "sigma")
        """
        fieldInstances = solutionStructure.GetInternalStorage()

        mf_mesh_strip = ML.MEDFileUMesh.New()
        mf_mesh_strip.setName("MeshStripped")

        for relLevel, (fieldInstance, _) in fieldInstances.items():
            mc_mesh_at_level = fieldInstance.getMesh()
            mc_mesh_at_level.setName("MeshStripped")
            mf_mesh_strip.setMeshAtLevel(relLevel, mc_mesh_at_level)

        if solutionStructure.discr in ("gauss", "cell"):
            mf_mesh_strip.zipCoords()

        numberOfModes, _ = reducedOrderBasis.shape

        f_mts=ML.MEDFileFieldMultiTS.New()
        f_mts.setName("base____" + self.Mordicus2MEDAster[fieldName])

        for imode in range(numberOfModes):
            offset = 0
            f1ts = ML.MEDFileField1TS.New()
            f1ts.setTime(imode+1, imode+1, float(imode+1))
            f1ts.setName("base____" + self.Mordicus2MEDAster[fieldName])

            for relLevel, (fieldInstance, _) in fieldInstances.items():
                f = safe_clone(fieldInstance)
                f.setTime(float(imode+1), imode+1, imode+1)
                f.setName("base____" + self.Mordicus2MEDAster[fieldName])

                # array needs to be put to the right shape and converted to DataArrayDouble
                offset_new = offset + f.getNumberOfTuples()*f.getNumberOfComponents()
                f.getArray().setValues(list(reducedOrderBasis[imode,offset:offset_new]), f.getNumberOfTuples(), f.getNumberOfComponents())
                offset = offset_new
                # write f to a new file
                f1ts.setFieldNoProfileSBT(f)

            f_mts.pushBackTimeStep(f1ts)

        mf_mesh_strip.write(fileName, 2)
        f_mts.write(fileName, 0)

    def WriteSparseFieldOfEmpiricalWeights(self, fileName, fieldStructure, np_coor_gauss, empirical_weights):
        """
        Writes found empirical_weights to a Gauss MED field

        Parameters
        ----------
        fileName : str
            MED file to write field of empirical weights to
        fieldStructure : MEDAsterSolutionStructure
            field structure giving the context to interpret the vector in terms of field values on the mesh
        np_coor_gauss
            numpy array of empirical Gauss points coordinates
        empirical_weights
            numpy array of empirical weights
        """
        import numpy as np
        if fieldStructure.discr != "gauss":
            raise ValueError("fieldStructure in argument to WriteSparseFieldOfEmpiricalWeights should be of type gauss.")
        fieldInstances = fieldStructure.GetInternalStorage()

        mf_mesh_strip = ML.MEDFileUMesh.New()
        mf_mesh_strip.setName("MeshStripped")

        for relLevel, (fieldInstance, _) in fieldInstances.items():
            mc_mesh_at_level = fieldInstance.getMesh()
            mc_mesh_at_level.setName("MeshStripped")
            mf_mesh_strip.setMeshAtLevel(relLevel, mc_mesh_at_level)
        mf_mesh_strip.zipCoords()

        f1ts=ML.MEDFileField1TS.New()
        f1ts.setTime(0, 0, 0.0)

        offset = 0
        for relLevel, (fieldInstance, _) in fieldInstances.items():
            # another solution would be to clone the field and replace the array, but this one seems cleaner
            f = ml.MEDCouplingFieldDouble.New(ml.ON_GAUSS_PT, ml.ONE_TIME)
            f.setMesh(fieldInstance.getMesh())
            f.setName("W_EMPI__")
            f.setTime(0.0, 0, 0)

            for i_gauss_loc in range(fieldInstance.getNbOfGaussLocalization()):
                gauss_loc = fieldInstance.getGaussLocalization(i_gauss_loc)
                cell_type, refCoo, gsCoo, wg = gauss_loc.getType(), gauss_loc.getRefCoords(), gauss_loc.getGaussCoords(), gauss_loc.getWeights()
                f.setGaussLocalizationOnType(cell_type, refCoo, gsCoo, wg)

            # 1 component per Gauss point, initiated to 0
            f.fillFromAnalytic(1, "0")
            offset_new = offset + f.getNumberOfTuples()

            # indices of non zero empirical_weights
            nonzero_indices = empirical_weights.nonzero()[0]
            char_len = fieldInstance.getMesh().getCaracteristicDimension()

            gauss_pts = fieldInstance.getLocalizationOfDiscr().toNumPyArray()
            nb_comps = gauss_pts.shape[1]

            # for each, find Gauss location, set field value there$
            f_non_zero_indices = nonzero_indices[np.where((nonzero_indices >= offset) & (nonzero_indices < offset_new))]
            for i_nonzero in f_non_zero_indices:

                f.getArray().toNumPyArray()[i_nonzero-offset] = np.double(empirical_weights[i_nonzero])
                dist = 0.
                for i_comp in range(nb_comps):
                    dist = dist + (gauss_pts[i_nonzero-offset, i_comp]-np_coor_gauss[i_nonzero, i_comp])**2
                dist = np.sqrt(dist)
                if dist > 1.e-8*char_len:
                    raise ValueError("The sample field provided for empirical weights is numbered differently")
            f.getArray().setInfoOnComponent(0, "X1")

            offset = offset_new

            f1ts.setFieldNoProfileSBT(f)

        mf_mesh_strip.write(fileName, 2)
        f1ts.write(fileName, 0)

    def WriteSolutionStructure(self, fileName, fieldStructure, fieldName):
        return self.WriteReducedOrderBasis(fileName,
                                           fieldStructure,
                                           np.zeros((1, fieldStructure.GetNumberOfNodes()*fieldStructure.GetNumberOfComponents())),
                                           fieldName)

    def WriteOperatorCompressionData(self, fileName, key, cpd):
        self.WriteSparseFieldOfEmpiricalWeights(fileName, cpd.solutionStructures["sigma"],
                                                cpd.operatorCompressionData[key][0],
                                                cpd.operatorCompressionData[key][1])

    def WriteSolution(self, fileName, fieldStructure, solution, fieldName, nameInFile=None, append=False):
        """
        Convert a Mordicus snapshot into a MED field, relying on SolutionStructure to build relations
        between the vector of values and the mesh.

        Arguments
        ---------
        fileName : str
            MED file to write solution to
        fieldStructure : MEDAsterSolutionStructure
            field structure giving the context to interpret the vector in terms of field values on the mesh
        solution : Solution
            solution to write
        fieldName : str
            identifier of the physical quantity for the field
        nameInFile : str
            to customize field name in output MED file *fileName*
        append : bool
            if true results are appended to fileName and mesh is not written (again). Otherwise fileName is written from scratch
        """
        all_iterations, MED_field_name = self._extract_name_and_sequence(fieldName)

        f_mts = ML.MEDFileFieldMultiTS.New()
        fieldInstances = fieldStructure.GetInternalStorage()

        for i, t in enumerate(solution.GetTimeSequenceFromSnapshots()):
            iteration, order = next((a, b) for (a, b, c) in all_iterations if math.isclose(c, t))

            # read the field under the form of a MEDFileField1TS
            f1ts = ML.MEDFileField1TS(self.fileName, MED_field_name, iteration, order)
            f1ts.setTime(i+1, i+1, t)
            mf_disc, = f1ts.getTypesOfFieldAvailable()
            if i == 0:
                mfMeshName = f1ts.getMeshName()
                mf_mesh = ML.MEDFileUMesh.New(self.fileName, mfMeshName)

            offset = 0
            for relLevel, (fieldInstance, idsInRelLevel) in fieldInstances.items():

                # start with background 0 field all over
                mc_field_bg = f1ts.getFieldOnMeshAtLevel(mf_disc, relLevel, mf_mesh)
                mc_field_bg.getArray().fillWithValue(0.0)

                mc_field_bg.setTime(t, i+1, i+1)

                f = safe_clone(fieldInstance)
                f.setTime(t, i+1, i+1)

                # reduce number of components all over
                compos_num_to_select = [mc_field_bg.getArray().getInfoOnComponents().index(c) for c in f.getArray().getInfoOnComponents()]

                # The following truncates the field: not the desired behavior, we want to leave unselected components to their older values
                #mc_field_bg = mc_field_bg.keepSelectedComponents(compos_num_to_select)
                has_component_selection = len(compos_num_to_select) < mc_field_bg.getNumberOfComponents()

                if nameInFile is not None:
                    target = "_"*8
                    expanded_name = nameInFile[:min(8,len(nameInFile))] + target[min(8,len(nameInFile)):]
                    mc_field_bg.setName(expanded_name + self.Mordicus2MEDAster[fieldName])

                f.setName(mc_field_bg.getName())
                f1ts.setName(mc_field_bg.getName())
                f_mts.setName(mc_field_bg.getName())

                # array needs to be put to the right shape and converted to DataArrayDouble
                offset_new = offset + f.getNumberOfTuples()*f.getNumberOfComponents()
                f.getArray().setValues(list(solution.GetSnapshot(t)[offset:offset_new]),
                                       f.getNumberOfTuples(), f.getNumberOfComponents())
                offset = offset_new

                # put that into mc_field_bg
                if idsInRelLevel is not None:
                    if fieldStructure.discr in ("gauss", "cell"):
                        tupIds = mc_field_bg.computeTupleIdsToSelectFromCellIds(idsInRelLevel)
                    if fieldStructure.discr in ("node", ):
                        tupIds = idsInRelLevel

                    # command in case there is no components selection
                    if not has_component_selection:
                        mc_field_bg.getArray()[tupIds] = f.getArray().deepCopy()
                    if has_component_selection:
                        mc_field_bg.getArray().toNumPyArray()[tupIds,np.array(compos_num_to_select)] = f.getArray().deepCopy().toNumPyArray()

                else:
                    # The following is native Medcoupling syntax but it s unclear whether it still works
                    #     if the N components to keep are not the first N components
                    # There seems to be a setPartOfValues2 function in later versions of medcoupling
                    # Not using it for now
                    #mc_field_bg.getArray().setPartOfValues1(f.getArray().deepCopy(), 0, f.getNumberOfTuples(), 1, 0, f.getNumberOfComponents(), 1)

                    if not has_component_selection:
                        f1ts.setFieldNoProfileSBT(f)
                        continue
                    if has_component_selection:
                        mc_field_bg.getArray().toNumPyArray()[:,np.array(compos_num_to_select)] = f.getArray().deepCopy().toNumPyArray()

                f1ts.setFieldNoProfileSBT(mc_field_bg)

            # setArray is almost always a bad idea
            f_mts.pushBackTimeStep(f1ts)

        if not append:
            mf_mesh.write(fileName, 2)
        f_mts.write(fileName, 0)

    def WriteNumbering(self, fileName, fieldStructure, fieldName, nameInFile=None):
        """
        Write an identity application on the input, gets the permutation application on the output.
        This method is used to convert operators from a FEM code to the numbering system associated
            with the format of the results (for instance be able to get a consistent numbering between
            a mass matrix computed with Code_Aster and the results written to MED format)

        Arguments
        ---------
        fileName : str
            MED file to write permutation to
        fieldStructure : MEDAsterSolutionStructure
            field structure for which the numbering system has to be converted
        fieldName : str
            identifier of the physical quantity for the field
        nameInFile : str
            to customize field name in output MED file *fileName*
        """
        # --- find right field to import by name and iteration
        all_iterations, MED_field_name = self._extract_name_and_sequence(fieldName)
        iteration, order = next((a, b) for (a, b, c) in all_iterations if math.isclose(c, self.timeIt))

        # read the field under the form of a MEDFileField1TS
        mf_field_bg = ML.MEDFileField1TS(self.fileName, MED_field_name, iteration, order)
        mf_disc, = mf_field_bg.getTypesOfFieldAvailable()
        mfMeshName = mf_field_bg.getMeshName()
        mf_mesh = ML.MEDFileUMesh.New(self.fileName, mfMeshName)

        ff=ML.MEDFileField1TS.New()
        ff.setTime(0, 0, 0.0)

        fieldInstances = fieldStructure.GetInternalStorage()

        offset = 0
        for relLevel, (fieldInstance, idsInRelLevel) in fieldInstances.items():

            # start with background -1 field all over
            mc_field_bg = mf_field_bg.getFieldOnMeshAtLevel(mf_disc, relLevel, mf_mesh)

            # do not use mc_field_bg.fillWithAnalytic !! For some reaseon it erases info on components
            mc_field_bg.getArray().fillWithValue(-1.0)
            mc_field_bg.setTime(0.0, 0, 0)

            f = safe_clone(fieldInstance)
            f.setTime(0.0, 0, 0)

            # reduce number of components all over
            #print("the infos : ", mc_field_bg.getArray().getInfoOnComponents())
            compos_num_to_select = [mc_field_bg.getArray().getInfoOnComponents().index(c) for c in f.getArray().getInfoOnComponents()]
            mc_field_bg = mc_field_bg.keepSelectedComponents(compos_num_to_select)

            if nameInFile is not None:
                target = "_"*8
                expanded_name = nameInFile[:min(8,len(nameInFile))] + target[min(8,len(nameInFile)):]
                mc_field_bg.setName(expanded_name + self.Mordicus2MEDAster[fieldName])

            f.setName(mc_field_bg.getName())
            ff.setName(mc_field_bg.getName())

            # array needs to be put to the right shape and converted to DataArrayDouble
            identity_appl = np.linspace(float(offset),
                                        float(offset-1+f.getNumberOfTuples()*f.getNumberOfComponents()),
                                        f.getNumberOfTuples()*f.getNumberOfComponents())
            f.getArray().setValues(list(identity_appl), f.getNumberOfTuples(), f.getNumberOfComponents())
            offset = offset + f.getNumberOfTuples()*f.getNumberOfComponents()

            # put that into mc_field_bg
            if idsInRelLevel is not None:
                if fieldStructure.discr in ("gauss", "cell"):
                    tupIds = mc_field_bg.computeTupleIdsToSelectFromCellIds(idsInRelLevel)
                if fieldStructure.discr in ("node", ):
                    tupIds = idsInRelLevel
                mc_field_bg.getArray()[tupIds] = f.getArray().deepCopy()

                # write f to a new file
                ff.setFieldNoProfileSBT(mc_field_bg)
            else:
                ff.setFieldNoProfileSBT(f)

        mf_mesh.write(fileName, 2)
        ff.write(fileName, 0)

    def ReadSolutionStructure(self, fieldName,
                              discretization,
                              components=[],
                              dimsRelativeToMax=[],
                              groups=[],
                              componentsByRelativeDimension=None):
        """
        Read the solution structure object. To move to MEDSolutionReader ?

        Arguments
        ---------
        fieldName : str
            identifier of the physical quantity for the field
        discretization : str
            type of field discretization. Into {"node", "gauss"} for now, to be expanded...
        components : list(str)
            list of components to select in the field. By default they are all kept
        dimsRelativeToMax : list(int)
            list of dimension of mesh entities to select relative to max dimension. Into {-3, -2, -1, 0}
        componentsByRelativeDimension : dict(int: list)
            dictionary of components to keep by relative dimension (relative to max dimension)
        groups :
            list of groups to keep. result is extracted on the union of these groups.

        Returns
        -------
        MEDAsterSolutionStructure
            A SolutionStructure for MEDAster
        """
        if discretization == "node":
            return self._readSolutionStructureNode(fieldName,
                                                   components,
                                                   dimsRelativeToMax,
                                                   groups)
        # --- find right field to import by name and iteration
        all_iterations, MED_field_name = self._extract_name_and_sequence(fieldName)
        iteration, order = next((a, b) for (a, b, c) in all_iterations if math.isclose(c, self.timeIt))

        # read the field under the form of a MEDFileField1TS
        f1ts = ML.MEDFileField1TS(self.fileName, MED_field_name, iteration, order)

        mfMeshName = f1ts.getMeshName()
        mf_mesh = ML.MEDFileUMesh.New(self.fileName, mfMeshName)

        solutionStructure = MEDAsterSolutionStructure(MEDMesh(mf_mesh), "gauss")
        solutionStructureInternalStorage = {}

        # --- Loop over relative dimensions
        nonEmptyLevels = reversed(sorted(list(f1ts.getNonEmptyLevels()[1])))
        includedLevels = reversed(sorted(dimsRelativeToMax)) if dimsRelativeToMax else nonEmptyLevels

        walkedLevels = reversed(sorted(list( set(nonEmptyLevels).intersection(set(includedLevels)) )))

        for relLevel in walkedLevels:

            # extract list of groups that are non empty at this level
            groupSubset = groups
            if groups:
                groupSubset = [grp for grp in groups if relLevel in mf_mesh.getGrpNonEmptyLevels(grp)]

                if not groupSubset:
                    continue

            mc_field = f1ts.getFieldOnMeshAtLevel(self.Disc2MLType[discretization], relLevel, mf_mesh)

            # Select groups
            elemIdsInRelLevel = None
            if groups:
                elemIdsInRelLevel = mf_mesh.getGroupsArr(relLevel, groupSubset, False)
                mc_field = mc_field[elemIdsInRelLevel] # or mc_field = mc_field.buildSubPart(elemIdsInRelLevel)

            # Select components for this relative dimension
            if components or (componentsByRelativeDimension and componentsByRelativeDimension[relLevel]):
                list_of_compos = mc_field.getArray().getInfoOnComponents()
                compos_to_select = componentsByRelativeDimension[relLevel] if componentsByRelativeDimension and componentsByRelativeDimension[relLevel] \
                    else components

                compos_num_to_select = [list_of_compos.index(c) for c in compos_to_select]

                mc_field = mc_field.keepSelectedComponents(compos_num_to_select)

            solutionStructureInternalStorage[relLevel] = (mc_field, elemIdsInRelLevel)

        solutionStructure.SetInternalStorage(solutionStructureInternalStorage)
        return solutionStructure

    def _readSolutionStructureNode(self, fieldName,
                                   components=[],
                                   dimsRelativeToMax=[],
                                   groups=[]):
        """
        Read solution structure for node fields
        """
        all_iterations, MED_field_name = self._extract_name_and_sequence(fieldName)
        iteration, order = next((a, b) for (a, b, c) in all_iterations if math.isclose(c, self.timeIt))

        # read the field under the form of a MEDFileField1TS
        mf_field = ML.MEDFileField1TS(self.fileName, MED_field_name, iteration, order)
        mfMeshName = mf_field.getMeshName()
        mf_mesh = ML.MEDFileUMesh.New(self.fileName, mfMeshName)
        solutionStructure = MEDAsterSolutionStructure(MEDMesh(mf_mesh), "node")

        #mc_field_bg = mf_field.getFieldAtTopLevel(ML.ON_NODES, False)
        mc_field_bg = mf_field.getFieldOnMeshAtLevel(ML.ON_NODES, 0, mf_mesh)

        # First, build array of nodes to keep
        extractDef = {}

        nodeIdsGlobalArray = None
        if groups:
            nodeIdsGlobalArray = mf_mesh.getNodeGroupsArr(groups, False)
            extractDef[1] = nodeIdsGlobalArray

        # --- Loop over relative dimensions
        nonEmptyLevels = list(mf_mesh.getNonEmptyLevels())
        includedLevels = reversed(sorted(dimsRelativeToMax)) if dimsRelativeToMax else nonEmptyLevels

        walkedLevels = reversed(sorted(list(set(nonEmptyLevels).intersection(includedLevels))))

        for level in walkedLevels:
            extractDef[level] = ml.DataArrayInt.New(list(range(mf_mesh.getNumberOfCellsAtLevel(level))))

        finalNodeIdsGlobalArray = mf_mesh.deduceNodeSubPartFromCellSubPart(extractDef)
        restricted_mf_mesh = mf_mesh.extractPart(extractDef)
        mc_restricted_mesh = restricted_mf_mesh.getMeshAtLevel(0, False)

        # Create MEDCouplingField from scratch, based on user doc
        fieldOnNodes=ml.MEDCouplingFieldDouble(ml.ON_NODES, ml.ONE_TIME)
        fieldOnNodes.setName(mc_field_bg.getName())
        fieldOnNodes.setMesh(mc_restricted_mesh)
        fieldOnNodes.setTime(self.timeIt, iteration, order)
        array=ml.DataArrayDouble()
        array.alloc(fieldOnNodes.getMesh().getNumberOfNodes(), mc_field_bg.getNumberOfComponents()) # Implicitly fieldOnNodes will be a 1 component field.
        array.fillWithValue(0.)
        array.setInfoOnComponents(mc_field_bg.getArray().getInfoOnComponents())

        fieldOnNodes.setArray(array)

        fieldOnNodes.checkConsistencyLight()

        if components:
            list_of_compos = fieldOnNodes.getArray().getInfoOnComponents()
            compos_num_to_select = [list_of_compos.index(c) for c in components]
            fieldOnNodes = fieldOnNodes.keepSelectedComponents(compos_num_to_select)

        internalStorage = {}

        # Put this array at level 0, and a global sample field
        internalStorage[0] = (fieldOnNodes, finalNodeIdsGlobalArray)

        #for level in walkedLevels:

        #    tab = {}
        #    tab[1] = extractDef[1]
        #    tab[level] = extractDef[level]

        #    nodeIdsArray = mf_mesh.deduceNodeSubPartFromCellSubPart(tab)
        #    restr_mf_mesh = mf_mesh.extractPart(tab)

        #    relNodeIds = ml.DataArrayInt(list(filter(lambda x: finalNodeIdsGlobalArray[x] in nodeIdsArray, range(len(finalNodeIdsGlobalArray)))))

        #    internalStorage[level] = relNodeIds

        solutionStructure.SetInternalStorage(internalStorage)
        return solutionStructure


import random
import os

def initMineList(row, col):
    mineList = []
    statusList = []
    minestatusList = []

    r = 0
    while r < row:
        colList = []
        colStatusList = []
        l = 0
        while l < col:
            colList.append(0)
            colStatusList.append(0)
            l += 1
        mineList.append(colList)
        statusList.append(colStatusList)
        r += 1
    minestatusList.append(mineList)
    minestatusList.append(statusList)
    return minestatusList

def setMine(mineList, num):
    count = 0
    row = len(mineList)
    col = len(mineList[0])
    while count < num:
        row_id = random.randint(0, row - 1)
        col_id = random.randint(0, col - 1)
        if(mineList[row_id][col_id] != -1):
            mineList[row_id][col_id] = -1
            count += 1

def displayBorad(mineList, statusList):
    os.system('cls')
    row_id = len(mineList)
    col_id = len(mineList[0])

    setMineBoardNum(mineList)

    r = 0
    while r < row_id:
        l = 0
        if(r == 0):
            print(" ",end='')
            while l < col_id:
                print(" %d " % l,end="")
                l += 1
            print("")
        l = 0
        while l < col_id:
            if(l == 0):
                print("%d" % r ,end="")
            if(statusList[r][l] == 0):
                print("[-]",end="")
            elif mineList[r][l] != -1:
                print("[%d]" % mineList[r][l],end="")
            else:
                print("[BOM]",end="")
            l += 1
        print("")
        r += 1

def setMineBoardNum(mineList):
    row_id = len(mineList)
    col_id = len(mineList[0])

    r = 0
    while r < row_id:
        l = 0
        while l < col_id:
            setLocNum(mineList, r, l)
            l += 1
        r += 1

def setLocNum(mineList, r, l):
    mineList[r][l] = getLocNum(mineList, r, l)

def getLocNum(mineList, r, l):
    row_id = len(mineList)
    col_id = len(mineList[0])

    if(mineList[r][l] == -1):
        return -1
    count = 0
    if(r - 1 >= 0):
        if(l - 1 >= 0 and mineList[r - 1][l - 1] == -1):
            count += 1
        if(mineList[r - 1][l] == -1):
            count += 1
        if(l + 1 < col_id and mineList[r - 1][l + 1] == -1):
            count += 1

    if(l - 1 >= 0 and mineList[r][l - 1] == -1):
        count += 1
    if(mineList[r][l] == -1):
        count += 1
    if(l + 1 < col_id and mineList[r][l + 1] == -1):
        count += 1

    if(r + 1 < row_id):
        if(l - 1 >= 0 and mineList[r + 1][l - 1] == -1):
            count += 1
        if(mineList[r + 1][l] == -1):
            count += 1
        if(l + 1 < col_id and mineList[r + 1][l + 1] == -1):
            count += 1

    return count

def gemeLoop(mineList, statusList):
    re = move(mineList, statusList)
    while(re == 0):
        re = move(mineList, statusList)
    return re

def move(mineList, statusList):
    if checkEnd(mineList, statusList) == 0:
        return 2
    else:
        row, col = input("Enter row col what you want uncovered : ").split()
        #row = 2
        #col = 3
        re = uncovered(mineList, statusList, int(row), int(col))
        if(re == 0):
            displayBorad(mineList, statusList)
            return 0
        elif(re == 1):
            displayBoom(mineList, statusList)
            return 1

def checkEnd(mineList, statusList):
    row = len(mineList)
    col = len(mineList[0])

    r = 0
    while r < row:
        l = 0
        while l < col:
            if(statusList[r][l] == 0):
                if(mineList[r][l] != -1):
                    return 1
            l += 1
        r += 1
    return 0

def displayBoom(mineList, statusList):
    os.system('cls')
    row_id = len(mineList)
    col_id = len(mineList[0])

    setMineBoardNum(mineList)

    r = 0
    while r < row_id:
        l = 0
        if(r == 0):
            print(" ",end='')
            while l < col_id:
                print(" %d " % l,end="")
                l += 1
            print("")
        l = 0
        while l < col_id:
            if(l == 0):
                print("%d" % r ,end="")
            if(mineList[r][l] == -1):
                print("[B]",end="")
            elif(statusList[r][l] == 0):
                print("[-]",end="")
            elif mineList[r][l] != -1:
                print("[%d]" % mineList[r][l],end="")
            l += 1
        print("")
        r += 1

def uncovered(mineList,statusList,row,col):
    r = len(mineList)
    l = len(mineList[0])
    if(row >= r or col >= l or row < 0 or col < 0):
        return 0
    if(statusList[row][col] == 1):
        return 0
    statusList[row][col] = 1

    if(mineList[row][col] == -1):
        return 1
    else:
        if(getLocNum(mineList,row,col) == 0):

            if(row - 1 >= 0):
                if(col - 1 >= 0):
                    uncovered(mineList,statusList,row - 1,col - 1)
                uncovered(mineList,statusList,row - 1,col)
                if(col + 1 < l):
                    uncovered(mineList,statusList,row - 1,col + 1)

            if(col - 1 >= 0):
                uncovered(mineList,statusList,row,col - 1)
            if(col + 1 < l):
                uncovered(mineList,statusList,row,col + 1)

            if(row + 1 < r):
                if(col - 1 >= 0):
                    uncovered(mineList,statusList,row + 1,col - 1)
                uncovered(mineList,statusList,row + 1,col)
                if(col + 1 >= 0):
                    uncovered(mineList,statusList,row + 1,col + 1)

        return 0