import numpy as np
import os,re
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Input
import time
import matplotlib.pyplot as plt
from tensorflow.keras.callbacks import ReduceLROnPlateau,EarlyStopping,ModelCheckpoint
import horovod.tensorflow.keras as hvd
import horovod.tensorflow as hvd_tf
import preprocessing
import argparse
import tensorflow.keras.backend as K
import pickle

import tensorflow_addons as tfa

import tensorflow_probability as tfp
tfb = tfp.bijectors
tfd = tfp.distributions
import fjord_regularization
tf.random.set_seed(1233)

class TubeletEmbedding(layers.Layer):
    def __init__(self, embed_dim, patch_size,is_1D=False, **kwargs):
        super().__init__(**kwargs)
        if is_1D:
            self.projection = layers.Conv1D(
                filters=embed_dim,
                kernel_size=patch_size,
                strides=patch_size,
                padding="VALID",
                use_bias=False,
            )
        else:
            self.projection = layers.Conv3D(
                filters=embed_dim,
                kernel_size=patch_size,
                strides=patch_size,
                padding="VALID",
                use_bias=False,
            )
        self.flatten = layers.Reshape(target_shape=(-1, embed_dim))

    def call(self, videos):
        projected_patches = self.projection(videos)
        flattened_patches = self.flatten(projected_patches)
        return flattened_patches

class PositionalEncoder(layers.Layer):
    def __init__(self, embed_dim, **kwargs):
        super().__init__(**kwargs)
        self.embed_dim = embed_dim

    def build(self, input_shape):
        _, num_tokens, _ = input_shape
        self.position_embedding = layers.Embedding(
            input_dim=num_tokens, output_dim=self.embed_dim
        )
        self.positions = tf.range(start=0, limit=num_tokens, delta=1)

    def call(self, encoded_tokens):
        # Encode the positions and add it to the encoded tokens
        encoded_positions = self.position_embedding(self.positions)
        encoded_tokens = encoded_tokens + encoded_positions
        return encoded_tokens

