"""
Laboratorio - Manipulación de Datos usando Pandas
-----------------------------------------------------------------------------------------

Este archivo contiene las preguntas que se van a realizar en el laboratorio.

Utilice los archivos `tbl0.tsv`, `tbl1.tsv` y `tbl2.tsv`, para resolver las preguntas.

"""
import pandas as pd

tbl0 = pd.read_csv("tbl0.tsv", sep="\t")
tbl1 = pd.read_csv("tbl1.tsv", sep="\t")
tbl2 = pd.read_csv("tbl2.tsv", sep="\t")

def pregunta_01():
    """
    ¿Cuál es la cantidad de filas en la tabla `tbl0.tsv`?

    Rta/
    40

    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    numerofilas = tbl0.shape
    numero =numerofilas[0]

    return numero

def pregunta_02():
    """
    ¿Cuál es la cantidad de columnas en la tabla `tbl0.tsv`?

    Rta/
    4

    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    numerocolumna = tbl0.shape
    numero =numerocolumna[1]

    return numero

def pregunta_03():
    """
    ¿Cuál es la cantidad de registros por cada letra de la columna _c1 del archivo
    `tbl0.tsv`?

    Rta/
    A     8
    B     7
    C     5
    D     6
    E    14
    Name: _c1, dtype: int64

    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    setcolumnauno = tbl0.filter(items=["_c1"])
    cuentaColumnaUno = setcolumnauno.groupby("_c1")["_c1"].count()

    return cuentaColumnaUno

def pregunta_04():
    """
    Calcule el promedio de _c2 por cada letra de la _c1 del archivo `tbl0.tsv`.

    Rta/
    A    4.625000
    B    5.142857
    C    5.400000
    D    3.833333
    E    4.785714
    Name: _c2, dtype: float64
    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    setcolumnauno = tbl0.filter(items=["_c1", "_c2"])
    promedioColumnados = setcolumnauno.groupby("_c1")["_c2"].mean()

    return promedioColumnados

def pregunta_05():
    """
    Calcule el valor máximo de _c2 por cada letra en la columna _c1 del archivo
    `tbl0.tsv`.

    Rta/
    _c1
    A    9
    B    9
    C    9
    D    7
    E    9
    Name: _c2, dtype: int64
    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    setcolumnauno = tbl0.filter(items=["_c1", "_c2"])
    maxColumnados = setcolumnauno.groupby("_c1")["_c2"].max()

    return maxColumnados

def pregunta_06():
    """
    Retorne una lista con los valores unicos de la columna _c4 de del archivo `tbl1.csv`
    en mayusculas y ordenados alfabéticamente.

    Rta/
    ['A', 'B', 'C', 'D', 'E', 'F', 'G']

    """
    tbl1 = pd.read_csv("tbl1.tsv", sep="\t")

    setcolumncuatro = tbl1.filter(items=["_c4"])
    setcolumncuatro = setcolumncuatro["_c4"].unique()
    litvaloresUnicos =[ x.upper() for x in setcolumncuatro]
    litvaloresUnicos = sorted(litvaloresUnicos)

    return litvaloresUnicos

def pregunta_07():
    """
    Calcule la suma de la _c2 por cada letra de la _c1 del archivo `tbl0.tsv`.

    Rta/
    _c1
    A    37
    B    36
    C    27
    D    23
    E    67
    Name: _c2, dtype: int64
    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    setcolumnauno = tbl0.filter(items=["_c1","_c2"])
    valoresUnicosColumnados = setcolumnauno.groupby("_c1")["_c2"].sum()

    return valoresUnicosColumnados

def pregunta_08():
    """
    Agregue una columna llamada `suma` con la suma de _c0 y _c2 al archivo `tbl0.tsv`.

    Rta/
        _c0 _c1  _c2         _c3  suma
    0     0   E    1  1999-02-28     1
    1     1   A    2  1999-10-28     3
    2     2   B    5  1998-05-02     7
    ...
    37   37   C    9  1997-07-22    46
    38   38   E    1  1999-09-28    39
    39   39   E    5  1998-01-26    44

    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    tbl0['suma'] = tbl0['_c0'] + tbl0['_c2']

    return tbl0

def pregunta_09():
    """
    Agregue el año como una columna al archivo `tbl0.tsv`.

    Rta/
        _c0 _c1  _c2         _c3  year
    0     0   E    1  1999-02-28  1999
    1     1   A    2  1999-10-28  1999
    2     2   B    5  1998-05-02  1998
    ...
    37   37   C    9  1997-07-22  1997
    38   38   E    1  1999-09-28  1999
    39   39   E    5  1998-01-26  1998

    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    tbl0['year'] = pd.to_datetime(tbl0['_c3'],infer_datetime_format=True,errors='ignore')
    tbl0['year'] = tbl0['year'].apply(lambda x: str(x)[0:4])

    return tbl0

def pregunta_10():
    """
    Construya una tabla que contenga _c1 y una lista separada por ':' de los valores de
    la columna _c2 para el archivo `tbl0.tsv`.

    Rta/
                                   _c1
      _c0
    0   A              1:1:2:3:6:7:8:9
    1   B                1:3:4:5:6:8:9
    2   C                    0:5:6:7:9
    3   D                  1:2:3:5:5:7
    4   E  1:1:2:3:3:4:5:5:5:6:7:8:8:9
    """
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    setcolumnauno = tbl0.filter(items=["_c1","_c2"])
    setcolumnauno = setcolumnauno.sort_values("_c2",ascending=True)
    setcolumnauno["_c2"] = setcolumnauno["_c2"].apply(lambda x: str(x))
    setcolumnaresultado = setcolumnauno.groupby(["_c1"], as_index=False).agg({"_c2": ":".join})
    setcolumnaresultado.set_index("_c1", inplace=True)

    return setcolumnaresultado

def pregunta_11():
    """
    Construya una tabla que contenga _c0 y una lista separada por ',' de los valores de
    la columna _c4 del archivo `tbl1.tsv`.

    Rta/
        _c0      _c4
    0     0    b,f,g
    1     1    a,c,f
    2     2  a,c,e,f
    3     3      a,b
    ...
    37   37  a,c,e,f
    38   38      d,e
    39   39    a,d,f
    """
    tbl1 = pd.read_csv("tbl1.tsv", sep="\t")

    setcolumna = tbl1.filter(items=["_c0","_c4"])
    setcolumna = setcolumna.sort_values("_c4",ascending=True)
    setcolumna["_c4"] = setcolumna["_c4"].apply(lambda x: str(x))
    setcolumnaresultado = setcolumna.groupby(["_c0"], as_index=False).agg({"_c4": ",".join})

    return setcolumnaresultado

