# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['clismo',
 'clismo.automata',
 'clismo.builtin',
 'clismo.compiler',
 'clismo.compiler.parsers',
 'clismo.ia',
 'clismo.lang',
 'clismo.optimization',
 'clismo.sim',
 'clismo.visitors']

package_data = \
{'': ['*']}

install_requires = \
['typer>=0.4.0,<0.5.0']

entry_points = \
{'console_scripts': ['clismo = clismo.main:app']}

setup_kwargs = {
    'name': 'clismo',
    'version': '0.1.2',
    'description': 'Programing language designed for simulating and optimizing client-server like discret events models',
    'long_description': '# Clismo\n\n## Índice\n\n- [Objetivos](https://github.com/jmorgadov/clismo#objetivos)\n- [Instalación](https://github.com/jmorgadov/clismo#instalaci%C3%B3n)\n- [Lenguaje](https://github.com/jmorgadov/clismo#lenguaje)\n  - [Características básicas](https://github.com/jmorgadov/clismo#caracter%C3%ADsticas-b%C3%A1sicas)\n    - [Declaración y uso de variables](https://github.com/jmorgadov/clismo#declaraci%C3%B3n-y-uso-de-variables)\n    - [Condicionales](https://github.com/jmorgadov/clismo#condicionales)\n    - [Ciclos](https://github.com/jmorgadov/clismo#cliclos)\n  - [Estructuras principales](https://github.com/jmorgadov/clismo#estructuras-principales)\n  - [Atributos y funciones especiales](https://github.com/jmorgadov/clismo#atributos-y-funciones-especiales)\n    - [Client](https://github.com/jmorgadov/clismo#client)\n    - [Server](https://github.com/jmorgadov/clismo#server)\n    - [Step](https://github.com/jmorgadov/clismo#step)\n    - [Simulation](https://github.com/jmorgadov/clismo#simulation)\n  - [General](https://github.com/jmorgadov/clismo#general)\n  - [Funciones built-in](https://github.com/jmorgadov/clismo#funciones-built-in)\n- [Implementación](https://github.com/jmorgadov/clismo#implementaci%C3%B3n)\n  - [Autómatas](https://github.com/jmorgadov/clismo#aut%C3%B3matas)\n  - [Motor de expresiones regulares](https://github.com/jmorgadov/clismo#motor-de-expresiones-regulares)\n  - [Tokenizador](https://github.com/jmorgadov/clismo#tokenizador)\n  - [Gramáticas](https://github.com/jmorgadov/clismo#gram%C3%A1ticas)\n  - [Árbol de Sintaxis Abstracta (AST)](https://github.com/jmorgadov/clismo#%C3%A1rbol-de-sintaxis-abstracta-ast)\n  - [Parser](https://github.com/jmorgadov/clismo#parser)\n  - [Visitors](https://github.com/jmorgadov/clismo#visitors)\n  - [Ejecución](https://github.com/jmorgadov/clismo#ejecuci%C3%B3n)\n    - [Reconocimiento de las estructuras declaradas](https://github.com/jmorgadov/clismo#reconocimiento-de-las-estructuras-declaradas)\n    - [Chequeo semántico](https://github.com/jmorgadov/clismo#chequeo-sem%C3%A1ntico)\n    - [Evaluación](https://github.com/jmorgadov/clismo#evaluaci%C3%B3n)\n- [Ejemplo](https://github.com/jmorgadov/clismo#ejemplo)\n\n## Objetivos\n\nEl objetivo de este proyecto es crear un lenguaje de programación\n(específicamente un DCL) que permita la creación, ejecución y optmización de\nmodelos de simulación de eventos discretos de tipo cliente-servidor. De esta\nforma, los usuarios pueden enfocarse más en la estructura de los modelos y no\nen la implementación interna de los mismos.\n\nEl lenguaje ofrece además la posibilidad de optimizar los modelos creados bajo\ncriterios que el mismo usuario puede especificar.\n\n## Instalación\n\n```bash\npip install clismo\n```\n\nPara más información sobre el uso del programa:\n\n```bash\nclismo --help\n```\n\n## Lenguaje\n\n**Clismo** es un lenguaje no orientado a objetos, y a pesar de ser un lenguaje\nde programación imperativo, el mismo tiene características funcionales. El\ntipado es estático, aunque no es necesario declarar los tipos en la sintaxis\n(son inferidos automáticamente).\n\nAl ser un lenguaje dedicado a la creación de modelos de simulación del tipo\ncliente-servidor, posee cuatro estructuras básicas: `client`, `server`, `step`\ny `simulation`. Las relaciones entre estas estructuras son:\n\n- Los servidores atienden a los clientes.\n- Un `step` contiene una serie de servidores (indica que están en paralelo).\n- Un `simulation` contiene una serie de steps (indica que están en secuencia).\n\nCada una de estas estructuras puede contener atributos y/o funciones especiales\nque definen el comportamiento del sistema que serán analizados en futuras\nsecciones.\n\n### Características básicas\n\nA continuación se presentan ejemplos de código que muestran las características\nbásicas del lenguaje.\n\n#### Declaración y uso de variables\n\n```text\nvar name = "John"\nvar is_elder = False\nvar count = 10\ncount = count + 3\n```\n\n#### Condicionales\n\n```text\nif count == 10:\n    ...\nelse:\n    ...\n```\n\n> Con indentaciones similar a Python.\n\n#### Cliclos\n\n- Ciclo infinito\n\n    ```text\n    loop:\n        ...\n    ```\n\n    > Dentro de los loops se pueden usar las palabras claves `endloop` o `nextloop`\n    > para terminar el loop o saltar a la siguiente iteración respectivamente.\n\n- Ciclo infinito con variable\n\n    ```text\n    loop i:\n        ...\n    ```\n\n- Ciclo infinito con variable y comienzo\n\n    ```text\n    loop i from 5:\n        ...\n    ```\n\n- Ciclo con comienzo y fin\n\n    ```text\n    loop i from 5 to 10:\n        ...\n    ```\n\n- Ciclo con comienzo, fin y paso\n\n    ```text\n    loop i from 5 to 10 by 2:\n        ...\n    ```\n\n### Estructuras principales\n\nLas estructuras principales (`client`, `server`, `step` y `simulation`) se\ncrean de la siguiente forma:\n\n```text\n<struct_type> <name>:\n    <attr_name> = <attr_value>\n    ...\n\n    <func_name>(<specifications>):\n        <func_body>\n        ...\n```\n\n> Los <specifications> no representan parámetros que se pasan a la función,\n> sino que son, como su nombre lo indica, especificaciones.\n\nPor ejemplo, para crear un servidor:\n\n```text\nserver server1:\n    my_own_attr = 10\n\n    attend_client():\n        return norm() + 10\n```\n\n> `attend_client` es una función especial que deben definir los servidores para\n> indicar el tiempo que tardará en atender a un cliente.\n\n### Atributos y funciones especiales\n\nAlgunas estructuras requieren de atributos y funciones especiales para\nser poder definidas. (Como en el ejemplo anterior `attend_client`, no\nse puede crear un servidor si no se define esta función en el cuerpo\ndel mismo).\n\n#### Client\n\nLos clientes no requieren de atributos especiales, pero si pueden definir\nopcionalmente una función `on_server_out` que será llamada cuando el cliente\nsale del servidor. Esta función no debe devolver ningun valor, se usa\ngeneralemente para indicar modificar attributos del cliente.\n\n> Más adelante se explicará como se pueden obtener y modificar los atributos\n> de una estructura.\n\n#### Server\n\nLos servidores requieren la implementación de la función `attend_client`.\nEsta función debe devolver el tiempo que tardará en atender a un cliente.\nDentro de ella se puede acceder al cliente actual mediante la variable\n`current_client`.\n\n#### Step\n\nLos steps no requieren ninguna función especial pero si la declaración del\natributo `servers`, una lista de servidores que se encargarán de atender a los\nclientes.\n\n```text\nstep step1:\n    servers = [server1, server2]\n```\n\n#### Simulation\n\nLas simulaciones requieren la declaración de los siguientes atributos:\n\n- `steps`: una lista de steps que representan los servidores en serie.\n- `time_limit` o `client_limit`: una cantidad de tiempo o cantidad de clientes que se desea simular. Solo es necesario uno de los dos (aunque se pueden indicar ambas)\n- `arrive(<client_type>)`: una función que devuelve el tiempo que tardará en llegar un cliente de un tipo determinado. Ejemplo:\n    \n    ```text\n    arrive(DefaultClient):\n        return norm() + 10\n    ```\n\n    > `DefaultClient` es un tipo de cliente que se usa por defecto. Cada simulación\n    > puede definir más funciones `arrive` con clientes de distintos tipos. También\n    > se puede definir una misma función `arrive` para varios tipos de clientes,\n    > por ejemplo `arrive(ClientType1, ClientType2)`.\n\nOpcionalemente, se puede definit el modo de ejecución de la simulación con el\natributo `mode`. El mismo puede ser `"run"` (por defecto) o `"optimize"`. Estos\nindican si se quiere ejecutar la simulación u optimizar el modelo.\n\nEn caso de que se quiera optimizar el modelo, se debe definir la función\n`minimize()` que devuelve el valor que se desea minimizar.\n\nPara la configuración del algoritmo genético utilizado para la optimización,\nse pueden especificar los atributos:\n\n- `pop_size`: cantidad de individuos en la población. Por defecto es 10.\n- `max_iter`: cantidad máxima de iteraciones. Por defecto es 5.\n- `mut_prob`: probabilidad de mutación. Por defecto es 0.1.\n- `best_sel`: cantidad de individuos que se seleccionan para la siguiente generación. Por defecto es 3.\n- `new_rand`: cantidad de nuevos individuos que se generan al final de cada iteración. Por defecto es 2.\n\n### General\n\nCada estructura puede definir una función `possible(<attr_name>)`. Esta función\ndebe devolver un posible valor (aleatorio) para el atributo especificado.\n\nEstas funciones se utlizarán para crear el vector de posibles valores para los\ndiferentes atributos. Con ello se pueden crear diferentes versiones de un mismo\nmodelo y es lo que el algoritmo genético utilizará para optimizar el mismo.\n\nAdicionalemente, en cualquier función se puede acceder a las variables globales\n`time` y `clients` que representan el tiempo actual se la simulación y la\ncantidad de clientes que han salido del sistema.\n\n### Funciones built-in\n\nDentro de la implementación de las funciones especiales se pueden usar diversas\nfunciones built-in. Dos de las funciones principales son: `get` y `set`. Con\nestas funciones se pueden obtener y modificar los atributos de una estructura.\nPor ejemplo:\n\n```text\nserver S1:\n    mean = 10\n    total = 0\n\n    attend_client():\n        set(self, "total", get(self, "total") + 1)\n        return nomr() + get(self, "mean")\n```\n\n> `self` es una referencia a la instancia de la estructura actual\n\nComo el lenguaje no es orintado a objetos, existen diversas funciones que\npermiten realizar todo tipo de acciones:\n\n- `get_at(<list>, <index>)`: devuelve el valor del elemento en la posición especificada de la lista.\n- `set_at(<list>, <index>, <value>)`: modifica el valor del elemento en la posición especificada de la lista.\n- `append(<list>, <value>)`: agrega un elemento al final de la lista.\n- `rand()`: devuelve un valor aleatorio entre 0 y 1.\n- `norm()`: devuelve un valor aleatorio entre 0 y 1 con distribución normal.\n- `randint(<min>, <max>)`: devuelve un valor aleatorio entre `min` y `max`.\n- `startswith(<string>, <substring>)`: devuelve `True` si `string` empieza con `substring`.\n- `lower(<string>)`: devuelve `string` en minúsculas.\n- `upper(<string>)`: devuelve `string` en mayúsculas.\n- `sqrt(<number>)`: devuelve la raíz cuadrada de `number`.\n- `abs(<number>)`: devuelve el valor absoluto de `number`.\n- `round(<number>, <decimals>)`: devuelve `number` redondeado a `decimals` decimales.\n\nEntre muchas otras similares a las funciones de python.\n\n## Implementación\n\n**Clismo** es un lenguaje evaluado escrito en Python. A continuación se\nexponen las características principales de la implementación de cada etapa.\n\n### Autómatas\n\nPara la creación de las algunas de las proximas funcionalidades, se realizaó\nuna implementación de un tipo `Automata` que permite simular una\nmáquina de estados de forma genérica. A los mismos se le pueden agregar\nestados así como transiciones entre los mismos. Cada autómata tiene un estado\ninicial y uno o varios estados finales.\n\nLa ejecución de una máquina de estados realizada con un autómata es bastante\nsimple. Dado una entrada iterable, se comienza en el estado inicial y se va\nejecutando cada transición hasta llegar a un estado final. En caso de llegar a\nun estado en el que ninguna transición es válida, se termina la ejecución y la\nentrada no es válida. En caso de terminar de recorrer la entrada se clasifica\nla entrada como válida o inválida en dependencia de si se llegó a un estado\nfinal o no respectivamente.\n\nLos autómatas pueden tener transiciones **épsilon** entre estados, en este\ncaso, la ejecución se bifurca y la maquina de estados se mueve por todos los\nestaos posibles al mismo timepo. Esto da la posibliadad de ejecutar autómatas\nno deterministas.\n\nSe implementó además, utilizando el algoritmo visto en clase (calculando los\n**goto** y **epsilon clausuras**) la opción de convertir un autómata no\ndeterminista (NFA) a un autómata determinista (DFA).\n\n### Motor de expresiones regulares\n\nLas principales funcionalidades implementadas son:\n\n- Operador `*`: Matchea cero o más veces la expresión anterior.\n- Operador `|`: Mathcea la expresión anterior o la siguiente.\n- Operador `^`: Matchea cualquier expresion excepto la expresión que le prosigue.\n- Caracter `.`: Matchea cualquier caracter (ASCII).\n- Caracter `\\`: Inicio de un caracter especial.\n- Caracter `\\d`: Matchea un dígito.\n- Caracter `\\a`: Matchea una letra minúscula.\n- Caracter `\\A`: Matchea una letra mayúscula.\n- Parentesis `(` y `)`: Agrupan una expresión regular.\n\n> Cualquier operador o caracter especal puede ser escapado con `\\`.\n\nPara la realización del motor de expresiones regulares se utilizó la clase\n`Automata`. Para cada expresión regular se construye un autómata finito no\ndeterminista (NFA) usando el algoritmo de Thompson y luego el mismo se\nconvierte a un DFA utlizando el método `to_dfa` de la clase `Automata`.\n\nSe ofrecen además dos funciones para el matcheo de cadenas segun una expresión\nregular: `match` (la cual tiene un comportamiento similar a `re.match`) y\n`compile_patt` (la cual tiene un comportamiento similar a `re.compile`). La\nventaja principal de usar `compile_patt` es que se no es necesario crear un\nautómata para cada vez que se desea matchear una cadena (ya que el autómata es\nconstruido una sola vez).\n\n### Tokenizador\n\nPara la implementación del tokenizador se creó una clase `Tokenizer`. Esta\nclase se encarga de tomar un texto y dividirlo en diferentes tipos de tokens.\nCada patrón que se agrega está definido por un nombre (tipo del token) y una\nexpresión regular (se hace uso del motor de expresiones regulares\nimplementado).\n\n```python\ntknz = Tokenizer()\ntknz.add_pattern("NUMBER", r"\\d\\d*|\\d\\d*\\.\\d\\d*")\n```\n\nAl tokenizar un texto, se revisan los patrones comenzando por el primero (en el\nmismo orden en el que fueron agregados) y el primero que matchee con un prefijo\nde la cadena se establece como un token nuevo (se toma como lexema la subcadena\nque matcheó con la expresión regular). Luego se vuelve a realizar esta\noperación con el resto de la cadena, así sucesivamente hasta terminar la misma.\nSi en algún punto no se encuentra un token que matchee con el inicio de la\ncadena, se considera que la cadena no se puede tokenizar (con los tipos de\ntokens establecidos).\n\nCada vez que se agrega un patrón al tokenizador se puede establecer una\nfunción que se aplicará al lexema antes de guardar su valor en el token.\n\nPor ejemplo, para quitar las comillas al tokenizar un **string**:\n\n```python\ntknz.add_pattern("STRING", r"\'((^\')|(\\\\\'))*(^\\\\)\'", lambda t: t[1:-1])\n```\n\nEsta función tambien puede ser utilizada para indicar que se quiere ignorar\nlos tokens de un tipo determinado. En tal caso basta con que la función devuelva\n`None`:\n\n```python\ntknz.add_pattern("SPACE", r"( | \\t)( |\\t)*", lambda t: None)\n```\n\nSe ofrece también la opción de agregar `keywords` (palabras claves) para una\nmayor comodidad. Esto se hace mediante el método `add_keywords()` el cual recibe\nuna lista de palabras. En el proceso de tokenización, si el prefijo matcheado\nconicide con alguna de las palabras clave, entonces el tipo del token se\nestablece como `KEYWORD`.\n\nEn caso de que se quiera aplicar una función para procesar todos los tokens\nobtenidos, se puede usar el decorador `process_tokens` de la clase `Tokenizer`.\nEste debe ser usado en una función que reciba un solo argumento (la lista de\ntokens) y devuelva una lista de tokens procesados.\n\n```python\n@tknz.process_tokens\ndef process_tokens(tokens):\n    # ...\n    return tokens\n```\n\nFinalmente, para obtener los tokens de un texto basta con usar la función\n`tokenize`:\n\n```python\ntokens = tknz.tokenize("some text")\n```\n\n### Gramáticas\n\nSe implementaron las clases `Grammar`, `NonTerminal`, `Terminal` y `Production`\nlas cuales son usadas para la representación de una gramática general. Se\nimplementó además un parser de gramáticas con el cual es posible crear\ngramáticas dado un formato, esto permite definir la gramática del lenguaje en\nun archivo y poder cambiarla fácilmente. Dado la sencillez del formato (el\nlenguaje de las gramáticas), se implementó un sencillo parser recursivo\ndescendente para la creación de las mismas.\n\nEl formato especificado es el siguiente:\n\n```\nexpression: production_1 | production_2 | ... | production_n\n```\n\nDe forma equivalente, para mayor legibilidad:\n\n```\nexpression:\n    | production_1 \n    | production_2\n    | ...\n    | production_n\n```\n\nEjemplo:\n\n```\nExprAB:\n    | \'a\' ExprAB \'b\'\n    | EPS\n```\n\n> EPS es un elemento especial en las gramáticas para representar *epsilon*\n\nLas gramáticas luego pueden ser cargadas como se muestra a continuación:\n\n```python\nfrom grammar im port Grammar\ngm = Grammar.open("expr_ab.gm")\n```\n\nLas gramáticas están compuestas por una lista de expresiones (no terminales).\nCada no terminal de la gramática, contiene una lista de producciones. Cada\nproducción contiene una lista de elementos (terminales o no terminales).\n\n### Árbol de Sintaxis Abstracta (AST)\n\nPara la creación de un AST se creó la clase abstracta `AST`. De esta clase\nheredan todos las clases que representan los nodos del árbol de sintaxis \nabstracta del lenguaje. En la clase se implementa también un método `dump`\nque permite mostrar el árbol de forma legible. Este método usa el\natributo `__slots__` mediante el cual se definen los atributos que se\nquieren mostrar.\n\nPara definir cómo se construye cada nodo del AST se pueden asignar los\nconstructores a cada producción de la gramática usando la función\n`assign_builders`. Esta función recibe un diccionario donde las llaves son la\nrepresentación textual de la producción y los valores son funciones que reciben\ncomo argumentos los elementos de la producción. En caso de que el símbolo sea\nun terminal la función recibirá dicho terminal, en caso de ser un no terminal,\nla función recibirá el resultado de la ejecución algunas de las funciones\nconstructoras de las producciones que tengan como cabeza a dicho no terminal.\n\nPor ejemplo, a continuación se muestran algunos de los constructores para\nla gramática de **Clismo**:\n\n```python\nbuilders = {\n    # -------------------------------------------------------------------------\n    "program -> obj_def program": lambda s, p: ast.Program([s] + p.stmts),\n    "program -> NEWLINE program": lambda n, p: p,\n    "program -> EPS": lambda: ast.Program([]),\n    # -------------------------------------------------------------------------\n    "obj_def -> client_def": lambda c: c,\n    "obj_def -> server_def": lambda s: s,\n    "obj_def -> step_def": lambda s: s,\n    "obj_def -> sim_def": lambda s: s,\n    # -------------------------------------------------------------------------\n    # ...\n    # ...\n```\n\n### Parser\n\nPara la implementación del parser principal del lenguaje se creó la clase\nabstacta `Parser`. Usando esta clase como base se creó una clase `LR1Parser`,\nla cual implementa un parser LR(1).\n\nPara la realización del parser LR(1) fue necesario implementar las clases\n`LR1Item` y `LR1Table`. La primera de estas clases representa un item del\nparser, el cual contiene: la producción que lo genera, la posición del punto\n(dot) en la producción y el terminal que le debe proseguir (lookahead).\n\nLa segunda clase (`LR1Table`) representa la tabla de transición del parser.\nCada posición de la tabla puede contener tres tipos de elementos: un **string**\n`"OK"`, que indica que el estado de aceptación; un valór numérico entero, que\nindica cual es el siguiente estado; o un no terminal de la gramática, el cual\nrepresenta que hay que realizar una reducción. Para no tener que recalcular la\ntabla cada vez que se va a parsear un texto, la misma puede ser serializada y\nluego cargada.\n\nLa construcción de la tabla se realizó siguiendo el algoritmo visto en las\nconferencias de la asignatura (calculando los **goto** y las **clausuras** de\nlos estados).\n\nEn el proceso de parsing, al realizar una acción de reducción, es donde se\nutilizan las funciones constructoras vistas en la sección anterior. En\ndependencia de la producción que se está reduciendo, se llama a la función\nconstructora correspondiente.\n\nPara una mayor comodidad se implementó también la clase `ParserManager`. Esta\nclase ofrece, dado una gramática, un tokenizador (opcional) y un parser\n(opcional, por defecto LR(1)), métodos como: `parse_file` (para parsear un\narchivo), `parse` (para parsear un texto) y `parse_tokens` (para parsear una\nlista de tokens directamete). Estas funciones devuelven el AST resultante del\nproceso de parsing.\n\n### Visitors\n\nUna vez obtenido el AST de un programa es necesario realizar recorridos sobre\nél. Para ello se implmentó una clase `Visitor` la cual contiene dos decoradores\n`@visitor` y `@callback`. Por cada **visitor** que se quiera implementar para\nel AST, se debe implementar una nueva clase que tenga como atributo de clase\nuna instancia de la clase `Visitor`. Luego, cada método de la clase que tenga\nel decorador `@visitor`, se establecerá como una sobrecarga. Es por ello que\ntodos estos métodos deben tener sus argumentos tipados (esta es la forma en la\nque el **visitor** sabe cual de los métodos de la clase debe llamar).\n\nPor ejemplo:\n\n```python\nfrom clismo.lang.visitor import Visitor\n\nclass EvalVisitor:\n    visitor_dec = Visitor().visitor\n\n    @visitor_dec\n    def eval(self, node: ast.Program):\n        for stmt in node.stmts:\n            stmt.eval(self)\n\n    @visitor_dec\n    def eval(self, node: ast.ClientDef): ...\n\n    # ...\n```\n\nEl decorador `@callback` se utiliza para definir funciones que se van a llamar\ncada vez que se llame a una función marcada como **visitor**.\n\n### Ejecución\n\nEl proceso de ejecución se divide en 3 partes:\n\n1. Reconocimiento de las estructuras declaradas.\n2. Chequeo semántico.\n3. Ejecución.\n\n#### Reconocimiento de las estructuras declaradas\n\nEn este paso se recorre el AST con un visitor el cual analiza las estructuras\ndeclaradas. Esta información es utilizada luego en el checqueo semántico. Esto\npermite usar dentro de las estructuras referencias a otras que por el orden del\ncódigo no han sido definidas aún.\n\n#### Chequeo semántico\n\nEn esta etapa se realiza una verificación a cada estructura declarado\n(igualmente con otro visitor). Se asegura que las estructuras declaradas tengan\nlos atributos y funciones necesarias para su correcta ejecución.\n\nAdemás se realiza un chequeo de tipos de los atributos y cuerpos de las\nfunciones. De esta forma se asegura que cada función devuelva un valor de\ntipo correcto.\n\n#### Evaluación\n\nFinalmente, un último vísitor se encarga de recorrer el AST configurando la\nsimulación según las estructuras declaradas para luego ejecutar (u optimizar)\nel modelo definido.\n\n## Ejemplo\n\nEn el script [clismo_example.csm](./clismo_example.csm) se muestra un ejemplo\nde un programa.\n\n```text\nclient Normal:\n    test_val = 5\n\n    possible(test_val):\n        return randint(2, 10)\n\nserver S1:\n    total = 0\n\n    attend_client():\n        var t = 1 * get(current_client, "test_val")\n        set(self, "total", get(self, "total") + t)\n        return t\n\nserver S2:\n    total = 0\n\n    attend_client():\n        var t = 3 * get(current_client, "test_val")\n        set(self, "total", get(self, "total") + t)\n        return t\n\nstep P1:\n    servers = [S2, S2, S1]\n\n    possible(servers):\n        var s = [S1, S2]\n        var count = len(get(self, "servers"))\n        var new_servers = list("server")\n        loop _ from 0 to count:\n            var r = randint(0, 1)\n            new_servers = append(new_servers, get_at(s, r))\n        return new_servers\n\nsimulation Test:\n    mode = "optimize"\n    steps = [P1]\n    client_limit = 50\n\n    max_iter = 20\n    pop_size = 3\n    mut_prob = 0.4\n    new_rand = 10\n\n    arrive(Normal):\n        return 5\n    \n    minimize():\n        var servers = get(get_at(get(self, "steps"), 0), "servers")\n        var count = len(servers)\n        var total = 0\n        loop i from 0 to count:\n            total = total + get(get_at(servers, i), "total")\n        return total\n```\n',
    'author': 'Jorge Morgado Vega',
    'author_email': 'jorge.morgadov@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': None,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