class BACKBONE_ODE(keras.Model):
    """ODE backbone network implementation"""
    def __init__(self, data_shape,num_cond,config,name='mlp_ode',
                 use_1D=False,use_dense=False):
        super(BACKBONE_ODE, self).__init__()
        if config is None:
            raise ValueError("Config file not given")
        self._num_cond = num_cond
        self._data_shape = data_shape
        self._data_shape_flat = [np.prod(data_shape)]

        self.config = config
        #config file with ML parameters to be used during training
        self.activation = self.config['ACT']
        self.use_1D=use_1D

        #transformation applied to conditional inputs
        inputs_time = Input((1))
        inputs_cond = Input((self._num_cond))

        if self.config['USE_MLP_COND']:
            conditional = layers.Dense(16,activation=self.activation)(inputs_cond)
            conditional = tf.concat([inputs_time,conditional],-1)
        else:
            conditional = tf.concat([inputs_time,inputs_cond],-1)

        if use_dense:
            inputs,outputs = self.DenseModel(conditional)
        else:
            inputs,outputs = self.ViTModel(conditional)

        self._model = keras.Model(inputs=[inputs,inputs_time,inputs_cond],outputs=outputs)

    def call(self, t, data,conditional):
        t_reshape = t*tf.ones_like(conditional[:,:1],dtype=tf.float32)
        return self._model([data,t_reshape,conditional])

    def DenseModel(self,time_embed):
        inputs = Input((self._data_shape))
        inputs_reshape = tf.expand_dims(inputs,-1)
        nlayers =self.config['NDENSE']
        ndim=self._data_shape[0]
        nhidden = self.config['HIDDEN_MULTIPLIER']*ndim

        layer_encoded = self.time_dense(inputs,time_embed,nhidden)
        for ilayer in range(1,nlayers-1):
            layer_encoded=self.time_dense(layer_encoded,time_embed,nhidden)

        #outputs=self.time_dense(layer_encoded,time_embed,ndim,activation=False)
        outputs = layers.Dense(ndim,activation=None)(layer_encoded)
        #outputs = layers.Flatten()(layer_encoded)
        return inputs, outputs

    def ViTModel(self,time_embed):
        ''' Visual transformer model as the network backbone for the FFJORD implementation'''
        inputs = Input((self._data_shape_flat))

        if self.use_1D:
            time_layer = tf.reshape(time_embed,(-1,1,time_embed.shape[-1]))
        else:
            time_layer = tf.reshape(time_embed,(-1,1,1,1,time_embed.shape[-1]))

        time_layer = tf.tile(time_layer,[1]+self._data_shape)
        transformer_layers = self.config['NLAYERS']
        num_heads=self.config['NHEADS']
        projection_dim = self.config['PROJECTION_SIZE']
        mlp_dim = self.config['MLP_SIZE']

        inputs_reshape = tf.concat([tf.reshape(inputs,[-1]+self._data_shape),time_layer],-1)
        patches = TubeletEmbedding(embed_dim=projection_dim,is_1D=self.use_1D,
                                   patch_size=self.config['STRIDE'])(inputs_reshape)

        # Encode patches.
        encoded_patches = PositionalEncoder(embed_dim=projection_dim)(patches)

        for _ in range(transformer_layers):
            # Layer normalization 1.
            x1 = layers.LayerNormalization(epsilon=1e-6)(encoded_patches)

            # Create a multi-head attention layer.
            attention_output = layers.MultiHeadAttention(
                num_heads=num_heads, key_dim=projection_dim)(x1, x1)
            # Skip connection 1.
            x2 = layers.Add()([attention_output, encoded_patches])

            # Layer normalization 2.
            x3 = layers.LayerNormalization(epsilon=1e-6)(x2)
            #x3=x2
            # MLP.
            #tf.nn.gelu

            # x3 = self.time_dense(x3,time_patch,2*projection_dim)
            # x3 = self.time_dense(x3,time_patch,projection_dim)

            x3 = layers.Dense(2*projection_dim,activation=self.activation)(x3)
            x3 = layers.Dense(projection_dim,activation=self.activation)(x3)

            # Skip connection 2.
            encoded_patches = layers.Add()([x3, x2])

        representation = layers.LayerNormalization(epsilon=1e-6)(encoded_patches)
        pooling = layers.GlobalAvgPool1D()(representation)
        representation = layers.Flatten()(representation)
        representation = self.time_dense(tf.concat([representation,pooling],-1),time_embed,mlp_dim)
        #representation = layers.Dropout(0.1)(representation)
        representation = self.time_dense(representation,time_embed,mlp_dim//2)
        outputs = layers.Dense(self._data_shape_flat[0])(representation)
        return inputs, outputs

    def time_dense(self,input_layer,embed,hidden_size,activation=True):
        #Incorporate the time information to each layer used in the model
        layer = tf.concat([input_layer,embed],-1)
        layer = layers.Dense(hidden_size,activation=None)(layer)

        #layer = layers.LayerNormalization(epsilon=1e-6)(layer)
        # layer = layers.Dropout(0.1)(layer)
        if activation:
            return self.activate(layer)
        else:
            return layer

    def activate(self,layer):
        if self.activation == 'leaky_relu':
            return keras.layers.LeakyReLU(-0.01)(layer)
        elif self.activation == 'tanh':
            return keras.activations.tanh(layer)
        elif self.activation == 'softplus':
            return keras.activations.softplus(layer)
        elif self.activation == 'relu':
            return keras.activations.relu(layer)
        elif self.activation == 'swish':
            return keras.activations.swish(layer)
        else:
            raise ValueError("Activation function not supported!")

def make_bijector_kwargs(bijector, name_to_kwargs):
    #Hack to pass the conditional information through all the bijector layers
    if hasattr(bijector, 'bijectors'):
        return {b.name: make_bijector_kwargs(b, name_to_kwargs) for b in bijector.bijectors}
    else:
        for name_regex, kwargs in name_to_kwargs.items():
            if re.match(name_regex, bijector.name):
                return kwargs
    return {}

def save_model(model,name="ffjord",checkpoint_dir = '../checkpoints'):
    if not os.path.exists(checkpoint_dir):
        os.makedirs(checkpoint_dir)
    model.save_weights('{}/{}'.format(checkpoint_dir,name),save_format='tf')
    np.save('optimizer.npy', model.optimizer.get_weights())

def load_model(model,name="ffjord",checkpoint_dir = '../checkpoints'):
    model.load_weights('{}/{}'.format(checkpoint_dir,name)).expect_partial()

class FFJORD(keras.Model):
    def __init__(self, stacked_layers,stacked_energies,
                 num_output,num_energy,
                 config,trace_type='hutchinson',
                 is_training=True,mask=None,name='FFJORD'):

        super(FFJORD, self).__init__()

        if config is None:
            raise ValueError("Config file not given")

        self.config = config

        ode_solve_fn = tfp.math.ode.DormandPrince(
            atol=1e-3,
            # rtol=1e-3,
            # first_step_size=0.2
        ).solve

        ode_solve_fn_energy = tfp.math.ode.DormandPrince(
            atol=1e-4,
            rtol=1e-4,
            #rtol=1e-5,
            #first_step_size=0.2
        ).solve

        #Gaussian noise to trace solver
        if trace_type=='hutchinson':
            if is_training:
                trace_augmentation_fn = fjord_regularization.trace_jacobian_hutchinson
            else:
                trace_augmentation_fn = tfb.ffjord.trace_jacobian_hutchinson
        elif trace_type == 'exact':
            trace_augmentation_fn = tfb.ffjord.trace_jacobian_exact #Regularization code only
        else:
            raise Exception("Invalid trace estimator")

        self.bijectors = []
        self.bijectors_energy = []

        for ilayer,layer in enumerate(stacked_layers):
            if is_training:
                ffjord = fjord_regularization.FFJORD(
                    state_time_derivative_fn=layer, #Each of the models we build
                    ode_solve_fn=ode_solve_fn,
                    is_training=is_training,
                    trace_augmentation_fn=trace_augmentation_fn,
                    name='bijector{}'.format(ilayer), #Bijectors need to be named to receive conditional inputs
                    jacobian_factor = self.config['JAC'], #Regularization strength
                    kinetic_factor = self.config['REG'],
                    b = self.config['b'],
                )
            else:

                ffjord = tfb.FFJORD(
                    state_time_derivative_fn=layer, #Each of the  models we build
                    ode_solve_fn=ode_solve_fn,
                    trace_augmentation_fn=tfb.ffjord.trace_jacobian_exact,
                    #trace_augmentation_fn=trace_augmentation_fn,
                    name='bijector{}'.format(ilayer),
            )

            self.bijectors.append(ffjord)

        for ilayer,layer in enumerate(stacked_energies):
            if is_training:
                ffjord = fjord_regularization.FFJORD(
                    state_time_derivative_fn=layer, #Each of the models we build
                    ode_solve_fn=ode_solve_fn_energy,
                    is_training=is_training,
                    trace_augmentation_fn=trace_augmentation_fn,
                    name='energy{}'.format(ilayer), #Bijectors need to be named to receive conditional inputs
                    jacobian_factor = self.config['JAC'], #Regularization strength
                    kinetic_factor = self.config['REG'],
                    b = self.config['b'],
                )
            else:

                ffjord = tfb.FFJORD(
                    state_time_derivative_fn=layer, #Each of the  models we build
                    ode_solve_fn=ode_solve_fn_energy,
                    trace_augmentation_fn=trace_augmentation_fn,
                    #trace_augmentation_fn=tfb.ffjord.trace_jacobian_exact,
                    name='energy{}'.format(ilayer),
                )

            self.bijectors_energy.append(ffjord)

        #Reverse the bijector order
        self.chain = tfb.Chain(list(reversed(self.bijectors)))
        self.chain_energy = tfb.Chain(list(reversed(self.bijectors_energy)))

        self.loss_tracker = keras.metrics.Mean(name="loss")

        #Determine the base distribution
        self.base_distribution = tfp.distributions.MultivariateNormalDiag(
            loc=np.zeros(num_output,dtype=np.float32),
            scale_diag=np.ones(num_output,dtype=np.float32)
        )

        self.base_distribution_energy = tfp.distributions.MultivariateNormalDiag(
            loc=np.zeros(num_energy,dtype=np.float32),
            scale_diag=np.ones(num_energy,dtype=np.float32)
        )

        self.flow=tfd.TransformedDistribution(distribution=self.base_distribution, bijector=self.chain)
        self.flow_energy=tfd.TransformedDistribution(distribution=self.base_distribution_energy,
                                                     bijector=self.chain_energy)
    @property
    def metrics(self):
        """List of the model's metrics.
        We make sure the loss tracker is listed as part of `model.metrics`
        so that `fit()` and `evaluate()` are able to `reset()` the loss tracker
        at the start of each epoch and at the start of an `evaluate()` call.
        """
        return [self.loss_tracker]

    def compile(self,e_optimizer, v_optimizer):
        super(FFJORD, self).compile(experimental_run_tf_function=False)
        self.e_optimizer = e_optimizer
        self.v_optimizer = v_optimizer

    # def call(self, inputs, conditional=None):
    #     kwargs = make_bijector_kwargs(self.flow.bijector,{'bijector.': {'conditional':conditional }})
    #     return self.flow.bijector.forward(inputs,**kwargs)

    def generate(self,conditional,nsplit=10):
        voxel = []
        energy = []

        for cond_split in np.array_split(np.array(conditional), nsplit):
            bijector_kwargs_energy = make_bijector_kwargs(
                self.flow_energy.bijector, {'energy.': {'conditional': cond_split}})
            energies = self.flow_energy.sample(cond_split.shape[0],bijector_kwargs= bijector_kwargs_energy)
            energy.append(energies.numpy())
            bijector_kwargs = make_bijector_kwargs(
                self.flow.bijector, {'bijector.': {'conditional': tf.concat([energies,cond_split],-1)}})
            voxels = self.flow.sample(cond_split.shape[0],bijector_kwargs= bijector_kwargs)
            voxel.append(voxels.numpy())
        voxel = np.concatenate(voxel,0)
        energy = np.concatenate(energy,0)
        return voxel,energy

    def log_loss(self,voxel,energies,conditional):
        bijector_kwargs = make_bijector_kwargs(
            self.flow.bijector, {'bijector.': {'conditional': tf.concat([energies,conditional],-1)}})

        #loss = loss_energy
        loss = -tf.reduce_mean(self.flow.log_prob(voxel,bijector_kwargs= bijector_kwargs))

        return loss

    def log_loss_energy(self,energies,conditional):
        bijector_kwargs_energy = make_bijector_kwargs(
            self.flow_energy.bijector, {'energy.': {'conditional': conditional}})

        loss_energy = -tf.reduce_mean(self.flow_energy.log_prob(energies,
                                                                bijector_kwargs= bijector_kwargs_energy))

        return loss_energy

    @tf.function()
    def train_step(self, inputs):
        data,energy,cond = inputs
        with tf.GradientTape() as tape:
            loss_voxel = self.log_loss(data,energy,cond)

        v = tape.gradient(loss_voxel, self.flow.trainable_variables)
        v = [tf.clip_by_norm(grad, 1) for grad in v]
        self.v_optimizer.apply_gradients(zip(v, self.flow.trainable_variables))
        self.loss_tracker.update_state(loss_voxel)

        with tf.GradientTape() as tape:
            loss_energy = self.log_loss_energy(energy,cond)

        e = tape.gradient(loss_energy, self.flow_energy.trainable_variables)
        self.e_optimizer.apply_gradients(zip(e, self.flow_energy.trainable_variables))

        return {"loss": self.loss_tracker.result(),'energy':loss_energy}

    @tf.function
    def test_step(self, inputs):
        data,energy,cond = inputs
        loss_voxel=self.log_loss(data,energy,cond)
        loss_energy = self.log_loss_energy(energy,cond)
        loss = loss_voxel+loss_energy
        self.loss_tracker.update_state(loss)
        return {"loss": self.loss_tracker.result()}

if __name__ == '__main__':
    #Start horovod and pin each GPU to a different process
    hvd.init()
    gpus = tf.config.experimental.list_physical_devices('GPU')
    for gpu in gpus:
        tf.config.experimental.set_memory_growth(gpu, True)
    if gpus:
        tf.config.experimental.set_visible_devices(gpus[hvd.local_rank()], 'GPU')

    parser = argparse.ArgumentParser()

    parser.add_argument('--model', default='calogan', help='Name of the model to train. Options are: mnist, challenge, calogan')
    parser.add_argument('--nevts', default=-1, help='Name of the model to train. Options are: mnist, moon, calorimeter')
    parser.add_argument('--load', action='store_true', default=False,help='Load pretrained weights to continue the training')
    flags = parser.parse_args()

    model_name = flags.model #Let's try the parallel training using different models and different network architectures. Possible options are [mnist,moon,calorimeter]

    if model_name == 'mnist':
        from tensorflow.keras.datasets import mnist, fashion_mnist
        dataset_config = preprocessing.LoadJson('config_mnist.json')
        (X_train, y_train), (X_test, y_test) = mnist.load_data()
        ntrain,samples_train =preprocessing.MNIST_prep(X_train, y_train)
        ntest,samples_test =preprocessing.MNIST_prep(X_test, y_use)

        test_1D = False #Use convolutional networks for the model backbone
    elif model_name == 'calogan':
        dataset_config = preprocessing.LoadJson('config_calogan_vit.json')
        file_path=dataset_config['FILE']
        ntrain,ntest,samples_train,samples_test,mask = preprocessing.CaloGAN_prep(file_path,int(flags.nevts),use_logit=True)
        use_1D = True
    elif model_name == 'calochallenge':
        dataset_config = preprocessing.LoadJson('config_challenge_vit.json')
        file_path=dataset_config['FILE']
        ntrain,ntest,samples_train,samples_test,mask = preprocessing.CaloChallenge_prep(file_path,int(flags.nevts),use_logit=True)
        use_1D = False
    else:
        raise ValueError("Model not implemented!")

    LR = float(dataset_config['LR'])
    NUM_EPOCHS = dataset_config['MAXEPOCH']
    STACKED_FFJORDS = dataset_config['NSTACKED'] #Number of stacked transformations

    NUM_LAYERS = dataset_config['NLAYERS'] #Hiddden layers per bijector
    BATCH_SIZE = dataset_config['BATCH']
    NLAYER = dataset_config['NENERGY']

    #Stack of bijectors
    stacked_vit = []
    stacked_dense = []
    for istack in range(1):
        vit_model = BACKBONE_ODE(dataset_config['SHAPE'], NLAYER+1,
                                 config=dataset_config,use_1D=use_1D)
        stacked_vit.append(vit_model)

    for istack in range(STACKED_FFJORDS):
        dense_model = BACKBONE_ODE([NLAYER], 1, config=dataset_config,use_1D=True,
                                   name='mlp_energy',use_dense=True)
        stacked_dense.append(dense_model)

    #Create the model
    model = FFJORD(stacked_vit,stacked_dense,
                   np.prod(list(dataset_config['SHAPE'])),NLAYER,
                   mask=mask,
                   config=dataset_config)

    opt_v = tfa.optimizers.AdamW(learning_rate=LR,weight_decay=1e-2*LR)
    # opt_v = tf.optimizers.Adam(learning_rate=LR)
    opt_v = hvd.DistributedOptimizer(opt_v)

    opt_e = tfa.optimizers.AdamW(learning_rate=0.1*LR,weight_decay=1e-2*LR)
    # opt_e = tf.optimizers.SGD(learning_rate=LR)
    # opt_e = tf.optimizers.Adam(learning_rate=LR)
    opt_e = hvd.DistributedOptimizer(opt_e)

    # Horovod: add Horovod DistributedOptimizer.
    model.compile(v_optimizer=opt_v,e_optimizer=opt_e)

    if flags.load:
        load_model(model,checkpoint_dir='../checkpoint_{}'.format(dataset_config['MODEL']))

    callbacks = [
        hvd.callbacks.BroadcastGlobalVariablesCallback(0),
        hvd.callbacks.MetricAverageCallback(),
        ReduceLROnPlateau(patience=30, min_lr=1e-7,verbose=hvd.rank() == 0),
        EarlyStopping(patience=dataset_config['EARLYSTOP'],restore_best_weights=True),
        hvd.callbacks.LearningRateWarmupCallback(warmup_epochs=10, initial_lr=LR, verbose=1),
    ]

    if hvd.rank()==0:
        checkpoint = ModelCheckpoint('../checkpoint_{}/ffjord'.format(dataset_config['MODEL']),save_best_only=True,mode='auto',
                                                               period=1,save_weights_only=True)
        callbacks.append(checkpoint)
        # model(np.zeros((1,dataset_config['SHAPE'][0])),np.zeros((1,1)))
        # print(model.summary())

    history = model.fit(
        samples_train.batch(BATCH_SIZE),
        steps_per_epoch=int(ntrain/BATCH_SIZE),
        validation_data=samples_test.batch(BATCH_SIZE),
        validation_steps=max(1,int(ntest/BATCH_SIZE)),
        epochs=NUM_EPOCHS,
        verbose=hvd.rank() == 0,
        callbacks=callbacks,
    )

    if hvd.rank() == 0:
        save_model(model,checkpoint_dir='../checkpoint_{}'.format(dataset_config['MODEL']))


# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division

############################################################################
#
#   Copyright (C) 2008-2015
#    Christian Kohlöffel
#    Jean-Paul Schouwstra
#
#   This file is part of DXF2GCODE.
#
#   DXF2GCODE is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   DXF2GCODE is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with DXF2GCODE.  If not, see <http://www.gnu.org/licenses/>.
#
############################################################################

from __future__ import absolute_import

from copy import deepcopy, copy
import logging

from core.point import Point
from dxfimport.classes import ContourClass
from dxfimport.geoent_arc import GeoentArc
from dxfimport.geoent_circle import GeoentCircle
from dxfimport.geoent_insert import GeoentInsert
from dxfimport.geoent_line import GeoentLine
from dxfimport.geoent_polyline import GeoentPolyline
from dxfimport.geoent_spline import GeoentSpline
from dxfimport.geoent_ellipse import GeoentEllipse
from dxfimport.geoent_lwpolyline import GeoentLwPolyline
from dxfimport.geoent_point import GeoentPoint

import globals.globals as g

from globals.six import text_type
import globals.constants as c
if c.PYQT5notPYQT4:
    from PyQt5.QtWidgets import QMessageBox
    from PyQt5 import QtCore
else:
    from PyQt4.QtGui import QMessageBox
    from PyQt4 import QtCore

logger = logging.getLogger("DxfImport.Import")

class ReadDXF(QtCore.QObject):
    # Initialise the class
    def __init__(self, filename=None):
        QtCore.QObject.__init__(self)

        # Setting up logger
        # logger = g.logger.logger

        str_ = self.Read_File(filename)
        g.config.metric = self.Get_Unit(str_)

        self.update_tool_values()

        # Load the contour and store the values in the classes
        self.line_pairs = self.Get_Line_Pairs(str_)

        # Debug Informationen
        # logger.info(("\n\nFile has   %0.0f Lines" % len(str_)), 1)
        # logger.info(("\nFile has   %0.0f Linepairs" % self.line_pairs.nrs), 1)

        logger.info(self.tr("Reading DXF Structure"))
        sections_pos = self.Get_Sections_pos()
        self.layers = self.Read_Layers(sections_pos)

        blocks_pos = self.Get_Blocks_pos(sections_pos)
        self.blocks = self.Read_Blocks(blocks_pos)
        self.entities = self.Read_Entities(sections_pos)

        # Aufruf der Klasse um die Konturen zur suchen
        # Schleife f�r die Anzahl der Bl�cke und den Layern
        # Call the class to define the contours of search
        # Loop for the number of blocks and the layer
        for i in range(len(self.blocks.Entities)):
            # '\n'
            # print self.blocks.Entities[i]
            logger.info(self.tr("Creating Contours of Block Nr: %i") %i)
            self.blocks.Entities[i].cont = self.Get_Contour(self.blocks.Entities[i])

        logger.info(self.tr("Creating Contours of Entities"))
        self.entities.cont = self.Get_Contour(self.entities)

    def tr(self, string_to_translate):
        """
        Translate a string using the QCoreApplication translation framework
        @param: string_to_translate: a unicode string
        @return: the translated unicode string if it was possible to translate
        """
        return text_type(QtCore.QCoreApplication.translate('ReadDXF',
                                                           string_to_translate))

    def Read_File(self, filename):
        """
        Read_File() - Load the selected DXF files
        @param: filename: name of the file to load
        @return: file contents as a list of strings
        """
        file_ = open(filename, 'r')
        str_ = file_.readlines()
        file_.close()
        return str_

    def Get_Unit(self, str):
        """
        Get_Unit() - Get unit of measure English (Imperial) or Metric from DXF file
        """
        # Set drawing units: 0 = English; 1 = Metric
        # Metric will be treated as being in millimeters
        # English as inches

        metric = 1  # default: metric

        for line in range(len(str) - 2):
            if str[line].startswith("$MEASUREMENT"):
                metric = int(str[line + 2].strip())
                break

        # Default drawing units for AutoCAD DesignCenter blocks:
        # 0 = Unitless; 1 = Inches; 2 = Feet; 3 = Miles; 4 = Millimeters;
        # 5 = Centimeters; 6 = Meters; 7 = Kilometers; 8 = Microinches;
        # 9 = Mils (thous); 10 = Yards; 11 = Angstroms; 12 = Nanometers;
        # 13 = Microns; 14 = Decimeters; 15 = Decameters;
        # 16 = Hectometers; 17 = Gigameters; 18 = Astronomical units;
        # 19 = Light years; 20 = Parsecs

        for line in range(len(str) - 2):
            if str[line].startswith("$INSUNITS"):
                if int(str[line + 2].strip()) == 1:
                    metric = 0
                elif int(str[line + 2].strip()) == 4:
                    metric = 1
                break

        return metric

    def update_tool_values(self):
        # update the tool default values depending on the unit of the drawing
        if g.config.tool_units_metric != g.config.metric:
            scale = 1/25.4 if g.config.metric == 0 else 25.4
            for key in g.config.vars.Plane_Coordinates:
                g.config.vars.Plane_Coordinates[key] *= scale
            for key in g.config.vars.Depth_Coordinates:
                g.config.vars.Depth_Coordinates[key] *= scale
            for key in g.config.vars.Feed_Rates:
                g.config.vars.Feed_Rates[key] *= scale
            for tool in g.config.vars.Tool_Parameters:
                g.config.vars.Tool_Parameters[tool]['diameter'] *= scale
                g.config.vars.Tool_Parameters[tool]['start_radius'] *= scale
            g.config.tool_units_metric = g.config.metric

    # Convert the uploaded file into line pairs (code & Value).
    def Get_Line_Pairs(self, string):
        line = 0
        line_pairs = dxflinepairsClass([])

        # Start at the first SECTION
        while not string[line].startswith("SECTION"):
            line += 1
        line -= 1

        # Continue to the end if no error occurs. Otherwise abort with error
        try:
            while line + 1 < len(string):
                line_pairs.line_pair.append(dxflinepairClass(int(string[line].strip()), string[line + 1].strip()))
                line += 2

        except ValueError:
            message = self.tr('Reading stopped at line %i.\n "%s" is not a valid code (number) - please, check/correct dxf file')\
                      % (line + 1, string[line].strip())
            logger.warning(message)
            QMessageBox.warning(g.window, self.tr("Warning reading linepairs"), message)

        line_pairs.nrs = len(line_pairs.line_pair)
        logger.debug(self.tr('Did read %i of linepairs from DXF') % line_pairs.nrs)
        return line_pairs

    # Search the sections in the DXF file to recognize Blocke.
    def Get_Sections_pos(self):
        """
        Get_Sections_pos()
        """
        sections = []

        start = self.line_pairs.index_both(0, "SECTION", 0)

        # Wenn eine Gefunden wurde diese anh�ngen
        # If a has been found for this attach ???
        while start is not None:
            # Wenn eine Gefunden wurde diese anh�ngen
            # If a has been found for this attach ???
            sections.append(SectionClass(len(sections)))
            sections[-1].begin = start
            name_pos = self.line_pairs.index_code(2, start + 1)
            sections[-1].name = self.line_pairs.line_pair[name_pos].value
            end = self.line_pairs.index_both(0, "ENDSEC", start + 1)

            # If section was not properly terminated
            if end is None:
                end = self.line_pairs.nrs - 1

            sections[-1].end = end

            start = self.line_pairs.index_both(0, "SECTION", end)

        # g.logger.logger.info(("\n\nSections found:"), 1)
        # for sect in sections:
            # g.logger.logger.info(str(sect), 1)

        return sections

    # Search the TABLES section of the sections within this include LAYERS ???
    def Read_Layers(self, section):
        """
        Read_Layers()
        """
        for sect_nr in range(len(section)):
            if section[sect_nr].name.startswith("TABLES"):
                # tables_section = section[sect_nr]
                break

        # If the DXF blocks has, read this???
        layers = []
        if 'tables_section' in vars():
            tables_section = section[sect_nr]
            start = tables_section.begin

            while start is not None:
                start = self.line_pairs.index_both(0, "LAYER", start + 1, tables_section.end)
                if start is not None:
                    start = self.line_pairs.index_code(2, start + 1)
                    layers.append(LayerClass(len(layers)))
                    layers[-1].name = self.line_pairs.line_pair[start].value

        # g.logger.logger.info(("Layers found:"), 1)
        # for lay in layers:
            # g.logger.logger.info(str(lay), 1)

        return layers

    # Search the BLOCKS section within sections
    def Get_Blocks_pos(self, section):
        """
        Get_Blocks_pos()
        """
        for sect_nr in range(len(section)):
            if section[sect_nr].name.startswith("BLOCKS"):
                blocks_section = section[sect_nr]
                break

        # If the DXF blocks has, read this???
        blocks = []
        if 'blocks_section' in vars():
            # start = blocks_section.begin
            start = self.line_pairs.index_both(0, "BLOCK", blocks_section.begin, blocks_section.end)
            while start is not None:
                blocks.append(SectionClass())
                blocks[-1].Nr = len(blocks)
                blocks[-1].begin = start
                name_pos = self.line_pairs.index_code(2, start + 1, blocks_section.end)
                blocks[-1].name = self.line_pairs.line_pair[name_pos].value
                end = self.line_pairs.index_both(0, "ENDBLK", start + 1, blocks_section.end)
                blocks[-1].end = end
                start = self.line_pairs.index_both(0, "BLOCK", end + 1, blocks_section.end)

        # g.logger.logger.info(("Blocks found:"), 1)
        # for bl in blocks:
            # g.logger.logger.info(str(bl), 1)

        return blocks

    def Read_Blocks(self, blocks_pos):
        """
        Read_Blocks() - Read the block geometries
        """
        blocks = BlocksClass([])
        for block_nr in range(len(blocks_pos)):
            logger.info("Reading Block %s; Nr: %i" % (blocks_pos[block_nr].name, block_nr))

            blocks.Entities.append(EntitiesClass(block_nr, blocks_pos[block_nr].name, []))
            # Read the Baseline values for the block
            s = blocks_pos[block_nr].begin + 1
            e = blocks_pos[block_nr].end - 1
            lp = self.line_pairs
            # X value
            s = lp.index_code(10, s + 1, e)
            logger.debug("Found block pos Value 10 at: %s" %s)

            if s is None:
                blocks.Entities[-1].basep.x = 0.0
                s = blocks_pos[block_nr].begin + 1
            else:
                blocks.Entities[-1].basep.x = float(lp.line_pair[s].value)

            # Y value
            s = lp.index_code(20, s + 1, e)
            if s is None:
                blocks.Entities[-1].basep.y = 0.0
                s = blocks_pos[block_nr].begin + 1
            else:
                blocks.Entities[-1].basep.y = float(lp.line_pair[s].value)

            # Read the geometries
            blocks.Entities[-1].geo = self.Get_Geo(s, e)

        return blocks

    def Read_Entities(self, sections):
        """
        Read_Entities() - Read the entities geometries
        """
        for section_nr in range(len(sections)):
            if sections[section_nr - 1].name.startswith("ENTITIES"):
                # g.logger.logger.info("Reading Entities", 1)
                entities = EntitiesClass(0, 'Entities', [])
                entities.geo = self.Get_Geo(sections[section_nr - 1].begin + 1,
                                                    sections[section_nr - 1].end - 1)

        return entities

    def Get_Geo(self, begin, end):
        """
        Get_Geo() - Read the geometries of Blocks and Entities
        """
        geos = []
        self.start = self.line_pairs.index_code(0, begin, end)
        # old_start = self.start

        while self.start is not None:
            # Load the currently found geometry
            name = self.line_pairs.line_pair[self.start].value
            entitie_geo = self.get_geo_entitie(len(geos), name)

            # Append only if something was found
            if entitie_geo is not None:
                geos.append(entitie_geo)

            # Start the next search after one just found
            self.start = self.line_pairs.index_code(0, self.start, end)

            # Show debugging information if desired
            # if self.start is not None:
            #     g.logger.logger.info("Found %s at Linepair %0.0f (Line %0.0f till %0.0f)" \
            #                          % (name, old_start, old_start * 2 + 4, end * 2 + 4), 1)
            # else:
            #     g.logger.logger.info("Found %s at Linepair %0.0f (Line %0.0f till %0.0f)" \
            #                          % (name, old_start, old_start * 2 + 4, self.start * 2 + 4), 1)

            # if len(geos) > 0:
            #     g.logger.logger.info(str(geos[-1]), 2)

            # old_start = self.start

        del self.start
        return geos

    # Verteiler f�r die Geo-Instanzen
    # wird in def Get_Geo aufgerufen
    # f�r einen Release kann der ganze Code gerne wieder in einer Datei landen.
    # Distributor for Geo instances ???
    # is called in def Get_Geo
    # For a release of the entire code can be happy again end up in a file. ???
    def get_geo_entitie(self, geo_nr, name):
        """
        get_geo_entitie()
        """
        # Entities:
        # 3DFACE, 3DSOLID, ACAD_PROXY_ENTITY, ARC, ATTDEF, ATTRIB, BODY
        # CIRCLE, DIMENSTION, ELLIPSE, HATCH, IMAGE, INSERT, LEADER, LINE,
        # LWPOLYLINE, MLINE, MTEXT, OLEFRAME, OLE2FRAME, POINT, POLYLINE,
        # RAY, REGION, SEQEND, SHAPE, SOLID, SPLINE, XT, TOLERANCE, TRACE,
        # VERTEX, VIEWPOINT, XLINE

        # Instanz des neuen Objekts anlegen und gleichzeitig laden
        # Create a new instance of the object and at the same load ???
        if name == "POLYLINE":
            geo = GeoentPolyline(geo_nr, self)
        elif name == "SPLINE":
            geo = GeoentSpline(geo_nr, self)
        elif name == "ARC":
            geo = GeoentArc(geo_nr, self)
        elif name == "CIRCLE":
            geo = GeoentCircle(geo_nr, self)
        elif name == "LINE":
            geo = GeoentLine(geo_nr, self)
        elif name == "INSERT":
            geo = GeoentInsert(geo_nr, self)
        elif name == "ELLIPSE":
            geo = GeoentEllipse(geo_nr, self)
        elif name == "LWPOLYLINE":
            geo = GeoentLwPolyline(geo_nr, self)
        elif name == "POINT":
            geo = GeoentPoint(geo_nr, self)
        else:
            logger.info(("Found unsupported geometry type: %s !" % name))
            self.start += 1  # Eins hochz�hlen sonst gibts ne dauer Schleife
            return None

        return geo

    def Get_Layer_Nr(self, Layer_Name):
        """
        Get_Layer_Nr() - Find the number of geometry layers
        """
        for i in range(len(self.layers)):
            if self.layers[i].name == Layer_Name:
                layer_nr = i
                return layer_nr
        layer_nr = len(self.layers)
        self.layers.append(LayerClass(layer_nr))
        self.layers[-1].name = Layer_Name
        return layer_nr

    def Get_Block_Nr(self, Block_Name):
        """
        Get_Block_Nr() - Find the number of blocks
        """
        block_nr = -1
        for i in range(len(self.blocks.Entities)):
            if self.blocks.Entities[i].Name == Block_Name:
                block_nr = i
                break
        return block_nr

    def Get_Contour(self, entities=None):
        """
        Get_Contour() - Find the best contour the composite geometries
        """
        cont = []

        points = self.App_Cont_or_Calc_IntPts(entities.geo, cont)
        points = self.Find_Common_Points(points)
        # points = self.Remove_Redundant_Geos(points)

        found_cont = self.Search_Contours(entities.geo, points)

#         for check_cont in found_cont:
#             logger.debug("Correcting Contour inaccuracies if found")
#             for geo_nr in range(1, len(check_cont.order)):
#                 geo1 = check_cont.order[geo_nr - 1]
#                 geo2 = check_cont_order[geo_nr]
#                 logger.debug(geo[geo_nr])

        cont += found_cont

        return cont

    def App_Cont_or_Calc_IntPts(self, geo=None, cont=None):
        """
        App_Cont_or_Calc_IntPts()
        Calculate and assign the start and end points
        """

        tol = g.config.point_tolerance
        points = []
        warning = 0
        for i in range(len(geo)):
            # logger.debug("geo: %s" %geo[i])
            warning = geo[i].App_Cont_or_Calc_IntPts(cont, points, i, tol, warning)

        if warning:
            QMessageBox.warning(g.window, self.tr("Short Elements"),
                                self.tr("Length of some Elements too short!"
                                "\nLength must be greater than tolerance."
                                "\nSkipped Geometries"))

        return points

    def Find_Common_Points(self, points=None):
        """
        Find_Common_Points() - Find common points
        """
        # tol = self.config.points_tolerance.get()
        tol = g.config.point_tolerance

        p_list = []

        # Einen List aus allen Punkten generieren
        # Generate list of all points
        for p in points:
            p_list.append([p.Layer_Nr, p.be.x, p.be.y, p.point_nr, 0])
            p_list.append([p.Layer_Nr, p.en.x, p.en.y, p.point_nr, 1])

        # Sort the list
        p_list.sort()
        # print p_list

        # Loop for number of list items
        # Start = where to begin the search of the same elements
        anf = []

        for l_nr in range(len(p_list)):
            inter = []
            # print ("Suche Starten f�r Geometrie Nr: %i, Punkt %i" % (p_list[l_nr][3], l_nr))

            if isinstance(anf, list):
                c_nr = 0
            else:
                c_nr = anf

            anf = []

            # Schleife bis n�chster X Wert Gr��er ist als selbst +tol und Layer Gr��er gleich
            # Loop until the next X value is greater than yourself and layer Gr + tol he same ???
            while p_list[c_nr][0] < p_list[l_nr][0] or \
                  p_list[c_nr][1] <= (p_list[l_nr][1] + tol):
                # print ("Suche Punkt %i" % (c_nr))

                # Erstes das �bereinstimmt is der n�chste Anfang
                # First, the match is the next start
                if isinstance(anf, list) and\
                   p_list[c_nr][0] == p_list[l_nr][0] and\
                   abs(p_list[c_nr][1] - p_list[l_nr][1]) <= tol:
                    anf = c_nr
                    # print ("N�chste Suche starten bei" +str(anf))

                # Falls gleich anh�ngen
                # Attach if equal ???
                if p_list[c_nr][0] == p_list[l_nr][0] and \
                   abs(p_list[c_nr][1] - p_list[l_nr][1]) <= tol and\
                   abs(p_list[c_nr][2] - p_list[l_nr][2]) <= tol and\
                   c_nr != l_nr:
                    inter.append(c_nr)
                    # print ("Gefunden" +str(inter))
                c_nr += 1

                if c_nr == len(p_list):
                    break

            # Anh�ngen der gefundenen Punkte an points
            # Append the found points
            for int_p in inter:
                # Common Anfangspunkt
                # Common starting point
                if p_list[l_nr][-1] == 0:
                    points[p_list[l_nr][-2]].be_cp.append(p_list[int_p][3:5])
                # Common Endpunkt
                # Common end point
                else:
                    points[p_list[l_nr][-2]].en_cp.append(p_list[int_p][3:5])

        return points

    def Remove_Redundant_Geos(self, geo=None, points=None):
        """
        Remove_Redundant_Geos() - Does nothing!
        """
        pass
#        del_points=[]
#        for p_nr in range(len(points)):
#            if not(p_nr in del_points):
#                for be_p in points[p_nr].be_cp:
#                    for en_p in points[p_nr].en_cp:
#                        if be_p[0] == en_p[0]:
#                            del_points.append(be_p[0])
#                            print ('Gleiche Punkte in Anfang: %s und Ende %s' % (be_p, en_p))
#
#        #L�schen der �berfl�ssigen Punkte
#        #Delete the ? points ???
#        for p_nr in del_points:
#            for j in range(len(points)):
#                if p_nr == points[j].point_nr:
#                    del points[j]
#                    break
#        return points

    def Search_Contours(self, geo = None, all_points = None):
        """
        Search_Contours() - Find the best continuous contours
        """

        found_contours = []
        points = deepcopy(all_points)

        while len(points) > 0:
            # print '\n Neue Suche'
            # Wenn nichts gefunden wird dann einfach die Kontur hochz�hlen
            # If nothing found then count up the contour
            if len(points[0].be_cp) == 0 and len(points[0].en_cp) == 0:
                # print '\nGibt Nix'
                found_contours.append(ContourClass(len(found_contours), 0, [[points[0].point_nr, 0]], 0))
            elif len(points[0].be_cp) == 0 and len(points[0].en_cp) > 0:
                # print '\nGibt was R�ckw�rts (Anfang in neg dir)'
                new_cont_pos = self.Search_Paths(0, [], points[0].point_nr, 0, points)
                found_contours.append(self.Get_Best_Contour(len(found_contours), new_cont_pos, geo, points))
            elif len(points[0].be_cp) > 0 and len(points[0].en_cp) == 0:
                # print '\nGibt was Vorw�rt (Ende in pos dir)'
                new_cont_neg = self.Search_Paths(0, [], points[0].point_nr, 1, points)
                found_contours.append(self.Get_Best_Contour(len(found_contours), new_cont_neg, geo, points))
            elif len(points[0].be_cp) > 0 and len(points[0].en_cp) > 0:
                # print '\nGibt was in beiden Richtungen'
                # Suchen der m�glichen Pfade
                # Search the possible paths
                new_cont_pos = self.Search_Paths(0, [], points[0].point_nr, 1, points)
                # Bestimmen des besten Pfades und �bergabe in cont
                # Determine the best path and Xbergabe in cont ???
                found_contours.append(self.Get_Best_Contour(len(found_contours), new_cont_pos, geo, points))
                # points = self.Remove_Used_Points(cont[-1], points)

                # Falls der Pfad nicht durch den ersten Punkt geschlossen ist
                # If the path is not closed by the first point
                if found_contours[-1].closed == 0:
                    # print '\nPfad nicht durch den ersten Punkt geschlossen'
                    found_contours[-1].reverse()
                    # print ("Neue Kontur umgedrejt %s" % cont[-1])
                    new_cont_neg = self.Search_Paths(0, [found_contours[-1]], points[0].point_nr, 0, points)
                    found_contours[-1] = self.Get_Best_Contour(len(found_contours) - 1, new_cont_neg + new_cont_pos, geo, points)

            else:
                print('FEHLER !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')

            points = self.Remove_Used_Points(found_contours[-1], points)

            found_contours[-1] = self.Contours_Points2Geo(found_contours[-1], all_points)
        return found_contours

    def Search_Paths(self, c_nr=None, c=None, p_nr=None, dir=None, points=None):
        """
        Search_Paths() - Search the paths through the Contour
        """

        # Define the direction of the search (1 = positive, 0 = neg or reverse)

        # If it is the first call a new contour is to be created
        if len(c) == 0:
            c.append(ContourClass(cont_nr=0, order=[[p_nr, dir]]))

        # Suchen des Punktes innerhalb der points List (n�tig da verwendete Punkte gel�scht werden)
        # Search for the item within the list of points (ntig used as points gelscht) ???
        for new_p_nr in range(len(points)):
            if points[new_p_nr].point_nr == p_nr:
                break

        # Next point depending on the direction
        if dir == 0:
            weiter = points[new_p_nr].en_cp
        elif dir == 1:
            weiter = points[new_p_nr].be_cp

        # Schleife f�r die Anzahl der Abzweig M�glichkeiten
        # Loop for the number of the branch can write ???
        for i in range(len(weiter)):
            # Wenn es die erste M�glichkeit ist Hinzuf�gen zur aktuellen Kontur
            # If it is the first possibility to add to the current contour
            if i == 0:
                if not(c[c_nr].is_contour_closed()):
                    c[c_nr].order.append(weiter[0])

            # There is a branch.  It is copied to the current contour and the
            # other branches follow
            elif i > 0:
                if not(c[c_nr].is_contour_closed()):
                    # print 'Abzweig ist m�glich'
                    c.append(deepcopy(c[c_nr]))
                    del c[-1].order[-1]
                    c[-1].order.append(weiter[i])

        for i in range(len(weiter)):
            # print 'I ist: ' +str(i)
            if i == 0:
                new_c_nr = c_nr
            else:
                new_c_nr = len(c) - len(weiter) + i

            new_p_nr = c[new_c_nr].order[-1][0]
            new_dir = c[new_c_nr].order[-1][1]
            if not(c[new_c_nr].is_contour_closed()):
                c = self.Search_Paths(copy(new_c_nr), c, copy(new_p_nr), copy(new_dir), points)
        return c

    def Get_Best_Contour(self, c_nr, c=None, geo=None, points=None):
        """
        Get_Best_Contour() - Seek for the best (in my opinion) countour
        """

        # Shortlist of the new contour
        best = None
        best_open = None
        # print ("Es wurden %0.0f Konturen gefunden" %len(c))
        for i in range(len(c)):
            # if len(c)>1:
            #     print ("Kontur Nr %0.0f" %i)
            #     print c[i]

            # Korrigieren der Kontur falls sie nicht in sich selbst geschlossen ist
            # The correct contour if it is not closed in on itself
            if c[i].closed == 2:
                c[i].remove_other_closed_contour()
                c[i].closed = 0
                c[i].calc_length(geo)

            # Search for the best geometry
            if c[i].closed == 1:
                c[i].calc_length(geo)
                if best is None:
                    best = i
                else:
                    if c[best].length < c[i].length:
                        best = i
            elif c[i].closed == 0:
                c[i].calc_length(geo)
                if best_open is None:
                    best_open = i
                else:
                    if c[best_open].length < c[i].length:
                        best_open = i

            # Falls keine Geschschlossene dabei ist Beste = Offene
            # If no Geschschlossene is best = Open ???
        if best is None:
            best = best_open

        best_c = c[best]
        best_c.cont_nr = c_nr

        # print "Beste Kontur Nr:%s" %best_c

        return best_c

    # All the points in the path from Point Clear to accelerate nights Search ???
    def Remove_Used_Points(self, cont=None, points=None):
        """
        Remove_Used_Points()
        """
        for p_nr in cont.order:

            # This has to be 2 separate loops, otherwise one element is missing
            for Point in points:
                if p_nr[0] == Point.point_nr:
                    del points[points.index(Point)]

            for Point in points:
                for be_cp in Point.be_cp:
                    if p_nr[0] == be_cp[0]:
                        del Point.be_cp[Point.be_cp.index(be_cp)]
                        break

                for en_cp in Point.en_cp:
                    if p_nr[0] == en_cp[0]:
                        del Point.en_cp[Point.en_cp.index(en_cp)]
                        break

        # Return to the contour ???
        return points

    # All the points in the path from Point Clear to accelerate nights Search ???
    def Contours_Points2Geo(self, cont=None, points=None):
        """
        Contours_Points2Geo()
        """
        # print cont.order
        for c_nr in range(len(cont.order)):
            cont.order[c_nr][0] = points[cont.order[c_nr][0]].geo_nr
        return cont

class dxflinepairClass:
    def __init__(self, code=None, value=None):
        self.code = code
        self.value = value

    def __str__(self):
        return 'Code ->' + str(self.code) + '\nvalue ->' + self.value

class dxflinepairsClass:
    def __init__(self, line_pair):
        self.nrs = 0
        self.line_pair = line_pair

    def __str__(self):
        return 'Number of Line Pairs: ' + str(self.nrs)

    # Search for information in the line pairs (both code & value)
    # Optional start and end values for the search
    def index_both(self, code=0, value=0, start=0, stop= -1):
        """
        index_both()
        """

        # If stop==-1 then stop at the end of the pairs
        if stop == -1:
            stop = len(self.line_pair)

        # Start the search within the specified parameters
        for i in range(start, stop):
            if self.line_pair[i].code == code and self.line_pair[i].value == value:
                return i

        #If nothing found return "None"
        return None

    #Sucht nach Code Angaben in den Line Pairs code & value
    # optional mit start und endwert f�r die Suche
    #Search for information in the Line Pairs (both code & value)
    #Optional start and end values for the search
    def index_code(self, code=0, start=0, stop= -1):
        """
        index_code()
        """

        # If stop == -1 then stop at the end of the pairs
        if stop == -1:
            stop = len(self.line_pair)

        # Start the search within the specified parameters
        for i in range(start, stop):
            if self.line_pair[i].code == code:
                return i

        # If nothing found return "None"
        return None

class LayerClass:
    def __init__(self, Nr=0, name=''):
        self.Nr = Nr
        self.name = name

    def __str__(self):
        # how to print the object
        return 'Nr ->' + str(self.Nr) + '\nName ->' + self.name

    def __len__(self):
        return self.__len__

class SectionClass:
    def __init__(self, Nr=0, name='', begin=0, end=1):
        self.Nr = Nr
        self.name = name
        self.begin = begin
        self.end = end

    def __str__(self):
        # how to print the object
        return 'Nr ->' + str(self.Nr) + '\nName ->' + self.name + '\nBegin ->' + str(self.begin) + '\nEnd: ->' + str(self.end)

    def __len__(self):
        return self.__len__

class EntitiesClass:
    def __init__(self, Nr=0, Name='', geo=[], cont=[]):
        self.Nr = Nr
        self.Name = Name
        self.basep = Point(x=0.0, y=0.0)
        self.geo = geo
        self.cont = cont

    def __str__(self):
        # how to print the object
        return "\nNr:      %s" % self.Nr +\
               "\nName:    %s" % self.Name +\
               "\nBasep:   %s" % self.basep +\
               "\nNumber of Geometries: %i" % len(self.geo) +\
               "\nNumber of Contours:   %i" % len(self.cont)

    def __len__(self):
        return self.__len__

    # Gibt einen List mit den Benutzten Layers des Blocks oder Entities zur�ck
    # Is a List back to results with the use of block layer or Entities ???
    def get_used_layers(self):
        used_layers = []
        for i in range(len(self.geo)):
            if (self.geo[i].Layer_Nr in used_layers) == 0:
                used_layers.append(self.geo[i].Layer_Nr)
        return used_layers
    # Gibt die Anzahl der Inserts in den Entities zur�ck
    # Returns the number of inserts back into the Entities ???

    def get_insert_nr(self):
        insert_nr = 0
        for i in range(len(self.geo)):
            if "Insert" in self.geo[i].Typ:
                insert_nr += 1
        return insert_nr

class BlocksClass:
    def __init__(self, Entities=[]):
        self.Entities = Entities

    def __str__(self):
        # how to print the object
        s = 'Blocks:\nNumber of Blocks ->' + str(len(self.Entities))
        for entitie in self.Entities:
            s += str(entitie)
        return s


from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.template import loader
from django.urls import reverse
from .models import pregunta, Eleccion

# Create your views here.
def index(request):
    #Consulta la DB ORM
    # SQL : Select * from pregunta orden by desc fecha pub limit 5
    ultimas_preguntas=pregunta.objects.order_by('-fecha_pub')[:10]

    template=loader.get_template('polls/index.html')
    contexto={
        'ultimas_preguntas':ultimas_preguntas
    }
    return HttpResponse(template.render(contexto, request))

def holamanuel(request):
    return HttpResponse("<h1> HOLA MANUEL </h1>")

def detalle(request,id_pregunta):
    try:
        #Select * from * pregunta where id=id_pregunta
        pre=pregunta.objects.get(pk=id_pregunta)
        template=loader.get_template('polls/detalle.html')
        contexto={
            'pregunta':pre
         }
        return HttpResponse(template.render(contexto, request))
    except pregunta.DoesNotExist:
        raise Http404("La pregunta no existe")

def resultados(request,id_pregunta):
    pre=get_object_or_404(pregunta, pk=id_pregunta)
    template=loader.get_template('polls/resultados.html')
    contexto = {
        'pregunta':pre
    }
    return HttpResponse(template.render(contexto, request))

def votar(request,id_pregunta):
    pre=get_object_or_404(pregunta, pk=id_pregunta)
    try:
         opcion_seleccionada=Eleccion.objects.get(pk=request.POST["elegir"])
    except (KeyError, Eleccion.DoesNotExist):
        template_detalle=loader.get_template("polls/detalle.html")
        contexto = {
            'pregunta':pre,
            'mensaje_error': "La opción que elegiste no existe... "
        }
        return HttpResponse(template_detalle.render(contexto, request))
    else:
        opcion_seleccionada.votos += 1
        opcion_seleccionada.save()
        return HttpResponseRedirect(reverse('polls:resultados', args=(pre.id, ) ))


import itertools

def offset_fft(digits):
    out = [digits[-1]]
    for i in range(2, len(digits) + 1):
        out.append(out[-1] + digits[-i])
    return [abs(i) % 10 for i in reversed(out)]

def phases_off(digits, n, off):
    digits = digits[off:]
    for _ in range(n):
        digits = offset_fft(digits)
    return digits

def fft(digits):
    out = []
    half = len(digits) // 2
    for i in range(1, half + 1):
        repeat = itertools.cycle(itertools.chain(itertools.repeat(0, i),
                                                 itertools.repeat(1, i),
                                                 itertools.repeat(0, i),
                                                 itertools.repeat(-1, i)))
        next(repeat)
        out.append(abs(sum(i * d for i, d in zip(repeat, digits))) % 10)
    out.extend(offset_fft(digits[half:]))
    return out

def phases(digits, n):
    for _ in range(n):
        digits = fft(digits)
    return digits

def decode(digits):
    offset = int(''.join(str(i) for i in digits[:7]))
    return phases_off(digits * 10000, 100, offset)[:8]

def run(data):
    val = [int(i) for i in data]
    part_a = ''.join(str(i) for i in phases(val, 100)[:8])
    part_b = ''.join(str(i) for i in decode(val))
    return part_a, part_b

if __name__ == '__main__':
    from aocd.models import Puzzle

    assert phases([1,2,3,4,5,6,7,8], 1) == [4,8,2,2,6,1,5,8]
    assert phases([1,2,3,4,5,6,7,8], 2) == [3,4,0,4,0,4,3,8]
    assert phases([1,2,3,4,5,6,7,8], 3) == [0,3,4,1,5,5,1,8]
    assert phases([1,2,3,4,5,6,7,8], 4) == [0,1,0,2,9,4,9,8]

    assert phases([8,0,8,7,1,2,2,4,5,8,5,9,1,4,5,4,6,6,1,9,0,8,3,2,1,8,6,4,5,5,9,5], 100)[:8] == [2,4,1,7,6,1,7,6]
    assert phases([1,9,6,1,7,8,0,4,2,0,7,2,0,2,2,0,9,1,4,4,9,1,6,0,4,4,1,8,9,9,1,7], 100)[:8] == [7,3,7,4,5,4,1,8]

    assert decode([0,3,0,3,6,7,3,2,5,7,7,2,1,2,9,4,4,0,6,3,4,9,1,5,6,5,4,7,4,6,6,4]) == [8,4,4,6,2,0,2,6]
    assert decode([0,2,9,3,5,1,0,9,6,9,9,9,4,0,8,0,7,4,0,7,5,8,5,4,4,7,0,3,4,3,2,3]) == [7,8,7,2,5,2,7,0]
    assert decode([0,3,0,8,1,7,7,0,8,8,4,9,2,1,9,5,9,7,3,1,1,6,5,4,4,6,8,5,0,5,1,7]) == [5,3,5,5,3,7,3,1]

    puz = Puzzle(2019, 16)
    part_a, part_b = run(puz.input_data)

    puz.answer_a = part_a
    print(f'Part 1: {puz.answer_a}')

    puz.answer_b = part_b
    print(f'Part 2: {puz.answer_b}')


import csv
import os
import sys
import time

from splunk.clilib.bundle_paths import make_splunkhome_path
sys.path.append(make_splunkhome_path(["etc", "apps", "SA-Utils", "bin"]))
sys.path.append(make_splunkhome_path(["etc", "apps", "SA-Utils", "lib"]))

import network.whois_handlers as handlers
from SolnCommon import netutils
from SolnCommon.modinput import logger
from SolnCommon.modinput import ModularInput
from SolnCommon.modinput.fields import BooleanField
from SolnCommon.modinput.fields import Field
from SolnCommon.modinput.fields import IntegerField
from SolnCommon.modinput.fields import RangeField
from SolnCommon.modinput.fields import FieldValidationException

#import logging
#logger.setLevel(logging.DEBUG)

class NetworkModularInput(ModularInput):

    def __init__(self):

        scheme_args = {'title': "Network Queries",
                       'description': "Perform network queries.",
                       'use_external_validation': "true",
                       'streaming_mode': "xml",
                       'use_single_instance': "true"}

        args = [
                Field("api_host", "API host", "Hostname used to access external API, if applicable.", required_on_create=True, required_on_edit=True),
                Field("api_user", "API user", "User name used to access external API, if applicable.", required_on_create=True, required_on_edit=True),
                Field("app", "Splunk application context", "Splunk application context used to retrieve stored credentials.", required_on_create=False, required_on_edit=False),
                Field("owner", "Splunk owner", "Splunk user account used to retrieve stored credentials.", required_on_create=False, required_on_edit=False),
                Field("provider", "Provider", "The name of the provider for this input.", required_on_create=True, required_on_edit=True),
                RangeField("proxy_port", "Proxy port", "The proxy server port (only applicable to methods that use HTTP).", low=0, high=65535, required_on_create=False, required_on_edit=False),
                Field("proxy_server", "Proxy server", "The proxy server (only applicable to methods that use HTTP).", required_on_create=False, required_on_edit=False),
                Field("proxy_user", "Proxy user", "The proxy user name (only applicable to methods that use HTTP).", required_on_create=False, required_on_edit=False),
                IntegerField("query_interval", "Query interval", "Interval between queries, in seconds.", required_on_create=True, required_on_edit=True),
                IntegerField("queue_interval", "Queue interval", "Interval between attempts to process the queue, in seconds.", required_on_create=True, required_on_edit=True),
                BooleanField("reverse_dns_enabled", "Reverse DNS enabled", "Attempt to resolve IP addresses to hostnames prior to making WHOIS queries.", required_on_create=False, required_on_edit=False),
                ]

        ModularInput.__init__(self, scheme_args, args)

    def do_validation(self, in_stream=sys.stdin):
        # TODO: 2-stage validation:
        # 1. Validate field contents.
        # 2. Add validation of params via the chosen provider here.

        data = self.get_validation_data(in_stream)

        try:
            self.validate(data)
            return True
        except FieldValidationException as e:
            self.print_error(str(e))
            return False

    def get_handler(self, name):
        return getattr(handlers, name)

    def run(self, stanzas):

        logger.debug("Entering run method.")
        logger.debug("Input config: {0}".format(str(self._input_config)))
        logger.debug("Cleaned params: {0}".format(str(stanzas)))

        # If an alternative session key for testing was provided
        # via command-line arguments, use it.
        try:
            if hasattr(self, '_alt_session_key'):
                self._input_config.session_key = self._alt_session_key
        except (AttributeError, NameError):
            # No alternate session key defined.
            pass

        # Get the first stanza (which should be the only stanza); otherwise exit.
        if stanzas:
            stanza = stanzas[0]
        else:
            logger.info('No WHOIS inputs configured. Exiting.')
            sys.exit(0)

        # Get the handler for the current configuration.
        try:
            handler = getattr(handlers, stanza['provider'])
        except AttributeError as e:
            logger.error("Query handler not found: %s" % (str(e)))
            raise e

        # Prepare the handler
        query_handler = handler(logger, self._input_config.session_key, **stanza)

        # Prepare the results for XML streaming output.
        doc = self._create_document()

        # Begin endless loop.
        while True:
            # Get the list of queries. This is a list of files, each in CSV format:
            #     _time,domain
            filenames = os.listdir(self._input_config.checkpoint_dir)

            # Check for checkpointed files; if none exist, sleep for queue_interval
            # seconds.
            if len(filenames) > 0:

                # Accept only "csv" checkpoint files.
                for filename in [f for f in filenames if f.endswith('.csv')]:
                    try:
                        checkpointed_file = open(os.path.join(self._input_config.checkpoint_dir, filename), 'r')
                    except IOError:
                        logger.error('Error opening checkpoint file: %s', os.path.join(self._input_config.checkpoint_dir, filename))
                        continue

                    logger.info('Processing checkpoint file: {0}'.format(checkpointed_file.name))
                    queries = csv.DictReader(checkpointed_file)

                    for count, query in enumerate(queries):

                        qstr = query['domain']
                        if stanza.get('reverse_dns_enabled', False):
                            # Attempt DNS resolution in case this is an IP address.
                            resolved_query = netutils.resolve(qstr)
                            if resolved_query:
                                qstr = resolved_query
                        else:
                            # IP addresses in incoming data will not be resolved.
                            pass

                        # Run the query
                        result = query_handler.run(qstr)

                        # Format the result to include the domain and resolved domain, if found.
                        # Result can be None at this point; format will return a properly-
                        # formatted JSON error message if so.
                        data = query_handler.format(query['domain'], qstr, result)

                        if data:

                            # The "data" field should be populated from the query.
                            # The "host" field is implicit for this type of modular input.
                            event_dict = {'stanza': stanza,
                                          'time': query['_time'],
                                          'data': data,
                                          'index': stanza['index'],
                                          'source': stanza['source'],
                                          'sourcetype': stanza['sourcetype']}

                            event = self._create_event(doc,
                                params=event_dict,
                                stanza=stanza,
                                unbroken=stanza.get('unbroken', False),
                                close=False)

                            # If using unbroken events, the last event must have been
                            # added with a "</done>" tag.
                            output = self._print_event(doc, event)

                            # Print the XML stream to stdout.
                            sys.stdout.write(output)
                            sys.stdout.flush()

                            # Sleep for a number of seconds between queries.
                            time.sleep(stanza['query_interval'])

                        else:
                            # The format() method may return None if Unicode or
                            # other bad input was received.
                            # In this case, log the position of the error.
                            logger.error('ERROR: WHOIS query handler failed on a domain (filename=%s, line=%d', checkpointed_file.name, count+1)

                    # Close resources and remove the checkpoint file.
                    checkpointed_file.close()
                    try:
                        # This may fail on Windows if file is still in use.
                        os.remove(checkpointed_file.name)
                        logger.info('Removed checkpoint file %s', checkpointed_file.name)
                    except IOError as e:
                        logger.error('ERROR: Failed to remove checkpoint file %s', checkpointed_file.name)

            else:
                logger.info('No checkpoint files found for WHOIS lookup... sleeping for %s seconds', stanza['queue_interval'])
                # Wait for new checkpoint files to be created.
                time.sleep(stanza['queue_interval'])

        # end while loop

if __name__ == '__main__':
    logger.info("Executing modular input.")
    modinput = NetworkModularInput()
    try:
        # execute() Loops indefinitely
        modinput.execute()
    except Exception as e:
        sys.exit(1)
    sys.exit(0)


import sys
from typing import Any, Dict, List, Optional

from kitty.cli import parse_args
from kitty.cli_stub import RCOptions, ResizeCLIOptions
from kitty.constants import version
from kitty.key_encoding import CTRL, EventType, KeyEvent
from kitty.rc.base import command_for_name, parse_subcommand_cli
from kitty.remote_control import encode_send, parse_rc_args
from kitty.utils import ScreenSize

from ..tui.handler import Handler
from ..tui.loop import Loop
from ..tui.operations import styled

global_opts = RCOptions()

class Resize(Handler):

    print_on_fail: Optional[str] = None

    def __init__(self, opts: ResizeCLIOptions):
        self.opts = opts

    def initialize(self) -> None:
        global global_opts
        global_opts = parse_rc_args(['kitty', '@resize-window'])[0]
        self.original_size = self.screen_size
        self.cmd.set_cursor_visible(False)
        self.cmd.set_line_wrapping(False)
        self.draw_screen()

    def do_window_resize(self, is_decrease: bool = False, is_horizontal: bool = True, reset: bool = False, multiplier: int = 1) -> None:
        resize_window = command_for_name('resize_window')
        increment = self.opts.horizontal_increment if is_horizontal else self.opts.vertical_increment
        increment *= multiplier
        if is_decrease:
            increment *= -1
        axis = 'reset' if reset else ('horizontal' if is_horizontal else 'vertical')
        cmdline = [resize_window.name, '--self', f'--increment={increment}', '--axis=' + axis]
        opts, items = parse_subcommand_cli(resize_window, cmdline)
        payload = resize_window.message_to_kitty(global_opts, opts, items)
        send = {'cmd': resize_window.name, 'version': version, 'payload': payload, 'no_response': False}
        self.write(encode_send(send))

    def on_kitty_cmd_response(self, response: Dict[str, Any]) -> None:
        if not response.get('ok'):
            err = response['error']
            if response.get('tb'):
                err += '\n' + response['tb']
            self.print_on_fail = err
            self.quit_loop(1)
            return
        res = response.get('data')
        if res:
            self.cmd.bell()

    def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
        text = text.upper()
        if text in 'WNTSR':
            self.do_window_resize(is_decrease=text in 'NS', is_horizontal=text in 'WN', reset=text == 'R')
        elif text == 'Q':
            self.quit_loop(0)

    def on_key(self, key_event: KeyEvent) -> None:
        if key_event.type is EventType.RELEASE:
            return
        if key_event.matches('esc'):
            self.quit_loop(0)
            return
        if key_event.key in ('w', 'n', 't', 's') and key_event.mods_without_locks == CTRL:
            self.do_window_resize(is_decrease=key_event.key in 'ns', is_horizontal=key_event.key in 'wn', multiplier=2)

    def on_resize(self, new_size: ScreenSize) -> None:
        self.draw_screen()

    def draw_screen(self) -> None:
        self.cmd.clear_screen()
        print = self.print
        print(styled('Resize this window', bold=True, fg='gray', fg_intense=True))
        print()
        print('Press one of the following keys:')
        print('  {}ider'.format(styled('W', fg='green')))
        print('  {}arrower'.format(styled('N', fg='green')))
        print('  {}aller'.format(styled('T', fg='green')))
        print('  {}horter'.format(styled('S', fg='green')))
        print('  {}eset'.format(styled('R', fg='red')))
        print()
        print('Press {} to quit resize mode'.format(styled('Esc', italic=True)))
        print('Hold down {} to double step size'.format(styled('Ctrl', italic=True)))
        print()
        print(styled('Sizes', bold=True, fg='white', fg_intense=True))
        print(f'Original: {self.original_size.rows} rows {self.original_size.cols} cols')
        print('Current:  {} rows {} cols'.format(
            styled(str(self.screen_size.rows), fg='magenta'), styled(str(self.screen_size.cols), fg='magenta')))

OPTIONS = r'''
--horizontal-increment
default=2
type=int
The base horizontal increment.

--vertical-increment
default=2
type=int
The base vertical increment.
'''.format

def main(args: List[str]) -> None:
    msg = 'Resize the current window'
    try:
        cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'resize_window', result_class=ResizeCLIOptions)
    except SystemExit as e:
        if e.code != 0:
            print(e.args[0], file=sys.stderr)
            input('Press Enter to quit')
        return

    loop = Loop()
    handler = Resize(cli_opts)
    loop.loop(handler)
    if handler.print_on_fail:
        print(handler.print_on_fail, file=sys.stderr)
        input('Press Enter to quit')
    raise SystemExit(loop.return_code)