def pregunta_12():
    """
    Construya una tabla que contenga _c0 y una lista separada por ',' de los valores de
    la columna _c5a y _c5b (unidos por ':') de la tabla `tbl2.tsv`.

    Rta/
        _c0                                  _c5
    0     0        bbb:0,ddd:9,ggg:8,hhh:2,jjj:3
    1     1              aaa:3,ccc:2,ddd:0,hhh:9
    2     2              ccc:6,ddd:2,ggg:5,jjj:1
    ...
    37   37                    eee:0,fff:2,hhh:6
    38   38                    eee:0,fff:9,iii:2
    39   39                    ggg:3,hhh:8,jjj:5
    """
    tbl2 = pd.read_csv("tbl2.tsv", sep="\t")

    setcolumna = tbl2.sort_values(["_c0","_c5a","_c5b"],ascending=True)
    setcolumna["_c5"] = setcolumna["_c5a"].map(str) + ":" + setcolumna["_c5b"].map(str)
    setcolumnaresultado = setcolumna.groupby(["_c0"], as_index=False).agg({"_c5": ",".join})

    return setcolumnaresultado

def pregunta_13():
    """
    Si la columna _c0 es la clave en los archivos `tbl0.tsv` y `tbl2.tsv`, compute la
    suma de tbl2._c5b por cada valor en tbl0._c1.

    Rta/
    _c1
    A    146
    B    134
    C     81
    D    112
    E    275
    Name: _c5b, dtype: int64
    """
    tbl2 = pd.read_csv("tbl2.tsv", sep="\t")
    tbl0 = pd.read_csv("tbl0.tsv", sep="\t")

    valoresUnicosColumnados = pd.DataFrame(tbl2.groupby("_c0")["_c5b"].sum())
    resultado = pd.merge(tbl0, valoresUnicosColumnados, how='inner', on=("_c0"))
    resultado = resultado.groupby("_c1")["_c5b"].sum()

    return resultado


import time
from Modules import timeformat as tf, zipmodule as zm, osoprations as osop,logmodule as lm
from Constant import ZipperApp as zac
logger = lm.getlogger()

while(True):
    textfilename=osop.fileexists(zac.textfilepath)
    if not (textfilename=="Only 1 .txt allowed" or textfilename=="no .txt found we'll keep checking"):
        logger.info(textfilename+" found")
        epochtimestamp=tf.getepoch()
        zipname=tf.getformattedtime(epochtimestamp,zac.timestringformat)
        if (zm.createzip(epochtimestamp,zipname,zac.textfilepath,textfilename,osop.dircreateexists(zac.zipdestinationfolder))):
            logger.info(zipname + " created")
            osop.delfile(zac.textfilepath + textfilename)
        else:
            logger.critical("zip creation failed")
    else:
        logger.info(textfilename)
        time.sleep(30)


import os
import numpy as np
import unittest
import redvox.api1000.wrapped_redvox_packet.sensors.image as image
import redvox.tests as test_utils

class TestImage(unittest.TestCase):
    def setUp(self) -> None:
        self.empty_image_sensor: image.Image = image.Image.new()
        self.non_empty_image_sensor: image.Image = image.Image.new()
        with open(test_utils.test_data('redvox_logo.png'), "rb") as image_in:
            data_as_bytes: bytes = image_in.read()
            self.non_empty_image_sensor.set_samples([data_as_bytes, data_as_bytes])
        self.non_empty_image_sensor.set_image_codec(image.ImageCodec.PNG)
        self.non_empty_image_sensor.get_timestamps().set_default_unit()
        self.non_empty_image_sensor.get_timestamps().set_timestamps(np.array([1000, 2000]), True)

    def test_append_value(self):
        with open(test_utils.test_data('redvox_logo.png'), "rb") as image_in:
            data_as_bytes: bytes = image_in.read()
            self.non_empty_image_sensor.append_value(data_as_bytes)
        self.assertEqual(self.non_empty_image_sensor.get_num_images(), 3)

    # def test_write_image(self):
    #     self.non_empty_image_sensor.write_image(0, out_file="redvox_logo_out")
    #     with open(test_utils.test_data('redvox_logo.png'), "rb") \
    #             as image_in:
    #         in_bytes: bytes = image_in.read()
    #         with open(test_utils.test_data('redvox_logo_out.png'), "rb") \
    #                 as image_out:
    #             out_bytes: bytes = image_out.read()
    #             self.assertEqual(in_bytes, out_bytes)
    #     os.remove(test_utils.test_data('redvox_logo_out.png'))

    def test_validate_image(self):
        error_list = image.validate_image(self.non_empty_image_sensor)
        self.assertEqual(error_list, [])
        error_list = image.validate_image(self.empty_image_sensor)
        self.assertNotEqual(error_list, [])


import logging
import requests
import zeep

from datetime import datetime, timezone

from config import STOCK_PRICE_TOKEN, STOCK_PRICE_URL, CURRENCY_SOAP_URL
from models import StockQuoteApiResponse

from persistence import get_cursor

LOGGER = logging.getLogger(__name__)
SOAP_CLIENT = zeep.Client(wsdl=CURRENCY_SOAP_URL)

def get_company_quote(symbol: str) -> StockQuoteApiResponse:
    """ Use the broker API to get the relevant information for a given company """
    data = requests.get(f"{STOCK_PRICE_URL}/markets/quotes",
        params={"symbols": symbol},
        headers={"Authorization": f"Bearer {STOCK_PRICE_TOKEN}", "Accept": "application/json"}
    )

    return StockQuoteApiResponse(**data.json()["quotes"]["quote"])

def convert_price_to_currency(price: float, currency_from: str, currency_to: str) -> float:
    """ Convert a given price from a currency to another """
    return SOAP_CLIENT.service.GetConversionRate(currency_from, currency_to)

def get_latest_price_for_company(symbol: str):
    """ Get the latest available price for a given company """
    with get_cursor() as cursor:
        cursor.execute("""SELECT price FROM prices WHERE company_symbol = %s
                          ORDER BY timestamp DESC LIMIT 1;""", (symbol,))
        price = cursor.fetchone()
    return price['price'] if price['price'] is not None else None

def insert_update_price_for_company(symbol: str, currency: str = 'GBP'):
    quote: StockQuoteApiResponse = get_company_quote(symbol)
    with get_cursor() as cursor:
        cursor.execute("""INSERT INTO prices (company_symbol, price, currency, timestamp)
                          VALUES (%s, %s, %s, %s)""",
                       (symbol, quote.last, currency, datetime.now(tz=timezone.utc).isoformat())
        )


import fire
from transformers import (
    AutoTokenizer,
    BertForTokenClassification,
    BertConfig,
    BertForMaskedLM,
)
from neo_stif.components.utils import create_label_map
import pandas as pd
from neo_stif.components.train_data_preparation import prepare_data_tagging_and_pointer
import datasets
from neo_stif.lit import LitTaggerOrInsertion
from torch.utils.data import DataLoader
from neo_stif.components.collator import FelixCollator, FelixInsertionCollator
from lightning import Trainer
from lightning.pytorch.callbacks import RichProgressBar, ModelCheckpoint, EarlyStopping
from neo_stif.components.utils import compute_class_weights
from datasets import load_from_disk

def taggerpoint(
    df_train,
    data_train,
    tokenizer,
    batch_size,
    model_path_or_name,
    with_validation=False,
    do_compute_class_weight=False,
    device="cuda",
    label_dict=None,
    df_dev=None,
    use_pointing=True,
    output_dir_path="output/stif-i-f/felix-tagger-pointer/",
    lr=3e-5,
    hidden_size_pointer=64,
    num_hidden_layers_pointer=2,
    from_scratch=False,
    src_label="informal",
    tgt_label="formal",
):
    rich_cb = RichProgressBar()

    checkpoint_callback = ModelCheckpoint(
        dirpath=output_dir_path,
        filename="{epoch}-{val_loss:.2f}",
        save_top_k=1,
        save_last=True,
        monitor="val_loss",
        mode="min",
    )
    # ea_stop = EarlyStopping(patience=15, monitor="val_loss", mode="min")
    dev_dl = None
    class_weights = (
        compute_class_weights(df_train.label.apply(eval), num_classes=len(label_dict))
        if do_compute_class_weight
        else None
    )
    print(class_weights)
    if with_validation:
        data_dev = datasets.Dataset.from_pandas(df_dev)
        data_dev, label_dict = prepare_data_tagging_and_pointer(
            data_dev, tokenizer, label_dict
        )
        dev_dl = DataLoader(
            data_dev,
            batch_size=batch_size,
            shuffle=True,
            collate_fn=FelixCollator(
                tokenizer,
                pad_label_as_input=len(label_dict),
                src=src_label,
                tgt=tgt_label,
            ),
        )

    if from_scratch:
        # copy config from model_path_or_name
        # and change the number of labels

        bert_config = BertConfig.from_pretrained(model_path_or_name)
        bert_config.num_labels = len(label_dict)
        pre_trained_bert = BertForTokenClassification(bert_config)  # from scratch
    else:
        pre_trained_bert = BertForTokenClassification.from_pretrained(
            model_path_or_name, num_labels=len(label_dict)
        )

    pointer_network_config = BertConfig(
        vocab_size=len(label_dict) + 1,
        num_hidden_layers=num_hidden_layers_pointer,
        hidden_size=hidden_size_pointer,
        num_attention_heads=1,
        pad_token_id=len(label_dict),
    )  # + 1 as the pad token

    lit_tagger = LitTaggerOrInsertion(
        pre_trained_bert,
        lr=lr,
        num_classes=len(label_dict),
        class_weight=class_weights,
        tokenizer=tokenizer,
        label_dict=label_dict,
        use_pointer=use_pointing,
        pointer_config=pointer_network_config,
    )
    train_dl = DataLoader(
        data_train,
        batch_size=batch_size,
        shuffle=True,
        collate_fn=FelixCollator(tokenizer, pad_label_as_input=len(label_dict)),
    )
    trainer = Trainer(
        accelerator=device,
        devices=1,
        val_check_interval=30,
        max_epochs=500,
        precision=16,
        check_val_every_n_epoch=None,
        callbacks=[rich_cb, checkpoint_callback],
    )
    trainer.fit(lit_tagger, train_dl, dev_dl)


"""recursion  --functions which calls itself again and again is called recursion  eg: factorial,fibonacci series"""
"""local variable can be accessed within a function
global variable can be accessed throughout the code
local can be changed to global by using 'global'"""
#recursion
#factorial of a number
def fact(no):
    if no==0 or no==1:
        return 1
    else:
        return (no*fact(no-1))
no=int(input("enter a no."))
print(fact(no))


# using Keras
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
import time
# Makes random numbers predictable
np.random.seed(7)
start_time = time.time()

# reading the dataset, splitting it into input and output
#load dataset
dataset = np.loadtxt("C:/Santhosh/AIML/data/pima-indians-diabetes.csv", delimiter=",")
#print(dataset)
print(dataset.shape)
# shape (768, 9) -- has 9 columns, Output -- 9th column, input -- first 8 columns
# X- input, Y- Output
x = dataset[:,0:8]
y = dataset[:,8]

print(x.shape) # (768, 8) -- 768 samples, 8 features
print(y.shape) # (768, ) -- 768 samples, 1 feature

print(x.size) # 6144 records/values
print(y.size) # 768 records/values

# Model
model = Sequential()
model.add((Dense(12, activation='relu', input_dim=8, name='Input')))
# model.add(Dense(8, activation='relu', name='hidden'))
model.add((Dense(1, activation='sigmoid', name='output')))

# Plotting model
from keras.utils.vis_utils import plot_model
plot_model(model, to_file='layers.png', show_shapes=True, show_layer_names=True)

#learning - loss, optimizer
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

#train - epochs, dataset
model.fit(x, y, epochs=150)

#Evaluate - Returns the loss value & metrics values
metrics = model.evaluate(x, y)

print("\n%s: %.2f%%" % (model.metrics_names[1], metrics[1]*100))
print('Elapsed TIme',time.time() - start_time)


import torch
import torch.nn as nn
from ..utils.hyperparams import ACTIVATION_FN_FACTORY