import pytest

@pytest.fixture(autouse=True)
def autouse_external_tx(external_tx):
    pass

@pytest.fixture
def lab(testapp):
    item = {
        'name': 'encode-lab',
        'title': 'ENCODE lab',
    }
    return testapp.post_json('/lab', item).json['@graph'][0]

@pytest.fixture
def remc_lab(testapp):
    item = {
        'name': 'remc-lab',
        'title': 'REMC lab',
    }
    return testapp.post_json('/lab', item).json['@graph'][0]

@pytest.fixture
def admin(testapp):
    item = {
        'first_name': 'Test',
        'last_name': 'Admin',
        'email': 'admin@example.org',
        'groups': ['admin'],
    }
    # User @@object view has keys omitted.
    res = testapp.post_json('/user', item)
    return testapp.get(res.location).json

@pytest.fixture
def wrangler(testapp):
    item = {
        # some objects have linkEnums
        'uuid': '4c23ec32-c7c8-4ac0-affb-04befcc881d4',
        'first_name': 'Wrangler',
        'last_name': 'Admin',
        'email': 'wrangler@example.org',
        'groups': ['admin'],
    }
    # User @@object view has keys omitted.
    res = testapp.post_json('/user', item)
    return testapp.get(res.location).json

@pytest.fixture
def submitter(testapp, lab, award):
    item = {
        'first_name': 'ENCODE',
        'last_name': 'Submitter',
        'email': 'encode_submitter@example.org',
        'submits_for': [lab['@id']],
        'viewing_groups': [award['viewing_group']],
    }
    # User @@object view has keys omitted.
    res = testapp.post_json('/user', item)
    return testapp.get(res.location).json