class DenseDiscriminator(nn.Module):

    def __init__(self, params):
        """
        This class specifies the discriminator of an AAE.
        It can be trained to distinguish real samples from a target distr.
        (e.g. Gaussian, Uniform, Gaussian Mixture ...) from fake samples
        constructed through the generator.

        Args:
            params (dict): A dict with the model parameters.

        """
        super(DenseDiscriminator, self).__init__()

        # Retrieve discriminator architecture
        self.disc_hidden_sizes = params['discriminator_hidden_sizes']
        self.input_size = params['input_size']
        self.disc_activation_fn = ACTIVATION_FN_FACTORY[
            params.get('discriminator_activation_fn', 'relu')]
        self.disc_dropout = (
            [params.get('discriminator_dropout', 0.0)] *
            len(self.hidden_sizes)
            if isinstance(params.get('discriminator_dropout', 0.0),
                          int) else params.get('discriminator_dropout', 0.0)
        )
        self._assertion_tests

        # Build discriminator
        num_units = [self.input_size] + self.disc_hidden_sizes
        ops = []
        for index in range(1, len(num_units)):
            ops.append(nn.Linear(num_units[index - 1], num_units[index]))
            ops.append(self.disc_activation_fn)
            if self.disc_dropout[index - 1] > 0.0:
                ops.append(nn.Dropout(p=self.disc_dropout[index - 1]))

        ops.append(nn.Linear(num_units[-1], 1))
        ops.append(nn.Sigmoid())
        self.discriminator = nn.Sequential(*ops)

    def forward(self, x):
        """The discriminator aiming to classify true and fake latent samples.

        Args:
            data (torch.Tensor) : Input data of shape batch_size x input_size.

        Returns:
            torch.Tensor: Logits, i.e. for each score a probability of being
                from the real target distribution p(z)
                of shape `[batch_size, 1]`.
        """
        return self.discriminator(x)

    def loss(self, real, fake):
        """
        The discriminator loss is fixed to be the binary cross entropy of the
        real and fake samples.

        Args:
            real (torch.Tensor): Discriminator logits for target distribution
                samples. Vector of length `batch_size`.
            fake (torch.Tensor): Discriminator logits for generator samples
                (ideally 0.0). Vector of length `batch_size`.

        Returns:
            torch.Tensor: binary_cross_entropy(real, fake)
        """
        return -torch.mean(torch.cat([torch.log(real), torch.log(1 - fake)]))

    def _assertion_tests(self):
        pass

    def load(self, path):
        """Load model from path."""
        weights = torch.load(path)
        self.load_state_dict(weights)

    def save(self, path):
        """Save model to path."""
        torch.save(self.state_dict(), path)


#!/usr/bin/env python3
import sys
import math
from itertools import combinations

def lcm(n1, n2):
    return (n1 * n2) // math.gcd(n1, n2)

def get_velocity(p0, p1):
    if p0 == p1:
        return 0
    elif p0 > p1:
        return -1
    else:
        return 1

def apply_velocity(points, pairs):
    # calculate velocity
    for i, (k0, k1) in enumerate(pairs):
        m0, v0 = points[k0]
        m1, v1 = points[k1]

        v0 += get_velocity(m0, m1)
        v1 += get_velocity(m1, m0)

        points[k0] = (m0, v0)
        points[k1] = (m1, v1)

    # apply velocity
    for key in points:
        m, v = points[key]
        points[key] = (m + v, v)

    return points

initial_x = {}
initial_y = {}
initial_z = {}

keys = []
points_x = {}
points_y = {}
points_z = {}
for line in sys.stdin:
    xs, ys, zs = line.strip()[1:-1].split(',')

    x = int(xs.strip()[2:])
    y = int(ys.strip()[2:])
    z = int(zs.strip()[2:])

    key = (x, y, z)
    keys.append(key)

    points_x[key] = (x, 0)
    points_y[key] = (y, 0)
    points_z[key] = (z, 0)

    initial_x[key] = (x, 0)
    initial_y[key] = (y, 0)
    initial_z[key] = (z, 0)

pairs = list(combinations(keys, 2))
step = 0

step_x = None
step_y = None
step_z = None
while True:
    points_x = apply_velocity(points_x, pairs)
    points_y = apply_velocity(points_y, pairs)
    points_z = apply_velocity(points_z, pairs)

    step += 1

    # check initial
    if step_x is None and points_x == initial_x:
        step_x = step
    if step_y is None and points_y == initial_y:
        step_y = step
    if step_z is None and points_z == initial_z:
        step_z = step

    if step_x and step_y and step_z:
        break

print(lcm(step_x, lcm(step_y, step_z)))


import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import BernoulliNB
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

df = pd.read_csv("data/processed_tweets.csv")
df = df.astype({"post_text":str})

count_vector = CountVectorizer(stop_words="english")
tfidf_transformer = TfidfTransformer()
sparse_matrix = count_vector.fit_transform(df.post_text)
sparse_matrix = tfidf_transformer.fit_transform(sparse_matrix)

x = sparse_matrix
y = df["label"].values

x_train, x_test, y_train, y_test = train_test_split(x, y, stratify = y, random_state = 611)

BNB = BernoulliNB()
clf = BNB.fit(x_train, y_train)
print("The Accuracy of Bernoulli Naive Bayes is {:2.2%}".format(clf.score(x_test, y_test)))
cm = confusion_matrix(y_test, clf.predict(x_test), labels=clf.classes_)
disp = ConfusionMatrixDisplay(cm, display_labels=clf.classes_)
disp.plot()
plt.title('The Confusion Matrix of Bernoulli Naive Bayes Classifier')
plt.savefig("result/cm_bnb.png")


from itertools import combinations_with_replacement

def solution(n, info):
    answer = [-1]*11
    score = -1

    #이기면 추가
    #경우의 수
    candidates = list(combinations_with_replacement(range(11), n)) #n arrow at index

    for cand in candidates:
        tmp = [0]*11
        tmp_score = 0
        for index in cand:
            tmp[index] += 1

        # compare with info
        for i in range(11):
            if info[i]==0 and tmp[i]==0:
                continue

            if info[i] < tmp[i]:
                tmp_score += (10 - i)
            else:
                tmp_score -= (10 - i)

        # appeach win
        if tmp_score <=0:
            continue

        if tmp_score > score:
            score = tmp_score
            answer = tmp

        elif tmp_score == score:
            if answer[::-1] >tmp [::-1]:
                answer = tmp

    return answer

if __name__ == "__main__":
    n = 5
    info = [2,1,1,1,0,0,0,0,0,0,0]
    print(solution(n, info))


"""
Count Paths for a Sum (medium)
Problem Statement #
Given a binary tree and a number ‘S’, find all paths in the tree such that the sum of all the node values of each path equals ‘S’. Please note that the paths can start or end at any node but all paths must follow direction from parent to child (top to bottom).
"""

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

def count_paths(root, S):
  return count_paths_recursive(root, S, [])

def count_paths_recursive(currentNode, S, currentPath):
  if currentNode is None:
    return 0

  # add the current node to the path
  currentPath.append(currentNode.val)
  pathCount, pathSum = 0, 0
  # find the sums of all sub-paths in the current path list
  for i in range(len(currentPath)-1, -1, -1):
    pathSum += currentPath[i]
    # if the sum of any sub-path is equal to 'S' we increment our path count.
    if pathSum == S:
      pathCount += 1

  # traverse the left sub-tree
  pathCount += count_paths_recursive(currentNode.left, S, currentPath)
  # traverse the right sub-tree
  pathCount += count_paths_recursive(currentNode.right, S, currentPath)

  # remove the current node from the path to backtrack
  # we need to remove the current node while we are going up the recursive call stack
  del currentPath[-1]

  return pathCount

def main():
  root = TreeNode(12)
  root.left = TreeNode(7)
  root.right = TreeNode(1)
  root.left.left = TreeNode(4)
  root.right.left = TreeNode(10)
  root.right.right = TreeNode(5)
  print("Tree has paths: " + str(count_paths(root, 11)))

main()

Patterns/Pattern-Tree Depth First Search/Count Paths for a Sum (medium).py


# Computes the average high temperature from a file that contains the daily
# high temperatures for last year at one location.

import sys

DATAFILE="data/alaska.dat"

# open the file
tempFile = open(DATAFILE, "r")

# read the data file
contents = tempFile.read()
tempFile.close()

# break the contents up by line
tempStrList = contents.split()

# Alternative way to find out how many temperatures are in the file
#altNumTemps = len(tempStrList)

# initialize the accumulators
tempTotal = 0
numTemps = 0

# iterate over the temperatures, which are strings
for tempStr in tempStrList:
    # need to convert the string to a float
    temp = float(tempStr)
    # accumulate the temperature total and the number of temperatures
    tempTotal += temp
    numTemps += 1

# Check to make sure we have the correct number of temperatures.
if numTemps != 365:
    print("Please check", DATAFILE)
    print("It does not seem to have the right number of temperatures in it.")
    sys.exit(1)

# compute the average
average = tempTotal/numTemps

# display the average...
print("The average high temperature from %s is %.2f" % (DATAFILE, average))


t = int(input())

def allOddOrEven(a):
    even = 0
    odd = 0
    for number in a:
        if number % 2 == 0:
            even += 1
        else:
            odd += 1
    if even == len(a):
        return True
    if odd == len(a):
        return True
    return False

for _ in range(t):
    n = int(input())
    a = [int(x) for x in input().split()]

    if allOddOrEven(a):
        print("YES")
    else:
        print("NO")


class Settings:
    def __init__(self):
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)

        self.ship_limit = 3

        self.fleet_drop_speed = 10

        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = (60, 60, 60)
        self.bullets_allowed = 3

        #If game becomes quicklier
        self.speedup_scale = 1.1
        #Have fast increase cost of aliens
        self.score_scale = 1.5
        self.initialize_dynamic_settings()

    def initialize_dynamic_settings(self):
        self.bullets_allowed = 10
        self.ship_speed = 3.0
        self.bullet_speed = 1.5
        self.alien_speed = 0.5
        #Getting score
        self.alien_point = 5

            # fleet_direction of 1 represents right; -1 represents left.
        self.fleet_direction = 1

    def increase_speed(self):
        """Increase settings of speed and cost of aliens"""
        self.ship_speed *= self.speedup_scale
        self.bullet_speed *= self.speedup_scale
        self.alien_speed *= self.speedup_scale
        self.alien_points = int(self.alien_points * self.score_scale)


import os
import argparse
import importlib
from glob import glob
import json

import ml.utils as utils

model_names = ['basic_kmer', 'basic_vector', 'basic_onehot', 'cnn_qrcode', 'rnn', 'lstm_vae_ordinal', 'lstm_vae_kmer_4', 'lstm_vae_kmer_5',
               'hybrid_vae_ordinal', 'hybrid_vae_kmer_4',
               'hybrid_vae_kmer_5']

def parse_args():
    parser = argparse.ArgumentParser(description='Taxonomic Classification')

    parser.add_argument('--model-name', type=str,
                        help=('Name of the model to train. Possible values ' +
                              'are {}'.format(','.join(model_names))),
                        choices=set(model_names))

    parser.add_argument('--is-demo', action='store_true',
                        help='If True, train script is run in demo mode with limited number of epochs (typically 1 or 2)')

    return parser.parse_args()

def get_interface_module(model_name):
    for fpath_interface in glob('ml/**/interface.py', recursive=True):
        module_name = fpath_interface.replace('/', '.').replace('.py', '')
        interface_module = importlib.import_module(module_name)

        if model_name in interface_module.RUN_OPTIONS:
            return interface_module

    raise ValueError('Model not implemented')

def train():
    args = parse_args()

    if not args.model_name:
        raise ValueError(
            'Model name is mandatory and should be one of - {}'.format(','.join(model_names)))

    data_dirpaths = ['cnn', 'hierarchy', 'vae', 'kmer']

    for data_dirpath in data_dirpaths:
        dirpath = os.path.join('data', data_dirpath)
        if not os.path.exists(dirpath):
            raise ValueError(
                'Directory \'{}\' not found. Please run process_data.py before proceeding!'.format(dirpath))

    path_config = json.loads(open('config/paths.json', 'r').read())

    interface = get_interface_module(args.model_name)
    interface.train_model(path_config, args=args)

if __name__ == '__main__':
    train()


from django.shortcuts import render,HttpResponse,redirect
from APP01 import models
# Create your views here.

def index(request):
    all_objs=models.Book.objects.all()
    return  render(request,'index.html',{'all_objs':all_objs})

def add_book(request):
    if request.method=='GET':
        publish_obj=models.Publish.objects.all()
        author_obj=models.Author.objects.all()
        return render(request,'add_book.html',{'publish_obj':publish_obj,'author_obj':author_obj})
    else:
        # print(request.POST)
        # title=request.POST.get('book_title')
        # price=request.POST.get('book_price')
        # pubdate=request.POST.get('book_pub_date')
        # publish=request.POST.get('publish')
        # author=request.POST.getlist('author')
        # print(title,price,pubdate,publish,author)
        # new_book_obj=models.Book.objects.create(
        #     title=title,
        #     price=price,
        #     publish_id=publish,
        #     publishDate=pubdate,
        # )
        # new_book_obj.authors.add(*author)

        all_data=request.POST.dict()
        # print(all_data)
        authors=request.POST.getlist('author')
        del all_data['csrfmiddlewaretoken']
        del all_data['author']
        # print(all_data)
        new_book_obj=models.Book.objects.create(
            **all_data
        )
        new_book_obj.authors.add(*authors)
        return redirect('index1')