@pytest.fixture
def access_key(testapp, submitter):
    description = 'My programmatic key'
    item = {
        'user': submitter['@id'],
        'description': description,
    }
    res = testapp.post_json('/access_key', item)
    result = res.json['@graph'][0].copy()
    result['secret_access_key'] = res.json['secret_access_key']
    return result

@pytest.fixture
def viewing_group_member(testapp, award):
    item = {
        'first_name': 'Viewing',
        'last_name': 'Group',
        'email': 'viewing_group_member@example.org',
        'viewing_groups': [award['viewing_group']],
    }
    # User @@object view has keys omitted.
    res = testapp.post_json('/user', item)
    return testapp.get(res.location).json

@pytest.fixture
def remc_member(testapp, remc_lab):
    item = {
        'first_name': 'REMC',
        'last_name': 'Member',
        'email': 'remc_member@example.org',
        'submits_for': [remc_lab['@id']],
        'viewing_groups': ['REMC'],
    }
    # User @@object view has keys omitted.
    res = testapp.post_json('/user', item)
    return testapp.get(res.location).json

@pytest.fixture
def award(testapp):
    item = {
        'name': 'encode3-award',
        'rfa': 'ENCODE3',
        'project': 'ENCODE',
        'viewing_group': 'ENCODE',
    }
    return testapp.post_json('/award', item).json['@graph'][0]