def edit_book(request,book_id):
    book_obj = models.Book.objects.filter(nid=book_id)
    if request.method=='GET':
        book_obj=book_obj.first()
        publish_obj=models.Publish.objects.all()
        author_obj=models.Author.objects.all()
        return render(request,'edit_book.html',{'book_obj':book_obj,'publish_obj':publish_obj,'author_obj':author_obj})
    else:
        all_data = request.POST.dict()
        authors=request.POST.getlist('author')
        del all_data['csrfmiddlewaretoken']
        del all_data['author']
        book_obj.update(**all_data)
        book_obj.first().authors.set(authors)
        return redirect('index1')
def del_book(request,book_id):
    models.Book.objects.filter(nid=book_id).delete()
    return redirect('index1')


import os
# import pickle
# import random
import shutil
import numpy as np
# import keras
import pandas as pd
# import csv

from PIL import Image

# def make_only_two_classes():
#         File1 = 'train.csv'
#         File2 = 'train_class.csv'
#
#         with open(File1, "r") as r, open(File2, "w") as w:
#             reader = csv.reader(r, lineterminator = "\n")
#             writer = csv.writer(w, lineterminator = "\n")
#             for row in reader:
#                 if(str(row[1])=='0'):
#                     line = row[0]+","+row[1]+"\n"
#                 else:
#                     line = row[0]+"," + "1"+"\n"
#                 print(row,line)
#                 w.write(line)
#
# def baseline_model(input_shape):
#     model = keras.models.Sequential()
#     model.add(keras.layers.BatchNormalization(input_shape=input_shape))
#     model.add(keras.layers.Conv2D(32, (3, 3), padding='same', activation='relu'))
#     model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
#     model.add(keras.layers.Dropout(0.25))
#
#     # model.add(keras.layers.BatchNormalization())
#     model.add(keras.layers.Conv2D(32, (3, 3), padding='same', activation='relu'))
#     model.add(keras.layers.MaxPooling2D(pool_size=(2, 2)))
#     model.add(keras.layers.Dropout(0.25))
#
#     model.add(keras.layers.Flatten())
#     model.add(keras.layers.Dense(32, activation='relu'))
#     model.add(keras.layers.Dropout(0.5))
#     model.add(keras.layers.Dense(2, activation='softmax'))
#
#     model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
#     model.summary()
#
#
#     return model

# model = baseline_model((512,512,1))

class_dr = "C:/Users/urani/Documents/Diabetic Retinopathy/DR_images/"
class_non_dr = "C:/Users/urani/Documents/Diabetic Retinopathy/Non_DR_images/"
src = "C:/Users/urani/Documents/Diabetic Retinopathy/normalized_images/"
df = pd.read_csv('train_class.csv')
print(df)

i = 1
data = []
labels = []
for root, dirs, files in os.walk('C:/Users/urani/Documents/Diabetic Retinopathy/normalized_images'):
    for file in files:
        if file.endswith('.png'):
            s = file.split('.')[0]
            s1 = s.replace("normalized_", "")
            dfrow = df[df['id_code'] == s1].index.values.astype(int)[0]
            label = df.iloc[dfrow]['diagnosis']
            labels.append(label)
            imgpath = os.path.join(root, file)
            img = Image.open(imgpath)
            print(imgpath)
            data.append(np.array(img.getdata()).reshape((512,512,1)))
            print(len(data))
            i = i + 1
            src_img = src + s + ".png"
            if str(label) == "0":
                shutil.copy(src_img, class_non_dr)
            elif str(label) == "1":
                shutil.copy(src_img, class_dr)

# print(len(data))
#
# random.shuffle(data)
#
# for s in data:
#     print(s[1])
#
# X = []
# y = []
# for im, la in data:
#     X.append(im)
#     y.append(la)
#
# X = np.array(X).reshape(-1, 512, 512, 1)
#
# y = np.array(y)
# p_out = open("X.pickle", "wb")
# pickle.dump(X, p_out)
# p_out.close()
#
# p_out = open("y.pickle", "wb")
# pickle.dump(y, p_out)
# p_out.close()
#
# X = pickle.load(open("X.pickle", "rb"))
# y = pickle.load(open("y.pickle", "rb"))
#
# print(y)
#
# model.fit(X,y,validation_split=0.1,epochs=10)


# 263. Ugly Number
# Easy
# 2.8K
# 1.5K
# company
# Bloomberg
# company
# Facebook
# company
# Amazon
# An ugly number is a positive integer whose prime factors are limited to 2, 3, and 5.

# Given an integer n, return true if n is an ugly number.

# Example 1:

# Input: n = 6
# Output: true
# Explanation: 6 = 2 × 3
# Example 2:

# Input: n = 1
# Output: true
# Explanation: 1 has no prime factors, therefore all of its prime factors are limited to 2, 3, and 5.
# Example 3:

# Input: n = 14
# Output: false
# Explanation: 14 is not ugly since it includes the prime factor 7.

# Constraints:

# -231 <= n <= 231 - 1

class Solution:

    def isUgly(self, n: int) -> bool:

        if n <= 0:
            return False

        validFactors = [2, 3, 5]

        for f in validFactors:
            while n % f == 0:
                n = n / f

        if n == 1:
            return True
        else:
            return False

        # if n < 1:
        #     return False
        # if n == 1:
        #     return True

        # def isPrime(num: int):
        #     i = 2

        #     while i < num:
        #         if num % i == 0:
        #             return False
        #         i += 1

        #     return True

        # validPrime = [2, 3, 5]

        # if isPrime(n) and not n in validPrime:
        #     return False

        # j = 2

        # while j <= (n // j):
        #     if n % j == 0:
        #         k = n // j
        #         if isPrime(j) and not j in validPrime or isPrime(k) and not k in validPrime:
        #             return False
        #     j += 1

        # return True


#Import all necesaries libraries
from selenium import webdriver
import pandas as pd
import time

#Create an instance of webdriver to scrap.
#It uses an chrome driver located on root path.
driver = webdriver.Chrome(executable_path = r'chromedriver.exe')

def get_links():

    #Get url to scrap all the movies.
    url_pelis = "https://www.starz.com/ar/es/view-all/blocks/1523534"

    #Set url to the drive. Wait 8 seconds to load all the data from url.
    driver.get(url_pelis)
    time.sleep(8)

    #Create an empty list to get all links in html variable.
    lista_links = []

    #Iterate throught xpath looking for href attribute.
    for link in driver.find_elements_by_xpath('.//a'):
        lista_links.append(link.get_attribute('href'))

    #Filter none objects.
    lista_links = list(filter(None, lista_links))

    #Movies links have '/movies/' string on their urls.
    #Create a varible to filter later.
    movies_match = '/movies/'

    #List comprehension to filter by the previous string.
    #Convert to set to remove duplicates.
    #Then, convert to list again to iterate.
    #Now, we have the links of all movies.
    return list(set([link for link in lista_links if movies_match in link]))

def get_metadata(useful_links):
    #Empty list to save the collection of dictionaries.
    #Dictionary to save metada from movies and append it on the list.
    lista_peliculas = []

    #Iterate throught movies link.
    #Get data from url and wait 5 second to get all data from the url.
    #Get data from xpath and save it.
    #Then, create a dictionary to store metadata and append it on the list.
    # It might crash if the driver doesn't have enough time to retrive the metadata.
    # If that happends, try to execute this loop again.
    for i in useful_links:
        print(i)
        driver.get(i)
        time.sleep(5)
        titulo = driver.find_element_by_xpath('.//*[@id="moviesDetailsH1"]').text[4:]
        clasificacion = driver.find_element_by_xpath('.//*[@id="subview-container"]/starz-movie-details/div/div/section/div[1]/div[2]/section/ul/li[1]').text
        duracion = driver.find_element_by_xpath('.//*[@id="subview-container"]/starz-movie-details/div/div/section/div[1]/div[2]/section/ul/li[2]').text
        genero = driver.find_element_by_xpath('//*[@id="subview-container"]/starz-movie-details/div/div/section/div[1]/div[2]/section/ul/li[3]').text
        anio = driver.find_element_by_xpath('.//*[@id="subview-container"]/starz-movie-details/div/div/section/div[1]/div[2]/section/ul/li[4]').text
        tipo_de_audio = driver.find_element_by_xpath('.//*[@id="subview-container"]/starz-movie-details/div/div/section/div[1]/div[2]/section/ul/li[5]').text
        sinopsis = driver.find_element_by_xpath('.//*[@id="subview-container"]/starz-movie-details/div/div/section/div[1]/div[2]/div[1]/p[1]').text
        diccionario_pelicula = {
            'titulo':titulo,
            'clasificacion':clasificacion,
            'duracion':duracion,
            'genero':genero,
            'año':anio,
            'tipo_de_audio':tipo_de_audio,
            'sinopsis':sinopsis
        }
        lista_peliculas.append(diccionario_pelicula)
    print('******FIN******')

    #Transform list to pandas dataframe.
    df = pd.DataFrame(lista_peliculas)

    #Save it to csv file
    df.to_csv('datasets/peliculas.csv')

    #Save it to json file
    df.to_json('datasets/peliculas.json')

lista_de_peliculas = get_links()
get_metadata(lista_de_peliculas)
driver.quit()


"""
Monte Carlo Functions
JJGC, September 2016
"""
import pandas as pd

def get_mctrks(mctrk, event_number=0):
    """
    Return all the mc trks in an event
    """
    mcparticle = {}
    mc_name = []
    mc_pdg = []
    mc_vxi = []
    mc_vxf = []
    mc_nhits = []
    mc_energy = []
    for row in mctrk.iterrows():
        if row['hit_indx'] == 0 and row['event_indx'] == event_number:
            mc_name.append(row['particle_name'])
            mc_pdg.append(row['pdg_code'])
            mc_vxi.append(row['initial_vertex'])
            mc_vxf.append(row['final_vertex'])
            mc_nhits.append(row['nof_hits'])
            mc_energy.append(row['energy'])

    mcparticle['name'] = mc_name
    mcparticle['pdg'] = mc_pdg
    mcparticle['vxi'] = mc_vxi
    mcparticle['vxf'] = mc_vxf
    mcparticle['nhits'] = mc_nhits
    mcparticle['energy'] = mc_energy

    return pd.DataFrame(mcparticle)

def get_mchits(mctrk, event_number=0, particle_number=0):
    """
    Return the mc hits of a mc particle in an event
    Takes the pointer to the table (not to the mctrk data frame)
    """
    mchits = {}
    hit_x = []
    hit_y = []
    hit_z = []
    hit_time = []
    hit_energy = []

    for row in mctrk.iterrows():
        if (row['mctrk_indx'] == particle_number and
           row['event_indx'] == event_number):
            hit_x.append(row['hit_position'][0])
            hit_y.append(row['hit_position'][1])
            hit_z.append(row['hit_position'][2])
            hit_time.append(row['hit_time'])
            hit_energy.append(row['hit_energy'])

    mchits['x'] = hit_x
    mchits['y'] = hit_y
    mchits['z'] = hit_z
    mchits['time'] = hit_time
    mchits['energy'] = hit_energy

    return pd.DataFrame(mchits)


#-*-coding:utf-8-*-
from django.urls import path
from . import views
from . views import ForCodeView

app_name = 'users'
urlpatterns = [
    path('myenter/',views.userEnter,name='userEnter'),
    path('agreement/',views.agreement,name='agreement'),
    path('forcode/',ForCodeView.as_view(),name='forcode'),
    path('login/',views.Login.as_view(),name='login'),
    path('logout/',views.logOut,name='logout'),
]


from vidgear.gears import VideoGear
from vidgear.gears import NetGear
from vidgear.gears import ScreenGear
from vidgear.gears.asyncio import WebGear
from vidgear.gears.asyncio.helper import reducer
import cv2
import uvicorn
import asyncio

options = {
    'copy': False,
    'track': False
}

options_webgear = {
    'frame_size_reduction': 1,
    'frame_jpeg_quality': 100,
    'frame_jpeg_optimize': True,
    'frame_jpeg_progressive': False
}

client = NetGear(
    # address='192.168.11.145', # school network
    address='192.168.11.137', # home network
    port='5454',
    pattern=2,
    receive_mode=True,
    logging=True,
    protocol='tcp',
    **options
)

web = WebGear(logging=True, **options_webgear)

async def frame_producer():
    stream = ScreenGear().start()
    while True:
        frame = client.recv()

        if frame is None:
            break

        # frame = await reducer(frame=frame, percentage=30)
        encoded_image = cv2.imencode(ext='.jpg', img=frame)[1].tobytes()

        yield (b"--frame\r\nContent-Type:video/jpeg2000\r\n\r\n" + encoded_image + b"\r\n")
        await asyncio.sleep(0.00001)

    stream.stop()

web.config['generator'] = frame_producer
uvicorn.run(app=web(), host='localhost', port=8000)