@pytest.fixture
def invalid_award(testapp):
    item = {
        'name': 'award names cannot have spaces',
        'rfa': 'ENCODE3',
        'project': 'ENCODE',
        'viewing_group': 'ENCODE',
    }
    return testapp.post_json('/award' + '?validate=false', item).json['@graph'][0]

RED_DOT = """data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg=="""

@pytest.fixture
def attachment():
    return {'download': 'red-dot.png', 'href': RED_DOT}

@pytest.fixture
def snowball(testapp, lab, award):
    item = {
        'award': award['@id'],
        'lab': lab['@id'],
        'method': 'hand-packed',
    }
    return testapp.post_json('/snowball', item).json['@graph'][0]

@pytest.fixture
def invalid_snowball(testapp, lab, award):
    item = {
        'award': award['uuid'],
        'lab': lab['uuid'],
        'method': 'hand-packed',
        'schema_version': '1',
        'status': 'FLYBARGS',
    }
    return testapp.post_json('/snowball/' + '?validate=false', item).json['@graph'][0]

@pytest.fixture
def invalid_snowball2(testapp, lab, award):
    item = {
        'award': award['uuid'],
        'lab': lab['uuid'],
        'method': 'hand-packed',
        'schema_version': '1',
        'status': 'FLINGERNEERS',
        'alternate_accessions': ['THE THING THAT SHOULD NOT BE']
    }
    return testapp.post_json('/snowball/' + '?validate=false', item).json['@graph'][0]