web.shutdown()

cv2.destroyAllWindows()


import requests
from bs4 import BeautifulSoup as bs4

headers_p = {"User-Agent":"{your-user-agent}"}

site_content = requests.get('https://pythonwebscraping.blogspot.com/', headers=headers_p)

site = site_content.content
soup = bs4(site, "html.parser")

# post title
titles = soup.find_all("h3", {"class":"post-title entry-title"})
for data in titles:
    print(data.text.strip())

# post image url
image_urls = soup.find_all("div", {"class":"separator"})
for data in image_urls:
    print(data.a['href'])

# post date
dates = soup.find_all("a", {"class":"timestamp-link"})
for data in dates:
    print(data.abbr.text)

# post comment
comments = soup.find_all("a", {"class":"comment-link"})
for data in comments:
    print(data.text.strip()+" "+data['href'])


import copy
from random import sample
from typing import List

class Sudoku:
    """A Sudoku puzzle

    https://en.wikipedia.org/wiki/Sudoku
    """
    BLANK_CELL = 0
    GRID_SIZE = 9
    SUBGRID_SIZE = GRID_SIZE // 3
    GRID_AREA = GRID_SIZE * GRID_SIZE

    def __init__(self, grid: List[List[int]] = None) -> None:
        """Initialize the Sudoku

        Generates our own Sudoku puzzle if grid is not provided
        """
        if not grid:
            grid = self.__generate_grid()

        self.__init_checkers()
        self.grid = copy.deepcopy(grid)
        self.original_grid = copy.deepcopy(grid)

    def __init_checkers(self) -> None:
        """Initializes all *_checker, which are lists of sets

        The *_checker is used to ensure that the same digit is not present in the current row, current column, and current 3x3 subgrid
        """
        self.row_checker = [set() for _ in range(self.GRID_SIZE)]
        self.col_checker = [set() for _ in range(self.GRID_SIZE)]
        self.subgrid_checker = [[set() for _ in range(self.SUBGRID_SIZE)] for _ in range(self.SUBGRID_SIZE)]

    def __fill_checkers(self) -> None:
        """Fill up the *_checkers"""
        for i in range(self.GRID_SIZE):
            for j in range(self.GRID_SIZE):
                digit = self.grid[i][j]
                if digit == self.BLANK_CELL:
                    continue

                if any([
                    digit in self.row_checker[i],
                    digit in self.col_checker[j],
                    digit in self.subgrid_checker[i // self.SUBGRID_SIZE][j // self.SUBGRID_SIZE],
                ]):
                    raise ValueError("Invalid grid")

                self.row_checker[i].add(digit)
                self.col_checker[j].add(digit)
                self.subgrid_checker[i // self.SUBGRID_SIZE][j // self.SUBGRID_SIZE].add(digit)

    def __generate_grid(self) -> List[List[int]]:
        """Randomly generates a 9x9 valid Sudoku grid/board puzzle

        Note that the Sudoku board generated may have > 1 solution
        Does not modify the current state of the Sudoku board

        https://stackoverflow.com/a/56581709/10067850
        """
        def pattern(r, c):
            return (self.SUBGRID_SIZE * (r % self.SUBGRID_SIZE) + r // self.SUBGRID_SIZE + c) % self.GRID_SIZE

        def shuffle(s):
            return sample(s, len(s))

        rows = [
            g * self.SUBGRID_SIZE + r for g in shuffle(range(self.SUBGRID_SIZE))
            for r in shuffle(range(self.SUBGRID_SIZE))
        ]

        cols = [
            g * self.SUBGRID_SIZE + c for g in shuffle(range(self.SUBGRID_SIZE))
            for c in shuffle(range(self.SUBGRID_SIZE))
        ]

        digits = shuffle(range(1, self.SUBGRID_SIZE * self.SUBGRID_SIZE + 1))

        board = [[digits[pattern(r, c)] for c in cols] for r in rows]

        empties = self.GRID_AREA * self.SUBGRID_SIZE // 4

        for p in sample(range(self.GRID_AREA), empties):
            board[p // self.GRID_SIZE][p % self.GRID_SIZE] = self.BLANK_CELL

        return board

    def display_grid(self) -> None:
        """Displays the current state of the Sudoku grid to stdout"""
        print("-" * self.GRID_SIZE * 3)
        for i in range(self.GRID_SIZE):
            print(self.grid[i])

    def generate_grid(self) -> None:
        """Overrides the current grid with a brand new valid Sudoku grid"""
        new_grid = self.__generate_grid()

        self.__init_checkers()
        self.grid = copy.deepcopy(new_grid)
        self.original_grid = copy.deepcopy(new_grid)

    def reset_grid(self) -> None:
        """Resets the current grid to its original state"""
        self.__init_checkers()
        self.grid = copy.deepcopy(self.original_grid)

    def solve_grid(self) -> List[List[int]]:
        """Solves a valid Sudoku grid"""
        self.__fill_checkers()

        for i in range(self.GRID_SIZE):
            for j in range(self.GRID_SIZE):
                if self.grid[i][j] == self.BLANK_CELL and self._backtrack(i, j):
                    return self.grid

        raise ValueError("Unable to solve Sudoku puzzle, are you sure that the puzzle is valid?")

    def _backtrack(self, i: int, j: int) -> bool:
        """A backtracking helper function to help solve the Sudoku puzzle"""
        if self.grid[i][j] != self.BLANK_CELL:
            j += 1
            if j == self.GRID_SIZE:
                i += 1
                j = 0

            if i == self.GRID_SIZE:
                return True

            return self._backtrack(i, j)

        for digit in range(1, self.GRID_SIZE + 1):
            if all([
                digit not in self.row_checker[i],
                digit not in self.col_checker[j],
                digit not in self.subgrid_checker[i // self.SUBGRID_SIZE][j // self.SUBGRID_SIZE],
            ]):
                self.grid[i][j] = digit
                self.row_checker[i].add(digit)
                self.col_checker[j].add(digit)
                self.subgrid_checker[i // self.SUBGRID_SIZE][j // self.SUBGRID_SIZE].add(digit)

                if self._backtrack(i, j):
                    return True

                self.grid[i][j] = self.BLANK_CELL
                self.row_checker[i].remove(digit)
                self.col_checker[j].remove(digit)
                self.subgrid_checker[i // self.SUBGRID_SIZE][j // self.SUBGRID_SIZE].remove(digit)

        return False

if __name__ == "__main__":
    sudoku = Sudoku()
    sudoku.display_grid()
    sudoku.solve_grid()
    sudoku.display_grid()