@pytest.fixture
def snowflake(testapp, lab, award, snowball):
    item = {
        'snowset': snowball['@id'],
        'type': 'fluffy',
        'status': 'in progress',
        'award': award['@id'],
        'lab': lab['@id'],
    }
    return testapp.post_json('/snowflake', item).json['@graph'][0]


from celery import shared_task
from django.core.mail import send_mail
from biblioteca_backend_drf_celery import settings

@shared_task
def send_email_authors(author, book):
    asunto = f'Se ha registrado un libro del que usted es autor {book.title}'
    mensaje = f'Estimado {author.name} {author.lastname}:\n' \
              f'Se ha registrado un libro del que usted es autor\n' \
              f'Libro: {book.title}'
    send_mail(
        asunto,
        mensaje,
        settings.EMAIL_HOST_USER,
        [author.email],
        fail_silently=False,
    )

    resultado = f'Mensaje enviado a {author.name} {author.lastname} ({author.email})'
    print(resultado)
    return True


"""
https://leetcode.com/problems/prime-palindrome/
"""

class Solution:
    def primePalindrome(self, N: int) -> int:
        """
        1. All palindrome numbers having even digits could be divided by 11.
            So only 11 is a prime, the remaining ones could be skipped.
        """
        def is_prime(x: int) -> bool:
            return x > 1 and all(x % d for d in range(2, int(x ** 0.5) + 1))

        def is_palindrome(x: int) -> bool:
            rslt = 0
            y = x
            while y:
                y, r = divmod(y, 10)
                rslt = 10 * rslt + r

            return rslt == x

        def get_digits(x: int) -> int:
            rslt = 0
            while x:
                x = x // 10
                rslt += 1

            return rslt

        rslt = N
        while True:
            if 8 <= rslt <= 11:
                return 11

            if is_palindrome(rslt) and is_prime(rslt):
                return rslt

            digits = get_digits(rslt)
            if digits & 1 == 0:  # The number has even digits.
                rslt = 10 ** digits + 1  # Skip to the next level.
            else:
                rslt += 1  # Continue to the next number.

print(Solution().primePalindrome(6))


import random
import sys
import argparse

class HashTable:
    def __init__(self, num_slots, cap_per_slot, occupancy, total_trials, hash_type):
        self.num_slots = num_slots
        self.cap_per_slot = cap_per_slot
        self.occupancy = occupancy
        self.total_trials = total_trials
        self.hash_type = hash_type

        self.num_elements = int(occupancy * num_slots * cap_per_slot)
        self.trials_with_collisions = 0

    def run_hash_table(self):
        self.trials_with_collisions = 0
        for seed in range(self.total_trials):
            random.seed(seed)
            occupancy_at_slot = [0] * self.num_slots
            for i in range(self.num_elements):
                if self.hash_type == "standard":
                    slot_number = self.standard_hashing(occupancy_at_slot)
                elif self.hash_type == "2choice":
                    slot_number = self.two_choice_hashing(occupancy_at_slot)
                elif self.hash_type == "2left":
                    slot_number = self.two_left_hashing(occupancy_at_slot)
                occupancy_at_slot[slot_number] += 1

            if any([counter > self.cap_per_slot for counter in occupancy_at_slot]):
                self.trials_with_collisions += 1

        trials_did_not_exceed = 1.0 - (self.trials_with_collisions/self.total_trials)
        print("Fraction of trials in which slot size did not exceed capacity ", trials_did_not_exceed)
        return trials_did_not_exceed

    def standard_hashing(self, occupancy_at_slot):
        #TODO: Implement standard hashing by picking a slot number randomly between 0 and NUM_SLOTS - 1

        # Set variable rand_num to random integer
        rand_num = random.randint(0, self.num_slots -1)

        #TODO: Return the final slot number that you pick.
        return rand_num

    def two_choice_hashing(self, occupancy_at_slot):
        #TODO: Implement 2 choice hashing: pick two slot numbers randomly and then pick the less occupied of the 2
        # Break ties randomly.

        # Set random integers to variables
        rand_num1 = random.randint(0, self.num_slots -1)
        rand_num2 = random.randint(0, self.num_slots -1)
        # If value at random number less, then set return value to lesser one
        if occupancy_at_slot[rand_num1] < occupancy_at_slot[rand_num2]:
            rand_num = rand_num1
        elif occupancy_at_slot[rand_num2] < occupancy_at_slot[rand_num1]:
            rand_num = rand_num2
        else:
            rand_num = random.choice([rand_num1, rand_num2])

        # Return the final slot number that you pick.
        return rand_num

    def two_left_hashing(self, occupancy_at_slot):
        #TODO: Implement 2 left hashing: pick two slots numbers from two sub tables and pick the less occupied of the 2
        # Always break ties towards one sub table

        # Set variable split_half to the number of slots divided by 2
        split_half = self.num_slots/2
        # Set random integers to variables
        rand_num1 = random.randint(0, split_half-1)
        rand_num2 = random.randint(split_half, self.num_slots -1)
        # If value at random number less, then set return value to lesser one
        if occupancy_at_slot[rand_num1] < occupancy_at_slot[rand_num2]:
            rand_num = rand_num1
        elif occupancy_at_slot[rand_num2] < occupancy_at_slot[rand_num1]:
            rand_num = rand_num2
        else:
            rand_num = rand_num2

        # Return the final slot number that you pick.
        return rand_num

def check_hash_type(hash_type):
    if hash_type not in ["standard", "2choice", "2left"]:
        raise argparse.ArgumentTypeError("In valid hash type.  Valid hash types include standard, 2choice, and 2left.")
    return hash_type

def main(args):
    if args.occupancy > 1:
        raise("Occupancy should be less than or equal to 1")

    ht = HashTable(100, 5, args.occupancy, 1000, args.hash_type)
    ht.run_hash_table()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='A hash table.')
    parser.add_argument('--occupancy', type=float,
                        help='how occupied is the table')
    parser.add_argument('--hash_type', type=check_hash_type,
                        help='type of table:  standard, 2choice, 2left')

    arguments = parser.parse_args()
    main(arguments)


from __future__ import unicode_literals
from sopel import tools, web
from sopel.module import rule
from sopel.formatting import color, bold

import json
import re

def setup(sopel):
    regex = re.compile('((?:.+\.)?deviantart\.com/.*)')
    if not sopel.memory.contains('url_callbacks'):
        sopel.memory['url_callbacks'] = tools.SopelMemory()
    sopel.memory['url_callbacks'][regex] = get_page_info

def get_prefix():
    prefix = color('[', 'grey') + 'deviant' + bold('art') + color(']', 'grey')
    return prefix

def get_info(url=None):
    if url:
        api_url = 'http://backend.deviantart.com/oembed?url={}'.format(url)
    else:
        return None
    data = web.get(api_url)
    data = json.loads(data)
    return data

@rule('.*https?://((?:.+\.)?deviantart\.com/.*).*')
def get_page_info(bot, trigger):
    try:
        requested = get_info('http://' + trigger.group(1))
    except:
        return
    if not requested:
        bot.say('{} Error:  does not exist.'.format(get_prefix()))
    else:
        message = '{} {} by {} ({}) [{}]'.format(get_prefix(), requested['title'], requested['author_name'], requested['type'], requested['category'])
        bot.say(message)